diff --git a/shell/view/BuddyPopup.py b/shell/view/BuddyPopup.py
index 5f5e1ba2..b7ca575c 100644
--- a/shell/view/BuddyPopup.py
+++ b/shell/view/BuddyPopup.py
@@ -2,90 +2,41 @@ import gtk
 import goocanvas
 import gobject
 
-from sugar.canvas.CanvasView import CanvasView
-from sugar.canvas.CanvasBox import CanvasBox
+from sugar.canvas.Menu import Menu
 from sugar.canvas.IconItem import IconItem
 from sugar.presence import PresenceService
 
-class BuddyPopup(gtk.Window):
+class BuddyPopup(Menu):
 	ACTION_MAKE_FRIEND = 0
 	ACTION_INVITE = 1
 	ACTION_REMOVE_FRIEND = 2
 
-	__gsignals__ = {
-		'action': (gobject.SIGNAL_RUN_FIRST,
-				   gobject.TYPE_NONE, ([int])),
-	}
-
 	def __init__(self, shell, buddy):
-		gtk.Window.__init__(self, gtk.WINDOW_POPUP)
+		color = buddy.get_color()
+		Menu.__init__(self, shell.get_grid(), buddy.get_name(),
+					  color.get_fill_color(), color.get_stroke_color())
 
 		self._buddy = buddy
-		self._hover = False
-		self._popdown_on_leave = False
-		self._width = 13
 		self._shell = shell
-		self._buddy = buddy
-
-		grid = shell.get_grid()
-
-		canvas = CanvasView()
-		self.add(canvas)
-		canvas.show()
-
-		model = goocanvas.CanvasModelSimple()
-		root = model.get_root_item()
-
-		color = self._buddy.get_color()
-		rect = goocanvas.Rect(fill_color=color.get_fill_color(),
-							  stroke_color=color.get_stroke_color(),
-							  line_width=3)
-		root.add_child(rect)
-
-		text = goocanvas.Text(text=self._buddy.get_name(), font="Sans bold 18",
-							  fill_color='black', anchor=gtk.ANCHOR_SW)
-		grid.set_constraints(text, 1, 3, self._width, 2)
-		root.add_child(text)
-
-		self._height = 4
 
 		owner = shell.get_model().get_owner()
 		if buddy.get_name() != owner.get_name():
-			self._add_actions(grid, root)
+			self._add_actions()
 
-		grid.set_constraints(canvas, 0, 0, self._width, self._height)				
-		grid.set_constraints(rect, 0, 0, self._width, self._height)
-
-		canvas.set_model(model)
-
-	def _add_actions(self, grid, root):
+	def _add_actions(self):
 		shell_model = self._shell.get_model()
 		pservice = PresenceService.get_instance()
 
-		separator = goocanvas.Path(data='M 15 0 L 185 0', line_width=3,
-								   fill_color='black')
-		grid.set_constraints(separator, 0, 4)
-		root.add_child(separator)
-
-		box = CanvasBox(grid, CanvasBox.HORIZONTAL, 1)
-		grid.set_constraints(box, 0, 5)
-
 		friends = shell_model.get_friends()
 		if friends.has_buddy(self._buddy):
 			icon = IconItem(icon_name='stock-remove-friend')
-			icon.connect('clicked', self._action_clicked_cb,
-						 BuddyPopup.ACTION_REMOVE_FRIEND)
+			self.add_action(icon, BuddyPopup.ACTION_REMOVE_FRIEND) 
 		else:
 			icon = IconItem(icon_name='stock-make-friend')
-			icon.connect('clicked', self._action_clicked_cb,
-						 BuddyPopup.ACTION_MAKE_FRIEND)
-		
-		box.set_constraints(icon, 3, 3)
-		box.add_child(icon)
+			self.add_action(icon, BuddyPopup.ACTION_MAKE_FRIEND)
 
 		icon = IconItem(icon_name='stock-chat')
-		box.set_constraints(icon, 3, 3)
-		box.add_child(icon)
+		self.add_action(icon, -1)
 
 		activity = shell_model.get_current_activity()
 		if activity != None:
@@ -94,20 +45,4 @@ class BuddyPopup(gtk.Window):
 			# FIXME check that the buddy is not in the activity already
 
 			icon = IconItem(icon_name='stock-invite')
-			icon.connect('clicked', self._action_clicked_cb,
-						 BuddyPopup.ACTION_INVITE)
-			box.set_constraints(icon, 3, 3)
-			box.add_child(icon)
-
-		root.add_child(box)
-
-		self._height = 10
-
-	def _action_clicked_cb(self, icon, action):
-		self.emit('action', action)
-
-	def get_width(self):
-		return self._width
-
-	def get_height(self):
-		return self._height
+			self.add_action(icon, BuddyPopup.ACTION_INVITE)
diff --git a/sugar/canvas/Menu.py b/sugar/canvas/Menu.py
new file mode 100644
index 00000000..f644a9db
--- /dev/null
+++ b/sugar/canvas/Menu.py
@@ -0,0 +1,79 @@
+import gtk
+import goocanvas
+import gobject
+
+from sugar.canvas.CanvasView import CanvasView
+from sugar.canvas.CanvasBox import CanvasBox
+from sugar.canvas.IconItem import IconItem
+
+class Menu(gtk.Window):
+	__gsignals__ = {
+		'action': (gobject.SIGNAL_RUN_FIRST,
+				   gobject.TYPE_NONE, ([int])),
+	}
+
+	def __init__(self, grid, title, fill, stroke):
+		gtk.Window.__init__(self, gtk.WINDOW_POPUP)
+
+		self._width = 13
+		self._grid = grid
+		self._action_box = None
+
+		self._canvas = CanvasView()
+		self.add(self._canvas)
+		self._canvas.show()
+
+		model = goocanvas.CanvasModelSimple()
+		self._root = model.get_root_item()
+
+		self._rect = goocanvas.Rect(fill_color=fill,stroke_color=stroke,
+							  		line_width=3)
+		self._root.add_child(self._rect)
+
+		text = goocanvas.Text(text=title, font="Sans bold 18",
+							  fill_color='black', anchor=gtk.ANCHOR_SW)
+		self._grid.set_constraints(text, 1, 3, self._width, 2)
+		self._root.add_child(text)
+
+		self._height = 4
+		self._update_constraints()
+
+		self._canvas.set_model(model)
+
+	def _create_action_box(self):
+		separator = goocanvas.Path(data='M 15 0 L 185 0', line_width=3,
+								   fill_color='black')
+		self._grid.set_constraints(separator, 0, 4)
+		self._root.add_child(separator)
+
+		box = CanvasBox(self._grid, CanvasBox.HORIZONTAL, 1)
+		self._grid.set_constraints(box, 0, 5)
+
+		return box
+
+	def add_action(self, icon, action_id):
+		if self._action_box == None:
+			self._action_box = self._create_action_box()
+			self._root.add_child(self._action_box)
+
+			self._height = 10
+			self._update_constraints()
+
+		icon.connect('clicked', self._action_clicked_cb, action_id)
+		self._action_box.set_constraints(icon, 3, 3)
+		self._action_box.add_child(icon)
+
+	def _action_clicked_cb(self, icon, action):
+		self.emit('action', action)
+
+	def _update_constraints(self):
+		self._grid.set_constraints(self._canvas, 0, 0,
+								   self._width, self._height)				
+		self._grid.set_constraints(self._rect, 0, 0,
+								   self._width, self._height)
+
+	def get_width(self):
+		return self._width
+
+	def get_height(self):
+		return self._height