Use list.sort's keyword parameters
[joel/kofoto.git] / src / packages / kofoto / gkofoto / albums.py
1 import gtk
2 import gobject
3 from kofoto.gkofoto.environment import env
4 from kofoto.gkofoto.albumdialog import AlbumDialog
5 from kofoto.gkofoto.menuhandler import MenuGroup
6 from kofoto.gkofoto.registerimagesdialog import RegisterImagesDialog
7
8 class Albums:
9
10 ###############################################################################
11 ### Public
12
13     __createAlbumLabel = "Create subalbum..."
14     __registerImagesLabel = "Register and add images..."
15     __generateHtmlLabel = "Generate HTML..."
16     __destroyAlbumLabel = "Destroy album..."
17     __editAlbumLabel = "Album properties..."
18
19     # TODO This class should probably be splited in a model and a view when/if
20     #      a multiple windows feature is introduced.
21
22     def __init__(self, mainWindow):
23         self.__menuGroup = None
24         self._connectedOids = []
25         self.__albumModel = gtk.TreeStore(gobject.TYPE_INT,      # ALBUM_ID
26                                           gobject.TYPE_STRING,   # TAG
27                                           gobject.TYPE_STRING,   # TEXT
28                                           gobject.TYPE_STRING,   # TYPE
29                                           gobject.TYPE_BOOLEAN)  # SELECTABLE
30         self.__mainWindow = mainWindow
31         self.__albumView = env.widgets["albumView"]
32         self.__albumView.set_model(self.__albumModel)
33         self.__albumView.connect("focus-in-event", self._treeViewFocusInEvent_cb)
34         self.__albumView.connect("focus-out-event", self._treeViewFocusOutEvent_cb)
35         renderer = gtk.CellRendererText()
36         column = gtk.TreeViewColumn("Albums", renderer, text=self.__COLUMN_TEXT)
37         column.set_clickable(True)
38         self.__albumView.append_column(column)
39         albumSelection = self.__albumView.get_selection()
40         albumSelection.connect("changed", self._albumSelectionUpdated_cb)
41         albumSelection.set_select_function(self._isSelectable, self.__albumModel)
42         self.__contextMenu = self.__createContextMenu()
43         self.__albumView.connect("button_press_event", self._button_pressed_cb)
44         self.loadAlbumTree()
45         iterator = self.__albumModel.get_iter_first()
46         albumSelection.select_iter(iterator)
47
48     def loadAlbumTree(self):
49         env.shelf.flushObjectCache()
50         self.__albumModel.clear()
51         self.__loadAlbumTreeHelper()
52         env.widgets["albumView"].expand_row(0, False) # Expand root album
53
54     def unselect(self):
55         self.__albumView.get_selection().unselect_all()
56
57 ###############################################################################
58 ### Callback functions registered by this class but invoked from other classes.
59
60     def _isSelectable(self, path, model):
61         return model[path][self.__COLUMN_SELECTABLE]
62
63     def _albumSelectionUpdated_cb(self, selection=None, load=True):
64         # The focus grab below is made to compensate for what could be
65         # some GTK bug. Without the call, the focus-out-event signal
66         # sometimes isn't emitted for the view widget in the table
67         # view, which messes up the menubar callback registrations.
68         self.__albumView.grab_focus()
69
70         if not selection:
71             selection = self.__albumView.get_selection()
72         albumModel, iterator =  self.__albumView.get_selection().get_selected()
73         createMenuItem = self.__menuGroup[self.__createAlbumLabel]
74         registerMenuItem = self.__menuGroup[self.__registerImagesLabel]
75         generateHtmlMenuItem = self.__menuGroup[self.__generateHtmlLabel]
76         destroyMenuItem = self.__menuGroup[self.__destroyAlbumLabel]
77         editMenuItem = self.__menuGroup[self.__editAlbumLabel]
78         if iterator:
79             albumTag = albumModel.get_value(
80                 iterator, self.__COLUMN_TAG).decode("utf-8")
81             if load:
82                 self.__mainWindow.loadQuery(u"/" + albumTag)
83             album = env.shelf.getAlbum(
84                 albumModel.get_value(iterator, self.__COLUMN_ALBUM_ID))
85             createMenuItem.set_sensitive(album.isMutable())
86             env.widgets["menubarCreateAlbumChild"].set_sensitive(album.isMutable())
87             registerMenuItem.set_sensitive(album.isMutable())
88             env.widgets["menubarRegisterAndAddImages"].set_sensitive(album.isMutable())
89             generateHtmlMenuItem.set_sensitive(True)
90             env.widgets["menubarGenerateHtml"].set_sensitive(True)
91             destroyMenuItem.set_sensitive(album != env.shelf.getRootAlbum())
92             env.widgets["menubarDestroy"].set_sensitive(album != env.shelf.getRootAlbum())
93             editMenuItem.set_sensitive(True)
94             env.widgets["menubarProperties"].set_sensitive(True)
95         else:
96             createMenuItem.set_sensitive(False)
97             registerMenuItem.set_sensitive(False)
98             generateHtmlMenuItem.set_sensitive(False)
99             destroyMenuItem.set_sensitive(False)
100             editMenuItem.set_sensitive(False)
101             env.widgets["menubarCreateAlbumChild"].set_sensitive(False)
102             env.widgets["menubarRegisterAndAddImages"].set_sensitive(False)
103             env.widgets["menubarGenerateHtml"].set_sensitive(False)
104             env.widgets["menubarDestroy"].set_sensitive(False)
105             env.widgets["menubarProperties"].set_sensitive(False)
106
107     def _createChildAlbum(self, *unused):
108         dialog = AlbumDialog("Create album")
109         dialog.run(self._createAlbumHelper)
110
111     def _registerImages(self, *unused):
112         albumModel, iterator =  self.__albumView.get_selection().get_selected()
113         selectedAlbumId = albumModel.get_value(iterator, self.__COLUMN_ALBUM_ID)
114         selectedAlbum = env.shelf.getAlbum(selectedAlbumId)
115         dialog = RegisterImagesDialog(selectedAlbum)
116         if dialog.run() == gtk.RESPONSE_OK:
117             self.__mainWindow.reload() # TODO: don't reload everything.
118         dialog.destroy()
119
120     def _generateHtml(self, *unused):
121         albumModel, iterator =  self.__albumView.get_selection().get_selected()
122         selectedAlbumId = albumModel.get_value(iterator, self.__COLUMN_ALBUM_ID)
123         selectedAlbum = env.shelf.getAlbum(selectedAlbumId)
124         self.__mainWindow.generateHtml(selectedAlbum)
125
126     def _createAlbumHelper(self, tag, desc):
127         newAlbum = env.shelf.createAlbum(tag)
128         if len(desc) > 0:
129             newAlbum.setAttribute(u"title", desc)
130         albumModel, iterator =  self.__albumView.get_selection().get_selected()
131         if iterator is None:
132             selectedAlbum = env.shelf.getRootAlbum()
133         else:
134             selectedAlbumId = albumModel.get_value(iterator, self.__COLUMN_ALBUM_ID)
135             selectedAlbum = env.shelf.getAlbum(selectedAlbumId)
136         children = list(selectedAlbum.getChildren())
137         children.append(newAlbum)
138         selectedAlbum.setChildren(children)
139         # TODO The whole tree should not be reloaded
140         self.loadAlbumTree()
141         # TODO update objectCollection?
142
143     def _destroyAlbum(self, *unused):
144         dialogId = "destroyAlbumsDialog"
145         widgets = gtk.glade.XML(env.gladeFile, dialogId)
146         dialog = widgets.get_widget(dialogId)
147         result = dialog.run()
148         if result == gtk.RESPONSE_OK:
149             albumModel, iterator =  self.__albumView.get_selection().get_selected()
150             selectedAlbumId = albumModel.get_value(iterator, self.__COLUMN_ALBUM_ID)
151             env.shelf.deleteAlbum(selectedAlbumId)
152             # TODO The whole tree should not be reloaded
153             self.loadAlbumTree()
154             # TODO update objectCollection?
155         dialog.destroy()
156
157     def _editAlbum(self, *unused):
158         albumModel, iterator =  self.__albumView.get_selection().get_selected()
159         selectedAlbumId = albumModel.get_value(iterator, self.__COLUMN_ALBUM_ID)
160         dialog = AlbumDialog("Edit album", selectedAlbumId)
161         dialog.run(self._editAlbumHelper)
162
163     def _editAlbumHelper(self, tag, desc):
164         albumModel, iterator =  self.__albumView.get_selection().get_selected()
165         selectedAlbumId = albumModel.get_value(iterator, self.__COLUMN_ALBUM_ID)
166         selectedAlbum = env.shelf.getAlbum(selectedAlbumId)
167         selectedAlbum.setTag(tag)
168         if len(desc) > 0:
169             selectedAlbum.setAttribute(u"title", desc)
170         else:
171             selectedAlbum.deleteAttribute(u"title")
172         # TODO The whole tree should not be reloaded
173         self.loadAlbumTree()
174         # TODO update objectCollection?
175
176     def _button_pressed_cb(self, treeView, event):
177         if event.button == 3:
178             self.__contextMenu.popup(None, None, None, event.button, event.time)
179             return True
180         else:
181             return False
182
183     def _treeViewFocusInEvent_cb(self, widget, event):
184         self._albumSelectionUpdated_cb(None, load=False)
185         for widgetName, function in [
186                 ("menubarCreateAlbumChild", self._createChildAlbum),
187                 ("menubarRegisterAndAddImages", self._registerImages),
188                 ("menubarGenerateHtml", self._generateHtml),
189                 ("menubarProperties", self._editAlbum),
190                 ("menubarDestroy", self._destroyAlbum),
191                 ]:
192             w = env.widgets[widgetName]
193             oid = w.connect("activate", function, None)
194             self._connectedOids.append((w, oid))
195
196     def _treeViewFocusOutEvent_cb(self, widget, event):
197         for (widget, oid) in self._connectedOids:
198             widget.disconnect(oid)
199         self._connectedOids = []
200         for widgetName in [
201                 "menubarCreateAlbumChild",
202                 "menubarRegisterAndAddImages",
203                 "menubarGenerateHtml",
204                 "menubarProperties",
205                 "menubarDestroy",
206                 ]:
207             env.widgets[widgetName].set_sensitive(False)
208
209 ###############################################################################
210 ### Private
211
212     __COLUMN_ALBUM_ID   = 0
213     __COLUMN_TAG        = 1
214     __COLUMN_TEXT       = 2
215     __COLUMN_TYPE       = 3
216     __COLUMN_SELECTABLE = 4
217
218     def __loadAlbumTreeHelper(self, parentAlbum=None, album=None, visited=None):
219         if visited is None:
220             visited = []
221         if not album:
222             album = env.shelf.getRootAlbum()
223         iterator = self.__albumModel.append(parentAlbum)
224         # TODO Do we have to use iterators here or can we use pygtks simplified syntax?
225         self.__albumModel.set_value(iterator, self.__COLUMN_ALBUM_ID, album.getId())
226         self.__albumModel.set_value(iterator, self.__COLUMN_TYPE, album.getType())
227         self.__albumModel.set_value(iterator, self.__COLUMN_TAG, album.getTag())
228         self.__albumModel.set_value(iterator, self.__COLUMN_SELECTABLE, True)
229         albumTitle = album.getAttribute(u"title")
230         if albumTitle == None or len(albumTitle) < 1:
231             self.__albumModel.set_value(iterator, self.__COLUMN_TEXT, album.getTag())
232         else:
233             self.__albumModel.set_value(iterator, self.__COLUMN_TEXT, albumTitle)
234         if album.getId() not in visited:
235             for child in album.getAlbumChildren():
236                 self.__loadAlbumTreeHelper(iterator, child, visited + [album.getId()])
237         else:
238             iterator = self.__albumModel.insert_before(iterator, None)
239             self.__albumModel.set_value(iterator, self.__COLUMN_TEXT, "[...]")
240             self.__albumModel.set_value(iterator, self.__COLUMN_SELECTABLE, False)
241
242     def __createContextMenu(self):
243         self.__menuGroup = MenuGroup()
244         self.__menuGroup.addMenuItem(
245             self.__createAlbumLabel, self._createChildAlbum)
246         self.__menuGroup.addMenuItem(
247             self.__registerImagesLabel, self._registerImages)
248         self.__menuGroup.addMenuItem(
249             self.__generateHtmlLabel, self._generateHtml)
250         self.__menuGroup.addMenuItem(
251             self.__destroyAlbumLabel, self._destroyAlbum)
252         self.__menuGroup.addStockImageMenuItem(
253             self.__editAlbumLabel,
254             gtk.STOCK_PROPERTIES,
255             self._editAlbum)
256         return self.__menuGroup.createGroupMenu()