1 2import os 3from xml.dom import minidom 4from collections import defaultdict 5 6 7from pychess.System.prefix import addDataPrefix 8 9from .PyDockLeaf import PyDockLeaf 10from .PyDockComposite import PyDockComposite 11from .ArrowButton import ArrowButton 12from .HighlightArea import HighlightArea 13from .__init__ import TabReceiver 14from .__init__ import NORTH, EAST, SOUTH, WEST, CENTER 15 16 17class PyDockTop(PyDockComposite, TabReceiver): 18 def __init__(self, id, perspective): 19 TabReceiver.__init__(self, perspective) 20 self.id = id 21 self.perspective = perspective 22 self.set_no_show_all(True) 23 self.highlightArea = HighlightArea(self) 24 25 self.button_cids = defaultdict(list) 26 27 self.buttons = ( 28 ArrowButton(self, addDataPrefix("glade/dock_top.svg"), NORTH), 29 ArrowButton(self, addDataPrefix("glade/dock_right.svg"), EAST), 30 ArrowButton(self, addDataPrefix("glade/dock_bottom.svg"), SOUTH), 31 ArrowButton(self, addDataPrefix("glade/dock_left.svg"), WEST)) 32 33 for button in self.buttons: 34 self.button_cids[button] += [ 35 button.connect("dropped", self.__onDrop), 36 button.connect("hovered", self.__onHover), 37 button.connect("left", self.__onLeave), 38 ] 39 40 def _del(self): 41 if self.highlightArea.handler_is_connected(self.highlightArea.cid): 42 self.highlightArea.disconnect(self.highlightArea.cid) 43 44 for button in self.buttons: 45 for cid in self.button_cids[button]: 46 if button.handler_is_connected(cid): 47 button.disconnect(cid) 48 button.myparent = None 49 50 self.button_cids = {} 51 self.highlightArea.myparent = None 52 53 TabReceiver._del(self) 54 PyDockComposite._del(self) 55 56 def getPosition(self): 57 return CENTER 58 59 def __repr__(self): 60 return "top (%s)" % self.id 61 62 # =========================================================================== 63 # Component stuff 64 # =========================================================================== 65 66 def addComponent(self, widget): 67 self.add(widget) 68 widget.show() 69 70 def changeComponent(self, old, new): 71 self.removeComponent(old) 72 self.addComponent(new) 73 74 def removeComponent(self, widget): 75 self.remove(widget) 76 77 def getComponents(self): 78 child = self.get_child() 79 if isinstance(child, PyDockComposite) or isinstance(child, PyDockLeaf): 80 return [child] 81 return [] 82 83 def dock(self, widget, position, title, id): 84 if not self.getComponents(): 85 leaf = PyDockLeaf(widget, title, id, self.perspective) 86 self.addComponent(leaf) 87 return leaf 88 else: 89 return self.get_child().dock(widget, position, title, id) 90 91 def clear(self): 92 self.remove(self.get_child()) 93 94 # =========================================================================== 95 # Signals 96 # =========================================================================== 97 98 def showArrows(self): 99 for button in self.buttons: 100 button._calcSize() 101 button.show() 102 103 def hideArrows(self): 104 for button in self.buttons: 105 button.hide() 106 self.highlightArea.hide() 107 108 def __onDrop(self, arrowButton, sender): 109 self.highlightArea.hide() 110 child = sender.get_nth_page(sender.get_current_page()) 111 112 for instance in sender.get_parent().getInstances(self.perspective): 113 instance.hideArrows() 114 115 title, id = sender.get_parent().undock(child) 116 self.dock(child, arrowButton.myposition, title, id) 117 118 def __onHover(self, arrowButton, widget): 119 self.highlightArea.showAt(arrowButton.myposition) 120 arrowButton.get_window().raise_() 121 122 def __onLeave(self, arrowButton): 123 self.highlightArea.hide() 124 125 # =========================================================================== 126 # XML 127 # =========================================================================== 128 129 def saveToXML(self, xmlpath): 130 """ 131 <docks> 132 <dock id="x"> 133 <v pos="200"> 134 <leaf current="x" dockable="False"> 135 <panel id="x" /> 136 </leaf> 137 <h pos="200"> 138 <leaf current="y" dockable="True"> 139 <panel id="y" /> 140 <panel id="z" /> 141 </leaf> 142 <leaf current="y" dockable="True"> 143 <panel id="y" /> 144 </leaf> 145 </h> 146 </v> 147 </dock> 148 </docks> 149 """ 150 dockElem = None 151 152 if os.path.isfile(xmlpath): 153 doc = minidom.parse(xmlpath) 154 for elem in doc.getElementsByTagName("dock"): 155 if elem.getAttribute("id") == self.id: 156 for node in elem.childNodes: 157 elem.removeChild(node) 158 dockElem = elem 159 break 160 161 if not dockElem: 162 doc = minidom.getDOMImplementation().createDocument(None, "docks", 163 None) 164 dockElem = doc.createElement("dock") 165 dockElem.setAttribute("id", self.id) 166 doc.documentElement.appendChild(dockElem) 167 168 if self.get_child(): 169 self.__addToXML(self.get_child(), dockElem, doc) 170 with open(xmlpath, "w") as f_handle: 171 doc.writexml(f_handle) 172 doc.unlink() 173 174 def __addToXML(self, component, parentElement, document): 175 if isinstance(component, PyDockComposite): 176 pos = component.paned.get_position() 177 if component.getPosition() in (NORTH, SOUTH): 178 childElement = document.createElement("v") 179 size = float(component.get_allocation().height) 180 else: 181 childElement = document.createElement("h") 182 size = float(component.get_allocation().width) 183# if component.getPosition() in (NORTH, SOUTH): 184# print "saving v position as %s out of %s (%s)" % (str(pos), str(size), str(pos/max(size,pos))) 185 childElement.setAttribute("pos", str(pos / max(size, pos))) 186 self.__addToXML(component.getComponents()[0], childElement, 187 document) 188 self.__addToXML(component.getComponents()[1], childElement, 189 document) 190 191 elif isinstance(component, PyDockLeaf): 192 childElement = document.createElement("leaf") 193 childElement.setAttribute("current", component.getCurrentPanel()) 194 childElement.setAttribute("dockable", str(component.isDockable())) 195 for panel, title, id in component.getPanels(): 196 element = document.createElement("panel") 197 element.setAttribute("id", id) 198 element.setAttribute("visible", str(panel.get_visible())) 199 childElement.appendChild(element) 200 201 parentElement.appendChild(childElement) 202 203 def loadFromXML(self, xmlpath, idToWidget): 204 """ idTowidget is a dictionary {id: (widget,title)} 205 asserts that self.id is in the xmlfile """ 206 doc = minidom.parse(xmlpath) 207 for elem in doc.getElementsByTagName("dock"): 208 if elem.getAttribute("id") == self.id: 209 break 210 else: 211 raise AttributeError( 212 "XML file contains no <dock> elements with id '%s'" % self.id) 213 214 child = [n for n in elem.childNodes if isinstance(n, minidom.Element)] 215 if child: 216 self.addComponent(self.__createWidgetFromXML(child[0], idToWidget)) 217 218 def __createWidgetFromXML(self, parentElement, idToWidget): 219 children = [n 220 for n in parentElement.childNodes 221 if isinstance(n, minidom.Element)] 222 223 if parentElement.tagName in ("h", "v"): 224 child1, child2 = children 225 if parentElement.tagName == "h": 226 new = PyDockComposite(EAST, self.perspective) 227 else: 228 new = PyDockComposite(SOUTH, self.perspective) 229 new.initChildren( 230 self.__createWidgetFromXML(child1, idToWidget), 231 self.__createWidgetFromXML(child2, idToWidget), 232 preserve_dimensions=True) 233 234 def cb(widget, event, pos): 235 allocation = widget.get_allocation() 236 if parentElement.tagName == "h": 237 widget.set_position(int(allocation.width * pos)) 238 else: 239 # print "loading v position as %s out of %s (%s)" % \ 240 # (int(allocation.height * pos), str(allocation.height), str(pos)) 241 widget.set_position(int(allocation.height * pos)) 242 widget.disconnect(conid) 243 244 conid = new.paned.connect("size-allocate", cb, float(parentElement.getAttribute("pos"))) 245 return new 246 247 elif parentElement.tagName == "leaf": 248 id = children[0].getAttribute("id") 249 try: 250 title, widget, menu_item = idToWidget[id] 251 except KeyError: 252 id = self.old2new(id) 253 title, widget, menu_item = idToWidget[id] 254 255 leaf = PyDockLeaf(widget, title, id, self.perspective) 256 visible = children[0].getAttribute("visible") 257 visible = visible == "" or visible == "True" 258 widget.set_visible(visible) 259 if menu_item is not None: 260 menu_item.set_active(visible) 261 262 for panelElement in children[1:]: 263 id = panelElement.getAttribute("id") 264 try: 265 title, widget, menu_item = idToWidget[id] 266 except KeyError: 267 id = self.old2new(id) 268 title, widget, menu_item = idToWidget[id] 269 270 visible = panelElement.getAttribute("visible") 271 visible = visible == "" or visible == "True" 272 leaf.dock(widget, CENTER, title, id) 273 widget.set_visible(visible) 274 if menu_item is not None: 275 menu_item.set_active(visible) 276 277 leaf.setCurrentPanel(self.old2new(parentElement.getAttribute("current"))) 278 if parentElement.getAttribute("dockable").lower() == "false": 279 leaf.setDockable(False) 280 return leaf 281 282 def old2new(self, name): 283 """ After 0.99.0 database perspective panel names changed """ 284 x = {"switcher": "SwitcherPanel", 285 "openingtree": "OpeningTreePanel", 286 "filter": "FilterPanel", 287 "preview": "PreviewPanel", 288 "chat": "ChatPanel", 289 "console": "ConsolePanel", 290 "news": "NewsPanel", 291 "seeklist": "SeekListPanel", 292 "seekgraph": "SeekGraphPanel", 293 "playerlist": "PlayerListPanel", 294 "gamelist": "GameListPanel", 295 "archivelist": "ArchiveListPanel", 296 } 297 return x[name] if name in x else name 298