| 1 |
#!/usr/bin/env python |
|---|
| 2 |
# -*- coding: utf-8 -*- |
|---|
| 3 |
import os |
|---|
| 4 |
import dabo |
|---|
| 5 |
dabo.ui.loadUI("wx") |
|---|
| 6 |
from dabo.dLocalize import _ |
|---|
| 7 |
import dabo.dEvents as dEvents |
|---|
| 8 |
from MenuDesignerComponents import CaptionPanel |
|---|
| 9 |
from MenuDesignerComponents import CaptionBitmapPanel |
|---|
| 10 |
from MenuDesignerComponents import SeparatorPanel |
|---|
| 11 |
from dabo.ui import makeDynamicProperty |
|---|
| 12 |
from dabo.ui import makeProxyProperty |
|---|
| 13 |
|
|---|
| 14 |
|
|---|
| 15 |
|
|---|
| 16 |
class MenuPanel(CaptionPanel): |
|---|
| 17 |
"""This class represents a menu.""" |
|---|
| 18 |
def afterInit(self): |
|---|
| 19 |
# Name for the saved mnxml file |
|---|
| 20 |
self._className = "MenuPanel" |
|---|
| 21 |
# Displayed name |
|---|
| 22 |
self._commonName = "Menu" |
|---|
| 23 |
|
|---|
| 24 |
class _ItemPanel(dabo.ui.dPanel): |
|---|
| 25 |
_className = "MenuItemPanel" |
|---|
| 26 |
def __init__(self, *args, **kwargs): |
|---|
| 27 |
# Name for the saved mnxml file |
|---|
| 28 |
self._controller = None |
|---|
| 29 |
super(_ItemPanel, self).__init__(*args, **kwargs) |
|---|
| 30 |
self.DynamicWidth = self._calcWidth |
|---|
| 31 |
|
|---|
| 32 |
def _calcWidth(self): |
|---|
| 33 |
kids = self.Children |
|---|
| 34 |
if not kids: |
|---|
| 35 |
return 0 |
|---|
| 36 |
else: |
|---|
| 37 |
wd = max([kid.Width for kid in kids]) |
|---|
| 38 |
dabo.ui.callAfter(self._resizeItems, wd) |
|---|
| 39 |
return wd |
|---|
| 40 |
|
|---|
| 41 |
def _resizeItems(self, wd): |
|---|
| 42 |
if not self: |
|---|
| 43 |
return |
|---|
| 44 |
for kid in self.Children: |
|---|
| 45 |
kid.Width = wd |
|---|
| 46 |
self.layout() |
|---|
| 47 |
self.refresh() |
|---|
| 48 |
|
|---|
| 49 |
def processContextMenu(self, obj, evt): |
|---|
| 50 |
self.Controller.processContextMenu(obj, evt) |
|---|
| 51 |
|
|---|
| 52 |
def _getController(self): |
|---|
| 53 |
return self._controller |
|---|
| 54 |
|
|---|
| 55 |
def _setController(self, val): |
|---|
| 56 |
self._controller = val |
|---|
| 57 |
|
|---|
| 58 |
Controller = property(_getController, _setController, None, |
|---|
| 59 |
_("Object to which this panel is associated (MenuPanel)")) |
|---|
| 60 |
|
|---|
| 61 |
|
|---|
| 62 |
# Add the panel that will hold the menu items. It needs to |
|---|
| 63 |
# be a child of the menu bar's parent, which would be this |
|---|
| 64 |
# control's grandparent. |
|---|
| 65 |
gp = self.Parent.Parent |
|---|
| 66 |
localPos = (self.Left, self.Bottom) |
|---|
| 67 |
formPos = self.Parent.formCoordinates(localPos) |
|---|
| 68 |
self.itemList = lst = _ItemPanel(gp, Controller=self, Position=formPos, |
|---|
| 69 |
Visible=True) |
|---|
| 70 |
|
|---|
| 71 |
sz = lst.Sizer = dabo.ui.dSizer("V") |
|---|
| 72 |
sz.DefaultBorder = 1 |
|---|
| 73 |
sz.DefaultBorderTop = True |
|---|
| 74 |
|
|---|
| 75 |
# Dict to hold additional item information |
|---|
| 76 |
self.itemDict = {} |
|---|
| 77 |
# Holds the active object during context menu actions |
|---|
| 78 |
self._contextObj = None |
|---|
| 79 |
|
|---|
| 80 |
|
|---|
| 81 |
def __del__(self): |
|---|
| 82 |
try: |
|---|
| 83 |
self.itemList.release() |
|---|
| 84 |
except: pass |
|---|
| 85 |
|
|---|
| 86 |
|
|---|
| 87 |
# def layout(self): |
|---|
| 88 |
# dabo.ui.callAfterInterval(100, self.itemList.layout, resetMin=True) |
|---|
| 89 |
# dabo.ui.callAfterInterval(100, self.itemList.fitToSizer) |
|---|
| 90 |
|
|---|
| 91 |
|
|---|
| 92 |
def append(self, caption, key=None, picture=None, help=None, |
|---|
| 93 |
separator=False): |
|---|
| 94 |
return self.insert(None, caption, key=None, picture=picture, |
|---|
| 95 |
help=help, separator=separator) |
|---|
| 96 |
|
|---|
| 97 |
|
|---|
| 98 |
def insert(self, pos, caption, key=None, picture=None, help=None, |
|---|
| 99 |
separator=False): |
|---|
| 100 |
if pos is None: |
|---|
| 101 |
# Called from append |
|---|
| 102 |
pos = len(self.itemList.Children) |
|---|
| 103 |
if separator: |
|---|
| 104 |
itm = SeparatorPanel(self.itemList, Controller=self, Visible=False) |
|---|
| 105 |
else: |
|---|
| 106 |
itm = CaptionBitmapPanel(self.itemList, Caption=caption, |
|---|
| 107 |
Controller=self, Visible=False) |
|---|
| 108 |
itm._className = "MenuItemPanel" |
|---|
| 109 |
itm._commonName = "Menu Item" |
|---|
| 110 |
itm.isMenuItem = True |
|---|
| 111 |
# Add the item to the dict |
|---|
| 112 |
self.itemDict[itm] = {"caption": caption, "key": key, "picture": picture} |
|---|
| 113 |
if picture: |
|---|
| 114 |
itm.Picture = picture |
|---|
| 115 |
|
|---|
| 116 |
itm.Visible = True |
|---|
| 117 |
self.itemList.Height += itm.Height |
|---|
| 118 |
self.itemList.Sizer.insert(pos, itm, "x") |
|---|
| 119 |
self.layout() |
|---|
| 120 |
self.Controller.updateLayout() |
|---|
| 121 |
return itm |
|---|
| 122 |
|
|---|
| 123 |
|
|---|
| 124 |
def appendSeparator(self): |
|---|
| 125 |
ret = self.append("", separator=True) |
|---|
| 126 |
self.layout() |
|---|
| 127 |
self.Controller.updateLayout() |
|---|
| 128 |
return ret |
|---|
| 129 |
|
|---|
| 130 |
|
|---|
| 131 |
def insertSeparator(self, pos): |
|---|
| 132 |
ret = self.insert(pos, "", separator=True) |
|---|
| 133 |
self.layout() |
|---|
| 134 |
self.Controller.updateLayout() |
|---|
| 135 |
return ret |
|---|
| 136 |
|
|---|
| 137 |
|
|---|
| 138 |
def processContextMenu(self, obj, evt): |
|---|
| 139 |
self._contextObj = obj |
|---|
| 140 |
pop = dabo.ui.dMenu() |
|---|
| 141 |
if not isinstance(obj, SeparatorPanel): |
|---|
| 142 |
pop.append("Edit", OnHit=self.onEdit) |
|---|
| 143 |
pop.appendSeparator() |
|---|
| 144 |
pop.append("Add Item Above", OnHit=self.onAddAbove) |
|---|
| 145 |
pop.append("Add Item Below", OnHit=self.onAddBelow) |
|---|
| 146 |
pop.append("Add Separator Above", OnHit=self.onAddAbove) |
|---|
| 147 |
pop.append("Add Separator Below", OnHit=self.onAddBelow) |
|---|
| 148 |
if obj.getPositionInSizer() != 0: |
|---|
| 149 |
pop.append("Move Up", OnHit=self.onMoveUp) |
|---|
| 150 |
if obj.getPositionInSizer() != len(obj.ControllingSizer.Children)-1: |
|---|
| 151 |
pop.append("Move Down", OnHit=self.onMoveDown) |
|---|
| 152 |
pop.appendSeparator() |
|---|
| 153 |
pop.append("Delete", OnHit=self.onDelete) |
|---|
| 154 |
self.showContextMenu(pop) |
|---|
| 155 |
|
|---|
| 156 |
|
|---|
| 157 |
def onEdit(self, evt): |
|---|
| 158 |
obj = self._contextObj |
|---|
| 159 |
txt = self.capText = dabo.ui.dTextBox(obj.Parent, Value=obj.Caption, |
|---|
| 160 |
Position=obj.Position, Size=obj.Size) |
|---|
| 161 |
txt.bindEvent(dEvents.LostFocus, self.onEndEdit) |
|---|
| 162 |
txt.bindEvent(dEvents.KeyChar, self.onTextCapChar) |
|---|
| 163 |
obj.hide() |
|---|
| 164 |
txt.setFocus() |
|---|
| 165 |
|
|---|
| 166 |
|
|---|
| 167 |
def onTextCapChar(self, evt): |
|---|
| 168 |
if evt.keyCode == 13: |
|---|
| 169 |
self.onEndEdit(evt) |
|---|
| 170 |
|
|---|
| 171 |
|
|---|
| 172 |
def onEndEdit(self, evt): |
|---|
| 173 |
cap = self.capText.Value |
|---|
| 174 |
self.capText.release() |
|---|
| 175 |
if cap: |
|---|
| 176 |
self._contextObj.Caption = cap |
|---|
| 177 |
self._contextObj.show() |
|---|
| 178 |
self._contextObj = None |
|---|
| 179 |
self.layout() |
|---|
| 180 |
self.Controller.updateLayout() |
|---|
| 181 |
|
|---|
| 182 |
|
|---|
| 183 |
def onAddAbove(self, evt): |
|---|
| 184 |
pos = self._contextObj.getPositionInSizer() |
|---|
| 185 |
return self.addFromContextMenu(pos, evt) |
|---|
| 186 |
|
|---|
| 187 |
|
|---|
| 188 |
def onAddBelow(self, evt): |
|---|
| 189 |
pos = self._contextObj.getPositionInSizer() + 1 |
|---|
| 190 |
return self.addFromContextMenu(pos, evt) |
|---|
| 191 |
|
|---|
| 192 |
|
|---|
| 193 |
def addFromContextMenu(self, pos, evt): |
|---|
| 194 |
obj = self._contextObj |
|---|
| 195 |
self._contextObj = None |
|---|
| 196 |
if "Separator" in evt.EventObject.Caption: |
|---|
| 197 |
return self.insertSeparator(pos) |
|---|
| 198 |
else: |
|---|
| 199 |
return self.addItem(pos) |
|---|
| 200 |
|
|---|
| 201 |
|
|---|
| 202 |
def addItem(self, pos): |
|---|
| 203 |
itm = None |
|---|
| 204 |
cap = dabo.ui.getString("Caption for new menu item?") |
|---|
| 205 |
if cap: |
|---|
| 206 |
itm = self.insert(pos, cap) |
|---|
| 207 |
self.Controller.select(itm) |
|---|
| 208 |
return itm |
|---|
| 209 |
|
|---|
| 210 |
|
|---|
| 211 |
def onMoveDown(self, evt): |
|---|
| 212 |
obj = self._contextObj |
|---|
| 213 |
pos = obj.getPositionInSizer() |
|---|
| 214 |
sz = obj.ControllingSizer |
|---|
| 215 |
sz.remove(obj) |
|---|
| 216 |
sz.insert(pos+1, obj, "x") |
|---|
| 217 |
sz.layout() |
|---|
| 218 |
self.Controller.updateLayout() |
|---|
| 219 |
|
|---|
| 220 |
|
|---|
| 221 |
def onMoveUp(self, evt): |
|---|
| 222 |
obj = self._contextObj |
|---|
| 223 |
pos = obj.getPositionInSizer() |
|---|
| 224 |
sz = obj.ControllingSizer |
|---|
| 225 |
sz.remove(obj) |
|---|
| 226 |
sz.insert(pos-1, obj, "x") |
|---|
| 227 |
sz.layout() |
|---|
| 228 |
self.Controller.updateLayout() |
|---|
| 229 |
|
|---|
| 230 |
|
|---|
| 231 |
def onDelete(self, evt): |
|---|
| 232 |
obj = self._contextObj |
|---|
| 233 |
if not obj: |
|---|
| 234 |
return |
|---|
| 235 |
obj.ControllingSizer.remove(obj) |
|---|
| 236 |
obj.release() |
|---|
| 237 |
self.layout() |
|---|
| 238 |
self.Controller.updateLayout() |
|---|
| 239 |
|
|---|
| 240 |
|
|---|
| 241 |
def onMouseLeftClick(self, evt): |
|---|
| 242 |
self.Parent.menuClick(self) |
|---|
| 243 |
|
|---|
| 244 |
|
|---|
| 245 |
def _getChildren(self): |
|---|
| 246 |
try: |
|---|
| 247 |
return self.itemList.Children |
|---|
| 248 |
except AttributeError: |
|---|
| 249 |
return [] |
|---|
| 250 |
|
|---|
| 251 |
|
|---|
| 252 |
def _getPanelVisible(self): |
|---|
| 253 |
return self.itemList.Visible |
|---|
| 254 |
|
|---|
| 255 |
def _setPanelVisible(self, val): |
|---|
| 256 |
if val: |
|---|
| 257 |
localPos = (self.Left, self.Bottom) |
|---|
| 258 |
formPos = self.Parent.formCoordinates(localPos) |
|---|
| 259 |
self.itemList.Position = formPos |
|---|
| 260 |
self.Controller.onShowPanel(self) |
|---|
| 261 |
# self.Visible = val |
|---|
| 262 |
self.itemList.Visible = val |
|---|
| 263 |
|
|---|
| 264 |
# itms = self._proxyDict.get("Visible", ()) |
|---|
| 265 |
# for xx in itms: |
|---|
| 266 |
# if xx == "Children": |
|---|
| 267 |
# for xkid in self.Children: |
|---|
| 268 |
# elif xx == "self": |
|---|
| 269 |
# print "SELF ITEMLIST", self.itemList.Visible |
|---|
| 270 |
# else: |
|---|
| 271 |
# obj = getattr(self, xx) |
|---|
| 272 |
# print xx, obj.Visible |
|---|
| 273 |
|
|---|
| 274 |
self.itemList.update() |
|---|
| 275 |
self.itemList.Parent.clear() |
|---|
| 276 |
self.layout() |
|---|
| 277 |
# self.itemList.Parent.refresh() |
|---|
| 278 |
|
|---|
| 279 |
|
|---|
| 280 |
Children = property(_getChildren, None, None, |
|---|
| 281 |
_("Returns a list of menu items for this menu (read-only) (list)")) |
|---|
| 282 |
|
|---|
| 283 |
PanelVisible = property(_getPanelVisible, _setPanelVisible, None, |
|---|
| 284 |
_("Determines if the menu is currently 'open' (bool)")) |
|---|
| 285 |
|
|---|
| 286 |
|
|---|
| 287 |
_proxyDict = {} |
|---|
| 288 |
Visible = makeProxyProperty(_proxyDict, "Visible", ("self", "Children", "_capText")) |
|---|