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