From 0014ea0da0cb9f3fd4ca16bb9885c4b2d2ab801c Mon Sep 17 00:00:00 2001
From: Eduardo Silva <edsiper@monotop.(none)>
Date: Fri, 1 Jun 2007 00:08:24 -0400
Subject: [PATCH] Palette: Automatic positioning

---
 sugar/graphics/palette.py    | 153 +++++++++++++++++++++++------------
 sugar/graphics/toolbutton.py |  26 +++---
 2 files changed, 117 insertions(+), 62 deletions(-)

diff --git a/sugar/graphics/palette.py b/sugar/graphics/palette.py
index 226ba31b..ea14d863 100644
--- a/sugar/graphics/palette.py
+++ b/sugar/graphics/palette.py
@@ -21,14 +21,15 @@ from gtk import gdk, keysyms
 import gobject
 import pango
 
-ALIGNMENT_BOTTOM_LEFT   = 0
-ALIGNMENT_BOTTOM_RIGHT  = 1
-ALIGNMENT_LEFT_BOTTOM   = 2
-ALIGNMENT_LEFT_TOP      = 3
-ALIGNMENT_RIGHT_BOTTOM  = 4
-ALIGNMENT_RIGHT_TOP     = 5
-ALIGNMENT_TOP_LEFT      = 6
-ALIGNMENT_TOP_RIGHT     = 7
+ALIGNMENT_AUTOMATIC     = 0
+ALIGNMENT_BOTTOM_LEFT   = 1
+ALIGNMENT_BOTTOM_RIGHT  = 2
+ALIGNMENT_LEFT_BOTTOM   = 3
+ALIGNMENT_LEFT_TOP      = 4
+ALIGNMENT_RIGHT_BOTTOM  = 5
+ALIGNMENT_RIGHT_TOP     = 6
+ALIGNMENT_TOP_LEFT      = 7
+ALIGNMENT_TOP_RIGHT     = 8
 
 class Palette(gtk.Window):
     __gtype_name__ = 'SugarPalette'
