Use list.sort's keyword parameters
[joel/kofoto.git] / src / packages / kofoto / gkofoto / objectcollectionview.py
1 import gtk
2 import os
3 from kofoto.gkofoto.environment import env
4 from kofoto.gkofoto.menuhandler import MenuGroup
5 from kofoto.gkofoto.objectcollection import ObjectCollection
6 from kofoto.common import UnimplementedError
7
8 class ObjectCollectionView:
9
10 ###############################################################################
11 ### Public
12
13     def __init__(self, view):
14         self._viewWidget = view
15         self._objectCollection = None
16         self._contextMenu = None
17         self.__objectCollectionLoaded = False
18         self.__hidden = True
19         self.__connections = []
20         self.__singleImageMenuGroup = None
21         self.__objectMenuGroup = None
22         self.__multipleImagesMenuGroup = None
23         self.__sortMenuGroup = None
24         self.__albumMenuGroup = None
25         self.__clipboardMenuGroup = None
26         self.__imageMenuGroup = None
27
28     def show(self, objectCollection):
29         if self.__hidden:
30             self.__hidden = False
31             self.__connectObjectCollection(objectCollection)
32             self._showHelper()
33             self._updateMenubarSortMenu()
34         else:
35             self.setObjectCollection(objectCollection)
36
37     def _connectMenubarImageItems(self):
38         self._connect(
39             env.widgets["menubarOpenImage"],
40             "activate",
41             self._objectCollection.openImage)
42         self._connect(
43             env.widgets["menubarDuplicateAndOpenImage"],
44             "activate",
45             self._objectCollection.duplicateAndOpenImage)
46         self._connect(
47             env.widgets["menubarRotateLeft"],
48             "activate",
49             self._objectCollection.rotateImage,
50             270)
51         self._connect(
52             env.widgets["menubarRotateRight"],
53             "activate",
54             self._objectCollection.rotateImage,
55             90)
56         self._connect(
57             env.widgets["menubarImageVersions"],
58             "activate",
59             self._objectCollection.imageVersions)
60         self._connect(
61             env.widgets["menubarRegisterImageVersions"],
62             "activate",
63             self._objectCollection.registerImageVersions)
64         self._connect(
65             env.widgets["menubarMergeImages"],
66             "activate",
67             self._objectCollection.mergeImages)
68
69     def _updateMenubarSortMenu(self):
70         sortMenuGroup = self.__createSortMenuGroup(self._objectCollection)
71         sortByItem = env.widgets["menubarSortBy"]
72         if self._objectCollection.isSortable():
73             sortByItem.set_sensitive(True)
74             sortByItem.set_submenu(sortMenuGroup.createGroupMenu())
75         else:
76             sortByItem.remove_submenu()
77             sortByItem.set_sensitive(False)
78
79     def hide(self):
80         if not self.__hidden:
81             self.__hidden = True
82             self._hideHelper()
83             self._clearAllConnections()
84             self.__disconnectObjectCollection()
85
86     def setObjectCollection(self, objectCollection):
87         if not self.__hidden:
88             env.debug("ObjectCollectionView sets object collection")
89             self.__connectObjectCollection(objectCollection)
90
91     def freeze(self):
92         self._freezeHelper()
93         self._objectCollection.getObjectSelection().removeChangedCallback(self.importSelection)
94         env.clipboard.removeChangedCallback(self._updateContextMenu)
95
96     def thaw(self):
97         self._thawHelper()
98         self._objectCollection.getObjectSelection().addChangedCallback(self.importSelection)
99         env.clipboard.addChangedCallback(self._updateContextMenu)
100         self.importSelection(self._objectCollection.getObjectSelection())
101         # importSelection makes an implicit _updateContextMenu()
102
103     def sortOrderChanged(self, sortOrder):
104         env.debug("Sort order is " + str(sortOrder))
105         self.__sortMenuGroup[sortOrder].activate()
106
107     def sortColumnChanged(self, sortColumn):
108         env.debug("Sort column is " + str(sortColumn))
109         self.__sortMenuGroup[sortColumn].activate()
110
111     def fieldsDisabled(self, fields):
112         pass
113
114     def fieldsEnabled(self, fields):
115         pass
116
117     def loadingFinished(self):
118         self._updateContextMenu()
119
120     def _mouse_button_pressed(self, widget, event):
121         widget.grab_focus()
122         if event.button == 3:
123             self._contextMenu.popup(None, None, None, event.button, event.time)
124             return True
125         else:
126             return False
127
128 ##############################################################################
129 ### Methods used by and overloaded by subclasses
130
131     def _hasFocus(self):
132         raise UnimplementedError
133
134     def _showHelper(self):
135         raise UnimplementedError
136
137     def _hideHelper(self):
138         raise UnimplementedError
139
140     def _freezeHelper(self):
141         raise UnimplementedError
142
143     def _thawHelper(self):
144         raise UnimplementedError
145
146     def _connectObjectCollectionHelper(self):
147         raise UnimplementedError
148
149     def _disconnectObjectCollectionHelper(self):
150         raise UnimplementedError
151
152     def importSelection(self, objectCollection):
153         raise UnimplementedError
154
155     def _connect(self, obj, signal, function, data=None):
156         oid = obj.connect(signal, function, data)
157         self.__connections.append((obj, oid))
158         return oid
159
160     def _disconnect(self, obj, oid):
161         obj.disconnect(oid)
162         self.__connections.remove((obj, oid))
163
164     def _clearAllConnections(self):
165         for (obj, oid) in self.__connections:
166             obj.disconnect(oid)
167         self.__connections = []
168
169     def _createContextMenu(self, objectCollection):
170         env.debug("Creating view context menu")
171         self._contextMenu = gtk.Menu()
172         self.__clipboardMenuGroup = self.__createClipboardMenuGroup(objectCollection)
173         for item in self.__clipboardMenuGroup:
174             self._contextMenu.add(item)
175         self.__objectMenuGroup = self.__createObjectMenuGroup(objectCollection)
176         for item in self.__objectMenuGroup:
177             self._contextMenu.add(item)
178         self.__albumMenuGroup = self.__createAlbumMenuGroup(objectCollection)
179         for item in self.__albumMenuGroup:
180             self._contextMenu.add(item)
181         self.__imageMenuGroup = self.__createImageMenuGroup(objectCollection)
182         for item in self.__imageMenuGroup:
183             self._contextMenu.add(item)
184         self.__singleImageMenuGroup = self.__createSingleImageMenuGroup(objectCollection)
185         for item in self.__singleImageMenuGroup:
186             self._contextMenu.add(item)
187         self.__multipleImagesMenuGroup = self.__createMultipleImagesMenuGroup(objectCollection)
188         for item in self.__multipleImagesMenuGroup:
189             self._contextMenu.add(item)
190         self.__sortMenuGroup = self.__createSortMenuGroup(objectCollection)
191         self._contextMenu.add(self.__sortMenuGroup.createGroupMenuItem())
192
193     def _clearContextMenu(self):
194         env.debug("Clearing view context menu")
195         self._contextMenu = None
196         self.__clipboardMenuGroup = None
197         self.__objectMenuGroup = None
198         self.__albumMenuGroup = None
199         self.__imageMenuGroup = None
200         self.__singleImageMenuGroup = None
201         self.__multipleImagesMenuGroup = None
202         self.__sortMenuGroup = None
203
204     def _updateContextMenu(self, *unused):
205         if not self._hasFocus():
206             return
207         env.debug("Updating context menu")
208         self.__objectMenuGroup[self._objectCollection.getDestroyLabel()].set_sensitive(False)
209         env.widgets["menubarDestroy"].set_sensitive(False)
210         mutable = self._objectCollection.isMutable()
211         loading = self._objectCollection.isLoading()
212         objectSelection = self._objectCollection.getObjectSelection()
213         if objectSelection:
214             model = self._objectCollection.getModel()
215             rootAlbumId = env.shelf.getRootAlbum().getId()
216
217             albumsSelected = 0
218             imagesSelected = 0
219             rootAlbumSelected = False
220             for position in objectSelection:
221                 iterator = model.get_iter(position)
222                 isAlbum = model.get_value(
223                     iterator, self._objectCollection.COLUMN_IS_ALBUM)
224                 if isAlbum:
225                     albumsSelected += 1
226                     if rootAlbumId == model.get_value(
227                         iterator, self._objectCollection.COLUMN_OBJECT_ID):
228                         rootAlbumSelected = True
229                 else:
230                     imagesSelected += 1
231
232             self.__clipboardMenuGroup[self._objectCollection.getCutLabel()].set_sensitive(mutable and not loading)
233             env.widgets["menubarCut"].set_sensitive(mutable and not loading)
234             self.__clipboardMenuGroup[self._objectCollection.getCopyLabel()].set_sensitive(True)
235             env.widgets["menubarCopy"].set_sensitive(True)
236             self.__clipboardMenuGroup[self._objectCollection.getDeleteLabel()].set_sensitive(mutable and not loading)
237             env.widgets["menubarDelete"].set_sensitive(mutable and not loading)
238             destroyActive = (imagesSelected == 0) ^ (albumsSelected == 0) and not rootAlbumSelected and not loading
239             self.__objectMenuGroup[self._objectCollection.getDestroyLabel()].set_sensitive(destroyActive)
240             env.widgets["menubarDestroy"].set_sensitive(destroyActive)
241             if albumsSelected == 1 and imagesSelected == 0:
242                 selectedAlbumId = model.get_value(
243                     iterator, self._objectCollection.COLUMN_OBJECT_ID)
244                 selectedAlbum = env.shelf.getAlbum(selectedAlbumId)
245                 if selectedAlbum.isMutable():
246                     self.__albumMenuGroup.enable()
247                     env.widgets["menubarCreateAlbumChild"].set_sensitive(True)
248                     env.widgets["menubarRegisterAndAddImages"].set_sensitive(True)
249                     env.widgets["menubarGenerateHtml"].set_sensitive(True)
250                     env.widgets["menubarProperties"].set_sensitive(True)
251                 else:
252                     self.__albumMenuGroup.disable()
253                     self.__albumMenuGroup[
254                         self._objectCollection.getAlbumPropertiesLabel()
255                         ].set_sensitive(True)
256                     env.widgets["menubarCreateAlbumChild"].set_sensitive(False)
257                     env.widgets["menubarRegisterAndAddImages"].set_sensitive(False)
258                     env.widgets["menubarGenerateHtml"].set_sensitive(True)
259                     env.widgets["menubarProperties"].set_sensitive(True)
260             else:
261                 self.__albumMenuGroup.disable()
262                 env.widgets["menubarCreateAlbumChild"].set_sensitive(False)
263                 env.widgets["menubarRegisterAndAddImages"].set_sensitive(False)
264                 env.widgets["menubarGenerateHtml"].set_sensitive(False)
265                 env.widgets["menubarProperties"].set_sensitive(False)
266             if albumsSelected == 0 and imagesSelected > 0:
267                 self.__imageMenuGroup.enable()
268                 env.widgets["menubarOpenImage"].set_sensitive(True)
269                 env.widgets["menubarRotateLeft"].set_sensitive(True)
270                 env.widgets["menubarRotateRight"].set_sensitive(True)
271                 if imagesSelected == 1:
272                     env.widgets["menubarDuplicateAndOpenImage"].set_sensitive(True)
273                     self.__singleImageMenuGroup.enable()
274                     env.widgets["menubarImageVersions"].set_sensitive(True)
275                     env.widgets["menubarRegisterImageVersions"].set_sensitive(True)
276                     self.__multipleImagesMenuGroup.disable()
277                     env.widgets["menubarMergeImages"].set_sensitive(False)
278                 else:
279                     self.__imageMenuGroup[
280                         self._objectCollection.getDuplicateAndOpenImageLabel()
281                         ].set_sensitive(False)
282                     env.widgets["menubarDuplicateAndOpenImage"].set_sensitive(False)
283                     self.__singleImageMenuGroup.disable()
284                     env.widgets["menubarImageVersions"].set_sensitive(False)
285                     env.widgets["menubarRegisterImageVersions"].set_sensitive(False)
286                     self.__multipleImagesMenuGroup.enable()
287                     env.widgets["menubarMergeImages"].set_sensitive(True)
288             else:
289                 self.__imageMenuGroup.disable()
290                 self.__singleImageMenuGroup.disable()
291                 env.widgets["menubarOpenImage"].set_sensitive(False)
292                 env.widgets["menubarDuplicateAndOpenImage"].set_sensitive(False)
293                 env.widgets["menubarRegisterImageVersions"].set_sensitive(False)
294                 env.widgets["menubarRotateLeft"].set_sensitive(False)
295                 env.widgets["menubarRotateRight"].set_sensitive(False)
296         else:
297             self.__clipboardMenuGroup.disable()
298             env.widgets["menubarCut"].set_sensitive(False)
299             env.widgets["menubarCopy"].set_sensitive(False)
300             env.widgets["menubarDelete"].set_sensitive(False)
301
302             self.__objectMenuGroup.disable()
303             env.widgets["menubarDestroy"].set_sensitive(False)
304
305             self.__albumMenuGroup.disable()
306             env.widgets["menubarCreateAlbumChild"].set_sensitive(False)
307             env.widgets["menubarRegisterAndAddImages"].set_sensitive(False)
308             env.widgets["menubarGenerateHtml"].set_sensitive(False)
309             env.widgets["menubarProperties"].set_sensitive(False)
310
311             self.__imageMenuGroup.disable()
312             env.widgets["menubarOpenImage"].set_sensitive(False)
313             env.widgets["menubarDuplicateAndOpenImage"].set_sensitive(False)
314             env.widgets["menubarRotateLeft"].set_sensitive(False)
315             env.widgets["menubarRotateRight"].set_sensitive(False)
316             self.__singleImageMenuGroup.disable()
317             env.widgets["menubarImageVersions"].set_sensitive(False)
318             env.widgets["menubarRegisterImageVersions"].set_sensitive(False)
319             self.__multipleImagesMenuGroup.disable()
320             env.widgets["menubarMergeImages"].set_sensitive(False)
321
322         if env.clipboard.hasObjects():
323             self.__clipboardMenuGroup[
324                 self._objectCollection.getPasteLabel()].set_sensitive(mutable)
325             env.widgets["menubarPaste"].set_sensitive(mutable)
326         else:
327             self.__clipboardMenuGroup[
328                 self._objectCollection.getPasteLabel()].set_sensitive(False)
329             env.widgets["menubarPaste"].set_sensitive(False)
330
331
332 ###############################################################################
333 ### Private
334
335     def __connectObjectCollection(self, objectCollection):
336         if self._objectCollection != None:
337             self.__disconnectObjectCollection()
338         self._objectCollection = objectCollection
339         self._createContextMenu(objectCollection)
340         self._connectObjectCollectionHelper()
341         self.thaw()
342         self._objectCollection.registerView(self)
343
344     def __disconnectObjectCollection(self):
345         if self._objectCollection is not None:
346             self._objectCollection.unRegisterView(self)
347             self.freeze()
348             self._disconnectObjectCollectionHelper()
349             self._clearContextMenu()
350             self._objectCollection = None
351
352     def __createSortMenuGroup(self, objectCollection):
353         menuGroup = MenuGroup("Sort by")
354         if objectCollection.isSortable():
355             env.debug("Creating sort menu group for sortable log collection")
356             menuGroup.addRadioMenuItem("Ascending",
357                                        objectCollection.setSortOrder,
358                                        gtk.SORT_ASCENDING)
359             menuGroup.addRadioMenuItem("Descending",
360                                        objectCollection.setSortOrder,
361                                        gtk.SORT_DESCENDING)
362             menuGroup.addSeparator()
363             objectMetadataMap = objectCollection.getObjectMetadataMap()
364             columnNames = sorted(objectMetadataMap)
365             for columnName in columnNames:
366                 if objectMetadataMap[columnName][ObjectCollection.TYPE] != gtk.gdk.Pixbuf:
367                     menuGroup.addRadioMenuItem(columnName,
368                                                objectCollection.setSortColumnName,
369                                                columnName)
370         return menuGroup
371
372     def __createClipboardMenuGroup(self, oc):
373         menuGroup = MenuGroup()
374         env.debug("Creating clipboard menu")
375         menuGroup.addStockImageMenuItem(
376             oc.getCutLabel(), gtk.STOCK_CUT, oc.cut)
377         menuGroup.addStockImageMenuItem(
378             oc.getCopyLabel(), gtk.STOCK_COPY, oc.copy)
379         menuGroup.addStockImageMenuItem(
380             oc.getPasteLabel(), gtk.STOCK_PASTE, oc.paste)
381         menuGroup.addStockImageMenuItem(
382             oc.getDeleteLabel(), gtk.STOCK_DELETE, oc.delete)
383         menuGroup.addSeparator()
384         return menuGroup
385
386     def __createObjectMenuGroup(self, oc):
387         menuGroup = MenuGroup()
388         menuGroup.addStockImageMenuItem(
389             oc.getDestroyLabel(), gtk.STOCK_DELETE, oc.destroy)
390         menuGroup.addSeparator()
391         return menuGroup
392
393     def __createAlbumMenuGroup(self, oc):
394         menuGroup = MenuGroup()
395         menuGroup.addMenuItem(
396             oc.getCreateAlbumChildLabel(), oc.createAlbumChild)
397         menuGroup.addMenuItem(
398             oc.getRegisterImagesLabel(), oc.registerAndAddImages)
399         menuGroup.addMenuItem(
400             oc.getGenerateHtmlLabel(), oc.generateHtml)
401         menuGroup.addStockImageMenuItem(
402             oc.getAlbumPropertiesLabel(),
403             gtk.STOCK_PROPERTIES,
404             oc.albumProperties)
405         menuGroup.addSeparator()
406         return menuGroup
407
408     def __createImageMenuGroup(self, oc):
409         menuGroup = MenuGroup()
410         menuGroup.addStockImageMenuItem(
411             oc.getOpenImageLabel(),
412             gtk.STOCK_OPEN,
413             oc.openImage)
414         menuGroup.addStockImageMenuItem(
415             oc.getDuplicateAndOpenImageLabel(),
416             gtk.STOCK_OPEN,
417             oc.duplicateAndOpenImage)
418         menuGroup.addImageMenuItem(
419             oc.getRotateImageLeftLabel(),
420             os.path.join(env.iconDir, "rotateleft.png"),
421             oc.rotateImage, 270)
422         menuGroup.addImageMenuItem(
423             oc.getRotateImageRightLabel(),
424             os.path.join(env.iconDir, "rotateright.png"),
425             oc.rotateImage, 90)
426         menuGroup.addSeparator()
427         return menuGroup
428
429     def __createSingleImageMenuGroup(self, oc):
430         menuGroup = MenuGroup()
431         menuGroup.addMenuItem(oc.getImageVersionsLabel(), oc.imageVersions)
432         menuGroup.addMenuItem(
433             oc.getRegisterImageVersionsLabel(), oc.registerImageVersions)
434         return menuGroup
435
436     def __createMultipleImagesMenuGroup(self, oc):
437         menuGroup = MenuGroup()
438         menuGroup.addMenuItem(oc.getMergeImagesLabel(), oc.mergeImages)
439         menuGroup.addSeparator()
440         return menuGroup