4 from kofoto.alternative import Alternative
5 from kofoto.shelf import ImageVersionType
6 from kofoto.gkofoto.environment import env
7 from kofoto.gkofoto.menuhandler import MenuGroup
8 from kofoto.gkofoto.imageversionsdialog import ImageVersionsDialog
9 from kofoto.gkofoto.duplicateandopenimagedialog import DuplicateAndOpenImageDialog
10 from kofoto.gkofoto.fullscreenwindow import FullScreenWindow
12 _imageVersionTypeToStringMap = {
13 ImageVersionType.Important: "Important",
14 ImageVersionType.Original: "Original",
15 ImageVersionType.Other: "Other",
18 _rotationDirection = Alternative("Left", "Right")
20 class ImageVersionsList(gtk.ScrolledWindow):
21 def __init__(self, singleObjectView):
22 gtk.ScrolledWindow.__init__(self)
23 self.__menuGroup = None
24 self.__imageWidgetToImageVersion = None
25 self.__imageWidgetList = None
26 self.__selectedImageWidgets = None
27 self.__singleObjectView = singleObjectView
28 self.__vbox = gtk.VBox()
29 self.__vbox.set_border_width(5)
30 self.__vbox.set_spacing(10)
32 self.add_with_viewport(self.__vbox)
33 self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
35 self.__tooltips = gtk.Tooltips()
36 self.__recentlySelectedImageWidget = None
37 self.connect("focus-in-event", self.__focusInEventHandler_cb)
38 self.connect("focus-out-event", self.__focusOutEventHandler_cb)
39 self.__contextMenu = self.__createContextMenu()
43 ("menubarViewImageVersion", self.__view_cb),
44 ("menubarViewImageVersionsFullScreen", self.__view_full_screen_cb),
45 ("menubarCopyImageVersionLocations", self.__copyImageLocation_cb),
46 ("menubarOpenImageVersions", self.__open_cb),
47 ("menubarDuplicateAndOpenImageVersion", self.__duplicateAndOpen_cb),
48 ("menubarRotateImageVersionLeft", self.__rotateLeft_cb),
49 ("menubarRotateImageVersionRight", self.__rotateRight_cb),
50 ("menubarSplitToIndependentImages", self.__split_cb),
51 ("menubarDestroyImageVersion", self.__destroy_cb),
52 ("menubarEditImageVersionProperties", self.__editProperties_cb),
54 for widgetName, callback in callbacks:
55 env.widgets[widgetName].connect("activate", callback)
58 for widget in self.__vbox.get_children():
59 self.__vbox.remove(widget)
60 self.__imageWidgetList = []
61 self.__imageWidgetToImageVersion = {}
62 self.__selectedImageWidgets = set()
65 def loadImage(self, image):
68 self.__tooltips.enable()
69 for iv in image.getImageVersions():
71 vbox.set_border_width(3)
72 self.__vbox.pack_start(vbox, expand=False, fill=False)
73 thumbnail = gtk.Image()
75 thumbnailLocation = env.imageCache.get(iv, 128, 128)[0]
76 thumbnail.set_from_file(thumbnailLocation)
77 except (IOError, OSError):
78 thumbnail.set_from_pixbuf(env.unknownImageIconPixbuf)
79 alignment = gtk.Alignment(0.5, 0.5, 0.5, 0.5)
80 alignment.add(thumbnail)
81 alignment.set_padding(5, 5, 5, 5)
82 eventbox = gtk.EventBox()
83 eventbox.add(alignment)
85 "button-press-event", self.__mouseButtonPressed_cb)
86 eventbox.connect_after(
87 "expose-event", self.__imageWidgetExposed_cb)
88 tooltipText = "Location: " + iv.getLocation()
90 tooltipText += "\nComment: " + iv.getComment()
91 self.__tooltips.set_tip(eventbox, tooltipText)
94 vbox.add(gtk.Label("Primary"))
95 vbox.add(gtk.Label(_imageVersionTypeToStringMap[iv.getType()]))
96 self.__imageWidgetList.append(eventbox)
97 self.__imageWidgetToImageVersion[eventbox] = iv
98 self.__vbox.show_all()
101 self.loadImage(self.__image)
103 def __createContextMenu(self):
105 menugroup = MenuGroup()
106 menugroup.addMenuItem(
109 menugroup.addMenuItem(
110 "View in full screen mode",
111 self.__view_full_screen_cb)
112 menugroup.addMenuItem(
113 "Copy image version location(s)",
114 self.__copyImageLocation_cb)
115 menugroup.addStockImageMenuItem(
116 "Open image version(s) in external program...",
119 menugroup.addStockImageMenuItem(
120 "Duplicate and open image version(s) in external program...",
122 self.__duplicateAndOpen_cb)
123 menugroup.addImageMenuItem(
125 os.path.join(env.iconDir, "rotateleft.png"),
126 self.__rotateLeft_cb)
127 menugroup.addImageMenuItem(
129 os.path.join(env.iconDir, "rotateright.png"),
130 self.__rotateRight_cb)
131 menugroup.addSeparator()
132 menugroup.addMenuItem(
133 "Split to independent image(s)",
135 menugroup.addSeparator()
136 menugroup.addStockImageMenuItem(
140 menugroup.addStockImageMenuItem(
141 "Edit properties...",
142 gtk.STOCK_PROPERTIES,
143 self.__editProperties_cb)
144 for item in menugroup:
146 self.__menuGroup = menugroup
149 def __updateMenus(self):
150 zeroSelected = len(self.__selectedImageWidgets) == 0
151 oneSelected = len(self.__selectedImageWidgets) == 1
153 len(self.__selectedImageWidgets) == len(self.__imageWidgetList))
155 env.widgets["menubarViewImageVersion"].set_sensitive(
157 env.widgets["menubarViewImageVersionsFullScreen"].set_sensitive(
159 env.widgets["menubarCopyImageVersionLocations"].set_sensitive(
161 env.widgets["menubarOpenImageVersions"].set_sensitive(
163 env.widgets["menubarDuplicateAndOpenImageVersion"].set_sensitive(
165 env.widgets["menubarRotateImageVersionLeft"].set_sensitive(
167 env.widgets["menubarRotateImageVersionRight"].set_sensitive(
169 env.widgets["menubarSplitToIndependentImages"].set_sensitive(
170 not zeroSelected and not allSelected)
171 env.widgets["menubarDestroyImageVersion"].set_sensitive(
173 env.widgets["menubarEditImageVersionProperties"].set_sensitive(
177 self.__menuGroup.disable()
179 self.__menuGroup.enable()
181 self.__menuGroup["View"].set_sensitive(False)
183 ("Duplicate and open image version(s) in external"
185 ].set_sensitive(False)
187 self.__menuGroup["Split to independent image(s)"].set_sensitive(False)
189 def __mouseButtonPressed_cb(self, widget, event):
190 def selectWidget(widget):
191 widget.set_state(gtk.STATE_SELECTED)
192 self.__selectedImageWidgets.add(widget)
193 def unselectWidget(widget):
194 widget.set_state(gtk.STATE_NORMAL)
195 self.__selectedImageWidgets.remove(widget)
196 def selectOnlyThis():
197 for x in list(self.__selectedImageWidgets):
203 if widget in self.__selectedImageWidgets:
204 unselectWidget(widget)
207 def extendSelection():
208 otherIndex = self.__imageWidgetList.index(
209 self.__recentlySelectedImageWidget)
210 thisIndex = self.__imageWidgetList.index(widget)
212 min(otherIndex, thisIndex), max(otherIndex, thisIndex) + 1):
213 selectWidget(self.__imageWidgetList[x])
216 if event.button == 1:
217 if event.type == gtk.gdk.BUTTON_PRESS:
218 if event.state & gtk.gdk.CONTROL_MASK:
220 elif event.state & gtk.gdk.SHIFT_MASK:
224 elif event.type == gtk.gdk._2BUTTON_PRESS:
226 gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK) == 0):
228 elif event.button == 3:
229 if widget in self.__selectedImageWidgets:
233 self.__contextMenu.popup(
234 None, None, None, event.button, event.time)
235 if self.__recentlySelectedImageWidget:
236 self.__recentlySelectedImageWidget.queue_draw()
237 self.__recentlySelectedImageWidget = widget
240 def __imageWidgetExposed_cb(self, widget, event):
241 if widget == self.__recentlySelectedImageWidget:
242 state = gtk.STATE_SELECTED
243 allocation = widget.get_allocation()
244 widget.style.paint_focus(
245 widget.window, state, None, widget, "",
246 2, 2, allocation.width - 4, allocation.height - 4)
248 state = gtk.STATE_NORMAL
250 def __view_cb(self, *args):
251 assert len(self.__selectedImageWidgets) == 1
252 widget = list(self.__selectedImageWidgets)[0]
253 imageVersion = self.__imageWidgetToImageVersion[widget]
254 location = imageVersion.getLocation()
255 self.__singleObjectView._loadImageAtLocation(location, False)
257 def __view_full_screen_cb(self, *args):
258 assert len(self.__selectedImageWidgets) > 0
259 widgets = self.__getSelectedImageVersionsInOrder()
262 self.__imageWidgetToImageVersion[x] for x in widgets]
263 window = FullScreenWindow(imageVersions)
266 self.__imageWidgetToImageVersion[x]
267 for x in self.__imageWidgetList]
268 index = self.__imageWidgetList.index(widgets[0])
269 window = FullScreenWindow(imageVersions, index)
272 def __copyImageLocation_cb(self, widget, param):
273 assert len(self.__selectedImageWidgets) > 0
274 clipboard = gtk.clipboard_get(gtk.gdk.SELECTION_CLIPBOARD)
275 primary = gtk.clipboard_get(gtk.gdk.SELECTION_PRIMARY)
276 location = "\n".join(
277 [self.__imageWidgetToImageVersion[x].getLocation()
278 for x in self.__getSelectedImageVersionsInOrder()])
279 clipboard.set_text(location)
280 primary.set_text(location)
282 def __open_cb(self, *args):
283 assert len(self.__selectedImageWidgets) > 0
285 self.__imageWidgetToImageVersion[x].getLocation()
286 for x in self.__getSelectedImageVersionsInOrder()]
287 command = env.openCommand % {"locations": " ".join(locations)}
289 result = subprocess.call(command, shell=True)
293 dialog = gtk.MessageDialog(
294 type=gtk.MESSAGE_ERROR,
295 buttons=gtk.BUTTONS_OK,
296 message_format="Failed to execute command: \"%s\"" % command)
300 def __duplicateAndOpen_cb(self, *args):
301 assert len(self.__selectedImageWidgets) == 1
302 imageWidget = list(self.__selectedImageWidgets)[0]
303 imageVersion = self.__imageWidgetToImageVersion[imageWidget]
304 dialog = DuplicateAndOpenImageDialog()
305 dialog.run(imageVersion)
307 def __split_cb(self, *args):
308 assert len(self.__selectedImageWidgets) > 0
309 assert len(self.__selectedImageWidgets) < len(self.__imageWidgetList)
310 for widget in self.__selectedImageWidgets:
311 imageVersion = self.__imageWidgetToImageVersion[widget]
312 image = env.shelf.createImage()
313 imageVersion.setImage(image)
314 for key, value in self.__image.getAttributeMap().items():
315 image.setAttribute(key, value)
316 for category in self.__image.getCategories():
317 image.addCategory(category)
318 self.__singleObjectView.reload()
320 def __rotateLeft_cb(self, *args):
321 assert len(self.__selectedImageWidgets) > 0
322 self.__rotate(_rotationDirection.Left)
324 def __rotateRight_cb(self, *args):
325 assert len(self.__selectedImageWidgets) > 0
326 self.__rotate(_rotationDirection.Right)
328 def __destroy_cb(self, *args):
329 assert len(self.__selectedImageWidgets) > 0
330 widgets = gtk.glade.XML(env.gladeFile, "destroyImageVersionsDialog")
331 dialog = widgets.get_widget("destroyImageVersionsDialog")
332 result = dialog.run()
333 if result == gtk.RESPONSE_OK:
334 checkbutton = widgets.get_widget("deleteImageFilesCheckbutton")
335 deleteFiles = checkbutton.get_active()
336 for widget in self.__selectedImageWidgets:
337 imageVersion = self.__imageWidgetToImageVersion[widget]
340 os.remove(imageVersion.getLocation())
341 # TODO: Delete from image cache too?
344 env.shelf.deleteImageVersion(imageVersion.getId())
345 self.__singleObjectView.reload()
348 def __editProperties_cb(self, *args):
349 assert len(self.__selectedImageWidgets) > 0
350 dialog = ImageVersionsDialog(self.__singleObjectView._objectCollection)
351 dialog.runViewImageVersions(self.__image)
352 self.__singleObjectView.reload()
354 def __focusInEventHandler_cb(self, widget, event):
355 for x in self.__selectedImageWidgets:
356 x.set_state(gtk.STATE_SELECTED)
358 def __focusOutEventHandler_cb(self, widget, event):
359 for x in self.__selectedImageWidgets:
360 x.set_state(gtk.STATE_ACTIVE)
362 def __rotate(self, rotationDirection):
363 for widget in self.__selectedImageWidgets:
364 imageVersion = self.__imageWidgetToImageVersion[widget]
365 if rotationDirection == _rotationDirection.Left:
366 rotateCommand = env.rotateLeftCommand
367 elif rotationDirection == _rotationDirection.Right:
368 rotateCommand = env.rotateRightCommand
372 location = imageVersion.getLocation()
373 env.pixbufLoader.unload_all(location)
374 command = rotateCommand % {"location": location}
376 result = subprocess.call(command, shell=True)
380 dialog = gtk.MessageDialog(
381 type=gtk.MESSAGE_ERROR,
382 buttons=gtk.BUTTONS_OK,
384 "Failed to execute command: \"%s\"" % command)
388 imageVersion.contentChanged()
389 self.__singleObjectView.reload()
391 def __getSelectedImageVersionsInOrder(self):
394 for x in self.__imageWidgetList
395 if x in self.__selectedImageWidgets]