@@ -36,7 +37,7 @@ class Palette(gtk.Window):
     __gproperties__ = {
         'parent': (object, None, None, gobject.PARAM_READWRITE),
 
-        'alignment': (gobject.TYPE_INT, None, None, 0, 7, ALIGNMENT_BOTTOM_LEFT,
+        'alignment': (gobject.TYPE_INT, None, None, 0, 8, ALIGNMENT_AUTOMATIC,
                     gobject.PARAM_READWRITE)
     }
 
@@ -47,6 +48,8 @@ class Palette(gtk.Window):
         gobject.GObject.__init__(self, type=gtk.WINDOW_POPUP)
         gtk.Window.__init__(self)
 
+        self._alignment = ALIGNMENT_AUTOMATIC
+
         self._palette_label = gtk.Label()
         self._palette_label.set_ellipsize(pango.ELLIPSIZE_START)
         self._palette_label.show()
@@ -81,9 +84,8 @@ class Palette(gtk.Window):
 
         self.set_border_width(self._WIN_BORDER)
         self.add(vbox)
-        
-    def do_set_property(self, pspec, value):
 
+    def do_set_property(self, pspec, value):
         if pspec.name == 'parent':
             self._parent_widget = value
         elif pspec.name == 'alignment':
@@ -92,46 +94,87 @@ class Palette(gtk.Window):
             raise AssertionError
 
     def set_position(self):
+        # Automatic Alignment
+        if self._alignment == ALIGNMENT_AUTOMATIC:
+            # Trying Different types of ALIGNMENTS, 
+            # and return the choosen one
+            if self._try_position(ALIGNMENT_BOTTOM_LEFT):
+                return ALIGNMENT_BOTTOM_LEFT
+            elif self._try_position(ALIGNMENT_BOTTOM_RIGHT):
+                return ALIGNMENT_BOTTOM_RIGHT
+            elif self._try_position(ALIGNMENT_LEFT_BOTTOM):
+                return ALIGNMENT_LEFT_BOTTOM
+            elif self._try_position(ALIGNMENT_LEFT_TOP):
+                return ALIGNMENT_LEFT_TOP
+            elif self._try_position(ALIGNMENT_RIGHT_BOTTOM):
+                return ALIGNMENT_RIGHT_BOTTOM
+            elif self._try_position(ALIGNMENT_RIGHT_TOP):
+                return ALIGNMENT_RIGHT_TOP
+            elif self._try_position(ALIGNMENT_TOP_LEFT):
+                return ALIGNMENT_TOP_LEFT
+            elif self._try_position(ALIGNMENT_TOP_RIGHT):
+                return ALIGNMENT_TOP_RIGHT
+        else:
+            # Manual Alignment
+            move_x, move_y = self._calc_position(self._alignment)
+            self.move(move_x, move_y)
 
-        window_axis = self._parent_widget.window.get_origin()
-        parent_rectangle = self._parent_widget.get_allocation()
+    def _try_position(self, alignment):
+        scr_width = gtk.gdk.screen_width()
+        scr_height = gtk.gdk.screen_height()
 
-        palette_width, palette_height = self.get_size_request()
-
-        # POSITIONING NOT TESTED
-        if self._alignment == ALIGNMENT_BOTTOM_LEFT:
-            move_x = window_axis[0] + parent_rectangle.x
-            move_y = window_axis[1] + parent_rectangle.y + parent_rectangle.height
-
-        elif self._alignment == ALIGNMENT_BOTTOM_RIGHT:
-            move_x = parent_rectangle.x - palette_width
-            move_y = window_axis[1] + parent_rectangle.y + parent_rectangle.height
-
-        elif self._alignment == ALIGNMENT_LEFT_BOTTOM:
-            move_x = parent_rectangle.x - palette_width
-            move_y = palette_rectangle.y
-
-        elif self._alignment == ALIGNMENT_LEFT_TOP:
-            move_x = parent_rectangle.x - palette_width
-            move_y = parent_rectangle.y + palette_rectangle.height
-
-        elif self._alignment == ALIGNMENT_RIGHT_BOTTOM:
-            move_x = parent_rectangle.x + parent_rectangle.width
-            move_y = parent_rectangle.y
-
-        elif self._alignment == ALIGNMENT_RIGHT_TOP:
-            move_x = parent_rectangle.x + parent_rectangle.width
-            move_y = parent_rectangle.y + parent_rectangle.height
-
-        elif self._alignment == ALIGNMENT_TOP_LEFT:
-            move_x = parent_rectangle.x
-            move_y = parent_rectangle.y - palette_height
-
-        elif self._alignment == ALIGNMENT_TOP_RIGHT:
-            move_x = (parent_rectangle.x + parent_rectangle.width) - palette_width
-            move_y = parent_rectangle.y - palette_height
+        plt_width, plt_height = self.size_request()
 
+        move_x, move_y = self._calc_position(alignment)
         self.move(move_x, move_y)
+        plt_x, plt_y = self.window.get_origin()
+
+        if (plt_x<0 or plt_x+plt_width>scr_width) or (plt_y<0 or plt_y+plt_height>scr_height):
+            return False
+        else:
+            self.move(move_x, move_y)
+            return True
+
+    def _calc_position(self, alignment):
+        win_x, win_y = self._parent_widget.window.get_origin()
+        parent_rectangle = self._parent_widget.get_allocation()
+        palette_rectangle = self.get_allocation()
+
+        palette_width, palette_height = self.size_request()
+
+        if alignment == ALIGNMENT_BOTTOM_LEFT:
+            move_x = win_x + parent_rectangle.x
+            move_y = win_y + parent_rectangle.y + parent_rectangle.height
+
+        elif alignment == ALIGNMENT_BOTTOM_RIGHT:
+            move_x = (win_x + parent_rectangle.x + parent_rectangle.width) - palette_width
+            move_y = win_y + parent_rectangle.y + parent_rectangle.height
+
+        elif alignment == ALIGNMENT_LEFT_BOTTOM:
+            move_x = (win_x + parent_rectangle.x) - palette_width
+            move_y = win_y + parent_rectangle.y
+
+        elif alignment == ALIGNMENT_LEFT_TOP:
+            move_x = (win_x + parent_rectangle.x) - palette_width
+            move_y = (win_y + parent_rectangle.y + parent_rectangle.height) - palette_rectangle.height 
+
+        elif alignment == ALIGNMENT_RIGHT_BOTTOM:
+            move_x = win_x + parent_rectangle.x + parent_rectangle.width
+            move_y = win_y + parent_rectangle.y
+
+        elif alignment == ALIGNMENT_RIGHT_TOP:
+            move_x = win_x + parent_rectangle.x + parent_rectangle.width
+            move_y = (win_y + parent_rectangle.y + parent_rectangle.height) - palette_rectangle.height
+
+        elif alignment == ALIGNMENT_TOP_LEFT:
+            move_x = (win_x + parent_rectangle.x)
+            move_y = (win_y + parent_rectangle.y) - (palette_rectangle.height)
+
+        elif alignment == ALIGNMENT_TOP_RIGHT:
+            move_x = (win_x + parent_rectangle.x + parent_rectangle.width) - palette_width
+            move_y = (win_y + parent_rectangle.y) - (palette_rectangle.height)
+
+        return move_x, move_y
 
     def set_primary_state(self, label, accel_path=None):
         if accel_path != None:
@@ -156,7 +199,17 @@ class Palette(gtk.Window):
         self._button_bar.pack_start(button, True, True, self._PADDING)
         button.show()
 
+    # Display the palette and set the position on the screen
     def popup(self):
+        # We need to know if the mouse pointer continue inside
+        # the parent widget (opener)
+        pointer_x, pointer_y = self._parent_widget.get_pointer()
+        parent_alloc = self._parent_widget.get_allocation()
+        pointer_rect = gdk.Rectangle(pointer_x + parent_alloc.x, pointer_y + parent_alloc.y, 1, 1)
+
+        if (self._parent_widget.allocation.intersect(pointer_rect).width == 0):
+            return
+
         self.show()
         self.set_position()
         self._pointer_grab()
@@ -166,7 +219,7 @@ class Palette(gtk.Window):
     def _is_mouse_out(self, window, event):
         # If we're clicking outside of the Palette
         # return True
-        event_rect = gdk.Rectangle(event.x, event.y, 1, 1)
+        event_rect = gdk.Rectangle(int(event.x), int(event.y), 1, 1)
 
         if (event.window != self.window or self.allocation.intersect(event_rect).width==0):
             return True
@@ -203,12 +256,12 @@ class Palette(gtk.Window):
     def _on_key_press_event_cb(self, window, event):
         # Escape or Alt+Up: Close
         # Enter, Return or Space: Select
-
         keyval = event.keyval
         state = event.state & gtk.accelerator_get_default_mod_mask()
+        
         if (keyval == keysyms.Escape or
             ((keyval == keysyms.Up or keyval == keysyms.KP_Up) and
              state == gdk.MOD1_MASK)):
             self._close_palette_cb()
         elif keyval == keysyms.Tab:
-            self._close_palette()
+            self._close_palette_cb()
diff --git a/sugar/graphics/toolbutton.py b/sugar/graphics/toolbutton.py
index 1967dd09..67216e37 100644
--- a/sugar/graphics/toolbutton.py
+++ b/sugar/graphics/toolbutton.py
@@ -16,29 +16,31 @@
 # Boston, MA 02111-1307, USA.
 
 import gtk
+import time
 
 from sugar.graphics.icon import Icon
 from sugar.graphics.palette import *
 
 class ToolButton(gtk.ToolButton):
-    def __init__(self, icon_name=None):
+    _POPUP_PALETTE_DELAY = 0.5
+
+    def __init__(self, named_icon=None):
         gtk.ToolButton.__init__(self)
-        self.set_icon(icon_name)
-        
-    def set_icon(self, icon_name):
-        icon = Icon(icon_name)
+        self.set_named_icon(named_icon)
+
+    def set_named_icon(self, named_icon):
+        icon = Icon(named_icon)
         self.set_icon_widget(icon)
         icon.show()
 
     def set_palette(self, palette):
         self._palette = palette
         self._palette.props.parent = self
-        self._palette.props.alignment = ALIGNMENT_BOTTOM_LEFT
-        self.connect('clicked', self._display_palette_cb)
-    
-    def set_tooltip(self, text):
-        tp = gtk.Tooltips()
-        self.set_tooltip(tp, text, text)
+        self.child.connect('enter-notify-event', self._show_palette_timeout_cb)
 
-    def _display_palette_cb(self, widget):
+    def set_tooltip(self, text):
+        pass
+
+    def _show_palette_timeout_cb(self, widget, event):
+        time.sleep(self._POPUP_PALETTE_DELAY)
         self._palette.popup()