home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2009 June / maximum-cd-2009-06.iso / DiscContents / digsby_setup.exe / lib / gui / uberwidgets / umenu.pyo (.txt) < prev   
Encoding:
Python Compiled Bytecode  |  2009-02-26  |  42.8 KB  |  1,359 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (Python 2.5)
  3.  
  4. from __future__ import with_statement
  5. from gui.skin.skinobjects import SkinColor
  6. import wx
  7. from wx import RectPS, Rect, ITEM_NORMAL, ITEM_SEPARATOR, ITEM_CHECK, ITEM_RADIO, Point, ALIGN_CENTER_VERTICAL, FindWindowAtPoint, MenuItem, CallLater, FindWindowAtPointer, GetMousePosition, wxEVT_MOTION, StockCursor, CURSOR_DEFAULT, GetMouseState, Window
  8. from wx import PyCommandEvent, wxEVT_MENU_OPEN
  9. from gui import skin
  10. from traceback import print_exc
  11. from gui.textutil import default_font
  12. from gui.windowfx import fadein
  13. from gui.vlist.skinvlist import SkinVListBox
  14. from gui.uberwidgets.UberButton import UberButton
  15. from gui.skin.skinobjects import Margins
  16. from gui.uberwidgets.skinnedpanel import SkinnedPanel
  17. from gui.uberwidgets.keycatcher import KeyCatcher
  18. from cgui import SplitImage4
  19. from gui.toolbox import Monitor
  20. import config
  21. from util import traceguard, memoize, Delegate, Storage as S, InstanceTracker
  22. from common import prefprop
  23. from weakref import ref
  24. from logging import getLogger
  25. log = getLogger('umenu')
  26. wxMSW = 'wxMSW' in wx.PlatformInfo
  27. WM_INITMENUPOPUP = 279
  28.  
  29. def MenuItem_repr(item):
  30.     text = None if item.IsSeparator() else item.Label
  31.     return '<%s %s>' % (item.__class__.__name__, text)
  32.  
  33. MenuItem.__repr__ = MenuItem_repr
  34. del MenuItem_repr
  35.  
  36. MenuItem.SetCallback = lambda item, callback: item.Menu.SetCallback(item.Id, callback)
  37. if wxMSW:
  38.     from ctypes import windll
  39.     ReleaseCapture_win32 = windll.user32.ReleaseCapture
  40.  
  41.  
  42. class UMenuTrayTimer(wx.Timer):
  43.     
  44.     def __init__(self, umenu):
  45.         wx.Timer.__init__(self)
  46.         self.umenu = umenu
  47.  
  48.     
  49.     def Notify(self):
  50.         
  51.         try:
  52.             mp = GetMousePosition()
  53.             ms = GetMouseState()
  54.         except Exception:
  55.             return None
  56.  
  57.         if not ms.LeftDown() and ms.RightDown() or ms.MiddleDown():
  58.             return None
  59.         
  60.         menu = self.umenu
  61.         while menu != None:
  62.             if menu.ScreenRect.Contains(mp):
  63.                 return None
  64.                 continue
  65.             submenu = menu.menu._childmenu
  66.             menu = None if submenu is not None else None
  67.         self.Stop()
  68.         self.umenu.Dismiss()
  69.  
  70.  
  71.  
  72. class UMenu(wx.Menu, InstanceTracker):
  73.     last_menu = None
  74.     
  75.     def __init__(self, parent, label = '', id = None, onshow = None, windowless = None):
  76.         if not isinstance(parent, wx.WindowClass):
  77.             raise TypeError('UMenu parent must be a wx.Window')
  78.         
  79.         wx.Menu.__init__(self, label)
  80.         InstanceTracker.track(self)
  81.         if not isinstance(id, (int, type(None))):
  82.             raise TypeError
  83.         
  84.         self._parentmenu = None
  85.         self._childmenu = None
  86.         self.Id = None if id is None else id
  87.         self.Window = parent
  88.         self.OnDismiss = Delegate()
  89.         self.cbs = { }
  90.         if onshow is not None:
  91.             self.Handler.AddShowCallback(self.Id, (lambda menu = (ref(self),): onshow(menu())))
  92.         
  93.         if wxMSW:
  94.             self.Handler.hwndMap[self.HMenu] = self
  95.         
  96.         self.Windowless = windowless
  97.         self.UpdateSkin()
  98.  
  99.     
  100.     def SetWindowless(self, val):
  101.         self._windowless = val
  102.  
  103.     
  104.     def GetWindowless(self):
  105.         return None if self._parentmenu else self._windowless
  106.  
  107.     Windowless = property(GetWindowless, SetWindowless)
  108.     
  109.     def IsShown(self):
  110.         if not self.popup:
  111.             return False
  112.         
  113.         
  114.         try:
  115.             return self.popup.IsShown()
  116.         except AttributeError:
  117.             return False
  118.  
  119.  
  120.     
  121.     def UpdateSkin(self):
  122.         mbskin = skin.get('MenuBar', None)
  123.         if 'wxMac' in wx.PlatformInfo and not mbskin and mbskin.get('menuskin', None) is None or mbskin.get('mode', 'skin').lower() == 'native':
  124.             self.skin = S(native = True)
  125.             native = True
  126.         else:
  127.             self.skin = skin.get(mbskin.menuskin)
  128.             native = False
  129.             self.skin.native = False
  130.         if not native and not hasattr(self, 'popup'):
  131.             self.popup = MenuPopupWindow(self.Window, self)
  132.         elif not native:
  133.             self.popup.UpdateSkin()
  134.             self.popup.vlist.UpdateSkin()
  135.         elif native:
  136.             if hasattr(self, 'popup'):
  137.                 self.popup.Destroy()
  138.                 del self.popup
  139.             
  140.         
  141.  
  142.     
  143.     def Display(self, caller = None):
  144.         self.PopupMenu(caller.ScreenRect)
  145.  
  146.     
  147.     def dismiss_old(self):
  148.         menuref = UMenu.last_menu
  149.         if menuref is None:
  150.             return None
  151.         
  152.         menu = menuref()
  153.         if menu is not None and not wx.IsDestroyed(menu):
  154.             menu.Dismiss()
  155.         
  156.         UMenu.last_menu = None
  157.  
  158.     
  159.     def PopupMenu(self, pos = None, submenu = False, event = None):
  160.         if not submenu:
  161.             self.dismiss_old()
  162.         
  163.         if event is not None:
  164.             event.Skip(False)
  165.         
  166.         if 'wxMSW' in wx.PlatformInfo and self.Windowless:
  167.             _smokeFrame = _smokeFrame
  168.             import gui.native.win.wineffects
  169.             if _smokeFrame:
  170.                 _smokeFrame.SetFocus()
  171.             
  172.         
  173.         self._menuevent = event
  174.         
  175.         try:
  176.             onshow = self._onshow
  177.         except AttributeError:
  178.             pass
  179.         else:
  180.             onshow(self)
  181.         finally:
  182.             del self._menuevent
  183.  
  184.         traceguard.__enter__()
  185.         
  186.         try:
  187.             return popup(pos)
  188.         finally:
  189.             pass
  190.  
  191.  
  192.     
  193.     def Dismiss(self):
  194.         if not self.skin.get('native', False) and not wx.IsDestroyed(self.popup):
  195.             return self.popup.vlist.Dismiss()
  196.         
  197.  
  198.     if wxMSW:
  199.         if hasattr(wx.Menu, 'GetHMenu'):
  200.             GetHMenu = wx.Menu.GetHMenu
  201.         else:
  202.             
  203.             def GetHMenu(self):
  204.                 cast = cast
  205.                 POINTER = POINTER
  206.                 c_long = c_long
  207.                 import ctypes
  208.                 p = cast(int(self.this), POINTER(c_long))
  209.                 return p[25]
  210.  
  211.         HMenu = property(GetHMenu)
  212.     
  213.     
  214.     def AddItem(self, text = '', bitmap = None, callback = None, id = -1):
  215.         return self._additem(text, bitmap, callback, id = id)
  216.  
  217.     
  218.     def AddItemAt(self, position, text = '', bitmap = None, callback = None, id = -1):
  219.         return self._additem(text, bitmap, callback, id = id, position = position)
  220.  
  221.     
  222.     def Append(self, id, text, bitmap = None, callback = None):
  223.         return self._additem(text, bitmap, callback, id = id)
  224.  
  225.     
  226.     def AddCheckItem(self, text, callback = None):
  227.         return self._additem(text, callback = callback, kind = ITEM_CHECK)
  228.  
  229.     
  230.     def AddRadioItem(self, text, callback = None):
  231.         return self._additem(text, callback = callback, kind = ITEM_RADIO)
  232.  
  233.     
  234.     def AddPrefCheck(self, pref, text, help = '', updatenow = True):
  235.         profile = profile
  236.         import common
  237.         prefs = profile.prefs
  238.         
  239.         def callback():
  240.             prefs[pref] = not prefs[pref]
  241.  
  242.         item = self._additem(text, callback = callback, kind = ITEM_CHECK)
  243.         prefs.link((pref,), (lambda val: item.Check(val)), obj = self)
  244.         return item
  245.  
  246.     
  247.     def AppendLazyMenu(self, name, callback, bitmap = None):
  248.         if not callable(callback):
  249.             raise TypeError, repr(callback)
  250.         
  251.         menu = UMenu(self.Window)
  252.         return self.AddSubMenu(menu, name, bitmap = bitmap, onshow = (lambda menu = (menu,): callback(menu)))
  253.  
  254.     
  255.     def _additem(self, text, bitmap = None, callback = None, kind = ITEM_NORMAL, id = -1, position = None):
  256.         item = MenuItem(self, id, text, kind = kind)
  257.         id = item.Id
  258.         if bitmap is not None:
  259.             self.SetItemBitmap(item, bitmap)
  260.         
  261.         if callback is not None:
  262.             self.SetCallback(id, callback)
  263.         
  264.         if position is None:
  265.             return self.AppendItem(item)
  266.         else:
  267.             return self.InsertItem(position, item)
  268.  
  269.     
  270.     def SetCallback(self, id, callback):
  271.         
  272.         callback = lambda cb = (callback,): self._refresh_callback(cb)
  273.         self.cbs[id] = callback
  274.         self.Handler.AddCallback(id, callback)
  275.  
  276.     
  277.     def _refresh_callback(self, cb):
  278.         m = self._parentmenu
  279.         if m is None:
  280.             m = self
  281.         else:
  282.             while m._parentmenu:
  283.                 m = m._parentmenu
  284.         if hasattr(m, '_button'):
  285.             if wx.IsDestroyed(m._button):
  286.                 del m._button
  287.             else:
  288.                 m._button.Refresh()
  289.                 m._button.Update()
  290.         
  291.         self.Window.Refresh()
  292.         self.Window.Update()
  293.         return cb()
  294.  
  295.     
  296.     def AddSubMenu(self, submenu, label, bitmap = None, onshow = None):
  297.         submenu._parentmenu = self
  298.         if onshow is not None:
  299.             self.Handler.AddShowCallback(submenu.Id, onshow)
  300.         
  301.         item = self.AppendSubMenu(submenu, label)
  302.         if bitmap is not None:
  303.             self.SetItemBitmap(item, bitmap)
  304.         
  305.         return item
  306.  
  307.     
  308.     def SetItemBitmap(self, item, bitmap):
  309.         if self.skin.native and bitmap.Ok():
  310.             bitmap = bitmap.ResizedSmaller(16)
  311.         
  312.         item.SetBitmap(bitmap)
  313.  
  314.     
  315.     def AddSep(self):
  316.         return self.AppendItem(MenuItem(self))
  317.  
  318.     
  319.     def AddSepAt(self, i):
  320.         return self.InsertItem(i, MenuItem(self))
  321.  
  322.     
  323.     def RemoveItems(self, items):
  324.         return [ self.RemoveItem(item) for item in items ]
  325.  
  326.     
  327.     def RemoveAll(self):
  328.         return self.RemoveItems(list(self))
  329.  
  330.     
  331.     def DestroyAll(self):
  332.         for item in self.RemoveAll():
  333.             item.Destroy()
  334.         
  335.  
  336.     
  337.     def GetItemById(self, id):
  338.         for i, myitem in enumerate(self):
  339.             if myitem.Id == id:
  340.                 return myitem
  341.                 continue
  342.         
  343.  
  344.     
  345.     def IndexOf(self, item):
  346.         id = item.Id
  347.         for i, myitem in enumerate(self):
  348.             if myitem.Id == id:
  349.                 return i
  350.                 continue
  351.         
  352.         return -1
  353.  
  354.     
  355.     def Break(self):
  356.         raise NotImplementedError('skinned menus cannot break')
  357.  
  358.     
  359.     def Top(self):
  360.         w = self.Window
  361.         while not isinstance(w, wx.TopLevelWindow):
  362.             
  363.             try:
  364.                 w = getattr(w, 'Window', getattr(w, 'ParentWindow', w.Parent))
  365.             continue
  366.             except AttributeError:
  367.                 print '***', w, '***'
  368.                 raise 
  369.                 continue
  370.             
  371.  
  372.             None<EXCEPTION MATCH>AttributeError
  373.         return w
  374.  
  375.     Top = property(Top)
  376.     
  377.     def Handler(self):
  378.         return menuEventHandler(self.Top)
  379.  
  380.     Handler = property(Handler)
  381.     
  382.     def _activate_item(self, item):
  383.         return self.popup.vlist._activate_item(item)
  384.  
  385.     
  386.     def __iter__(self):
  387.         return iter(self.GetMenuItems())
  388.  
  389.     
  390.     def __getitem__(self, n):
  391.         return self.GetMenuItems()[n % len(self)]
  392.  
  393.     
  394.     def __len__(self):
  395.         return self.GetMenuItemCount()
  396.  
  397.     
  398.     def __contains__(self, item):
  399.         return (any,)((lambda .0: for i in .0:
  400. i.Id == item.Id)(self))
  401.  
  402.     
  403.     def __repr__(self):
  404.         return '<%s %r>' % (self.__class__.__name__, self.Title)
  405.  
  406.  
  407.  
  408. class UMenuBar(wx.MenuBar):
  409.     
  410.     def __init__(self, parent, skinkey = 'MenuBar'):
  411.         wx.MenuBar.__init__(self)
  412.         self.toptitles = { }
  413.         self.skinkey = skinkey
  414.         self.accelremoves = Delegate()
  415.         self.panel = SkinnedPanel(parent, 'MenuBar')
  416.         self.panel.UpdateSkin = self.UpdateSkin
  417.         self.panel.Hide()
  418.         self.UpdateSkin()
  419.  
  420.     
  421.     def UpdateSkin(self):
  422.         s = self.skin = skin.get(self.skinkey)
  423.         if not s.get('mode', 'skin').lower() == 'native':
  424.             pass
  425.         self.native = 'wxMac' in wx.PlatformInfo
  426.         if not hasattr(self, 'panel'):
  427.             return None
  428.         
  429.         if self.native:
  430.             for child in self.panel.Children:
  431.                 traceguard.__enter__()
  432.                 
  433.                 try:
  434.                     child.Destroy()
  435.                 finally:
  436.                     pass
  437.  
  438.             
  439.             self.panel.Hide()
  440.         else:
  441.             self._constructSkinElements()
  442.             win = self.ParentWindow.Top
  443.             if win.MenuBar is self:
  444.                 win.SetMenuBar(None)
  445.             
  446.             self.panel.Sizer.Layout()
  447.             self.panel.Show(self.IsShown())
  448.  
  449.     
  450.     def Append(self, menu, title, onshow = None):
  451.         self.toptitles[menu] = title
  452.         i = wx.MenuBar.Append(self, menu, title)
  453.         if not self.native:
  454.             self._constructSkinElements()
  455.         
  456.         if onshow is not None:
  457.             self.Handler.AddShowCallback(menu.Id, (lambda menu = (menu,): onshow(menu)))
  458.         
  459.         return i
  460.  
  461.     
  462.     def _constructSkinElements(self):
  463.         s = self.skin
  464.         p = self.panel
  465.         p.bg = s.get('background', SkinColor(wx.WHITE))
  466.         pad = p.padding = s.get('padding', wx.Point(0, 0))
  467.         for child in list(p.Children):
  468.             child.Hide()
  469.             child.Destroy()
  470.         
  471.         v = wx.BoxSizer(wx.VERTICAL)
  472.         h = wx.BoxSizer(wx.HORIZONTAL)
  473.         v.Add(h, 1, wx.EXPAND | wx.TOP | wx.BOTTOM, pad.y)
  474.         p.Sizer = s.get('margins', Margins()).Sizer(v)
  475.         self.buttons = []
  476.         addb = self.buttons.append
  477.         menus = self.Menus
  478.         nummenus = len(self.Menus)
  479.         for menu, label in enumerate(menus):
  480.             del menu.OnDismiss[:]
  481.             label = self.toptitles.get(menu, label)
  482.             button = UberButton(p, -1, skin = s.itemskin, label = label, type = 'menu', menu = menu, menubarmode = True)
  483.             addb(button)
  484.             menu._next = menus[(i + 1) % nummenus][0]
  485.             menu._prev = menus[i - 1][0]
  486.             menu._button = button
  487.         
  488.         (h.AddMany,)((lambda .0: for b in .0:
  489. (b, 0, wx.EXPAND | wx.LEFT, pad.x))(self.buttons))
  490.         self._bindAccelerators()
  491.  
  492.     
  493.     def Show(self, val):
  494.         native = self.native
  495.         if native:
  496.             win = self.ParentWindow.Top
  497.             self.panel.Hide()
  498.             if val and win.MenuBar is not self:
  499.                 win.MenuBar = self
  500.             elif not val and win.MenuBar is self:
  501.                 win.MenuBar = None
  502.             
  503.         else:
  504.             self.panel.Show(val)
  505.  
  506.     
  507.     def _bindAccelerators(self):
  508.         accelrems = self.accelremoves
  509.         parentproc = self.ParentWindow.ProcessEvent
  510.         keybind = self.KeyCatcher.OnDown
  511.         accelrems()
  512.         del accelrems[:]
  513.         for menu in self:
  514.             for item in menu:
  515.                 accel = GetAccelText(item)
  516.                 if accel:
  517.                     
  518.                     cb = lambda e, item = item, menu = (menu,): parentproc(menuevt(item))
  519.                     accelrems.append(keybind(accel, cb))
  520.                     continue
  521.             
  522.         
  523.  
  524.     
  525.     def KeyCatcher(self):
  526.         
  527.         try:
  528.             return self.ParentWindow._keycatcher
  529.         except AttributeError:
  530.             k = self.ParentWindow._keycatcher = KeyCatcher(self.ParentWindow)
  531.             return k
  532.  
  533.  
  534.     KeyCatcher = property(KeyCatcher)
  535.     
  536.     def __len__(self):
  537.         return self.GetMenuCount()
  538.  
  539.     
  540.     def __iter__(self):
  541.         return []([ self.GetMenu(i) for i in xrange(self.GetMenuCount()) ])
  542.  
  543.     
  544.     def SizableWindow(self):
  545.         return self.panel
  546.  
  547.     SizableWindow = property(SizableWindow)
  548.     
  549.     def ParentWindow(self):
  550.         return self.panel.Parent
  551.  
  552.     ParentWindow = property(ParentWindow)
  553.     
  554.     def Handler(self):
  555.         return menuEventHandler(self.panel.Top)
  556.  
  557.     Handler = property(Handler)
  558.  
  559. registered_activate_app = False
  560.  
  561. def _activateapp(e):
  562.     vlist = MenuListBox._lastvlist()
  563.     if vlist is not None and not wx.IsDestroyed(vlist) and not wx.IsDestroyed(vlist.menu):
  564.         if vlist.menu and vlist.menu.Windowless and not e.GetActive():
  565.             vlist.DismissRoot()
  566.         
  567.     
  568.  
  569.  
  570. class MenuListBox(SkinVListBox):
  571.     
  572.     def __init__(self, parent, menu):
  573.         global registered_activate_app
  574.         SkinVListBox.__init__(self, parent, style = wx.NO_BORDER | wx.FULL_REPAINT_ON_RESIZE | wx.WANTS_CHARS)
  575.         self.menu = menu
  576.         self.UpdateSkin()
  577.         self.timer = wx.PyTimer(self._on_submenu_timer)
  578.         Bind = self.Bind
  579.         Bind(wx.EVT_MOUSE_EVENTS, self._mouseevents)
  580.         Bind(wx.EVT_MOUSE_CAPTURE_CHANGED, self._capturechanged)
  581.         Bind(wx.EVT_LISTBOX, self._listbox)
  582.         Bind(wx.EVT_KEY_DOWN, self._keydown)
  583.         self.mouseCallbacks = {
  584.             wx.wxEVT_MOTION: self._motion,
  585.             wx.wxEVT_RIGHT_DOWN: self._rdown,
  586.             wx.wxEVT_LEFT_DOWN: self._ldown,
  587.             wx.wxEVT_LEFT_UP: self._lup }
  588.         MenuListBox._lastvlist = ref(self)
  589.         if not registered_activate_app:
  590.             wx.GetApp().Bind(wx.EVT_ACTIVATE_APP, _activateapp)
  591.             registered_activate_app = True
  592.         
  593.  
  594.     
  595.     def reassign(self, menu):
  596.         self.menu = menu
  597.  
  598.     menuOpenDelayMs = prefprop('menus.submenu_delay', 250)
  599.     
  600.     def __repr__(self):
  601.         return '<%s for %r>' % (self.__class__.__name__, self.menu)
  602.  
  603.     
  604.     def ParentPopup(self):
  605.         pmenu = self.menu._parentmenu
  606.         return None if pmenu is None else pmenu.popup.vlist
  607.  
  608.     ParentPopup = property(ParentPopup)
  609.     
  610.     def CalcSize(self):
  611.         self.SetItemCount(len(self.menu))
  612.         height = 0
  613.         dc = wx.MemoryDC()
  614.         dc.Font = self.font
  615.         s = self.skin
  616.         padx = self.padding[0]
  617.         iconsize = s.iconsize
  618.         subw = s.submenuicon.Width
  619.         sepheight = s.separatorimage.Size.height
  620.         itemheight = self.itemheight
  621.         textExtent = dc.GetTextExtent
  622.         labelw = accelw = 0
  623.         for item in self.menu:
  624.             if item.Kind == ITEM_SEPARATOR:
  625.                 height += sepheight
  626.                 continue
  627.             height += itemheight
  628.             labelw = max(labelw, textExtent(item.Label)[0])
  629.             accelw = max(accelw, textExtent(item.AccelText)[0])
  630.         
  631.         self.accelColumnX = padx + iconsize + padx + padx + labelw + padx
  632.         width = self.accelColumnX + padx + max(accelw, subw) + padx
  633.         self.MinSize = self.Size = wx.Size(width, height)
  634.  
  635.     
  636.     def OnDrawItem(self, dc, rect, n):
  637.         item = self.menu[n]
  638.         kind = item.Kind
  639.         s = self.skin
  640.         iconsize = s.iconsize
  641.         submenuicon = s.submenuicon
  642.         padx = self.padding.x
  643.         selected = self.IsSelected(n)
  644.         drawbitmap = dc.DrawBitmap
  645.         drawlabel = dc.DrawLabel
  646.         if kind == ITEM_SEPARATOR:
  647.             s.separatorimage.Draw(dc, rect, n)
  648.         else:
  649.             dc.Font = self.font
  650.             if not item.IsEnabled():
  651.                 fg = 'disabled'
  652.             elif selected:
  653.                 fg = 'selection'
  654.             else:
  655.                 fg = 'normal'
  656.             dc.TextForeground = getattr(s.fontcolors, fg)
  657.             grect = Rect(*rect)
  658.             grect.width = padx + iconsize + padx
  659.             bmap = item.Bitmap
  660.             if bmap and bmap.Ok():
  661.                 bmap = bmap.ResizedSmaller(iconsize)
  662.                 drawbitmap(bmap, grect.HCenter(bmap), rect.VCenter(bmap), True)
  663.             
  664.             if item.IsCheckable() and item.IsChecked():
  665.                 if bmap:
  666.                     checkx = grect.Right - s.checkedicon.Width
  667.                 else:
  668.                     checkx = grect.HCenter(s.checkedicon)
  669.                 if kind == ITEM_CHECK:
  670.                     drawbitmap(s.checkedicon, checkx, rect.VCenter(s.checkedicon), True)
  671.                 elif kind == ITEM_RADIO:
  672.                     drawbitmap(s.checkedicon, checkx, rect.VCenter(s.checkedicon), True)
  673.                 
  674.             
  675.             rect.Subtract(left = iconsize + 3 * padx)
  676.             drawlabel(item.Label, rect, indexAccel = item.Text.split('\t')[0].find('&'), alignment = ALIGN_CENTER_VERTICAL)
  677.             rect.Subtract(right = submenuicon.Width + padx)
  678.             if item.SubMenu is not None:
  679.                 drawbitmap(submenuicon, rect.Right, rect.VCenter(submenuicon), True)
  680.             
  681.             acceltext = item.AccelText
  682.             if acceltext:
  683.                 rect.x = self.accelColumnX + padx
  684.                 drawlabel(acceltext, rect, alignment = ALIGN_CENTER_VERTICAL)
  685.             
  686.  
  687.     
  688.     def OnDrawBackground(self, dc, rect, n):
  689.         s = self.skin
  690.         bgs = s.backgrounds
  691.         bgname = None if self.menu[n].Kind != ITEM_SEPARATOR and self.IsSelected(n) else 'item'
  692.         bg = getattr(bgs, bgname, None)
  693.         if bg:
  694.             bg.Draw(dc, rect, n)
  695.         
  696.  
  697.     
  698.     def PaintMoreBackground(self, dc, rect):
  699.         g = self.skin.backgrounds.gutter
  700.         if g:
  701.             g.Draw(dc, Rect(rect.x, rect.y, self.skin.iconsize + self.padding.x * 2, rect.height))
  702.         
  703.  
  704.     
  705.     def OnMeasureItem(self, n):
  706.         item = self.menu[n]
  707.         kind = item.Kind
  708.         if kind == ITEM_SEPARATOR:
  709.             return self.sepheight
  710.         else:
  711.             return self.itemheight
  712.  
  713.     
  714.     def OnPopup(self):
  715.         parentless = self.TopMenu == self.menu
  716.         if parentless:
  717.             if wxMSW:
  718.                 ReleaseCapture_win32()
  719.             
  720.             if self.menu.Windowless:
  721.                 if not hasattr(self, 'traytimer'):
  722.                     self.traytimer = UMenuTrayTimer(self)
  723.                 
  724.                 self.traytimer.Start(50)
  725.             
  726.         
  727.         if not self.menu._parentmenu:
  728.             self._grabkeyboard()
  729.         
  730.         if wx.LeftDown():
  731.             self._leftbuttondown = True
  732.         
  733.         if not self.HasCapture():
  734.             self.CaptureMouse()
  735.         
  736.         self.SetCursor(StockCursor(CURSOR_DEFAULT))
  737.         self.SetFocus()
  738.  
  739.     
  740.     def Dismiss(self):
  741.         if hasattr(self, 'traytimer'):
  742.             self.traytimer.Stop()
  743.         
  744.         if self.menu._childmenu:
  745.             self.menu._childmenu.Dismiss()
  746.             self.menu._childmenu = None
  747.         
  748.         while self.HasCapture():
  749.             self.ReleaseMouse()
  750.         self.Parent.Hide()
  751.         m = self.menu
  752.         if m._parentmenu is None:
  753.             if hasattr(self, 'focusHandler'):
  754.                 self.focusHandler.close()
  755.                 del self.focusHandler
  756.             
  757.             wx.CallAfter(self.menu.OnDismiss)
  758.         else:
  759.             m._parentmenu._childmenu = None
  760.  
  761.     
  762.     def DismissRoot(self):
  763.         self.TopMenu.Dismiss()
  764.  
  765.     
  766.     def TopMenu(self):
  767.         m = self.menu
  768.         while m._parentmenu is not None:
  769.             m = m._parentmenu
  770.         return m
  771.  
  772.     TopMenu = property(TopMenu)
  773.     
  774.     def UpdateSkin(self):
  775.         self.SetMargins(wx.Point(0, 0))
  776.         s = self.skin = self.menu.skin
  777.         self.sepheight = s.separatorimage.Size.height
  778.         
  779.         try:
  780.             self.font = s.font
  781.         except KeyError:
  782.             self.font = default_font()
  783.  
  784.         self.fontheight = s.font.Height
  785.         
  786.         try:
  787.             self.padding = s.padding
  788.         except Exception:
  789.             self.padding = wx.Point(3, 3)
  790.  
  791.         self.itemheight = int(self.fontheight + self.padding.y * 2)
  792.         self.Background = s.backgrounds.menu
  793.  
  794.     
  795.     def Window(self):
  796.         return self.menu.Window
  797.  
  798.     Window = property(Window)
  799.     
  800.     def _grabkeyboard(self):
  801.         if 'wxMSW' in wx.PlatformInfo:
  802.             self._focusWin = wx.Window.FindFocus()
  803.         elif 'wxGTK' in wx.Platform:
  804.             self._focusWin = self
  805.         else:
  806.             self._focusWin = None
  807.         f = self._focusWin
  808.         if f:
  809.             self.focusHandler = FocusHandler(self, f)
  810.         
  811.  
  812.     
  813.     def _showsubmenu(self, i, highlight = False):
  814.         item = self.menu[i]
  815.         submenu = item.SubMenu
  816.         child = self.menu._childmenu
  817.         if child is submenu:
  818.             return None
  819.         
  820.         if child is not None:
  821.             if child:
  822.                 child.Dismiss()
  823.             
  824.             self.menu._childmenu = None
  825.         
  826.         if i != -1 and submenu is not None:
  827.             r = self.ClientRect
  828.             r.Y = self.GetItemY(i)
  829.             r.Height = self.OnMeasureItem(i)
  830.             self.menu._childmenu = submenu
  831.             submenu._parentmenu = self.menu
  832.             submenu._parentindex = i
  833.             submenu.PopupMenu(r.ToScreen(self), submenu = True)
  834.             if highlight:
  835.                 submenu.popup.vlist.Selection = 0
  836.             
  837.         
  838.  
  839.     
  840.     def _on_submenu_timer(self):
  841.         i = self.Selection
  842.         if i != -1 and self.IsShown() and FindWindowAtPointer() is self:
  843.             self._showsubmenu(i)
  844.         
  845.  
  846.     
  847.     def _listbox(self, e):
  848.         self.timer.Start(self.menuOpenDelayMs, True)
  849.  
  850.     
  851.     def _emit_menuevent(self, id, type = wx.wxEVT_COMMAND_MENU_SELECTED):
  852.         event = wx.CommandEvent(type, id)
  853.         self.menu.Handler.AddPendingEvent(event)
  854.  
  855.     
  856.     def _mouseevents(self, e, wxEVT_MOTION = wxEVT_MOTION, FindWindowAtPoint = FindWindowAtPoint, UberButton = UberButton):
  857.         rect = self.ClientRect
  858.         pt_original = pt = e.Position
  859.         if not rect.Contains(pt):
  860.             menu = self.ParentPopup
  861.             oldmenu = self
  862.             while menu:
  863.                 pt = menu.ScreenToClient(oldmenu.ClientToScreen(pt))
  864.                 if menu.ClientRect.Contains(pt):
  865.                     (e.m_x, e.m_y) = pt
  866.                     return menu._mouseevents(e)
  867.                 
  868.                 oldmenu = menu
  869.                 menu = menu.ParentPopup
  870.             
  871.             try:
  872.                 button = self.TopMenu._button
  873.             except AttributeError:
  874.                 pass
  875.  
  876.             if e.GetEventType() == wxEVT_MOTION:
  877.                 ctrl = FindWindowAtPoint(self.ClientToScreen(e.Position))
  878.                 if ctrl is not None:
  879.                     if getattr(self, '_motionswitch', -1) is ctrl:
  880.                         self._motionswitch = None
  881.                         self.DismissRoot()
  882.                         ctrl.menu._showquick = True
  883.                         ctrl.OnLeftDown()
  884.                     elif isinstance(ctrl, UberButton) and ctrl.menubarmode and hasattr(ctrl, 'menu'):
  885.                         if ctrl.Parent is button.Parent and ctrl is not button:
  886.                             self._motionswitch = ctrl
  887.                         
  888.                     
  889.                 
  890.             
  891.         
  892.         (e.m_x, e.m_y) = pt_original
  893.         
  894.         try:
  895.             cb = self.mouseCallbacks[e.EventType]
  896.         except KeyError:
  897.             pass
  898.  
  899.         cb(e)
  900.  
  901.     
  902.     def _motion(self, e):
  903.         p = e.Position
  904.         i = None if self.ClientRect.Contains(p) else -1
  905.         s = self.Selection
  906.         if i != s:
  907.             p = self.ParentPopup
  908.             if p is not None:
  909.                 pi = getattr(self.menu, '_parentindex', None)
  910.                 if pi is not None and p.Selection != pi:
  911.                     p.Selection = pi
  912.                 
  913.             
  914.             self.SetSelection(i)
  915.             self._emit_lbox_selection(i)
  916.         
  917.  
  918.     
  919.     def LeafMenu(self):
  920.         s = self.menu
  921.         while s._childmenu:
  922.             s = s._childmenu
  923.         return s
  924.  
  925.     LeafMenu = property(LeafMenu)
  926.     
  927.     def _keydown(self, e):
  928.         self = self.LeafMenu.popup.vlist
  929.         code = e.KeyCode
  930.         i = self.Selection
  931.         j = -1
  932.         m = self.menu
  933.         if code == wx.WXK_DOWN:
  934.             j = (i + 1) % len(m)
  935.             while j != i and m[j].Kind == wx.ITEM_SEPARATOR:
  936.                 j = (j + 1) % len(m)
  937.         elif code == wx.WXK_UP:
  938.             if i == -1:
  939.                 i = len(m)
  940.             
  941.             j = (i - 1) % len(m)
  942.             while j != i and m[j].Kind == wx.ITEM_SEPARATOR:
  943.                 j = (j - 1) % len(m)
  944.         elif code == wx.WXK_RETURN:
  945.             return self._activate_item(i, submenu = True, highlight = True)
  946.         elif code == wx.WXK_RIGHT:
  947.             if i == -1:
  948.                 pass
  949.             elif m[i].SubMenu is not None:
  950.                 self.timer.Stop()
  951.                 return self._showsubmenu(i, highlight = True)
  952.             
  953.             while m._parentmenu:
  954.                 m = m._parentmenu
  955.             next = getattr(m, '_next', None)
  956.             if next is not None:
  957.                 wx.CallAfter(self.DismissRoot)
  958.                 next._showquick = True
  959.                 wx.CallAfter(next._button.OnLeftDown)
  960.             
  961.         elif code == wx.WXK_ESCAPE:
  962.             self.Dismiss()
  963.         elif code == wx.WXK_LEFT:
  964.             if m._parentmenu:
  965.                 self.Dismiss()
  966.             else:
  967.                 prev = getattr(self.menu, '_prev', None)
  968.                 if prev is not None:
  969.                     wx.CallAfter(self.DismissRoot)
  970.                     prev._showquick = True
  971.                     wx.CallAfter(prev._button.OnLeftDown)
  972.                 
  973.         elif code < 256:
  974.             self._on_char(unichr(e.UnicodeKey))
  975.         
  976.         if j != -1:
  977.             self.SetSelection(j)
  978.         
  979.  
  980.     
  981.     def _on_char(self, char):
  982.         char = char.lower()
  983.         items = []
  984.         for item in self.menu:
  985.             amp_char = GetAmpChar(item)
  986.             if amp_char is not None and char == amp_char.lower():
  987.                 return self._activate_item(item, submenu = True, highlight = True)
  988.                 continue
  989.         
  990.         items = []
  991.         for i in rotated(range(0, len(self.menu)), -(self.Selection) - 1):
  992.             item = self.menu[i]
  993.             label_text = item.GetItemLabelText()
  994.             if label_text and label_text[0].lower() == char:
  995.                 items.append(i)
  996.                 continue
  997.         
  998.         if len(items) == 1:
  999.             self._activate_item(items[0], submenu = True, highlight = True)
  1000.         elif len(items) > 1:
  1001.             self.SetSelection(items[0])
  1002.         
  1003.  
  1004.     
  1005.     def _rdown(self, e):
  1006.         p = e.Position
  1007.         rect = self.ClientRect
  1008.         if not rect.Contains(p):
  1009.             return self.DismissRoot()
  1010.         
  1011.  
  1012.     
  1013.     def _ldown(self, e):
  1014.         p = e.Position
  1015.         rect = self.ClientRect
  1016.         if not rect.Contains(p):
  1017.             return self.DismissRoot()
  1018.         
  1019.         i = self.HitTest(p)
  1020.         if i != -1:
  1021.             if self.menu[i].SubMenu is not None:
  1022.                 self.timer.Stop()
  1023.                 return self._showsubmenu(i)
  1024.             
  1025.         
  1026.  
  1027.     
  1028.     def _lup(self, e):
  1029.         p = e.Position
  1030.         i = self.HitTest(e.Position)
  1031.         if self.ClientRect.Contains(p):
  1032.             self._activate_item(i)
  1033.         else:
  1034.             ctrl = FindWindowAtPointer()
  1035.             if not isinstance(ctrl, UberButton) or not (ctrl.type == 'menu'):
  1036.                 self.DismissRoot()
  1037.             
  1038.  
  1039.     
  1040.     def _activate_item(self, i, submenu = False, highlight = False):
  1041.         if isinstance(i, int):
  1042.             if i == -1:
  1043.                 return None
  1044.             
  1045.             item = self.menu[i]
  1046.         else:
  1047.             item = i
  1048.             i = self.menu.IndexOf(item)
  1049.         if submenu:
  1050.             if item.SubMenu is not None:
  1051.                 self.timer.Stop()
  1052.                 if not self.Selection == i:
  1053.                     self.SetSelection(i)
  1054.                 
  1055.                 return self._showsubmenu(i, highlight = highlight)
  1056.             
  1057.         
  1058.         if item.Kind != ITEM_SEPARATOR and item.IsEnabled() and item.SubMenu is None:
  1059.             if item.IsCheckable():
  1060.                 item.Check(not item.IsChecked())
  1061.             
  1062.             self._emit_menuevent(item.Id)
  1063.             self.DismissRoot()
  1064.         
  1065.  
  1066.     
  1067.     def _capturechanged(self, e):
  1068.         
  1069.         def active():
  1070.             
  1071.             try:
  1072.                 if self.menu.Windowless or not hasattr(self.menu.Window.Top, 'IsActive'):
  1073.                     return True
  1074.                 else:
  1075.                     return self.menu.Window.Top.IsActive()
  1076.             except Exception:
  1077.                 print_exc()
  1078.                 return True
  1079.  
  1080.  
  1081.         if not active():
  1082.             (wx.CallAfter,)((lambda : self.DismissRoot()))
  1083.         
  1084.  
  1085.  
  1086.  
  1087. class MenuWindowBase(object):
  1088.     
  1089.     def __init__(self, parent, menu):
  1090.         self.vlist = MenuListBox(self, menu)
  1091.         self.UpdateSkin()
  1092.         self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
  1093.         self.Bind(wx.EVT_PAINT, self._paint)
  1094.  
  1095.     
  1096.     def reassign(self, parent, menu):
  1097.         self.Reparent(parent)
  1098.         self.vlist.reassign(menu)
  1099.  
  1100.     
  1101.     def _paint(self, e):
  1102.         dc = wx.BufferedPaintDC(self)
  1103.         self.bg.Draw(dc, self.ClientRect)
  1104.  
  1105.     
  1106.     def UpdateSkin(self):
  1107.         s = self.skin = self.vlist.menu.skin
  1108.         if self.Sizer and not wx.IsDestroyed(self.Sizer):
  1109.             self.Sizer.Clear()
  1110.         
  1111.         self.framesize = s.get('framesize', skin.ZeroMargins)
  1112.         self.Sizer = self.framesize.Sizer(self.vlist)
  1113.         self.bg = s.frame
  1114.  
  1115.     
  1116.     def PopupMenu(self, pos = None, submenu = False):
  1117.         v = self.vlist
  1118.         v.menu.Handler._menu_open(menu = v.menu)
  1119.         v.SetSelection(-1)
  1120.         v.CalcSize()
  1121.         self.Fit()
  1122.         self.Sizer.Layout()
  1123.         if isinstance(self.bg, SplitImage4):
  1124.             self.Cut(self.bg.GetBitmap(self.Size))
  1125.         else:
  1126.             self.Cut()
  1127.         pos = None if pos is None else pos
  1128.         
  1129.         try:
  1130.             
  1131.             try:
  1132.                 disp = Monitor.GetFromPoint(pos[:2]).Geometry
  1133.             except Exception:
  1134.                 disp = Monitor.GetFromPoint(pos.BottomRight).Geometry
  1135.  
  1136.         except Exception:
  1137.             print_exc()
  1138.             log.critical('could not find display for %s, falling back to zero', pos)
  1139.             disp = Monitor.All()[0].Geometry
  1140.  
  1141.         size = self.Size
  1142.         rects = []
  1143.         
  1144.         add = lambda *seq: []([ RectPS(p, size) for p in seq ])
  1145.         singlepoint = len(pos) == 2
  1146.         offset = (None, None) if singlepoint else 0
  1147.         if singlepoint:
  1148.             pos = wx.RectPS(pos, (0, 0))
  1149.         
  1150.         w = Point(size.width - offset, 0)
  1151.         h = Point(0, size.height - offset)
  1152.         wh = Point(size.width - offset, size.height - offset)
  1153.         difftop = Point(0, self.framesize.top)
  1154.         diffbottom = Point(0, self.framesize.bottom)
  1155.         if submenu:
  1156.             add(pos.TopRight - difftop, pos.TopLeft - w - difftop, (pos.BottomRight - h) + diffbottom, (pos.BottomLeft - wh) + diffbottom)
  1157.         else:
  1158.             add(pos.BottomLeft, pos.TopLeft - h, pos.BottomRight - w, pos.TopRight - h, pos.TopRight - wh, pos.BottomLeft - h)
  1159.         for rect in rects:
  1160.             if disp.ContainsRect(rect):
  1161.                 self._showat(rect)
  1162.                 return None
  1163.                 continue
  1164.         
  1165.         rect = rects[0]
  1166.         if hasattr(v.menu, '_button'):
  1167.             brect = v.menu._button.ScreenRect
  1168.             if rect.Intersects(brect):
  1169.                 rect.Offset((brect.Width, 0))
  1170.             
  1171.         
  1172.         self._showat(rect)
  1173.  
  1174.     
  1175.     def _showat(self, rect, nofade = False):
  1176.         self.SetRect(rect)
  1177.         self.EnsureInScreen(client_area = False)
  1178.         if nofade:
  1179.             self.Show()
  1180.         elif getattr(self.vlist.menu, '_showquick', False):
  1181.             self.vlist.menu._showquick = False
  1182.             self.Show()
  1183.         else:
  1184.             fadein(self, 'xfast')
  1185.         if wxMSW:
  1186.             CallLater((1,), (lambda : None if self else None))
  1187.         
  1188.         self.vlist.OnPopup()
  1189.  
  1190.  
  1191.  
  1192. class MenuPopupWindow(MenuWindowBase, wx.PopupWindow):
  1193.     
  1194.     def __init__(self, parent, menu):
  1195.         wx.PopupWindow.__init__(self, parent)
  1196.         MenuWindowBase.__init__(self, parent, menu)
  1197.  
  1198.  
  1199.  
  1200. class MenuFrameWindow(MenuWindowBase, wx.Frame):
  1201.     
  1202.     def __init__(self, parent, menu):
  1203.         wx.Frame.__init__(self, parent)
  1204.         MenuWindowBase.__init__(self, parent, menu)
  1205.  
  1206.  
  1207. from weakref import WeakValueDictionary
  1208.  
  1209. class MenuEventHandler(wx.EvtHandler):
  1210.     
  1211.     def __init__(self, parentFrame):
  1212.         wx.EvtHandler.__init__(self)
  1213.         parentFrame.PushEventHandler(self)
  1214.         self.Bind(wx.EVT_MENU, self._MenuEventHandler__menu)
  1215.         if not config.newMenubar:
  1216.             self.Bind(wx.EVT_MENU_OPEN, self._menu_open)
  1217.         
  1218.         if 'wxMSW' in wx.PlatformInfo:
  1219.             parentFrame.BindWin32(WM_INITMENUPOPUP, self._initmenupopup)
  1220.         
  1221.         self.cbs = WeakValueDictionary()
  1222.         self.showcbs = { }
  1223.         if wxMSW:
  1224.             self.hwndMap = WeakValueDictionary()
  1225.         
  1226.  
  1227.     
  1228.     def AddCallback(self, id, callback):
  1229.         self.cbs[id] = callback
  1230.  
  1231.     
  1232.     def AddShowCallback(self, id, callback):
  1233.         self.showcbs[id] = callback
  1234.  
  1235.     
  1236.     def __menu(self, e):
  1237.         id = e.Id
  1238.         
  1239.         try:
  1240.             cb = self.cbs[id]
  1241.         except KeyError:
  1242.             e.Skip()
  1243.  
  1244.         cb()
  1245.  
  1246.     
  1247.     def _menu_open(self, e = None, menu = None):
  1248.         if e is not None:
  1249.             e.Skip()
  1250.             menu = e.Menu
  1251.         
  1252.         if menu is None:
  1253.             return None
  1254.         
  1255.         
  1256.         try:
  1257.             cb = self.showcbs[menu.Id]
  1258.         except KeyError:
  1259.             pass
  1260.  
  1261.         cb()
  1262.  
  1263.     if wxMSW:
  1264.         
  1265.         def _initmenupopup(self, hWnd, msg, wParam, lParam):
  1266.             
  1267.             try:
  1268.                 menu = self.hwndMap[wParam]
  1269.             except KeyError:
  1270.                 return None
  1271.  
  1272.             if menu._parentmenu:
  1273.                 evt = PyCommandEvent(wxEVT_MENU_OPEN, menu.Id)
  1274.                 evt.Menu = menu
  1275.                 menu.Handler.ProcessEvent(evt)
  1276.             
  1277.  
  1278.     
  1279.  
  1280.  
  1281. class FocusHandler(wx.EvtHandler):
  1282.     
  1283.     def __init__(self, menu, ctrl):
  1284.         wx.EvtHandler.__init__(self)
  1285.         self._menu = menu
  1286.         self._ctrl = ctrl
  1287.         self.Bind(wx.EVT_KEY_DOWN, self._menu._keydown)
  1288.         self.Bind(wx.EVT_NAVIGATION_KEY, self._menu._keydown)
  1289.         self.wantschars = bool(ctrl.WindowStyleFlag & wx.WANTS_CHARS)
  1290.         if not self.wantschars:
  1291.             ctrl.SetWindowStyleFlag(ctrl.WindowStyleFlag | wx.WANTS_CHARS)
  1292.         
  1293.         ctrl.PushEventHandler(self)
  1294.  
  1295.     
  1296.     def close(self):
  1297.         ctrl = self._ctrl
  1298.         ctrl.RemoveEventHandler(self)
  1299.         f = ctrl.WindowStyleFlag
  1300.         if not self.wantschars:
  1301.             f = f & ~(wx.WANTS_CHARS)
  1302.             ctrl.SetWindowStyleFlag(f)
  1303.         
  1304.  
  1305.     
  1306.     def SetMenu(self, menu):
  1307.         self._menu = menu
  1308.  
  1309.     
  1310.     def OnKeyDown(self, event):
  1311.         self._menu.OnKeyDown(event)
  1312.  
  1313.  
  1314.  
  1315. def menuEventHandler(f):
  1316.     
  1317.     try:
  1318.         return f._menuevthandler
  1319.     except AttributeError:
  1320.         h = f._menuevthandler = MenuEventHandler(f)
  1321.  
  1322.     return h
  1323.  
  1324. from gui.toolbox.keynames import keynames, modifiernames
  1325.  
  1326. def menuevt(item):
  1327.     return wx.CommandEvent(wx.wxEVT_COMMAND_MENU_SELECTED, item.Id)
  1328.  
  1329.  
  1330. def GetAccelText(item):
  1331.     a = item.Accel
  1332.     return None if not a else _getacceltext(a.Flags, a.KeyCode)
  1333.  
  1334. if not hasattr(wx, '_MenuItem'):
  1335.     wx._MenuItem = wx.MenuItem
  1336.  
  1337. wx._MenuItem.AccelText = property(GetAccelText)
  1338.  
  1339. def GetAmpChar(item):
  1340.     text = item.Text
  1341.     amp_index = text.find('&')
  1342.     if amp_index != -1 and amp_index < len(text) - 1:
  1343.         return text[amp_index + 1]
  1344.     
  1345.  
  1346.  
  1347. def _getacceltext(modifiers, key, joinstr = '+'):
  1348.     return [](_[1] + [
  1349.         keynames.get(key, chr(key).upper())])
  1350.  
  1351. _getacceltext = memoize(_getacceltext)
  1352. from collections import deque
  1353.  
  1354. def rotated(iter, n):
  1355.     d = deque(iter)
  1356.     d.rotate(n)
  1357.     return d
  1358.  
  1359.