Codebase list mirage / be9925a
Format Python files with Black Thomas Ross 4 years ago
2 changed file(s) with 6619 addition(s) and 4475 deletion(s). Raw diff Collapse all Expand all
+6514
-4404
mirage.py less more
2020 """
2121
2222 import pygtk
23 pygtk.require('2.0')
23
24 pygtk.require("2.0")
2425 import gtk
2526 import os, sys, getopt, configparser, string, gc
2627 import random, urllib.request, gobject, gettext, locale
2728 import stat, time, subprocess, shutil, filecmp
2829 import tempfile, socket, threading
30
2931 try:
30 import hashlib
31 HAS_HASHLIB = True
32 import hashlib
33
34 HAS_HASHLIB = True
3235 except:
33 HAS_HASHLIB= False
34 import md5
36 HAS_HASHLIB = False
37 import md5
38
3539 try:
36 import imgfuncs
37 HAS_IMGFUNCS = True
40 import imgfuncs
41
42 HAS_IMGFUNCS = True
3843 except:
39 HAS_IMGFUNCS = False
40 print("imgfuncs.so module not found, rotating/flipping images will be disabled.")
44 HAS_IMGFUNCS = False
45 print("imgfuncs.so module not found, rotating/flipping images will be disabled.")
46
4147 try:
42 import xmouse
43 HAS_XMOUSE = True
48 import xmouse
49
50 HAS_XMOUSE = True
4451 except:
45 HAS_XMOUSE = False
46 print("xmouse.so module not found, some screenshot capabilities will be disabled.")
52 HAS_XMOUSE = False
53 print("xmouse.so module not found, some screenshot capabilities will be disabled.")
54
4755 try:
48 import gconf
56 import gconf
4957 except:
50 pass
58 pass
5159
5260 if gtk.gtk_version < (2, 10, 0):
53 sys.stderr.write("Mirage requires GTK+ 2.10.0 or newer..\n")
54 sys.exit(1)
61 sys.stderr.write("Mirage requires GTK+ 2.10.0 or newer..\n")
62 sys.exit(1)
5563 if gtk.pygtk_version < (2, 12, 0):
56 sys.stderr.write("Mirage requires PyGTK 2.12.0 or newer.\n")
57 sys.exit(1)
58
64 sys.stderr.write("Mirage requires PyGTK 2.12.0 or newer.\n")
65 sys.exit(1)
66
67
5968 def valid_int(inputstring):
60 try:
61 x = int(inputstring)
62 return True
63 except:
64 return False
69 try:
70 x = int(inputstring)
71 return True
72 except:
73 return False
74
6575
6676 class Base:
67
68 def __init__(self):
69
70 gtk.gdk.threads_init()
71
72 # FIX THIS! Does not work on windows and what happens if mo-files exists
73 # in both dirs?
74 gettext.install('mirage', '/usr/share/locale')
75 gettext.install('mirage', '/usr/local/share/locale')
76
77 # Constants
78 self.open_mode_smart = 0
79 self.open_mode_fit = 1
80 self.open_mode_1to1 = 2
81 self.open_mode_last = 3
82 self.min_zoomratio = 0.02
83
84 # Initialize vars:
85 width=600
86 height=400
87 bgcolor_found = False
88 self.simple_bgcolor = False
89 # Current image:
90 self.curr_img_in_list = 0
91 self.currimg_name = ""
92 self.currimg_width = 0
93 self.currimg_height = 0
94 self.currimg_pixbuf = None
95 self.currimg_pixbuf_original = None
96 self.currimg_zoomratio = 1
97 self.currimg_is_animation = False
98 # This is the actual pixbuf that is loaded in Mirage. This will
99 # usually be the same as self.curr_img_in_list except for scenarios
100 # like when the user presses 'next image' multiple times in a row.
101 # In this case, self.curr_img_in_list will increment while
102 # self.loaded_img_in_list will retain the current loaded image.
103 self.loaded_img_in_list = 0
104 # Next preloaded image:
105 self.preloadimg_next_in_list = -1
106 self.preloadimg_next_name = ""
107 self.preloadimg_next_width = 0
108 self.preloadimg_next_height = 0
109 self.preloadimg_next_pixbuf = None
110 self.preloadimg_next_pixbuf_original = None
111 self.preloadimg_next_zoomratio = 1
112 self.preloadimg_next_is_animation = False
113 # Previous preloaded image:
114 self.preloadimg_prev_in_list = -1
115 self.preloadimg_prev_name = ""
116 self.preloadimg_prev_width = 0
117 self.preloadimg_prev_height = 0
118 self.preloadimg_prev_pixbuf = None
119 self.preloadimg_prev_pixbuf_original = None
120 self.preloadimg_prev_zoomratio = 1
121 self.preloadimg_prev_is_animation = False
122 # Settings, misc:
123 self.toolbar_show = True
124 self.thumbpane_show = True
125 self.statusbar_show = True
126 self.fullscreen_mode = False
127 self.opendialogpath = ""
128 self.zoom_quality = gtk.gdk.INTERP_BILINEAR
129 self.recursive = False
130 self.verbose = False
131 self.image_loaded = False
132 self.open_all_images = True # open all images in the directory(ies)
133 self.use_last_dir = True
134 self.last_dir = os.path.expanduser("~")
135 self.fixed_dir = os.path.expanduser("~")
136 self.image_list = []
137 self.open_mode = self.open_mode_smart
138 self.last_mode = self.open_mode_smart
139 self.listwrap_mode = 0 # 0=no, 1=yes, 2=ask
140 self.user_prompt_visible = False # the "wrap?" prompt
141 self.slideshow_delay = 1 # seconds
142 self.slideshow_mode = False
143 self.slideshow_random = False
144 self.slideshow_controls_visible = False # fullscreen slideshow controls
145 self.controls_moving = False
146 self.zoomvalue = 2
147 self.quality_save = 90
148 self.updating_adjustments = False
149 self.disable_screensaver = False
150 self.slideshow_in_fullscreen = False
151 self.closing_app = False
152 self.confirm_delete = True
153 self.preloading_images = True
154 self.action_names = ["Open in GIMP", "Create Thumbnail", "Create Thumbnails", "Move to Favorites"]
155 self.action_shortcuts = ["<Control>e", "<Alt>t", "<Control><Alt>t", "<Control><Alt>f"]
156 self.action_commands = ["gimp-remote-2.4 %F", "convert %F -thumbnail 150x150 %Pt_%N.jpg", "convert %F -thumbnail 150x150 %Pt_%N.jpg", "mkdir -p ~/mirage-favs; mv %F ~/mirage-favs; [NEXT]"]
157 self.action_batch = [False, False, True, False]
158 self.onload_cmd = None
159 self.searching_for_images = False
160 self.preserve_aspect = True
161 self.ignore_preserve_aspect_callback = False
162 self.savemode = 2
163 self.image_modified = False
164 self.image_zoomed = False
165 self.start_in_fullscreen = False
166 self.running_custom_actions = False
167 self.merge_id = None
168 self.actionGroupCustom = None
169 self.merge_id_recent = None
170 self.actionGroupRecent = None
171 self.open_hidden_files = False
172 self.thumbnail_sizes = ["128", "96", "72", "64", "48", "32"]
173 self.thumbnail_size = 128 # Default to 128 x 128
174 self.thumbnail_loaded = []
175 self.thumbpane_updating = False
176 self.recentfiles = ["", "", "", "", ""]
177 self.screenshot_delay = 2
178 self.thumbpane_bottom_coord_loaded = 0
179
180 # Read any passed options/arguments:
181 try:
182 opts, args = getopt.getopt(sys.argv[1:], "hRvVsfo:", ["help", "version", "recursive", "verbose", "slideshow", "fullscreen", "onload="])
183 except getopt.GetoptError:
184 # print help information and exit:
185 self.print_usage()
186 sys.exit(2)
187 # If options were passed, perform action on them.
188 if opts != []:
189 for o, a in opts:
190 if o in ("-v", "--version"):
191 self.print_version()
192 sys.exit(2)
193 elif o in ("-h", "--help"):
194 self.print_usage()
195 sys.exit(2)
196 elif o in ("-R", "--recursive"):
197 self.recursive = True
198 elif o in ("-V", "--verbose"):
199 self.verbose = True
200 elif o in ("-s", "--slideshow", "-f", "--fullscreen"):
201 #This will be handled later
202 None
203 elif o in ("-o", "--onload"):
204 self.onload_cmd = a
205 else:
206 self.print_usage()
207 sys.exit(2)
208
209
210 # Determine config dir, first try the environment variable XDG_CONFIG_HOME
211 # according to XDG specification and as a fallback use ~/.config/mirage
212 self.config_dir = (os.getenv('XDG_CONFIG_HOME') or os.path.expanduser('~/.config')) + '/mirage'
213 # Load config from disk:
214 conf = configparser.ConfigParser()
215 if os.path.isfile(self.config_dir + '/miragerc'):
216 conf.read(self.config_dir + '/miragerc')
217 if conf.has_option('window', 'w'):
218 width = conf.getint('window', 'w')
219 if conf.has_option('window', 'h'):
220 height = conf.getint('window', 'h')
221 if conf.has_option('window', 'toolbar'):
222 self.toolbar_show = conf.getboolean('window', 'toolbar')
223 if conf.has_option('window', 'statusbar'):
224 self.statusbar_show = conf.getboolean('window', 'statusbar')
225 if conf.has_option('window', 'thumbpane'):
226 self.thumbpane_show = conf.getboolean('window', 'thumbpane')
227 if conf.has_option('prefs', 'simple-bgcolor'):
228 self.simple_bgcolor = conf.getboolean('prefs', 'simple-bgcolor')
229 if conf.has_option('prefs', 'bgcolor-red'):
230 bgr = conf.getint('prefs', 'bgcolor-red')
231 bgg = conf.getint('prefs', 'bgcolor-green')
232 bgb = conf.getint('prefs', 'bgcolor-blue')
233 bgcolor_found = True
234 self.bgcolor = gtk.gdk.Color(red=bgr, green=bgg, blue=bgb)
235 if conf.has_option('prefs', 'use_last_dir'):
236 self.use_last_dir = conf.getboolean('prefs', 'use_last_dir')
237 if conf.has_option('prefs', 'last_dir'):
238 self.last_dir = conf.get('prefs', 'last_dir')
239 if conf.has_option('prefs', 'fixed_dir'):
240 self.fixed_dir = conf.get('prefs', 'fixed_dir')
241 if conf.has_option('prefs', 'open_all'):
242 self.open_all_images = conf.getboolean('prefs', 'open_all')
243 if conf.has_option('prefs', 'hidden'):
244 self.open_hidden_files = conf.getboolean('prefs', 'hidden')
245 if conf.has_option('prefs', 'open_mode'):
246 self.open_mode = conf.getint('prefs', 'open_mode')
247 if conf.has_option('prefs', 'last_mode'):
248 self.last_mode = conf.getint('prefs', 'last_mode')
249 if conf.has_option('prefs', 'listwrap_mode'):
250 self.listwrap_mode = conf.getint('prefs', 'listwrap_mode')
251 if conf.has_option('prefs', 'slideshow_delay'):
252 self.slideshow_delay = conf.getint('prefs', 'slideshow_delay')
253 if conf.has_option('prefs', 'slideshow_random'):
254 self.slideshow_random = conf.getboolean('prefs', 'slideshow_random')
255 if conf.has_option('prefs', 'zoomquality'):
256 self.zoomvalue = conf.getint('prefs', 'zoomquality')
257 if int(round(self.zoomvalue, 0)) == 0:
258 self.zoom_quality = gtk.gdk.INTERP_NEAREST
259 elif int(round(self.zoomvalue, 0)) == 1:
260 self.zoom_quality = gtk.gdk.INTERP_TILES
261 elif int(round(self.zoomvalue, 0)) == 2:
262 self.zoom_quality = gtk.gdk.INTERP_BILINEAR
263 elif int(round(self.zoomvalue, 0)) == 3:
264 self.zoom_quality = gtk.gdk.INTERP_HYPER
265 if conf.has_option('prefs', 'quality_save'):
266 self.quality_save = conf.getint('prefs', 'quality_save')
267 if conf.has_option('prefs', 'disable_screensaver'):
268 self.disable_screensaver = conf.getboolean('prefs', 'disable_screensaver')
269 if conf.has_option('prefs', 'slideshow_in_fullscreen'):
270 self.slideshow_in_fullscreen = conf.getboolean('prefs', 'slideshow_in_fullscreen')
271 if conf.has_option('prefs', 'preloading_images'):
272 self.preloading_images = conf.getboolean('prefs', 'preloading_images')
273 if conf.has_option('prefs', 'thumbsize'):
274 self.thumbnail_size = conf.getint('prefs', 'thumbsize')
275 if conf.has_option('prefs', 'screenshot_delay'):
276 self.screenshot_delay = conf.getint('prefs', 'screenshot_delay')
277 if conf.has_option('actions', 'num_actions'):
278 num_actions = conf.getint('actions', 'num_actions')
279 self.action_names = []
280 self.action_commands = []
281 self.action_shortcuts = []
282 self.action_batch = []
283 for i in range(num_actions):
284 if conf.has_option('actions', 'names[' + str(i) + ']') and conf.has_option('actions', 'commands[' + str(i) + ']') and conf.has_option('actions', 'shortcuts[' + str(i) + ']') and conf.has_option('actions', 'batch[' + str(i) + ']'):
285 self.action_names.append(conf.get('actions', 'names[' + str(i) + ']'))
286 self.action_commands.append(conf.get('actions', 'commands[' + str(i) + ']'))
287 self.action_shortcuts.append(conf.get('actions', 'shortcuts[' + str(i) + ']'))
288 self.action_batch.append(conf.getboolean('actions', 'batch[' + str(i) + ']'))
289 if conf.has_option('prefs', 'savemode'):
290 self.savemode = conf.getint('prefs', 'savemode')
291 if conf.has_option('prefs', 'start_in_fullscreen'):
292 self.start_in_fullscreen = conf.getboolean('prefs', 'start_in_fullscreen')
293 if conf.has_option('prefs', 'confirm_delete'):
294 self.confirm_delete = conf.getboolean('prefs', 'confirm_delete')
295 self.recentfiles = []
296 if conf.has_option('recent', 'num_recent'):
297 num_recent = conf.getint('recent', 'num_recent')
298 for i in range(num_recent):
299 self.recentfiles.append('')
300 if conf.has_option('recent', 'urls[' + str(i) + ',0]'):
301 self.recentfiles[i] = conf.get('recent', 'urls[' + str(i) + ',0]')
302 # slideshow_delay is the user's preference, whereas curr_slideshow_delay is
303 # the current delay (which can be changed without affecting the 'default')
304 self.curr_slideshow_delay = self.slideshow_delay
305 # Same for randomization:
306 self.curr_slideshow_random = self.slideshow_random
307
308 # Read accel_map file, if it exists
309 if os.path.isfile(self.config_dir + '/accel_map'):
310 gtk.accel_map_load(self.config_dir + '/accel_map')
311
312 # Directory/ies in which to find application images/pixmaps
313 self.resource_path_list = False
314
315 self.blank_image = gtk.gdk.pixbuf_new_from_file(self.find_path("mirage_blank.png"))
316
317 # Define the main menubar and toolbar:
318 factory = gtk.IconFactory()
319 iconname = 'stock_leave-fullscreen.png'
320 iconname2 = 'stock_fullscreen.png'
321 leave_fullscreen_icon_path = self.find_path(iconname)
322 pixbuf = gtk.gdk.pixbuf_new_from_file(leave_fullscreen_icon_path)
323 iconset = gtk.IconSet(pixbuf)
324 factory.add('leave-fullscreen', iconset)
325 factory.add_default()
326 fullscreen_icon_path = self.find_path(iconname2)
327 pixbuf = gtk.gdk.pixbuf_new_from_file(fullscreen_icon_path)
328 iconset = gtk.IconSet(pixbuf)
329 factory.add('fullscreen', iconset)
330 factory.add_default()
331 try:
332 test = gtk.Button("", gtk.STOCK_LEAVE_FULLSCREEN)
333 leave_fullscreen_icon = gtk.STOCK_LEAVE_FULLSCREEN
334 fullscreen_icon = gtk.STOCK_FULLSCREEN
335 except:
336 # This will allow gtk 2.6 users to run Mirage
337 leave_fullscreen_icon = 'leave-fullscreen'
338 fullscreen_icon = 'fullscreen'
339 actions = (
340 ('FileMenu', None, _('_File')),
341 ('EditMenu', None, _('_Edit')),
342 ('ViewMenu', None, _('_View')),
343 ('GoMenu', None, _('_Go')),
344 ('HelpMenu', None, _('_Help')),
345 ('ActionSubMenu', None, _('Custom _Actions')),
346 ('Open Image', gtk.STOCK_FILE, _('_Open Image...'), '<Ctrl>O', _('Open Image'), self.open_file),
347 ('Open Remote Image', gtk.STOCK_NETWORK, _('Open _Remote image...'), None, _('Open Remote Image'), self.open_file_remote),
348 ('Open Folder', gtk.STOCK_DIRECTORY, _('Open _Folder...'), '<Ctrl>F', _('Open Folder'), self.open_folder),
349 ('Save', gtk.STOCK_SAVE, _('_Save Image'), '<Ctrl>S', _('Save Image'), self.save_image),
350 ('Save As', gtk.STOCK_SAVE, _('Save Image _As...'), '<Shift><Ctrl>S', _('Save Image As'), self.save_image_as),
351 ('Crop', None, _('_Crop...'), None, _('Crop Image'), self.crop_image),
352 ('Resize', None, _('R_esize...'), None, _('Resize Image'), self.resize_image),
353 ('Saturation', None, _('_Saturation...'), None, _('Modify saturation'), self.saturation),
354 ('Quit', gtk.STOCK_QUIT, _('_Quit'), '<Ctrl>Q', _('Quit'), self.exit_app),
355 ('Previous Image', gtk.STOCK_GO_BACK, _('_Previous Image'), 'Left', _('Previous Image'), self.goto_prev_image),
356 ('Next Image', gtk.STOCK_GO_FORWARD, _('_Next Image'), 'Right', _('Next Image'), self.goto_next_image),
357 ('Previous2', gtk.STOCK_GO_BACK, _('_Previous'), 'Left', _('Previous'), self.goto_prev_image),
358 ('Next2', gtk.STOCK_GO_FORWARD, _('_Next'), 'Right', _('Next'), self.goto_next_image),
359 ('Random Image', None, _('_Random Image'), 'R', _('Random Image'), self.goto_random_image),
360 ('First Image', gtk.STOCK_GOTO_FIRST, _('_First Image'), 'Home', _('First Image'), self.goto_first_image),
361 ('Last Image', gtk.STOCK_GOTO_LAST, _('_Last Image'), 'End', _('Last Image'), self.goto_last_image),
362 ('In', gtk.STOCK_ZOOM_IN, _('Zoom _In'), '<Ctrl>Up', _('Zoom In'), self.zoom_in),
363 ('Out', gtk.STOCK_ZOOM_OUT, _('Zoom _Out'), '<Ctrl>Down', _('Zoom Out'), self.zoom_out),
364 ('Fit', gtk.STOCK_ZOOM_FIT, _('Zoom To _Fit'), '<Ctrl>0', _('Fit'), self.zoom_to_fit_window_action),
365 ('1:1', gtk.STOCK_ZOOM_100, _('_1:1'), '<Ctrl>1', _('1:1'), self.zoom_1_to_1_action),
366 ('Rotate Left', None, _('Rotate _Left'), '<Ctrl>Left', _('Rotate Left'), self.rotate_left),
367 ('Rotate Right', None, _('Rotate _Right'), '<Ctrl>Right', _('Rotate Right'), self.rotate_right),
368 ('Flip Vertically', None, _('Flip _Vertically'), '<Ctrl>V', _('Flip Vertically'), self.flip_image_vert),
369 ('Flip Horizontally', None, _('Flip _Horizontally'), '<Ctrl>H', _('Flip Horizontally'), self.flip_image_horiz),
370 ('About', gtk.STOCK_ABOUT, _('_About'), None, _('About'), self.show_about),
371 ('Contents', gtk.STOCK_HELP, _('_Contents'), 'F1', _('Contents'), self.show_help),
372 ('Preferences', gtk.STOCK_PREFERENCES, _('_Preferences...'), '<Ctrl>P', _('Preferences'), self.show_prefs),
373 ('Full Screen', fullscreen_icon, _('_Full Screen'), 'F11', _('Full Screen'), self.enter_fullscreen),
374 ('Exit Full Screen', leave_fullscreen_icon, _('E_xit Full Screen'), None, _('Exit Full Screen'), self.leave_fullscreen),
375 ('Start Slideshow', gtk.STOCK_MEDIA_PLAY, _('_Start Slideshow'), 'F5', _('Start Slideshow'), self.toggle_slideshow),
376 ('Stop Slideshow', gtk.STOCK_MEDIA_STOP, _('_Stop Slideshow'), 'F5', _('Stop Slideshow'), self.toggle_slideshow),
377 ('Delete Image', gtk.STOCK_DELETE, _('_Delete...'), 'Delete', _('Delete Image'), self.delete_image),
378 ('Rename Image', None, _('Re_name...'), 'F2', _('Rename Image'), self.rename_image),
379 ('Take Screenshot', None, _('_Take Screenshot...'), None, _('Take Screenshot'), self.screenshot),
380 ('Properties', gtk.STOCK_PROPERTIES, _('_Properties...'), None, _('Properties'), self.show_properties),
381 ('Custom Actions', None, _('_Configure...'), None, _('Custom Actions'), self.show_custom_actions),
382 ('MiscKeysMenuHidden', None, 'Keys'),
383 ('Escape', None, '', 'Escape', _('Exit Full Screen'), self.leave_fullscreen),
384 ('Minus', None, '', 'minus', _('Zoom Out'), self.zoom_out),
385 ('Plus', None, '', 'plus', _('Zoom In'), self.zoom_in),
386 ('Equal', None, '', 'equal', _('Zoom In'), self.zoom_in),
387 ('Space', None, '', 'space', _('Next Image'), self.goto_next_image),
388 ('Ctrl-KP_Insert', None, '', '<Ctrl>KP_Insert', _('Fit'), self.zoom_to_fit_window_action),
389 ('Ctrl-KP_End', None, '', '<Ctrl>KP_End', _('1:1'), self.zoom_1_to_1_action),
390 ('Ctrl-KP_Subtract', None, '', '<Ctrl>KP_Subtract', _('Zoom Out'), self.zoom_out),
391 ('Ctrl-KP_Add', None, '', '<Ctrl>KP_Add', _('Zoom In'), self.zoom_in),
392 ('Ctrl-KP_0', None, '', '<Ctrl>KP_0', _('Fit'), self.zoom_to_fit_window_action),
393 ('Ctrl-KP_1', None, '', '<Ctrl>KP_1', _('1:1'), self.zoom_1_to_1_action),
394 ('Full Screen Key', None, '', '<Shift>Return', None, self.enter_fullscreen),
395 ('Prev', None, '', 'Up', _('Previous Image'), self.goto_prev_image),
396 ('Next', None, '', 'Down', _('Next Image'), self.goto_next_image),
397 ('PgUp', None, '', 'Page_Up', _('Previous Image'), self.goto_prev_image),
398 ('PgDn', None, '', 'Page_Down', _('Next Image'), self.goto_next_image),
399 ('BackSpace', None, '', 'BackSpace', _('Previous Image'), self.goto_prev_image),
400 ('OriginalSize', None, '', '1', _('1:1'), self.zoom_1_to_1_action),
401 ('ZoomIn', None, '', 'KP_Add', _('Zoom In'), self.zoom_in),
402 ('ZoomOut', None, '', 'KP_Subtract', _('Zoom Out'), self.zoom_out)
403 )
404 toggle_actions = (
405 ('Status Bar', None, _('_Status Bar'), None, _('Status Bar'), self.toggle_status_bar, self.statusbar_show),
406 ('Toolbar', None, _('_Toolbar'), None, _('Toolbar'), self.toggle_toolbar, self.toolbar_show),
407 ('Thumbnails Pane', None, _('Thumbnails _Pane'), None, _('Thumbnails Pane'), self.toggle_thumbpane, self.thumbpane_show)
408 )
409
410 # Populate keys[]:
411 self.keys=[]
412 for i in range(len(actions)):
413 if len(actions[i]) > 3:
414 if actions[i][3] != None:
415 self.keys.append([actions[i][4], actions[i][3]])
416
417 uiDescription = """
77 def __init__(self):
78
79 gtk.gdk.threads_init()
80
81 # FIX THIS! Does not work on windows and what happens if mo-files exists
82 # in both dirs?
83 gettext.install("mirage", "/usr/share/locale")
84 gettext.install("mirage", "/usr/local/share/locale")
85
86 # Constants
87 self.open_mode_smart = 0
88 self.open_mode_fit = 1
89 self.open_mode_1to1 = 2
90 self.open_mode_last = 3
91 self.min_zoomratio = 0.02
92
93 # Initialize vars:
94 width = 600
95 height = 400
96 bgcolor_found = False
97 self.simple_bgcolor = False
98 # Current image:
99 self.curr_img_in_list = 0
100 self.currimg_name = ""
101 self.currimg_width = 0
102 self.currimg_height = 0
103 self.currimg_pixbuf = None
104 self.currimg_pixbuf_original = None
105 self.currimg_zoomratio = 1
106 self.currimg_is_animation = False
107 # This is the actual pixbuf that is loaded in Mirage. This will
108 # usually be the same as self.curr_img_in_list except for scenarios
109 # like when the user presses 'next image' multiple times in a row.
110 # In this case, self.curr_img_in_list will increment while
111 # self.loaded_img_in_list will retain the current loaded image.
112 self.loaded_img_in_list = 0
113 # Next preloaded image:
114 self.preloadimg_next_in_list = -1
115 self.preloadimg_next_name = ""
116 self.preloadimg_next_width = 0
117 self.preloadimg_next_height = 0
118 self.preloadimg_next_pixbuf = None
119 self.preloadimg_next_pixbuf_original = None
120 self.preloadimg_next_zoomratio = 1
121 self.preloadimg_next_is_animation = False
122 # Previous preloaded image:
123 self.preloadimg_prev_in_list = -1
124 self.preloadimg_prev_name = ""
125 self.preloadimg_prev_width = 0
126 self.preloadimg_prev_height = 0
127 self.preloadimg_prev_pixbuf = None
128 self.preloadimg_prev_pixbuf_original = None
129 self.preloadimg_prev_zoomratio = 1
130 self.preloadimg_prev_is_animation = False
131 # Settings, misc:
132 self.toolbar_show = True
133 self.thumbpane_show = True
134 self.statusbar_show = True
135 self.fullscreen_mode = False
136 self.opendialogpath = ""
137 self.zoom_quality = gtk.gdk.INTERP_BILINEAR
138 self.recursive = False
139 self.verbose = False
140 self.image_loaded = False
141 self.open_all_images = True # open all images in the directory(ies)
142 self.use_last_dir = True
143 self.last_dir = os.path.expanduser("~")
144 self.fixed_dir = os.path.expanduser("~")
145 self.image_list = []
146 self.open_mode = self.open_mode_smart
147 self.last_mode = self.open_mode_smart
148 self.listwrap_mode = 0 # 0=no, 1=yes, 2=ask
149 self.user_prompt_visible = False # the "wrap?" prompt
150 self.slideshow_delay = 1 # seconds
151 self.slideshow_mode = False
152 self.slideshow_random = False
153 self.slideshow_controls_visible = False # fullscreen slideshow controls
154 self.controls_moving = False
155 self.zoomvalue = 2
156 self.quality_save = 90
157 self.updating_adjustments = False
158 self.disable_screensaver = False
159 self.slideshow_in_fullscreen = False
160 self.closing_app = False
161 self.confirm_delete = True
162 self.preloading_images = True
163 self.action_names = [
164 "Open in GIMP",
165 "Create Thumbnail",
166 "Create Thumbnails",
167 "Move to Favorites",
168 ]
169 self.action_shortcuts = [
170 "<Control>e",
171 "<Alt>t",
172 "<Control><Alt>t",
173 "<Control><Alt>f",
174 ]
175 self.action_commands = [
176 "gimp-remote-2.4 %F",
177 "convert %F -thumbnail 150x150 %Pt_%N.jpg",
178 "convert %F -thumbnail 150x150 %Pt_%N.jpg",
179 "mkdir -p ~/mirage-favs; mv %F ~/mirage-favs; [NEXT]",
180 ]
181 self.action_batch = [False, False, True, False]
182 self.onload_cmd = None
183 self.searching_for_images = False
184 self.preserve_aspect = True
185 self.ignore_preserve_aspect_callback = False
186 self.savemode = 2
187 self.image_modified = False
188 self.image_zoomed = False
189 self.start_in_fullscreen = False
190 self.running_custom_actions = False
191 self.merge_id = None
192 self.actionGroupCustom = None
193 self.merge_id_recent = None
194 self.actionGroupRecent = None
195 self.open_hidden_files = False
196 self.thumbnail_sizes = ["128", "96", "72", "64", "48", "32"]
197 self.thumbnail_size = 128 # Default to 128 x 128
198 self.thumbnail_loaded = []
199 self.thumbpane_updating = False
200 self.recentfiles = ["", "", "", "", ""]
201 self.screenshot_delay = 2
202 self.thumbpane_bottom_coord_loaded = 0
203
204 # Read any passed options/arguments:
205 try:
206 opts, args = getopt.getopt(
207 sys.argv[1:],
208 "hRvVsfo:",
209 [
210 "help",
211 "version",
212 "recursive",
213 "verbose",
214 "slideshow",
215 "fullscreen",
216 "onload=",
217 ],
218 )
219 except getopt.GetoptError:
220 # print help information and exit:
221 self.print_usage()
222 sys.exit(2)
223 # If options were passed, perform action on them.
224 if opts != []:
225 for o, a in opts:
226 if o in ("-v", "--version"):
227 self.print_version()
228 sys.exit(2)
229 elif o in ("-h", "--help"):
230 self.print_usage()
231 sys.exit(2)
232 elif o in ("-R", "--recursive"):
233 self.recursive = True
234 elif o in ("-V", "--verbose"):
235 self.verbose = True
236 elif o in ("-s", "--slideshow", "-f", "--fullscreen"):
237 # This will be handled later
238 None
239 elif o in ("-o", "--onload"):
240 self.onload_cmd = a
241 else:
242 self.print_usage()
243 sys.exit(2)
244
245 # Determine config dir, first try the environment variable XDG_CONFIG_HOME
246 # according to XDG specification and as a fallback use ~/.config/mirage
247 self.config_dir = (
248 os.getenv("XDG_CONFIG_HOME") or os.path.expanduser("~/.config")
249 ) + "/mirage"
250 # Load config from disk:
251 conf = configparser.ConfigParser()
252 if os.path.isfile(self.config_dir + "/miragerc"):
253 conf.read(self.config_dir + "/miragerc")
254 if conf.has_option("window", "w"):
255 width = conf.getint("window", "w")
256 if conf.has_option("window", "h"):
257 height = conf.getint("window", "h")
258 if conf.has_option("window", "toolbar"):
259 self.toolbar_show = conf.getboolean("window", "toolbar")
260 if conf.has_option("window", "statusbar"):
261 self.statusbar_show = conf.getboolean("window", "statusbar")
262 if conf.has_option("window", "thumbpane"):
263 self.thumbpane_show = conf.getboolean("window", "thumbpane")
264 if conf.has_option("prefs", "simple-bgcolor"):
265 self.simple_bgcolor = conf.getboolean("prefs", "simple-bgcolor")
266 if conf.has_option("prefs", "bgcolor-red"):
267 bgr = conf.getint("prefs", "bgcolor-red")
268 bgg = conf.getint("prefs", "bgcolor-green")
269 bgb = conf.getint("prefs", "bgcolor-blue")
270 bgcolor_found = True
271 self.bgcolor = gtk.gdk.Color(red=bgr, green=bgg, blue=bgb)
272 if conf.has_option("prefs", "use_last_dir"):
273 self.use_last_dir = conf.getboolean("prefs", "use_last_dir")
274 if conf.has_option("prefs", "last_dir"):
275 self.last_dir = conf.get("prefs", "last_dir")
276 if conf.has_option("prefs", "fixed_dir"):
277 self.fixed_dir = conf.get("prefs", "fixed_dir")
278 if conf.has_option("prefs", "open_all"):
279 self.open_all_images = conf.getboolean("prefs", "open_all")
280 if conf.has_option("prefs", "hidden"):
281 self.open_hidden_files = conf.getboolean("prefs", "hidden")
282 if conf.has_option("prefs", "open_mode"):
283 self.open_mode = conf.getint("prefs", "open_mode")
284 if conf.has_option("prefs", "last_mode"):
285 self.last_mode = conf.getint("prefs", "last_mode")
286 if conf.has_option("prefs", "listwrap_mode"):
287 self.listwrap_mode = conf.getint("prefs", "listwrap_mode")
288 if conf.has_option("prefs", "slideshow_delay"):
289 self.slideshow_delay = conf.getint("prefs", "slideshow_delay")
290 if conf.has_option("prefs", "slideshow_random"):
291 self.slideshow_random = conf.getboolean("prefs", "slideshow_random")
292 if conf.has_option("prefs", "zoomquality"):
293 self.zoomvalue = conf.getint("prefs", "zoomquality")
294 if int(round(self.zoomvalue, 0)) == 0:
295 self.zoom_quality = gtk.gdk.INTERP_NEAREST
296 elif int(round(self.zoomvalue, 0)) == 1:
297 self.zoom_quality = gtk.gdk.INTERP_TILES
298 elif int(round(self.zoomvalue, 0)) == 2:
299 self.zoom_quality = gtk.gdk.INTERP_BILINEAR
300 elif int(round(self.zoomvalue, 0)) == 3:
301 self.zoom_quality = gtk.gdk.INTERP_HYPER
302 if conf.has_option("prefs", "quality_save"):
303 self.quality_save = conf.getint("prefs", "quality_save")
304 if conf.has_option("prefs", "disable_screensaver"):
305 self.disable_screensaver = conf.getboolean("prefs", "disable_screensaver")
306 if conf.has_option("prefs", "slideshow_in_fullscreen"):
307 self.slideshow_in_fullscreen = conf.getboolean(
308 "prefs", "slideshow_in_fullscreen"
309 )
310 if conf.has_option("prefs", "preloading_images"):
311 self.preloading_images = conf.getboolean("prefs", "preloading_images")
312 if conf.has_option("prefs", "thumbsize"):
313 self.thumbnail_size = conf.getint("prefs", "thumbsize")
314 if conf.has_option("prefs", "screenshot_delay"):
315 self.screenshot_delay = conf.getint("prefs", "screenshot_delay")
316 if conf.has_option("actions", "num_actions"):
317 num_actions = conf.getint("actions", "num_actions")
318 self.action_names = []
319 self.action_commands = []
320 self.action_shortcuts = []
321 self.action_batch = []
322 for i in range(num_actions):
323 if (
324 conf.has_option("actions", "names[" + str(i) + "]")
325 and conf.has_option("actions", "commands[" + str(i) + "]")
326 and conf.has_option("actions", "shortcuts[" + str(i) + "]")
327 and conf.has_option("actions", "batch[" + str(i) + "]")
328 ):
329 self.action_names.append(
330 conf.get("actions", "names[" + str(i) + "]")
331 )
332 self.action_commands.append(
333 conf.get("actions", "commands[" + str(i) + "]")
334 )
335 self.action_shortcuts.append(
336 conf.get("actions", "shortcuts[" + str(i) + "]")
337 )
338 self.action_batch.append(
339 conf.getboolean("actions", "batch[" + str(i) + "]")
340 )
341 if conf.has_option("prefs", "savemode"):
342 self.savemode = conf.getint("prefs", "savemode")
343 if conf.has_option("prefs", "start_in_fullscreen"):
344 self.start_in_fullscreen = conf.getboolean("prefs", "start_in_fullscreen")
345 if conf.has_option("prefs", "confirm_delete"):
346 self.confirm_delete = conf.getboolean("prefs", "confirm_delete")
347 self.recentfiles = []
348 if conf.has_option("recent", "num_recent"):
349 num_recent = conf.getint("recent", "num_recent")
350 for i in range(num_recent):
351 self.recentfiles.append("")
352 if conf.has_option("recent", "urls[" + str(i) + ",0]"):
353 self.recentfiles[i] = conf.get("recent", "urls[" + str(i) + ",0]")
354 # slideshow_delay is the user's preference, whereas curr_slideshow_delay is
355 # the current delay (which can be changed without affecting the 'default')
356 self.curr_slideshow_delay = self.slideshow_delay
357 # Same for randomization:
358 self.curr_slideshow_random = self.slideshow_random
359
360 # Read accel_map file, if it exists
361 if os.path.isfile(self.config_dir + "/accel_map"):
362 gtk.accel_map_load(self.config_dir + "/accel_map")
363
364 # Directory/ies in which to find application images/pixmaps
365 self.resource_path_list = False
366
367 self.blank_image = gtk.gdk.pixbuf_new_from_file(
368 self.find_path("mirage_blank.png")
369 )
370
371 # Define the main menubar and toolbar:
372 factory = gtk.IconFactory()
373 iconname = "stock_leave-fullscreen.png"
374 iconname2 = "stock_fullscreen.png"
375 leave_fullscreen_icon_path = self.find_path(iconname)
376 pixbuf = gtk.gdk.pixbuf_new_from_file(leave_fullscreen_icon_path)
377 iconset = gtk.IconSet(pixbuf)
378 factory.add("leave-fullscreen", iconset)
379 factory.add_default()
380 fullscreen_icon_path = self.find_path(iconname2)
381 pixbuf = gtk.gdk.pixbuf_new_from_file(fullscreen_icon_path)
382 iconset = gtk.IconSet(pixbuf)
383 factory.add("fullscreen", iconset)
384 factory.add_default()
385 try:
386 test = gtk.Button("", gtk.STOCK_LEAVE_FULLSCREEN)
387 leave_fullscreen_icon = gtk.STOCK_LEAVE_FULLSCREEN
388 fullscreen_icon = gtk.STOCK_FULLSCREEN
389 except:
390 # This will allow gtk 2.6 users to run Mirage
391 leave_fullscreen_icon = "leave-fullscreen"
392 fullscreen_icon = "fullscreen"
393 actions = (
394 ("FileMenu", None, _("_File")),
395 ("EditMenu", None, _("_Edit")),
396 ("ViewMenu", None, _("_View")),
397 ("GoMenu", None, _("_Go")),
398 ("HelpMenu", None, _("_Help")),
399 ("ActionSubMenu", None, _("Custom _Actions")),
400 (
401 "Open Image",
402 gtk.STOCK_FILE,
403 _("_Open Image..."),
404 "<Ctrl>O",
405 _("Open Image"),
406 self.open_file,
407 ),
408 (
409 "Open Remote Image",
410 gtk.STOCK_NETWORK,
411 _("Open _Remote image..."),
412 None,
413 _("Open Remote Image"),
414 self.open_file_remote,
415 ),
416 (
417 "Open Folder",
418 gtk.STOCK_DIRECTORY,
419 _("Open _Folder..."),
420 "<Ctrl>F",
421 _("Open Folder"),
422 self.open_folder,
423 ),
424 (
425 "Save",
426 gtk.STOCK_SAVE,
427 _("_Save Image"),
428 "<Ctrl>S",
429 _("Save Image"),
430 self.save_image,
431 ),
432 (
433 "Save As",
434 gtk.STOCK_SAVE,
435 _("Save Image _As..."),
436 "<Shift><Ctrl>S",
437 _("Save Image As"),
438 self.save_image_as,
439 ),
440 ("Crop", None, _("_Crop..."), None, _("Crop Image"), self.crop_image),
441 (
442 "Resize",
443 None,
444 _("R_esize..."),
445 None,
446 _("Resize Image"),
447 self.resize_image,
448 ),
449 (
450 "Saturation",
451 None,
452 _("_Saturation..."),
453 None,
454 _("Modify saturation"),
455 self.saturation,
456 ),
457 ("Quit", gtk.STOCK_QUIT, _("_Quit"), "<Ctrl>Q", _("Quit"), self.exit_app),
458 (
459 "Previous Image",
460 gtk.STOCK_GO_BACK,
461 _("_Previous Image"),
462 "Left",
463 _("Previous Image"),
464 self.goto_prev_image,
465 ),
466 (
467 "Next Image",
468 gtk.STOCK_GO_FORWARD,
469 _("_Next Image"),
470 "Right",
471 _("Next Image"),
472 self.goto_next_image,
473 ),
474 (
475 "Previous2",
476 gtk.STOCK_GO_BACK,
477 _("_Previous"),
478 "Left",
479 _("Previous"),
480 self.goto_prev_image,
481 ),
482 (
483 "Next2",
484 gtk.STOCK_GO_FORWARD,
485 _("_Next"),
486 "Right",
487 _("Next"),
488 self.goto_next_image,
489 ),
490 (
491 "Random Image",
492 None,
493 _("_Random Image"),
494 "R",
495 _("Random Image"),
496 self.goto_random_image,
497 ),
498 (
499 "First Image",
500 gtk.STOCK_GOTO_FIRST,
501 _("_First Image"),
502 "Home",
503 _("First Image"),
504 self.goto_first_image,
505 ),
506 (
507 "Last Image",
508 gtk.STOCK_GOTO_LAST,
509 _("_Last Image"),
510 "End",
511 _("Last Image"),
512 self.goto_last_image,
513 ),
514 (
515 "In",
516 gtk.STOCK_ZOOM_IN,
517 _("Zoom _In"),
518 "<Ctrl>Up",
519 _("Zoom In"),
520 self.zoom_in,
521 ),
522 (
523 "Out",
524 gtk.STOCK_ZOOM_OUT,
525 _("Zoom _Out"),
526 "<Ctrl>Down",
527 _("Zoom Out"),
528 self.zoom_out,
529 ),
530 (
531 "Fit",
532 gtk.STOCK_ZOOM_FIT,
533 _("Zoom To _Fit"),
534 "<Ctrl>0",
535 _("Fit"),
536 self.zoom_to_fit_window_action,
537 ),
538 (
539 "1:1",
540 gtk.STOCK_ZOOM_100,
541 _("_1:1"),
542 "<Ctrl>1",
543 _("1:1"),
544 self.zoom_1_to_1_action,
545 ),
546 (
547 "Rotate Left",
548 None,
549 _("Rotate _Left"),
550 "<Ctrl>Left",
551 _("Rotate Left"),
552 self.rotate_left,
553 ),
554 (
555 "Rotate Right",
556 None,
557 _("Rotate _Right"),
558 "<Ctrl>Right",
559 _("Rotate Right"),
560 self.rotate_right,
561 ),
562 (
563 "Flip Vertically",
564 None,
565 _("Flip _Vertically"),
566 "<Ctrl>V",
567 _("Flip Vertically"),
568 self.flip_image_vert,
569 ),
570 (
571 "Flip Horizontally",
572 None,
573 _("Flip _Horizontally"),
574 "<Ctrl>H",
575 _("Flip Horizontally"),
576 self.flip_image_horiz,
577 ),
578 ("About", gtk.STOCK_ABOUT, _("_About"), None, _("About"), self.show_about),
579 (
580 "Contents",
581 gtk.STOCK_HELP,
582 _("_Contents"),
583 "F1",
584 _("Contents"),
585 self.show_help,
586 ),
587 (
588 "Preferences",
589 gtk.STOCK_PREFERENCES,
590 _("_Preferences..."),
591 "<Ctrl>P",
592 _("Preferences"),
593 self.show_prefs,
594 ),
595 (
596 "Full Screen",
597 fullscreen_icon,
598 _("_Full Screen"),
599 "F11",
600 _("Full Screen"),
601 self.enter_fullscreen,
602 ),
603 (
604 "Exit Full Screen",
605 leave_fullscreen_icon,
606 _("E_xit Full Screen"),
607 None,
608 _("Exit Full Screen"),
609 self.leave_fullscreen,
610 ),
611 (
612 "Start Slideshow",
613 gtk.STOCK_MEDIA_PLAY,
614 _("_Start Slideshow"),
615 "F5",
616 _("Start Slideshow"),
617 self.toggle_slideshow,
618 ),
619 (
620 "Stop Slideshow",
621 gtk.STOCK_MEDIA_STOP,
622 _("_Stop Slideshow"),
623 "F5",
624 _("Stop Slideshow"),
625 self.toggle_slideshow,
626 ),
627 (
628 "Delete Image",
629 gtk.STOCK_DELETE,
630 _("_Delete..."),
631 "Delete",
632 _("Delete Image"),
633 self.delete_image,
634 ),
635 (
636 "Rename Image",
637 None,
638 _("Re_name..."),
639 "F2",
640 _("Rename Image"),
641 self.rename_image,
642 ),
643 (
644 "Take Screenshot",
645 None,
646 _("_Take Screenshot..."),
647 None,
648 _("Take Screenshot"),
649 self.screenshot,
650 ),
651 (
652 "Properties",
653 gtk.STOCK_PROPERTIES,
654 _("_Properties..."),
655 None,
656 _("Properties"),
657 self.show_properties,
658 ),
659 (
660 "Custom Actions",
661 None,
662 _("_Configure..."),
663 None,
664 _("Custom Actions"),
665 self.show_custom_actions,
666 ),
667 ("MiscKeysMenuHidden", None, "Keys"),
668 (
669 "Escape",
670 None,
671 "",
672 "Escape",
673 _("Exit Full Screen"),
674 self.leave_fullscreen,
675 ),
676 ("Minus", None, "", "minus", _("Zoom Out"), self.zoom_out),
677 ("Plus", None, "", "plus", _("Zoom In"), self.zoom_in),
678 ("Equal", None, "", "equal", _("Zoom In"), self.zoom_in),
679 ("Space", None, "", "space", _("Next Image"), self.goto_next_image),
680 (
681 "Ctrl-KP_Insert",
682 None,
683 "",
684 "<Ctrl>KP_Insert",
685 _("Fit"),
686 self.zoom_to_fit_window_action,
687 ),
688 (
689 "Ctrl-KP_End",
690 None,
691 "",
692 "<Ctrl>KP_End",
693 _("1:1"),
694 self.zoom_1_to_1_action,
695 ),
696 (
697 "Ctrl-KP_Subtract",
698 None,
699 "",
700 "<Ctrl>KP_Subtract",
701 _("Zoom Out"),
702 self.zoom_out,
703 ),
704 ("Ctrl-KP_Add", None, "", "<Ctrl>KP_Add", _("Zoom In"), self.zoom_in),
705 (
706 "Ctrl-KP_0",
707 None,
708 "",
709 "<Ctrl>KP_0",
710 _("Fit"),
711 self.zoom_to_fit_window_action,
712 ),
713 ("Ctrl-KP_1", None, "", "<Ctrl>KP_1", _("1:1"), self.zoom_1_to_1_action),
714 ("Full Screen Key", None, "", "<Shift>Return", None, self.enter_fullscreen),
715 ("Prev", None, "", "Up", _("Previous Image"), self.goto_prev_image),
716 ("Next", None, "", "Down", _("Next Image"), self.goto_next_image),
717 ("PgUp", None, "", "Page_Up", _("Previous Image"), self.goto_prev_image),
718 ("PgDn", None, "", "Page_Down", _("Next Image"), self.goto_next_image),
719 (
720 "BackSpace",
721 None,
722 "",
723 "BackSpace",
724 _("Previous Image"),
725 self.goto_prev_image,
726 ),
727 ("OriginalSize", None, "", "1", _("1:1"), self.zoom_1_to_1_action),
728 ("ZoomIn", None, "", "KP_Add", _("Zoom In"), self.zoom_in),
729 ("ZoomOut", None, "", "KP_Subtract", _("Zoom Out"), self.zoom_out),
730 )
731 toggle_actions = (
732 (
733 "Status Bar",
734 None,
735 _("_Status Bar"),
736 None,
737 _("Status Bar"),
738 self.toggle_status_bar,
739 self.statusbar_show,
740 ),
741 (
742 "Toolbar",
743 None,
744 _("_Toolbar"),
745 None,
746 _("Toolbar"),
747 self.toggle_toolbar,
748 self.toolbar_show,
749 ),
750 (
751 "Thumbnails Pane",
752 None,
753 _("Thumbnails _Pane"),
754 None,
755 _("Thumbnails Pane"),
756 self.toggle_thumbpane,
757 self.thumbpane_show,
758 ),
759 )
760
761 # Populate keys[]:
762 self.keys = []
763 for i in range(len(actions)):
764 if len(actions[i]) > 3:
765 if actions[i][3] != None:
766 self.keys.append([actions[i][4], actions[i][3]])
767
768 uiDescription = """
418769 <ui>
419770 <popup name="Popup">
420771 <menuitem action="Next Image"/>
532883 </ui>
533884 """
534885
535 # Create interface
536 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
537 self.update_title()
538 icon_path = self.find_path('mirage.png')
539 try:
540 gtk.window_set_default_icon_from_file(icon_path)
541 except:
542 pass
543 vbox = gtk.VBox(False, 0)
544 self.UIManager = gtk.UIManager()
545 actionGroup = gtk.ActionGroup('Actions')
546 actionGroup.add_actions(actions)
547 actionGroup.add_toggle_actions(toggle_actions)
548 self.UIManager.insert_action_group(actionGroup, 0)
549 self.UIManager.add_ui_from_string(uiDescription)
550 self.refresh_custom_actions_menu()
551 self.refresh_recent_files_menu()
552 self.window.add_accel_group(self.UIManager.get_accel_group())
553 self.menubar = self.UIManager.get_widget('/MainMenu')
554 vbox.pack_start(self.menubar, False, False, 0)
555 self.toolbar = self.UIManager.get_widget('/MainToolbar')
556 vbox.pack_start(self.toolbar, False, False, 0)
557 self.layout = gtk.Layout()
558 self.vscroll = gtk.VScrollbar(None)
559 self.vscroll.set_adjustment(self.layout.get_vadjustment())
560 self.hscroll = gtk.HScrollbar(None)
561 self.hscroll.set_adjustment(self.layout.get_hadjustment())
562 self.table = gtk.Table(3, 2, False)
563
564 self.thumblist = gtk.ListStore(gtk.gdk.Pixbuf)
565 self.thumbpane = gtk.TreeView(self.thumblist)
566 self.thumbcolumn = gtk.TreeViewColumn(None)
567 self.thumbcell = gtk.CellRendererPixbuf()
568 self.thumbcolumn.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
569 self.thumbpane_set_size()
570 self.thumbpane.append_column(self.thumbcolumn)
571 self.thumbcolumn.pack_start(self.thumbcell, True)
572 self.thumbcolumn.set_attributes(self.thumbcell, pixbuf=0)
573 self.thumbpane.get_selection().set_mode(gtk.SELECTION_SINGLE)
574 self.thumbpane.set_headers_visible(False)
575 self.thumbpane.set_property('can-focus', False)
576 self.thumbscroll = gtk.ScrolledWindow()
577 self.thumbscroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
578 self.thumbscroll.add(self.thumbpane)
579
580 self.table.attach(self.thumbscroll, 0, 1, 0, 1, 0, gtk.FILL|gtk.EXPAND, 0, 0)
581 self.table.attach(self.layout, 1, 2, 0, 1, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 0, 0)
582 self.table.attach(self.hscroll, 1, 2, 1, 2, gtk.FILL|gtk.SHRINK, gtk.FILL|gtk.SHRINK, 0, 0)
583 self.table.attach(self.vscroll, 2, 3, 0, 1, gtk.FILL|gtk.SHRINK, gtk.FILL|gtk.SHRINK, 0, 0)
584 vbox.pack_start(self.table, True, True, 0)
585 if not bgcolor_found:
586 self.bgcolor = gtk.gdk.Color(0, 0, 0) # Default to black
587 if self.simple_bgcolor:
588 self.layout.modify_bg(gtk.STATE_NORMAL, None)
589 else:
590 self.layout.modify_bg(gtk.STATE_NORMAL, self.bgcolor)
591 self.imageview = gtk.Image()
592 self.layout.add(self.imageview)
593
594 self.statusbar = gtk.Statusbar()
595 self.statusbar2 = gtk.Statusbar()
596 self.statusbar.set_has_resize_grip(False)
597 self.statusbar2.set_has_resize_grip(True)
598 self.statusbar2.set_size_request(200, -1)
599 hbox_statusbar = gtk.HBox()
600 hbox_statusbar.pack_start(self.statusbar, expand=True)
601 hbox_statusbar.pack_start(self.statusbar2, expand=False)
602 vbox.pack_start(hbox_statusbar, False, False, 0)
603 self.window.add(vbox)
604 self.window.set_property('allow-shrink', False)
605 self.window.set_default_size(width,height)
606
607 # Slideshow control:
608 self.slideshow_window = gtk.Window(gtk.WINDOW_POPUP)
609 self.slideshow_controls = gtk.HBox()
610 self.ss_back = gtk.Button()
611 self.ss_back.add(gtk.image_new_from_stock(gtk.STOCK_GO_BACK, gtk.ICON_SIZE_BUTTON))
612 self.ss_back.set_property('can-focus', False)
613 self.ss_back.connect('clicked', self.goto_prev_image)
614 self.ss_start = gtk.Button("", gtk.STOCK_MEDIA_PLAY)
615 self.ss_start.get_child().get_child().get_children()[1].set_text('')
616 self.ss_start.set_property('can-focus', False)
617 self.ss_start.connect('clicked', self.toggle_slideshow)
618 self.ss_stop = gtk.Button("", gtk.STOCK_MEDIA_STOP)
619 self.ss_stop.get_child().get_child().get_children()[1].set_text('')
620 self.ss_stop.set_property('can-focus', False)
621 self.ss_stop.connect('clicked', self.toggle_slideshow)
622 self.ss_forward = gtk.Button("", gtk.STOCK_GO_FORWARD)
623 self.ss_forward.get_child().get_child().get_children()[1].set_text('')
624 self.ss_forward.set_property('can-focus', False)
625 self.ss_forward.connect('clicked', self.goto_next_image)
626 self.slideshow_controls.pack_start(self.ss_back, False, False, 0)
627 self.slideshow_controls.pack_start(self.ss_start, False, False, 0)
628 self.slideshow_controls.pack_start(self.ss_stop, False, False, 0)
629 self.slideshow_controls.pack_start(self.ss_forward, False, False, 0)
630 self.slideshow_window.add(self.slideshow_controls)
631 if self.simple_bgcolor:
632 self.slideshow_window.modify_bg(gtk.STATE_NORMAL, None)
633 else:
634 self.slideshow_window.modify_bg(gtk.STATE_NORMAL, self.bgcolor)
635 self.slideshow_window2 = gtk.Window(gtk.WINDOW_POPUP)
636 self.slideshow_controls2 = gtk.HBox()
637 try:
638 self.ss_exit = gtk.Button("", gtk.STOCK_LEAVE_FULLSCREEN)
639 self.ss_exit.get_child().get_child().get_children()[1].set_text('')
640 except:
641 self.ss_exit = gtk.Button()
642 self.ss_exit.set_image(gtk.image_new_from_stock('leave-fullscreen', gtk.ICON_SIZE_MENU))
643 self.ss_exit.set_property('can-focus', False)
644 self.ss_exit.connect('clicked', self.leave_fullscreen)
645 self.ss_randomize = gtk.ToggleButton()
646 icon_path = self.find_path('stock_shuffle.png')
647 try:
648 pixbuf = gtk.gdk.pixbuf_new_from_file(icon_path)
649 iconset = gtk.IconSet(pixbuf)
650 factory.add('stock-shuffle', iconset)
651 factory.add_default()
652 self.ss_randomize.set_image(gtk.image_new_from_stock('stock-shuffle', gtk.ICON_SIZE_MENU))
653 except:
654 self.ss_randomize.set_label("Rand")
655 self.ss_randomize.connect('toggled', self.random_changed)
656
657 spin_adj = gtk.Adjustment(self.slideshow_delay, 0, 50000, 1,100, 0)
658 self.ss_delayspin = gtk.SpinButton(spin_adj, 1.0, 0)
659 self.ss_delayspin.set_numeric(True)
660 self.ss_delayspin.connect('changed', self.delay_changed)
661 self.slideshow_controls2.pack_start(self.ss_randomize, False, False, 0)
662 self.slideshow_controls2.pack_start(self.ss_delayspin, False, False, 0)
663 self.slideshow_controls2.pack_start(self.ss_exit, False, False, 0)
664 self.slideshow_window2.add(self.slideshow_controls2)
665 if self.simple_bgcolor:
666 self.slideshow_window2.modify_bg(gtk.STATE_NORMAL, None)
667 else:
668 self.slideshow_window2.modify_bg(gtk.STATE_NORMAL, self.bgcolor)
669
670 # Connect signals
671 self.window.connect("delete_event", self.delete_event)
672 self.window.connect("destroy", self.destroy)
673 self.window.connect("size-allocate", self.window_resized)
674 self.window.connect('key-press-event', self.topwindow_keypress)
675 self.toolbar.connect('focus', self.toolbar_focused)
676 self.layout.drag_dest_set(gtk.DEST_DEFAULT_HIGHLIGHT | gtk.DEST_DEFAULT_DROP, [("text/uri-list", 0, 80)], gtk.gdk.ACTION_DEFAULT)
677 self.layout.connect('drag_motion', self.motion_cb)
678 self.layout.connect('drag_data_received', self.drop_cb)
679 self.layout.add_events(gtk.gdk.KEY_PRESS_MASK | gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_MOTION_MASK | gtk.gdk.SCROLL_MASK)
680 self.layout.connect("scroll-event", self.mousewheel_scrolled)
681 self.layout.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.KEY_PRESS_MASK)
682 self.layout.connect("button_press_event", self.button_pressed)
683 self.layout.add_events(gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.POINTER_MOTION_HINT_MASK | gtk.gdk.BUTTON_RELEASE_MASK)
684 self.layout.connect("motion-notify-event", self.mouse_moved)
685 self.layout.connect("button-release-event", self.button_released)
686 self.imageview.connect("expose-event", self.expose_event)
687 self.thumb_sel_handler = self.thumbpane.get_selection().connect('changed', self.thumbpane_selection_changed)
688 self.thumb_scroll_handler = self.thumbscroll.get_vscrollbar().connect("value-changed", self.thumbpane_scrolled)
689
690 # Since GNOME does its own thing for the toolbar style...
691 # Requires gnome-python installed to work (but optional)
692 try:
693 client = gconf.client_get_default()
694 style = client.get_string('/desktop/gnome/interface/toolbar_style')
695 if style == "both":
696 self.toolbar.set_style(gtk.TOOLBAR_BOTH)
697 elif style == "both-horiz":
698 self.toolbar.set_style(gtk.TOOLBAR_BOTH_HORIZ)
699 elif style == "icons":
700 self.toolbar.set_style(gtk.TOOLBAR_ICONS)
701 elif style == "text":
702 self.toolbar.set_style(gtk.TOOLBAR_TEXT)
703 client.add_dir("/desktop/gnome/interface", gconf.CLIENT_PRELOAD_NONE)
704 client.notify_add("/desktop/gnome/interface/toolbar_style", self.gconf_key_changed)
705 except:
706 pass
707
708 # Show GUI:
709 if not self.toolbar_show:
710 self.toolbar.set_property('visible', False)
711 self.toolbar.set_no_show_all(True)
712 if not self.statusbar_show:
713 self.statusbar.set_property('visible', False)
714 self.statusbar.set_no_show_all(True)
715 self.statusbar2.set_property('visible', False)
716 self.statusbar2.set_no_show_all(True)
717 if not self.thumbpane_show:
718 self.thumbscroll.set_property('visible', False)
719 self.thumbscroll.set_no_show_all(True)
720 self.hscroll.set_no_show_all(True)
721 self.vscroll.set_no_show_all(True)
722 go_into_fullscreen = False
723 if opts != []:
724 for o, a in opts:
725 if (o in ("-f", "--fullscreen")) or ((o in ("-s", "--slideshow")) and self.slideshow_in_fullscreen):
726 go_into_fullscreen = True
727 if go_into_fullscreen or self.start_in_fullscreen:
728 self.enter_fullscreen(None)
729 self.statusbar.set_no_show_all(True)
730 self.statusbar2.set_no_show_all(True)
731 self.toolbar.set_no_show_all(True)
732 self.menubar.set_no_show_all(True)
733 self.thumbscroll.set_no_show_all(True)
734 self.window.show_all()
735 self.ss_exit.set_size_request(self.ss_start.size_request()[0], self.ss_stop.size_request()[1])
736 self.ss_randomize.set_size_request(self.ss_start.size_request()[0], -1)
737 self.ss_start.set_size_request(self.ss_start.size_request()[0]*2, -1)
738 self.ss_stop.set_size_request(self.ss_stop.size_request()[0]*2, -1)
739 self.UIManager.get_widget('/Popup/Exit Full Screen').hide()
740 self.layout.set_flags(gtk.CAN_FOCUS)
741 self.window.set_focus(self.layout)
742
743 #sets the visibility of some menu entries
744 self.set_slideshow_sensitivities()
745 self.UIManager.get_widget('/MainMenu/MiscKeysMenuHidden').set_property('visible', False)
746 if opts != []:
747 for o, a in opts:
748 if o in ("-f", "--fullscreen"):
749 self.UIManager.get_widget('/Popup/Exit Full Screen').show()
750
751 # If arguments (filenames) were passed, try to open them:
752 self.image_list = []
753 if args != []:
754 for i in range(len(args)):
755 args[i] = urllib.request.url2pathname(args[i])
756 self.expand_filelist_and_load_image(args)
757 else:
758 self.set_go_sensitivities(False)
759 self.set_image_sensitivities(False)
760
761 if opts != []:
762 for o, a in opts:
763 if o in ("-s", "--slideshow"):
764 self.toggle_slideshow(None)
765
766 def refresh_recent_files_menu(self):
767 if self.merge_id_recent:
768 self.UIManager.remove_ui(self.merge_id_recent)
769 if self.actionGroupRecent:
770 self.UIManager.remove_action_group(self.actionGroupRecent)
771 self.actionGroupRecent = None
772 self.actionGroupRecent = gtk.ActionGroup('RecentFiles')
773 self.UIManager.ensure_update()
774 for i in range(len(self.recentfiles)):
775 if len(self.recentfiles[i]) > 0:
776 filename = self.recentfiles[i].split("/")[-1]
777 if len(filename) > 0:
778 if len(filename) > 27:
779 # Replace end of file name (excluding extension) with ..
780 try:
781 menu_name = filename[:25] + '..' + os.path.splitext(filename)[1]
782 except:
783 menu_name = filename[0]
784 else:
785 menu_name = filename
786 menu_name = menu_name.replace('_','__')
787 action = [(str(i), None, menu_name, '<Alt>' + str(i+1), None, self.recent_action_click)]
788 self.actionGroupRecent.add_actions(action)
789 uiDescription = """
886 # Create interface
887 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
888 self.update_title()
889 icon_path = self.find_path("mirage.png")
890 try:
891 gtk.window_set_default_icon_from_file(icon_path)
892 except:
893 pass
894 vbox = gtk.VBox(False, 0)
895 self.UIManager = gtk.UIManager()
896 actionGroup = gtk.ActionGroup("Actions")
897 actionGroup.add_actions(actions)
898 actionGroup.add_toggle_actions(toggle_actions)
899 self.UIManager.insert_action_group(actionGroup, 0)
900 self.UIManager.add_ui_from_string(uiDescription)
901 self.refresh_custom_actions_menu()
902 self.refresh_recent_files_menu()
903 self.window.add_accel_group(self.UIManager.get_accel_group())
904 self.menubar = self.UIManager.get_widget("/MainMenu")
905 vbox.pack_start(self.menubar, False, False, 0)
906 self.toolbar = self.UIManager.get_widget("/MainToolbar")
907 vbox.pack_start(self.toolbar, False, False, 0)
908 self.layout = gtk.Layout()
909 self.vscroll = gtk.VScrollbar(None)
910 self.vscroll.set_adjustment(self.layout.get_vadjustment())
911 self.hscroll = gtk.HScrollbar(None)
912 self.hscroll.set_adjustment(self.layout.get_hadjustment())
913 self.table = gtk.Table(3, 2, False)
914
915 self.thumblist = gtk.ListStore(gtk.gdk.Pixbuf)
916 self.thumbpane = gtk.TreeView(self.thumblist)
917 self.thumbcolumn = gtk.TreeViewColumn(None)
918 self.thumbcell = gtk.CellRendererPixbuf()
919 self.thumbcolumn.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
920 self.thumbpane_set_size()
921 self.thumbpane.append_column(self.thumbcolumn)
922 self.thumbcolumn.pack_start(self.thumbcell, True)
923 self.thumbcolumn.set_attributes(self.thumbcell, pixbuf=0)
924 self.thumbpane.get_selection().set_mode(gtk.SELECTION_SINGLE)
925 self.thumbpane.set_headers_visible(False)
926 self.thumbpane.set_property("can-focus", False)
927 self.thumbscroll = gtk.ScrolledWindow()
928 self.thumbscroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
929 self.thumbscroll.add(self.thumbpane)
930
931 self.table.attach(self.thumbscroll, 0, 1, 0, 1, 0, gtk.FILL | gtk.EXPAND, 0, 0)
932 self.table.attach(
933 self.layout, 1, 2, 0, 1, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 0, 0
934 )
935 self.table.attach(
936 self.hscroll, 1, 2, 1, 2, gtk.FILL | gtk.SHRINK, gtk.FILL | gtk.SHRINK, 0, 0
937 )
938 self.table.attach(
939 self.vscroll, 2, 3, 0, 1, gtk.FILL | gtk.SHRINK, gtk.FILL | gtk.SHRINK, 0, 0
940 )
941 vbox.pack_start(self.table, True, True, 0)
942 if not bgcolor_found:
943 self.bgcolor = gtk.gdk.Color(0, 0, 0) # Default to black
944 if self.simple_bgcolor:
945 self.layout.modify_bg(gtk.STATE_NORMAL, None)
946 else:
947 self.layout.modify_bg(gtk.STATE_NORMAL, self.bgcolor)
948 self.imageview = gtk.Image()
949 self.layout.add(self.imageview)
950
951 self.statusbar = gtk.Statusbar()
952 self.statusbar2 = gtk.Statusbar()
953 self.statusbar.set_has_resize_grip(False)
954 self.statusbar2.set_has_resize_grip(True)
955 self.statusbar2.set_size_request(200, -1)
956 hbox_statusbar = gtk.HBox()
957 hbox_statusbar.pack_start(self.statusbar, expand=True)
958 hbox_statusbar.pack_start(self.statusbar2, expand=False)
959 vbox.pack_start(hbox_statusbar, False, False, 0)
960 self.window.add(vbox)
961 self.window.set_property("allow-shrink", False)
962 self.window.set_default_size(width, height)
963
964 # Slideshow control:
965 self.slideshow_window = gtk.Window(gtk.WINDOW_POPUP)
966 self.slideshow_controls = gtk.HBox()
967 self.ss_back = gtk.Button()
968 self.ss_back.add(
969 gtk.image_new_from_stock(gtk.STOCK_GO_BACK, gtk.ICON_SIZE_BUTTON)
970 )
971 self.ss_back.set_property("can-focus", False)
972 self.ss_back.connect("clicked", self.goto_prev_image)
973 self.ss_start = gtk.Button("", gtk.STOCK_MEDIA_PLAY)
974 self.ss_start.get_child().get_child().get_children()[1].set_text("")
975 self.ss_start.set_property("can-focus", False)
976 self.ss_start.connect("clicked", self.toggle_slideshow)
977 self.ss_stop = gtk.Button("", gtk.STOCK_MEDIA_STOP)
978 self.ss_stop.get_child().get_child().get_children()[1].set_text("")
979 self.ss_stop.set_property("can-focus", False)
980 self.ss_stop.connect("clicked", self.toggle_slideshow)
981 self.ss_forward = gtk.Button("", gtk.STOCK_GO_FORWARD)
982 self.ss_forward.get_child().get_child().get_children()[1].set_text("")
983 self.ss_forward.set_property("can-focus", False)
984 self.ss_forward.connect("clicked", self.goto_next_image)
985 self.slideshow_controls.pack_start(self.ss_back, False, False, 0)
986 self.slideshow_controls.pack_start(self.ss_start, False, False, 0)
987 self.slideshow_controls.pack_start(self.ss_stop, False, False, 0)
988 self.slideshow_controls.pack_start(self.ss_forward, False, False, 0)
989 self.slideshow_window.add(self.slideshow_controls)
990 if self.simple_bgcolor:
991 self.slideshow_window.modify_bg(gtk.STATE_NORMAL, None)
992 else:
993 self.slideshow_window.modify_bg(gtk.STATE_NORMAL, self.bgcolor)
994 self.slideshow_window2 = gtk.Window(gtk.WINDOW_POPUP)
995 self.slideshow_controls2 = gtk.HBox()
996 try:
997 self.ss_exit = gtk.Button("", gtk.STOCK_LEAVE_FULLSCREEN)
998 self.ss_exit.get_child().get_child().get_children()[1].set_text("")
999 except:
1000 self.ss_exit = gtk.Button()
1001 self.ss_exit.set_image(
1002 gtk.image_new_from_stock("leave-fullscreen", gtk.ICON_SIZE_MENU)
1003 )
1004 self.ss_exit.set_property("can-focus", False)
1005 self.ss_exit.connect("clicked", self.leave_fullscreen)
1006 self.ss_randomize = gtk.ToggleButton()
1007 icon_path = self.find_path("stock_shuffle.png")
1008 try:
1009 pixbuf = gtk.gdk.pixbuf_new_from_file(icon_path)
1010 iconset = gtk.IconSet(pixbuf)
1011 factory.add("stock-shuffle", iconset)
1012 factory.add_default()
1013 self.ss_randomize.set_image(
1014 gtk.image_new_from_stock("stock-shuffle", gtk.ICON_SIZE_MENU)
1015 )
1016 except:
1017 self.ss_randomize.set_label("Rand")
1018 self.ss_randomize.connect("toggled", self.random_changed)
1019
1020 spin_adj = gtk.Adjustment(self.slideshow_delay, 0, 50000, 1, 100, 0)
1021 self.ss_delayspin = gtk.SpinButton(spin_adj, 1.0, 0)
1022 self.ss_delayspin.set_numeric(True)
1023 self.ss_delayspin.connect("changed", self.delay_changed)
1024 self.slideshow_controls2.pack_start(self.ss_randomize, False, False, 0)
1025 self.slideshow_controls2.pack_start(self.ss_delayspin, False, False, 0)
1026 self.slideshow_controls2.pack_start(self.ss_exit, False, False, 0)
1027 self.slideshow_window2.add(self.slideshow_controls2)
1028 if self.simple_bgcolor:
1029 self.slideshow_window2.modify_bg(gtk.STATE_NORMAL, None)
1030 else:
1031 self.slideshow_window2.modify_bg(gtk.STATE_NORMAL, self.bgcolor)
1032
1033 # Connect signals
1034 self.window.connect("delete_event", self.delete_event)
1035 self.window.connect("destroy", self.destroy)
1036 self.window.connect("size-allocate", self.window_resized)
1037 self.window.connect("key-press-event", self.topwindow_keypress)
1038 self.toolbar.connect("focus", self.toolbar_focused)
1039 self.layout.drag_dest_set(
1040 gtk.DEST_DEFAULT_HIGHLIGHT | gtk.DEST_DEFAULT_DROP,
1041 [("text/uri-list", 0, 80)],
1042 gtk.gdk.ACTION_DEFAULT,
1043 )
1044 self.layout.connect("drag_motion", self.motion_cb)
1045 self.layout.connect("drag_data_received", self.drop_cb)
1046 self.layout.add_events(
1047 gtk.gdk.KEY_PRESS_MASK
1048 | gtk.gdk.POINTER_MOTION_MASK
1049 | gtk.gdk.BUTTON_PRESS_MASK
1050 | gtk.gdk.BUTTON_MOTION_MASK
1051 | gtk.gdk.SCROLL_MASK
1052 )
1053 self.layout.connect("scroll-event", self.mousewheel_scrolled)
1054 self.layout.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.KEY_PRESS_MASK)
1055 self.layout.connect("button_press_event", self.button_pressed)
1056 self.layout.add_events(
1057 gtk.gdk.POINTER_MOTION_MASK
1058 | gtk.gdk.POINTER_MOTION_HINT_MASK
1059 | gtk.gdk.BUTTON_RELEASE_MASK
1060 )
1061 self.layout.connect("motion-notify-event", self.mouse_moved)
1062 self.layout.connect("button-release-event", self.button_released)
1063 self.imageview.connect("expose-event", self.expose_event)
1064 self.thumb_sel_handler = self.thumbpane.get_selection().connect(
1065 "changed", self.thumbpane_selection_changed
1066 )
1067 self.thumb_scroll_handler = self.thumbscroll.get_vscrollbar().connect(
1068 "value-changed", self.thumbpane_scrolled
1069 )
1070
1071 # Since GNOME does its own thing for the toolbar style...
1072 # Requires gnome-python installed to work (but optional)
1073 try:
1074 client = gconf.client_get_default()
1075 style = client.get_string("/desktop/gnome/interface/toolbar_style")
1076 if style == "both":
1077 self.toolbar.set_style(gtk.TOOLBAR_BOTH)
1078 elif style == "both-horiz":
1079 self.toolbar.set_style(gtk.TOOLBAR_BOTH_HORIZ)
1080 elif style == "icons":
1081 self.toolbar.set_style(gtk.TOOLBAR_ICONS)
1082 elif style == "text":
1083 self.toolbar.set_style(gtk.TOOLBAR_TEXT)
1084 client.add_dir("/desktop/gnome/interface", gconf.CLIENT_PRELOAD_NONE)
1085 client.notify_add(
1086 "/desktop/gnome/interface/toolbar_style", self.gconf_key_changed
1087 )
1088 except:
1089 pass
1090
1091 # Show GUI:
1092 if not self.toolbar_show:
1093 self.toolbar.set_property("visible", False)
1094 self.toolbar.set_no_show_all(True)
1095 if not self.statusbar_show:
1096 self.statusbar.set_property("visible", False)
1097 self.statusbar.set_no_show_all(True)
1098 self.statusbar2.set_property("visible", False)
1099 self.statusbar2.set_no_show_all(True)
1100 if not self.thumbpane_show:
1101 self.thumbscroll.set_property("visible", False)
1102 self.thumbscroll.set_no_show_all(True)
1103 self.hscroll.set_no_show_all(True)
1104 self.vscroll.set_no_show_all(True)
1105 go_into_fullscreen = False
1106 if opts != []:
1107 for o, a in opts:
1108 if (o in ("-f", "--fullscreen")) or (
1109 (o in ("-s", "--slideshow")) and self.slideshow_in_fullscreen
1110 ):
1111 go_into_fullscreen = True
1112 if go_into_fullscreen or self.start_in_fullscreen:
1113 self.enter_fullscreen(None)
1114 self.statusbar.set_no_show_all(True)
1115 self.statusbar2.set_no_show_all(True)
1116 self.toolbar.set_no_show_all(True)
1117 self.menubar.set_no_show_all(True)
1118 self.thumbscroll.set_no_show_all(True)
1119 self.window.show_all()
1120 self.ss_exit.set_size_request(
1121 self.ss_start.size_request()[0], self.ss_stop.size_request()[1]
1122 )
1123 self.ss_randomize.set_size_request(self.ss_start.size_request()[0], -1)
1124 self.ss_start.set_size_request(self.ss_start.size_request()[0] * 2, -1)
1125 self.ss_stop.set_size_request(self.ss_stop.size_request()[0] * 2, -1)
1126 self.UIManager.get_widget("/Popup/Exit Full Screen").hide()
1127 self.layout.set_flags(gtk.CAN_FOCUS)
1128 self.window.set_focus(self.layout)
1129
1130 # sets the visibility of some menu entries
1131 self.set_slideshow_sensitivities()
1132 self.UIManager.get_widget("/MainMenu/MiscKeysMenuHidden").set_property(
1133 "visible", False
1134 )
1135 if opts != []:
1136 for o, a in opts:
1137 if o in ("-f", "--fullscreen"):
1138 self.UIManager.get_widget("/Popup/Exit Full Screen").show()
1139
1140 # If arguments (filenames) were passed, try to open them:
1141 self.image_list = []
1142 if args != []:
1143 for i in range(len(args)):
1144 args[i] = urllib.request.url2pathname(args[i])
1145 self.expand_filelist_and_load_image(args)
1146 else:
1147 self.set_go_sensitivities(False)
1148 self.set_image_sensitivities(False)
1149
1150 if opts != []:
1151 for o, a in opts:
1152 if o in ("-s", "--slideshow"):
1153 self.toggle_slideshow(None)
1154
1155 def refresh_recent_files_menu(self):
1156 if self.merge_id_recent:
1157 self.UIManager.remove_ui(self.merge_id_recent)
1158 if self.actionGroupRecent:
1159 self.UIManager.remove_action_group(self.actionGroupRecent)
1160 self.actionGroupRecent = None
1161 self.actionGroupRecent = gtk.ActionGroup("RecentFiles")
1162 self.UIManager.ensure_update()
1163 for i in range(len(self.recentfiles)):
1164 if len(self.recentfiles[i]) > 0:
1165 filename = self.recentfiles[i].split("/")[-1]
1166 if len(filename) > 0:
1167 if len(filename) > 27:
1168 # Replace end of file name (excluding extension) with ..
1169 try:
1170 menu_name = (
1171 filename[:25] + ".." + os.path.splitext(filename)[1]
1172 )
1173 except:
1174 menu_name = filename[0]
1175 else:
1176 menu_name = filename
1177 menu_name = menu_name.replace("_", "__")
1178 action = [
1179 (
1180 str(i),
1181 None,
1182 menu_name,
1183 "<Alt>" + str(i + 1),
1184 None,
1185 self.recent_action_click,
1186 )
1187 ]
1188 self.actionGroupRecent.add_actions(action)
1189 uiDescription = """
7901190 <ui>
7911191 <menubar name="MainMenu">
7921192 <menu action="FileMenu">
7931193 <placeholder name="Recent Files">
7941194 """
795 for i in range(len(self.recentfiles)):
796 if len(self.recentfiles[i]) > 0:
797 uiDescription = uiDescription + """<menuitem action=\"""" + str(i) + """\"/>"""
798 uiDescription = uiDescription + """</placeholder></menu></menubar></ui>"""
799 self.merge_id_recent = self.UIManager.add_ui_from_string(uiDescription)
800 self.UIManager.insert_action_group(self.actionGroupRecent, 0)
801 self.UIManager.get_widget('/MainMenu/MiscKeysMenuHidden').set_property('visible', False)
802
803 def refresh_custom_actions_menu(self):
804 if self.merge_id:
805 self.UIManager.remove_ui(self.merge_id)
806 if self.actionGroupCustom:
807 self.UIManager.remove_action_group(self.actionGroupCustom)
808 self.actionGroupCustom = None
809 self.actionGroupCustom = gtk.ActionGroup('CustomActions')
810 self.UIManager.ensure_update()
811 for i in range(len(self.action_names)):
812 action = [(self.action_names[i], None, self.action_names[i], self.action_shortcuts[i], None, self.custom_action_click)]
813 self.actionGroupCustom.add_actions(action)
814 uiDescription = """
1195 for i in range(len(self.recentfiles)):
1196 if len(self.recentfiles[i]) > 0:
1197 uiDescription = (
1198 uiDescription + """<menuitem action=\"""" + str(i) + """\"/>"""
1199 )
1200 uiDescription = uiDescription + """</placeholder></menu></menubar></ui>"""
1201 self.merge_id_recent = self.UIManager.add_ui_from_string(uiDescription)
1202 self.UIManager.insert_action_group(self.actionGroupRecent, 0)
1203 self.UIManager.get_widget("/MainMenu/MiscKeysMenuHidden").set_property(
1204 "visible", False
1205 )
1206
1207 def refresh_custom_actions_menu(self):
1208 if self.merge_id:
1209 self.UIManager.remove_ui(self.merge_id)
1210 if self.actionGroupCustom:
1211 self.UIManager.remove_action_group(self.actionGroupCustom)
1212 self.actionGroupCustom = None
1213 self.actionGroupCustom = gtk.ActionGroup("CustomActions")
1214 self.UIManager.ensure_update()
1215 for i in range(len(self.action_names)):
1216 action = [
1217 (
1218 self.action_names[i],
1219 None,
1220 self.action_names[i],
1221 self.action_shortcuts[i],
1222 None,
1223 self.custom_action_click,
1224 )
1225 ]
1226 self.actionGroupCustom.add_actions(action)
1227 uiDescription = """
8151228 <ui>
8161229 <menubar name="MainMenu">
8171230 <menu action="EditMenu">
8181231 <menu action="ActionSubMenu">
8191232 """
820 for i in range(len(self.action_names)):
821 uiDescription = uiDescription + """<menuitem action=\"""" + self.action_names[len(self.action_names)-i-1].replace('&','&amp;') + """\" position="top"/>"""
822 uiDescription = uiDescription + """</menu></menu></menubar></ui>"""
823 self.merge_id = self.UIManager.add_ui_from_string(uiDescription)
824 self.UIManager.insert_action_group(self.actionGroupCustom, 0)
825 self.UIManager.get_widget('/MainMenu/MiscKeysMenuHidden').set_property('visible', False)
826
827 def thumbpane_update_images(self, clear_first=False, force_upto_imgnum=-1):
828 self.stop_now = False
829 # When first populating the thumbpane, make sure we go up to at least
830 # force_upto_imgnum so that we can show this image selected:
831 if clear_first:
832 self.thumbpane_clear_list()
833 # Load all images up to the bottom ofo the visible thumbpane rect:
834 rect = self.thumbpane.get_visible_rect()
835 bottom_coord = rect.y + rect.height + self.thumbnail_size
836 if bottom_coord > self.thumbpane_bottom_coord_loaded:
837 self.thumbpane_bottom_coord_loaded = bottom_coord
838 # update images:
839 if not self.thumbpane_updating:
840 thread = threading.Thread(target=self.thumbpane_update_pending_images, args=(force_upto_imgnum, None))
841 thread.setDaemon(True)
842 thread.start()
843
844 def thumbpane_create_dir(self):
845 if not os.path.exists(os.path.expanduser('~/.thumbnails/')):
846 os.mkdir(os.path.expanduser('~/.thumbnails/'))
847 if not os.path.exists(os.path.expanduser('~/.thumbnails/normal/')):
848 os.mkdir(os.path.expanduser('~/.thumbnails/normal/'))
849
850 def thumbpane_update_pending_images(self, force_upto_imgnum, foo):
851 self.thumbpane_updating = True
852 self.thumbpane_create_dir()
853 # Check to see if any images need their thumbnails generated.
854 curr_coord = 0
855 imgnum = 0
856 while curr_coord < self.thumbpane_bottom_coord_loaded or imgnum <= force_upto_imgnum:
857 if self.closing_app or self.stop_now or not self.thumbpane_show:
858 break
859 if imgnum >= len(self.image_list):
860 break
861 self.thumbpane_set_image(self.image_list[imgnum], imgnum)
862 curr_coord += self.thumbpane.get_background_area((imgnum,),self.thumbcolumn).height
863 if force_upto_imgnum == imgnum:
864 # Verify that the user hasn't switched images while we're loading thumbnails:
865 if force_upto_imgnum == self.curr_img_in_list:
866 gobject.idle_add(self.thumbpane_select, force_upto_imgnum)
867 imgnum += 1
868 self.thumbpane_updating = False
869
870 def thumbpane_clear_list(self):
871 self.thumbpane_bottom_coord_loaded = 0
872 self.thumbscroll.get_vscrollbar().handler_block(self.thumb_scroll_handler)
873 self.thumblist.clear()
874 self.thumbscroll.get_vscrollbar().handler_unblock(self.thumb_scroll_handler)
875 for image in self.image_list:
876 blank_pix = self.get_blank_pix_for_image(image)
877 self.thumblist.append([blank_pix])
878 self.thumbnail_loaded = [False]*len(self.image_list)
879
880 def thumbpane_set_image(self, image_name, imgnum, force_update=False):
881 if self.thumbpane_show:
882 if not self.thumbnail_loaded[imgnum] or force_update:
883 filename, thumbfile = self.thumbnail_get_name(image_name)
884 pix = self.thumbpane_get_pixbuf(thumbfile, filename, force_update)
885 if pix:
886 if self.thumbnail_size != 128:
887 # 128 is the size of the saved thumbnail, so convert if different:
888 pix, image_width, image_height = self.get_pixbuf_of_size(pix, self.thumbnail_size, gtk.gdk.INTERP_TILES)
889 self.thumbnail_loaded[imgnum] = True
890 self.thumbscroll.get_vscrollbar().handler_block(self.thumb_scroll_handler)
891 pix = self.pixbuf_add_border(pix)
892 try:
893 self.thumblist[imgnum] = [pix]
894 except:
895 pass
896 self.thumbscroll.get_vscrollbar().handler_unblock(self.thumb_scroll_handler)
897
898 def thumbnail_get_name(self, image_name):
899 filename = os.path.expanduser('file://' + image_name)
900 uriname = os.path.expanduser('file://' + urllib.request.pathname2url(image_name))
901 if HAS_HASHLIB:
902 m = hashlib.md5()
903 else:
904 m = md5.new()
905 m.update(uriname)
906 mhex = m.hexdigest()
907 mhex_filename = os.path.expanduser('~/.thumbnails/normal/' + mhex + '.png')
908 return filename, mhex_filename
909
910 def thumbpane_get_pixbuf(self, thumb_url, image_url, force_generation):
911 # Returns a valid pixbuf or None if a pixbuf cannot be generated. Tries to re-use
912 # a thumbnail from ~/.thumbails/normal/, otherwise generates one with the
913 # XDG filename: md5(file:///full/path/to/image).png
914 imgfile = image_url
915 if imgfile[:7] == 'file://':
916 imgfile = imgfile[7:]
917 try:
918 if os.path.exists(thumb_url) and not force_generation:
919 pix = gtk.gdk.pixbuf_new_from_file(thumb_url)
920 pix_mtime = pix.get_option('tEXt::Thumb::MTime')
921 if pix_mtime:
922 st = os.stat(imgfile)
923 file_mtime = str(st[stat.ST_MTIME])
924 # If the mtimes match, we're good. if not, regenerate the thumbnail..
925 if pix_mtime == file_mtime:
926 return pix
927 # Create the 128x128 thumbnail:
928 uri = 'file://' + urllib.request.pathname2url(imgfile)
929 pix = gtk.gdk.pixbuf_new_from_file(imgfile)
930 pix, image_width, image_height = self.get_pixbuf_of_size(pix, 128, gtk.gdk.INTERP_TILES)
931 st = os.stat(imgfile)
932 file_mtime = str(st[stat.ST_MTIME])
933 # Save image to .thumbnails:
934 pix.save(thumb_url, "png", {'tEXt::Thumb::URI':uri, 'tEXt::Thumb::MTime':file_mtime, 'tEXt::Software':'Mirage' + __version__})
935 return pix
936 except:
937 return None
938
939 def thumbpane_load_image(self, treeview, imgnum):
940 if imgnum != self.curr_img_in_list:
941 gobject.idle_add(self.goto_image, str(imgnum), None)
942
943 def thumbpane_selection_changed(self, treeview):
944 cancel = self.autosave_image()
945 if cancel:
946 # Revert selection...
947 gobject.idle_add(self.thumbpane_select, self.curr_img_in_list)
948 return True
949 try:
950 model, paths = self.thumbpane.get_selection().get_selected_rows()
951 imgnum = paths[0][0]
952 if not self.thumbnail_loaded[imgnum]:
953 self.thumbpane_set_image(self.image_list[imgnum], imgnum)
954 gobject.idle_add(self.thumbpane_load_image, treeview, imgnum)
955 except:
956 pass
957
958 def thumbpane_select(self, imgnum):
959 if self.thumbpane_show:
960 self.thumbpane.get_selection().handler_block(self.thumb_sel_handler)
961 try:
962 self.thumbpane.get_selection().select_path((imgnum,))
963 self.thumbpane.scroll_to_cell((imgnum,))
964 except:
965 pass
966 self.thumbpane.get_selection().handler_unblock(self.thumb_sel_handler)
967
968 def thumbpane_set_size(self):
969 self.thumbcolumn.set_fixed_width(self.thumbpane_get_size())
970 self.window_resized(None, self.window.allocation, True)
971
972 def thumbpane_get_size(self):
973 return int(self.thumbnail_size * 1.3)
974
975 def thumbpane_scrolled(self, range):
976 self.thumbpane_update_images()
977
978 def get_blank_pix_for_image(self, image):
979 # Sizes the "blank image" icon for the thumbpane. This will ensure that we don't
980 # load a humongous icon for a small pix, for example, and will keep the thumbnails
981 # from shifting around when they are actually loaded.
982 try:
983 info = gtk.gdk.pixbuf_get_file_info(image)
984 imgwidth = float(info[1])
985 imgheight = float(info[2])
986 if imgheight > self.thumbnail_size:
987 if imgheight > imgwidth:
988 imgheight = self.thumbnail_size
989 else:
990 imgheight = (imgheight // imgwidth) * self.thumbnail_size
991 imgheight = 2 + int(imgheight) # Account for border that will be added to thumbnails..
992 imgwidth = self.thumbnail_size
993 except:
994 imgheight = 2 + self.thumbnail_size
995 imgwidth = self.thumbnail_size
996 blank_pix = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, imgwidth, imgheight)
997 blank_pix.fill(0x00000000)
998 imgwidth2 = int(imgheight*0.8)
999 imgheight2 = int(imgheight*0.8)
1000 composite_pix = self.blank_image.scale_simple(imgwidth2, imgheight2, gtk.gdk.INTERP_BILINEAR)
1001 leftcoord = int((imgwidth - imgwidth2) // 2)
1002 topcoord = int((imgheight - imgheight2) // 2)
1003 composite_pix.copy_area(0, 0, imgwidth2, imgheight2, blank_pix, leftcoord, topcoord)
1004 return blank_pix
1005
1006 def find_path(self, filename, exit_on_fail=True):
1007 """ Find a pixmap or icon by looking through standard dirs.
1233 for i in range(len(self.action_names)):
1234 uiDescription = (
1235 uiDescription
1236 + """<menuitem action=\""""
1237 + self.action_names[len(self.action_names) - i - 1].replace(
1238 "&", "&amp;"
1239 )
1240 + """\" position="top"/>"""
1241 )
1242 uiDescription = uiDescription + """</menu></menu></menubar></ui>"""
1243 self.merge_id = self.UIManager.add_ui_from_string(uiDescription)
1244 self.UIManager.insert_action_group(self.actionGroupCustom, 0)
1245 self.UIManager.get_widget("/MainMenu/MiscKeysMenuHidden").set_property(
1246 "visible", False
1247 )
1248
1249 def thumbpane_update_images(self, clear_first=False, force_upto_imgnum=-1):
1250 self.stop_now = False
1251 # When first populating the thumbpane, make sure we go up to at least
1252 # force_upto_imgnum so that we can show this image selected:
1253 if clear_first:
1254 self.thumbpane_clear_list()
1255 # Load all images up to the bottom ofo the visible thumbpane rect:
1256 rect = self.thumbpane.get_visible_rect()
1257 bottom_coord = rect.y + rect.height + self.thumbnail_size
1258 if bottom_coord > self.thumbpane_bottom_coord_loaded:
1259 self.thumbpane_bottom_coord_loaded = bottom_coord
1260 # update images:
1261 if not self.thumbpane_updating:
1262 thread = threading.Thread(
1263 target=self.thumbpane_update_pending_images,
1264 args=(force_upto_imgnum, None),
1265 )
1266 thread.setDaemon(True)
1267 thread.start()
1268
1269 def thumbpane_create_dir(self):
1270 if not os.path.exists(os.path.expanduser("~/.thumbnails/")):
1271 os.mkdir(os.path.expanduser("~/.thumbnails/"))
1272 if not os.path.exists(os.path.expanduser("~/.thumbnails/normal/")):
1273 os.mkdir(os.path.expanduser("~/.thumbnails/normal/"))
1274
1275 def thumbpane_update_pending_images(self, force_upto_imgnum, foo):
1276 self.thumbpane_updating = True
1277 self.thumbpane_create_dir()
1278 # Check to see if any images need their thumbnails generated.
1279 curr_coord = 0
1280 imgnum = 0
1281 while (
1282 curr_coord < self.thumbpane_bottom_coord_loaded
1283 or imgnum <= force_upto_imgnum
1284 ):
1285 if self.closing_app or self.stop_now or not self.thumbpane_show:
1286 break
1287 if imgnum >= len(self.image_list):
1288 break
1289 self.thumbpane_set_image(self.image_list[imgnum], imgnum)
1290 curr_coord += self.thumbpane.get_background_area(
1291 (imgnum,), self.thumbcolumn
1292 ).height
1293 if force_upto_imgnum == imgnum:
1294 # Verify that the user hasn't switched images while we're loading thumbnails:
1295 if force_upto_imgnum == self.curr_img_in_list:
1296 gobject.idle_add(self.thumbpane_select, force_upto_imgnum)
1297 imgnum += 1
1298 self.thumbpane_updating = False
1299
1300 def thumbpane_clear_list(self):
1301 self.thumbpane_bottom_coord_loaded = 0
1302 self.thumbscroll.get_vscrollbar().handler_block(self.thumb_scroll_handler)
1303 self.thumblist.clear()
1304 self.thumbscroll.get_vscrollbar().handler_unblock(self.thumb_scroll_handler)
1305 for image in self.image_list:
1306 blank_pix = self.get_blank_pix_for_image(image)
1307 self.thumblist.append([blank_pix])
1308 self.thumbnail_loaded = [False] * len(self.image_list)
1309
1310 def thumbpane_set_image(self, image_name, imgnum, force_update=False):
1311 if self.thumbpane_show:
1312 if not self.thumbnail_loaded[imgnum] or force_update:
1313 filename, thumbfile = self.thumbnail_get_name(image_name)
1314 pix = self.thumbpane_get_pixbuf(thumbfile, filename, force_update)
1315 if pix:
1316 if self.thumbnail_size != 128:
1317 # 128 is the size of the saved thumbnail, so convert if different:
1318 pix, image_width, image_height = self.get_pixbuf_of_size(
1319 pix, self.thumbnail_size, gtk.gdk.INTERP_TILES
1320 )
1321 self.thumbnail_loaded[imgnum] = True
1322 self.thumbscroll.get_vscrollbar().handler_block(
1323 self.thumb_scroll_handler
1324 )
1325 pix = self.pixbuf_add_border(pix)
1326 try:
1327 self.thumblist[imgnum] = [pix]
1328 except:
1329 pass
1330 self.thumbscroll.get_vscrollbar().handler_unblock(
1331 self.thumb_scroll_handler
1332 )
1333
1334 def thumbnail_get_name(self, image_name):
1335 filename = os.path.expanduser("file://" + image_name)
1336 uriname = os.path.expanduser(
1337 "file://" + urllib.request.pathname2url(image_name)
1338 )
1339 if HAS_HASHLIB:
1340 m = hashlib.md5()
1341 else:
1342 m = md5.new()
1343 m.update(uriname)
1344 mhex = m.hexdigest()
1345 mhex_filename = os.path.expanduser("~/.thumbnails/normal/" + mhex + ".png")
1346 return filename, mhex_filename
1347
1348 def thumbpane_get_pixbuf(self, thumb_url, image_url, force_generation):
1349 # Returns a valid pixbuf or None if a pixbuf cannot be generated. Tries to re-use
1350 # a thumbnail from ~/.thumbails/normal/, otherwise generates one with the
1351 # XDG filename: md5(file:///full/path/to/image).png
1352 imgfile = image_url
1353 if imgfile[:7] == "file://":
1354 imgfile = imgfile[7:]
1355 try:
1356 if os.path.exists(thumb_url) and not force_generation:
1357 pix = gtk.gdk.pixbuf_new_from_file(thumb_url)
1358 pix_mtime = pix.get_option("tEXt::Thumb::MTime")
1359 if pix_mtime:
1360 st = os.stat(imgfile)
1361 file_mtime = str(st[stat.ST_MTIME])
1362 # If the mtimes match, we're good. if not, regenerate the thumbnail..
1363 if pix_mtime == file_mtime:
1364 return pix
1365 # Create the 128x128 thumbnail:
1366 uri = "file://" + urllib.request.pathname2url(imgfile)
1367 pix = gtk.gdk.pixbuf_new_from_file(imgfile)
1368 pix, image_width, image_height = self.get_pixbuf_of_size(
1369 pix, 128, gtk.gdk.INTERP_TILES
1370 )
1371 st = os.stat(imgfile)
1372 file_mtime = str(st[stat.ST_MTIME])
1373 # Save image to .thumbnails:
1374 pix.save(
1375 thumb_url,
1376 "png",
1377 {
1378 "tEXt::Thumb::URI": uri,
1379 "tEXt::Thumb::MTime": file_mtime,
1380 "tEXt::Software": "Mirage" + __version__,
1381 },
1382 )
1383 return pix
1384 except:
1385 return None
1386
1387 def thumbpane_load_image(self, treeview, imgnum):
1388 if imgnum != self.curr_img_in_list:
1389 gobject.idle_add(self.goto_image, str(imgnum), None)
1390
1391 def thumbpane_selection_changed(self, treeview):
1392 cancel = self.autosave_image()
1393 if cancel:
1394 # Revert selection...
1395 gobject.idle_add(self.thumbpane_select, self.curr_img_in_list)
1396 return True
1397 try:
1398 model, paths = self.thumbpane.get_selection().get_selected_rows()
1399 imgnum = paths[0][0]
1400 if not self.thumbnail_loaded[imgnum]:
1401 self.thumbpane_set_image(self.image_list[imgnum], imgnum)
1402 gobject.idle_add(self.thumbpane_load_image, treeview, imgnum)
1403 except:
1404 pass
1405
1406 def thumbpane_select(self, imgnum):
1407 if self.thumbpane_show:
1408 self.thumbpane.get_selection().handler_block(self.thumb_sel_handler)
1409 try:
1410 self.thumbpane.get_selection().select_path((imgnum,))
1411 self.thumbpane.scroll_to_cell((imgnum,))
1412 except:
1413 pass
1414 self.thumbpane.get_selection().handler_unblock(self.thumb_sel_handler)
1415
1416 def thumbpane_set_size(self):
1417 self.thumbcolumn.set_fixed_width(self.thumbpane_get_size())
1418 self.window_resized(None, self.window.allocation, True)
1419
1420 def thumbpane_get_size(self):
1421 return int(self.thumbnail_size * 1.3)
1422
1423 def thumbpane_scrolled(self, range):
1424 self.thumbpane_update_images()
1425
1426 def get_blank_pix_for_image(self, image):
1427 # Sizes the "blank image" icon for the thumbpane. This will ensure that we don't
1428 # load a humongous icon for a small pix, for example, and will keep the thumbnails
1429 # from shifting around when they are actually loaded.
1430 try:
1431 info = gtk.gdk.pixbuf_get_file_info(image)
1432 imgwidth = float(info[1])
1433 imgheight = float(info[2])
1434 if imgheight > self.thumbnail_size:
1435 if imgheight > imgwidth:
1436 imgheight = self.thumbnail_size
1437 else:
1438 imgheight = (imgheight // imgwidth) * self.thumbnail_size
1439 imgheight = 2 + int(
1440 imgheight
1441 ) # Account for border that will be added to thumbnails..
1442 imgwidth = self.thumbnail_size
1443 except:
1444 imgheight = 2 + self.thumbnail_size
1445 imgwidth = self.thumbnail_size
1446 blank_pix = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, imgwidth, imgheight)
1447 blank_pix.fill(0x00000000)
1448 imgwidth2 = int(imgheight * 0.8)
1449 imgheight2 = int(imgheight * 0.8)
1450 composite_pix = self.blank_image.scale_simple(
1451 imgwidth2, imgheight2, gtk.gdk.INTERP_BILINEAR
1452 )
1453 leftcoord = int((imgwidth - imgwidth2) // 2)
1454 topcoord = int((imgheight - imgheight2) // 2)
1455 composite_pix.copy_area(
1456 0, 0, imgwidth2, imgheight2, blank_pix, leftcoord, topcoord
1457 )
1458 return blank_pix
1459
1460 def find_path(self, filename, exit_on_fail=True):
1461 """ Find a pixmap or icon by looking through standard dirs.
10081462 If the image isn't found exit with error status 1 unless
10091463 exit_on_fail is set to False, then return None """
1010 if not self.resource_path_list:
1011 #If executed from mirage in bin this points to the basedir
1012 basedir_mirage = os.path.split(sys.path[0])[0]
1013 #If executed from mirage.py module in python lib this points to the basedir
1014 f0 = os.path.split(__file__)[0].split('/lib')[0]
1015 self.resource_path_list = list(set(filter(os.path.isdir, [
1016 os.path.join(basedir_mirage, 'share', 'mirage'),
1017 os.path.join(basedir_mirage, 'share', 'pixmaps'),
1018 os.path.join(sys.prefix, 'share', 'mirage'),
1019 os.path.join(sys.prefix, 'share', 'pixmaps'),
1020 os.path.join(sys.prefix, 'local', 'share', 'mirage'),
1021 os.path.join(sys.prefix, 'local', 'share', 'pixmaps'),
1022 sys.path[0], #If it's run non-installed
1023 os.path.join(f0, 'share', 'mirage'),
1024 os.path.join(f0, 'share', 'pixmaps'),
1025 ])))
1026 for path in self.resource_path_list:
1027 pix = os.path.join(path, filename)
1028 if os.path.exists(pix):
1029 return pix
1030 # If we reached here, we didn't find the pixmap
1031 if exit_on_fail:
1032 print(_("Couldn't find the image %s. Please check your installation.") % filename)
1033 sys.exit(1)
1034 else:
1035 return None
1036
1037 def gconf_key_changed(self, client, cnxn_id, entry, label):
1038 if entry.value.type == gconf.VALUE_STRING:
1039 style = entry.value.to_string()
1040 if style == "both":
1041 self.toolbar.set_style(gtk.TOOLBAR_BOTH)
1042 elif style == "both-horiz":
1043 self.toolbar.set_style(gtk.TOOLBAR_BOTH_HORIZ)
1044 elif style == "icons":
1045 self.toolbar.set_style(gtk.TOOLBAR_ICONS)
1046 elif style == "text":
1047 self.toolbar.set_style(gtk.TOOLBAR_TEXT)
1048 if self.image_loaded and self.last_image_action_was_fit:
1049 if self.last_image_action_was_smart_fit:
1050 self.zoom_to_fit_or_1_to_1(None, False, False)
1051 else:
1052 self.zoom_to_fit_window(None, False, False)
1053
1054 def toolbar_focused(self, widget, direction):
1055 self.layout.grab_focus()
1056 return True
1057
1058 def topwindow_keypress(self, widget, event):
1059 # For whatever reason, 'Left' and 'Right' cannot be used as menu
1060 # accelerators so we will manually check for them here:
1061 if (not (event.state & gtk.gdk.SHIFT_MASK)) and not (event.state & gtk.gdk.CONTROL_MASK) and not (event.state & gtk.gdk.MOD1_MASK) and not (event.state & gtk.gdk.MOD2_MASK) and not (event.state & gtk.gdk.CONTROL_MASK):
1062 if event.keyval == gtk.gdk.keyval_from_name('Left') or event.keyval == gtk.gdk.keyval_from_name('Up'):
1063 self.goto_prev_image(None)
1064 return
1065 elif event.keyval == gtk.gdk.keyval_from_name('Right') or event.keyval == gtk.gdk.keyval_from_name('Down'):
1066 self.goto_next_image(None)
1067 return
1068 shortcut = gtk.accelerator_name(event.keyval, event.state)
1069 if "Escape" in shortcut:
1070 self.stop_now = True
1071 self.searching_for_images = False
1072 while gtk.events_pending():
1073 gtk.main_iteration()
1074 self.update_title()
1075 return
1076
1077 def parse_action_command(self, command, batchmode):
1078 self.running_custom_actions = True
1079 self.change_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
1080 while gtk.events_pending():
1081 gtk.main_iteration()
1082 self.curr_custom_action = 0
1083 if batchmode:
1084 self.num_custom_actions = len(self.image_list)
1085 for i in range(self.num_custom_actions):
1086 self.curr_custom_action += 1
1087 self.update_statusbar()
1088 while gtk.events_pending():
1089 gtk.main_iteration()
1090 imagename = self.image_list[i]
1091 self.parse_action_command2(command, imagename)
1092 else:
1093 self.num_custom_actions = 1
1094 self.curr_custom_action = 1
1095 self.update_statusbar()
1096 while gtk.events_pending():
1097 gtk.main_iteration()
1098 self.parse_action_command2(command, self.currimg_name)
1099 gc.collect()
1100 self.change_cursor(None)
1101 # Refresh the current image or any preloaded needed if they have changed:
1102 if not os.path.exists(self.currimg_name):
1103 self.currimg_pixbuf_original = None
1104 self.image_load_failed(False)
1105 else:
1106 animtest = gtk.gdk.PixbufAnimation(self.currimg_name)
1107 if animtest.is_static_image():
1108 if self.images_are_different(animtest.get_static_image(), self.currimg_pixbuf_original):
1109 self.load_new_image2(False, False, True, False)
1110 else:
1111 if self.images_are_different(animtest, self.currimg_pixbuf_original):
1112 self.load_new_image2(False, False, True, False)
1113 self.running_custom_actions = False
1114 self.update_statusbar()
1115 while gtk.events_pending():
1116 gtk.main_iteration()
1117 if not os.path.exists(self.preloadimg_prev_name):
1118 self.preloadimg_prev_in_list = -1
1119 else:
1120 animtest = gtk.gdk.PixbufAnimation(self.preloadimg_prev_name)
1121 if animtest.is_static_image():
1122 if self.images_are_different(animtest.get_static_image(), self.preloadimg_prev_pixbuf_original):
1123 self.preloadimg_prev_in_list = -1
1124 self.preload_when_idle = gobject.idle_add(self.preload_prev_image, False)
1125 else:
1126 if self.images_are_different(animtest, self.preloadimg_prev_pixbuf_original):
1127 self.preloadimg_prev_in_list = -1
1128 self.preload_when_idle = gobject.idle_add(self.preload_prev_image, False)
1129 if not os.path.exists(self.preloadimg_next_name):
1130 self.preloadimg_next_in_list = -1
1131 else:
1132 animtest = gtk.gdk.PixbufAnimation(self.preloadimg_next_name)
1133 if animtest.is_static_image():
1134 if self.images_are_different(animtest.get_static_image(), self.preloadimg_next_pixbuf_original):
1135 self.preloadimg_next_in_list = -1
1136 self.preload_when_idle = gobject.idle_add(self.preload_next_image, False)
1137 else:
1138 if self.images_are_different(animtest, self.preloadimg_next_pixbuf_original):
1139 self.preloadimg_next_in_list = -1
1140 self.preload_when_idle = gobject.idle_add(self.preload_next_image, False)
1141 self.stop_now = False
1142 if batchmode:
1143 # Update all thumbnails:
1144 gobject.idle_add(self.thumbpane_update_images, True, self.curr_img_in_list)
1145 else:
1146 # Update only the current thumbnail:
1147 gobject.idle_add(self.thumbpane_set_image, self.image_list[self.curr_img_in_list], self.curr_img_in_list, True)
1148
1149 def images_are_different(self, pixbuf1, pixbuf2):
1150 if pixbuf1.get_pixels() == pixbuf2.get_pixels():
1151 return False
1152 else:
1153 return True
1154
1155 def recent_action_click(self, action):
1156 self.stop_now = True
1157 while gtk.events_pending():
1158 gtk.main_iteration()
1159 cancel = self.autosave_image()
1160 if cancel:
1161 return
1162 index = int(action.get_name())
1163 if os.path.isfile(self.recentfiles[index]) or os.path.exists(self.recentfiles[index]) or self.recentfiles[index].startswith('http://') or self.recentfiles[index].startswith('ftp://'):
1164 self.expand_filelist_and_load_image([self.recentfiles[index]])
1165 else:
1166 self.image_list = []
1167 self.curr_img_in_list = 0
1168 self.image_list.append(self.recentfiles[index])
1169 self.image_load_failed(False)
1170 self.recent_file_remove_and_refresh(index)
1171
1172 def recent_file_remove_and_refresh_name(self, rmfile):
1173 index_num = 0
1174 for imgfile in self.recentfiles:
1175 if imgfile == rmfile:
1176 self.recent_file_remove_and_refresh(index_num)
1177 break
1178 index_num += index_num
1179
1180 def recent_file_remove_and_refresh(self, index_num):
1181 i = index_num
1182 while i < len(self.recentfiles)-1:
1183 self.recentfiles[i] = self.recentfiles[i+1]
1184 i = i + 1
1185 # Set last item empty:
1186 self.recentfiles[len(self.recentfiles)-1] = ''
1187 self.refresh_recent_files_menu()
1188
1189 def recent_file_add_and_refresh(self, addfile):
1190 # First check if the filename is already in the list:
1191 for i in range(len(self.recentfiles)):
1192 if len(self.recentfiles[i]) > 0:
1193 if addfile == self.recentfiles[i]:
1194 # If found in list, put to position 1 and decrement the rest:
1195 j = i
1196 while j > 0:
1197 self.recentfiles[j] = self.recentfiles[j-1]
1198 j = j - 1
1199 self.recentfiles[0] = addfile
1200 self.refresh_recent_files_menu()
1201 return
1202 # If not found, put to position 1, decrement the rest:
1203 j = len(self.recentfiles)-1
1204 while j > 0:
1205 self.recentfiles[j] = self.recentfiles[j-1]
1206 j = j - 1
1207 if len(self.recentfiles) > 0:
1208 self.recentfiles[0] = addfile
1209 self.refresh_recent_files_menu()
1210
1211 def custom_action_click(self, action):
1212 if self.UIManager.get_widget('/MainMenu/EditMenu/ActionSubMenu/' + action.get_name()).get_property('sensitive'):
1213 for i in range(len(self.action_shortcuts)):
1214 try:
1215 if action.get_name() == self.action_names[i]:
1216 self.parse_action_command(self.action_commands[i], self.action_batch[i])
1217 except:
1218 pass
1219
1220
1221 def parse_action_command2(self, cmd, imagename):
1222 # Executes the given command using ``os.system``, substituting "%"-macros approprately.
1223 def sh_esc(s):
1224 import re
1225 return re.sub(r'[^/._a-zA-Z0-9-]', lambda c: '\\'+c.group(), s)
1226 cmd = cmd.strip()
1227 # [NEXT] and [PREV] are only valid alone or at the end of the command
1228 if cmd == "[NEXT]":
1229 self.goto_next_image(None)
1230 return
1231 elif cmd == "[PREV]":
1232 self.goto_prev_image(None)
1233 return
1234 # -1=go to previous, 1=go to next, 0=don't change
1235 prev_or_next=0
1236 if cmd[-6:] == "[NEXT]":
1237 prev_or_next=1
1238 cmd = cmd[:-6]
1239 elif cmd[-6:] == "[PREV]":
1240 prev_or_next=-1
1241 cmd = cmd[:-6]
1242 if "%F" in cmd:
1243 cmd = cmd.replace("%F", sh_esc(imagename))
1244 if "%N" in cmd:
1245 cmd = cmd.replace("%N", sh_esc(os.path.splitext(os.path.basename(imagename))[0]))
1246 if "%P" in cmd:
1247 cmd = cmd.replace("%P", sh_esc(os.path.dirname(imagename) + "/"))
1248 if "%E" in cmd:
1249 cmd = cmd.replace("%E", sh_esc(os.path.splitext(os.path.basename(imagename))[1]))
1250 if "%L" in cmd:
1251 cmd = cmd.replace("%L", " ".join([sh_esc(s) for s in self.image_list]))
1252 if self.verbose:
1253 print(_("Action: %s") % cmd)
1254 shell_rc = os.system(cmd) >> 8
1255 if self.verbose:
1256 print(_("Action return code: %s") % shell_rc)
1257 if shell_rc != 0:
1258 msg = _('Unable to launch \"%s\". Please specify a valid command from Edit > Custom Actions.') % cmd
1259 error_dialog = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_WARNING, gtk.BUTTONS_CLOSE, msg)
1260 error_dialog.set_title(_("Invalid Custom Action"))
1261 error_dialog.run()
1262 error_dialog.destroy()
1263 elif prev_or_next == 1:
1264 self.goto_next_image(None)
1265 elif prev_or_next == -1:
1266 self.goto_prev_image(None)
1267 self.running_custom_actions = False
1268
1269 def set_go_sensitivities(self, enable):
1270 self.UIManager.get_widget('/MainMenu/GoMenu/Previous Image').set_sensitive(enable)
1271 self.UIManager.get_widget('/MainMenu/GoMenu/Next Image').set_sensitive(enable)
1272 self.UIManager.get_widget('/MainMenu/GoMenu/Random Image').set_sensitive(enable)
1273 self.UIManager.get_widget('/MainMenu/GoMenu/First Image').set_sensitive(enable)
1274 self.UIManager.get_widget('/MainMenu/GoMenu/Last Image').set_sensitive(enable)
1275 self.UIManager.get_widget('/Popup/Previous Image').set_sensitive(enable)
1276 self.UIManager.get_widget('/Popup/Next Image').set_sensitive(enable)
1277 self.UIManager.get_widget('/MainToolbar/Previous2').set_sensitive(enable)
1278 self.UIManager.get_widget('/MainToolbar/Next2').set_sensitive(enable)
1279 self.ss_forward.set_sensitive(enable)
1280 self.ss_back.set_sensitive(enable)
1281
1282 def set_image_sensitivities(self, enable):
1283 self.set_zoom_in_sensitivities(enable)
1284 self.set_zoom_out_sensitivities(enable)
1285 self.UIManager.get_widget('/MainMenu/ViewMenu/1:1').set_sensitive(enable)
1286 self.UIManager.get_widget('/MainMenu/ViewMenu/Fit').set_sensitive(enable)
1287 self.UIManager.get_widget('/MainMenu/EditMenu/Delete Image').set_sensitive(enable)
1288 self.UIManager.get_widget('/MainMenu/EditMenu/Rename Image').set_sensitive(enable)
1289 self.UIManager.get_widget('/MainMenu/EditMenu/Crop').set_sensitive(enable)
1290 self.UIManager.get_widget('/MainMenu/EditMenu/Resize').set_sensitive(enable)
1291 self.UIManager.get_widget('/MainMenu/EditMenu/Saturation').set_sensitive(enable)
1292 self.UIManager.get_widget('/MainToolbar/1:1').set_sensitive(enable)
1293 self.UIManager.get_widget('/MainToolbar/Fit').set_sensitive(enable)
1294 self.UIManager.get_widget('/Popup/1:1').set_sensitive(enable)
1295 self.UIManager.get_widget('/Popup/Fit').set_sensitive(enable)
1296 self.UIManager.get_widget('/MainMenu/FileMenu/Save As').set_sensitive(enable)
1297 self.UIManager.get_widget('/MainMenu/FileMenu/Save').set_sensitive(False)
1298 self.UIManager.get_widget('/MainMenu/FileMenu/Properties').set_sensitive(False)
1299 # Only jpeg, png, and bmp images are currently supported for saving
1300 if len(self.image_list) > 0:
1301 try:
1302 filetype = gtk.gdk.pixbuf_get_file_info(self.currimg_name)[0]['name']
1303 self.UIManager.get_widget('/MainMenu/FileMenu/Properties').set_sensitive(True)
1304 if self.filetype_is_writable(filetype):
1305 self.UIManager.get_widget('/MainMenu/FileMenu/Save').set_sensitive(enable)
1306 except:
1307 self.UIManager.get_widget('/MainMenu/FileMenu/Save').set_sensitive(False)
1308 if self.actionGroupCustom:
1309 for action in self.action_names:
1310 self.UIManager.get_widget('/MainMenu/EditMenu/ActionSubMenu/' + action).set_sensitive(enable)
1311 if not HAS_IMGFUNCS:
1312 enable = False
1313 self.UIManager.get_widget('/MainMenu/EditMenu/Rotate Left').set_sensitive(enable)
1314 self.UIManager.get_widget('/MainMenu/EditMenu/Rotate Right').set_sensitive(enable)
1315 self.UIManager.get_widget('/MainMenu/EditMenu/Flip Vertically').set_sensitive(enable)
1316 self.UIManager.get_widget('/MainMenu/EditMenu/Flip Horizontally').set_sensitive(enable)
1317
1318 def set_zoom_in_sensitivities(self, enable):
1319 self.UIManager.get_widget('/MainMenu/ViewMenu/In').set_sensitive(enable)
1320 self.UIManager.get_widget('/MainToolbar/In').set_sensitive(enable)
1321 self.UIManager.get_widget('/Popup/In').set_sensitive(enable)
1322
1323 def set_zoom_out_sensitivities(self, enable):
1324 self.UIManager.get_widget('/MainMenu/ViewMenu/Out').set_sensitive(enable)
1325 self.UIManager.get_widget('/MainToolbar/Out').set_sensitive(enable)
1326 self.UIManager.get_widget('/Popup/Out').set_sensitive(enable)
1327
1328 def set_next_image_sensitivities(self, enable):
1329 self.UIManager.get_widget('/MainToolbar/Next2').set_sensitive(enable)
1330 self.UIManager.get_widget('/MainMenu/GoMenu/Next Image').set_sensitive(enable)
1331 self.UIManager.get_widget('/Popup/Next Image').set_sensitive(enable)
1332 self.ss_forward.set_sensitive(enable)
1333
1334 def set_previous_image_sensitivities(self, enable):
1335 self.UIManager.get_widget('/MainToolbar/Previous2').set_sensitive(enable)
1336 self.UIManager.get_widget('/MainMenu/GoMenu/Previous Image').set_sensitive(enable)
1337 self.UIManager.get_widget('/Popup/Previous Image').set_sensitive(enable)
1338 self.ss_back.set_sensitive(enable)
1339
1340 def set_first_image_sensitivities(self, enable):
1341 self.UIManager.get_widget('/MainMenu/GoMenu/First Image').set_sensitive(enable)
1342
1343 def set_last_image_sensitivities(self, enable):
1344 self.UIManager.get_widget('/MainMenu/GoMenu/Last Image').set_sensitive(enable)
1345
1346 def set_random_image_sensitivities(self, enable):
1347 self.UIManager.get_widget('/MainMenu/GoMenu/Random Image').set_sensitive(enable)
1348
1349 def set_slideshow_sensitivities(self):
1350 if len(self.image_list) <=1:
1351 self.UIManager.get_widget('/MainMenu/GoMenu/Start Slideshow').show()
1352 self.UIManager.get_widget('/MainMenu/GoMenu/Start Slideshow').set_sensitive(False)
1353 self.UIManager.get_widget('/MainMenu/GoMenu/Stop Slideshow').hide()
1354 self.UIManager.get_widget('/MainMenu/GoMenu/Stop Slideshow').set_sensitive(False)
1355 elif self.slideshow_mode:
1356 self.UIManager.get_widget('/MainMenu/GoMenu/Start Slideshow').hide()
1357 self.UIManager.get_widget('/MainMenu/GoMenu/Start Slideshow').set_sensitive(False)
1358 self.UIManager.get_widget('/MainMenu/GoMenu/Stop Slideshow').show()
1359 self.UIManager.get_widget('/MainMenu/GoMenu/Stop Slideshow').set_sensitive(True)
1360 else:
1361 self.UIManager.get_widget('/MainMenu/GoMenu/Start Slideshow').show()
1362 self.UIManager.get_widget('/MainMenu/GoMenu/Start Slideshow').set_sensitive(True)
1363 self.UIManager.get_widget('/MainMenu/GoMenu/Stop Slideshow').hide()
1364 self.UIManager.get_widget('/MainMenu/GoMenu/Stop Slideshow').set_sensitive(False)
1365 if self.slideshow_mode:
1366 self.UIManager.get_widget('/Popup/Start Slideshow').hide()
1367 self.UIManager.get_widget('/Popup/Stop Slideshow').show()
1368 else:
1369 self.UIManager.get_widget('/Popup/Start Slideshow').show()
1370 self.UIManager.get_widget('/Popup/Stop Slideshow').hide()
1371 if len(self.image_list) <=1:
1372 self.UIManager.get_widget('/Popup/Start Slideshow').set_sensitive(False)
1373 else:
1374 self.UIManager.get_widget('/Popup/Start Slideshow').set_sensitive(True)
1375
1376 def set_zoom_sensitivities(self):
1377 if not self.currimg_is_animation:
1378 self.set_zoom_out_sensitivities(True)
1379 self.set_zoom_in_sensitivities(True)
1380 else:
1381 self.set_zoom_out_sensitivities(False)
1382 self.set_zoom_in_sensitivities(False)
1383
1384 def print_version(self):
1385 print(_("Version: Mirage"), __version__)
1386 print(_("Website: http://mirageiv.berlios.de"))
1387
1388 def print_usage(self):
1389 self.print_version()
1390 print("")
1391 print(_("Usage: mirage [OPTION]... FILES|FOLDERS..."))
1392 print("")
1393 print(_("Options") + ":")
1394 print(" -h, --help " + _("Show this help and exit"))
1395 print(" -v, --version " + _("Show version information and exit"))
1396 print(" -V, --verbose " + _("Show more detailed information"))
1397 print(" -R, --recursive " + _("Recursively include all images found in"))
1398 print(" " + _("subdirectories of FOLDERS"))
1399 print(" -s, --slideshow " + _("Start in slideshow mode"))
1400 print(" -f, --fullscreen " + _("Start in fullscreen mode"))
1401 print(" -o, --onload 'cmd' " + _("Execute 'cmd' when an image is loaded"))
1402 print(" " + _("uses same syntax as custom actions,\n"))
1403 print(" " + _("i.e. mirage -o 'echo file is %F'"))
1404
1405 def delay_changed(self, action):
1406 self.curr_slideshow_delay = self.ss_delayspin.get_value()
1407 if self.slideshow_mode:
1408 gobject.source_remove(self.timer_delay)
1409 if self.curr_slideshow_random:
1410 self.timer_delay = gobject.timeout_add(int(self.curr_slideshow_delay*1000), self.goto_random_image, "ss")
1411 else:
1412 self.timer_delay = gobject.timeout_add((self.curr_slideshow_delay*1000), self.goto_next_image, "ss")
1413 self.window.set_focus(self.layout)
1414
1415 def random_changed(self, action):
1416 self.curr_slideshow_random = self.ss_randomize.get_active()
1417
1418 def motion_cb(self, widget, context, x, y, time):
1419 context.drag_status(gtk.gdk.ACTION_COPY, time)
1420 return True
1421
1422 def drop_cb(self, widget, context, x, y, selection, info, time):
1423 uri = selection.data.strip()
1424 path = urllib.request.url2pathname(uri)
1425 paths = path.rsplit('\n')
1426 for i, path in enumerate(paths):
1427 paths[i] = path.rstrip('\r')
1428 self.expand_filelist_and_load_image(paths)
1429
1430 def put_error_image_to_window(self):
1431 self.imageview.set_from_stock(gtk.STOCK_MISSING_IMAGE, gtk.ICON_SIZE_LARGE_TOOLBAR)
1432 self.currimg_width = self.imageview.size_request()[0]
1433 self.currimg_height = self.imageview.size_request()[1]
1434 self.center_image()
1435 self.set_go_sensitivities(False)
1436 self.set_image_sensitivities(False)
1437 self.update_statusbar()
1438 self.loaded_img_in_list = -1
1439 return
1440
1441 def expose_event(self, widget, event):
1442 if self.updating_adjustments:
1443 return
1444 self.updating_adjustments = True
1445 if self.hscroll.get_property('visible'):
1446 try:
1447 zoomratio = float(self.currimg_width)/self.previmg_width
1448 newvalue = abs(self.layout.get_hadjustment().get_value() * zoomratio + ((self.available_image_width()) * (zoomratio - 1)) // 2)
1449 if newvalue >= self.layout.get_hadjustment().lower and newvalue <= (self.layout.get_hadjustment().upper - self.layout.get_hadjustment().page_size):
1450 self.layout.get_hadjustment().set_value(newvalue)
1451 except:
1452 pass
1453 if self.vscroll.get_property('visible'):
1454 try:
1455 newvalue = abs(self.layout.get_vadjustment().get_value() * zoomratio + ((self.available_image_height()) * (zoomratio - 1)) // 2)
1456 if newvalue >= self.layout.get_vadjustment().lower and newvalue <= (self.layout.get_vadjustment().upper - self.layout.get_vadjustment().page_size):
1457 self.layout.get_vadjustment().set_value(newvalue)
1458 self.previmg_width = self.currimg_width
1459 except:
1460 pass
1461 self.updating_adjustments = False
1462
1463 def window_resized(self, widget, allocation, force_update=False):
1464 # Update the image size on window resize if the current image was last fit:
1465 if self.image_loaded:
1466 if force_update or allocation.width != self.prevwinwidth or allocation.height != self.prevwinheight:
1467 if self.last_image_action_was_fit:
1468 if self.last_image_action_was_smart_fit:
1469 self.zoom_to_fit_or_1_to_1(None, False, False)
1470 else:
1471 self.zoom_to_fit_window(None, False, False)
1472 else:
1473 self.center_image()
1474 self.load_new_image_stop_now()
1475 self.show_scrollbars_if_needed()
1476 # Also, regenerate preloaded image for new window size:
1477 self.preload_when_idle = gobject.idle_add(self.preload_next_image, True)
1478 self.preload_when_idle2 = gobject.idle_add(self.preload_prev_image, True)
1479 self.prevwinwidth = allocation.width
1480 self.prevwinheight = allocation.height
1481 return
1482
1483 def save_settings(self):
1484 conf = configparser.ConfigParser()
1485 conf.add_section('window')
1486 conf.set('window', 'w', self.window.get_allocation().width)
1487 conf.set('window', 'h', self.window.get_allocation().height)
1488 conf.set('window', 'toolbar', self.toolbar_show)
1489 conf.set('window', 'statusbar', self.statusbar_show)
1490 conf.set('window', 'thumbpane', self.thumbpane_show)
1491 conf.add_section('prefs')
1492 conf.set('prefs', 'simple-bgcolor', self.simple_bgcolor)
1493 conf.set('prefs', 'bgcolor-red', self.bgcolor.red)
1494 conf.set('prefs', 'bgcolor-green', self.bgcolor.green)
1495 conf.set('prefs', 'bgcolor-blue', self.bgcolor.blue)
1496 conf.set('prefs', 'open_all', self.open_all_images)
1497 conf.set('prefs', 'hidden', self.open_hidden_files)
1498 conf.set('prefs', 'use_last_dir', self.use_last_dir)
1499 conf.set('prefs', 'last_dir', self.last_dir)
1500 conf.set('prefs', 'fixed_dir', self.fixed_dir)
1501 conf.set('prefs', 'open_mode', self.open_mode)
1502 conf.set('prefs', 'last_mode', self.last_mode)
1503 conf.set('prefs', 'listwrap_mode', self.listwrap_mode)
1504 conf.set('prefs', 'slideshow_delay', int(self.slideshow_delay))
1505 conf.set('prefs', 'slideshow_random', self.slideshow_random)
1506 conf.set('prefs', 'zoomquality', self.zoomvalue)
1507 conf.set('prefs', 'quality_save', int(self.quality_save))
1508 conf.set('prefs', 'disable_screensaver', self.disable_screensaver)
1509 conf.set('prefs', 'slideshow_in_fullscreen', self.slideshow_in_fullscreen)
1510 conf.set('prefs', 'confirm_delete', self.confirm_delete)
1511 conf.set('prefs', 'preloading_images', self.preloading_images)
1512 conf.set('prefs', 'savemode', self.savemode)
1513 conf.set('prefs', 'start_in_fullscreen', self.start_in_fullscreen)
1514 conf.set('prefs', 'thumbsize', self.thumbnail_size)
1515 conf.set('prefs', 'screenshot_delay', self.screenshot_delay)
1516 conf.add_section('actions')
1517 conf.set('actions', 'num_actions', len(self.action_names))
1518 for i in range(len(self.action_names)):
1519 conf.set('actions', 'names[' + str(i) + ']', self.action_names[i])
1520 conf.set('actions', 'commands[' + str(i) + ']', self.action_commands[i])
1521 conf.set('actions', 'shortcuts[' + str(i) + ']', self.action_shortcuts[i])
1522 conf.set('actions', 'batch[' + str(i) + ']', self.action_batch[i])
1523 conf.add_section('recent')
1524 conf.set('recent', 'num_recent', len(self.recentfiles))
1525 for i in range(len(self.recentfiles)):
1526 conf.set('recent', 'num[' + str(i) + ']', len(self.recentfiles[i]))
1527 conf.set('recent', 'urls[' + str(i) + ',0]', self.recentfiles[i])
1528 if not os.path.exists(self.config_dir):
1529 os.makedirs(self.config_dir)
1530 conf.write(file(self.config_dir + '/miragerc', 'w'))
1531
1532 # Also, save accel_map:
1533 gtk.accel_map_save(self.config_dir + '/accel_map')
1534
1535 return
1536
1537 def delete_event(self, widget, event, data=None):
1538 cancel = self.autosave_image()
1539 if cancel:
1540 return True
1541 self.stop_now = True
1542 self.closing_app = True
1543 self.save_settings()
1544 sys.exit(0)
1545
1546 def destroy(self, event, data=None):
1547 cancel = self.autosave_image()
1548 if cancel:
1549 return True
1550 self.stop_now = True
1551 self.closing_app = True
1552 self.save_settings()
1553
1554 def exit_app(self, action):
1555 cancel = self.autosave_image()
1556 if cancel:
1557 return True
1558 self.stop_now = True
1559 self.closing_app = True
1560 self.save_settings()
1561 sys.exit(0)
1562
1563 def put_zoom_image_to_window(self, currimg_preloaded):
1564 self.window.window.freeze_updates()
1565 if not currimg_preloaded:
1566 # Always start with the original image to preserve quality!
1567 # Calculate image size:
1568 finalimg_width = int(self.currimg_pixbuf_original.get_width() * self.currimg_zoomratio)
1569 finalimg_height = int(self.currimg_pixbuf_original.get_height() * self.currimg_zoomratio)
1570 if not self.currimg_is_animation:
1571 # Scale image:
1572 if not self.currimg_pixbuf_original.get_has_alpha():
1573 self.currimg_pixbuf = self.currimg_pixbuf_original.scale_simple(finalimg_width, finalimg_height, self.zoom_quality)
1574 else:
1575 colormap = self.imageview.get_colormap()
1576 light_grey = colormap.alloc_color('#666666', True, True)
1577 dark_grey = colormap.alloc_color('#999999', True, True)
1578 self.currimg_pixbuf = self.currimg_pixbuf_original.composite_color_simple(finalimg_width, finalimg_height, self.zoom_quality, 255, 8, light_grey.pixel, dark_grey.pixel)
1579 else:
1580 self.currimg_pixbuf = self.currimg_pixbuf_original
1581 self.currimg_width, self.currimg_height = finalimg_width, finalimg_height
1582 self.layout.set_size(self.currimg_width, self.currimg_height)
1583 self.center_image()
1584 self.show_scrollbars_if_needed()
1585 if not self.currimg_is_animation:
1586 self.imageview.set_from_pixbuf(self.currimg_pixbuf)
1587 self.previmage_is_animation = False
1588 else:
1589 self.imageview.set_from_animation(self.currimg_pixbuf)
1590 self.previmage_is_animation = True
1591 # Clean up (free memory) because I'm lazy
1592 gc.collect()
1593 self.window.window.thaw_updates()
1594 self.loaded_img_in_list = self.curr_img_in_list
1595
1596 def show_scrollbars_if_needed(self):
1597 if self.currimg_width > self.available_image_width():
1598 self.hscroll.show()
1599 else:
1600 self.hscroll.hide()
1601 if self.currimg_height > self.available_image_height():
1602 self.vscroll.show()
1603 else:
1604 self.vscroll.hide()
1605
1606 def center_image(self):
1607 x_shift = int((self.available_image_width() - self.currimg_width) // 2)
1608 if x_shift < 0:
1609 x_shift = 0
1610 y_shift = int((self.available_image_height() - self.currimg_height) // 2)
1611 if y_shift < 0:
1612 y_shift = 0
1613 self.layout.move(self.imageview, x_shift, y_shift)
1614
1615 def available_image_width(self):
1616 width = self.window.get_size()[0]
1617 if not self.fullscreen_mode:
1618 if self.thumbpane_show:
1619 width -= self.thumbscroll.size_request()[0]
1620 return width
1621
1622 def available_image_height(self):
1623 height = self.window.get_size()[1]
1624 if not self.fullscreen_mode:
1625 height -= self.menubar.size_request()[1]
1626 if self.toolbar_show:
1627 height -= self.toolbar.size_request()[1]
1628 if self.statusbar_show:
1629 height -= self.statusbar.size_request()[1]
1630 return height
1631
1632 def save_image(self, action):
1633 if self.UIManager.get_widget('/MainMenu/FileMenu/Save').get_property('sensitive'):
1634 self.save_image_now(self.currimg_name, gtk.gdk.pixbuf_get_file_info(self.currimg_name)[0]['name'])
1635
1636 def save_image_as(self, action):
1637 dialog = gtk.FileChooserDialog(title=_("Save As"),action=gtk.FILE_CHOOSER_ACTION_SAVE,buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_SAVE,gtk.RESPONSE_OK))
1638 dialog.set_default_response(gtk.RESPONSE_OK)
1639 filename = os.path.basename(self.currimg_name)
1640 filetype = None
1641 dialog.set_current_folder(os.path.dirname(self.currimg_name))
1642 dialog.set_current_name(filename)
1643 dialog.set_do_overwrite_confirmation(True)
1644 response = dialog.run()
1645 if response == gtk.RESPONSE_OK:
1646 prev_name = self.currimg_name
1647 filename = dialog.get_filename()
1648 dialog.destroy()
1649 fileext = os.path.splitext(os.path.basename(filename))[1].lower()
1650 if len(fileext) > 0:
1651 fileext = fileext[1:]
1652 # Override filetype if user typed a filename with a different extension:
1653 for i in gtk.gdk.pixbuf_get_formats():
1654 if fileext in i['extensions']:
1655 filetype = i['name']
1656 self.save_image_now(filename, filetype)
1657 self.register_file_with_recent_docs(filename)
1658 else:
1659 dialog.destroy()
1660
1661 def save_image_now(self, dest_name, filetype):
1662 try:
1663 self.change_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
1664 while gtk.events_pending():
1665 gtk.main_iteration()
1666 if filetype == None:
1667 filetype = gtk.gdk.pixbuf_get_file_info(self.currimg_name)[0]['name']
1668 if self.filetype_is_writable(filetype):
1669 self.currimg_pixbuf_original.save(dest_name, filetype, {'quality': str(self.quality_save)})
1670 self.currimg_name = dest_name
1671 self.image_list[self.curr_img_in_list] = dest_name
1672 self.update_title()
1673 self.update_statusbar()
1674 # Update thumbnail:
1675 gobject.idle_add(self.thumbpane_set_image, dest_name, self.curr_img_in_list, True)
1676 self.image_modified = False
1677 else:
1678 error_dialog = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_WARNING, gtk.BUTTONS_YES_NO, _('The %s format is not supported for saving. Do you wish to save the file in a different format?') % filetype)
1679 error_dialog.set_title(_("Save"))
1680 response = error_dialog.run()
1681 if response == gtk.RESPONSE_YES:
1682 error_dialog.destroy()
1683 while gtk.events_pending():
1684 gtk.main_iteration()
1685 self.save_image_as(None)
1686 else:
1687 error_dialog.destroy()
1688 except:
1689 error_dialog = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_WARNING, gtk.BUTTONS_CLOSE, _('Unable to save %s') % dest_name)
1690 error_dialog.set_title(_("Save"))
1691 error_dialog.run()
1692 error_dialog.destroy()
1693 self.change_cursor(None)
1694
1695 def autosave_image(self):
1696 # Returns True if the user has canceled out of the dialog
1697 # Never call this function from an idle or timeout loop! That will cause
1698 # the app to freeze.
1699 if self.image_modified:
1700 if self.savemode == 1:
1701 temp = self.UIManager.get_widget('/MainMenu/FileMenu/Save').get_property('sensitive')
1702 self.UIManager.get_widget('/MainMenu/FileMenu/Save').set_property('sensitive', True)
1703 self.save_image(None)
1704 self.UIManager.get_widget('/MainMenu/FileMenu/Save').set_property('sensitive', temp)
1705 elif self.savemode == 2:
1706 dialog = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_QUESTION, gtk.BUTTONS_NONE, _("The current image has been modified. Save changes?"))
1707 dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
1708 dialog.add_button(gtk.STOCK_NO, gtk.RESPONSE_NO)
1709 dialog.add_button(gtk.STOCK_SAVE, gtk.RESPONSE_YES)
1710 dialog.set_title(_("Save?"))
1711 dialog.set_default_response(gtk.RESPONSE_YES)
1712 response = dialog.run()
1713 dialog.destroy()
1714 if response == gtk.RESPONSE_YES:
1715 temp = self.UIManager.get_widget('/MainMenu/FileMenu/Save').get_property('sensitive')
1716 self.UIManager.get_widget('/MainMenu/FileMenu/Save').set_property('sensitive', True)
1717 self.save_image(None)
1718 self.UIManager.get_widget('/MainMenu/FileMenu/Save').set_property('sensitive', temp)
1719 self.image_modified = False
1720 elif response == gtk.RESPONSE_NO:
1721 self.image_modified = False
1722 # Ensures that we don't use the current pixbuf for any preload pixbufs if we are in
1723 # the process of loading the previous or next image in the list:
1724 self.currimg_pixbuf = self.currimg_pixbuf_original
1725 self.preloadimg_next_in_list = -1
1726 self.preloadimg_prev_in_list = -1
1727 self.loaded_img_in_list = -1
1728 else:
1729 return True
1730
1731 def filetype_is_writable(self, filetype):
1732 # Determine if filetype is a writable format
1733 filetype_is_writable = True
1734 for i in gtk.gdk.pixbuf_get_formats():
1735 if filetype in i['extensions']:
1736 if i['is_writable']:
1737 return True
1738 return False
1739
1740 def open_file(self, action):
1741 self.stop_now = True
1742 while gtk.events_pending():
1743 gtk.main_iteration()
1744 self.open_file_or_folder(action, True)
1745
1746 def open_file_remote(self, action):
1747 # Prompt user for the url:
1748 dialog = gtk.Dialog(_("Open Remote"), self.window, gtk.DIALOG_MODAL, buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
1749 location = gtk.Entry()
1750 location.set_size_request(300, -1)
1751 location.set_activates_default(True)
1752 hbox = gtk.HBox()
1753 hbox.pack_start(gtk.Label(_("Image Location (URL):")), False, False, 5)
1754 hbox.pack_start(location, True, True, 5)
1755 dialog.vbox.pack_start(hbox, True, True, 10)
1756 dialog.set_default_response(gtk.RESPONSE_OK)
1757 dialog.vbox.show_all()
1758 dialog.connect('response', self.open_file_remote_response, location)
1759 response = dialog.show()
1760
1761 def open_file_remote_response(self, dialog, response, location):
1762 if response == gtk.RESPONSE_OK:
1763 filenames = []
1764 filenames.append(location.get_text())
1765 dialog.destroy()
1766 while gtk.events_pending():
1767 gtk.main_iteration()
1768 self.expand_filelist_and_load_image(filenames)
1769 else:
1770 dialog.destroy()
1771
1772 def open_folder(self, action):
1773 self.stop_now = True
1774 while gtk.events_pending():
1775 gtk.main_iteration()
1776 self.open_file_or_folder(action, False)
1777
1778 def open_file_or_folder(self, action, isfile):
1779 self.thumbpane_create_dir()
1780 cancel = self.autosave_image()
1781 if cancel:
1782 return
1783 # If isfile = True, file; If isfile = False, folder
1784 dialog = gtk.FileChooserDialog(title=_("Open"),action=gtk.FILE_CHOOSER_ACTION_OPEN,buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
1785 if isfile:
1786 filter = gtk.FileFilter()
1787 filter.set_name(_("Images"))
1788 filter.add_pixbuf_formats()
1789 dialog.add_filter(filter)
1790 filter = gtk.FileFilter()
1791 filter.set_name(_("All files"))
1792 filter.add_pattern("*")
1793 dialog.add_filter(filter)
1794 preview = gtk.Image()
1795 dialog.set_preview_widget(preview)
1796 dialog.set_use_preview_label(False)
1797 dialog.connect("update-preview", self.update_preview, preview)
1798 recursivebutton = None
1799 else:
1800 dialog.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
1801 recursivebutton = gtk.CheckButton(label=_("Include images in subdirectories"))
1802 dialog.set_extra_widget(recursivebutton)
1803 dialog.set_default_response(gtk.RESPONSE_OK)
1804 dialog.set_select_multiple(True)
1805 if self.use_last_dir:
1806 if self.last_dir != None:
1807 dialog.set_current_folder(self.last_dir)
1808 else:
1809 if self.fixed_dir != None:
1810 dialog.set_current_folder(self.fixed_dir)
1811 dialog.connect("response", self.open_file_or_folder_response, isfile, recursivebutton)
1812 response = dialog.show()
1813
1814 def open_file_or_folder_response(self, dialog, response, isfile, recursivebutton):
1815 if response == gtk.RESPONSE_OK:
1816 if self.use_last_dir:
1817 self.last_dir = dialog.get_current_folder()
1818 if not isfile and recursivebutton.get_property('active'):
1819 self.recursive = True
1820 filenames = dialog.get_filenames()
1821 dialog.destroy()
1822 while gtk.events_pending():
1823 gtk.main_iteration()
1824 self.expand_filelist_and_load_image(filenames)
1825 else:
1826 dialog.destroy()
1827
1828 def update_preview(self, file_chooser, preview):
1829 filename = file_chooser.get_preview_filename()
1830 if not filename:
1831 return
1832 filename, thumbfile = self.thumbnail_get_name(filename)
1833 pixbuf = self.thumbpane_get_pixbuf(thumbfile, filename, False)
1834 if pixbuf:
1835 preview.set_from_pixbuf(pixbuf)
1836 else:
1837 pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, 1, 8, 128, 128)
1838 pixbuf.fill(0x00000000)
1839 preview.set_from_pixbuf(pixbuf)
1840 have_preview = True
1841 file_chooser.set_preview_widget_active(have_preview)
1842 del pixbuf
1843 gc.collect()
1844
1845 def hide_cursor(self):
1846 if self.fullscreen_mode and not self.user_prompt_visible and not self.slideshow_controls_visible:
1847 pix_data = """/* XPM */
1464 if not self.resource_path_list:
1465 # If executed from mirage in bin this points to the basedir
1466 basedir_mirage = os.path.split(sys.path[0])[0]
1467 # If executed from mirage.py module in python lib this points to the basedir
1468 f0 = os.path.split(__file__)[0].split("/lib")[0]
1469 self.resource_path_list = list(
1470 set(
1471 filter(
1472 os.path.isdir,
1473 [
1474 os.path.join(basedir_mirage, "share", "mirage"),
1475 os.path.join(basedir_mirage, "share", "pixmaps"),
1476 os.path.join(sys.prefix, "share", "mirage"),
1477 os.path.join(sys.prefix, "share", "pixmaps"),
1478 os.path.join(sys.prefix, "local", "share", "mirage"),
1479 os.path.join(sys.prefix, "local", "share", "pixmaps"),
1480 sys.path[0], # If it's run non-installed
1481 os.path.join(f0, "share", "mirage"),
1482 os.path.join(f0, "share", "pixmaps"),
1483 ],
1484 )
1485 )
1486 )
1487 for path in self.resource_path_list:
1488 pix = os.path.join(path, filename)
1489 if os.path.exists(pix):
1490 return pix
1491 # If we reached here, we didn't find the pixmap
1492 if exit_on_fail:
1493 print(
1494 _("Couldn't find the image %s. Please check your installation.")
1495 % filename
1496 )
1497 sys.exit(1)
1498 else:
1499 return None
1500
1501 def gconf_key_changed(self, client, cnxn_id, entry, label):
1502 if entry.value.type == gconf.VALUE_STRING:
1503 style = entry.value.to_string()
1504 if style == "both":
1505 self.toolbar.set_style(gtk.TOOLBAR_BOTH)
1506 elif style == "both-horiz":
1507 self.toolbar.set_style(gtk.TOOLBAR_BOTH_HORIZ)
1508 elif style == "icons":
1509 self.toolbar.set_style(gtk.TOOLBAR_ICONS)
1510 elif style == "text":
1511 self.toolbar.set_style(gtk.TOOLBAR_TEXT)
1512 if self.image_loaded and self.last_image_action_was_fit:
1513 if self.last_image_action_was_smart_fit:
1514 self.zoom_to_fit_or_1_to_1(None, False, False)
1515 else:
1516 self.zoom_to_fit_window(None, False, False)
1517
1518 def toolbar_focused(self, widget, direction):
1519 self.layout.grab_focus()
1520 return True
1521
1522 def topwindow_keypress(self, widget, event):
1523 # For whatever reason, 'Left' and 'Right' cannot be used as menu
1524 # accelerators so we will manually check for them here:
1525 if (
1526 (not (event.state & gtk.gdk.SHIFT_MASK))
1527 and not (event.state & gtk.gdk.CONTROL_MASK)
1528 and not (event.state & gtk.gdk.MOD1_MASK)
1529 and not (event.state & gtk.gdk.MOD2_MASK)
1530 and not (event.state & gtk.gdk.CONTROL_MASK)
1531 ):
1532 if event.keyval == gtk.gdk.keyval_from_name(
1533 "Left"
1534 ) or event.keyval == gtk.gdk.keyval_from_name("Up"):
1535 self.goto_prev_image(None)
1536 return
1537 elif event.keyval == gtk.gdk.keyval_from_name(
1538 "Right"
1539 ) or event.keyval == gtk.gdk.keyval_from_name("Down"):
1540 self.goto_next_image(None)
1541 return
1542 shortcut = gtk.accelerator_name(event.keyval, event.state)
1543 if "Escape" in shortcut:
1544 self.stop_now = True
1545 self.searching_for_images = False
1546 while gtk.events_pending():
1547 gtk.main_iteration()
1548 self.update_title()
1549 return
1550
1551 def parse_action_command(self, command, batchmode):
1552 self.running_custom_actions = True
1553 self.change_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
1554 while gtk.events_pending():
1555 gtk.main_iteration()
1556 self.curr_custom_action = 0
1557 if batchmode:
1558 self.num_custom_actions = len(self.image_list)
1559 for i in range(self.num_custom_actions):
1560 self.curr_custom_action += 1
1561 self.update_statusbar()
1562 while gtk.events_pending():
1563 gtk.main_iteration()
1564 imagename = self.image_list[i]
1565 self.parse_action_command2(command, imagename)
1566 else:
1567 self.num_custom_actions = 1
1568 self.curr_custom_action = 1
1569 self.update_statusbar()
1570 while gtk.events_pending():
1571 gtk.main_iteration()
1572 self.parse_action_command2(command, self.currimg_name)
1573 gc.collect()
1574 self.change_cursor(None)
1575 # Refresh the current image or any preloaded needed if they have changed:
1576 if not os.path.exists(self.currimg_name):
1577 self.currimg_pixbuf_original = None
1578 self.image_load_failed(False)
1579 else:
1580 animtest = gtk.gdk.PixbufAnimation(self.currimg_name)
1581 if animtest.is_static_image():
1582 if self.images_are_different(
1583 animtest.get_static_image(), self.currimg_pixbuf_original
1584 ):
1585 self.load_new_image2(False, False, True, False)
1586 else:
1587 if self.images_are_different(animtest, self.currimg_pixbuf_original):
1588 self.load_new_image2(False, False, True, False)
1589 self.running_custom_actions = False
1590 self.update_statusbar()
1591 while gtk.events_pending():
1592 gtk.main_iteration()
1593 if not os.path.exists(self.preloadimg_prev_name):
1594 self.preloadimg_prev_in_list = -1
1595 else:
1596 animtest = gtk.gdk.PixbufAnimation(self.preloadimg_prev_name)
1597 if animtest.is_static_image():
1598 if self.images_are_different(
1599 animtest.get_static_image(), self.preloadimg_prev_pixbuf_original
1600 ):
1601 self.preloadimg_prev_in_list = -1
1602 self.preload_when_idle = gobject.idle_add(
1603 self.preload_prev_image, False
1604 )
1605 else:
1606 if self.images_are_different(
1607 animtest, self.preloadimg_prev_pixbuf_original
1608 ):
1609 self.preloadimg_prev_in_list = -1
1610 self.preload_when_idle = gobject.idle_add(
1611 self.preload_prev_image, False
1612 )
1613 if not os.path.exists(self.preloadimg_next_name):
1614 self.preloadimg_next_in_list = -1
1615 else:
1616 animtest = gtk.gdk.PixbufAnimation(self.preloadimg_next_name)
1617 if animtest.is_static_image():
1618 if self.images_are_different(
1619 animtest.get_static_image(), self.preloadimg_next_pixbuf_original
1620 ):
1621 self.preloadimg_next_in_list = -1
1622 self.preload_when_idle = gobject.idle_add(
1623 self.preload_next_image, False
1624 )
1625 else:
1626 if self.images_are_different(
1627 animtest, self.preloadimg_next_pixbuf_original
1628 ):
1629 self.preloadimg_next_in_list = -1
1630 self.preload_when_idle = gobject.idle_add(
1631 self.preload_next_image, False
1632 )
1633 self.stop_now = False
1634 if batchmode:
1635 # Update all thumbnails:
1636 gobject.idle_add(self.thumbpane_update_images, True, self.curr_img_in_list)
1637 else:
1638 # Update only the current thumbnail:
1639 gobject.idle_add(
1640 self.thumbpane_set_image,
1641 self.image_list[self.curr_img_in_list],
1642 self.curr_img_in_list,
1643 True,
1644 )
1645
1646 def images_are_different(self, pixbuf1, pixbuf2):
1647 if pixbuf1.get_pixels() == pixbuf2.get_pixels():
1648 return False
1649 else:
1650 return True
1651
1652 def recent_action_click(self, action):
1653 self.stop_now = True
1654 while gtk.events_pending():
1655 gtk.main_iteration()
1656 cancel = self.autosave_image()
1657 if cancel:
1658 return
1659 index = int(action.get_name())
1660 if (
1661 os.path.isfile(self.recentfiles[index])
1662 or os.path.exists(self.recentfiles[index])
1663 or self.recentfiles[index].startswith("http://")
1664 or self.recentfiles[index].startswith("ftp://")
1665 ):
1666 self.expand_filelist_and_load_image([self.recentfiles[index]])
1667 else:
1668 self.image_list = []
1669 self.curr_img_in_list = 0
1670 self.image_list.append(self.recentfiles[index])
1671 self.image_load_failed(False)
1672 self.recent_file_remove_and_refresh(index)
1673
1674 def recent_file_remove_and_refresh_name(self, rmfile):
1675 index_num = 0
1676 for imgfile in self.recentfiles:
1677 if imgfile == rmfile:
1678 self.recent_file_remove_and_refresh(index_num)
1679 break
1680 index_num += index_num
1681
1682 def recent_file_remove_and_refresh(self, index_num):
1683 i = index_num
1684 while i < len(self.recentfiles) - 1:
1685 self.recentfiles[i] = self.recentfiles[i + 1]
1686 i = i + 1
1687 # Set last item empty:
1688 self.recentfiles[len(self.recentfiles) - 1] = ""
1689 self.refresh_recent_files_menu()
1690
1691 def recent_file_add_and_refresh(self, addfile):
1692 # First check if the filename is already in the list:
1693 for i in range(len(self.recentfiles)):
1694 if len(self.recentfiles[i]) > 0:
1695 if addfile == self.recentfiles[i]:
1696 # If found in list, put to position 1 and decrement the rest:
1697 j = i
1698 while j > 0:
1699 self.recentfiles[j] = self.recentfiles[j - 1]
1700 j = j - 1
1701 self.recentfiles[0] = addfile
1702 self.refresh_recent_files_menu()
1703 return
1704 # If not found, put to position 1, decrement the rest:
1705 j = len(self.recentfiles) - 1
1706 while j > 0:
1707 self.recentfiles[j] = self.recentfiles[j - 1]
1708 j = j - 1
1709 if len(self.recentfiles) > 0:
1710 self.recentfiles[0] = addfile
1711 self.refresh_recent_files_menu()
1712
1713 def custom_action_click(self, action):
1714 if self.UIManager.get_widget(
1715 "/MainMenu/EditMenu/ActionSubMenu/" + action.get_name()
1716 ).get_property("sensitive"):
1717 for i in range(len(self.action_shortcuts)):
1718 try:
1719 if action.get_name() == self.action_names[i]:
1720 self.parse_action_command(
1721 self.action_commands[i], self.action_batch[i]
1722 )
1723 except:
1724 pass
1725
1726 def parse_action_command2(self, cmd, imagename):
1727 # Executes the given command using ``os.system``, substituting "%"-macros approprately.
1728 def sh_esc(s):
1729 import re
1730
1731 return re.sub(r"[^/._a-zA-Z0-9-]", lambda c: "\\" + c.group(), s)
1732
1733 cmd = cmd.strip()
1734 # [NEXT] and [PREV] are only valid alone or at the end of the command
1735 if cmd == "[NEXT]":
1736 self.goto_next_image(None)
1737 return
1738 elif cmd == "[PREV]":
1739 self.goto_prev_image(None)
1740 return
1741 # -1=go to previous, 1=go to next, 0=don't change
1742 prev_or_next = 0
1743 if cmd[-6:] == "[NEXT]":
1744 prev_or_next = 1
1745 cmd = cmd[:-6]
1746 elif cmd[-6:] == "[PREV]":
1747 prev_or_next = -1
1748 cmd = cmd[:-6]
1749 if "%F" in cmd:
1750 cmd = cmd.replace("%F", sh_esc(imagename))
1751 if "%N" in cmd:
1752 cmd = cmd.replace(
1753 "%N", sh_esc(os.path.splitext(os.path.basename(imagename))[0])
1754 )
1755 if "%P" in cmd:
1756 cmd = cmd.replace("%P", sh_esc(os.path.dirname(imagename) + "/"))
1757 if "%E" in cmd:
1758 cmd = cmd.replace(
1759 "%E", sh_esc(os.path.splitext(os.path.basename(imagename))[1])
1760 )
1761 if "%L" in cmd:
1762 cmd = cmd.replace("%L", " ".join([sh_esc(s) for s in self.image_list]))
1763 if self.verbose:
1764 print(_("Action: %s") % cmd)
1765 shell_rc = os.system(cmd) >> 8
1766 if self.verbose:
1767 print(_("Action return code: %s") % shell_rc)
1768 if shell_rc != 0:
1769 msg = (
1770 _(
1771 'Unable to launch "%s". Please specify a valid command from Edit > Custom Actions.'
1772 )
1773 % cmd
1774 )
1775 error_dialog = gtk.MessageDialog(
1776 self.window,
1777 gtk.DIALOG_MODAL,
1778 gtk.MESSAGE_WARNING,
1779 gtk.BUTTONS_CLOSE,
1780 msg,
1781 )
1782 error_dialog.set_title(_("Invalid Custom Action"))
1783 error_dialog.run()
1784 error_dialog.destroy()
1785 elif prev_or_next == 1:
1786 self.goto_next_image(None)
1787 elif prev_or_next == -1:
1788 self.goto_prev_image(None)
1789 self.running_custom_actions = False
1790
1791 def set_go_sensitivities(self, enable):
1792 self.UIManager.get_widget("/MainMenu/GoMenu/Previous Image").set_sensitive(
1793 enable
1794 )
1795 self.UIManager.get_widget("/MainMenu/GoMenu/Next Image").set_sensitive(enable)
1796 self.UIManager.get_widget("/MainMenu/GoMenu/Random Image").set_sensitive(enable)
1797 self.UIManager.get_widget("/MainMenu/GoMenu/First Image").set_sensitive(enable)
1798 self.UIManager.get_widget("/MainMenu/GoMenu/Last Image").set_sensitive(enable)
1799 self.UIManager.get_widget("/Popup/Previous Image").set_sensitive(enable)
1800 self.UIManager.get_widget("/Popup/Next Image").set_sensitive(enable)
1801 self.UIManager.get_widget("/MainToolbar/Previous2").set_sensitive(enable)
1802 self.UIManager.get_widget("/MainToolbar/Next2").set_sensitive(enable)
1803 self.ss_forward.set_sensitive(enable)
1804 self.ss_back.set_sensitive(enable)
1805
1806 def set_image_sensitivities(self, enable):
1807 self.set_zoom_in_sensitivities(enable)
1808 self.set_zoom_out_sensitivities(enable)
1809 self.UIManager.get_widget("/MainMenu/ViewMenu/1:1").set_sensitive(enable)
1810 self.UIManager.get_widget("/MainMenu/ViewMenu/Fit").set_sensitive(enable)
1811 self.UIManager.get_widget("/MainMenu/EditMenu/Delete Image").set_sensitive(
1812 enable
1813 )
1814 self.UIManager.get_widget("/MainMenu/EditMenu/Rename Image").set_sensitive(
1815 enable
1816 )
1817 self.UIManager.get_widget("/MainMenu/EditMenu/Crop").set_sensitive(enable)
1818 self.UIManager.get_widget("/MainMenu/EditMenu/Resize").set_sensitive(enable)
1819 self.UIManager.get_widget("/MainMenu/EditMenu/Saturation").set_sensitive(enable)
1820 self.UIManager.get_widget("/MainToolbar/1:1").set_sensitive(enable)
1821 self.UIManager.get_widget("/MainToolbar/Fit").set_sensitive(enable)
1822 self.UIManager.get_widget("/Popup/1:1").set_sensitive(enable)
1823 self.UIManager.get_widget("/Popup/Fit").set_sensitive(enable)
1824 self.UIManager.get_widget("/MainMenu/FileMenu/Save As").set_sensitive(enable)
1825 self.UIManager.get_widget("/MainMenu/FileMenu/Save").set_sensitive(False)
1826 self.UIManager.get_widget("/MainMenu/FileMenu/Properties").set_sensitive(False)
1827 # Only jpeg, png, and bmp images are currently supported for saving
1828 if len(self.image_list) > 0:
1829 try:
1830 filetype = gtk.gdk.pixbuf_get_file_info(self.currimg_name)[0]["name"]
1831 self.UIManager.get_widget(
1832 "/MainMenu/FileMenu/Properties"
1833 ).set_sensitive(True)
1834 if self.filetype_is_writable(filetype):
1835 self.UIManager.get_widget("/MainMenu/FileMenu/Save").set_sensitive(
1836 enable
1837 )
1838 except:
1839 self.UIManager.get_widget("/MainMenu/FileMenu/Save").set_sensitive(
1840 False
1841 )
1842 if self.actionGroupCustom:
1843 for action in self.action_names:
1844 self.UIManager.get_widget(
1845 "/MainMenu/EditMenu/ActionSubMenu/" + action
1846 ).set_sensitive(enable)
1847 if not HAS_IMGFUNCS:
1848 enable = False
1849 self.UIManager.get_widget("/MainMenu/EditMenu/Rotate Left").set_sensitive(
1850 enable
1851 )
1852 self.UIManager.get_widget("/MainMenu/EditMenu/Rotate Right").set_sensitive(
1853 enable
1854 )
1855 self.UIManager.get_widget("/MainMenu/EditMenu/Flip Vertically").set_sensitive(
1856 enable
1857 )
1858 self.UIManager.get_widget("/MainMenu/EditMenu/Flip Horizontally").set_sensitive(
1859 enable
1860 )
1861
1862 def set_zoom_in_sensitivities(self, enable):
1863 self.UIManager.get_widget("/MainMenu/ViewMenu/In").set_sensitive(enable)
1864 self.UIManager.get_widget("/MainToolbar/In").set_sensitive(enable)
1865 self.UIManager.get_widget("/Popup/In").set_sensitive(enable)
1866
1867 def set_zoom_out_sensitivities(self, enable):
1868 self.UIManager.get_widget("/MainMenu/ViewMenu/Out").set_sensitive(enable)
1869 self.UIManager.get_widget("/MainToolbar/Out").set_sensitive(enable)
1870 self.UIManager.get_widget("/Popup/Out").set_sensitive(enable)
1871
1872 def set_next_image_sensitivities(self, enable):
1873 self.UIManager.get_widget("/MainToolbar/Next2").set_sensitive(enable)
1874 self.UIManager.get_widget("/MainMenu/GoMenu/Next Image").set_sensitive(enable)
1875 self.UIManager.get_widget("/Popup/Next Image").set_sensitive(enable)
1876 self.ss_forward.set_sensitive(enable)
1877
1878 def set_previous_image_sensitivities(self, enable):
1879 self.UIManager.get_widget("/MainToolbar/Previous2").set_sensitive(enable)
1880 self.UIManager.get_widget("/MainMenu/GoMenu/Previous Image").set_sensitive(
1881 enable
1882 )
1883 self.UIManager.get_widget("/Popup/Previous Image").set_sensitive(enable)
1884 self.ss_back.set_sensitive(enable)
1885
1886 def set_first_image_sensitivities(self, enable):
1887 self.UIManager.get_widget("/MainMenu/GoMenu/First Image").set_sensitive(enable)
1888
1889 def set_last_image_sensitivities(self, enable):
1890 self.UIManager.get_widget("/MainMenu/GoMenu/Last Image").set_sensitive(enable)
1891
1892 def set_random_image_sensitivities(self, enable):
1893 self.UIManager.get_widget("/MainMenu/GoMenu/Random Image").set_sensitive(enable)
1894
1895 def set_slideshow_sensitivities(self):
1896 if len(self.image_list) <= 1:
1897 self.UIManager.get_widget("/MainMenu/GoMenu/Start Slideshow").show()
1898 self.UIManager.get_widget("/MainMenu/GoMenu/Start Slideshow").set_sensitive(
1899 False
1900 )
1901 self.UIManager.get_widget("/MainMenu/GoMenu/Stop Slideshow").hide()
1902 self.UIManager.get_widget("/MainMenu/GoMenu/Stop Slideshow").set_sensitive(
1903 False
1904 )
1905 elif self.slideshow_mode:
1906 self.UIManager.get_widget("/MainMenu/GoMenu/Start Slideshow").hide()
1907 self.UIManager.get_widget("/MainMenu/GoMenu/Start Slideshow").set_sensitive(
1908 False
1909 )
1910 self.UIManager.get_widget("/MainMenu/GoMenu/Stop Slideshow").show()
1911 self.UIManager.get_widget("/MainMenu/GoMenu/Stop Slideshow").set_sensitive(
1912 True
1913 )
1914 else:
1915 self.UIManager.get_widget("/MainMenu/GoMenu/Start Slideshow").show()
1916 self.UIManager.get_widget("/MainMenu/GoMenu/Start Slideshow").set_sensitive(
1917 True
1918 )
1919 self.UIManager.get_widget("/MainMenu/GoMenu/Stop Slideshow").hide()
1920 self.UIManager.get_widget("/MainMenu/GoMenu/Stop Slideshow").set_sensitive(
1921 False
1922 )
1923 if self.slideshow_mode:
1924 self.UIManager.get_widget("/Popup/Start Slideshow").hide()
1925 self.UIManager.get_widget("/Popup/Stop Slideshow").show()
1926 else:
1927 self.UIManager.get_widget("/Popup/Start Slideshow").show()
1928 self.UIManager.get_widget("/Popup/Stop Slideshow").hide()
1929 if len(self.image_list) <= 1:
1930 self.UIManager.get_widget("/Popup/Start Slideshow").set_sensitive(False)
1931 else:
1932 self.UIManager.get_widget("/Popup/Start Slideshow").set_sensitive(True)
1933
1934 def set_zoom_sensitivities(self):
1935 if not self.currimg_is_animation:
1936 self.set_zoom_out_sensitivities(True)
1937 self.set_zoom_in_sensitivities(True)
1938 else:
1939 self.set_zoom_out_sensitivities(False)
1940 self.set_zoom_in_sensitivities(False)
1941
1942 def print_version(self):
1943 print(_("Version: Mirage"), __version__)
1944 print(_("Website: http://mirageiv.berlios.de"))
1945
1946 def print_usage(self):
1947 self.print_version()
1948 print("")
1949 print(_("Usage: mirage [OPTION]... FILES|FOLDERS..."))
1950 print("")
1951 print(_("Options") + ":")
1952 print(" -h, --help " + _("Show this help and exit"))
1953 print(" -v, --version " + _("Show version information and exit"))
1954 print(" -V, --verbose " + _("Show more detailed information"))
1955 print(" -R, --recursive " + _("Recursively include all images found in"))
1956 print(" " + _("subdirectories of FOLDERS"))
1957 print(" -s, --slideshow " + _("Start in slideshow mode"))
1958 print(" -f, --fullscreen " + _("Start in fullscreen mode"))
1959 print(" -o, --onload 'cmd' " + _("Execute 'cmd' when an image is loaded"))
1960 print(" " + _("uses same syntax as custom actions,\n"))
1961 print(" " + _("i.e. mirage -o 'echo file is %F'"))
1962
1963 def delay_changed(self, action):
1964 self.curr_slideshow_delay = self.ss_delayspin.get_value()
1965 if self.slideshow_mode:
1966 gobject.source_remove(self.timer_delay)
1967 if self.curr_slideshow_random:
1968 self.timer_delay = gobject.timeout_add(
1969 int(self.curr_slideshow_delay * 1000), self.goto_random_image, "ss"
1970 )
1971 else:
1972 self.timer_delay = gobject.timeout_add(
1973 (self.curr_slideshow_delay * 1000), self.goto_next_image, "ss"
1974 )
1975 self.window.set_focus(self.layout)
1976
1977 def random_changed(self, action):
1978 self.curr_slideshow_random = self.ss_randomize.get_active()
1979
1980 def motion_cb(self, widget, context, x, y, time):
1981 context.drag_status(gtk.gdk.ACTION_COPY, time)
1982 return True
1983
1984 def drop_cb(self, widget, context, x, y, selection, info, time):
1985 uri = selection.data.strip()
1986 path = urllib.request.url2pathname(uri)
1987 paths = path.rsplit("\n")
1988 for i, path in enumerate(paths):
1989 paths[i] = path.rstrip("\r")
1990 self.expand_filelist_and_load_image(paths)
1991
1992 def put_error_image_to_window(self):
1993 self.imageview.set_from_stock(
1994 gtk.STOCK_MISSING_IMAGE, gtk.ICON_SIZE_LARGE_TOOLBAR
1995 )
1996 self.currimg_width = self.imageview.size_request()[0]
1997 self.currimg_height = self.imageview.size_request()[1]
1998 self.center_image()
1999 self.set_go_sensitivities(False)
2000 self.set_image_sensitivities(False)
2001 self.update_statusbar()
2002 self.loaded_img_in_list = -1
2003 return
2004
2005 def expose_event(self, widget, event):
2006 if self.updating_adjustments:
2007 return
2008 self.updating_adjustments = True
2009 if self.hscroll.get_property("visible"):
2010 try:
2011 zoomratio = float(self.currimg_width) / self.previmg_width
2012 newvalue = abs(
2013 self.layout.get_hadjustment().get_value() * zoomratio
2014 + ((self.available_image_width()) * (zoomratio - 1)) // 2
2015 )
2016 if newvalue >= self.layout.get_hadjustment().lower and newvalue <= (
2017 self.layout.get_hadjustment().upper
2018 - self.layout.get_hadjustment().page_size
2019 ):
2020 self.layout.get_hadjustment().set_value(newvalue)
2021 except:
2022 pass
2023 if self.vscroll.get_property("visible"):
2024 try:
2025 newvalue = abs(
2026 self.layout.get_vadjustment().get_value() * zoomratio
2027 + ((self.available_image_height()) * (zoomratio - 1)) // 2
2028 )
2029 if newvalue >= self.layout.get_vadjustment().lower and newvalue <= (
2030 self.layout.get_vadjustment().upper
2031 - self.layout.get_vadjustment().page_size
2032 ):
2033 self.layout.get_vadjustment().set_value(newvalue)
2034 self.previmg_width = self.currimg_width
2035 except:
2036 pass
2037 self.updating_adjustments = False
2038
2039 def window_resized(self, widget, allocation, force_update=False):
2040 # Update the image size on window resize if the current image was last fit:
2041 if self.image_loaded:
2042 if (
2043 force_update
2044 or allocation.width != self.prevwinwidth
2045 or allocation.height != self.prevwinheight
2046 ):
2047 if self.last_image_action_was_fit:
2048 if self.last_image_action_was_smart_fit:
2049 self.zoom_to_fit_or_1_to_1(None, False, False)
2050 else:
2051 self.zoom_to_fit_window(None, False, False)
2052 else:
2053 self.center_image()
2054 self.load_new_image_stop_now()
2055 self.show_scrollbars_if_needed()
2056 # Also, regenerate preloaded image for new window size:
2057 self.preload_when_idle = gobject.idle_add(self.preload_next_image, True)
2058 self.preload_when_idle2 = gobject.idle_add(
2059 self.preload_prev_image, True
2060 )
2061 self.prevwinwidth = allocation.width
2062 self.prevwinheight = allocation.height
2063 return
2064
2065 def save_settings(self):
2066 conf = configparser.ConfigParser()
2067 conf.add_section("window")
2068 conf.set("window", "w", self.window.get_allocation().width)
2069 conf.set("window", "h", self.window.get_allocation().height)
2070 conf.set("window", "toolbar", self.toolbar_show)
2071 conf.set("window", "statusbar", self.statusbar_show)
2072 conf.set("window", "thumbpane", self.thumbpane_show)
2073 conf.add_section("prefs")
2074 conf.set("prefs", "simple-bgcolor", self.simple_bgcolor)
2075 conf.set("prefs", "bgcolor-red", self.bgcolor.red)
2076 conf.set("prefs", "bgcolor-green", self.bgcolor.green)
2077 conf.set("prefs", "bgcolor-blue", self.bgcolor.blue)
2078 conf.set("prefs", "open_all", self.open_all_images)
2079 conf.set("prefs", "hidden", self.open_hidden_files)
2080 conf.set("prefs", "use_last_dir", self.use_last_dir)
2081 conf.set("prefs", "last_dir", self.last_dir)
2082 conf.set("prefs", "fixed_dir", self.fixed_dir)
2083 conf.set("prefs", "open_mode", self.open_mode)
2084 conf.set("prefs", "last_mode", self.last_mode)
2085 conf.set("prefs", "listwrap_mode", self.listwrap_mode)
2086 conf.set("prefs", "slideshow_delay", int(self.slideshow_delay))
2087 conf.set("prefs", "slideshow_random", self.slideshow_random)
2088 conf.set("prefs", "zoomquality", self.zoomvalue)
2089 conf.set("prefs", "quality_save", int(self.quality_save))
2090 conf.set("prefs", "disable_screensaver", self.disable_screensaver)
2091 conf.set("prefs", "slideshow_in_fullscreen", self.slideshow_in_fullscreen)
2092 conf.set("prefs", "confirm_delete", self.confirm_delete)
2093 conf.set("prefs", "preloading_images", self.preloading_images)
2094 conf.set("prefs", "savemode", self.savemode)
2095 conf.set("prefs", "start_in_fullscreen", self.start_in_fullscreen)
2096 conf.set("prefs", "thumbsize", self.thumbnail_size)
2097 conf.set("prefs", "screenshot_delay", self.screenshot_delay)
2098 conf.add_section("actions")
2099 conf.set("actions", "num_actions", len(self.action_names))
2100 for i in range(len(self.action_names)):
2101 conf.set("actions", "names[" + str(i) + "]", self.action_names[i])
2102 conf.set("actions", "commands[" + str(i) + "]", self.action_commands[i])
2103 conf.set("actions", "shortcuts[" + str(i) + "]", self.action_shortcuts[i])
2104 conf.set("actions", "batch[" + str(i) + "]", self.action_batch[i])
2105 conf.add_section("recent")
2106 conf.set("recent", "num_recent", len(self.recentfiles))
2107 for i in range(len(self.recentfiles)):
2108 conf.set("recent", "num[" + str(i) + "]", len(self.recentfiles[i]))
2109 conf.set("recent", "urls[" + str(i) + ",0]", self.recentfiles[i])
2110 if not os.path.exists(self.config_dir):
2111 os.makedirs(self.config_dir)
2112 conf.write(file(self.config_dir + "/miragerc", "w"))
2113
2114 # Also, save accel_map:
2115 gtk.accel_map_save(self.config_dir + "/accel_map")
2116
2117 return
2118
2119 def delete_event(self, widget, event, data=None):
2120 cancel = self.autosave_image()
2121 if cancel:
2122 return True
2123 self.stop_now = True
2124 self.closing_app = True
2125 self.save_settings()
2126 sys.exit(0)
2127
2128 def destroy(self, event, data=None):
2129 cancel = self.autosave_image()
2130 if cancel:
2131 return True
2132 self.stop_now = True
2133 self.closing_app = True
2134 self.save_settings()
2135
2136 def exit_app(self, action):
2137 cancel = self.autosave_image()
2138 if cancel:
2139 return True
2140 self.stop_now = True
2141 self.closing_app = True
2142 self.save_settings()
2143 sys.exit(0)
2144
2145 def put_zoom_image_to_window(self, currimg_preloaded):
2146 self.window.window.freeze_updates()
2147 if not currimg_preloaded:
2148 # Always start with the original image to preserve quality!
2149 # Calculate image size:
2150 finalimg_width = int(
2151 self.currimg_pixbuf_original.get_width() * self.currimg_zoomratio
2152 )
2153 finalimg_height = int(
2154 self.currimg_pixbuf_original.get_height() * self.currimg_zoomratio
2155 )
2156 if not self.currimg_is_animation:
2157 # Scale image:
2158 if not self.currimg_pixbuf_original.get_has_alpha():
2159 self.currimg_pixbuf = self.currimg_pixbuf_original.scale_simple(
2160 finalimg_width, finalimg_height, self.zoom_quality
2161 )
2162 else:
2163 colormap = self.imageview.get_colormap()
2164 light_grey = colormap.alloc_color("#666666", True, True)
2165 dark_grey = colormap.alloc_color("#999999", True, True)
2166 self.currimg_pixbuf = self.currimg_pixbuf_original.composite_color_simple(
2167 finalimg_width,
2168 finalimg_height,
2169 self.zoom_quality,
2170 255,
2171 8,
2172 light_grey.pixel,
2173 dark_grey.pixel,
2174 )
2175 else:
2176 self.currimg_pixbuf = self.currimg_pixbuf_original
2177 self.currimg_width, self.currimg_height = finalimg_width, finalimg_height
2178 self.layout.set_size(self.currimg_width, self.currimg_height)
2179 self.center_image()
2180 self.show_scrollbars_if_needed()
2181 if not self.currimg_is_animation:
2182 self.imageview.set_from_pixbuf(self.currimg_pixbuf)
2183 self.previmage_is_animation = False
2184 else:
2185 self.imageview.set_from_animation(self.currimg_pixbuf)
2186 self.previmage_is_animation = True
2187 # Clean up (free memory) because I'm lazy
2188 gc.collect()
2189 self.window.window.thaw_updates()
2190 self.loaded_img_in_list = self.curr_img_in_list
2191
2192 def show_scrollbars_if_needed(self):
2193 if self.currimg_width > self.available_image_width():
2194 self.hscroll.show()
2195 else:
2196 self.hscroll.hide()
2197 if self.currimg_height > self.available_image_height():
2198 self.vscroll.show()
2199 else:
2200 self.vscroll.hide()
2201
2202 def center_image(self):
2203 x_shift = int((self.available_image_width() - self.currimg_width) // 2)
2204 if x_shift < 0:
2205 x_shift = 0
2206 y_shift = int((self.available_image_height() - self.currimg_height) // 2)
2207 if y_shift < 0:
2208 y_shift = 0
2209 self.layout.move(self.imageview, x_shift, y_shift)
2210
2211 def available_image_width(self):
2212 width = self.window.get_size()[0]
2213 if not self.fullscreen_mode:
2214 if self.thumbpane_show:
2215 width -= self.thumbscroll.size_request()[0]
2216 return width
2217
2218 def available_image_height(self):
2219 height = self.window.get_size()[1]
2220 if not self.fullscreen_mode:
2221 height -= self.menubar.size_request()[1]
2222 if self.toolbar_show:
2223 height -= self.toolbar.size_request()[1]
2224 if self.statusbar_show:
2225 height -= self.statusbar.size_request()[1]
2226 return height
2227
2228 def save_image(self, action):
2229 if self.UIManager.get_widget("/MainMenu/FileMenu/Save").get_property(
2230 "sensitive"
2231 ):
2232 self.save_image_now(
2233 self.currimg_name,
2234 gtk.gdk.pixbuf_get_file_info(self.currimg_name)[0]["name"],
2235 )
2236
2237 def save_image_as(self, action):
2238 dialog = gtk.FileChooserDialog(
2239 title=_("Save As"),
2240 action=gtk.FILE_CHOOSER_ACTION_SAVE,
2241 buttons=(
2242 gtk.STOCK_CANCEL,
2243 gtk.RESPONSE_CANCEL,
2244 gtk.STOCK_SAVE,
2245 gtk.RESPONSE_OK,
2246 ),
2247 )
2248 dialog.set_default_response(gtk.RESPONSE_OK)
2249 filename = os.path.basename(self.currimg_name)
2250 filetype = None
2251 dialog.set_current_folder(os.path.dirname(self.currimg_name))
2252 dialog.set_current_name(filename)
2253 dialog.set_do_overwrite_confirmation(True)
2254 response = dialog.run()
2255 if response == gtk.RESPONSE_OK:
2256 prev_name = self.currimg_name
2257 filename = dialog.get_filename()
2258 dialog.destroy()
2259 fileext = os.path.splitext(os.path.basename(filename))[1].lower()
2260 if len(fileext) > 0:
2261 fileext = fileext[1:]
2262 # Override filetype if user typed a filename with a different extension:
2263 for i in gtk.gdk.pixbuf_get_formats():
2264 if fileext in i["extensions"]:
2265 filetype = i["name"]
2266 self.save_image_now(filename, filetype)
2267 self.register_file_with_recent_docs(filename)
2268 else:
2269 dialog.destroy()
2270
2271 def save_image_now(self, dest_name, filetype):
2272 try:
2273 self.change_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
2274 while gtk.events_pending():
2275 gtk.main_iteration()
2276 if filetype == None:
2277 filetype = gtk.gdk.pixbuf_get_file_info(self.currimg_name)[0]["name"]
2278 if self.filetype_is_writable(filetype):
2279 self.currimg_pixbuf_original.save(
2280 dest_name, filetype, {"quality": str(self.quality_save)}
2281 )
2282 self.currimg_name = dest_name
2283 self.image_list[self.curr_img_in_list] = dest_name
2284 self.update_title()
2285 self.update_statusbar()
2286 # Update thumbnail:
2287 gobject.idle_add(
2288 self.thumbpane_set_image, dest_name, self.curr_img_in_list, True
2289 )
2290 self.image_modified = False
2291 else:
2292 error_dialog = gtk.MessageDialog(
2293 self.window,
2294 gtk.DIALOG_MODAL,
2295 gtk.MESSAGE_WARNING,
2296 gtk.BUTTONS_YES_NO,
2297 _(
2298 "The %s format is not supported for saving. Do you wish to save the file in a different format?"
2299 )
2300 % filetype,
2301 )
2302 error_dialog.set_title(_("Save"))
2303 response = error_dialog.run()
2304 if response == gtk.RESPONSE_YES:
2305 error_dialog.destroy()
2306 while gtk.events_pending():
2307 gtk.main_iteration()
2308 self.save_image_as(None)
2309 else:
2310 error_dialog.destroy()
2311 except:
2312 error_dialog = gtk.MessageDialog(
2313 self.window,
2314 gtk.DIALOG_MODAL,
2315 gtk.MESSAGE_WARNING,
2316 gtk.BUTTONS_CLOSE,
2317 _("Unable to save %s") % dest_name,
2318 )
2319 error_dialog.set_title(_("Save"))
2320 error_dialog.run()
2321 error_dialog.destroy()
2322 self.change_cursor(None)
2323
2324 def autosave_image(self):
2325 # Returns True if the user has canceled out of the dialog
2326 # Never call this function from an idle or timeout loop! That will cause
2327 # the app to freeze.
2328 if self.image_modified:
2329 if self.savemode == 1:
2330 temp = self.UIManager.get_widget(
2331 "/MainMenu/FileMenu/Save"
2332 ).get_property("sensitive")
2333 self.UIManager.get_widget("/MainMenu/FileMenu/Save").set_property(
2334 "sensitive", True
2335 )
2336 self.save_image(None)
2337 self.UIManager.get_widget("/MainMenu/FileMenu/Save").set_property(
2338 "sensitive", temp
2339 )
2340 elif self.savemode == 2:
2341 dialog = gtk.MessageDialog(
2342 self.window,
2343 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
2344 gtk.MESSAGE_QUESTION,
2345 gtk.BUTTONS_NONE,
2346 _("The current image has been modified. Save changes?"),
2347 )
2348 dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
2349 dialog.add_button(gtk.STOCK_NO, gtk.RESPONSE_NO)
2350 dialog.add_button(gtk.STOCK_SAVE, gtk.RESPONSE_YES)
2351 dialog.set_title(_("Save?"))
2352 dialog.set_default_response(gtk.RESPONSE_YES)
2353 response = dialog.run()
2354 dialog.destroy()
2355 if response == gtk.RESPONSE_YES:
2356 temp = self.UIManager.get_widget(
2357 "/MainMenu/FileMenu/Save"
2358 ).get_property("sensitive")
2359 self.UIManager.get_widget("/MainMenu/FileMenu/Save").set_property(
2360 "sensitive", True
2361 )
2362 self.save_image(None)
2363 self.UIManager.get_widget("/MainMenu/FileMenu/Save").set_property(
2364 "sensitive", temp
2365 )
2366 self.image_modified = False
2367 elif response == gtk.RESPONSE_NO:
2368 self.image_modified = False
2369 # Ensures that we don't use the current pixbuf for any preload pixbufs if we are in
2370 # the process of loading the previous or next image in the list:
2371 self.currimg_pixbuf = self.currimg_pixbuf_original
2372 self.preloadimg_next_in_list = -1
2373 self.preloadimg_prev_in_list = -1
2374 self.loaded_img_in_list = -1
2375 else:
2376 return True
2377
2378 def filetype_is_writable(self, filetype):
2379 # Determine if filetype is a writable format
2380 filetype_is_writable = True
2381 for i in gtk.gdk.pixbuf_get_formats():
2382 if filetype in i["extensions"]:
2383 if i["is_writable"]:
2384 return True
2385 return False
2386
2387 def open_file(self, action):
2388 self.stop_now = True
2389 while gtk.events_pending():
2390 gtk.main_iteration()
2391 self.open_file_or_folder(action, True)
2392
2393 def open_file_remote(self, action):
2394 # Prompt user for the url:
2395 dialog = gtk.Dialog(
2396 _("Open Remote"),
2397 self.window,
2398 gtk.DIALOG_MODAL,
2399 buttons=(
2400 gtk.STOCK_CANCEL,
2401 gtk.RESPONSE_CANCEL,
2402 gtk.STOCK_OPEN,
2403 gtk.RESPONSE_OK,
2404 ),
2405 )
2406 location = gtk.Entry()
2407 location.set_size_request(300, -1)
2408 location.set_activates_default(True)
2409 hbox = gtk.HBox()
2410 hbox.pack_start(gtk.Label(_("Image Location (URL):")), False, False, 5)
2411 hbox.pack_start(location, True, True, 5)
2412 dialog.vbox.pack_start(hbox, True, True, 10)
2413 dialog.set_default_response(gtk.RESPONSE_OK)
2414 dialog.vbox.show_all()
2415 dialog.connect("response", self.open_file_remote_response, location)
2416 response = dialog.show()
2417
2418 def open_file_remote_response(self, dialog, response, location):
2419 if response == gtk.RESPONSE_OK:
2420 filenames = []
2421 filenames.append(location.get_text())
2422 dialog.destroy()
2423 while gtk.events_pending():
2424 gtk.main_iteration()
2425 self.expand_filelist_and_load_image(filenames)
2426 else:
2427 dialog.destroy()
2428
2429 def open_folder(self, action):
2430 self.stop_now = True
2431 while gtk.events_pending():
2432 gtk.main_iteration()
2433 self.open_file_or_folder(action, False)
2434
2435 def open_file_or_folder(self, action, isfile):
2436 self.thumbpane_create_dir()
2437 cancel = self.autosave_image()
2438 if cancel:
2439 return
2440 # If isfile = True, file; If isfile = False, folder
2441 dialog = gtk.FileChooserDialog(
2442 title=_("Open"),
2443 action=gtk.FILE_CHOOSER_ACTION_OPEN,
2444 buttons=(
2445 gtk.STOCK_CANCEL,
2446 gtk.RESPONSE_CANCEL,
2447 gtk.STOCK_OPEN,
2448 gtk.RESPONSE_OK,
2449 ),
2450 )
2451 if isfile:
2452 filter = gtk.FileFilter()
2453 filter.set_name(_("Images"))
2454 filter.add_pixbuf_formats()
2455 dialog.add_filter(filter)
2456 filter = gtk.FileFilter()
2457 filter.set_name(_("All files"))
2458 filter.add_pattern("*")
2459 dialog.add_filter(filter)
2460 preview = gtk.Image()
2461 dialog.set_preview_widget(preview)
2462 dialog.set_use_preview_label(False)
2463 dialog.connect("update-preview", self.update_preview, preview)
2464 recursivebutton = None
2465 else:
2466 dialog.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
2467 recursivebutton = gtk.CheckButton(
2468 label=_("Include images in subdirectories")
2469 )
2470 dialog.set_extra_widget(recursivebutton)
2471 dialog.set_default_response(gtk.RESPONSE_OK)
2472 dialog.set_select_multiple(True)
2473 if self.use_last_dir:
2474 if self.last_dir != None:
2475 dialog.set_current_folder(self.last_dir)
2476 else:
2477 if self.fixed_dir != None:
2478 dialog.set_current_folder(self.fixed_dir)
2479 dialog.connect(
2480 "response", self.open_file_or_folder_response, isfile, recursivebutton
2481 )
2482 response = dialog.show()
2483
2484 def open_file_or_folder_response(self, dialog, response, isfile, recursivebutton):
2485 if response == gtk.RESPONSE_OK:
2486 if self.use_last_dir:
2487 self.last_dir = dialog.get_current_folder()
2488 if not isfile and recursivebutton.get_property("active"):
2489 self.recursive = True
2490 filenames = dialog.get_filenames()
2491 dialog.destroy()
2492 while gtk.events_pending():
2493 gtk.main_iteration()
2494 self.expand_filelist_and_load_image(filenames)
2495 else:
2496 dialog.destroy()
2497
2498 def update_preview(self, file_chooser, preview):
2499 filename = file_chooser.get_preview_filename()
2500 if not filename:
2501 return
2502 filename, thumbfile = self.thumbnail_get_name(filename)
2503 pixbuf = self.thumbpane_get_pixbuf(thumbfile, filename, False)
2504 if pixbuf:
2505 preview.set_from_pixbuf(pixbuf)
2506 else:
2507 pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, 1, 8, 128, 128)
2508 pixbuf.fill(0x00000000)
2509 preview.set_from_pixbuf(pixbuf)
2510 have_preview = True
2511 file_chooser.set_preview_widget_active(have_preview)
2512 del pixbuf
2513 gc.collect()
2514
2515 def hide_cursor(self):
2516 if (
2517 self.fullscreen_mode
2518 and not self.user_prompt_visible
2519 and not self.slideshow_controls_visible
2520 ):
2521 pix_data = """/* XPM */
18482522 static char * invisible_xpm[] = {
18492523 "1 1 1 1",
18502524 " c None",
18512525 " "};"""
1852 color = gtk.gdk.Color()
1853 pix = gtk.gdk.pixmap_create_from_data(None, pix_data, 1, 1, 1, color, color)
1854 invisible = gtk.gdk.Cursor(pix, pix, color, color, 0, 0)
1855 self.change_cursor(invisible)
1856 return False
1857
1858 def enter_fullscreen(self, action):
1859 if not self.fullscreen_mode:
1860 self.fullscreen_mode = True
1861 self.UIManager.get_widget('/Popup/Full Screen').hide()
1862 self.UIManager.get_widget('/Popup/Exit Full Screen').show()
1863 self.statusbar.hide()
1864 self.statusbar2.hide()
1865 self.toolbar.hide()
1866 self.menubar.hide()
1867 self.thumbscroll.hide()
1868 self.thumbpane.hide()
1869 self.window.fullscreen()
1870 self.timer_id = gobject.timeout_add(2000, self.hide_cursor)
1871 self.set_slideshow_sensitivities()
1872 if self.simple_bgcolor:
1873 self.layout.modify_bg(gtk.STATE_NORMAL, self.bgcolor)
1874 else:
1875 if self.simple_bgcolor:
1876 self.layout.modify_bg(gtk.STATE_NORMAL, None)
1877 self.leave_fullscreen(action)
1878
1879 def leave_fullscreen(self, action):
1880 if self.fullscreen_mode:
1881 self.slideshow_controls_visible = False
1882 self.slideshow_window.hide_all()
1883 self.slideshow_window2.hide_all()
1884 self.fullscreen_mode = False
1885 self.UIManager.get_widget('/Popup/Full Screen').show()
1886 self.UIManager.get_widget('/Popup/Exit Full Screen').hide()
1887 if self.toolbar_show:
1888 self.toolbar.show()
1889 self.menubar.show()
1890 if self.statusbar_show:
1891 self.statusbar.show()
1892 self.statusbar2.show()
1893 if self.thumbpane_show:
1894 self.thumbscroll.show()
1895 self.thumbpane.show()
1896 self.thumbpane_update_images(False, self.curr_img_in_list)
1897 self.window.unfullscreen()
1898 self.change_cursor(None)
1899 self.set_slideshow_sensitivities()
1900 if self.simple_bgcolor:
1901 self.layout.modify_bg(gtk.STATE_NORMAL, None)
1902
1903 def toggle_status_bar(self, action):
1904 if self.statusbar.get_property('visible'):
1905 self.statusbar.hide()
1906 self.statusbar2.hide()
1907 self.statusbar_show = False
1908 else:
1909 self.statusbar.show()
1910 self.statusbar2.show()
1911 self.statusbar_show = True
1912 if self.image_loaded and self.last_image_action_was_fit:
1913 if self.last_image_action_was_smart_fit:
1914 self.zoom_to_fit_or_1_to_1(None, False, False)
1915 else:
1916 self.zoom_to_fit_window(None, False, False)
1917
1918 def toggle_thumbpane(self, action):
1919 if self.thumbscroll.get_property('visible'):
1920 self.thumbscroll.hide()
1921 self.thumbpane.hide()
1922 self.thumbpane_show = False
1923 else:
1924 self.thumbscroll.show()
1925 self.thumbpane.show()
1926 self.thumbpane_show = True
1927 self.stop_now = False
1928 gobject.idle_add(self.thumbpane_update_images, True, self.curr_img_in_list)
1929 if self.image_loaded and self.last_image_action_was_fit:
1930 if self.last_image_action_was_smart_fit:
1931 self.zoom_to_fit_or_1_to_1(None, False, False)
1932 else:
1933 self.zoom_to_fit_window(None, False, False)
1934
1935 def toggle_toolbar(self, action):
1936 if self.toolbar.get_property('visible'):
1937 self.toolbar.hide()
1938 self.toolbar_show = False
1939 else:
1940 self.toolbar.show()
1941 self.toolbar_show = True
1942 if self.image_loaded and self.last_image_action_was_fit:
1943 if self.last_image_action_was_smart_fit:
1944 self.zoom_to_fit_or_1_to_1(None, False, False)
1945 else:
1946 self.zoom_to_fit_window(None, False, False)
1947
1948 def update_statusbar(self):
1949 # Update status bar:
1950 try:
1951 st = os.stat(self.currimg_name)
1952 filesize = st[stat.ST_SIZE] // 1000
1953 ratio = int(100 * self.currimg_zoomratio)
1954 status_text = os.path.basename(self.currimg_name)+ ": " + str(self.currimg_pixbuf_original.get_width()) + "x" + str(self.currimg_pixbuf_original.get_height()) + " " + str(filesize) + "KB " + str(ratio) + "% "
1955 except:
1956 status_text=_("Cannot load image.")
1957 self.statusbar.push(self.statusbar.get_context_id(""), status_text)
1958 status_text = ""
1959 if self.running_custom_actions:
1960 status_text = _('Custom actions: %(current)i of %(total)i') % {'current': self.curr_custom_action,'total': self.num_custom_actions}
1961 elif self.searching_for_images:
1962 status_text = _('Scanning...')
1963 self.statusbar2.push(self.statusbar2.get_context_id(""), status_text)
1964
1965 def show_custom_actions(self, action):
1966 self.actions_dialog = gtk.Dialog(title=_("Configure Custom Actions"), parent=self.window)
1967 self.actions_dialog.set_has_separator(False)
1968 self.actions_dialog.set_resizable(False)
1969 table_actions = gtk.Table(13, 2, False)
1970 table_actions.attach(gtk.Label(), 1, 2, 1, 2, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 0, 0)
1971 actionscrollwindow = gtk.ScrolledWindow()
1972 self.actionstore = gtk.ListStore(str, str, str)
1973 self.actionwidget = gtk.TreeView()
1974 self.actionwidget.set_enable_search(False)
1975 self.actionwidget.set_rules_hint(True)
1976 self.actionwidget.connect('row-activated', self.edit_custom_action2)
1977 actionscrollwindow.add(self.actionwidget)
1978 actionscrollwindow.set_shadow_type(gtk.SHADOW_IN)
1979 actionscrollwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
1980 actionscrollwindow.set_size_request(500, 200)
1981 self.actionwidget.set_model(self.actionstore)
1982 self.cell = gtk.CellRendererText()
1983 self.cellbool = gtk.CellRendererPixbuf()
1984 self.tvcolumn0 = gtk.TreeViewColumn(_("Batch"))
1985 self.tvcolumn1 = gtk.TreeViewColumn(_("Action"), self.cell, markup=0)
1986 self.tvcolumn2 = gtk.TreeViewColumn(_("Shortcut"))
1987 self.tvcolumn1.set_max_width(self.actionwidget.size_request()[0] - self.tvcolumn0.get_width() - self.tvcolumn2.get_width())
1988 self.actionwidget.append_column(self.tvcolumn0)
1989 self.actionwidget.append_column(self.tvcolumn1)
1990 self.actionwidget.append_column(self.tvcolumn2)
1991 self.populate_treeview()
1992 if len(self.action_names) > 0:
1993 self.actionwidget.get_selection().select_path(0)
1994 vbox_actions = gtk.VBox()
1995 addbutton = gtk.Button("", gtk.STOCK_ADD)
1996 addbutton.get_child().get_child().get_children()[1].set_text('')
1997 addbutton.connect('clicked', self.add_custom_action, self.actionwidget)
1998 addbutton.set_tooltip_text(_("Add action"))
1999 editbutton = gtk.Button("", gtk.STOCK_EDIT)
2000 editbutton.get_child().get_child().get_children()[1].set_text('')
2001 editbutton.connect('clicked', self.edit_custom_action, self.actionwidget)
2002 editbutton.set_tooltip_text(_("Edit selected action."))
2003 removebutton = gtk.Button("", gtk.STOCK_REMOVE)
2004 removebutton.get_child().get_child().get_children()[1].set_text('')
2005 removebutton.connect('clicked', self.remove_custom_action)
2006 removebutton.set_tooltip_text(_("Remove selected action."))
2007 upbutton = gtk.Button("", gtk.STOCK_GO_UP)
2008 upbutton.get_child().get_child().get_children()[1].set_text('')
2009 upbutton.connect('clicked', self.custom_action_move_up, self.actionwidget)
2010 upbutton.set_tooltip_text(_("Move selected action up."))
2011 downbutton = gtk.Button("", gtk.STOCK_GO_DOWN)
2012 downbutton.get_child().get_child().get_children()[1].set_text('')
2013 downbutton.connect('clicked', self.custom_action_move_down, self.actionwidget)
2014 downbutton.set_tooltip_text(_("Move selected action down."))
2015 vbox_buttons = gtk.VBox()
2016 propertyinfo = gtk.Label()
2017 propertyinfo.set_markup('<small>' + _("Parameters") + ':\n<span font_family="Monospace">%F</span> - ' + _("File path, name, and extension") + '\n<span font_family="Monospace">%P</span> - ' + _("File path") + '\n<span font_family="Monospace">%N</span> - ' + _("File name without file extension") + '\n<span font_family="Monospace">%E</span> - ' + _("File extension (i.e. \".png\")") + '\n<span font_family="Monospace">%L</span> - ' + _("List of files, space-separated") + '</small>')
2018 propertyinfo.set_alignment(0, 0)
2019 actioninfo = gtk.Label()
2020 actioninfo.set_markup('<small>' + _("Operations") + ':\n<span font_family="Monospace">[NEXT]</span> - ' + _("Go to next image") + '\n<span font_family="Monospace">[PREV]</span> - ' + _("Go to previous image") +'</small>')
2021 actioninfo.set_alignment(0, 0)
2022 hbox_info = gtk.HBox()
2023 hbox_info.pack_start(propertyinfo, False, False, 15)
2024 hbox_info.pack_start(actioninfo, False, False, 15)
2025 vbox_buttons.pack_start(addbutton, False, False, 5)
2026 vbox_buttons.pack_start(editbutton, False, False, 5)
2027 vbox_buttons.pack_start(removebutton, False, False, 5)
2028 vbox_buttons.pack_start(upbutton, False, False, 5)
2029 vbox_buttons.pack_start(downbutton, False, False, 0)
2030 hbox_top = gtk.HBox()
2031 hbox_top.pack_start(actionscrollwindow, True, True, 5)
2032 hbox_top.pack_start(vbox_buttons, False, False, 5)
2033 vbox_actions.pack_start(hbox_top, True, True, 5)
2034 vbox_actions.pack_start(hbox_info, False, False, 5)
2035 hbox_instructions = gtk.HBox()
2036 info_image = gtk.Image()
2037 info_image.set_from_stock(gtk.STOCK_DIALOG_INFO, gtk.ICON_SIZE_BUTTON)
2038 hbox_instructions.pack_start(info_image, False, False, 5)
2039 instructions = gtk.Label(_("Here you can define custom actions with shortcuts. Actions use the built-in parameters and operations listed below and can have multiple statements separated by a semicolon. Batch actions apply to all images in the list."))
2040 instructions.set_line_wrap(True)
2041 instructions.set_alignment(0, 0.5)
2042 hbox_instructions.pack_start(instructions, False, False, 5)
2043 table_actions.attach(hbox_instructions, 1, 3, 2, 3, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 5, 0)
2044 table_actions.attach(gtk.Label(), 1, 3, 3, 4, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 15, 0)
2045 table_actions.attach(vbox_actions, 1, 3, 4, 12, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 15, 0)
2046 table_actions.attach(gtk.Label(), 1, 3, 12, 13, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 15, 0)
2047 self.actions_dialog.vbox.pack_start(table_actions, False, False, 0)
2048 # Show dialog:
2049 self.actions_dialog.vbox.show_all()
2050 instructions.set_size_request(self.actions_dialog.size_request()[0]-50, -1)
2051 close_button = self.actions_dialog.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)
2052 close_button.grab_focus()
2053 self.actions_dialog.run()
2054 self.refresh_custom_actions_menu()
2055 while gtk.events_pending():
2056 gtk.main_iteration()
2057 if len(self.image_list) == 0:
2058 self.set_image_sensitivities(False)
2059 self.actions_dialog.destroy()
2060
2061 def add_custom_action(self, button, treeview):
2062 self.open_custom_action_dialog(True, '', '', 'None', False, treeview)
2063
2064 def edit_custom_action2(self, treeview, path, view_column):
2065 self.edit_custom_action(None, treeview)
2066
2067 def edit_custom_action(self, button, treeview):
2068 (model, iter) = self.actionwidget.get_selection().get_selected()
2069 if iter != None:
2070 (row, ) = self.actionstore.get_path(iter)
2071 self.open_custom_action_dialog(False, self.action_names[row], self.action_commands[row], self.action_shortcuts[row], self.action_batch[row], treeview)
2072
2073 def open_custom_action_dialog(self, add_call, name, command, shortcut, batch, treeview):
2074 if add_call:
2075 self.dialog_name = gtk.Dialog(_("Add Custom Action"), self.actions_dialog, gtk.DIALOG_MODAL, (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
2076 else:
2077 self.dialog_name = gtk.Dialog(_("Edit Custom Action"), self.actions_dialog, gtk.DIALOG_MODAL, (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
2078 self.dialog_name.set_modal(True)
2079 table = gtk.Table(2, 4, False)
2080 action_name_label = gtk.Label(_("Action Name:"))
2081 action_name_label.set_alignment(0, 0.5)
2082 action_command_label = gtk.Label(_("Command:"))
2083 action_command_label.set_alignment(0, 0.5)
2084 shortcut_label = gtk.Label(_("Shortcut:"))
2085 shortcut_label.set_alignment(0, 0.5)
2086 table.attach(action_name_label, 0, 1, 0, 1, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 15, 0)
2087 table.attach(action_command_label, 0, 1, 1, 2, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 15, 0)
2088 table.attach(shortcut_label, 0, 1, 2, 3, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 15, 0)
2089 action_name = gtk.Entry()
2090 action_name.set_text(name)
2091 action_command = gtk.Entry()
2092 action_command.set_text(command)
2093 table.attach(action_name, 1, 2, 0, 1, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 15, 0)
2094 table.attach(action_command, 1, 2, 1, 2, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 15, 0)
2095 self.shortcut = gtk.Button(shortcut)
2096 self.shortcut.connect('clicked', self.shortcut_clicked)
2097 table.attach(self.shortcut, 1, 2, 2, 3, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 15, 0)
2098 batchmode = gtk.CheckButton(_("Perform action on all images (Batch)"))
2099 batchmode.set_active(batch)
2100 table.attach(batchmode, 0, 2, 3, 4, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 15, 0)
2101 self.dialog_name.vbox.pack_start(table, False, False, 5)
2102 self.dialog_name.vbox.show_all()
2103 self.dialog_name.connect('response', self.dialog_name_response, add_call, action_name, action_command, self.shortcut, batchmode, treeview)
2104 self.dialog_name.run()
2105
2106 def dialog_name_response(self, dialog, response, add_call, action_name, action_command, shortcut, batchmode, treeview):
2107 if response == gtk.RESPONSE_ACCEPT:
2108 if not (action_command.get_text() == "" or action_name.get_text() == "" or self.shortcut.get_label() == "None"):
2109 name = action_name.get_text()
2110 command = action_command.get_text()
2111 if ((("[NEXT]" in command.strip()) and command.strip()[-6:] != "[NEXT]") or (("[PREV]" in command.strip()) and command.strip()[-6:] != "[PREV]") ):
2112 error_dialog = gtk.MessageDialog(self.actions_dialog, gtk.DIALOG_MODAL, gtk.MESSAGE_WARNING, gtk.BUTTONS_CLOSE, _('[PREV] and [NEXT] are only valid alone or at the end of the command'))
2113 error_dialog.set_title(_("Invalid Custom Action"))
2114 error_dialog.run()
2115 error_dialog.destroy()
2116 return
2117 shortcut = shortcut.get_label()
2118 batch = batchmode.get_active()
2119 dialog.destroy()
2120 if add_call:
2121 self.action_names.append(name)
2122 self.action_commands.append(command)
2123 self.action_shortcuts.append(shortcut)
2124 self.action_batch.append(batch)
2125 else:
2126 (model, iter) = self.actionwidget.get_selection().get_selected()
2127 (rownum, ) = self.actionstore.get_path(iter)
2128 self.action_names[rownum] = name
2129 self.action_commands[rownum] = command
2130 self.action_shortcuts[rownum] = shortcut
2131 self.action_batch[rownum] = batch
2132 self.populate_treeview()
2133 if add_call:
2134 rownum = len(self.action_names)-1
2135 treeview.get_selection().select_path(rownum)
2136 while gtk.events_pending():
2137 gtk.main_iteration()
2138 # Keep item in visible rect:
2139 visible_rect = treeview.get_visible_rect()
2140 row_rect = treeview.get_background_area(rownum, self.tvcolumn1)
2141 if row_rect.y + row_rect.height > visible_rect.height:
2142 top_coord = (row_rect.y + row_rect.height - visible_rect.height) + visible_rect.y
2143 treeview.scroll_to_point(-1, top_coord)
2144 elif row_rect.y < 0:
2145 treeview.scroll_to_cell(rownum)
2146 else:
2147 error_dialog = gtk.MessageDialog(self.actions_dialog, gtk.DIALOG_MODAL, gtk.MESSAGE_WARNING, gtk.BUTTONS_CLOSE, _('Incomplete custom action specified.'))
2148 error_dialog.set_title(_("Invalid Custom Action"))
2149 error_dialog.run()
2150 error_dialog.destroy()
2151 else:
2152 dialog.destroy()
2153
2154 def custom_action_move_down(self, button, treeview):
2155 iter = None
2156 selection = treeview.get_selection()
2157 model, iter = selection.get_selected()
2158 if iter:
2159 rownum = int(model.get_string_from_iter(iter))
2160 if rownum < len(self.action_names)-1:
2161 # Move item down:
2162 temp_name = self.action_names[rownum]
2163 temp_shortcut = self.action_shortcuts[rownum]
2164 temp_command = self.action_commands[rownum]
2165 temp_batch = self.action_batch[rownum]
2166 self.action_names[rownum] = self.action_names[rownum+1]
2167 self.action_shortcuts[rownum] = self.action_shortcuts[rownum+1]
2168 self.action_commands[rownum] = self.action_commands[rownum+1]
2169 self.action_batch[rownum] = self.action_batch[rownum+1]
2170 self.action_names[rownum+1] = temp_name
2171 self.action_shortcuts[rownum+1] = temp_shortcut
2172 self.action_commands[rownum+1] = temp_command
2173 self.action_batch[rownum+1] = temp_batch
2174 # Repopulate treeview and keep item selected:
2175 self.populate_treeview()
2176 selection.select_path((rownum+1,))
2177 while gtk.events_pending():
2178 gtk.main_iteration()
2179 # Keep item in visible rect:
2180 rownum = rownum + 1
2181 visible_rect = treeview.get_visible_rect()
2182 row_rect = treeview.get_background_area(rownum, self.tvcolumn1)
2183 if row_rect.y + row_rect.height > visible_rect.height:
2184 top_coord = (row_rect.y + row_rect.height - visible_rect.height) + visible_rect.y
2185 treeview.scroll_to_point(-1, top_coord)
2186 elif row_rect.y < 0:
2187 treeview.scroll_to_cell(rownum)
2188
2189 def custom_action_move_up(self, button, treeview):
2190 iter = None
2191 selection = treeview.get_selection()
2192 model, iter = selection.get_selected()
2193 if iter:
2194 rownum = int(model.get_string_from_iter(iter))
2195 if rownum > 0:
2196 # Move item down:
2197 temp_name = self.action_names[rownum]
2198 temp_shortcut = self.action_shortcuts[rownum]
2199 temp_command = self.action_commands[rownum]
2200 temp_batch = self.action_batch[rownum]
2201 self.action_names[rownum] = self.action_names[rownum-1]
2202 self.action_shortcuts[rownum] = self.action_shortcuts[rownum-1]
2203 self.action_commands[rownum] = self.action_commands[rownum-1]
2204 self.action_batch[rownum] = self.action_batch[rownum-1]
2205 self.action_names[rownum-1] = temp_name
2206 self.action_shortcuts[rownum-1] = temp_shortcut
2207 self.action_commands[rownum-1] = temp_command
2208 self.action_batch[rownum-1] = temp_batch
2209 # Repopulate treeview and keep item selected:
2210 self.populate_treeview()
2211 selection.select_path((rownum-1,))
2212 while gtk.events_pending():
2213 gtk.main_iteration()
2214 # Keep item in visible rect:
2215 rownum = rownum - 1
2216 visible_rect = treeview.get_visible_rect()
2217 row_rect = treeview.get_background_area(rownum, self.tvcolumn1)
2218 if row_rect.y + row_rect.height > visible_rect.height:
2219 top_coord = (row_rect.y + row_rect.height - visible_rect.height) + visible_rect.y
2220 treeview.scroll_to_point(-1, top_coord)
2221 elif row_rect.y < 0:
2222 treeview.scroll_to_cell(rownum)
2223
2224 def shortcut_clicked(self, widget):
2225 self.dialog_shortcut = gtk.Dialog(_("Action Shortcut"), self.dialog_name, gtk.DIALOG_MODAL, (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT))
2226 self.shortcut_label = gtk.Label(_("Press the desired shortcut for the action."))
2227 hbox = gtk.HBox()
2228 hbox.pack_start(self.shortcut_label, False, False, 15)
2229 self.dialog_shortcut.vbox.pack_start(hbox, False, False, 5)
2230 self.dialog_shortcut.vbox.show_all()
2231 self.dialog_shortcut.connect('key-press-event', self.shortcut_keypress)
2232 self.dialog_shortcut.run()
2233 self.dialog_shortcut.destroy()
2234
2235 def shortcut_keypress(self, widget, event):
2236 shortcut = gtk.accelerator_name(event.keyval, event.state)
2237 if "<Mod2>" in shortcut:
2238 shortcut = shortcut.replace("<Mod2>", "")
2239 if shortcut[(len(shortcut)-2):len(shortcut)] != "_L" and shortcut[(len(shortcut)-2):len(shortcut)] != "_R":
2240 # Validate to make sure the shortcut hasn't already been used:
2241 for i in range(len(self.keys)):
2242 if shortcut == self.keys[i][1]:
2243 error_dialog = gtk.MessageDialog(self.dialog_shortcut, gtk.DIALOG_MODAL, gtk.MESSAGE_WARNING, gtk.BUTTONS_CLOSE, _('The shortcut \'%(shortcut)s\' is already used for \'%(key)s\'.') % {'shortcut': shortcut, 'key': self.keys[i][0]})
2244 error_dialog.set_title(_("Invalid Shortcut"))
2245 error_dialog.run()
2246 error_dialog.destroy()
2247 return
2248 for i in range(len(self.action_shortcuts)):
2249 if shortcut == self.action_shortcuts[i]:
2250 error_dialog = gtk.MessageDialog(self.dialog_shortcut, gtk.DIALOG_MODAL, gtk.MESSAGE_WARNING, gtk.BUTTONS_CLOSE, _('The shortcut \'%(shortcut)s\' is already used for \'%(key)s\'.') % {'shortcut': shortcut, 'key': self.action_names[i]})
2251 error_dialog.set_title(_("Invalid Shortcut"))
2252 error_dialog.run()
2253 error_dialog.destroy()
2254 return
2255 self.shortcut.set_label(shortcut)
2256 widget.destroy()
2257
2258 def remove_custom_action(self, button):
2259 (model, iter) = self.actionwidget.get_selection().get_selected()
2260 if iter != None:
2261 (row, ) = self.actionstore.get_path(iter)
2262 self.action_names.pop(row)
2263 self.action_shortcuts.pop(row)
2264 self.action_commands.pop(row)
2265 self.action_batch.pop(row)
2266 self.populate_treeview()
2267 self.actionwidget.grab_focus()
2268
2269 def populate_treeview(self):
2270 self.actionstore.clear()
2271 for i in range(len(self.action_names)):
2272 if self.action_batch[i]:
2273 pb = gtk.STOCK_APPLY
2274 else:
2275 pb = None
2276 self.actionstore.append([pb, '<big><b>' + self.action_names[i].replace('&','&amp;') + '</b></big>\n<small>' + self.action_commands[i].replace('&','&amp;') + '</small>', self.action_shortcuts[i]])
2277 self.tvcolumn0.clear()
2278 self.tvcolumn1.clear()
2279 self.tvcolumn2.clear()
2280 self.tvcolumn0.pack_start(self.cellbool)
2281 self.tvcolumn1.pack_start(self.cell)
2282 self.tvcolumn2.pack_start(self.cell)
2283 self.tvcolumn0.add_attribute(self.cellbool, "stock-id", 0)
2284 self.tvcolumn1.set_attributes(self.cell, markup=1)
2285 self.tvcolumn2.set_attributes(self.cell, text=2)
2286 self.tvcolumn1.set_expand(True)
2287
2288 def screenshot(self, action):
2289 cancel = self.autosave_image()
2290 if cancel:
2291 return
2292 # Dialog:
2293 dialog = gtk.Dialog(_("Screenshot"), self.window, gtk.DIALOG_MODAL, (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT))
2294 snapbutton = dialog.add_button(_("_Snap"), gtk.RESPONSE_ACCEPT)
2295 snapimage = gtk.Image()
2296 snapimage.set_from_stock(gtk.STOCK_OK, gtk.ICON_SIZE_BUTTON)
2297 snapbutton.set_image(snapimage)
2298 loc = gtk.Label()
2299 loc.set_markup('<b>' + _('Location') + '</b>')
2300 loc.set_alignment(0, 0)
2301 area = gtk.RadioButton()
2302 area1 = gtk.RadioButton(group=area, label=_("Entire screen"))
2303 area2 = gtk.RadioButton(group=area, label=_("Window under pointer"))
2304 if not HAS_XMOUSE:
2305 area2.set_sensitive(False)
2306 area1.set_active(True)
2307 de = gtk.Label()
2308 de.set_markup('<b>' + _("Delay") + '</b>')
2309 de.set_alignment(0, 0)
2310 delaybox = gtk.HBox()
2311 adj = gtk.Adjustment(self.screenshot_delay, 0, 30, 1, 10, 0)
2312 delay = gtk.SpinButton(adj, 0, 0)
2313 delay.set_numeric(True)
2314 delay.set_update_policy(gtk.UPDATE_IF_VALID)
2315 delay.set_wrap(False)
2316 delaylabel = gtk.Label(_(" seconds"))
2317 delaybox.pack_start(delay, False)
2318 delaybox.pack_start(delaylabel, False)
2319 table = gtk.Table()
2320 table.attach(gtk.Label(), 1, 2, 1, 2, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 0, 0)
2321 table.attach(loc, 1, 2, 2, 3, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 15, 0)
2322 table.attach(gtk.Label(), 1, 2, 3, 4, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 0, 0)
2323 table.attach(area1, 1, 2, 4, 5, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2324 table.attach(area2, 1, 2, 5, 6, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2325 table.attach(gtk.Label(), 1, 2, 6, 7, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2326 table.attach(de, 1, 2, 7, 8, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 15, 0)
2327 table.attach(gtk.Label(), 1, 2, 8, 9, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2328 table.attach(delaybox, 1, 2, 9, 10, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2329 table.attach(gtk.Label(), 1, 2, 10, 11, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2330 dialog.vbox.pack_start(table)
2331 dialog.set_default_response(gtk.RESPONSE_ACCEPT)
2332 dialog.vbox.show_all()
2333 response = dialog.run()
2334 if response == gtk.RESPONSE_ACCEPT:
2335 dialog.destroy()
2336 while gtk.events_pending():
2337 gtk.main_iteration()
2338 self.screenshot_delay = delay.get_value_as_int()
2339 gobject.timeout_add(int(self.screenshot_delay*1000), self._screenshot_grab, area1.get_active())
2340 else:
2341 dialog.destroy()
2342
2343 def _screenshot_grab(self, entire_screen):
2344 root_win = gtk.gdk.get_default_root_window()
2345 if entire_screen:
2346 x = 0
2347 y = 0
2348 width = gtk.gdk.screen_width()
2349 height = gtk.gdk.screen_height()
2350 else:
2351 (x, y, width, height) = xmouse.geometry()
2352 pix = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, width, height)
2353 pix = pix.get_from_drawable(root_win, gtk.gdk.colormap_get_system(), x, y, 0, 0, width, height)
2354 # Save as /tmp/mirage-<random>/filename.ext
2355 tmpdir = tempfile.mkdtemp(prefix="mirage-") + "/"
2356 tmpfile = tmpdir + "screenshot.png"
2357 pix.save(tmpfile, 'png')
2358 # Load file:
2359 self.image_list = [tmpfile]
2360 self.curr_img_in_list = 0
2361 gobject.idle_add(self.load_new_image2, False, False, False, False, True)
2362 self.update_statusbar()
2363 self.set_go_navigation_sensitivities(False)
2364 self.set_slideshow_sensitivities()
2365 self.thumbpane_update_images(True, self.curr_img_in_list)
2366 del pix
2367 self.window.present()
2368
2369 def show_properties(self, action):
2370 show_props = gtk.Dialog(_("Properties"), self.window)
2371 show_props.set_has_separator(False)
2372 show_props.set_resizable(False)
2373 table = gtk.Table(3, 3, False)
2374 image = gtk.Image()
2375 animtest = gtk.gdk.PixbufAnimation(self.currimg_name)
2376 image_is_anim = False
2377 if animtest.is_static_image():
2378 pixbuf, image_width, image_height = self.get_pixbuf_of_size(self.currimg_pixbuf_original, 180, self.zoom_quality)
2379 else:
2380 pixbuf, image_width, image_height = self.get_pixbuf_of_size(animtest.get_static_image(), 180, self.zoom_quality)
2381 image_is_anim = True
2382 image.set_from_pixbuf(self.pixbuf_add_border(pixbuf))
2383 vbox_left = gtk.VBox()
2384 filename = gtk.Label(_("File name:"))
2385 filename.set_alignment(1, 1)
2386 filedate = gtk.Label(_("File modified:"))
2387 filedate.set_alignment(1, 1)
2388 imagesize = gtk.Label(_("Dimensions:"))
2389 imagesize.set_alignment(1, 1)
2390 filesize = gtk.Label(_("File size:"))
2391 filesize.set_alignment(1, 1)
2392 filetype = gtk.Label(_("File type:"))
2393 filetype.set_alignment(1, 1)
2394 transparency = gtk.Label(_("Transparency:"))
2395 transparency.set_alignment(1, 1)
2396 animation = gtk.Label(_("Animation:"))
2397 animation.set_alignment(1, 1)
2398 bits = gtk.Label(_("Bits per sample:"))
2399 bits.set_alignment(1, 1)
2400 channels = gtk.Label(_("Channels:"))
2401 channels.set_alignment(1, 1)
2402 vbox_left.pack_start(filename, False, False, 2)
2403 vbox_left.pack_start(filedate, False, False, 2)
2404 vbox_left.pack_start(imagesize, False, False, 2)
2405 vbox_left.pack_start(filesize, False, False, 2)
2406 vbox_left.pack_start(filetype, False, False, 2)
2407 vbox_left.pack_start(transparency, False, False, 2)
2408 vbox_left.pack_start(animation, False, False, 2)
2409 vbox_left.pack_start(bits, False, False, 2)
2410 vbox_left.pack_start(channels, False, False, 2)
2411 vbox_right = gtk.VBox()
2412 filestat = os.stat(self.currimg_name)
2413 filename2 = gtk.Label(os.path.basename(self.currimg_name))
2414 filedate2 = gtk.Label(time.strftime('%c', time.localtime(filestat[stat.ST_MTIME])))
2415 imagesize2 = gtk.Label(str(self.currimg_pixbuf_original.get_width()) + "x" + str(self.currimg_pixbuf_original.get_height()))
2416 filetype2 = gtk.Label(gtk.gdk.pixbuf_get_file_info(self.currimg_name)[0]['mime_types'][0])
2417 filesize2 = gtk.Label(str(filestat[stat.ST_SIZE] // 1000) + "KB")
2418 if not image_is_anim and pixbuf.get_has_alpha():
2419 transparency2 = gtk.Label(_("Yes"))
2420 else:
2421 transparency2 = gtk.Label(_("No"))
2422 if animtest.is_static_image():
2423 animation2 = gtk.Label(_("No"))
2424 else:
2425 animation2 = gtk.Label(_("Yes"))
2426 bits2 = gtk.Label(str(pixbuf.get_bits_per_sample()))
2427 channels2 = gtk.Label(str(pixbuf.get_n_channels()))
2428 filename2.set_alignment(0, 1)
2429 filedate2.set_alignment(0, 1)
2430 imagesize2.set_alignment(0, 1)
2431 filesize2.set_alignment(0, 1)
2432 filetype2.set_alignment(0, 1)
2433 transparency2.set_alignment(0, 1)
2434 animation2.set_alignment(0, 1)
2435 bits2.set_alignment(0, 1)
2436 channels2.set_alignment(0, 1)
2437 vbox_right.pack_start(filename2, False, False, 2)
2438 vbox_right.pack_start(filedate2, False, False, 2)
2439 vbox_right.pack_start(imagesize2, False, False, 2)
2440 vbox_right.pack_start(filesize2, False, False, 2)
2441 vbox_right.pack_start(filetype2, False, False, 2)
2442 vbox_right.pack_start(transparency2, False, False, 2)
2443 vbox_right.pack_start(animation2, False, False, 2)
2444 vbox_right.pack_start(bits2, False, False, 2)
2445 vbox_right.pack_start(channels2, False, False, 2)
2446 hbox = gtk.HBox()
2447 hbox.pack_start(vbox_left, False, False, 3)
2448 hbox.pack_start(vbox_right, False, False, 3)
2449 table.attach(image, 1, 2, 1, 3, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 15, 0)
2450 table.attach(hbox, 2, 3, 1, 3, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 15, 0)
2451 show_props.vbox.pack_start(table, False, False, 15)
2452 show_props.vbox.show_all()
2453 close_button = show_props.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)
2454 close_button.grab_focus()
2455 show_props.run()
2456 show_props.destroy()
2457
2458 def show_prefs(self, action):
2459 prev_thumbnail_size = self.thumbnail_size
2460 self.prefs_dialog = gtk.Dialog(_("Mirage Preferences"), self.window)
2461 self.prefs_dialog.set_has_separator(False)
2462 self.prefs_dialog.set_resizable(False)
2463 # "Interface" prefs:
2464 table_settings = gtk.Table(14, 3, False)
2465 bglabel = gtk.Label()
2466 bglabel.set_markup('<b>' + _('Interface') + '</b>')
2467 bglabel.set_alignment(0, 1)
2468 color_hbox = gtk.HBox(False, 0)
2469 colortext = gtk.Label(_('Background color:'))
2470 self.colorbutton = gtk.ColorButton(self.bgcolor)
2471 self.colorbutton.connect('color-set', self.bgcolor_selected)
2472 self.colorbutton.set_size_request(150, -1)
2473 self.colorbutton.set_tooltip_text(_("Sets the background color for the application."))
2474 color_hbox.pack_start(colortext, False, False, 0)
2475 color_hbox.pack_start(self.colorbutton, False, False, 0)
2476 color_hbox.pack_start(gtk.Label(), True, True, 0)
2477
2478 simplecolor_hbox = gtk.HBox(False, 0)
2479 simplecolortext = gtk.Label(_('Simple background color:'))
2480 simplecolorbutton = gtk.CheckButton()
2481 simplecolorbutton.connect('toggled', self.simple_bgcolor_selected)
2482 simplecolor_hbox.pack_start(simplecolortext, False, False, 0)
2483 simplecolor_hbox.pack_start(simplecolorbutton, False, False, 0)
2484 simplecolor_hbox.pack_start(gtk.Label(), True, True, 0)
2485 if self.simple_bgcolor:
2486 simplecolorbutton.set_active(True)
2487
2488 fullscreen = gtk.CheckButton(_("Open Mirage in fullscreen mode"))
2489 fullscreen.set_active(self.start_in_fullscreen)
2490 thumbbox = gtk.HBox()
2491 thumblabel = gtk.Label(_("Thumbnail size:"))
2492 thumbbox.pack_start(thumblabel, False, False, 0)
2493 thumbsize = gtk.combo_box_new_text()
2494 option = 0
2495 for size in self.thumbnail_sizes:
2496 thumbsize.append_text(size + " x " + size)
2497 if self.thumbnail_size == int(size):
2498 thumbsize.set_active(option)
2499 option += 1
2500 thumbbox.pack_start(thumbsize, False, False, 5)
2501 table_settings.attach(gtk.Label(), 1, 3, 1, 2, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 0, 0)
2502 table_settings.attach(bglabel, 1, 3, 2, 3, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 15, 0)
2503 table_settings.attach(gtk.Label(), 1, 3, 3, 4, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 0, 0)
2504 table_settings.attach(simplecolor_hbox, 1, 2, 4, 5, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2505 table_settings.attach(color_hbox, 1, 2, 5, 6, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2506 table_settings.attach(gtk.Label(), 1, 3, 6, 7, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 0, 0)
2507 table_settings.attach(thumbbox, 1, 3, 7, 8, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2508 table_settings.attach(gtk.Label(), 1, 3, 8, 9, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2509 table_settings.attach(fullscreen, 1, 3, 9, 10, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2510 table_settings.attach(gtk.Label(), 1, 3, 10, 11, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2511 table_settings.attach(gtk.Label(), 1, 3, 11, 12, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2512 table_settings.attach(gtk.Label(), 1, 3, 12, 13, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2513 table_settings.attach(gtk.Label(), 1, 3, 13, 14, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2514 table_settings.attach(gtk.Label(), 1, 3, 14, 15, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2515 # "Behavior" tab:
2516 table_behavior = gtk.Table(14, 2, False)
2517 openlabel = gtk.Label()
2518 openlabel.set_markup('<b>' + _('Open Behavior') + '</b>')
2519 openlabel.set_alignment(0, 1)
2520 hbox_openmode = gtk.HBox()
2521 hbox_openmode.pack_start(gtk.Label(_('Open new image in:')), False, False, 0)
2522 combobox = gtk.combo_box_new_text()
2523 combobox.append_text(_("Smart Mode"))
2524 combobox.append_text(_("Zoom To Fit Mode"))
2525 combobox.append_text(_("1:1 Mode"))
2526 combobox.append_text(_("Last Active Mode"))
2527 combobox.set_active(self.open_mode)
2528 hbox_openmode.pack_start(combobox, False, False, 5)
2529 openallimages = gtk.CheckButton(_("Load all images in current directory"))
2530 openallimages.set_active(self.open_all_images)
2531 openallimages.set_tooltip_text(_("If enabled, opening an image in Mirage will automatically load all images found in that image's directory."))
2532 hiddenimages = gtk.CheckButton(_("Allow loading hidden files"))
2533 hiddenimages.set_active(self.open_hidden_files)
2534 hiddenimages.set_tooltip_text(_("If checked, Mirage will open hidden files. Otherwise, hidden files will be ignored."))
2535 openpref = gtk.RadioButton()
2536 openpref1 = gtk.RadioButton(group=openpref, label=_("Use last chosen directory"))
2537 openpref1.set_tooltip_text(_("The default 'Open' directory will be the last directory used."))
2538 openpref2 = gtk.RadioButton(group=openpref, label=_("Use this fixed directory:"))
2539 openpref2.connect('toggled', self.prefs_use_fixed_dir_clicked)
2540 openpref2.set_tooltip_text(_("The default 'Open' directory will be this specified directory."))
2541 hbox_defaultdir = gtk.HBox()
2542 self.defaultdir = gtk.Button()
2543 hbox_defaultdir.pack_start(gtk.Label(), True, True, 0)
2544 hbox_defaultdir.pack_start(self.defaultdir, False, False, 0)
2545 hbox_defaultdir.pack_start(gtk.Label(), True, True, 0)
2546 if len(self.fixed_dir) > 25:
2547 self.defaultdir.set_label('...' + self.fixed_dir[-22:])
2548 else:
2549 self.defaultdir.set_label(self.fixed_dir)
2550 self.defaultdir.connect('clicked', self.defaultdir_clicked)
2551 self.defaultdir.set_size_request(250, -1)
2552 if self.use_last_dir:
2553 openpref1.set_active(True)
2554 self.defaultdir.set_sensitive(False)
2555 else:
2556 openpref2.set_active(True)
2557 self.defaultdir.set_sensitive(True)
2558 table_behavior.attach(gtk.Label(), 1, 2, 1, 2, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 0, 0)
2559 table_behavior.attach(openlabel, 1, 2, 2, 3, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 15, 0)
2560 table_behavior.attach(gtk.Label(), 1, 2, 3, 4, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 0, 0)
2561 table_behavior.attach(hbox_openmode, 1, 2, 4, 5, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2562 table_behavior.attach(gtk.Label(), 1, 2, 5, 6, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 0, 0)
2563 table_behavior.attach(openallimages, 1, 2, 6, 7, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2564 table_behavior.attach(hiddenimages, 1, 2, 7, 8, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2565 table_behavior.attach(gtk.Label(), 1, 2, 8, 9, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 0, 0)
2566 table_behavior.attach(openpref1, 1, 2, 9, 10, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2567 table_behavior.attach(openpref2, 1, 2, 10, 11, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2568 table_behavior.attach(hbox_defaultdir, 1, 2, 11, 12, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 45, 0)
2569 table_behavior.attach(gtk.Label(), 1, 2, 12, 13, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 45, 0)
2570 # "Navigation" tab:
2571 table_navigation = gtk.Table(14, 2, False)
2572 navlabel = gtk.Label()
2573 navlabel.set_markup('<b>' + _('Navigation') + '</b>')
2574 navlabel.set_alignment(0, 1)
2575 preloadnav = gtk.CheckButton(label=_("Preload images for faster navigation"))
2576 preloadnav.set_active(self.preloading_images)
2577 preloadnav.set_tooltip_text(_("If enabled, the next and previous images in the list will be preloaded during idle time. Note that the speed increase comes at the expense of memory usage, so it is recommended to disable this option on machines with limited ram."))
2578 hbox_listwrap = gtk.HBox()
2579 hbox_listwrap.pack_start(gtk.Label(_("Wrap around imagelist:")), False, False, 0)
2580 combobox2 = gtk.combo_box_new_text()
2581 combobox2.append_text(_("No"))
2582 combobox2.append_text(_("Yes"))
2583 combobox2.append_text(_("Prompt User"))
2584 combobox2.set_active(self.listwrap_mode)
2585 hbox_listwrap.pack_start(combobox2, False, False, 5)
2586 table_navigation.attach(gtk.Label(), 1, 2, 1, 2, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 0, 0)
2587 table_navigation.attach(navlabel, 1, 2, 2, 3, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 15, 0)
2588 table_navigation.attach(gtk.Label(), 1, 2, 3, 4, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 0, 0)
2589 table_navigation.attach(hbox_listwrap, 1, 2, 4, 5, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2590 table_navigation.attach(gtk.Label(), 1, 2, 5, 6, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 0, 0)
2591 table_navigation.attach(preloadnav, 1, 2, 6, 7, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2592 table_navigation.attach(gtk.Label(), 1, 2, 7, 8, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2593 table_navigation.attach(gtk.Label(), 1, 2, 8, 9, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 0, 0)
2594 table_navigation.attach(gtk.Label(), 1, 2, 9, 10, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2595 table_navigation.attach(gtk.Label(), 1, 2, 10, 11, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2596 table_navigation.attach(gtk.Label(), 1, 2, 11, 12, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 0, 0)
2597 table_navigation.attach(gtk.Label(), 1, 2, 12, 13, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 0, 0)
2598 table_navigation.attach(gtk.Label(), 1, 2, 13, 14, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 0, 0)
2599 # "Slideshow" tab:
2600 table_slideshow = gtk.Table(14, 2, False)
2601 slideshowlabel = gtk.Label()
2602 slideshowlabel.set_markup('<b>' + _('Slideshow Mode') + '</b>')
2603 slideshowlabel.set_alignment(0, 1)
2604 hbox_delay = gtk.HBox()
2605 hbox_delay.pack_start(gtk.Label(_("Delay between images in seconds:")), False, False, 0)
2606 spin_adj = gtk.Adjustment(self.slideshow_delay, 0, 50000, 1, 10, 0)
2607 delayspin = gtk.SpinButton(spin_adj, 1.0, 0)
2608 delayspin.set_numeric(True)
2609 hbox_delay.pack_start(delayspin, False, False, 5)
2610 randomize = gtk.CheckButton(_("Randomize order of images"))
2611 randomize.set_active(self.slideshow_random)
2612 randomize.set_tooltip_text(_("If enabled, a random image will be chosen during slideshow mode (without loading any image twice)."))
2613 disable_screensaver = gtk.CheckButton(_("Disable screensaver in slideshow mode"))
2614 disable_screensaver.set_active(self.disable_screensaver)
2615 disable_screensaver.set_tooltip_text(_("If enabled, xscreensaver will be temporarily disabled during slideshow mode."))
2616 ss_in_fs = gtk.CheckButton(_("Always start in fullscreen mode"))
2617 ss_in_fs.set_tooltip_text(_("If enabled, starting a slideshow will put the application in fullscreen mode."))
2618 ss_in_fs.set_active(self.slideshow_in_fullscreen)
2619 table_slideshow.attach(gtk.Label(), 1, 2, 1, 2, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 0, 0)
2620 table_slideshow.attach(slideshowlabel, 1, 2, 2, 3, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 15, 0)
2621 table_slideshow.attach(gtk.Label(), 1, 2, 3, 4, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 0, 0)
2622 table_slideshow.attach(hbox_delay, 1, 2, 4, 5, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2623 table_slideshow.attach(gtk.Label(), 1, 2, 5, 6, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 0, 0)
2624 table_slideshow.attach(disable_screensaver, 1, 2, 6, 7, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2625 table_slideshow.attach(ss_in_fs, 1, 2, 7, 8, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2626 table_slideshow.attach(randomize, 1, 2, 8, 9, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2627 table_slideshow.attach(gtk.Label(), 1, 2, 9, 10, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 0, 0)
2628 table_slideshow.attach(gtk.Label(), 1, 2, 10, 11, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 0, 0)
2629 table_slideshow.attach(gtk.Label(), 1, 2, 11, 12, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 0, 0)
2630 table_slideshow.attach(gtk.Label(), 1, 2, 12, 13, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 0, 0)
2631 table_slideshow.attach(gtk.Label(), 1, 2, 13, 14, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 0, 0)
2632 # "Image" tab:
2633 table_image = gtk.Table(14, 2, False)
2634 imagelabel = gtk.Label()
2635 imagelabel.set_markup('<b>' + _('Image Editing') + '</b>')
2636 imagelabel.set_alignment(0, 1)
2637 deletebutton = gtk.CheckButton(_("Confirm image delete"))
2638 deletebutton.set_active(self.confirm_delete)
2639
2640 zoom_hbox = gtk.HBox()
2641 zoom_hbox.pack_start(gtk.Label(_('Scaling quality:')), False, False, 0)
2642 zoomcombo = gtk.combo_box_new_text()
2643 zoomcombo.append_text(_("Nearest (Fastest)"))
2644 zoomcombo.append_text(_("Tiles"))
2645 zoomcombo.append_text(_("Bilinear"))
2646 zoomcombo.append_text(_("Hyper (Best)"))
2647 zoomcombo.set_active(self.zoomvalue)
2648 zoom_hbox.pack_start(zoomcombo, False, False, 0)
2649 zoom_hbox.pack_start(gtk.Label(), True, True, 0)
2650
2651 hbox_save = gtk.HBox()
2652 savelabel = gtk.Label(_("Modified images:"))
2653 savecombo = gtk.combo_box_new_text()
2654 savecombo.append_text(_("Ignore Changes"))
2655 savecombo.append_text(_("Auto-Save"))
2656 savecombo.append_text(_("Prompt For Action"))
2657 savecombo.set_active(self.savemode)
2658 hbox_save.pack_start(savelabel, False, False, 0)
2659 hbox_save.pack_start(savecombo, False, False, 5)
2660
2661 hbox_quality = gtk.HBox()
2662 qualitylabel = gtk.Label(_("Quality to save in:"))
2663 qspin_adj = gtk.Adjustment(self.quality_save, 0, 100, 1, 100, 0)
2664 qualityspin = gtk.SpinButton(qspin_adj, 1.0, 0)
2665 qualityspin.set_numeric(True)
2666 hbox_quality.pack_start(qualitylabel, False, False, 0)
2667 hbox_quality.pack_start(qualityspin, False, False, 5)
2668 table_image.attach(gtk.Label(), 1, 3, 1, 2, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2669 table_image.attach(imagelabel, 1, 3, 2, 3, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 15, 0)
2670 table_image.attach(gtk.Label(), 1, 3, 3, 4, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2671 table_image.attach(zoom_hbox, 1, 3, 4, 5, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2672 table_image.attach(gtk.Label(), 1, 3, 5, 6, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2673 table_image.attach(hbox_save, 1, 3, 6, 7, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2674 table_image.attach(gtk.Label(), 1, 3, 7, 8, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2675 table_image.attach(hbox_quality, 1, 3, 8, 9, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2676 table_image.attach(gtk.Label(), 1, 3, 9, 10, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2677 table_image.attach(deletebutton, 1, 3, 10, 11, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2678 table_image.attach(gtk.Label(), 1, 3, 11, 12, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2679 table_image.attach(gtk.Label(), 1, 3, 12, 13, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2680 table_image.attach(gtk.Label(), 1, 3, 13, 14, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2681 table_image.attach(gtk.Label(), 1, 3, 14, 15, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 30, 0)
2682 # Add tabs:
2683 notebook = gtk.Notebook()
2684 notebook.append_page(table_behavior, gtk.Label(_("Behavior")))
2685 notebook.append_page(table_navigation, gtk.Label(_("Navigation")))
2686 notebook.append_page(table_settings, gtk.Label(_("Interface")))
2687 notebook.append_page(table_slideshow, gtk.Label(_("Slideshow")))
2688 notebook.append_page(table_image, gtk.Label(_("Image")))
2689 notebook.set_current_page(0)
2690 hbox = gtk.HBox()
2691 self.prefs_dialog.vbox.pack_start(hbox, False, False, 7)
2692 hbox.pack_start(notebook, False, False, 7)
2693 notebook.connect('switch-page', self.prefs_tab_switched)
2694 # Show prefs:
2695 self.prefs_dialog.vbox.show_all()
2696 self.close_button = self.prefs_dialog.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)
2697 self.close_button.grab_focus()
2698 response = self.prefs_dialog.run()
2699 if response == gtk.RESPONSE_CLOSE or response == gtk.RESPONSE_DELETE_EVENT:
2700 self.zoomvalue = zoomcombo.get_active()
2701 if int(round(self.zoomvalue, 0)) == 0:
2702 self.zoom_quality = gtk.gdk.INTERP_NEAREST
2703 elif int(round(self.zoomvalue, 0)) == 1:
2704 self.zoom_quality = gtk.gdk.INTERP_TILES
2705 elif int(round(self.zoomvalue, 0)) == 2:
2706 self.zoom_quality = gtk.gdk.INTERP_BILINEAR
2707 elif int(round(self.zoomvalue, 0)) == 3:
2708 self.zoom_quality = gtk.gdk.INTERP_HYPER
2709 self.open_all_images = openallimages.get_active()
2710 self.open_hidden_files = hiddenimages.get_active()
2711 if openpref1.get_active():
2712 self.use_last_dir = True
2713 else:
2714 self.use_last_dir = False
2715 open_mode_prev = self.open_mode
2716 self.open_mode = combobox.get_active()
2717 preloading_images_prev = self.preloading_images
2718 self.preloading_images = preloadnav.get_active()
2719 self.listwrap_mode = combobox2.get_active()
2720 self.slideshow_delay = delayspin.get_value()
2721 self.curr_slideshow_delay = self.slideshow_delay
2722 self.slideshow_random = randomize.get_active()
2723 self.curr_slideshow_random = self.slideshow_random
2724 self.disable_screensaver = disable_screensaver.get_active()
2725 self.slideshow_in_fullscreen = ss_in_fs.get_active()
2726 self.savemode = savecombo.get_active()
2727 self.start_in_fullscreen = fullscreen.get_active()
2728 self.confirm_delete = deletebutton.get_active()
2729 self.quality_save = qualityspin.get_value()
2730 self.thumbnail_size = int(self.thumbnail_sizes[thumbsize.get_active()])
2731 if self.thumbnail_size != prev_thumbnail_size:
2732 gobject.idle_add(self.thumbpane_set_size)
2733 gobject.idle_add(self.thumbpane_update_images, True, self.curr_img_in_list)
2734 self.prefs_dialog.destroy()
2735 self.set_go_navigation_sensitivities(False)
2736 if (self.preloading_images and not preloading_images_prev) or (open_mode_prev != self.open_mode):
2737 # The user just turned on preloading, so do it:
2738 self.preloadimg_next_in_list = -1
2739 self.preloadimg_prev_in_list = -1
2740 self.preload_when_idle = gobject.idle_add(self.preload_next_image, False)
2741 self.preload_when_idle2 = gobject.idle_add(self.preload_prev_image, False)
2742 elif not self.preloading_images:
2743 self.preloadimg_next_in_list = -1
2744 self.preloadimg_prev_in_list = -1
2745
2746 def prefs_use_fixed_dir_clicked(self, button):
2747 if button.get_active():
2748 self.defaultdir.set_sensitive(True)
2749 else:
2750 self.defaultdir.set_sensitive(False)
2751
2752 def rename_image(self, action):
2753 if len(self.image_list) > 0:
2754 temp_slideshow_mode = self.slideshow_mode
2755 if self.slideshow_mode:
2756 self.toggle_slideshow(None)
2757 rename_dialog = gtk.Dialog(_('Rename Image'), self.window, gtk.DIALOG_MODAL)
2758 self.rename_txt = gtk.Entry()
2759 filename = os.path.basename(self.currimg_name)
2760 self.rename_txt.set_text(filename)
2761 self.rename_txt.set_activates_default(True)
2762 cancelbutton = rename_dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
2763 renamebutton = rename_dialog.add_button(_("_Rename"), gtk.RESPONSE_ACCEPT)
2764 renameimage = gtk.Image()
2765 renameimage.set_from_stock(gtk.STOCK_OK, gtk.ICON_SIZE_BUTTON)
2766 renamebutton.set_image(renameimage)
2767 animtest = gtk.gdk.PixbufAnimation(self.currimg_name)
2768 if animtest.is_static_image():
2769 pixbuf, image_width, image_height = self.get_pixbuf_of_size(self.currimg_pixbuf_original, 60, self.zoom_quality)
2770 else:
2771 pixbuf, image_width, image_height = self.get_pixbuf_of_size(animtest.get_static_image(), 60, self.zoom_quality)
2772 image = gtk.Image()
2773 image.set_from_pixbuf(pixbuf)
2774 instructions = gtk.Label(_("Enter the new name:"))
2775 instructions.set_alignment(0, 1)
2776 hbox = gtk.HBox()
2777 hbox.pack_start(image, False, False, 10)
2778 vbox_stuff = gtk.VBox()
2779 vbox_stuff.pack_start(gtk.Label(), False, False, 0)
2780 vbox_stuff.pack_start(instructions, False, False, 0)
2781 vbox_stuff.pack_start(gtk.Label(), False, False, 0)
2782 vbox_stuff.pack_start(self.rename_txt, True, True, 0)
2783 vbox_stuff.pack_start(gtk.Label(), False, False, 0)
2784 hbox.pack_start(vbox_stuff, True, True, 10)
2785 rename_dialog.vbox.pack_start(hbox, False, False, 0)
2786 rename_dialog.set_has_separator(True)
2787 rename_dialog.set_default_response(gtk.RESPONSE_ACCEPT)
2788 rename_dialog.set_size_request(300, -1)
2789 rename_dialog.vbox.show_all()
2790 rename_dialog.connect('show', self.select_rename_text)
2791 response = rename_dialog.run()
2792 if response == gtk.RESPONSE_ACCEPT:
2793 try:
2794 new_filename = os.path.dirname(self.currimg_name) + "/" + self.rename_txt.get_text()
2795 shutil.move(self.currimg_name, new_filename)
2796 # Update thumbnail filename:
2797 try:
2798 shutil.move(self_get_name(self.currimg_name)[1], self.thumbnail_get_name(new_filename)[1])
2799 except:
2800 pass
2801 self.recent_file_remove_and_refresh_name(self.currimg_name)
2802 self.currimg_name = new_filename
2803 self.register_file_with_recent_docs(self.currimg_name)
2804 self.update_title()
2805 except:
2806 error_dialog = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_WARNING, gtk.BUTTONS_OK, _('Unable to rename %s') % self.currimg_name)
2807 error_dialog.set_title(_("Unable to rename"))
2808 error_dialog.run()
2809 error_dialog.destroy()
2810 rename_dialog.destroy()
2811 if temp_slideshow_mode:
2812 self.toggle_slideshow(None)
2813
2814 def select_rename_text(self, widget):
2815 filename = os.path.basename(self.currimg_name)
2816 fileext = os.path.splitext(os.path.basename(self.currimg_name))[1]
2817 self.rename_txt.select_region(0, len(filename) - len(fileext))
2818
2819 def delete_image(self, action):
2820 if len(self.image_list) > 0:
2821 temp_slideshow_mode = self.slideshow_mode
2822 if self.slideshow_mode:
2823 self.toggle_slideshow(None)
2824 delete_dialog = gtk.Dialog(_('Delete Image'), self.window, gtk.DIALOG_MODAL)
2825 if self.confirm_delete:
2826 permlabel = gtk.Label(_('Are you sure you wish to permanently delete %s?') % os.path.split(self.currimg_name)[1])
2827 permlabel.set_line_wrap(True)
2828 permlabel.set_alignment(0, 0.1)
2829 warningicon = gtk.Image()
2830 warningicon.set_from_stock(gtk.STOCK_DIALOG_WARNING, gtk.ICON_SIZE_DIALOG)
2831 hbox = gtk.HBox()
2832 hbox.pack_start(warningicon, False, False, 10)
2833 hbox.pack_start(permlabel, False, False, 10)
2834 delete_dialog.vbox.pack_start(gtk.Label(), False, False, 0)
2835 delete_dialog.vbox.pack_start(hbox, False, False, 0)
2836 cancelbutton = delete_dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
2837 deletebutton = delete_dialog.add_button(gtk.STOCK_DELETE, gtk.RESPONSE_YES)
2838 delete_dialog.set_has_separator(False)
2839 deletebutton.set_property('has-focus', True)
2840 delete_dialog.set_default_response(gtk.RESPONSE_YES)
2841 delete_dialog.vbox.show_all()
2842 response = delete_dialog.run()
2843 else:
2844 response = gtk.RESPONSE_YES
2845 if response == gtk.RESPONSE_YES:
2846 try:
2847 os.remove(self.currimg_name)
2848 self.image_modified = False
2849 try:
2850 os.remove(self.thumbnail_get_name(self.currimg_name)[1])
2851 except:
2852 pass
2853 self.recent_file_remove_and_refresh_name(self.currimg_name)
2854 iter = self.thumblist.get_iter((self.curr_img_in_list,))
2855 try:
2856 self.thumbnail_loaded.pop(self.curr_img_in_list)
2857 self.thumbpane_update_images()
2858 except:
2859 pass
2860 self.thumblist.remove(iter)
2861 templist = self.image_list
2862 self.image_list = []
2863 for item in templist:
2864 if item != self.currimg_name:
2865 self.image_list.append(item)
2866 if len(self.image_list) >= 1:
2867 if len(self.image_list) == 1:
2868 self.curr_img_in_list = 0
2869 elif self.curr_img_in_list == len(self.image_list):
2870 self.curr_img_in_list -= 1
2871 self.change_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
2872 self.preloadimg_prev_in_list = -1
2873 self.preloadimg_next_in_list = -1
2874 self.load_when_idle = gobject.idle_add(self.load_new_image, False, False, True, True, True, True)
2875 self.set_go_navigation_sensitivities(False)
2876 else:
2877 self.imageview.clear()
2878 self.update_title()
2879 self.statusbar.push(self.statusbar.get_context_id(""), "")
2880 self.image_loaded = False
2881 self.set_slideshow_sensitivities()
2882 self.set_image_sensitivities(False)
2883 self.set_go_navigation_sensitivities(False)
2884 # Select new item:
2885 self.thumbpane_select(self.curr_img_in_list)
2886 except:
2887 error_dialog = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_WARNING, gtk.BUTTONS_OK, _('Unable to delete %s') % self.currimg_name)
2888 error_dialog.set_title(_("Unable to delete"))
2889 error_dialog.run()
2890 error_dialog.destroy()
2891 delete_dialog.destroy()
2892 if temp_slideshow_mode:
2893 self.toggle_slideshow(None)
2894
2895 def defaultdir_clicked(self, button):
2896 getdir = gtk.FileChooserDialog(title=_("Choose directory"),action=gtk.FILE_CHOOSER_ACTION_OPEN,buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
2897 getdir.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
2898 getdir.set_filename(self.fixed_dir)
2899 getdir.set_default_response(gtk.RESPONSE_OK)
2900 response = getdir.run()
2901 if response == gtk.RESPONSE_OK:
2902 self.fixed_dir = getdir.get_filenames()[0]
2903 if len(self.fixed_dir) > 25:
2904 button.set_label('...' + self.fixed_dir[-22:])
2905 else:
2906 button.set_label(self.fixed_dir)
2907 getdir.destroy()
2908 else:
2909 getdir.destroy()
2910
2911 def prefs_tab_switched(self, notebook, page, page_num):
2912 do_when_idle = gobject.idle_add(self.grab_close_button)
2913
2914 def grab_close_button(self):
2915 self.close_button.grab_focus()
2916
2917 def bgcolor_selected(self, widget):
2918 # When the user selects a color, store this color in self.bgcolor (which will
2919 # later be saved to .miragerc) and set this background color:
2920 self.bgcolor = widget.get_property('color')
2921 if not self.simple_bgcolor:
2922 self.layout.modify_bg(gtk.STATE_NORMAL, self.bgcolor)
2923 self.slideshow_window.modify_bg(gtk.STATE_NORMAL, self.bgcolor)
2924 self.slideshow_window2.modify_bg(gtk.STATE_NORMAL, self.bgcolor)
2925
2926 def simple_bgcolor_selected(self, widget):
2927 if widget.get_active():
2928 self.simple_bgcolor = True
2929 self.layout.modify_bg(gtk.STATE_NORMAL, None)
2930 else:
2931 self.simple_bgcolor = False
2932 self.bgcolor_selected(self.colorbutton)
2933
2934 def show_about(self, action):
2935 # Help > About
2936 self.about_dialog = gtk.AboutDialog()
2937 try:
2938 self.about_dialog.set_transient_for(self.window)
2939 self.about_dialog.set_modal(True)
2940 except:
2941 pass
2942 self.about_dialog.set_name('Mirage')
2943 self.about_dialog.set_version(__version__)
2944 self.about_dialog.set_comments(_('A fast GTK+ Image Viewer.'))
2945 self.about_dialog.set_license(__license__)
2946 self.about_dialog.set_authors(['Scott Horowitz <stonecrest@gmail.com>', 'Fredric Johansson <fredric.miscmail@gmail.com>'])
2947 self.about_dialog.set_artists(['William Rea <sillywilly@gmail.com>'])
2948 self.about_dialog.set_translator_credits('cs - Petr Pisar <petr.pisar@atlas.cz>\nde - Bjoern Martensen <bjoern.martensen@gmail.com>\nes - Isidro Arribas <cdhotfire@gmail.com>\nfr - Mike Massonnet <mmassonnet@gmail.com>\nhu - Sandor Lisovszki <lisovszki@dunakanyar.net>\nnl - Pascal De Vuyst <pascal.devuyst@gmail.com>\npl - Tomasz Dominikowski <dominikowski@gmail.com>\npt_BR - Danilo Martins <mawkee@gmail.com>\nru - mavka <mavka@justos.org>\nit - Daniele Maggio <dado84@freemail.it>\nzh_CN - Jayden Suen <no.sun@163.com>')
2949 gtk.about_dialog_set_url_hook(self.show_website, "http://mirageiv.berlios.de")
2950 self.about_dialog.set_website_label("http://mirageiv.berlios.de")
2951 icon_path = self.find_path('mirage.png')
2952 try:
2953 icon_pixbuf = gtk.gdk.pixbuf_new_from_file(icon_path)
2954 self.about_dialog.set_logo(icon_pixbuf)
2955 except:
2956 pass
2957 self.about_dialog.connect('response', self.close_about)
2958 self.about_dialog.connect('delete_event', self.close_about)
2959 self.about_dialog.show_all()
2960
2961 def show_website(self, dialog, blah, link):
2962 self.browser_load(link)
2963
2964 def show_help(self, action):
2965 self.browser_load("http://mirageiv.berlios.de/docs.html")
2966
2967 def browser_load(self, docslink):
2968 try:
2969 pid = subprocess.Popen(["gnome-open", docslink]).pid
2970 except:
2971 try:
2972 pid = subprocess.Popen(["exo-open", docslink]).pid
2973 except:
2974 try:
2975 pid = subprocess.Popen(["kfmclient", "openURL", docslink]).pid
2976 except:
2977 try:
2978 pid = subprocess.Popen(["firefox", docslink]).pid
2979 except:
2980 try:
2981 pid = subprocess.Popen(["mozilla", docslink]).pid
2982 except:
2983 try:
2984 pid = subprocess.Popen(["opera", docslink]).pid
2985 except:
2986 error_dialog = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_WARNING, gtk.BUTTONS_CLOSE, _('Unable to launch a suitable browser.'))
2987 error_dialog.run()
2988 error_dialog.destroy()
2989
2990 def close_about(self, event, data=None):
2991 self.about_dialog.hide()
2992 return True
2993
2994 def mousewheel_scrolled(self, widget, event):
2995 if event.type == gtk.gdk.SCROLL:
2996 # Zooming of the image by Ctrl-mousewheel
2997 if event.state & gtk.gdk.CONTROL_MASK:
2998 if event.direction == gtk.gdk.SCROLL_UP:
2999 self.zoom_in(None)
3000 elif event.direction == gtk.gdk.SCROLL_DOWN:
3001 self.zoom_out(None)
3002 return True
3003 # Navigation of images with mousewheel:
3004 else:
3005 if event.direction == gtk.gdk.SCROLL_UP:
3006 self.goto_prev_image(None)
3007 elif event.direction == gtk.gdk.SCROLL_DOWN:
3008 self.goto_next_image(None)
3009 return True
3010
3011 def mouse_moved(self, widget, event):
3012 # This handles the panning of the image
3013 if event.is_hint:
3014 x, y, state = event.window.get_pointer()
3015 else:
3016 state = event.state
3017 x, y = event.x_root, event.y_root
3018 if (state & gtk.gdk.BUTTON2_MASK) or (state & gtk.gdk.BUTTON1_MASK):
3019 # Prevent self.expose_event() from potentially further changing the
3020 # adjustments upon the adjustment value changes
3021 self.updating_adjustments = True
3022 xadjust = self.layout.get_hadjustment()
3023 newx = xadjust.value + (self.prevmousex - x)
3024 if newx >= xadjust.lower and newx <= xadjust.upper - xadjust.page_size:
3025 xadjust.set_value(newx)
3026 self.layout.set_hadjustment(xadjust)
3027 yadjust = self.layout.get_vadjustment()
3028 newy = yadjust.value + (self.prevmousey - y)
3029 if newy >= yadjust.lower and newy <= yadjust.upper - yadjust.page_size:
3030 yadjust.set_value(newy)
3031 self.layout.set_vadjustment(yadjust)
3032 self.updating_adjustments = False
3033 self.prevmousex = x
3034 self.prevmousey = y
3035 if self.fullscreen_mode:
3036 # Show cursor on movement, then hide after 2 seconds of no movement
3037 self.change_cursor(None)
3038 if not self.slideshow_controls_visible:
3039 gobject.source_remove(self.timer_id)
3040 if not self.closing_app:
3041 while gtk.events_pending():
3042 gtk.main_iteration()
3043 self.timer_id = gobject.timeout_add(2000, self.hide_cursor)
3044 if y > 0.9*self.available_image_height():
3045 self.slideshow_controls_show()
3046 else:
3047 self.slideshow_controls_hide()
3048 return True
3049
3050 def button_pressed(self, widget, event):
3051 if self.image_loaded:
3052 # Changes the cursor to the 'resize' cursor, like GIMP, on a middle click:
3053 if (event.button == 2 or event.button == 1) and (self.hscroll.get_property('visible')==True or self.vscroll.get_property('visible')==True):
3054 self.change_cursor(gtk.gdk.Cursor(gtk.gdk.FLEUR))
3055 self.prevmousex = event.x_root
3056 self.prevmousey = event.y_root
3057 # Right-click popup:
3058 elif self.image_loaded and event.button == 3:
3059 self.UIManager.get_widget('/Popup').popup(None, None, None, event.button, event.time)
3060 return True
3061
3062 def button_released(self, widget, event):
3063 # Resets the cursor when middle mouse button is released
3064 if event.button == 2 or event.button == 1:
3065 self.change_cursor(None)
3066 return True
3067
3068 def zoom_in(self, action):
3069 if self.currimg_name != "" and self.UIManager.get_widget('/MainMenu/ViewMenu/In').get_property('sensitive'):
3070 self.image_zoomed = True
3071 self.currimg_zoomratio = self.currimg_zoomratio * 1.25
3072 self.set_zoom_sensitivities()
3073 self.last_image_action_was_fit = False
3074 self.put_zoom_image_to_window(False)
3075 self.update_statusbar()
3076
3077 def zoom_out(self, action):
3078 if self.currimg_name != "" and self.UIManager.get_widget('/MainMenu/ViewMenu/Out').get_property('sensitive'):
3079 if self.currimg_zoomratio == self.min_zoomratio:
3080 # No point in proceeding..
3081 return
3082 self.image_zoomed = True
3083 self.currimg_zoomratio = self.currimg_zoomratio * 1/1.25
3084 if self.currimg_zoomratio < self.min_zoomratio:
3085 self.currimg_zoomratio = self.min_zoomratio
3086 self.set_zoom_sensitivities()
3087 self.last_image_action_was_fit = False
3088 self.put_zoom_image_to_window(False)
3089 self.update_statusbar()
3090
3091 def zoom_to_fit_window_action(self, action):
3092 self.zoom_to_fit_window(action, False, False)
3093
3094 def zoom_to_fit_window(self, action, is_preloadimg_next, is_preloadimg_prev):
3095 if is_preloadimg_next:
3096 if self.preloading_images and self.preloadimg_next_in_list != -1:
3097 win_width = self.available_image_width()
3098 win_height = self.available_image_height()
3099 preimg_width = self.preloadimg_next_pixbuf_original.get_width()
3100 preimg_height = self.preloadimg_next_pixbuf_original.get_height()
3101 prewidth_ratio = float(preimg_width)/win_width
3102 preheight_ratio = float(preimg_height)/win_height
3103 if prewidth_ratio < preheight_ratio:
3104 premax_ratio = preheight_ratio
3105 else:
3106 premax_ratio = prewidth_ratio
3107 self.preloadimg_next_zoomratio = 1/float(max_ratio)
3108 elif is_preloadimg_prev:
3109 if self.preloading_images and self.preloadimg_prev_in_list != -1:
3110 win_width = self.available_image_width()
3111 win_height = self.available_image_height()
3112 preimg_width = self.preloadimg_prev_pixbuf_original.get_width()
3113 preimg_height = self.preloadimg_prev_pixbuf_original.get_height()
3114 prewidth_ratio = float(preimg_width)/win_width
3115 preheight_ratio = float(preimg_height)/win_height
3116 if prewidth_ratio < preheight_ratio:
3117 premax_ratio = preheight_ratio
3118 else:
3119 premax_ratio = prewidth_ratio
3120 self.preloadimg_prev_zoomratio = 1/float(max_ratio)
3121 else:
3122 if self.currimg_name != "" and (self.slideshow_mode or self.UIManager.get_widget('/MainMenu/ViewMenu/Fit').get_property('sensitive')):
3123 self.image_zoomed = True
3124 self.last_mode = self.open_mode_fit
3125 self.last_image_action_was_fit = True
3126 self.last_image_action_was_smart_fit = False
3127 # Calculate zoomratio needed to fit to window:
3128 win_width = self.available_image_width()
3129 win_height = self.available_image_height()
3130 img_width = self.currimg_pixbuf_original.get_width()
3131 img_height = self.currimg_pixbuf_original.get_height()
3132 width_ratio = float(img_width)/win_width
3133 height_ratio = float(img_height)/win_height
3134 if width_ratio < height_ratio:
3135 max_ratio = height_ratio
3136 else:
3137 max_ratio = width_ratio
3138 self.currimg_zoomratio = 1/float(max_ratio)
3139 self.set_zoom_sensitivities()
3140 self.put_zoom_image_to_window(False)
3141 self.update_statusbar()
3142
3143 def zoom_to_fit_or_1_to_1(self, action, is_preloadimg_next, is_preloadimg_prev):
3144 if is_preloadimg_next:
3145 if self.preloading_images and self.preloadimg_next_in_list != -1:
3146 win_width = self.available_image_width()
3147 win_height = self.available_image_height()
3148 preimg_width = self.preloadimg_next_pixbuf_original.get_width()
3149 preimg_height = self.preloadimg_next_pixbuf_original.get_height()
3150 prewidth_ratio = float(preimg_width)/win_width
3151 preheight_ratio = float(preimg_height)/win_height
3152 if prewidth_ratio < preheight_ratio:
3153 premax_ratio = preheight_ratio
3154 else:
3155 premax_ratio = prewidth_ratio
3156 self.preloadimg_next_zoomratio = 1/float(premax_ratio)
3157 if self.preloadimg_next_zoomratio > 1:
3158 self.preloadimg_next_zoomratio = 1
3159 elif is_preloadimg_prev:
3160 if self.preloading_images and self.preloadimg_prev_in_list != -1:
3161 win_width = self.available_image_width()
3162 win_height = self.available_image_height()
3163 preimg_width = self.preloadimg_prev_pixbuf_original.get_width()
3164 preimg_height = self.preloadimg_prev_pixbuf_original.get_height()
3165 prewidth_ratio = float(preimg_width)/win_width
3166 preheight_ratio = float(preimg_height)/win_height
3167 if prewidth_ratio < preheight_ratio:
3168 premax_ratio = preheight_ratio
3169 else:
3170 premax_ratio = prewidth_ratio
3171 self.preloadimg_prev_zoomratio = 1/float(premax_ratio)
3172 if self.preloadimg_prev_zoomratio > 1:
3173 self.preloadimg_prev_zoomratio = 1
3174 else:
3175 if self.currimg_name != "":
3176 self.image_zoomed = True
3177 # Calculate zoomratio needed to fit to window:
3178 win_width = self.available_image_width()
3179 win_height = self.available_image_height()
3180 img_width = self.currimg_pixbuf_original.get_width()
3181 img_height = self.currimg_pixbuf_original.get_height()
3182 width_ratio = float(img_width)/win_width
3183 height_ratio = float(img_height)/win_height
3184 if width_ratio < height_ratio:
3185 max_ratio = height_ratio
3186 else:
3187 max_ratio = width_ratio
3188 self.currimg_zoomratio = 1/float(max_ratio)
3189 self.set_zoom_sensitivities()
3190 if self.currimg_zoomratio > 1:
3191 # Revert to 1:1 zoom
3192 self.zoom_1_to_1(action, False, False)
3193 else:
3194 self.put_zoom_image_to_window(False)
3195 self.update_statusbar()
3196 self.last_image_action_was_fit = True
3197 self.last_image_action_was_smart_fit = True
3198
3199 def zoom_1_to_1_action(self, action):
3200 self.zoom_1_to_1(action, False, False)
3201
3202 def zoom_1_to_1(self, action, is_preloadimg_next, is_preloadimg_prev):
3203 if is_preloadimg_next:
3204 if self.preloading_images:
3205 self.preloadimg_next_zoomratio = 1
3206 elif is_preloadimg_prev:
3207 if self.preloading_images:
3208 self.preloadimg_prev_zoomratio = 1
3209 else:
3210 if self.currimg_name != "" and (self.slideshow_mode or self.currimg_is_animation or (not self.currimg_is_animation and self.UIManager.get_widget('/MainMenu/ViewMenu/1:1').get_property('sensitive'))):
3211 self.image_zoomed = True
3212 self.last_mode = self.open_mode_1to1
3213 self.last_image_action_was_fit = False
3214 self.currimg_zoomratio = 1
3215 self.put_zoom_image_to_window(False)
3216 self.update_statusbar()
3217
3218 def rotate_left(self, action):
3219 self.rotate_left_or_right(self.UIManager.get_widget('/MainMenu/EditMenu/Rotate Left'), 90)
3220
3221 def rotate_right(self, action):
3222 self.rotate_left_or_right(self.UIManager.get_widget('/MainMenu/EditMenu/Rotate Right'), 270)
3223
3224 def rotate_left_or_right(self, widget, angle):
3225 if self.currimg_name != "" and widget.get_property('sensitive'):
3226 self.currimg_pixbuf_original = self.image_rotate(self.currimg_pixbuf_original, angle)
3227 if self.last_image_action_was_fit:
3228 if self.last_image_action_was_smart_fit:
3229 self.zoom_to_fit_or_1_to_1(None, False, False)
3230 else:
3231 self.zoom_to_fit_window(None, False, False)
3232 else:
3233 self.currimg_width, self.currimg_height = self.currimg_height, self.currimg_width
3234 self.layout.set_size(self.currimg_width, self.currimg_height)
3235 self.currimg_pixbuf = self.image_rotate(self.currimg_pixbuf, angle)
3236 self.imageview.set_from_pixbuf(self.currimg_pixbuf)
3237 self.show_scrollbars_if_needed()
3238 self.center_image()
3239 self.update_statusbar()
3240 self.image_modified = True
3241
3242
3243 def flip_image_vert(self, action):
3244 self.flip_image_vert_or_horiz(self.UIManager.get_widget('/MainMenu/EditMenu/Flip Vertically'), True)
3245
3246 def flip_image_horiz(self, action):
3247 self.flip_image_vert_or_horiz(self.UIManager.get_widget('/MainMenu/EditMenu/Flip Horizontally'), False)
3248
3249 def flip_image_vert_or_horiz(self, widget, vertical):
3250 if self.currimg_name != "" and widget.get_property('sensitive'):
3251 self.currimg_pixbuf = self.image_flip(self.currimg_pixbuf, vertical)
3252 self.currimg_pixbuf_original = self.image_flip(self.currimg_pixbuf_original, vertical)
3253 self.imageview.set_from_pixbuf(self.currimg_pixbuf)
3254 self.image_modified = True
3255
3256 def get_pixbuf_of_size(self, pixbuf, size, zoom_quality):
3257 # Creates a pixbuf that fits in the specified square of sizexsize
3258 # while preserving the aspect ratio
3259 # Returns tuple: (scaled_pixbuf, actual_width, actual_height)
3260 image_width = pixbuf.get_width()
3261 image_height = pixbuf.get_height()
3262 if image_width-size > image_height-size:
3263 if image_width > size:
3264 image_height = int(size/float(image_width)*image_height)
3265 image_width = size
3266 else:
3267 if image_height > size:
3268 image_width = int(size/float(image_height)*image_width)
3269 image_height = size
3270 if not pixbuf.get_has_alpha():
3271 crop_pixbuf = pixbuf.scale_simple(image_width, image_height, zoom_quality)
3272 else:
3273 colormap = self.imageview.get_colormap()
3274 light_grey = colormap.alloc_color('#666666', True, True)
3275 dark_grey = colormap.alloc_color('#999999', True, True)
3276 crop_pixbuf = pixbuf.composite_color_simple(image_width, image_height, zoom_quality, 255, 8, light_grey.pixel, dark_grey.pixel)
3277 return (crop_pixbuf, image_width, image_height)
3278
3279 def pixbuf_add_border(self, pix):
3280 # Add a gray outline to pix. This will increase the pixbuf size by
3281 # 2 pixels lengthwise and heightwise, 1 on each side. Returns pixbuf.
3282 try:
3283 width = pix.get_width()
3284 height = pix.get_height()
3285 newpix = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, width+2, height+2)
3286 newpix.fill(0x858585ff)
3287 pix.copy_area(0, 0, width, height, newpix, 1, 1)
3288 return newpix
3289 except:
3290 return pix
3291
3292 def crop_image(self, action):
3293 dialog = gtk.Dialog(_("Crop Image"), self.window, gtk.DIALOG_MODAL, (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT))
3294 cropbutton = dialog.add_button(_("C_rop"), gtk.RESPONSE_ACCEPT)
3295 cropimage = gtk.Image()
3296 cropimage.set_from_stock(gtk.STOCK_OK, gtk.ICON_SIZE_BUTTON)
3297 cropbutton.set_image(cropimage)
3298 image = gtk.DrawingArea()
3299 crop_pixbuf, image_width, image_height = self.get_pixbuf_of_size(self.currimg_pixbuf_original, 400, self.zoom_quality)
3300 image.set_size_request(image_width, image_height)
3301 hbox = gtk.HBox()
3302 hbox.pack_start(gtk.Label(), expand=True)
3303 hbox.pack_start(image, expand=False)
3304 hbox.pack_start(gtk.Label(), expand=True)
3305 vbox_left = gtk.VBox()
3306 x_adj = gtk.Adjustment(0, 0, self.currimg_pixbuf_original.get_width(), 1, 10, 0)
3307 x = gtk.SpinButton(x_adj, 0, 0)
3308 x.set_numeric(True)
3309 x.set_update_policy(gtk.UPDATE_IF_VALID)
3310 x.set_wrap(False)
3311 x_label = gtk.Label("X:")
3312 x_label.set_alignment(0, 0.7)
3313 y_adj = gtk.Adjustment(0, 0, self.currimg_pixbuf_original.get_height(), 1, 10, 0)
3314 y = gtk.SpinButton(y_adj, 0, 0)
3315 y.set_numeric(True)
3316 y.set_update_policy(gtk.UPDATE_IF_VALID)
3317 y.set_wrap(False)
3318 y_label = gtk.Label("Y:")
3319 x_label.set_size_request(y_label.size_request()[0], -1)
3320 hbox_x = gtk.HBox()
3321 hbox_y = gtk.HBox()
3322 hbox_x.pack_start(x_label, False, False, 10)
3323 hbox_x.pack_start(x, False, False, 0)
3324 hbox_x.pack_start(gtk.Label(), False, False, 3)
3325 hbox_y.pack_start(y_label, False, False, 10)
3326 hbox_y.pack_start(y, False, False, 0)
3327 hbox_y.pack_start(gtk.Label(), False, False, 3)
3328 vbox_left.pack_start(hbox_x, False, False, 0)
3329 vbox_left.pack_start(hbox_y, False, False, 0)
3330 vbox_right = gtk.VBox()
3331 width_adj = gtk.Adjustment(self.currimg_pixbuf_original.get_width(), 1, self.currimg_pixbuf_original.get_width(), 1, 10, 0)
3332 width = gtk.SpinButton(width_adj, 0, 0)
3333 width.set_numeric(True)
3334 width.set_update_policy(gtk.UPDATE_IF_VALID)
3335 width.set_wrap(False)
3336 width_label = gtk.Label(_("Width:"))
3337 width_label.set_alignment(0, 0.7)
3338 height_adj = gtk.Adjustment(self.currimg_pixbuf_original.get_height(), 1, self.currimg_pixbuf_original.get_height(), 1, 10, 0)
3339 height = gtk.SpinButton(height_adj, 0, 0)
3340 height.set_numeric(True)
3341 height.set_update_policy(gtk.UPDATE_IF_VALID)
3342 height.set_wrap(False)
3343 height_label = gtk.Label(_("Height:"))
3344 width_label.set_size_request(height_label.size_request()[0], -1)
3345 height_label.set_alignment(0, 0.7)
3346 hbox_width = gtk.HBox()
3347 hbox_height = gtk.HBox()
3348 hbox_width.pack_start(width_label, False, False, 10)
3349 hbox_width.pack_start(width, False, False, 0)
3350 hbox_height.pack_start(height_label, False, False, 10)
3351 hbox_height.pack_start(height, False, False, 0)
3352 vbox_right.pack_start(hbox_width, False, False, 0)
3353 vbox_right.pack_start(hbox_height, False, False, 0)
3354 hbox2 = gtk.HBox()
3355 hbox2.pack_start(gtk.Label(), expand=True)
3356 hbox2.pack_start(vbox_left, False, False, 0)
3357 hbox2.pack_start(vbox_right, False, False, 0)
3358 hbox2.pack_start(gtk.Label(), expand=True)
3359 dialog.vbox.pack_start(hbox, False, False, 0)
3360 dialog.vbox.pack_start(hbox2, False, False, 15)
3361 dialog.set_resizable(False)
3362 dialog.vbox.show_all()
3363 image.set_events(gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.POINTER_MOTION_HINT_MASK | gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_MOTION_MASK | gtk.gdk.BUTTON_RELEASE_MASK)
3364 image.connect("expose-event", self.crop_image_expose_cb, crop_pixbuf, image_width, image_height)
3365 image.connect("motion_notify_event", self.crop_image_mouse_moved, image, 0, 0, x, y, width, height, image_width, image_height, width_adj, height_adj)
3366 image.connect("button_press_event", self.crop_image_button_press, image)
3367 image.connect("button_release_event", self.crop_image_button_release)
3368 self.x_changed = x.connect('value-changed', self.crop_value_changed, x, y, width, height, width_adj, height_adj, image_width, image_height, image, 0)
3369 self.y_changed = y.connect('value-changed', self.crop_value_changed, x, y, width, height, width_adj, height_adj, image_width, image_height, image, 1)
3370 self.width_changed = width.connect('value-changed', self.crop_value_changed, x, y, width, height, width_adj, height_adj, image_width, image_height, image, 2)
3371 self.height_changed = height.connect('value-changed', self.crop_value_changed, x, y, width, height, width_adj, height_adj, image_width, image_height, image, 3)
3372 image.realize()
3373 self.crop_rectangle = [0, 0]
3374 self.drawing_crop_rectangle = False
3375 self.update_rectangle = False
3376 self.rect = None
3377 response = dialog.run()
3378 if response == gtk.RESPONSE_ACCEPT:
3379 dialog.destroy()
3380 if self.rect != None:
3381 temp_pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, self.currimg_pixbuf_original.get_has_alpha(), 8, self.coords[2], self.coords[3])
3382 self.currimg_pixbuf_original.copy_area(self.coords[0], self.coords[1], self.coords[2], self.coords[3], temp_pixbuf, 0, 0)
3383 self.currimg_pixbuf_original = temp_pixbuf
3384 del temp_pixbuf
3385 gc.collect()
3386 self.load_new_image2(False, True, False, False)
3387 self.image_modified = True
3388 else:
3389 dialog.destroy()
3390
3391 def crop_value_changed(self, currspinbox, x, y, width, height, width_adj, height_adj, image_width, image_height, image, type):
3392 if type == 0: # X
3393 if x.get_value() + width.get_value() > self.currimg_pixbuf_original.get_width():
3394 width.handler_block(self.width_changed)
3395 width.set_value(self.currimg_pixbuf_original.get_width() - x.get_value())
3396 width.handler_unblock(self.width_changed)
3397 elif type == 1: # Y
3398 if y.get_value() + height.get_value() > self.currimg_pixbuf_original.get_height():
3399 height.handler_block(self.height_changed)
3400 height.set_value(self.currimg_pixbuf_original.get_height() - y.get_value())
3401 height.handler_unblock(self.height_changed)
3402 self.coords = [int(x.get_value()), int(y.get_value()), int(width.get_value()), int(height.get_value())]
3403 self.crop_rectangle[0] = int(round(float(self.coords[0])/self.currimg_pixbuf_original.get_width()*image_width, 0))
3404 self.crop_rectangle[1] = int(round(float(self.coords[1])/self.currimg_pixbuf_original.get_height()*image_height, 0))
3405 x2 = int(round(float(self.coords[2])/self.currimg_pixbuf_original.get_width()*image_width, 0)) + self.crop_rectangle[0]
3406 y2 = int(round(float(self.coords[3])/self.currimg_pixbuf_original.get_height()*image_height, 0)) + self.crop_rectangle[1]
3407 self.drawing_crop_rectangle = True
3408 self.update_rectangle = True
3409 self.crop_image_mouse_moved(None, None, image, x2, y2, x, y, width, height, image_width, image_height, width_adj, height_adj)
3410 self.update_rectangle = False
3411 self.drawing_crop_rectangle = False
3412
3413 def crop_image_expose_cb(self, image, event, pixbuf, width, height):
3414 image.window.draw_pixbuf(None, pixbuf, 0, 0, 0, 0, width, height)
3415
3416 def crop_image_mouse_moved(self, widget, event, image, x2, y2, x, y, width, height, image_width, image_height, width_adj, height_adj):
3417 if event != None:
3418 x2, y2, state = event.window.get_pointer()
3419 if self.drawing_crop_rectangle:
3420 if self.crop_rectangle != None or self.update_rectangle:
3421 gc = image.window.new_gc(function=gtk.gdk.INVERT)
3422 if self.rect != None:
3423 # Get rid of the previous drawn rectangle:
3424 image.window.draw_rectangle(gc, False, self.rect[0], self.rect[1], self.rect[2], self.rect[3])
3425 self.rect = [0, 0, 0, 0]
3426 if self.crop_rectangle[0] > x2:
3427 self.rect[0] = x2
3428 self.rect[2] = self.crop_rectangle[0]-x2
3429 else:
3430 self.rect[0] = self.crop_rectangle[0]
3431 self.rect[2] = x2-self.crop_rectangle[0]
3432 if self.crop_rectangle[1] > y2:
3433 self.rect[1] = y2
3434 self.rect[3] = self.crop_rectangle[1]-y2
3435 else:
3436 self.rect[1] = self.crop_rectangle[1]
3437 self.rect[3] = y2-self.crop_rectangle[1]
3438 image.window.draw_rectangle(gc, False, self.rect[0], self.rect[1], self.rect[2], self.rect[3])
3439 # Convert the rectangle coordinates of the current image
3440 # to coordinates of pixbuf_original
3441 if self.rect[0] < 0:
3442 self.rect[2] = self.rect[2] + self.rect[0]
3443 self.rect[0] = 0
3444 if self.rect[1] < 0:
3445 self.rect[3] = self.rect[3] + self.rect[1]
3446 self.rect[1] = 0
3447 if event != None:
3448 self.coords = [0,0,0,0]
3449 self.coords[0] = int(round(float(self.rect[0])/image_width*self.currimg_pixbuf_original.get_width(), 0))
3450 self.coords[1] = int(round(float(self.rect[1])/image_height*self.currimg_pixbuf_original.get_height(), 0))
3451 self.coords[2] = int(round(float(self.rect[2])/image_width*self.currimg_pixbuf_original.get_width(), 0))
3452 self.coords[3] = int(round(float(self.rect[3])/image_height*self.currimg_pixbuf_original.get_height(), 0))
3453 if self.coords[0] + self.coords[2] > self.currimg_pixbuf_original.get_width():
3454 self.coords[2] = self.currimg_pixbuf_original.get_width() - self.coords[0]
3455 if self.coords[1] + self.coords[3] > self.currimg_pixbuf_original.get_height():
3456 self.coords[3] = self.currimg_pixbuf_original.get_height() - self.coords[1]
3457 x.handler_block(self.x_changed)
3458 y.handler_block(self.y_changed)
3459 width.handler_block(self.width_changed)
3460 height.handler_block(self.height_changed)
3461 x.set_value(self.coords[0])
3462 y.set_value(self.coords[1])
3463 width.set_value(self.coords[2])
3464 height.set_value(self.coords[3])
3465 x.handler_unblock(self.x_changed)
3466 y.handler_unblock(self.y_changed)
3467 width_adj.set_property('upper', self.currimg_pixbuf_original.get_width() - self.coords[0])
3468 height_adj.set_property('upper', self.currimg_pixbuf_original.get_height() - self.coords[1])
3469 width.handler_unblock(self.width_changed)
3470 height.handler_unblock(self.height_changed)
3471
3472 def crop_image_button_press(self, widget, event, image):
3473 x, y, state = event.window.get_pointer()
3474 if (state & gtk.gdk.BUTTON1_MASK):
3475 self.drawing_crop_rectangle = True
3476 self.crop_rectangle = [x, y]
3477 gc = image.window.new_gc(function=gtk.gdk.INVERT)
3478 if self.rect != None:
3479 # Get rid of the previous drawn rectangle:
3480 image.window.draw_rectangle(gc, False, self.rect[0], self.rect[1], self.rect[2], self.rect[3])
3481 self.rect = None
3482
3483 def crop_image_button_release(self, widget, event):
3484 x, y, state = event.window.get_pointer()
3485 if not (state & gtk.gdk.BUTTON1_MASK):
3486 self.drawing_crop_rectangle = False
3487
3488 def saturation(self, action):
3489 dialog = gtk.Dialog(_("Saturation"), self.window, gtk.DIALOG_MODAL, (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT))
3490 resizebutton = dialog.add_button(_("_Saturate"), gtk.RESPONSE_ACCEPT)
3491 resizeimage = gtk.Image()
3492 resizeimage.set_from_stock(gtk.STOCK_OK, gtk.ICON_SIZE_BUTTON)
3493 resizebutton.set_image(resizeimage)
3494 scale = gtk.HScale()
3495 scale.set_draw_value(False)
3496 scale.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
3497 scale.set_range(0, 2)
3498 scale.set_increments(0.1, 0.5)
3499 scale.set_value(1)
3500 scale.connect('value-changed', self.saturation_preview)
3501 label = gtk.Label(_("Saturation level:"))
3502 label.set_alignment(0, 0.5)
3503 hbox1 = gtk.HBox()
3504 hbox1.pack_start(label, True, True, 10)
3505 hbox2 = gtk.HBox()
3506 hbox2.pack_start(scale, True, True, 20)
3507 dialog.vbox.pack_start(gtk.Label(" "))
3508 dialog.vbox.pack_start(hbox1, False)
3509 dialog.vbox.pack_start(hbox2, True, True, 10)
3510 dialog.vbox.pack_start(gtk.Label(" "))
3511 dialog.set_default_response(gtk.RESPONSE_ACCEPT)
3512 dialog.vbox.show_all()
3513 response = dialog.run()
3514 if response == gtk.RESPONSE_ACCEPT:
3515 self.currimg_pixbuf_original.saturate_and_pixelate(self.currimg_pixbuf_original, scale.get_value(), False)
3516 self.currimg_pixbuf.saturate_and_pixelate(self.currimg_pixbuf, scale.get_value(), False)
3517 self.imageview.set_from_pixbuf(self.currimg_pixbuf)
3518 self.image_modified = True
3519 dialog.destroy()
3520 else:
3521 self.imageview.set_from_pixbuf(self.currimg_pixbuf)
3522 dialog.destroy()
3523
3524 def saturation_preview(self, range):
3525 while gtk.events_pending():
3526 gtk.main_iteration()
3527 try:
3528 bak = self.currimg_pixbuf.copy()
3529 self.currimg_pixbuf.saturate_and_pixelate(self.currimg_pixbuf, range.get_value(), False)
3530 self.imageview.set_from_pixbuf(self.currimg_pixbuf)
3531 self.currimg_pixbuf = bak.copy()
3532 del bak
3533 except:
3534 pass
3535 gc.collect()
3536
3537 def resize_image(self, action):
3538 dialog = gtk.Dialog(_("Resize Image"), self.window, gtk.DIALOG_MODAL, (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT))
3539 resizebutton = dialog.add_button(_("_Resize"), gtk.RESPONSE_ACCEPT)
3540 resizeimage = gtk.Image()
3541 resizeimage.set_from_stock(gtk.STOCK_OK, gtk.ICON_SIZE_BUTTON)
3542 resizebutton.set_image(resizeimage)
3543 hbox_width = gtk.HBox()
3544 width_adj = gtk.Adjustment(self.currimg_pixbuf_original.get_width(), 1, 100000000000, 1, 10, 0)
3545 width = gtk.SpinButton(width_adj, 0, 0)
3546 width.set_numeric(True)
3547 width.set_update_policy(gtk.UPDATE_IF_VALID)
3548 width.set_wrap(False)
3549 width_label = gtk.Label(_("Width:"))
3550 width_label.set_alignment(0, 0.7)
3551 hbox_width.pack_start(width_label, False, False, 10)
3552 hbox_width.pack_start(width, False, False, 0)
3553 hbox_width.pack_start(gtk.Label(_("pixels")), False, False, 10)
3554 hbox_height = gtk.HBox()
3555 height_adj = gtk.Adjustment(self.currimg_pixbuf_original.get_height(), 1, 100000000000, 1, 10, 0)
3556 height = gtk.SpinButton(height_adj, 0, 0)
3557 height.set_numeric(True)
3558 height.set_update_policy(gtk.UPDATE_IF_VALID)
3559 height.set_wrap(False)
3560 height_label = gtk.Label(_("Height:"))
3561 width_label.set_size_request(height_label.size_request()[0], -1)
3562 height_label.set_alignment(0, 0.7)
3563 hbox_height.pack_start(height_label, False, False, 10)
3564 hbox_height.pack_start(height, False, False, 0)
3565 hbox_height.pack_start(gtk.Label(_("pixels")), False, False, 10)
3566 hbox_aspect = gtk.HBox()
3567 aspect_checkbox = gtk.CheckButton(_("Preserve aspect ratio"))
3568 aspect_checkbox.set_active(self.preserve_aspect)
3569 hbox_aspect.pack_start(aspect_checkbox, False, False, 10)
3570 vbox = gtk.VBox()
3571 vbox.pack_start(gtk.Label(), False, False, 0)
3572 vbox.pack_start(hbox_width, False, False, 0)
3573 vbox.pack_start(hbox_height, False, False, 0)
3574 vbox.pack_start(gtk.Label(), False, False, 0)
3575 vbox.pack_start(hbox_aspect, False, False, 0)
3576 vbox.pack_start(gtk.Label(), False, False, 0)
3577 hbox_total = gtk.HBox()
3578 animtest = gtk.gdk.PixbufAnimation(self.currimg_name)
3579 if animtest.is_static_image():
3580 pixbuf, image_width, image_height = self.get_pixbuf_of_size(self.currimg_pixbuf_original, 96, self.zoom_quality)
3581 else:
3582 pixbuf, image_width, image_height = self.get_pixbuf_of_size(animtest.get_static_image(), 96, self.zoom_quality)
3583 image = gtk.Image()
3584 image.set_from_pixbuf(self.pixbuf_add_border(pixbuf))
3585 hbox_total.pack_start(image, False, False, 10)
3586 hbox_total.pack_start(vbox, False, False, 10)
3587 dialog.vbox.pack_start(hbox_total, False, False, 0)
3588 width.connect('value-changed', self.preserve_image_aspect, "width", height)
3589 height.connect('value-changed', self.preserve_image_aspect, "height", width)
3590 aspect_checkbox.connect('toggled', self.aspect_ratio_toggled, width, height)
3591 dialog.set_default_response(gtk.RESPONSE_ACCEPT)
3592 dialog.vbox.show_all()
3593 response = dialog.run()
3594 if response == gtk.RESPONSE_ACCEPT:
3595 pixelheight = height.get_value_as_int()
3596 pixelwidth = width.get_value_as_int()
3597 dialog.destroy()
3598 self.currimg_pixbuf_original = self.currimg_pixbuf_original.scale_simple(pixelwidth, pixelheight, self.zoom_quality)
3599 self.load_new_image2(False, True, False, False)
3600 self.image_modified = True
3601 else:
3602 dialog.destroy()
3603
3604 def aspect_ratio_toggled(self, togglebutton, width, height):
3605 self.preserve_aspect = togglebutton.get_active()
3606 if self.preserve_aspect:
3607 # Set height based on width and aspect ratio
3608 target_value = float(width.get_value_as_int())/self.currimg_pixbuf_original.get_width()
3609 target_value = int(target_value * self.currimg_pixbuf_original.get_height())
3610 self.ignore_preserve_aspect_callback = True
3611 height.set_value(target_value)
3612 self.ignore_preserve_aspect_callback = False
3613
3614 def preserve_image_aspect(self, currspinbox, type, otherspinbox):
3615 if not self.preserve_aspect:
3616 return
3617 if self.ignore_preserve_aspect_callback:
3618 return
3619 if type == "width":
3620 target_value = float(currspinbox.get_value_as_int())/self.currimg_pixbuf_original.get_width()
3621 target_value = int(target_value * self.currimg_pixbuf_original.get_height())
3622 else:
3623 target_value = float(currspinbox.get_value_as_int())/self.currimg_pixbuf_original.get_height()
3624 target_value = int(target_value * self.currimg_pixbuf_original.get_width())
3625 self.ignore_preserve_aspect_callback = True
3626 otherspinbox.set_value(target_value)
3627 self.ignore_preserve_aspect_callback = False
3628
3629 def goto_prev_image(self, action):
3630 self.goto_image("PREV", action)
3631
3632 def goto_next_image(self, action):
3633 self.goto_image("NEXT", action)
3634
3635 def goto_random_image(self, action):
3636 self.goto_image("RANDOM", action)
3637
3638 def goto_first_image(self, action):
3639 self.goto_image("FIRST", action)
3640
3641 def goto_last_image(self, action):
3642 self.goto_image("LAST", action)
3643
3644 def goto_image(self, location, action):
3645 # location can be "LAST", "FIRST", "NEXT", "PREV", "RANDOM", or a number
3646 if self.slideshow_mode and action != "ss":
3647 gobject.source_remove(self.timer_delay)
3648 if ((location=="PREV" or location=="NEXT" or location=="RANDOM") and len(self.image_list) > 1) or (location=="FIRST" and (len(self.image_list) > 1 and self.curr_img_in_list != 0)) or (location=="LAST" and (len(self.image_list) > 1 and self.curr_img_in_list != len(self.image_list)-1)) or valid_int(location):
3649 self.load_new_image_stop_now()
3650 cancel = self.autosave_image()
3651 if cancel:
3652 return
3653 check_wrap = False
3654 if location != "RANDOM":
3655 self.randomlist = []
3656 if location == "FIRST":
3657 self.curr_img_in_list = 0
3658 elif location == "RANDOM":
3659 if self.randomlist == []:
3660 self.reinitialize_randomlist()
3661 else:
3662 # check if we have seen every image; if so, reinitialize array and repeat:
3663 all_items_are_true = True
3664 for item in self.randomlist:
3665 if not item:
3666 all_items_are_true = False
3667 if all_items_are_true:
3668 if not self.slideshow_mode or (self.slideshow_mode and self.listwrap_mode == 1):
3669 self.reinitialize_randomlist()
3670 else:
3671 check_wrap = True
3672 elif location == "LAST":
3673 self.curr_img_in_list = len(self.image_list)-1
3674 elif location == "PREV":
3675 if self.curr_img_in_list > 0:
3676 self.curr_img_in_list -= 1
3677 else:
3678 check_wrap = True
3679 elif location == "NEXT":
3680 if self.curr_img_in_list < len(self.image_list) - 1:
3681 self.curr_img_in_list += 1
3682 else:
3683 check_wrap = True
3684 if check_wrap:
3685 if self.listwrap_mode == 0:
3686 if location == "NEXT":
3687 if self.slideshow_mode:
3688 self.toggle_slideshow(None)
3689 return
3690 elif (location == "PREV" or location == "NEXT") and self.listwrap_mode == 1:
3691 if location == "PREV":
3692 self.curr_img_in_list = len(self.image_list) - 1
3693 elif location == "NEXT":
3694 self.curr_img_in_list = 0
3695 else:
3696 if self.curr_img_in_list != self.loaded_img_in_list:
3697 # Ensure that the user is looking at the correct "last" image before
3698 # they are asked the wrap question:
3699 if location == "PREV":
3700 self.load_new_image(True, False, True, True, True, True)
3701 else:
3702 self.load_new_image(False, False, True, True, True, True)
3703 self.set_go_navigation_sensitivities(False)
3704 self.thumbpane_select(self.curr_img_in_list)
3705 if self.fullscreen_mode:
3706 self.change_cursor(None)
3707 if location == "PREV":
3708 dialog = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, _("You are viewing the first image in the list. Wrap around to the last image?"))
3709 elif location == "NEXT":
3710 dialog = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, _("You are viewing the last image in the list. Wrap around to the first image?"))
3711 elif location == "RANDOM":
3712 dialog = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, _("All images have been viewed. Would you like to cycle through the images again?"))
3713 dialog.set_title(_("Wrap?"))
3714 dialog.label.set_property('can-focus', False)
3715 dialog.set_default_response(gtk.RESPONSE_YES)
3716 self.user_prompt_visible = True
3717 response = dialog.run()
3718 if response == gtk.RESPONSE_YES:
3719 if location == "PREV":
3720 self.curr_img_in_list = len(self.image_list)-1
3721 elif location == "NEXT":
3722 self.curr_img_in_list = 0
3723 elif location == "RANDOM":
3724 self.reinitialize_randomlist()
3725 dialog.destroy()
3726 self.user_prompt_visible = False
3727 if self.fullscreen_mode:
3728 self.hide_cursor
3729 else:
3730 dialog.destroy()
3731 self.user_prompt_visible = False
3732 if self.fullscreen_mode:
3733 self.hide_cursor
3734 else:
3735 self.change_cursor(None)
3736 if self.slideshow_mode:
3737 self.toggle_slideshow(None)
3738 return
3739 if location == "RANDOM":
3740 # Find random image that hasn't already been chosen:
3741 j = random.randint(0, len(self.image_list)-1)
3742 while self.randomlist[j]:
3743 j = random.randint(0, len(self.image_list)-1)
3744 self.curr_img_in_list = j
3745 self.randomlist[j] = True
3746 self.currimg_name = str(self.image_list[self.curr_img_in_list])
3747 if valid_int(location):
3748 prev_img = self.curr_img_in_list
3749 self.curr_img_in_list = int(location)
3750 if not self.fullscreen_mode and (not self.slideshow_mode or (self.slideshow_mode and action != "ss")):
3751 self.change_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
3752 if location == "PREV" or (valid_int(location) and int(location) == prev_img-1):
3753 self.load_when_idle = gobject.idle_add(self.load_new_image, True, False, True, True, True, True)
3754 else:
3755 self.load_when_idle = gobject.idle_add(self.load_new_image, False, False, True, True, True, True)
3756 self.set_go_navigation_sensitivities(False)
3757 if self.slideshow_mode:
3758 if self.curr_slideshow_random:
3759 self.timer_delay = gobject.timeout_add(int(self.curr_slideshow_delay*1000), self.goto_random_image, "ss")
3760 else:
3761 self.timer_delay = gobject.timeout_add(int(self.curr_slideshow_delay*1000), self.goto_next_image, "ss")
3762 gobject.idle_add(self.thumbpane_select, self.curr_img_in_list)
3763
3764 def set_go_navigation_sensitivities(self, skip_initial_check):
3765 # setting skip_image_list_check to True is useful when calling from
3766 # expand_filelist_and_load_image() for example, as self.image_list has not
3767 # yet fully populated
3768 if (not self.image_loaded or len(self.image_list) == 1) and not skip_initial_check:
3769 self.set_previous_image_sensitivities(False)
3770 self.set_first_image_sensitivities(False)
3771 self.set_next_image_sensitivities(False)
3772 self.set_last_image_sensitivities(False)
3773 self.set_random_image_sensitivities(False)
3774 elif self.curr_img_in_list == 0:
3775 if self.listwrap_mode == 0:
3776 self.set_previous_image_sensitivities(False)
3777 else:
3778 self.set_previous_image_sensitivities(True)
3779 self.set_first_image_sensitivities(False)
3780 self.set_next_image_sensitivities(True)
3781 self.set_last_image_sensitivities(True)
3782 self.set_random_image_sensitivities(True)
3783 elif self.curr_img_in_list == len(self.image_list)-1:
3784 self.set_previous_image_sensitivities(True)
3785 self.set_first_image_sensitivities(True)
3786 if self.listwrap_mode == 0:
3787 self.set_next_image_sensitivities(False)
3788 else:
3789 self.set_next_image_sensitivities(True)
3790 self.set_last_image_sensitivities(False)
3791 self.set_random_image_sensitivities(True)
3792 else:
3793 self.set_previous_image_sensitivities(True)
3794 self.set_first_image_sensitivities(True)
3795 self.set_next_image_sensitivities(True)
3796 self.set_last_image_sensitivities(True)
3797 self.set_random_image_sensitivities(True)
3798
3799 def reinitialize_randomlist(self):
3800 self.randomlist = []
3801 for i in range(len(self.image_list)):
3802 self.randomlist.append(False)
3803 self.randomlist[self.curr_img_in_list] = True
3804
3805 def image_load_failed(self, reset_cursor, filename=""):
3806 # If a filename is provided, use it for display:
3807 if len(filename) == 0:
3808 self.currimg_name = str(self.image_list[self.curr_img_in_list])
3809 else:
3810 self.currmg_name = filename
3811 if self.verbose and self.currimg_name != "":
3812 print(_("Loading: %s") % self.currimg_name)
3813 self.update_title()
3814 self.put_error_image_to_window()
3815 self.image_loaded = False
3816 self.currimg_pixbuf_original = None
3817 if reset_cursor:
3818 if not self.fullscreen_mode:
3819 self.change_cursor(None)
3820
3821 def load_new_image_stop_now(self):
3822 try:
3823 gobject.source_remove(self.load_when_idle)
3824 except:
3825 pass
3826 try:
3827 gobject.source_remove(self.preload_when_idle)
3828 except:
3829 pass
3830 try:
3831 gobject.source_remove(self.preload_when_idle2)
3832 except:
3833 pass
3834
3835 def load_new_image(self, check_prev_last, use_current_pixbuf_original, reset_cursor, perform_onload_action, preload_next_image_after, preload_prev_image_after):
3836 try:
3837 self.load_new_image2(check_prev_last, use_current_pixbuf_original, reset_cursor, perform_onload_action)
3838 except:
3839 self.image_load_failed(True)
3840 if preload_next_image_after:
3841 self.preload_when_idle = gobject.idle_add(self.preload_next_image, False)
3842 if preload_prev_image_after:
3843 self.preload_when_idle2 = gobject.idle_add(self.preload_prev_image, False)
3844
3845 def check_preloadimg_prev_for_existing(self, prev_index, reset_preloadimg_prev_in_list):
3846 # Determines if preloadimg_prev needs to be updated; if so,
3847 # checks if the image is already stored in self.currimg
3848 # or self.preloadimg_next and can be reused.
3849 reset_preloadimg_prev_in_list = False
3850 if prev_index != self.preloadimg_prev_in_list and prev_index != -1:
3851 # Need to update preloadimg_prev:
3852 if prev_index == self.loaded_img_in_list and not self.image_modified and not self.image_zoomed:
3853 self.preloadimg_prev_in_list = self.loaded_img_in_list
3854 self.preloadimg_prev_name = self.currimg_name
3855 self.preloadimg_prev_width = self.currimg_width
3856 self.preloadimg_prev_height = self.currimg_height
3857 self.preloadimg_prev_pixbuf = self.currimg_pixbuf
3858 self.preloadimg_prev_pixbuf_original = self.currimg_pixbuf_original
3859 self.preloadimg_prev_zoomratio = self.currimg_zoomratio
3860 self.preloadimg_prev_is_animation = self.currimg_is_animation
3861 elif prev_index == self.preloadimg_next_in_list:
3862 self.preloadimg_prev_in_list = self.preloadimg_next_in_list
3863 self.preloadimg_prev_name = self.preloadimg_next_name
3864 self.preloadimg_prev_width = self.preloadimg_next_width
3865 self.preloadimg_prev_height = self.preloadimg_next_height
3866 self.preloadimg_prev_pixbuf = self.preloadimg_next_pixbuf
3867 self.preloadimg_prev_pixbuf_original = self.preloadimg_next_pixbuf_original
3868 self.preloadimg_prev_zoomratio = self.preloadimg_next_zoomratio
3869 self.preloadimg_prev_is_animation = self.preloadimg_next_is_animation
3870 else:
3871 reset_preloadimg_prev_in_list = True
3872 elif prev_index == -1:
3873 reset_preloadimg_prev_in_list = True
3874
3875 def check_preloadimg_next_for_existing(self, next_index, reset_preloadimg_next_in_list):
3876 # Determines if preloadimg_next needs to be updated; if so,
3877 # checks if the image is already stored in self.currimg
3878 # or self.preloadimg_prev and can be reused.
3879 reset_preloadimg_next_in_list = False
3880 if next_index != self.preloadimg_next_in_list and next_index != -1:
3881 # Need to update preloadimg_next:
3882 if next_index == self.loaded_img_in_list and not self.image_modified and not self.image_zoomed:
3883 self.preloadimg_next_in_list = self.loaded_img_in_list
3884 self.preloadimg_next_name = self.currimg_name
3885 self.preloadimg_next_width = self.currimg_width
3886 self.preloadimg_next_height = self.currimg_height
3887 self.preloadimg_next_pixbuf = self.currimg_pixbuf
3888 self.preloadimg_next_pixbuf_original = self.currimg_pixbuf_original
3889 self.preloadimg_next_zoomratio = self.currimg_zoomratio
3890 self.preloadimg_next_is_animation = self.currimg_is_animation
3891 elif next_index == self.preloadimg_prev_in_list:
3892 self.preloadimg_next_in_list = self.preloadimg_prev_in_list
3893 self.preloadimg_next_name = self.preloadimg_prev_name
3894 self.preloadimg_next_width = self.preloadimg_prev_width
3895 self.preloadimg_next_height = self.preloadimg_prev_height
3896 self.preloadimg_next_pixbuf = self.preloadimg_prev_pixbuf
3897 self.preloadimg_next_pixbuf_original = self.preloadimg_prev_pixbuf_original
3898 self.preloadimg_next_zoomratio = self.preloadimg_prev_zoomratio
3899 self.preloadimg_next_is_animation = self.preloadimg_prev_is_animation
3900 else:
3901 reset_preloadimg_next_in_list = True
3902 elif next_index == -1:
3903 reset_preloadimg_next_in_list = True
3904
3905 def check_currimg_for_existing(self):
3906 # Determines if currimg needs to be updated; if so,
3907 # checks if the image is already stored in self.preloadimg_next
3908 # or self.preloadimg_prev and can be reused (the whole point of
3909 # preloading!)
3910 used_prev = False
3911 used_next = False
3912 if self.curr_img_in_list != self.loaded_img_in_list:
3913 # Need to update currimg:
3914 if self.curr_img_in_list == self.preloadimg_prev_in_list:
3915 # Set preload_prev_image as current image
3916 self.currimg_name = self.preloadimg_prev_name
3917 self.currimg_width = self.preloadimg_prev_width
3918 self.currimg_height = self.preloadimg_prev_height
3919 self.currimg_pixbuf = self.preloadimg_prev_pixbuf
3920 self.currimg_pixbuf_original = self.preloadimg_prev_pixbuf_original
3921 self.currimg_zoomratio = self.preloadimg_prev_zoomratio
3922 self.currimg_is_animation = self.preloadimg_prev_is_animation
3923 used_prev = True
3924 if self.verbose and self.currimg_name != "":
3925 print(_("Loading: %s") % self.currimg_name)
3926 self.put_zoom_image_to_window(True)
3927 if not self.currimg_is_animation:
3928 self.set_image_sensitivities(True)
3929 else:
3930 self.set_image_sensitivities(False)
3931 elif self.curr_img_in_list == self.preloadimg_next_in_list:
3932 # Use preload_next_image as current image
3933 self.currimg_name = self.preloadimg_next_name
3934 self.currimg_width = self.preloadimg_next_width
3935 self.currimg_height = self.preloadimg_next_height
3936 self.currimg_pixbuf = self.preloadimg_next_pixbuf
3937 self.currimg_pixbuf_original = self.preloadimg_next_pixbuf_original
3938 self.currimg_zoomratio = self.preloadimg_next_zoomratio
3939 self.currimg_is_animation = self.preloadimg_next_is_animation
3940 used_next = True
3941 if self.verbose and self.currimg_name != "":
3942 print(_("Loading: %s") % self.currimg_name)
3943 self.put_zoom_image_to_window(True)
3944 if not self.currimg_is_animation:
3945 self.set_image_sensitivities(True)
3946 else:
3947 self.set_image_sensitivities(False)
3948 return used_prev, used_next
3949
3950 def load_new_image2(self, check_prev_last, use_current_pixbuf_original, reset_cursor, perform_onload_action, skip_recentfiles=False):
3951 # check_prev_last is used to determine if we should check whether
3952 # preloadimg_prev can be reused last. This should really only
3953 # be done if the user just clicked the previous image button in
3954 # order to reduce the number of image loads.
3955 # If use_current_pixbuf_original == True, do not reload the
3956 # self.currimg_pixbuf_original from the file; instead, use the existing
3957 # one. This is only currently useful for resizing images.
3958 # Determine the indices in the self.image_list array for the
3959 # previous and next preload images.
3960 next_index = self.curr_img_in_list + 1
3961 if next_index > len(self.image_list)-1:
3962 if self.listwrap_mode == 0:
3963 next_index = -1
3964 else:
3965 next_index = 0
3966 prev_index = self.curr_img_in_list - 1
3967 if prev_index < 0:
3968 if self.listwrap_mode == 0:
3969 prev_index = -1
3970 else:
3971 prev_index = len(self.image_list)-1
3972 if self.preloading_images:
3973 reset_preloadimg_next_in_list = False
3974 reset_preloadimg_prev_in_list = False
3975 if check_prev_last:
3976 self.check_preloadimg_next_for_existing(next_index, reset_preloadimg_next_in_list)
3977 else:
3978 self.check_preloadimg_prev_for_existing(prev_index, reset_preloadimg_prev_in_list)
3979 used_prev, used_next = self.check_currimg_for_existing()
3980 if self.preloading_images:
3981 if check_prev_last:
3982 self.check_preloadimg_prev_for_existing(prev_index, reset_preloadimg_prev_in_list)
3983 else:
3984 self.check_preloadimg_next_for_existing(next_index, reset_preloadimg_next_in_list)
3985 if reset_preloadimg_prev_in_list:
3986 self.preloadimg_prev_in_list = -1
3987 if reset_preloadimg_next_in_list:
3988 self.preloadimg_next_in_list = -1
3989 if used_prev or used_next:
3990 # If we used a preload image, set the correct boolean variables
3991 if self.open_mode == self.open_mode_smart or (self.open_mode == self.open_mode_last and self.last_mode == self.open_mode_smart):
3992 self.last_image_action_was_fit = True
3993 self.last_image_action_was_smart_fit = True
3994 elif self.open_mode == self.open_mode_fit or (self.open_mode == self.open_mode_last and self.last_mode == self.open_mode_fit):
3995 self.last_image_action_was_fit = True
3996 self.last_image_action_was_smart_fit = False
3997 elif self.open_mode == self.open_mode_1to1 or (self.open_mode == self.open_mode_last and self.last_mode == self.open_mode_1to1):
3998 self.last_image_action_was_fit = False
3999 else:
4000 # Need to load the current image
4001 self.currimg_pixbuf = None
4002 self.currimg_zoomratio = 1
4003 self.currimg_name = str(self.image_list[self.curr_img_in_list])
4004 if self.verbose and self.currimg_name != "":
4005 print(_("Loading: %s") % self.currimg_name)
4006 animtest = gtk.gdk.PixbufAnimation(self.currimg_name)
4007 if animtest.is_static_image() or (use_current_pixbuf_original and not self.currimg_is_animation):
4008 self.currimg_is_animation = False
4009 if not use_current_pixbuf_original:
4010 self.currimg_pixbuf_original = animtest.get_static_image()
4011 self.set_image_sensitivities(True)
4012 if self.open_mode == self.open_mode_smart or (self.open_mode == self.open_mode_last and self.last_mode == self.open_mode_smart):
4013 self.zoom_to_fit_or_1_to_1(None, False, False)
4014 elif self.open_mode == self.open_mode_fit or (self.open_mode == self.open_mode_last and self.last_mode == self.open_mode_fit):
4015 self.zoom_to_fit_window(None, False, False)
4016 elif self.open_mode == self.open_mode_1to1 or (self.open_mode == self.open_mode_last and self.last_mode == self.open_mode_1to1):
4017 self.zoom_1_to_1(None, False, False)
4018 else:
4019 self.currimg_is_animation = True
4020 if not use_current_pixbuf_original:
4021 self.currimg_pixbuf_original = animtest
4022 self.zoom_1_to_1(None, False, False)
4023 self.set_image_sensitivities(False)
4024 if self.onload_cmd != None and perform_onload_action:
4025 self.parse_action_command(self.onload_cmd, False)
4026 self.update_statusbar()
4027 self.update_title()
4028 self.image_loaded = True
4029 self.image_modified = False
4030 self.image_zoomed = False
4031 self.set_slideshow_sensitivities()
4032 if not skip_recentfiles:
4033 self.register_file_with_recent_docs(self.currimg_name)
4034 if reset_cursor:
4035 if not self.fullscreen_mode:
4036 self.change_cursor(None)
4037
4038 def preload_next_image(self, use_existing_image):
4039 try:
4040 if self.preloading_images and len(self.image_list) > 1:
4041 if not use_existing_image:
4042 next_index = self.curr_img_in_list + 1
4043 if next_index > len(self.image_list)-1:
4044 if self.listwrap_mode == 0:
4045 self.preloadimg_next_in_list == -1
4046 return
4047 else:
4048 next_index = 0
4049 if next_index == self.preloadimg_next_in_list:
4050 return
4051 self.preloadimg_next_in_list = next_index
4052 self.preloadimg_next_name = str(self.image_list[next_index])
4053 pre_animtest = gtk.gdk.PixbufAnimation(self.preloadimg_next_name)
4054 if pre_animtest.is_static_image():
4055 self.preloadimg_next_is_animation = False
4056 self.preloadimg_next_pixbuf_original = pre_animtest.get_static_image()
4057 else:
4058 self.preloadimg_next_is_animation = True
4059 self.preloadimg_next_pixbuf_original = pre_animtest
4060 if self.preloadimg_next_in_list == -1:
4061 return
4062 # Determine self.preloadimg_next_zoomratio
4063 if self.open_mode == self.open_mode_smart or (self.open_mode == self.open_mode_last and self.last_mode == self.open_mode_smart):
4064 self.zoom_to_fit_or_1_to_1(None, True, False)
4065 elif self.open_mode == self.open_mode_fit or (self.open_mode == self.open_mode_last and self.last_mode == self.open_mode_fit):
4066 self.zoom_to_fit_window(None, True, False)
4067 elif self.open_mode == self.open_mode_1to1 or (self.open_mode == self.open_mode_last and self.last_mode == self.open_mode_1to1):
4068 self.zoom_1_to_1(None, True, False)
4069 # Always start with the original image to preserve quality!
4070 # Calculate image size:
4071 self.preloadimg_next_width = int(self.preloadimg_next_pixbuf_original.get_width() * self.preloadimg_next_zoomratio)
4072 self.preloadimg_next_height = int(self.preloadimg_next_pixbuf_original.get_height() * self.preloadimg_next_zoomratio)
4073 if not self.preloadimg_next_is_animation:
4074 # Scale image:
4075 if not self.preloadimg_next_pixbuf_original.get_has_alpha():
4076 self.preloadimg_next_pixbuf = self.preloadimg_next_pixbuf_original.scale_simple(self.preloadimg_next_width, self.preloadimg_next_height, self.zoom_quality)
4077 else:
4078 colormap = self.imageview.get_colormap()
4079 light_grey = colormap.alloc_color('#666666', True, True)
4080 dark_grey = colormap.alloc_color('#999999', True, True)
4081 self.preloadimg_next_pixbuf = self.preloadimg_next_pixbuf_original.composite_color_simple(self.preloadimg_next_width, self.preloadimg_next_height, self.zoom_quality, 255, 8, light_grey.pixel, dark_grey.pixel)
4082 else:
4083 self.preloadimg_next_pixbuf = self.preloadimg_next_pixbuf_original
4084 gc.collect()
4085 if self.verbose:
4086 print(_("Preloading: %s") % self.preloadimg_next_name)
4087 except:
4088 self.preloadimg_next_in_list = -1
4089
4090 def preload_prev_image(self, use_existing_image):
4091 try:
4092 if self.preloading_images and len(self.image_list) > 1:
4093 if not use_existing_image:
4094 prev_index = self.curr_img_in_list - 1
4095 if prev_index < 0:
4096 if self.listwrap_mode == 0:
4097 self.preloadimg_prev_in_list == -1
4098 return
4099 else:
4100 prev_index = len(self.image_list)-1
4101 if prev_index == self.preloadimg_prev_in_list:
4102 return
4103 self.preloadimg_prev_in_list = prev_index
4104 self.preloadimg_prev_name = str(self.image_list[prev_index])
4105 pre_animtest = gtk.gdk.PixbufAnimation(self.preloadimg_prev_name)
4106 if pre_animtest.is_static_image():
4107 self.preloadimg_prev_is_animation = False
4108 self.preloadimg_prev_pixbuf_original = pre_animtest.get_static_image()
4109 else:
4110 self.preloadimg_prev_is_animation = True
4111 self.preloadimg_prev_pixbuf_original = pre_animtest
4112 if self.preloadimg_prev_in_list == -1:
4113 return
4114 # Determine self.preloadimg_prev_zoomratio
4115 if self.open_mode == self.open_mode_smart or (self.open_mode == self.open_mode_last and self.last_mode == self.open_mode_smart):
4116 self.zoom_to_fit_or_1_to_1(None, False, True)
4117 elif self.open_mode == self.open_mode_fit or (self.open_mode == self.open_mode_last and self.last_mode == self.open_mode_fit):
4118 self.zoom_to_fit_window(None, False, True)
4119 elif self.open_mode == self.open_mode_1to1 or (self.open_mode == self.open_mode_last and self.last_mode == self.open_mode_1to1):
4120 self.zoom_1_to_1(None, False, True)
4121 # Always start with the original image to preserve quality!
4122 # Calculate image size:
4123 self.preloadimg_prev_width = int(self.preloadimg_prev_pixbuf_original.get_width() * self.preloadimg_prev_zoomratio)
4124 self.preloadimg_prev_height = int(self.preloadimg_prev_pixbuf_original.get_height() * self.preloadimg_prev_zoomratio)
4125 if not self.preloadimg_prev_is_animation:
4126 # Scale image:
4127 if not self.preloadimg_prev_pixbuf_original.get_has_alpha():
4128 self.preloadimg_prev_pixbuf = self.preloadimg_prev_pixbuf_original.scale_simple(self.preloadimg_prev_width, self.preloadimg_prev_height, self.zoom_quality)
4129 else:
4130 colormap = self.imageview.get_colormap()
4131 light_grey = colormap.alloc_color('#666666', True, True)
4132 dark_grey = colormap.alloc_color('#999999', True, True)
4133 self.preloadimg_prev_pixbuf = self.preloadimg_prev_pixbuf_original.composite_color_simple(self.preloadimg_prev_width, self.preloadimg_prev_height, self.zoom_quality, 255, 8, light_grey.pixel, dark_grey.pixel)
4134 else:
4135 self.preloadimg_prev_pixbuf = self.preloadimg_prev_pixbuf_original
4136 gc.collect()
4137 if self.verbose:
4138 print(_("Preloading: %s") % self.preloadimg_prev_name)
4139 except:
4140 self.preloadimg_prev_in_list = -1
4141
4142 def change_cursor(self, type):
4143 for i in gtk.gdk.window_get_toplevels():
4144 if i.get_window_type() != gtk.gdk.WINDOW_TEMP and i.get_window_type() != gtk.gdk.WINDOW_CHILD:
4145 i.set_cursor(type)
4146 self.layout.window.set_cursor(type)
4147
4148 def expand_filelist_and_load_image(self, inputlist):
4149 # Takes the current list (i.e. ["pic.jpg", "pic2.gif", "../images"]) and
4150 # expands it into a list of all pictures found
4151 self.thumblist.clear()
4152 first_image_loaded_successfully = False
4153 self.images_found = 0
4154 self.stop_now = True # Make sure that any previous search process is stopped
4155 self.change_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
4156 # Reset preload images:
4157 self.preloadimg_next_in_list = -1
4158 self.preloadimg_prev_in_list = -1
4159 # If any directories were passed, display "Searching..." in statusbar:
4160 self.searching_for_images = False
4161 for item in inputlist:
4162 if os.path.isdir(item):
4163 self.searching_for_images = True
4164 self.update_statusbar()
4165 if not self.closing_app:
4166 while gtk.events_pending():
4167 gtk.main_iteration()
4168 first_image = ""
4169 first_image_found = False
4170 first_image_loaded = False
4171 second_image = ""
4172 second_image_found = False
4173 second_image_preloaded = False
4174 self.randomlist = []
4175 folderlist = []
4176 self.image_list = []
4177 self.curr_img_in_list = 0
4178 go_buttons_enabled = False
4179 self.set_go_sensitivities(False)
4180 # Clean up list (remove preceding "file://" or "file:" and trailing "/")
4181 for itemnum in range(len(inputlist)):
4182 # Strip off preceding file..
4183 if inputlist[itemnum].startswith('file://'):
4184 inputlist[itemnum] = inputlist[itemnum][7:]
4185 elif inputlist[itemnum].startswith('file:'):
4186 inputlist[itemnum] = inputlist[itemnum][5:]
4187 # Strip off trailing "/" if it exists:
4188 if inputlist[itemnum][len(inputlist[itemnum])-1] == "/":
4189 inputlist[itemnum] = inputlist[itemnum][:(len(inputlist[itemnum])-1)]
4190 if not (inputlist[itemnum].startswith('http://') or inputlist[itemnum].startswith('ftp://')):
4191 inputlist[itemnum] = os.path.abspath(inputlist[itemnum])
4192 else:
4193 try:
4194 # Remote file. Save as /tmp/mirage-<random>/filename.ext
4195 tmpdir = tempfile.mkdtemp(prefix="mirage-") + "/"
4196 tmpfile = tmpdir + os.path.basename(inputlist[itemnum])
4197 socket.setdefaulttimeout(5)
4198 urllib.request.urlretrieve(inputlist[itemnum], tmpfile)
4199 inputlist[itemnum] = tmpfile
4200 except:
4201 pass
4202 # Remove hidden files from list:
4203 if not self.open_hidden_files:
4204 tmplist = []
4205 for item in inputlist:
4206 if os.path.basename(item)[0] != '.':
4207 tmplist.append(item)
4208 elif self.verbose:
4209 print(_("Skipping: %s") % item)
4210 inputlist = tmplist
4211 if len(inputlist) == 0:
4212 # All files/dirs were hidden, exit..
4213 self.currimg_name = ""
4214 self.searching_for_images = False
4215 self.set_go_navigation_sensitivities(False)
4216 self.set_slideshow_sensitivities()
4217 if not self.closing_app:
4218 self.change_cursor(None)
4219 self.recursive = False
4220 self.put_error_image_to_window()
4221 self.update_title()
4222 return
4223 init_image = os.path.abspath(inputlist[0])
4224 self.stop_now = False
4225 # If open all images in dir...
4226 if self.open_all_images:
4227 temp = inputlist
4228 inputlist = []
4229 for item in temp:
4230 if os.path.isfile(item):
4231 itempath = os.path.dirname(os.path.abspath(item))
4232 temp = self.recursive
4233 self.recursive = False
4234 self.stop_now = False
4235 self.expand_directory(itempath, False, go_buttons_enabled, False, False)
4236 self.recursive = temp
4237 else:
4238 inputlist.append(item)
4239 for item in self.image_list:
4240 inputlist.append(item)
4241 if first_image_found and not second_image_found:
4242 second_image_found = True
4243 second_image = item
4244 second_image_came_from_dir = False
4245 if item == init_image:
4246 first_image_found = True
4247 first_image = item
4248 first_image_came_from_dir = False
4249 self.curr_img_in_list = len(inputlist)-1
4250 self.image_list = []
4251 for item in inputlist:
4252 if not self.closing_app:
4253 if os.path.isfile(item):
4254 if self.valid_image(item):
4255 if not second_image_found and first_image_found:
4256 second_image_found = True
4257 second_image = item
4258 second_image_came_from_dir = False
4259 if not first_image_found:
4260 first_image_found = True
4261 first_image = item
4262 first_image_came_from_dir = False
4263 self.image_list.append(item)
4264 if self.verbose:
4265 self.images_found += 1
4266 print(_("Found: %(item)s [%(number)i]") % {'item': item, 'number': self.images_found})
4267 else:
4268 # If it's a directory that was explicitly selected or passed to
4269 # the program, get all the files in the dir.
4270 # Retrieve only images in the top directory specified by the user
4271 # unless explicitly told to recurse (via -R or in Settings>Preferences)
4272 folderlist.append(item)
4273 if not second_image_found:
4274 # See if we can find an image in this directory:
4275 self.stop_now = False
4276 self.expand_directory(item, True, go_buttons_enabled, False, False)
4277 itemnum = 0
4278 while itemnum < len(self.image_list) and not second_image_found:
4279 if os.path.isfile(self.image_list[itemnum]):
4280 if not second_image_found and first_image_found:
4281 second_image_found = True
4282 second_image_came_from_dir = True
4283 second_image = self.image_list[itemnum]
4284 self.set_go_navigation_sensitivities(True)
4285 go_buttons_enabled = True
4286 while gtk.events_pending():
4287 gtk.main_iteration(True)
4288 if not first_image_found:
4289 first_image_found = True
4290 first_image = self.image_list[itemnum]
4291 first_image_came_from_dir = True
4292 itemnum += 1
4293 # Load first image and display:
4294 if first_image_found and not first_image_loaded and self.curr_img_in_list <= len(self.image_list)-1:
4295 first_image_loaded = True
4296 if self.slideshow_mode:
4297 self.toggle_slideshow(None)
4298 if self.verbose and self.currimg_name != "":
4299 print(_("Loading: %s") % self.currimg_name)
4300 try:
4301 self.load_new_image2(False, False, True, True)
4302 # Calling load_new_image2 will reset the following two vars
4303 # to 0, so ensure they are -1 again (no images preloaded)
4304 self.preloadimg_prev_in_list = -1
4305 self.preloadimg_next_in_list = -1
4306 if not self.currimg_is_animation:
4307 self.previmg_width = self.currimg_pixbuf.get_width()
4308 else:
4309 self.previmg_width = self.currimg_pixbuf.get_static_image().get_width()
4310 self.image_loaded = True
4311 first_image_loaded_successfully = True
4312 if not self.closing_app:
4313 while gtk.events_pending():
4314 gtk.main_iteration(True)
4315 except:
4316 pass
4317 if first_image_came_from_dir:
4318 self.image_list = []
4319 # Pre-load second image:
4320 if second_image_found and not second_image_preloaded and ((not second_image_came_from_dir and self.curr_img_in_list+1 <= len(self.image_list)-1) or second_image_came_from_dir):
4321 second_image_preloaded = True
4322 temp = self.image_list
4323 self.image_list = []
4324 while len(self.image_list) < self.curr_img_in_list+1:
4325 self.image_list.append(first_image)
4326 self.image_list.append(second_image)
4327 self.preload_next_image(False)
4328 self.image_list = temp
4329 if first_image_found:
4330 # Sort the filelist and folderlist alphabetically, and recurse into folderlist:
4331 if first_image_came_from_dir:
4332 self.add_folderlist_images(folderlist, go_buttons_enabled)
4333 self.do_image_list_stuff(first_image, second_image)
4334 else:
4335 self.do_image_list_stuff(first_image, second_image)
4336 self.add_folderlist_images(folderlist, go_buttons_enabled)
4337 self.update_title()
4338 if not self.closing_app:
4339 while gtk.events_pending():
4340 gtk.main_iteration(True)
4341 if not first_image_loaded_successfully:
4342 self.image_load_failed(False, init_image)
4343 self.searching_for_images = False
4344 self.update_statusbar()
4345 self.set_go_navigation_sensitivities(False)
4346 self.set_slideshow_sensitivities()
4347 self.thumbpane_update_images(True, self.curr_img_in_list)
4348 if not self.closing_app:
4349 self.change_cursor(None)
4350 self.recursive = False
4351
4352 def add_folderlist_images(self, folderlist, go_buttons_enabled):
4353 if len(folderlist) > 0:
4354 folderlist.sort(locale.strcoll)
4355 folderlist = list(set(folderlist))
4356 for item in folderlist:
4357 if not self.closing_app:
4358 if (not self.open_hidden_files and os.path.basename(item)[0] != '.') or self.open_hidden_files:
4359 self.stop_now = False
4360 self.expand_directory(item, False, go_buttons_enabled, True, True)
4361
4362 def do_image_list_stuff(self, first_image, second_image):
4363 if len(self.image_list) > 0:
4364 self.set_go_navigation_sensitivities(True)
4365 self.image_list = list(set(self.image_list))
4366 self.image_list.sort(locale.strcoll)
4367
4368 def expand_directory(self, item, stop_when_second_image_found, go_buttons_enabled, update_window_title, print_found_msg):
4369 if not self.stop_now and not self.closing_app:
4370 folderlist = []
4371 filelist = []
4372 if not os.access(item, os.R_OK):
4373 return False
4374 for item2 in os.listdir(item):
4375 if not self.closing_app and not self.stop_now:
4376 while gtk.events_pending():
4377 gtk.main_iteration(True)
4378 item2 = item + os.sep + item2
4379 item_fullpath2 = os.path.abspath(item2)
4380 if (not self.open_hidden_files and os.path.basename(item_fullpath2)[0] != '.') or self.open_hidden_files:
4381 if os.path.isfile(item_fullpath2) and self.valid_image(item_fullpath2):
4382 filelist.append(item2)
4383 if self.verbose and print_found_msg:
4384 self.images_found += 1
4385 print(_("Found: %(fullpath)s [%(number)i]") % {'fullpath': item_fullpath2, 'number': self.images_found})
4386 elif os.path.isdir(item_fullpath2) and self.recursive:
4387 folderlist.append(item_fullpath2)
4388 elif self.verbose:
4389 print(_("Skipping: %s") % item_fullpath2)
4390 if len(self.image_list)>0 and update_window_title:
4391 self.update_title()
4392 # Sort the filelist and folderlist alphabetically:
4393 if len(filelist) > 0:
4394 filelist.sort(locale.strcoll)
4395 for item2 in filelist:
4396 if not item2 in self.image_list:
4397 self.image_list.append(item2)
4398 if stop_when_second_image_found and len(self.image_list)==2:
4399 return
4400 if not go_buttons_enabled and len(self.image_list) > 1:
4401 self.set_go_navigation_sensitivities(True)
4402 go_buttons_enabled = True
4403 # Recurse into the folderlist:
4404 if len(folderlist) > 0:
4405 folderlist.sort(locale.strcoll)
4406 for item2 in folderlist:
4407 if not self.stop_now:
4408 self.expand_directory(item2, stop_when_second_image_found, go_buttons_enabled, update_window_title, print_found_msg)
4409
4410 def register_file_with_recent_docs(self, imgfile):
4411 self.recent_file_add_and_refresh(imgfile)
4412 if os.path.isfile(imgfile) and gtk.check_version(2, 10, 0) == None:
4413 try:
4414 gtk_recent_manager = gtk.recent_manager_get_default()
4415 uri = ''
4416 if imgfile[:7] != 'file://':
4417 uri = 'file://'
4418 uri = uri + urllib.request.pathname2url(os.path.abspath(imgfile))
4419 gtk_recent_manager.add_item(uri)
4420 except:
4421 #Isnt currently functional on win32
4422 if sys.platform == "win32":
4423 pass
4424 else:
4425 raise
4426
4427 def valid_image(self, file):
4428 test = gtk.gdk.pixbuf_get_file_info(file)
4429 if test == None:
4430 return False
4431 elif test[0]['name'] == "wbmp":
4432 # some regular files are thought to be wbmp for whatever reason,
4433 # so let's check further.. :(
4434 try:
4435 test2 = gtk.gdk.pixbuf_new_from_file(file)
4436 return True
4437 except:
4438 return False
4439 else:
4440 return True
4441
4442 def image_flip(self, old_pix, vertical):
4443 width = old_pix.get_width()
4444 height = old_pix.get_height()
4445 d = None
4446 if vertical:
4447 d, w, h, rws = imgfuncs.vert(old_pix.get_pixels(), width, height, old_pix.get_rowstride(), old_pix.get_n_channels())
4448 else:
4449 d, w, h, rws = imgfuncs.horiz(old_pix.get_pixels(), width, height, old_pix.get_rowstride(), old_pix.get_n_channels())
4450 if d:
4451 new_pix = gtk.gdk.pixbuf_new_from_data(d, old_pix.get_colorspace(), old_pix.get_has_alpha(), old_pix.get_bits_per_sample(), w, h, rws)
4452 return new_pix
4453 return old_pix
4454
4455 def image_rotate(self, old_pix, full_angle):
4456 width = old_pix.get_width()
4457 height = old_pix.get_height()
4458 angle = full_angle - (int(full_angle) // 360) * 360
4459 if angle:
4460 d = None
4461 if angle % 270 == 0:
4462 d, w, h, rws = imgfuncs.right(old_pix.get_pixels(), width, height, old_pix.get_rowstride(), old_pix.get_n_channels())
4463 elif angle % 180 == 0:
4464 d, w, h, rws = imgfuncs.mirror(old_pix.get_pixels(), width, height, old_pix.get_rowstride(), old_pix.get_n_channels())
4465 elif angle % 90 == 0:
4466 d, w, h, rws = imgfuncs.left(old_pix.get_pixels(), width, height, old_pix.get_rowstride(), old_pix.get_n_channels())
4467 if d:
4468 new_pix = gtk.gdk.pixbuf_new_from_data(d, old_pix.get_colorspace(), old_pix.get_has_alpha(), old_pix.get_bits_per_sample(), w, h, rws)
4469 return new_pix
4470 return old_pix
4471
4472 def toggle_slideshow(self, action):
4473 if len(self.image_list) > 1:
4474 if not self.slideshow_mode:
4475 if self.slideshow_in_fullscreen and not self.fullscreen_mode:
4476 self.enter_fullscreen(None)
4477 self.slideshow_mode = True
4478 self.update_title()
4479 self.set_slideshow_sensitivities()
4480 if not self.curr_slideshow_random:
4481 self.timer_delay = gobject.timeout_add(int(self.curr_slideshow_delay*1000), self.goto_next_image, "ss")
4482 else:
4483 self.reinitialize_randomlist()
4484 self.timer_delay = gobject.timeout_add(int(self.curr_slideshow_delay*1000), self.goto_random_image, "ss")
4485 self.ss_start.hide()
4486 self.ss_stop.show()
4487 timer_screensaver = gobject.timeout_add(1000, self.disable_screensaver_in_slideshow_mode)
4488 else:
4489 self.slideshow_mode = False
4490 gobject.source_remove(self.timer_delay)
4491 self.update_title()
4492 self.set_slideshow_sensitivities()
4493 self.set_zoom_sensitivities()
4494 self.ss_stop.hide()
4495 self.ss_start.show()
4496
4497 def update_title(self):
4498 if len(self.image_list) == 0:
4499 title = "Mirage"
4500 else:
4501 title = "Mirage - " +_("[%(current)i of %(total)i]") % {'current': self.curr_img_in_list+1, 'total': len(self.image_list)} + ' ' + os.path.basename(self.currimg_name)
4502 if self.slideshow_mode:
4503 title = title + ' - ' + _('Slideshow Mode')
4504 self.window.set_title(title)
4505
4506 def slideshow_controls_show(self):
4507 if not self.slideshow_controls_visible and not self.controls_moving:
4508 self.slideshow_controls_visible = True
4509
4510 self.ss_delayspin.set_value(self.curr_slideshow_delay)
4511 self.ss_randomize.set_active(self.curr_slideshow_random)
4512
4513 if self.slideshow_mode:
4514 self.ss_start.set_no_show_all(True)
4515 self.ss_stop.set_no_show_all(False)
4516 else:
4517 self.ss_start.set_no_show_all(False)
4518 self.ss_stop.set_no_show_all(True)
4519
4520 (xpos, ypos) = self.window.get_position()
4521 screen = self.window.get_screen()
4522 self.slideshow_window.set_screen(screen)
4523 self.slideshow_window2.set_screen(screen)
4524
4525 self.slideshow_window.show_all()
4526 self.slideshow_window2.show_all()
4527 if not self.closing_app:
4528 while gtk.events_pending():
4529 gtk.main_iteration()
4530
4531 ss_winheight = self.slideshow_window.allocation.height
4532 ss_win2width = self.slideshow_window2.allocation.width
4533 winheight = self.window.allocation.height
4534 winwidth = self.window.allocation.width
4535 y = -3.0
4536 self.controls_moving = True
4537 while y < ss_winheight:
4538 self.slideshow_window.move(2+xpos, int(winheight-y-2))
4539 self.slideshow_window2.move(winwidth-ss_win2width-2+xpos, int(winheight-y-2))
4540 y += 0.05
4541 if not self.closing_app:
4542 while gtk.events_pending():
4543 gtk.main_iteration()
4544 self.controls_moving = False
4545
4546 def slideshow_controls_hide(self):
4547 if self.slideshow_controls_visible and not self.controls_moving:
4548 self.slideshow_controls_visible = False
4549
4550 (xpos, ypos) = self.window.get_position()
4551
4552 ss_winheight = self.slideshow_window.allocation.height
4553 ss_win2width = self.slideshow_window2.allocation.width
4554 winheight = self.window.allocation.height
4555 winwidth = self.window.allocation.width
4556 y = float(self.slideshow_window.allocation.height*1.0)
4557 self.controls_moving = True
4558 while y > -3:
4559 self.slideshow_window.move(2+xpos, int(winheight-y-2))
4560 self.slideshow_window2.move(winwidth-ss_win2width-2+xpos, int(winheight-y-2))
4561 y -= 0.05
4562 if not self.closing_app:
4563 while gtk.events_pending():
4564 gtk.main_iteration()
4565 self.controls_moving = False
4566
4567 def disable_screensaver_in_slideshow_mode(self):
4568 if self.slideshow_mode and self.disable_screensaver:
4569 test = os.spawnlp(os.P_WAIT, "/usr/bin/xscreensaver-command", "xscreensaver-command", "-deactivate")
4570 if test != 127:
4571 timer_screensaver = gobject.timeout_add(1000, self.disable_screensaver_in_slideshow_mode)
4572
4573 def main(self):
4574 gtk.main()
2526 color = gtk.gdk.Color()
2527 pix = gtk.gdk.pixmap_create_from_data(None, pix_data, 1, 1, 1, color, color)
2528 invisible = gtk.gdk.Cursor(pix, pix, color, color, 0, 0)
2529 self.change_cursor(invisible)
2530 return False
2531
2532 def enter_fullscreen(self, action):
2533 if not self.fullscreen_mode:
2534 self.fullscreen_mode = True
2535 self.UIManager.get_widget("/Popup/Full Screen").hide()
2536 self.UIManager.get_widget("/Popup/Exit Full Screen").show()
2537 self.statusbar.hide()
2538 self.statusbar2.hide()
2539 self.toolbar.hide()
2540 self.menubar.hide()
2541 self.thumbscroll.hide()
2542 self.thumbpane.hide()
2543 self.window.fullscreen()
2544 self.timer_id = gobject.timeout_add(2000, self.hide_cursor)
2545 self.set_slideshow_sensitivities()
2546 if self.simple_bgcolor:
2547 self.layout.modify_bg(gtk.STATE_NORMAL, self.bgcolor)
2548 else:
2549 if self.simple_bgcolor:
2550 self.layout.modify_bg(gtk.STATE_NORMAL, None)
2551 self.leave_fullscreen(action)
2552
2553 def leave_fullscreen(self, action):
2554 if self.fullscreen_mode:
2555 self.slideshow_controls_visible = False
2556 self.slideshow_window.hide_all()
2557 self.slideshow_window2.hide_all()
2558 self.fullscreen_mode = False
2559 self.UIManager.get_widget("/Popup/Full Screen").show()
2560 self.UIManager.get_widget("/Popup/Exit Full Screen").hide()
2561 if self.toolbar_show:
2562 self.toolbar.show()
2563 self.menubar.show()
2564 if self.statusbar_show:
2565 self.statusbar.show()
2566 self.statusbar2.show()
2567 if self.thumbpane_show:
2568 self.thumbscroll.show()
2569 self.thumbpane.show()
2570 self.thumbpane_update_images(False, self.curr_img_in_list)
2571 self.window.unfullscreen()
2572 self.change_cursor(None)
2573 self.set_slideshow_sensitivities()
2574 if self.simple_bgcolor:
2575 self.layout.modify_bg(gtk.STATE_NORMAL, None)
2576
2577 def toggle_status_bar(self, action):
2578 if self.statusbar.get_property("visible"):
2579 self.statusbar.hide()
2580 self.statusbar2.hide()
2581 self.statusbar_show = False
2582 else:
2583 self.statusbar.show()
2584 self.statusbar2.show()
2585 self.statusbar_show = True
2586 if self.image_loaded and self.last_image_action_was_fit:
2587 if self.last_image_action_was_smart_fit:
2588 self.zoom_to_fit_or_1_to_1(None, False, False)
2589 else:
2590 self.zoom_to_fit_window(None, False, False)
2591
2592 def toggle_thumbpane(self, action):
2593 if self.thumbscroll.get_property("visible"):
2594 self.thumbscroll.hide()
2595 self.thumbpane.hide()
2596 self.thumbpane_show = False
2597 else:
2598 self.thumbscroll.show()
2599 self.thumbpane.show()
2600 self.thumbpane_show = True
2601 self.stop_now = False
2602 gobject.idle_add(self.thumbpane_update_images, True, self.curr_img_in_list)
2603 if self.image_loaded and self.last_image_action_was_fit:
2604 if self.last_image_action_was_smart_fit:
2605 self.zoom_to_fit_or_1_to_1(None, False, False)
2606 else:
2607 self.zoom_to_fit_window(None, False, False)
2608
2609 def toggle_toolbar(self, action):
2610 if self.toolbar.get_property("visible"):
2611 self.toolbar.hide()
2612 self.toolbar_show = False
2613 else:
2614 self.toolbar.show()
2615 self.toolbar_show = True
2616 if self.image_loaded and self.last_image_action_was_fit:
2617 if self.last_image_action_was_smart_fit:
2618 self.zoom_to_fit_or_1_to_1(None, False, False)
2619 else:
2620 self.zoom_to_fit_window(None, False, False)
2621
2622 def update_statusbar(self):
2623 # Update status bar:
2624 try:
2625 st = os.stat(self.currimg_name)
2626 filesize = st[stat.ST_SIZE] // 1000
2627 ratio = int(100 * self.currimg_zoomratio)
2628 status_text = (
2629 os.path.basename(self.currimg_name)
2630 + ": "
2631 + str(self.currimg_pixbuf_original.get_width())
2632 + "x"
2633 + str(self.currimg_pixbuf_original.get_height())
2634 + " "
2635 + str(filesize)
2636 + "KB "
2637 + str(ratio)
2638 + "% "
2639 )
2640 except:
2641 status_text = _("Cannot load image.")
2642 self.statusbar.push(self.statusbar.get_context_id(""), status_text)
2643 status_text = ""
2644 if self.running_custom_actions:
2645 status_text = _("Custom actions: %(current)i of %(total)i") % {
2646 "current": self.curr_custom_action,
2647 "total": self.num_custom_actions,
2648 }
2649 elif self.searching_for_images:
2650 status_text = _("Scanning...")
2651 self.statusbar2.push(self.statusbar2.get_context_id(""), status_text)
2652
2653 def show_custom_actions(self, action):
2654 self.actions_dialog = gtk.Dialog(
2655 title=_("Configure Custom Actions"), parent=self.window
2656 )
2657 self.actions_dialog.set_has_separator(False)
2658 self.actions_dialog.set_resizable(False)
2659 table_actions = gtk.Table(13, 2, False)
2660 table_actions.attach(
2661 gtk.Label(), 1, 2, 1, 2, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 0, 0
2662 )
2663 actionscrollwindow = gtk.ScrolledWindow()
2664 self.actionstore = gtk.ListStore(str, str, str)
2665 self.actionwidget = gtk.TreeView()
2666 self.actionwidget.set_enable_search(False)
2667 self.actionwidget.set_rules_hint(True)
2668 self.actionwidget.connect("row-activated", self.edit_custom_action2)
2669 actionscrollwindow.add(self.actionwidget)
2670 actionscrollwindow.set_shadow_type(gtk.SHADOW_IN)
2671 actionscrollwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
2672 actionscrollwindow.set_size_request(500, 200)
2673 self.actionwidget.set_model(self.actionstore)
2674 self.cell = gtk.CellRendererText()
2675 self.cellbool = gtk.CellRendererPixbuf()
2676 self.tvcolumn0 = gtk.TreeViewColumn(_("Batch"))
2677 self.tvcolumn1 = gtk.TreeViewColumn(_("Action"), self.cell, markup=0)
2678 self.tvcolumn2 = gtk.TreeViewColumn(_("Shortcut"))
2679 self.tvcolumn1.set_max_width(
2680 self.actionwidget.size_request()[0]
2681 - self.tvcolumn0.get_width()
2682 - self.tvcolumn2.get_width()
2683 )
2684 self.actionwidget.append_column(self.tvcolumn0)
2685 self.actionwidget.append_column(self.tvcolumn1)
2686 self.actionwidget.append_column(self.tvcolumn2)
2687 self.populate_treeview()
2688 if len(self.action_names) > 0:
2689 self.actionwidget.get_selection().select_path(0)
2690 vbox_actions = gtk.VBox()
2691 addbutton = gtk.Button("", gtk.STOCK_ADD)
2692 addbutton.get_child().get_child().get_children()[1].set_text("")
2693 addbutton.connect("clicked", self.add_custom_action, self.actionwidget)
2694 addbutton.set_tooltip_text(_("Add action"))
2695 editbutton = gtk.Button("", gtk.STOCK_EDIT)
2696 editbutton.get_child().get_child().get_children()[1].set_text("")
2697 editbutton.connect("clicked", self.edit_custom_action, self.actionwidget)
2698 editbutton.set_tooltip_text(_("Edit selected action."))
2699 removebutton = gtk.Button("", gtk.STOCK_REMOVE)
2700 removebutton.get_child().get_child().get_children()[1].set_text("")
2701 removebutton.connect("clicked", self.remove_custom_action)
2702 removebutton.set_tooltip_text(_("Remove selected action."))
2703 upbutton = gtk.Button("", gtk.STOCK_GO_UP)
2704 upbutton.get_child().get_child().get_children()[1].set_text("")
2705 upbutton.connect("clicked", self.custom_action_move_up, self.actionwidget)
2706 upbutton.set_tooltip_text(_("Move selected action up."))
2707 downbutton = gtk.Button("", gtk.STOCK_GO_DOWN)
2708 downbutton.get_child().get_child().get_children()[1].set_text("")
2709 downbutton.connect("clicked", self.custom_action_move_down, self.actionwidget)
2710 downbutton.set_tooltip_text(_("Move selected action down."))
2711 vbox_buttons = gtk.VBox()
2712 propertyinfo = gtk.Label()
2713 propertyinfo.set_markup(
2714 "<small>"
2715 + _("Parameters")
2716 + ':\n<span font_family="Monospace">%F</span> - '
2717 + _("File path, name, and extension")
2718 + '\n<span font_family="Monospace">%P</span> - '
2719 + _("File path")
2720 + '\n<span font_family="Monospace">%N</span> - '
2721 + _("File name without file extension")
2722 + '\n<span font_family="Monospace">%E</span> - '
2723 + _('File extension (i.e. ".png")')
2724 + '\n<span font_family="Monospace">%L</span> - '
2725 + _("List of files, space-separated")
2726 + "</small>"
2727 )
2728 propertyinfo.set_alignment(0, 0)
2729 actioninfo = gtk.Label()
2730 actioninfo.set_markup(
2731 "<small>"
2732 + _("Operations")
2733 + ':\n<span font_family="Monospace">[NEXT]</span> - '
2734 + _("Go to next image")
2735 + '\n<span font_family="Monospace">[PREV]</span> - '
2736 + _("Go to previous image")
2737 + "</small>"
2738 )
2739 actioninfo.set_alignment(0, 0)
2740 hbox_info = gtk.HBox()
2741 hbox_info.pack_start(propertyinfo, False, False, 15)
2742 hbox_info.pack_start(actioninfo, False, False, 15)
2743 vbox_buttons.pack_start(addbutton, False, False, 5)
2744 vbox_buttons.pack_start(editbutton, False, False, 5)
2745 vbox_buttons.pack_start(removebutton, False, False, 5)
2746 vbox_buttons.pack_start(upbutton, False, False, 5)
2747 vbox_buttons.pack_start(downbutton, False, False, 0)
2748 hbox_top = gtk.HBox()
2749 hbox_top.pack_start(actionscrollwindow, True, True, 5)
2750 hbox_top.pack_start(vbox_buttons, False, False, 5)
2751 vbox_actions.pack_start(hbox_top, True, True, 5)
2752 vbox_actions.pack_start(hbox_info, False, False, 5)
2753 hbox_instructions = gtk.HBox()
2754 info_image = gtk.Image()
2755 info_image.set_from_stock(gtk.STOCK_DIALOG_INFO, gtk.ICON_SIZE_BUTTON)
2756 hbox_instructions.pack_start(info_image, False, False, 5)
2757 instructions = gtk.Label(
2758 _(
2759 "Here you can define custom actions with shortcuts. Actions use the built-in parameters and operations listed below and can have multiple statements separated by a semicolon. Batch actions apply to all images in the list."
2760 )
2761 )
2762 instructions.set_line_wrap(True)
2763 instructions.set_alignment(0, 0.5)
2764 hbox_instructions.pack_start(instructions, False, False, 5)
2765 table_actions.attach(
2766 hbox_instructions,
2767 1,
2768 3,
2769 2,
2770 3,
2771 gtk.FILL | gtk.EXPAND,
2772 gtk.FILL | gtk.EXPAND,
2773 5,
2774 0,
2775 )
2776 table_actions.attach(
2777 gtk.Label(), 1, 3, 3, 4, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 15, 0
2778 )
2779 table_actions.attach(
2780 vbox_actions,
2781 1,
2782 3,
2783 4,
2784 12,
2785 gtk.FILL | gtk.EXPAND,
2786 gtk.FILL | gtk.EXPAND,
2787 15,
2788 0,
2789 )
2790 table_actions.attach(
2791 gtk.Label(),
2792 1,
2793 3,
2794 12,
2795 13,
2796 gtk.FILL | gtk.EXPAND,
2797 gtk.FILL | gtk.EXPAND,
2798 15,
2799 0,
2800 )
2801 self.actions_dialog.vbox.pack_start(table_actions, False, False, 0)
2802 # Show dialog:
2803 self.actions_dialog.vbox.show_all()
2804 instructions.set_size_request(self.actions_dialog.size_request()[0] - 50, -1)
2805 close_button = self.actions_dialog.add_button(
2806 gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE
2807 )
2808 close_button.grab_focus()
2809 self.actions_dialog.run()
2810 self.refresh_custom_actions_menu()
2811 while gtk.events_pending():
2812 gtk.main_iteration()
2813 if len(self.image_list) == 0:
2814 self.set_image_sensitivities(False)
2815 self.actions_dialog.destroy()
2816
2817 def add_custom_action(self, button, treeview):
2818 self.open_custom_action_dialog(True, "", "", "None", False, treeview)
2819
2820 def edit_custom_action2(self, treeview, path, view_column):
2821 self.edit_custom_action(None, treeview)
2822
2823 def edit_custom_action(self, button, treeview):
2824 (model, iter) = self.actionwidget.get_selection().get_selected()
2825 if iter != None:
2826 (row,) = self.actionstore.get_path(iter)
2827 self.open_custom_action_dialog(
2828 False,
2829 self.action_names[row],
2830 self.action_commands[row],
2831 self.action_shortcuts[row],
2832 self.action_batch[row],
2833 treeview,
2834 )
2835
2836 def open_custom_action_dialog(
2837 self, add_call, name, command, shortcut, batch, treeview
2838 ):
2839 if add_call:
2840 self.dialog_name = gtk.Dialog(
2841 _("Add Custom Action"),
2842 self.actions_dialog,
2843 gtk.DIALOG_MODAL,
2844 (
2845 gtk.STOCK_CANCEL,
2846 gtk.RESPONSE_REJECT,
2847 gtk.STOCK_OK,
2848 gtk.RESPONSE_ACCEPT,
2849 ),
2850 )
2851 else:
2852 self.dialog_name = gtk.Dialog(
2853 _("Edit Custom Action"),
2854 self.actions_dialog,
2855 gtk.DIALOG_MODAL,
2856 (
2857 gtk.STOCK_CANCEL,
2858 gtk.RESPONSE_REJECT,
2859 gtk.STOCK_OK,
2860 gtk.RESPONSE_ACCEPT,
2861 ),
2862 )
2863 self.dialog_name.set_modal(True)
2864 table = gtk.Table(2, 4, False)
2865 action_name_label = gtk.Label(_("Action Name:"))
2866 action_name_label.set_alignment(0, 0.5)
2867 action_command_label = gtk.Label(_("Command:"))
2868 action_command_label.set_alignment(0, 0.5)
2869 shortcut_label = gtk.Label(_("Shortcut:"))
2870 shortcut_label.set_alignment(0, 0.5)
2871 table.attach(
2872 action_name_label,
2873 0,
2874 1,
2875 0,
2876 1,
2877 gtk.FILL | gtk.EXPAND,
2878 gtk.FILL | gtk.EXPAND,
2879 15,
2880 0,
2881 )
2882 table.attach(
2883 action_command_label,
2884 0,
2885 1,
2886 1,
2887 2,
2888 gtk.FILL | gtk.EXPAND,
2889 gtk.FILL | gtk.EXPAND,
2890 15,
2891 0,
2892 )
2893 table.attach(
2894 shortcut_label,
2895 0,
2896 1,
2897 2,
2898 3,
2899 gtk.FILL | gtk.EXPAND,
2900 gtk.FILL | gtk.EXPAND,
2901 15,
2902 0,
2903 )
2904 action_name = gtk.Entry()
2905 action_name.set_text(name)
2906 action_command = gtk.Entry()
2907 action_command.set_text(command)
2908 table.attach(
2909 action_name, 1, 2, 0, 1, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 15, 0
2910 )
2911 table.attach(
2912 action_command,
2913 1,
2914 2,
2915 1,
2916 2,
2917 gtk.FILL | gtk.EXPAND,
2918 gtk.FILL | gtk.EXPAND,
2919 15,
2920 0,
2921 )
2922 self.shortcut = gtk.Button(shortcut)
2923 self.shortcut.connect("clicked", self.shortcut_clicked)
2924 table.attach(
2925 self.shortcut,
2926 1,
2927 2,
2928 2,
2929 3,
2930 gtk.FILL | gtk.EXPAND,
2931 gtk.FILL | gtk.EXPAND,
2932 15,
2933 0,
2934 )
2935 batchmode = gtk.CheckButton(_("Perform action on all images (Batch)"))
2936 batchmode.set_active(batch)
2937 table.attach(
2938 batchmode, 0, 2, 3, 4, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 15, 0
2939 )
2940 self.dialog_name.vbox.pack_start(table, False, False, 5)
2941 self.dialog_name.vbox.show_all()
2942 self.dialog_name.connect(
2943 "response",
2944 self.dialog_name_response,
2945 add_call,
2946 action_name,
2947 action_command,
2948 self.shortcut,
2949 batchmode,
2950 treeview,
2951 )
2952 self.dialog_name.run()
2953
2954 def dialog_name_response(
2955 self,
2956 dialog,
2957 response,
2958 add_call,
2959 action_name,
2960 action_command,
2961 shortcut,
2962 batchmode,
2963 treeview,
2964 ):
2965 if response == gtk.RESPONSE_ACCEPT:
2966 if not (
2967 action_command.get_text() == ""
2968 or action_name.get_text() == ""
2969 or self.shortcut.get_label() == "None"
2970 ):
2971 name = action_name.get_text()
2972 command = action_command.get_text()
2973 if (
2974 ("[NEXT]" in command.strip()) and command.strip()[-6:] != "[NEXT]"
2975 ) or (
2976 ("[PREV]" in command.strip()) and command.strip()[-6:] != "[PREV]"
2977 ):
2978 error_dialog = gtk.MessageDialog(
2979 self.actions_dialog,
2980 gtk.DIALOG_MODAL,
2981 gtk.MESSAGE_WARNING,
2982 gtk.BUTTONS_CLOSE,
2983 _(
2984 "[PREV] and [NEXT] are only valid alone or at the end of the command"
2985 ),
2986 )
2987 error_dialog.set_title(_("Invalid Custom Action"))
2988 error_dialog.run()
2989 error_dialog.destroy()
2990 return
2991 shortcut = shortcut.get_label()
2992 batch = batchmode.get_active()
2993 dialog.destroy()
2994 if add_call:
2995 self.action_names.append(name)
2996 self.action_commands.append(command)
2997 self.action_shortcuts.append(shortcut)
2998 self.action_batch.append(batch)
2999 else:
3000 (model, iter) = self.actionwidget.get_selection().get_selected()
3001 (rownum,) = self.actionstore.get_path(iter)
3002 self.action_names[rownum] = name
3003 self.action_commands[rownum] = command
3004 self.action_shortcuts[rownum] = shortcut
3005 self.action_batch[rownum] = batch
3006 self.populate_treeview()
3007 if add_call:
3008 rownum = len(self.action_names) - 1
3009 treeview.get_selection().select_path(rownum)
3010 while gtk.events_pending():
3011 gtk.main_iteration()
3012 # Keep item in visible rect:
3013 visible_rect = treeview.get_visible_rect()
3014 row_rect = treeview.get_background_area(rownum, self.tvcolumn1)
3015 if row_rect.y + row_rect.height > visible_rect.height:
3016 top_coord = (
3017 row_rect.y + row_rect.height - visible_rect.height
3018 ) + visible_rect.y
3019 treeview.scroll_to_point(-1, top_coord)
3020 elif row_rect.y < 0:
3021 treeview.scroll_to_cell(rownum)
3022 else:
3023 error_dialog = gtk.MessageDialog(
3024 self.actions_dialog,
3025 gtk.DIALOG_MODAL,
3026 gtk.MESSAGE_WARNING,
3027 gtk.BUTTONS_CLOSE,
3028 _("Incomplete custom action specified."),
3029 )
3030 error_dialog.set_title(_("Invalid Custom Action"))
3031 error_dialog.run()
3032 error_dialog.destroy()
3033 else:
3034 dialog.destroy()
3035
3036 def custom_action_move_down(self, button, treeview):
3037 iter = None
3038 selection = treeview.get_selection()
3039 model, iter = selection.get_selected()
3040 if iter:
3041 rownum = int(model.get_string_from_iter(iter))
3042 if rownum < len(self.action_names) - 1:
3043 # Move item down:
3044 temp_name = self.action_names[rownum]
3045 temp_shortcut = self.action_shortcuts[rownum]
3046 temp_command = self.action_commands[rownum]
3047 temp_batch = self.action_batch[rownum]
3048 self.action_names[rownum] = self.action_names[rownum + 1]
3049 self.action_shortcuts[rownum] = self.action_shortcuts[rownum + 1]
3050 self.action_commands[rownum] = self.action_commands[rownum + 1]
3051 self.action_batch[rownum] = self.action_batch[rownum + 1]
3052 self.action_names[rownum + 1] = temp_name
3053 self.action_shortcuts[rownum + 1] = temp_shortcut
3054 self.action_commands[rownum + 1] = temp_command
3055 self.action_batch[rownum + 1] = temp_batch
3056 # Repopulate treeview and keep item selected:
3057 self.populate_treeview()
3058 selection.select_path((rownum + 1,))
3059 while gtk.events_pending():
3060 gtk.main_iteration()
3061 # Keep item in visible rect:
3062 rownum = rownum + 1
3063 visible_rect = treeview.get_visible_rect()
3064 row_rect = treeview.get_background_area(rownum, self.tvcolumn1)
3065 if row_rect.y + row_rect.height > visible_rect.height:
3066 top_coord = (
3067 row_rect.y + row_rect.height - visible_rect.height
3068 ) + visible_rect.y
3069 treeview.scroll_to_point(-1, top_coord)
3070 elif row_rect.y < 0:
3071 treeview.scroll_to_cell(rownum)
3072
3073 def custom_action_move_up(self, button, treeview):
3074 iter = None
3075 selection = treeview.get_selection()
3076 model, iter = selection.get_selected()
3077 if iter:
3078 rownum = int(model.get_string_from_iter(iter))
3079 if rownum > 0:
3080 # Move item down:
3081 temp_name = self.action_names[rownum]
3082 temp_shortcut = self.action_shortcuts[rownum]
3083 temp_command = self.action_commands[rownum]
3084 temp_batch = self.action_batch[rownum]
3085 self.action_names[rownum] = self.action_names[rownum - 1]
3086 self.action_shortcuts[rownum] = self.action_shortcuts[rownum - 1]
3087 self.action_commands[rownum] = self.action_commands[rownum - 1]
3088 self.action_batch[rownum] = self.action_batch[rownum - 1]
3089 self.action_names[rownum - 1] = temp_name
3090 self.action_shortcuts[rownum - 1] = temp_shortcut
3091 self.action_commands[rownum - 1] = temp_command
3092 self.action_batch[rownum - 1] = temp_batch
3093 # Repopulate treeview and keep item selected:
3094 self.populate_treeview()
3095 selection.select_path((rownum - 1,))
3096 while gtk.events_pending():
3097 gtk.main_iteration()
3098 # Keep item in visible rect:
3099 rownum = rownum - 1
3100 visible_rect = treeview.get_visible_rect()
3101 row_rect = treeview.get_background_area(rownum, self.tvcolumn1)
3102 if row_rect.y + row_rect.height > visible_rect.height:
3103 top_coord = (
3104 row_rect.y + row_rect.height - visible_rect.height
3105 ) + visible_rect.y
3106 treeview.scroll_to_point(-1, top_coord)
3107 elif row_rect.y < 0:
3108 treeview.scroll_to_cell(rownum)
3109
3110 def shortcut_clicked(self, widget):
3111 self.dialog_shortcut = gtk.Dialog(
3112 _("Action Shortcut"),
3113 self.dialog_name,
3114 gtk.DIALOG_MODAL,
3115 (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT),
3116 )
3117 self.shortcut_label = gtk.Label(_("Press the desired shortcut for the action."))
3118 hbox = gtk.HBox()
3119 hbox.pack_start(self.shortcut_label, False, False, 15)
3120 self.dialog_shortcut.vbox.pack_start(hbox, False, False, 5)
3121 self.dialog_shortcut.vbox.show_all()
3122 self.dialog_shortcut.connect("key-press-event", self.shortcut_keypress)
3123 self.dialog_shortcut.run()
3124 self.dialog_shortcut.destroy()
3125
3126 def shortcut_keypress(self, widget, event):
3127 shortcut = gtk.accelerator_name(event.keyval, event.state)
3128 if "<Mod2>" in shortcut:
3129 shortcut = shortcut.replace("<Mod2>", "")
3130 if (
3131 shortcut[(len(shortcut) - 2) : len(shortcut)] != "_L"
3132 and shortcut[(len(shortcut) - 2) : len(shortcut)] != "_R"
3133 ):
3134 # Validate to make sure the shortcut hasn't already been used:
3135 for i in range(len(self.keys)):
3136 if shortcut == self.keys[i][1]:
3137 error_dialog = gtk.MessageDialog(
3138 self.dialog_shortcut,
3139 gtk.DIALOG_MODAL,
3140 gtk.MESSAGE_WARNING,
3141 gtk.BUTTONS_CLOSE,
3142 _("The shortcut '%(shortcut)s' is already used for '%(key)s'.")
3143 % {"shortcut": shortcut, "key": self.keys[i][0]},
3144 )
3145 error_dialog.set_title(_("Invalid Shortcut"))
3146 error_dialog.run()
3147 error_dialog.destroy()
3148 return
3149 for i in range(len(self.action_shortcuts)):
3150 if shortcut == self.action_shortcuts[i]:
3151 error_dialog = gtk.MessageDialog(
3152 self.dialog_shortcut,
3153 gtk.DIALOG_MODAL,
3154 gtk.MESSAGE_WARNING,
3155 gtk.BUTTONS_CLOSE,
3156 _("The shortcut '%(shortcut)s' is already used for '%(key)s'.")
3157 % {"shortcut": shortcut, "key": self.action_names[i]},
3158 )
3159 error_dialog.set_title(_("Invalid Shortcut"))
3160 error_dialog.run()
3161 error_dialog.destroy()
3162 return
3163 self.shortcut.set_label(shortcut)
3164 widget.destroy()
3165
3166 def remove_custom_action(self, button):
3167 (model, iter) = self.actionwidget.get_selection().get_selected()
3168 if iter != None:
3169 (row,) = self.actionstore.get_path(iter)
3170 self.action_names.pop(row)
3171 self.action_shortcuts.pop(row)
3172 self.action_commands.pop(row)
3173 self.action_batch.pop(row)
3174 self.populate_treeview()
3175 self.actionwidget.grab_focus()
3176
3177 def populate_treeview(self):
3178 self.actionstore.clear()
3179 for i in range(len(self.action_names)):
3180 if self.action_batch[i]:
3181 pb = gtk.STOCK_APPLY
3182 else:
3183 pb = None
3184 self.actionstore.append(
3185 [
3186 pb,
3187 "<big><b>"
3188 + self.action_names[i].replace("&", "&amp;")
3189 + "</b></big>\n<small>"
3190 + self.action_commands[i].replace("&", "&amp;")
3191 + "</small>",
3192 self.action_shortcuts[i],
3193 ]
3194 )
3195 self.tvcolumn0.clear()
3196 self.tvcolumn1.clear()
3197 self.tvcolumn2.clear()
3198 self.tvcolumn0.pack_start(self.cellbool)
3199 self.tvcolumn1.pack_start(self.cell)
3200 self.tvcolumn2.pack_start(self.cell)
3201 self.tvcolumn0.add_attribute(self.cellbool, "stock-id", 0)
3202 self.tvcolumn1.set_attributes(self.cell, markup=1)
3203 self.tvcolumn2.set_attributes(self.cell, text=2)
3204 self.tvcolumn1.set_expand(True)
3205
3206 def screenshot(self, action):
3207 cancel = self.autosave_image()
3208 if cancel:
3209 return
3210 # Dialog:
3211 dialog = gtk.Dialog(
3212 _("Screenshot"),
3213 self.window,
3214 gtk.DIALOG_MODAL,
3215 (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT),
3216 )
3217 snapbutton = dialog.add_button(_("_Snap"), gtk.RESPONSE_ACCEPT)
3218 snapimage = gtk.Image()
3219 snapimage.set_from_stock(gtk.STOCK_OK, gtk.ICON_SIZE_BUTTON)
3220 snapbutton.set_image(snapimage)
3221 loc = gtk.Label()
3222 loc.set_markup("<b>" + _("Location") + "</b>")
3223 loc.set_alignment(0, 0)
3224 area = gtk.RadioButton()
3225 area1 = gtk.RadioButton(group=area, label=_("Entire screen"))
3226 area2 = gtk.RadioButton(group=area, label=_("Window under pointer"))
3227 if not HAS_XMOUSE:
3228 area2.set_sensitive(False)
3229 area1.set_active(True)
3230 de = gtk.Label()
3231 de.set_markup("<b>" + _("Delay") + "</b>")
3232 de.set_alignment(0, 0)
3233 delaybox = gtk.HBox()
3234 adj = gtk.Adjustment(self.screenshot_delay, 0, 30, 1, 10, 0)
3235 delay = gtk.SpinButton(adj, 0, 0)
3236 delay.set_numeric(True)
3237 delay.set_update_policy(gtk.UPDATE_IF_VALID)
3238 delay.set_wrap(False)
3239 delaylabel = gtk.Label(_(" seconds"))
3240 delaybox.pack_start(delay, False)
3241 delaybox.pack_start(delaylabel, False)
3242 table = gtk.Table()
3243 table.attach(
3244 gtk.Label(), 1, 2, 1, 2, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 0, 0
3245 )
3246 table.attach(
3247 loc, 1, 2, 2, 3, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 15, 0
3248 )
3249 table.attach(
3250 gtk.Label(), 1, 2, 3, 4, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 0, 0
3251 )
3252 table.attach(
3253 area1, 1, 2, 4, 5, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 30, 0
3254 )
3255 table.attach(
3256 area2, 1, 2, 5, 6, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 30, 0
3257 )
3258 table.attach(
3259 gtk.Label(), 1, 2, 6, 7, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 30, 0
3260 )
3261 table.attach(
3262 de, 1, 2, 7, 8, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 15, 0
3263 )
3264 table.attach(
3265 gtk.Label(), 1, 2, 8, 9, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 30, 0
3266 )
3267 table.attach(
3268 delaybox, 1, 2, 9, 10, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 30, 0
3269 )
3270 table.attach(
3271 gtk.Label(),
3272 1,
3273 2,
3274 10,
3275 11,
3276 gtk.FILL | gtk.EXPAND,
3277 gtk.FILL | gtk.EXPAND,
3278 30,
3279 0,
3280 )
3281 dialog.vbox.pack_start(table)
3282 dialog.set_default_response(gtk.RESPONSE_ACCEPT)
3283 dialog.vbox.show_all()
3284 response = dialog.run()
3285 if response == gtk.RESPONSE_ACCEPT:
3286 dialog.destroy()
3287 while gtk.events_pending():
3288 gtk.main_iteration()
3289 self.screenshot_delay = delay.get_value_as_int()
3290 gobject.timeout_add(
3291 int(self.screenshot_delay * 1000),
3292 self._screenshot_grab,
3293 area1.get_active(),
3294 )
3295 else:
3296 dialog.destroy()
3297
3298 def _screenshot_grab(self, entire_screen):
3299 root_win = gtk.gdk.get_default_root_window()
3300 if entire_screen:
3301 x = 0
3302 y = 0
3303 width = gtk.gdk.screen_width()
3304 height = gtk.gdk.screen_height()
3305 else:
3306 (x, y, width, height) = xmouse.geometry()
3307 pix = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, width, height)
3308 pix = pix.get_from_drawable(
3309 root_win, gtk.gdk.colormap_get_system(), x, y, 0, 0, width, height
3310 )
3311 # Save as /tmp/mirage-<random>/filename.ext
3312 tmpdir = tempfile.mkdtemp(prefix="mirage-") + "/"
3313 tmpfile = tmpdir + "screenshot.png"
3314 pix.save(tmpfile, "png")
3315 # Load file:
3316 self.image_list = [tmpfile]
3317 self.curr_img_in_list = 0
3318 gobject.idle_add(self.load_new_image2, False, False, False, False, True)
3319 self.update_statusbar()
3320 self.set_go_navigation_sensitivities(False)
3321 self.set_slideshow_sensitivities()
3322 self.thumbpane_update_images(True, self.curr_img_in_list)
3323 del pix
3324 self.window.present()
3325
3326 def show_properties(self, action):
3327 show_props = gtk.Dialog(_("Properties"), self.window)
3328 show_props.set_has_separator(False)
3329 show_props.set_resizable(False)
3330 table = gtk.Table(3, 3, False)
3331 image = gtk.Image()
3332 animtest = gtk.gdk.PixbufAnimation(self.currimg_name)
3333 image_is_anim = False
3334 if animtest.is_static_image():
3335 pixbuf, image_width, image_height = self.get_pixbuf_of_size(
3336 self.currimg_pixbuf_original, 180, self.zoom_quality
3337 )
3338 else:
3339 pixbuf, image_width, image_height = self.get_pixbuf_of_size(
3340 animtest.get_static_image(), 180, self.zoom_quality
3341 )
3342 image_is_anim = True
3343 image.set_from_pixbuf(self.pixbuf_add_border(pixbuf))
3344 vbox_left = gtk.VBox()
3345 filename = gtk.Label(_("File name:"))
3346 filename.set_alignment(1, 1)
3347 filedate = gtk.Label(_("File modified:"))
3348 filedate.set_alignment(1, 1)
3349 imagesize = gtk.Label(_("Dimensions:"))
3350 imagesize.set_alignment(1, 1)
3351 filesize = gtk.Label(_("File size:"))
3352 filesize.set_alignment(1, 1)
3353 filetype = gtk.Label(_("File type:"))
3354 filetype.set_alignment(1, 1)
3355 transparency = gtk.Label(_("Transparency:"))
3356 transparency.set_alignment(1, 1)
3357 animation = gtk.Label(_("Animation:"))
3358 animation.set_alignment(1, 1)
3359 bits = gtk.Label(_("Bits per sample:"))
3360 bits.set_alignment(1, 1)
3361 channels = gtk.Label(_("Channels:"))
3362 channels.set_alignment(1, 1)
3363 vbox_left.pack_start(filename, False, False, 2)
3364 vbox_left.pack_start(filedate, False, False, 2)
3365 vbox_left.pack_start(imagesize, False, False, 2)
3366 vbox_left.pack_start(filesize, False, False, 2)
3367 vbox_left.pack_start(filetype, False, False, 2)
3368 vbox_left.pack_start(transparency, False, False, 2)
3369 vbox_left.pack_start(animation, False, False, 2)
3370 vbox_left.pack_start(bits, False, False, 2)
3371 vbox_left.pack_start(channels, False, False, 2)
3372 vbox_right = gtk.VBox()
3373 filestat = os.stat(self.currimg_name)
3374 filename2 = gtk.Label(os.path.basename(self.currimg_name))
3375 filedate2 = gtk.Label(
3376 time.strftime("%c", time.localtime(filestat[stat.ST_MTIME]))
3377 )
3378 imagesize2 = gtk.Label(
3379 str(self.currimg_pixbuf_original.get_width())
3380 + "x"
3381 + str(self.currimg_pixbuf_original.get_height())
3382 )
3383 filetype2 = gtk.Label(
3384 gtk.gdk.pixbuf_get_file_info(self.currimg_name)[0]["mime_types"][0]
3385 )
3386 filesize2 = gtk.Label(str(filestat[stat.ST_SIZE] // 1000) + "KB")
3387 if not image_is_anim and pixbuf.get_has_alpha():
3388 transparency2 = gtk.Label(_("Yes"))
3389 else:
3390 transparency2 = gtk.Label(_("No"))
3391 if animtest.is_static_image():
3392 animation2 = gtk.Label(_("No"))
3393 else:
3394 animation2 = gtk.Label(_("Yes"))
3395 bits2 = gtk.Label(str(pixbuf.get_bits_per_sample()))
3396 channels2 = gtk.Label(str(pixbuf.get_n_channels()))
3397 filename2.set_alignment(0, 1)
3398 filedate2.set_alignment(0, 1)
3399 imagesize2.set_alignment(0, 1)
3400 filesize2.set_alignment(0, 1)
3401 filetype2.set_alignment(0, 1)
3402 transparency2.set_alignment(0, 1)
3403 animation2.set_alignment(0, 1)
3404 bits2.set_alignment(0, 1)
3405 channels2.set_alignment(0, 1)
3406 vbox_right.pack_start(filename2, False, False, 2)
3407 vbox_right.pack_start(filedate2, False, False, 2)
3408 vbox_right.pack_start(imagesize2, False, False, 2)
3409 vbox_right.pack_start(filesize2, False, False, 2)
3410 vbox_right.pack_start(filetype2, False, False, 2)
3411 vbox_right.pack_start(transparency2, False, False, 2)
3412 vbox_right.pack_start(animation2, False, False, 2)
3413 vbox_right.pack_start(bits2, False, False, 2)
3414 vbox_right.pack_start(channels2, False, False, 2)
3415 hbox = gtk.HBox()
3416 hbox.pack_start(vbox_left, False, False, 3)
3417 hbox.pack_start(vbox_right, False, False, 3)
3418 table.attach(
3419 image, 1, 2, 1, 3, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 15, 0
3420 )
3421 table.attach(
3422 hbox, 2, 3, 1, 3, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 15, 0
3423 )
3424 show_props.vbox.pack_start(table, False, False, 15)
3425 show_props.vbox.show_all()
3426 close_button = show_props.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)
3427 close_button.grab_focus()
3428 show_props.run()
3429 show_props.destroy()
3430
3431 def show_prefs(self, action):
3432 prev_thumbnail_size = self.thumbnail_size
3433 self.prefs_dialog = gtk.Dialog(_("Mirage Preferences"), self.window)
3434 self.prefs_dialog.set_has_separator(False)
3435 self.prefs_dialog.set_resizable(False)
3436 # "Interface" prefs:
3437 table_settings = gtk.Table(14, 3, False)
3438 bglabel = gtk.Label()
3439 bglabel.set_markup("<b>" + _("Interface") + "</b>")
3440 bglabel.set_alignment(0, 1)
3441 color_hbox = gtk.HBox(False, 0)
3442 colortext = gtk.Label(_("Background color:"))
3443 self.colorbutton = gtk.ColorButton(self.bgcolor)
3444 self.colorbutton.connect("color-set", self.bgcolor_selected)
3445 self.colorbutton.set_size_request(150, -1)
3446 self.colorbutton.set_tooltip_text(
3447 _("Sets the background color for the application.")
3448 )
3449 color_hbox.pack_start(colortext, False, False, 0)
3450 color_hbox.pack_start(self.colorbutton, False, False, 0)
3451 color_hbox.pack_start(gtk.Label(), True, True, 0)
3452
3453 simplecolor_hbox = gtk.HBox(False, 0)
3454 simplecolortext = gtk.Label(_("Simple background color:"))
3455 simplecolorbutton = gtk.CheckButton()
3456 simplecolorbutton.connect("toggled", self.simple_bgcolor_selected)
3457 simplecolor_hbox.pack_start(simplecolortext, False, False, 0)
3458 simplecolor_hbox.pack_start(simplecolorbutton, False, False, 0)
3459 simplecolor_hbox.pack_start(gtk.Label(), True, True, 0)
3460 if self.simple_bgcolor:
3461 simplecolorbutton.set_active(True)
3462
3463 fullscreen = gtk.CheckButton(_("Open Mirage in fullscreen mode"))
3464 fullscreen.set_active(self.start_in_fullscreen)
3465 thumbbox = gtk.HBox()
3466 thumblabel = gtk.Label(_("Thumbnail size:"))
3467 thumbbox.pack_start(thumblabel, False, False, 0)
3468 thumbsize = gtk.combo_box_new_text()
3469 option = 0
3470 for size in self.thumbnail_sizes:
3471 thumbsize.append_text(size + " x " + size)
3472 if self.thumbnail_size == int(size):
3473 thumbsize.set_active(option)
3474 option += 1
3475 thumbbox.pack_start(thumbsize, False, False, 5)
3476 table_settings.attach(
3477 gtk.Label(), 1, 3, 1, 2, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 0, 0
3478 )
3479 table_settings.attach(
3480 bglabel, 1, 3, 2, 3, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 15, 0
3481 )
3482 table_settings.attach(
3483 gtk.Label(), 1, 3, 3, 4, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 0, 0
3484 )
3485 table_settings.attach(
3486 simplecolor_hbox,
3487 1,
3488 2,
3489 4,
3490 5,
3491 gtk.FILL | gtk.EXPAND,
3492 gtk.FILL | gtk.EXPAND,
3493 30,
3494 0,
3495 )
3496 table_settings.attach(
3497 color_hbox, 1, 2, 5, 6, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 30, 0
3498 )
3499 table_settings.attach(
3500 gtk.Label(), 1, 3, 6, 7, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 0, 0
3501 )
3502 table_settings.attach(
3503 thumbbox, 1, 3, 7, 8, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 30, 0
3504 )
3505 table_settings.attach(
3506 gtk.Label(), 1, 3, 8, 9, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 30, 0
3507 )
3508 table_settings.attach(
3509 fullscreen, 1, 3, 9, 10, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 30, 0
3510 )
3511 table_settings.attach(
3512 gtk.Label(),
3513 1,
3514 3,
3515 10,
3516 11,
3517 gtk.FILL | gtk.EXPAND,
3518 gtk.FILL | gtk.EXPAND,
3519 30,
3520 0,
3521 )
3522 table_settings.attach(
3523 gtk.Label(),
3524 1,
3525 3,
3526 11,
3527 12,
3528 gtk.FILL | gtk.EXPAND,
3529 gtk.FILL | gtk.EXPAND,
3530 30,
3531 0,
3532 )
3533 table_settings.attach(
3534 gtk.Label(),
3535 1,
3536 3,
3537 12,
3538 13,
3539 gtk.FILL | gtk.EXPAND,
3540 gtk.FILL | gtk.EXPAND,
3541 30,
3542 0,
3543 )
3544 table_settings.attach(
3545 gtk.Label(),
3546 1,
3547 3,
3548 13,
3549 14,
3550 gtk.FILL | gtk.EXPAND,
3551 gtk.FILL | gtk.EXPAND,
3552 30,
3553 0,
3554 )
3555 table_settings.attach(
3556 gtk.Label(),
3557 1,
3558 3,
3559 14,
3560 15,
3561 gtk.FILL | gtk.EXPAND,
3562 gtk.FILL | gtk.EXPAND,
3563 30,
3564 0,
3565 )
3566 # "Behavior" tab:
3567 table_behavior = gtk.Table(14, 2, False)
3568 openlabel = gtk.Label()
3569 openlabel.set_markup("<b>" + _("Open Behavior") + "</b>")
3570 openlabel.set_alignment(0, 1)
3571 hbox_openmode = gtk.HBox()
3572 hbox_openmode.pack_start(gtk.Label(_("Open new image in:")), False, False, 0)
3573 combobox = gtk.combo_box_new_text()
3574 combobox.append_text(_("Smart Mode"))
3575 combobox.append_text(_("Zoom To Fit Mode"))
3576 combobox.append_text(_("1:1 Mode"))
3577 combobox.append_text(_("Last Active Mode"))
3578 combobox.set_active(self.open_mode)
3579 hbox_openmode.pack_start(combobox, False, False, 5)
3580 openallimages = gtk.CheckButton(_("Load all images in current directory"))
3581 openallimages.set_active(self.open_all_images)
3582 openallimages.set_tooltip_text(
3583 _(
3584 "If enabled, opening an image in Mirage will automatically load all images found in that image's directory."
3585 )
3586 )
3587 hiddenimages = gtk.CheckButton(_("Allow loading hidden files"))
3588 hiddenimages.set_active(self.open_hidden_files)
3589 hiddenimages.set_tooltip_text(
3590 _(
3591 "If checked, Mirage will open hidden files. Otherwise, hidden files will be ignored."
3592 )
3593 )
3594 openpref = gtk.RadioButton()
3595 openpref1 = gtk.RadioButton(
3596 group=openpref, label=_("Use last chosen directory")
3597 )
3598 openpref1.set_tooltip_text(
3599 _("The default 'Open' directory will be the last directory used.")
3600 )
3601 openpref2 = gtk.RadioButton(
3602 group=openpref, label=_("Use this fixed directory:")
3603 )
3604 openpref2.connect("toggled", self.prefs_use_fixed_dir_clicked)
3605 openpref2.set_tooltip_text(
3606 _("The default 'Open' directory will be this specified directory.")
3607 )
3608 hbox_defaultdir = gtk.HBox()
3609 self.defaultdir = gtk.Button()
3610 hbox_defaultdir.pack_start(gtk.Label(), True, True, 0)
3611 hbox_defaultdir.pack_start(self.defaultdir, False, False, 0)
3612 hbox_defaultdir.pack_start(gtk.Label(), True, True, 0)
3613 if len(self.fixed_dir) > 25:
3614 self.defaultdir.set_label("..." + self.fixed_dir[-22:])
3615 else:
3616 self.defaultdir.set_label(self.fixed_dir)
3617 self.defaultdir.connect("clicked", self.defaultdir_clicked)
3618 self.defaultdir.set_size_request(250, -1)
3619 if self.use_last_dir:
3620 openpref1.set_active(True)
3621 self.defaultdir.set_sensitive(False)
3622 else:
3623 openpref2.set_active(True)
3624 self.defaultdir.set_sensitive(True)
3625 table_behavior.attach(
3626 gtk.Label(), 1, 2, 1, 2, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 0, 0
3627 )
3628 table_behavior.attach(
3629 openlabel, 1, 2, 2, 3, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 15, 0
3630 )
3631 table_behavior.attach(
3632 gtk.Label(), 1, 2, 3, 4, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 0, 0
3633 )
3634 table_behavior.attach(
3635 hbox_openmode,
3636 1,
3637 2,
3638 4,
3639 5,
3640 gtk.FILL | gtk.EXPAND,
3641 gtk.FILL | gtk.EXPAND,
3642 30,
3643 0,
3644 )
3645 table_behavior.attach(
3646 gtk.Label(), 1, 2, 5, 6, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 0, 0
3647 )
3648 table_behavior.attach(
3649 openallimages,
3650 1,
3651 2,
3652 6,
3653 7,
3654 gtk.FILL | gtk.EXPAND,
3655 gtk.FILL | gtk.EXPAND,
3656 30,
3657 0,
3658 )
3659 table_behavior.attach(
3660 hiddenimages,
3661 1,
3662 2,
3663 7,
3664 8,
3665 gtk.FILL | gtk.EXPAND,
3666 gtk.FILL | gtk.EXPAND,
3667 30,
3668 0,
3669 )
3670 table_behavior.attach(
3671 gtk.Label(), 1, 2, 8, 9, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 0, 0
3672 )
3673 table_behavior.attach(
3674 openpref1, 1, 2, 9, 10, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 30, 0
3675 )
3676 table_behavior.attach(
3677 openpref2, 1, 2, 10, 11, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 30, 0
3678 )
3679 table_behavior.attach(
3680 hbox_defaultdir,
3681 1,
3682 2,
3683 11,
3684 12,
3685 gtk.FILL | gtk.EXPAND,
3686 gtk.FILL | gtk.EXPAND,
3687 45,
3688 0,
3689 )
3690 table_behavior.attach(
3691 gtk.Label(),
3692 1,
3693 2,
3694 12,
3695 13,
3696 gtk.FILL | gtk.EXPAND,
3697 gtk.FILL | gtk.EXPAND,
3698 45,
3699 0,
3700 )
3701 # "Navigation" tab:
3702 table_navigation = gtk.Table(14, 2, False)
3703 navlabel = gtk.Label()
3704 navlabel.set_markup("<b>" + _("Navigation") + "</b>")
3705 navlabel.set_alignment(0, 1)
3706 preloadnav = gtk.CheckButton(label=_("Preload images for faster navigation"))
3707 preloadnav.set_active(self.preloading_images)
3708 preloadnav.set_tooltip_text(
3709 _(
3710 "If enabled, the next and previous images in the list will be preloaded during idle time. Note that the speed increase comes at the expense of memory usage, so it is recommended to disable this option on machines with limited ram."
3711 )
3712 )
3713 hbox_listwrap = gtk.HBox()
3714 hbox_listwrap.pack_start(
3715 gtk.Label(_("Wrap around imagelist:")), False, False, 0
3716 )
3717 combobox2 = gtk.combo_box_new_text()
3718 combobox2.append_text(_("No"))
3719 combobox2.append_text(_("Yes"))
3720 combobox2.append_text(_("Prompt User"))
3721 combobox2.set_active(self.listwrap_mode)
3722 hbox_listwrap.pack_start(combobox2, False, False, 5)
3723 table_navigation.attach(
3724 gtk.Label(), 1, 2, 1, 2, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 0, 0
3725 )
3726 table_navigation.attach(
3727 navlabel, 1, 2, 2, 3, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 15, 0
3728 )
3729 table_navigation.attach(
3730 gtk.Label(), 1, 2, 3, 4, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 0, 0
3731 )
3732 table_navigation.attach(
3733 hbox_listwrap,
3734 1,
3735 2,
3736 4,
3737 5,
3738 gtk.FILL | gtk.EXPAND,
3739 gtk.FILL | gtk.EXPAND,
3740 30,
3741 0,
3742 )
3743 table_navigation.attach(
3744 gtk.Label(), 1, 2, 5, 6, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 0, 0
3745 )
3746 table_navigation.attach(
3747 preloadnav, 1, 2, 6, 7, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 30, 0
3748 )
3749 table_navigation.attach(
3750 gtk.Label(), 1, 2, 7, 8, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 30, 0
3751 )
3752 table_navigation.attach(
3753 gtk.Label(), 1, 2, 8, 9, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 0, 0
3754 )
3755 table_navigation.attach(
3756 gtk.Label(),
3757 1,
3758 2,
3759 9,
3760 10,
3761 gtk.FILL | gtk.EXPAND,
3762 gtk.FILL | gtk.EXPAND,
3763 30,
3764 0,
3765 )
3766 table_navigation.attach(
3767 gtk.Label(),
3768 1,
3769 2,
3770 10,
3771 11,
3772 gtk.FILL | gtk.EXPAND,
3773 gtk.FILL | gtk.EXPAND,
3774 30,
3775 0,
3776 )
3777 table_navigation.attach(
3778 gtk.Label(),
3779 1,
3780 2,
3781 11,
3782 12,
3783 gtk.FILL | gtk.EXPAND,
3784 gtk.FILL | gtk.EXPAND,
3785 0,
3786 0,
3787 )
3788 table_navigation.attach(
3789 gtk.Label(),
3790 1,
3791 2,
3792 12,
3793 13,
3794 gtk.FILL | gtk.EXPAND,
3795 gtk.FILL | gtk.EXPAND,
3796 0,
3797 0,
3798 )
3799 table_navigation.attach(
3800 gtk.Label(),
3801 1,
3802 2,
3803 13,
3804 14,
3805 gtk.FILL | gtk.EXPAND,
3806 gtk.FILL | gtk.EXPAND,
3807 0,
3808 0,
3809 )
3810 # "Slideshow" tab:
3811 table_slideshow = gtk.Table(14, 2, False)
3812 slideshowlabel = gtk.Label()
3813 slideshowlabel.set_markup("<b>" + _("Slideshow Mode") + "</b>")
3814 slideshowlabel.set_alignment(0, 1)
3815 hbox_delay = gtk.HBox()
3816 hbox_delay.pack_start(
3817 gtk.Label(_("Delay between images in seconds:")), False, False, 0
3818 )
3819 spin_adj = gtk.Adjustment(self.slideshow_delay, 0, 50000, 1, 10, 0)
3820 delayspin = gtk.SpinButton(spin_adj, 1.0, 0)
3821 delayspin.set_numeric(True)
3822 hbox_delay.pack_start(delayspin, False, False, 5)
3823 randomize = gtk.CheckButton(_("Randomize order of images"))
3824 randomize.set_active(self.slideshow_random)
3825 randomize.set_tooltip_text(
3826 _(
3827 "If enabled, a random image will be chosen during slideshow mode (without loading any image twice)."
3828 )
3829 )
3830 disable_screensaver = gtk.CheckButton(
3831 _("Disable screensaver in slideshow mode")
3832 )
3833 disable_screensaver.set_active(self.disable_screensaver)
3834 disable_screensaver.set_tooltip_text(
3835 _(
3836 "If enabled, xscreensaver will be temporarily disabled during slideshow mode."
3837 )
3838 )
3839 ss_in_fs = gtk.CheckButton(_("Always start in fullscreen mode"))
3840 ss_in_fs.set_tooltip_text(
3841 _(
3842 "If enabled, starting a slideshow will put the application in fullscreen mode."
3843 )
3844 )
3845 ss_in_fs.set_active(self.slideshow_in_fullscreen)
3846 table_slideshow.attach(
3847 gtk.Label(), 1, 2, 1, 2, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 0, 0
3848 )
3849 table_slideshow.attach(
3850 slideshowlabel,
3851 1,
3852 2,
3853 2,
3854 3,
3855 gtk.FILL | gtk.EXPAND,
3856 gtk.FILL | gtk.EXPAND,
3857 15,
3858 0,
3859 )
3860 table_slideshow.attach(
3861 gtk.Label(), 1, 2, 3, 4, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 0, 0
3862 )
3863 table_slideshow.attach(
3864 hbox_delay, 1, 2, 4, 5, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 30, 0
3865 )
3866 table_slideshow.attach(
3867 gtk.Label(), 1, 2, 5, 6, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 0, 0
3868 )
3869 table_slideshow.attach(
3870 disable_screensaver,
3871 1,
3872 2,
3873 6,
3874 7,
3875 gtk.FILL | gtk.EXPAND,
3876 gtk.FILL | gtk.EXPAND,
3877 30,
3878 0,
3879 )
3880 table_slideshow.attach(
3881 ss_in_fs, 1, 2, 7, 8, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 30, 0
3882 )
3883 table_slideshow.attach(
3884 randomize, 1, 2, 8, 9, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 30, 0
3885 )
3886 table_slideshow.attach(
3887 gtk.Label(), 1, 2, 9, 10, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 0, 0
3888 )
3889 table_slideshow.attach(
3890 gtk.Label(),
3891 1,
3892 2,
3893 10,
3894 11,
3895 gtk.FILL | gtk.EXPAND,
3896 gtk.FILL | gtk.EXPAND,
3897 0,
3898 0,
3899 )
3900 table_slideshow.attach(
3901 gtk.Label(),
3902 1,
3903 2,
3904 11,
3905 12,
3906 gtk.FILL | gtk.EXPAND,
3907 gtk.FILL | gtk.EXPAND,
3908 0,
3909 0,
3910 )
3911 table_slideshow.attach(
3912 gtk.Label(),
3913 1,
3914 2,
3915 12,
3916 13,
3917 gtk.FILL | gtk.EXPAND,
3918 gtk.FILL | gtk.EXPAND,
3919 0,
3920 0,
3921 )
3922 table_slideshow.attach(
3923 gtk.Label(),
3924 1,
3925 2,
3926 13,
3927 14,
3928 gtk.FILL | gtk.EXPAND,
3929 gtk.FILL | gtk.EXPAND,
3930 0,
3931 0,
3932 )
3933 # "Image" tab:
3934 table_image = gtk.Table(14, 2, False)
3935 imagelabel = gtk.Label()
3936 imagelabel.set_markup("<b>" + _("Image Editing") + "</b>")
3937 imagelabel.set_alignment(0, 1)
3938 deletebutton = gtk.CheckButton(_("Confirm image delete"))
3939 deletebutton.set_active(self.confirm_delete)
3940
3941 zoom_hbox = gtk.HBox()
3942 zoom_hbox.pack_start(gtk.Label(_("Scaling quality:")), False, False, 0)
3943 zoomcombo = gtk.combo_box_new_text()
3944 zoomcombo.append_text(_("Nearest (Fastest)"))
3945 zoomcombo.append_text(_("Tiles"))
3946 zoomcombo.append_text(_("Bilinear"))
3947 zoomcombo.append_text(_("Hyper (Best)"))
3948 zoomcombo.set_active(self.zoomvalue)
3949 zoom_hbox.pack_start(zoomcombo, False, False, 0)
3950 zoom_hbox.pack_start(gtk.Label(), True, True, 0)
3951
3952 hbox_save = gtk.HBox()
3953 savelabel = gtk.Label(_("Modified images:"))
3954 savecombo = gtk.combo_box_new_text()
3955 savecombo.append_text(_("Ignore Changes"))
3956 savecombo.append_text(_("Auto-Save"))
3957 savecombo.append_text(_("Prompt For Action"))
3958 savecombo.set_active(self.savemode)
3959 hbox_save.pack_start(savelabel, False, False, 0)
3960 hbox_save.pack_start(savecombo, False, False, 5)
3961
3962 hbox_quality = gtk.HBox()
3963 qualitylabel = gtk.Label(_("Quality to save in:"))
3964 qspin_adj = gtk.Adjustment(self.quality_save, 0, 100, 1, 100, 0)
3965 qualityspin = gtk.SpinButton(qspin_adj, 1.0, 0)
3966 qualityspin.set_numeric(True)
3967 hbox_quality.pack_start(qualitylabel, False, False, 0)
3968 hbox_quality.pack_start(qualityspin, False, False, 5)
3969 table_image.attach(
3970 gtk.Label(), 1, 3, 1, 2, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 30, 0
3971 )
3972 table_image.attach(
3973 imagelabel, 1, 3, 2, 3, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 15, 0
3974 )
3975 table_image.attach(
3976 gtk.Label(), 1, 3, 3, 4, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 30, 0
3977 )
3978 table_image.attach(
3979 zoom_hbox, 1, 3, 4, 5, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 30, 0
3980 )
3981 table_image.attach(
3982 gtk.Label(), 1, 3, 5, 6, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 30, 0
3983 )
3984 table_image.attach(
3985 hbox_save, 1, 3, 6, 7, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 30, 0
3986 )
3987 table_image.attach(
3988 gtk.Label(), 1, 3, 7, 8, gtk.FILL | gtk.EXPAND, gtk.FILL | gtk.EXPAND, 30, 0
3989 )
3990 table_image.attach(
3991 hbox_quality,
3992 1,
3993 3,
3994 8,
3995 9,
3996 gtk.FILL | gtk.EXPAND,
3997 gtk.FILL | gtk.EXPAND,
3998 30,
3999 0,
4000 )
4001 table_image.attach(
4002 gtk.Label(),
4003 1,
4004 3,
4005 9,
4006 10,
4007 gtk.FILL | gtk.EXPAND,
4008 gtk.FILL | gtk.EXPAND,
4009 30,
4010 0,
4011 )
4012 table_image.attach(
4013 deletebutton,
4014 1,
4015 3,
4016 10,
4017 11,
4018 gtk.FILL | gtk.EXPAND,
4019 gtk.FILL | gtk.EXPAND,
4020 30,
4021 0,
4022 )
4023 table_image.attach(
4024 gtk.Label(),
4025 1,
4026 3,
4027 11,
4028 12,
4029 gtk.FILL | gtk.EXPAND,
4030 gtk.FILL | gtk.EXPAND,
4031 30,
4032 0,
4033 )
4034 table_image.attach(
4035 gtk.Label(),
4036 1,
4037 3,
4038 12,
4039 13,
4040 gtk.FILL | gtk.EXPAND,
4041 gtk.FILL | gtk.EXPAND,
4042 30,
4043 0,
4044 )
4045 table_image.attach(
4046 gtk.Label(),
4047 1,
4048 3,
4049 13,
4050 14,
4051 gtk.FILL | gtk.EXPAND,
4052 gtk.FILL | gtk.EXPAND,
4053 30,
4054 0,
4055 )
4056 table_image.attach(
4057 gtk.Label(),
4058 1,
4059 3,
4060 14,
4061 15,
4062 gtk.FILL | gtk.EXPAND,
4063 gtk.FILL | gtk.EXPAND,
4064 30,
4065 0,
4066 )
4067 # Add tabs:
4068 notebook = gtk.Notebook()
4069 notebook.append_page(table_behavior, gtk.Label(_("Behavior")))
4070 notebook.append_page(table_navigation, gtk.Label(_("Navigation")))
4071 notebook.append_page(table_settings, gtk.Label(_("Interface")))
4072 notebook.append_page(table_slideshow, gtk.Label(_("Slideshow")))
4073 notebook.append_page(table_image, gtk.Label(_("Image")))
4074 notebook.set_current_page(0)
4075 hbox = gtk.HBox()
4076 self.prefs_dialog.vbox.pack_start(hbox, False, False, 7)
4077 hbox.pack_start(notebook, False, False, 7)
4078 notebook.connect("switch-page", self.prefs_tab_switched)
4079 # Show prefs:
4080 self.prefs_dialog.vbox.show_all()
4081 self.close_button = self.prefs_dialog.add_button(
4082 gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE
4083 )
4084 self.close_button.grab_focus()
4085 response = self.prefs_dialog.run()
4086 if response == gtk.RESPONSE_CLOSE or response == gtk.RESPONSE_DELETE_EVENT:
4087 self.zoomvalue = zoomcombo.get_active()
4088 if int(round(self.zoomvalue, 0)) == 0:
4089 self.zoom_quality = gtk.gdk.INTERP_NEAREST
4090 elif int(round(self.zoomvalue, 0)) == 1:
4091 self.zoom_quality = gtk.gdk.INTERP_TILES
4092 elif int(round(self.zoomvalue, 0)) == 2:
4093 self.zoom_quality = gtk.gdk.INTERP_BILINEAR
4094 elif int(round(self.zoomvalue, 0)) == 3:
4095 self.zoom_quality = gtk.gdk.INTERP_HYPER
4096 self.open_all_images = openallimages.get_active()
4097 self.open_hidden_files = hiddenimages.get_active()
4098 if openpref1.get_active():
4099 self.use_last_dir = True
4100 else:
4101 self.use_last_dir = False
4102 open_mode_prev = self.open_mode
4103 self.open_mode = combobox.get_active()
4104 preloading_images_prev = self.preloading_images
4105 self.preloading_images = preloadnav.get_active()
4106 self.listwrap_mode = combobox2.get_active()
4107 self.slideshow_delay = delayspin.get_value()
4108 self.curr_slideshow_delay = self.slideshow_delay
4109 self.slideshow_random = randomize.get_active()
4110 self.curr_slideshow_random = self.slideshow_random
4111 self.disable_screensaver = disable_screensaver.get_active()
4112 self.slideshow_in_fullscreen = ss_in_fs.get_active()
4113 self.savemode = savecombo.get_active()
4114 self.start_in_fullscreen = fullscreen.get_active()
4115 self.confirm_delete = deletebutton.get_active()
4116 self.quality_save = qualityspin.get_value()
4117 self.thumbnail_size = int(self.thumbnail_sizes[thumbsize.get_active()])
4118 if self.thumbnail_size != prev_thumbnail_size:
4119 gobject.idle_add(self.thumbpane_set_size)
4120 gobject.idle_add(
4121 self.thumbpane_update_images, True, self.curr_img_in_list
4122 )
4123 self.prefs_dialog.destroy()
4124 self.set_go_navigation_sensitivities(False)
4125 if (self.preloading_images and not preloading_images_prev) or (
4126 open_mode_prev != self.open_mode
4127 ):
4128 # The user just turned on preloading, so do it:
4129 self.preloadimg_next_in_list = -1
4130 self.preloadimg_prev_in_list = -1
4131 self.preload_when_idle = gobject.idle_add(
4132 self.preload_next_image, False
4133 )
4134 self.preload_when_idle2 = gobject.idle_add(
4135 self.preload_prev_image, False
4136 )
4137 elif not self.preloading_images:
4138 self.preloadimg_next_in_list = -1
4139 self.preloadimg_prev_in_list = -1
4140
4141 def prefs_use_fixed_dir_clicked(self, button):
4142 if button.get_active():
4143 self.defaultdir.set_sensitive(True)
4144 else:
4145 self.defaultdir.set_sensitive(False)
4146
4147 def rename_image(self, action):
4148 if len(self.image_list) > 0:
4149 temp_slideshow_mode = self.slideshow_mode
4150 if self.slideshow_mode:
4151 self.toggle_slideshow(None)
4152 rename_dialog = gtk.Dialog(_("Rename Image"), self.window, gtk.DIALOG_MODAL)
4153 self.rename_txt = gtk.Entry()
4154 filename = os.path.basename(self.currimg_name)
4155 self.rename_txt.set_text(filename)
4156 self.rename_txt.set_activates_default(True)
4157 cancelbutton = rename_dialog.add_button(
4158 gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL
4159 )
4160 renamebutton = rename_dialog.add_button(_("_Rename"), gtk.RESPONSE_ACCEPT)
4161 renameimage = gtk.Image()
4162 renameimage.set_from_stock(gtk.STOCK_OK, gtk.ICON_SIZE_BUTTON)
4163 renamebutton.set_image(renameimage)
4164 animtest = gtk.gdk.PixbufAnimation(self.currimg_name)
4165 if animtest.is_static_image():
4166 pixbuf, image_width, image_height = self.get_pixbuf_of_size(
4167 self.currimg_pixbuf_original, 60, self.zoom_quality
4168 )
4169 else:
4170 pixbuf, image_width, image_height = self.get_pixbuf_of_size(
4171 animtest.get_static_image(), 60, self.zoom_quality
4172 )
4173 image = gtk.Image()
4174 image.set_from_pixbuf(pixbuf)
4175 instructions = gtk.Label(_("Enter the new name:"))
4176 instructions.set_alignment(0, 1)
4177 hbox = gtk.HBox()
4178 hbox.pack_start(image, False, False, 10)
4179 vbox_stuff = gtk.VBox()
4180 vbox_stuff.pack_start(gtk.Label(), False, False, 0)
4181 vbox_stuff.pack_start(instructions, False, False, 0)
4182 vbox_stuff.pack_start(gtk.Label(), False, False, 0)
4183 vbox_stuff.pack_start(self.rename_txt, True, True, 0)
4184 vbox_stuff.pack_start(gtk.Label(), False, False, 0)
4185 hbox.pack_start(vbox_stuff, True, True, 10)
4186 rename_dialog.vbox.pack_start(hbox, False, False, 0)
4187 rename_dialog.set_has_separator(True)
4188 rename_dialog.set_default_response(gtk.RESPONSE_ACCEPT)
4189 rename_dialog.set_size_request(300, -1)
4190 rename_dialog.vbox.show_all()
4191 rename_dialog.connect("show", self.select_rename_text)
4192 response = rename_dialog.run()
4193 if response == gtk.RESPONSE_ACCEPT:
4194 try:
4195 new_filename = (
4196 os.path.dirname(self.currimg_name)
4197 + "/"
4198 + self.rename_txt.get_text()
4199 )
4200 shutil.move(self.currimg_name, new_filename)
4201 # Update thumbnail filename:
4202 try:
4203 shutil.move(
4204 self_get_name(self.currimg_name)[1],
4205 self.thumbnail_get_name(new_filename)[1],
4206 )
4207 except:
4208 pass
4209 self.recent_file_remove_and_refresh_name(self.currimg_name)
4210 self.currimg_name = new_filename
4211 self.register_file_with_recent_docs(self.currimg_name)
4212 self.update_title()
4213 except:
4214 error_dialog = gtk.MessageDialog(
4215 self.window,
4216 gtk.DIALOG_MODAL,
4217 gtk.MESSAGE_WARNING,
4218 gtk.BUTTONS_OK,
4219 _("Unable to rename %s") % self.currimg_name,
4220 )
4221 error_dialog.set_title(_("Unable to rename"))
4222 error_dialog.run()
4223 error_dialog.destroy()
4224 rename_dialog.destroy()
4225 if temp_slideshow_mode:
4226 self.toggle_slideshow(None)
4227
4228 def select_rename_text(self, widget):
4229 filename = os.path.basename(self.currimg_name)
4230 fileext = os.path.splitext(os.path.basename(self.currimg_name))[1]
4231 self.rename_txt.select_region(0, len(filename) - len(fileext))
4232
4233 def delete_image(self, action):
4234 if len(self.image_list) > 0:
4235 temp_slideshow_mode = self.slideshow_mode
4236 if self.slideshow_mode:
4237 self.toggle_slideshow(None)
4238 delete_dialog = gtk.Dialog(_("Delete Image"), self.window, gtk.DIALOG_MODAL)
4239 if self.confirm_delete:
4240 permlabel = gtk.Label(
4241 _("Are you sure you wish to permanently delete %s?")
4242 % os.path.split(self.currimg_name)[1]
4243 )
4244 permlabel.set_line_wrap(True)
4245 permlabel.set_alignment(0, 0.1)
4246 warningicon = gtk.Image()
4247 warningicon.set_from_stock(
4248 gtk.STOCK_DIALOG_WARNING, gtk.ICON_SIZE_DIALOG
4249 )
4250 hbox = gtk.HBox()
4251 hbox.pack_start(warningicon, False, False, 10)
4252 hbox.pack_start(permlabel, False, False, 10)
4253 delete_dialog.vbox.pack_start(gtk.Label(), False, False, 0)
4254 delete_dialog.vbox.pack_start(hbox, False, False, 0)
4255 cancelbutton = delete_dialog.add_button(
4256 gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL
4257 )
4258 deletebutton = delete_dialog.add_button(
4259 gtk.STOCK_DELETE, gtk.RESPONSE_YES
4260 )
4261 delete_dialog.set_has_separator(False)
4262 deletebutton.set_property("has-focus", True)
4263 delete_dialog.set_default_response(gtk.RESPONSE_YES)
4264 delete_dialog.vbox.show_all()
4265 response = delete_dialog.run()
4266 else:
4267 response = gtk.RESPONSE_YES
4268 if response == gtk.RESPONSE_YES:
4269 try:
4270 os.remove(self.currimg_name)
4271 self.image_modified = False
4272 try:
4273 os.remove(self.thumbnail_get_name(self.currimg_name)[1])
4274 except:
4275 pass
4276 self.recent_file_remove_and_refresh_name(self.currimg_name)
4277 iter = self.thumblist.get_iter((self.curr_img_in_list,))
4278 try:
4279 self.thumbnail_loaded.pop(self.curr_img_in_list)
4280 self.thumbpane_update_images()
4281 except:
4282 pass
4283 self.thumblist.remove(iter)
4284 templist = self.image_list
4285 self.image_list = []
4286 for item in templist:
4287 if item != self.currimg_name:
4288 self.image_list.append(item)
4289 if len(self.image_list) >= 1:
4290 if len(self.image_list) == 1:
4291 self.curr_img_in_list = 0
4292 elif self.curr_img_in_list == len(self.image_list):
4293 self.curr_img_in_list -= 1
4294 self.change_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
4295 self.preloadimg_prev_in_list = -1
4296 self.preloadimg_next_in_list = -1
4297 self.load_when_idle = gobject.idle_add(
4298 self.load_new_image, False, False, True, True, True, True
4299 )
4300 self.set_go_navigation_sensitivities(False)
4301 else:
4302 self.imageview.clear()
4303 self.update_title()
4304 self.statusbar.push(self.statusbar.get_context_id(""), "")
4305 self.image_loaded = False
4306 self.set_slideshow_sensitivities()
4307 self.set_image_sensitivities(False)
4308 self.set_go_navigation_sensitivities(False)
4309 # Select new item:
4310 self.thumbpane_select(self.curr_img_in_list)
4311 except:
4312 error_dialog = gtk.MessageDialog(
4313 self.window,
4314 gtk.DIALOG_MODAL,
4315 gtk.MESSAGE_WARNING,
4316 gtk.BUTTONS_OK,
4317 _("Unable to delete %s") % self.currimg_name,
4318 )
4319 error_dialog.set_title(_("Unable to delete"))
4320 error_dialog.run()
4321 error_dialog.destroy()
4322 delete_dialog.destroy()
4323 if temp_slideshow_mode:
4324 self.toggle_slideshow(None)
4325
4326 def defaultdir_clicked(self, button):
4327 getdir = gtk.FileChooserDialog(
4328 title=_("Choose directory"),
4329 action=gtk.FILE_CHOOSER_ACTION_OPEN,
4330 buttons=(
4331 gtk.STOCK_CANCEL,
4332 gtk.RESPONSE_CANCEL,
4333 gtk.STOCK_OPEN,
4334 gtk.RESPONSE_OK,
4335 ),
4336 )
4337 getdir.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
4338 getdir.set_filename(self.fixed_dir)
4339 getdir.set_default_response(gtk.RESPONSE_OK)
4340 response = getdir.run()
4341 if response == gtk.RESPONSE_OK:
4342 self.fixed_dir = getdir.get_filenames()[0]
4343 if len(self.fixed_dir) > 25:
4344 button.set_label("..." + self.fixed_dir[-22:])
4345 else:
4346 button.set_label(self.fixed_dir)
4347 getdir.destroy()
4348 else:
4349 getdir.destroy()
4350
4351 def prefs_tab_switched(self, notebook, page, page_num):
4352 do_when_idle = gobject.idle_add(self.grab_close_button)
4353
4354 def grab_close_button(self):
4355 self.close_button.grab_focus()
4356
4357 def bgcolor_selected(self, widget):
4358 # When the user selects a color, store this color in self.bgcolor (which will
4359 # later be saved to .miragerc) and set this background color:
4360 self.bgcolor = widget.get_property("color")
4361 if not self.simple_bgcolor:
4362 self.layout.modify_bg(gtk.STATE_NORMAL, self.bgcolor)
4363 self.slideshow_window.modify_bg(gtk.STATE_NORMAL, self.bgcolor)
4364 self.slideshow_window2.modify_bg(gtk.STATE_NORMAL, self.bgcolor)
4365
4366 def simple_bgcolor_selected(self, widget):
4367 if widget.get_active():
4368 self.simple_bgcolor = True
4369 self.layout.modify_bg(gtk.STATE_NORMAL, None)
4370 else:
4371 self.simple_bgcolor = False
4372 self.bgcolor_selected(self.colorbutton)
4373
4374 def show_about(self, action):
4375 # Help > About
4376 self.about_dialog = gtk.AboutDialog()
4377 try:
4378 self.about_dialog.set_transient_for(self.window)
4379 self.about_dialog.set_modal(True)
4380 except:
4381 pass
4382 self.about_dialog.set_name("Mirage")
4383 self.about_dialog.set_version(__version__)
4384 self.about_dialog.set_comments(_("A fast GTK+ Image Viewer."))
4385 self.about_dialog.set_license(__license__)
4386 self.about_dialog.set_authors(
4387 [
4388 "Scott Horowitz <stonecrest@gmail.com>",
4389 "Fredric Johansson <fredric.miscmail@gmail.com>",
4390 ]
4391 )
4392 self.about_dialog.set_artists(["William Rea <sillywilly@gmail.com>"])
4393 self.about_dialog.set_translator_credits(
4394 "cs - Petr Pisar <petr.pisar@atlas.cz>\nde - Bjoern Martensen <bjoern.martensen@gmail.com>\nes - Isidro Arribas <cdhotfire@gmail.com>\nfr - Mike Massonnet <mmassonnet@gmail.com>\nhu - Sandor Lisovszki <lisovszki@dunakanyar.net>\nnl - Pascal De Vuyst <pascal.devuyst@gmail.com>\npl - Tomasz Dominikowski <dominikowski@gmail.com>\npt_BR - Danilo Martins <mawkee@gmail.com>\nru - mavka <mavka@justos.org>\nit - Daniele Maggio <dado84@freemail.it>\nzh_CN - Jayden Suen <no.sun@163.com>"
4395 )
4396 gtk.about_dialog_set_url_hook(self.show_website, "http://mirageiv.berlios.de")
4397 self.about_dialog.set_website_label("http://mirageiv.berlios.de")
4398 icon_path = self.find_path("mirage.png")
4399 try:
4400 icon_pixbuf = gtk.gdk.pixbuf_new_from_file(icon_path)
4401 self.about_dialog.set_logo(icon_pixbuf)
4402 except:
4403 pass
4404 self.about_dialog.connect("response", self.close_about)
4405 self.about_dialog.connect("delete_event", self.close_about)
4406 self.about_dialog.show_all()
4407
4408 def show_website(self, dialog, blah, link):
4409 self.browser_load(link)
4410
4411 def show_help(self, action):
4412 self.browser_load("http://mirageiv.berlios.de/docs.html")
4413
4414 def browser_load(self, docslink):
4415 try:
4416 pid = subprocess.Popen(["gnome-open", docslink]).pid
4417 except:
4418 try:
4419 pid = subprocess.Popen(["exo-open", docslink]).pid
4420 except:
4421 try:
4422 pid = subprocess.Popen(["kfmclient", "openURL", docslink]).pid
4423 except:
4424 try:
4425 pid = subprocess.Popen(["firefox", docslink]).pid
4426 except:
4427 try:
4428 pid = subprocess.Popen(["mozilla", docslink]).pid
4429 except:
4430 try:
4431 pid = subprocess.Popen(["opera", docslink]).pid
4432 except:
4433 error_dialog = gtk.MessageDialog(
4434 self.window,
4435 gtk.DIALOG_MODAL,
4436 gtk.MESSAGE_WARNING,
4437 gtk.BUTTONS_CLOSE,
4438 _("Unable to launch a suitable browser."),
4439 )
4440 error_dialog.run()
4441 error_dialog.destroy()
4442
4443 def close_about(self, event, data=None):
4444 self.about_dialog.hide()
4445 return True
4446
4447 def mousewheel_scrolled(self, widget, event):
4448 if event.type == gtk.gdk.SCROLL:
4449 # Zooming of the image by Ctrl-mousewheel
4450 if event.state & gtk.gdk.CONTROL_MASK:
4451 if event.direction == gtk.gdk.SCROLL_UP:
4452 self.zoom_in(None)
4453 elif event.direction == gtk.gdk.SCROLL_DOWN:
4454 self.zoom_out(None)
4455 return True
4456 # Navigation of images with mousewheel:
4457 else:
4458 if event.direction == gtk.gdk.SCROLL_UP:
4459 self.goto_prev_image(None)
4460 elif event.direction == gtk.gdk.SCROLL_DOWN:
4461 self.goto_next_image(None)
4462 return True
4463
4464 def mouse_moved(self, widget, event):
4465 # This handles the panning of the image
4466 if event.is_hint:
4467 x, y, state = event.window.get_pointer()
4468 else:
4469 state = event.state
4470 x, y = event.x_root, event.y_root
4471 if (state & gtk.gdk.BUTTON2_MASK) or (state & gtk.gdk.BUTTON1_MASK):
4472 # Prevent self.expose_event() from potentially further changing the
4473 # adjustments upon the adjustment value changes
4474 self.updating_adjustments = True
4475 xadjust = self.layout.get_hadjustment()
4476 newx = xadjust.value + (self.prevmousex - x)
4477 if newx >= xadjust.lower and newx <= xadjust.upper - xadjust.page_size:
4478 xadjust.set_value(newx)
4479 self.layout.set_hadjustment(xadjust)
4480 yadjust = self.layout.get_vadjustment()
4481 newy = yadjust.value + (self.prevmousey - y)
4482 if newy >= yadjust.lower and newy <= yadjust.upper - yadjust.page_size:
4483 yadjust.set_value(newy)
4484 self.layout.set_vadjustment(yadjust)
4485 self.updating_adjustments = False
4486 self.prevmousex = x
4487 self.prevmousey = y
4488 if self.fullscreen_mode:
4489 # Show cursor on movement, then hide after 2 seconds of no movement
4490 self.change_cursor(None)
4491 if not self.slideshow_controls_visible:
4492 gobject.source_remove(self.timer_id)
4493 if not self.closing_app:
4494 while gtk.events_pending():
4495 gtk.main_iteration()
4496 self.timer_id = gobject.timeout_add(2000, self.hide_cursor)
4497 if y > 0.9 * self.available_image_height():
4498 self.slideshow_controls_show()
4499 else:
4500 self.slideshow_controls_hide()
4501 return True
4502
4503 def button_pressed(self, widget, event):
4504 if self.image_loaded:
4505 # Changes the cursor to the 'resize' cursor, like GIMP, on a middle click:
4506 if (event.button == 2 or event.button == 1) and (
4507 self.hscroll.get_property("visible") == True
4508 or self.vscroll.get_property("visible") == True
4509 ):
4510 self.change_cursor(gtk.gdk.Cursor(gtk.gdk.FLEUR))
4511 self.prevmousex = event.x_root
4512 self.prevmousey = event.y_root
4513 # Right-click popup:
4514 elif self.image_loaded and event.button == 3:
4515 self.UIManager.get_widget("/Popup").popup(
4516 None, None, None, event.button, event.time
4517 )
4518 return True
4519
4520 def button_released(self, widget, event):
4521 # Resets the cursor when middle mouse button is released
4522 if event.button == 2 or event.button == 1:
4523 self.change_cursor(None)
4524 return True
4525
4526 def zoom_in(self, action):
4527 if self.currimg_name != "" and self.UIManager.get_widget(
4528 "/MainMenu/ViewMenu/In"
4529 ).get_property("sensitive"):
4530 self.image_zoomed = True
4531 self.currimg_zoomratio = self.currimg_zoomratio * 1.25
4532 self.set_zoom_sensitivities()
4533 self.last_image_action_was_fit = False
4534 self.put_zoom_image_to_window(False)
4535 self.update_statusbar()
4536
4537 def zoom_out(self, action):
4538 if self.currimg_name != "" and self.UIManager.get_widget(
4539 "/MainMenu/ViewMenu/Out"
4540 ).get_property("sensitive"):
4541 if self.currimg_zoomratio == self.min_zoomratio:
4542 # No point in proceeding..
4543 return
4544 self.image_zoomed = True
4545 self.currimg_zoomratio = self.currimg_zoomratio * 1 / 1.25
4546 if self.currimg_zoomratio < self.min_zoomratio:
4547 self.currimg_zoomratio = self.min_zoomratio
4548 self.set_zoom_sensitivities()
4549 self.last_image_action_was_fit = False
4550 self.put_zoom_image_to_window(False)
4551 self.update_statusbar()
4552
4553 def zoom_to_fit_window_action(self, action):
4554 self.zoom_to_fit_window(action, False, False)
4555
4556 def zoom_to_fit_window(self, action, is_preloadimg_next, is_preloadimg_prev):
4557 if is_preloadimg_next:
4558 if self.preloading_images and self.preloadimg_next_in_list != -1:
4559 win_width = self.available_image_width()
4560 win_height = self.available_image_height()
4561 preimg_width = self.preloadimg_next_pixbuf_original.get_width()
4562 preimg_height = self.preloadimg_next_pixbuf_original.get_height()
4563 prewidth_ratio = float(preimg_width) / win_width
4564 preheight_ratio = float(preimg_height) / win_height
4565 if prewidth_ratio < preheight_ratio:
4566 premax_ratio = preheight_ratio
4567 else:
4568 premax_ratio = prewidth_ratio
4569 self.preloadimg_next_zoomratio = 1 / float(max_ratio)
4570 elif is_preloadimg_prev:
4571 if self.preloading_images and self.preloadimg_prev_in_list != -1:
4572 win_width = self.available_image_width()
4573 win_height = self.available_image_height()
4574 preimg_width = self.preloadimg_prev_pixbuf_original.get_width()
4575 preimg_height = self.preloadimg_prev_pixbuf_original.get_height()
4576 prewidth_ratio = float(preimg_width) / win_width
4577 preheight_ratio = float(preimg_height) / win_height
4578 if prewidth_ratio < preheight_ratio:
4579 premax_ratio = preheight_ratio
4580 else:
4581 premax_ratio = prewidth_ratio
4582 self.preloadimg_prev_zoomratio = 1 / float(max_ratio)
4583 else:
4584 if self.currimg_name != "" and (
4585 self.slideshow_mode
4586 or self.UIManager.get_widget("/MainMenu/ViewMenu/Fit").get_property(
4587 "sensitive"
4588 )
4589 ):
4590 self.image_zoomed = True
4591 self.last_mode = self.open_mode_fit
4592 self.last_image_action_was_fit = True
4593 self.last_image_action_was_smart_fit = False
4594 # Calculate zoomratio needed to fit to window:
4595 win_width = self.available_image_width()
4596 win_height = self.available_image_height()
4597 img_width = self.currimg_pixbuf_original.get_width()
4598 img_height = self.currimg_pixbuf_original.get_height()
4599 width_ratio = float(img_width) / win_width
4600 height_ratio = float(img_height) / win_height
4601 if width_ratio < height_ratio:
4602 max_ratio = height_ratio
4603 else:
4604 max_ratio = width_ratio
4605 self.currimg_zoomratio = 1 / float(max_ratio)
4606 self.set_zoom_sensitivities()
4607 self.put_zoom_image_to_window(False)
4608 self.update_statusbar()
4609
4610 def zoom_to_fit_or_1_to_1(self, action, is_preloadimg_next, is_preloadimg_prev):
4611 if is_preloadimg_next:
4612 if self.preloading_images and self.preloadimg_next_in_list != -1:
4613 win_width = self.available_image_width()
4614 win_height = self.available_image_height()
4615 preimg_width = self.preloadimg_next_pixbuf_original.get_width()
4616 preimg_height = self.preloadimg_next_pixbuf_original.get_height()
4617 prewidth_ratio = float(preimg_width) / win_width
4618 preheight_ratio = float(preimg_height) / win_height
4619 if prewidth_ratio < preheight_ratio:
4620 premax_ratio = preheight_ratio
4621 else:
4622 premax_ratio = prewidth_ratio
4623 self.preloadimg_next_zoomratio = 1 / float(premax_ratio)
4624 if self.preloadimg_next_zoomratio > 1:
4625 self.preloadimg_next_zoomratio = 1
4626 elif is_preloadimg_prev:
4627 if self.preloading_images and self.preloadimg_prev_in_list != -1:
4628 win_width = self.available_image_width()
4629 win_height = self.available_image_height()
4630 preimg_width = self.preloadimg_prev_pixbuf_original.get_width()
4631 preimg_height = self.preloadimg_prev_pixbuf_original.get_height()
4632 prewidth_ratio = float(preimg_width) / win_width
4633 preheight_ratio = float(preimg_height) / win_height
4634 if prewidth_ratio < preheight_ratio:
4635 premax_ratio = preheight_ratio
4636 else:
4637 premax_ratio = prewidth_ratio
4638 self.preloadimg_prev_zoomratio = 1 / float(premax_ratio)
4639 if self.preloadimg_prev_zoomratio > 1:
4640 self.preloadimg_prev_zoomratio = 1
4641 else:
4642 if self.currimg_name != "":
4643 self.image_zoomed = True
4644 # Calculate zoomratio needed to fit to window:
4645 win_width = self.available_image_width()
4646 win_height = self.available_image_height()
4647 img_width = self.currimg_pixbuf_original.get_width()
4648 img_height = self.currimg_pixbuf_original.get_height()
4649 width_ratio = float(img_width) / win_width
4650 height_ratio = float(img_height) / win_height
4651 if width_ratio < height_ratio:
4652 max_ratio = height_ratio
4653 else:
4654 max_ratio = width_ratio
4655 self.currimg_zoomratio = 1 / float(max_ratio)
4656 self.set_zoom_sensitivities()
4657 if self.currimg_zoomratio > 1:
4658 # Revert to 1:1 zoom
4659 self.zoom_1_to_1(action, False, False)
4660 else:
4661 self.put_zoom_image_to_window(False)
4662 self.update_statusbar()
4663 self.last_image_action_was_fit = True
4664 self.last_image_action_was_smart_fit = True
4665
4666 def zoom_1_to_1_action(self, action):
4667 self.zoom_1_to_1(action, False, False)
4668
4669 def zoom_1_to_1(self, action, is_preloadimg_next, is_preloadimg_prev):
4670 if is_preloadimg_next:
4671 if self.preloading_images:
4672 self.preloadimg_next_zoomratio = 1
4673 elif is_preloadimg_prev:
4674 if self.preloading_images:
4675 self.preloadimg_prev_zoomratio = 1
4676 else:
4677 if self.currimg_name != "" and (
4678 self.slideshow_mode
4679 or self.currimg_is_animation
4680 or (
4681 not self.currimg_is_animation
4682 and self.UIManager.get_widget(
4683 "/MainMenu/ViewMenu/1:1"
4684 ).get_property("sensitive")
4685 )
4686 ):
4687 self.image_zoomed = True
4688 self.last_mode = self.open_mode_1to1
4689 self.last_image_action_was_fit = False
4690 self.currimg_zoomratio = 1
4691 self.put_zoom_image_to_window(False)
4692 self.update_statusbar()
4693
4694 def rotate_left(self, action):
4695 self.rotate_left_or_right(
4696 self.UIManager.get_widget("/MainMenu/EditMenu/Rotate Left"), 90
4697 )
4698
4699 def rotate_right(self, action):
4700 self.rotate_left_or_right(
4701 self.UIManager.get_widget("/MainMenu/EditMenu/Rotate Right"), 270
4702 )
4703
4704 def rotate_left_or_right(self, widget, angle):
4705 if self.currimg_name != "" and widget.get_property("sensitive"):
4706 self.currimg_pixbuf_original = self.image_rotate(
4707 self.currimg_pixbuf_original, angle
4708 )
4709 if self.last_image_action_was_fit:
4710 if self.last_image_action_was_smart_fit:
4711 self.zoom_to_fit_or_1_to_1(None, False, False)
4712 else:
4713 self.zoom_to_fit_window(None, False, False)
4714 else:
4715 self.currimg_width, self.currimg_height = (
4716 self.currimg_height,
4717 self.currimg_width,
4718 )
4719 self.layout.set_size(self.currimg_width, self.currimg_height)
4720 self.currimg_pixbuf = self.image_rotate(self.currimg_pixbuf, angle)
4721 self.imageview.set_from_pixbuf(self.currimg_pixbuf)
4722 self.show_scrollbars_if_needed()
4723 self.center_image()
4724 self.update_statusbar()
4725 self.image_modified = True
4726
4727 def flip_image_vert(self, action):
4728 self.flip_image_vert_or_horiz(
4729 self.UIManager.get_widget("/MainMenu/EditMenu/Flip Vertically"), True
4730 )
4731
4732 def flip_image_horiz(self, action):
4733 self.flip_image_vert_or_horiz(
4734 self.UIManager.get_widget("/MainMenu/EditMenu/Flip Horizontally"), False
4735 )
4736
4737 def flip_image_vert_or_horiz(self, widget, vertical):
4738 if self.currimg_name != "" and widget.get_property("sensitive"):
4739 self.currimg_pixbuf = self.image_flip(self.currimg_pixbuf, vertical)
4740 self.currimg_pixbuf_original = self.image_flip(
4741 self.currimg_pixbuf_original, vertical
4742 )
4743 self.imageview.set_from_pixbuf(self.currimg_pixbuf)
4744 self.image_modified = True
4745
4746 def get_pixbuf_of_size(self, pixbuf, size, zoom_quality):
4747 # Creates a pixbuf that fits in the specified square of sizexsize
4748 # while preserving the aspect ratio
4749 # Returns tuple: (scaled_pixbuf, actual_width, actual_height)
4750 image_width = pixbuf.get_width()
4751 image_height = pixbuf.get_height()
4752 if image_width - size > image_height - size:
4753 if image_width > size:
4754 image_height = int(size / float(image_width) * image_height)
4755 image_width = size
4756 else:
4757 if image_height > size:
4758 image_width = int(size / float(image_height) * image_width)
4759 image_height = size
4760 if not pixbuf.get_has_alpha():
4761 crop_pixbuf = pixbuf.scale_simple(image_width, image_height, zoom_quality)
4762 else:
4763 colormap = self.imageview.get_colormap()
4764 light_grey = colormap.alloc_color("#666666", True, True)
4765 dark_grey = colormap.alloc_color("#999999", True, True)
4766 crop_pixbuf = pixbuf.composite_color_simple(
4767 image_width,
4768 image_height,
4769 zoom_quality,
4770 255,
4771 8,
4772 light_grey.pixel,
4773 dark_grey.pixel,
4774 )
4775 return (crop_pixbuf, image_width, image_height)
4776
4777 def pixbuf_add_border(self, pix):
4778 # Add a gray outline to pix. This will increase the pixbuf size by
4779 # 2 pixels lengthwise and heightwise, 1 on each side. Returns pixbuf.
4780 try:
4781 width = pix.get_width()
4782 height = pix.get_height()
4783 newpix = gtk.gdk.Pixbuf(
4784 gtk.gdk.COLORSPACE_RGB, True, 8, width + 2, height + 2
4785 )
4786 newpix.fill(0x858585FF)
4787 pix.copy_area(0, 0, width, height, newpix, 1, 1)
4788 return newpix
4789 except:
4790 return pix
4791
4792 def crop_image(self, action):
4793 dialog = gtk.Dialog(
4794 _("Crop Image"),
4795 self.window,
4796 gtk.DIALOG_MODAL,
4797 (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT),
4798 )
4799 cropbutton = dialog.add_button(_("C_rop"), gtk.RESPONSE_ACCEPT)
4800 cropimage = gtk.Image()
4801 cropimage.set_from_stock(gtk.STOCK_OK, gtk.ICON_SIZE_BUTTON)
4802 cropbutton.set_image(cropimage)
4803 image = gtk.DrawingArea()
4804 crop_pixbuf, image_width, image_height = self.get_pixbuf_of_size(
4805 self.currimg_pixbuf_original, 400, self.zoom_quality
4806 )
4807 image.set_size_request(image_width, image_height)
4808 hbox = gtk.HBox()
4809 hbox.pack_start(gtk.Label(), expand=True)
4810 hbox.pack_start(image, expand=False)
4811 hbox.pack_start(gtk.Label(), expand=True)
4812 vbox_left = gtk.VBox()
4813 x_adj = gtk.Adjustment(0, 0, self.currimg_pixbuf_original.get_width(), 1, 10, 0)
4814 x = gtk.SpinButton(x_adj, 0, 0)
4815 x.set_numeric(True)
4816 x.set_update_policy(gtk.UPDATE_IF_VALID)
4817 x.set_wrap(False)
4818 x_label = gtk.Label("X:")
4819 x_label.set_alignment(0, 0.7)
4820 y_adj = gtk.Adjustment(
4821 0, 0, self.currimg_pixbuf_original.get_height(), 1, 10, 0
4822 )
4823 y = gtk.SpinButton(y_adj, 0, 0)
4824 y.set_numeric(True)
4825 y.set_update_policy(gtk.UPDATE_IF_VALID)
4826 y.set_wrap(False)
4827 y_label = gtk.Label("Y:")
4828 x_label.set_size_request(y_label.size_request()[0], -1)
4829 hbox_x = gtk.HBox()
4830 hbox_y = gtk.HBox()
4831 hbox_x.pack_start(x_label, False, False, 10)
4832 hbox_x.pack_start(x, False, False, 0)
4833 hbox_x.pack_start(gtk.Label(), False, False, 3)
4834 hbox_y.pack_start(y_label, False, False, 10)
4835 hbox_y.pack_start(y, False, False, 0)
4836 hbox_y.pack_start(gtk.Label(), False, False, 3)
4837 vbox_left.pack_start(hbox_x, False, False, 0)
4838 vbox_left.pack_start(hbox_y, False, False, 0)
4839 vbox_right = gtk.VBox()
4840 width_adj = gtk.Adjustment(
4841 self.currimg_pixbuf_original.get_width(),
4842 1,
4843 self.currimg_pixbuf_original.get_width(),
4844 1,
4845 10,
4846 0,
4847 )
4848 width = gtk.SpinButton(width_adj, 0, 0)
4849 width.set_numeric(True)
4850 width.set_update_policy(gtk.UPDATE_IF_VALID)
4851 width.set_wrap(False)
4852 width_label = gtk.Label(_("Width:"))
4853 width_label.set_alignment(0, 0.7)
4854 height_adj = gtk.Adjustment(
4855 self.currimg_pixbuf_original.get_height(),
4856 1,
4857 self.currimg_pixbuf_original.get_height(),
4858 1,
4859 10,
4860 0,
4861 )
4862 height = gtk.SpinButton(height_adj, 0, 0)
4863 height.set_numeric(True)
4864 height.set_update_policy(gtk.UPDATE_IF_VALID)
4865 height.set_wrap(False)
4866 height_label = gtk.Label(_("Height:"))
4867 width_label.set_size_request(height_label.size_request()[0], -1)
4868 height_label.set_alignment(0, 0.7)
4869 hbox_width = gtk.HBox()
4870 hbox_height = gtk.HBox()
4871 hbox_width.pack_start(width_label, False, False, 10)
4872 hbox_width.pack_start(width, False, False, 0)
4873 hbox_height.pack_start(height_label, False, False, 10)
4874 hbox_height.pack_start(height, False, False, 0)
4875 vbox_right.pack_start(hbox_width, False, False, 0)
4876 vbox_right.pack_start(hbox_height, False, False, 0)
4877 hbox2 = gtk.HBox()
4878 hbox2.pack_start(gtk.Label(), expand=True)
4879 hbox2.pack_start(vbox_left, False, False, 0)
4880 hbox2.pack_start(vbox_right, False, False, 0)
4881 hbox2.pack_start(gtk.Label(), expand=True)
4882 dialog.vbox.pack_start(hbox, False, False, 0)
4883 dialog.vbox.pack_start(hbox2, False, False, 15)
4884 dialog.set_resizable(False)
4885 dialog.vbox.show_all()
4886 image.set_events(
4887 gtk.gdk.POINTER_MOTION_MASK
4888 | gtk.gdk.POINTER_MOTION_HINT_MASK
4889 | gtk.gdk.BUTTON_PRESS_MASK
4890 | gtk.gdk.BUTTON_MOTION_MASK
4891 | gtk.gdk.BUTTON_RELEASE_MASK
4892 )
4893 image.connect(
4894 "expose-event",
4895 self.crop_image_expose_cb,
4896 crop_pixbuf,
4897 image_width,
4898 image_height,
4899 )
4900 image.connect(
4901 "motion_notify_event",
4902 self.crop_image_mouse_moved,
4903 image,
4904 0,
4905 0,
4906 x,
4907 y,
4908 width,
4909 height,
4910 image_width,
4911 image_height,
4912 width_adj,
4913 height_adj,
4914 )
4915 image.connect("button_press_event", self.crop_image_button_press, image)
4916 image.connect("button_release_event", self.crop_image_button_release)
4917 self.x_changed = x.connect(
4918 "value-changed",
4919 self.crop_value_changed,
4920 x,
4921 y,
4922 width,
4923 height,
4924 width_adj,
4925 height_adj,
4926 image_width,
4927 image_height,
4928 image,
4929 0,
4930 )
4931 self.y_changed = y.connect(
4932 "value-changed",
4933 self.crop_value_changed,
4934 x,
4935 y,
4936 width,
4937 height,
4938 width_adj,
4939 height_adj,
4940 image_width,
4941 image_height,
4942 image,
4943 1,
4944 )
4945 self.width_changed = width.connect(
4946 "value-changed",
4947 self.crop_value_changed,
4948 x,
4949 y,
4950 width,
4951 height,
4952 width_adj,
4953 height_adj,
4954 image_width,
4955 image_height,
4956 image,
4957 2,
4958 )
4959 self.height_changed = height.connect(
4960 "value-changed",
4961 self.crop_value_changed,
4962 x,
4963 y,
4964 width,
4965 height,
4966 width_adj,
4967 height_adj,
4968 image_width,
4969 image_height,
4970 image,
4971 3,
4972 )
4973 image.realize()
4974 self.crop_rectangle = [0, 0]
4975 self.drawing_crop_rectangle = False
4976 self.update_rectangle = False
4977 self.rect = None
4978 response = dialog.run()
4979 if response == gtk.RESPONSE_ACCEPT:
4980 dialog.destroy()
4981 if self.rect != None:
4982 temp_pixbuf = gtk.gdk.Pixbuf(
4983 gtk.gdk.COLORSPACE_RGB,
4984 self.currimg_pixbuf_original.get_has_alpha(),
4985 8,
4986 self.coords[2],
4987 self.coords[3],
4988 )
4989 self.currimg_pixbuf_original.copy_area(
4990 self.coords[0],
4991 self.coords[1],
4992 self.coords[2],
4993 self.coords[3],
4994 temp_pixbuf,
4995 0,
4996 0,
4997 )
4998 self.currimg_pixbuf_original = temp_pixbuf
4999 del temp_pixbuf
5000 gc.collect()
5001 self.load_new_image2(False, True, False, False)
5002 self.image_modified = True
5003 else:
5004 dialog.destroy()
5005
5006 def crop_value_changed(
5007 self,
5008 currspinbox,
5009 x,
5010 y,
5011 width,
5012 height,
5013 width_adj,
5014 height_adj,
5015 image_width,
5016 image_height,
5017 image,
5018 type,
5019 ):
5020 if type == 0: # X
5021 if (
5022 x.get_value() + width.get_value()
5023 > self.currimg_pixbuf_original.get_width()
5024 ):
5025 width.handler_block(self.width_changed)
5026 width.set_value(
5027 self.currimg_pixbuf_original.get_width() - x.get_value()
5028 )
5029 width.handler_unblock(self.width_changed)
5030 elif type == 1: # Y
5031 if (
5032 y.get_value() + height.get_value()
5033 > self.currimg_pixbuf_original.get_height()
5034 ):
5035 height.handler_block(self.height_changed)
5036 height.set_value(
5037 self.currimg_pixbuf_original.get_height() - y.get_value()
5038 )
5039 height.handler_unblock(self.height_changed)
5040 self.coords = [
5041 int(x.get_value()),
5042 int(y.get_value()),
5043 int(width.get_value()),
5044 int(height.get_value()),
5045 ]
5046 self.crop_rectangle[0] = int(
5047 round(
5048 float(self.coords[0])
5049 / self.currimg_pixbuf_original.get_width()
5050 * image_width,
5051 0,
5052 )
5053 )
5054 self.crop_rectangle[1] = int(
5055 round(
5056 float(self.coords[1])
5057 / self.currimg_pixbuf_original.get_height()
5058 * image_height,
5059 0,
5060 )
5061 )
5062 x2 = (
5063 int(
5064 round(
5065 float(self.coords[2])
5066 / self.currimg_pixbuf_original.get_width()
5067 * image_width,
5068 0,
5069 )
5070 )
5071 + self.crop_rectangle[0]
5072 )
5073 y2 = (
5074 int(
5075 round(
5076 float(self.coords[3])
5077 / self.currimg_pixbuf_original.get_height()
5078 * image_height,
5079 0,
5080 )
5081 )
5082 + self.crop_rectangle[1]
5083 )
5084 self.drawing_crop_rectangle = True
5085 self.update_rectangle = True
5086 self.crop_image_mouse_moved(
5087 None,
5088 None,
5089 image,
5090 x2,
5091 y2,
5092 x,
5093 y,
5094 width,
5095 height,
5096 image_width,
5097 image_height,
5098 width_adj,
5099 height_adj,
5100 )
5101 self.update_rectangle = False
5102 self.drawing_crop_rectangle = False
5103
5104 def crop_image_expose_cb(self, image, event, pixbuf, width, height):
5105 image.window.draw_pixbuf(None, pixbuf, 0, 0, 0, 0, width, height)
5106
5107 def crop_image_mouse_moved(
5108 self,
5109 widget,
5110 event,
5111 image,
5112 x2,
5113 y2,
5114 x,
5115 y,
5116 width,
5117 height,
5118 image_width,
5119 image_height,
5120 width_adj,
5121 height_adj,
5122 ):
5123 if event != None:
5124 x2, y2, state = event.window.get_pointer()
5125 if self.drawing_crop_rectangle:
5126 if self.crop_rectangle != None or self.update_rectangle:
5127 gc = image.window.new_gc(function=gtk.gdk.INVERT)
5128 if self.rect != None:
5129 # Get rid of the previous drawn rectangle:
5130 image.window.draw_rectangle(
5131 gc,
5132 False,
5133 self.rect[0],
5134 self.rect[1],
5135 self.rect[2],
5136 self.rect[3],
5137 )
5138 self.rect = [0, 0, 0, 0]
5139 if self.crop_rectangle[0] > x2:
5140 self.rect[0] = x2
5141 self.rect[2] = self.crop_rectangle[0] - x2
5142 else:
5143 self.rect[0] = self.crop_rectangle[0]
5144 self.rect[2] = x2 - self.crop_rectangle[0]
5145 if self.crop_rectangle[1] > y2:
5146 self.rect[1] = y2
5147 self.rect[3] = self.crop_rectangle[1] - y2
5148 else:
5149 self.rect[1] = self.crop_rectangle[1]
5150 self.rect[3] = y2 - self.crop_rectangle[1]
5151 image.window.draw_rectangle(
5152 gc, False, self.rect[0], self.rect[1], self.rect[2], self.rect[3]
5153 )
5154 # Convert the rectangle coordinates of the current image
5155 # to coordinates of pixbuf_original
5156 if self.rect[0] < 0:
5157 self.rect[2] = self.rect[2] + self.rect[0]
5158 self.rect[0] = 0
5159 if self.rect[1] < 0:
5160 self.rect[3] = self.rect[3] + self.rect[1]
5161 self.rect[1] = 0
5162 if event != None:
5163 self.coords = [0, 0, 0, 0]
5164 self.coords[0] = int(
5165 round(
5166 float(self.rect[0])
5167 / image_width
5168 * self.currimg_pixbuf_original.get_width(),
5169 0,
5170 )
5171 )
5172 self.coords[1] = int(
5173 round(
5174 float(self.rect[1])
5175 / image_height
5176 * self.currimg_pixbuf_original.get_height(),
5177 0,
5178 )
5179 )
5180 self.coords[2] = int(
5181 round(
5182 float(self.rect[2])
5183 / image_width
5184 * self.currimg_pixbuf_original.get_width(),
5185 0,
5186 )
5187 )
5188 self.coords[3] = int(
5189 round(
5190 float(self.rect[3])
5191 / image_height
5192 * self.currimg_pixbuf_original.get_height(),
5193 0,
5194 )
5195 )
5196 if (
5197 self.coords[0] + self.coords[2]
5198 > self.currimg_pixbuf_original.get_width()
5199 ):
5200 self.coords[2] = (
5201 self.currimg_pixbuf_original.get_width() - self.coords[0]
5202 )
5203 if (
5204 self.coords[1] + self.coords[3]
5205 > self.currimg_pixbuf_original.get_height()
5206 ):
5207 self.coords[3] = (
5208 self.currimg_pixbuf_original.get_height() - self.coords[1]
5209 )
5210 x.handler_block(self.x_changed)
5211 y.handler_block(self.y_changed)
5212 width.handler_block(self.width_changed)
5213 height.handler_block(self.height_changed)
5214 x.set_value(self.coords[0])
5215 y.set_value(self.coords[1])
5216 width.set_value(self.coords[2])
5217 height.set_value(self.coords[3])
5218 x.handler_unblock(self.x_changed)
5219 y.handler_unblock(self.y_changed)
5220 width_adj.set_property(
5221 "upper", self.currimg_pixbuf_original.get_width() - self.coords[0]
5222 )
5223 height_adj.set_property(
5224 "upper", self.currimg_pixbuf_original.get_height() - self.coords[1]
5225 )
5226 width.handler_unblock(self.width_changed)
5227 height.handler_unblock(self.height_changed)
5228
5229 def crop_image_button_press(self, widget, event, image):
5230 x, y, state = event.window.get_pointer()
5231 if state & gtk.gdk.BUTTON1_MASK:
5232 self.drawing_crop_rectangle = True
5233 self.crop_rectangle = [x, y]
5234 gc = image.window.new_gc(function=gtk.gdk.INVERT)
5235 if self.rect != None:
5236 # Get rid of the previous drawn rectangle:
5237 image.window.draw_rectangle(
5238 gc, False, self.rect[0], self.rect[1], self.rect[2], self.rect[3]
5239 )
5240 self.rect = None
5241
5242 def crop_image_button_release(self, widget, event):
5243 x, y, state = event.window.get_pointer()
5244 if not (state & gtk.gdk.BUTTON1_MASK):
5245 self.drawing_crop_rectangle = False
5246
5247 def saturation(self, action):
5248 dialog = gtk.Dialog(
5249 _("Saturation"),
5250 self.window,
5251 gtk.DIALOG_MODAL,
5252 (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT),
5253 )
5254 resizebutton = dialog.add_button(_("_Saturate"), gtk.RESPONSE_ACCEPT)
5255 resizeimage = gtk.Image()
5256 resizeimage.set_from_stock(gtk.STOCK_OK, gtk.ICON_SIZE_BUTTON)
5257 resizebutton.set_image(resizeimage)
5258 scale = gtk.HScale()
5259 scale.set_draw_value(False)
5260 scale.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
5261 scale.set_range(0, 2)
5262 scale.set_increments(0.1, 0.5)
5263 scale.set_value(1)
5264 scale.connect("value-changed", self.saturation_preview)
5265 label = gtk.Label(_("Saturation level:"))
5266 label.set_alignment(0, 0.5)
5267 hbox1 = gtk.HBox()
5268 hbox1.pack_start(label, True, True, 10)
5269 hbox2 = gtk.HBox()
5270 hbox2.pack_start(scale, True, True, 20)
5271 dialog.vbox.pack_start(gtk.Label(" "))
5272 dialog.vbox.pack_start(hbox1, False)
5273 dialog.vbox.pack_start(hbox2, True, True, 10)
5274 dialog.vbox.pack_start(gtk.Label(" "))
5275 dialog.set_default_response(gtk.RESPONSE_ACCEPT)
5276 dialog.vbox.show_all()
5277 response = dialog.run()
5278 if response == gtk.RESPONSE_ACCEPT:
5279 self.currimg_pixbuf_original.saturate_and_pixelate(
5280 self.currimg_pixbuf_original, scale.get_value(), False
5281 )
5282 self.currimg_pixbuf.saturate_and_pixelate(
5283 self.currimg_pixbuf, scale.get_value(), False
5284 )
5285 self.imageview.set_from_pixbuf(self.currimg_pixbuf)
5286 self.image_modified = True
5287 dialog.destroy()
5288 else:
5289 self.imageview.set_from_pixbuf(self.currimg_pixbuf)
5290 dialog.destroy()
5291
5292 def saturation_preview(self, range):
5293 while gtk.events_pending():
5294 gtk.main_iteration()
5295 try:
5296 bak = self.currimg_pixbuf.copy()
5297 self.currimg_pixbuf.saturate_and_pixelate(
5298 self.currimg_pixbuf, range.get_value(), False
5299 )
5300 self.imageview.set_from_pixbuf(self.currimg_pixbuf)
5301 self.currimg_pixbuf = bak.copy()
5302 del bak
5303 except:
5304 pass
5305 gc.collect()
5306
5307 def resize_image(self, action):
5308 dialog = gtk.Dialog(
5309 _("Resize Image"),
5310 self.window,
5311 gtk.DIALOG_MODAL,
5312 (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT),
5313 )
5314 resizebutton = dialog.add_button(_("_Resize"), gtk.RESPONSE_ACCEPT)
5315 resizeimage = gtk.Image()
5316 resizeimage.set_from_stock(gtk.STOCK_OK, gtk.ICON_SIZE_BUTTON)
5317 resizebutton.set_image(resizeimage)
5318 hbox_width = gtk.HBox()
5319 width_adj = gtk.Adjustment(
5320 self.currimg_pixbuf_original.get_width(), 1, 100000000000, 1, 10, 0
5321 )
5322 width = gtk.SpinButton(width_adj, 0, 0)
5323 width.set_numeric(True)
5324 width.set_update_policy(gtk.UPDATE_IF_VALID)
5325 width.set_wrap(False)
5326 width_label = gtk.Label(_("Width:"))
5327 width_label.set_alignment(0, 0.7)
5328 hbox_width.pack_start(width_label, False, False, 10)
5329 hbox_width.pack_start(width, False, False, 0)
5330 hbox_width.pack_start(gtk.Label(_("pixels")), False, False, 10)
5331 hbox_height = gtk.HBox()
5332 height_adj = gtk.Adjustment(
5333 self.currimg_pixbuf_original.get_height(), 1, 100000000000, 1, 10, 0
5334 )
5335 height = gtk.SpinButton(height_adj, 0, 0)
5336 height.set_numeric(True)
5337 height.set_update_policy(gtk.UPDATE_IF_VALID)
5338 height.set_wrap(False)
5339 height_label = gtk.Label(_("Height:"))
5340 width_label.set_size_request(height_label.size_request()[0], -1)
5341 height_label.set_alignment(0, 0.7)
5342 hbox_height.pack_start(height_label, False, False, 10)
5343 hbox_height.pack_start(height, False, False, 0)
5344 hbox_height.pack_start(gtk.Label(_("pixels")), False, False, 10)
5345 hbox_aspect = gtk.HBox()
5346 aspect_checkbox = gtk.CheckButton(_("Preserve aspect ratio"))
5347 aspect_checkbox.set_active(self.preserve_aspect)
5348 hbox_aspect.pack_start(aspect_checkbox, False, False, 10)
5349 vbox = gtk.VBox()
5350 vbox.pack_start(gtk.Label(), False, False, 0)
5351 vbox.pack_start(hbox_width, False, False, 0)
5352 vbox.pack_start(hbox_height, False, False, 0)
5353 vbox.pack_start(gtk.Label(), False, False, 0)
5354 vbox.pack_start(hbox_aspect, False, False, 0)
5355 vbox.pack_start(gtk.Label(), False, False, 0)
5356 hbox_total = gtk.HBox()
5357 animtest = gtk.gdk.PixbufAnimation(self.currimg_name)
5358 if animtest.is_static_image():
5359 pixbuf, image_width, image_height = self.get_pixbuf_of_size(
5360 self.currimg_pixbuf_original, 96, self.zoom_quality
5361 )
5362 else:
5363 pixbuf, image_width, image_height = self.get_pixbuf_of_size(
5364 animtest.get_static_image(), 96, self.zoom_quality
5365 )
5366 image = gtk.Image()
5367 image.set_from_pixbuf(self.pixbuf_add_border(pixbuf))
5368 hbox_total.pack_start(image, False, False, 10)
5369 hbox_total.pack_start(vbox, False, False, 10)
5370 dialog.vbox.pack_start(hbox_total, False, False, 0)
5371 width.connect("value-changed", self.preserve_image_aspect, "width", height)
5372 height.connect("value-changed", self.preserve_image_aspect, "height", width)
5373 aspect_checkbox.connect("toggled", self.aspect_ratio_toggled, width, height)
5374 dialog.set_default_response(gtk.RESPONSE_ACCEPT)
5375 dialog.vbox.show_all()
5376 response = dialog.run()
5377 if response == gtk.RESPONSE_ACCEPT:
5378 pixelheight = height.get_value_as_int()
5379 pixelwidth = width.get_value_as_int()
5380 dialog.destroy()
5381 self.currimg_pixbuf_original = self.currimg_pixbuf_original.scale_simple(
5382 pixelwidth, pixelheight, self.zoom_quality
5383 )
5384 self.load_new_image2(False, True, False, False)
5385 self.image_modified = True
5386 else:
5387 dialog.destroy()
5388
5389 def aspect_ratio_toggled(self, togglebutton, width, height):
5390 self.preserve_aspect = togglebutton.get_active()
5391 if self.preserve_aspect:
5392 # Set height based on width and aspect ratio
5393 target_value = (
5394 float(width.get_value_as_int())
5395 / self.currimg_pixbuf_original.get_width()
5396 )
5397 target_value = int(target_value * self.currimg_pixbuf_original.get_height())
5398 self.ignore_preserve_aspect_callback = True
5399 height.set_value(target_value)
5400 self.ignore_preserve_aspect_callback = False
5401
5402 def preserve_image_aspect(self, currspinbox, type, otherspinbox):
5403 if not self.preserve_aspect:
5404 return
5405 if self.ignore_preserve_aspect_callback:
5406 return
5407 if type == "width":
5408 target_value = (
5409 float(currspinbox.get_value_as_int())
5410 / self.currimg_pixbuf_original.get_width()
5411 )
5412 target_value = int(target_value * self.currimg_pixbuf_original.get_height())
5413 else:
5414 target_value = (
5415 float(currspinbox.get_value_as_int())
5416 / self.currimg_pixbuf_original.get_height()
5417 )
5418 target_value = int(target_value * self.currimg_pixbuf_original.get_width())
5419 self.ignore_preserve_aspect_callback = True
5420 otherspinbox.set_value(target_value)
5421 self.ignore_preserve_aspect_callback = False
5422
5423 def goto_prev_image(self, action):
5424 self.goto_image("PREV", action)
5425
5426 def goto_next_image(self, action):
5427 self.goto_image("NEXT", action)
5428
5429 def goto_random_image(self, action):
5430 self.goto_image("RANDOM", action)
5431
5432 def goto_first_image(self, action):
5433 self.goto_image("FIRST", action)
5434
5435 def goto_last_image(self, action):
5436 self.goto_image("LAST", action)
5437
5438 def goto_image(self, location, action):
5439 # location can be "LAST", "FIRST", "NEXT", "PREV", "RANDOM", or a number
5440 if self.slideshow_mode and action != "ss":
5441 gobject.source_remove(self.timer_delay)
5442 if (
5443 (
5444 (location == "PREV" or location == "NEXT" or location == "RANDOM")
5445 and len(self.image_list) > 1
5446 )
5447 or (
5448 location == "FIRST"
5449 and (len(self.image_list) > 1 and self.curr_img_in_list != 0)
5450 )
5451 or (
5452 location == "LAST"
5453 and (
5454 len(self.image_list) > 1
5455 and self.curr_img_in_list != len(self.image_list) - 1
5456 )
5457 )
5458 or valid_int(location)
5459 ):
5460 self.load_new_image_stop_now()
5461 cancel = self.autosave_image()
5462 if cancel:
5463 return
5464 check_wrap = False
5465 if location != "RANDOM":
5466 self.randomlist = []
5467 if location == "FIRST":
5468 self.curr_img_in_list = 0
5469 elif location == "RANDOM":
5470 if self.randomlist == []:
5471 self.reinitialize_randomlist()
5472 else:
5473 # check if we have seen every image; if so, reinitialize array and repeat:
5474 all_items_are_true = True
5475 for item in self.randomlist:
5476 if not item:
5477 all_items_are_true = False
5478 if all_items_are_true:
5479 if not self.slideshow_mode or (
5480 self.slideshow_mode and self.listwrap_mode == 1
5481 ):
5482 self.reinitialize_randomlist()
5483 else:
5484 check_wrap = True
5485 elif location == "LAST":
5486 self.curr_img_in_list = len(self.image_list) - 1
5487 elif location == "PREV":
5488 if self.curr_img_in_list > 0:
5489 self.curr_img_in_list -= 1
5490 else:
5491 check_wrap = True
5492 elif location == "NEXT":
5493 if self.curr_img_in_list < len(self.image_list) - 1:
5494 self.curr_img_in_list += 1
5495 else:
5496 check_wrap = True
5497 if check_wrap:
5498 if self.listwrap_mode == 0:
5499 if location == "NEXT":
5500 if self.slideshow_mode:
5501 self.toggle_slideshow(None)
5502 return
5503 elif (
5504 location == "PREV" or location == "NEXT"
5505 ) and self.listwrap_mode == 1:
5506 if location == "PREV":
5507 self.curr_img_in_list = len(self.image_list) - 1
5508 elif location == "NEXT":
5509 self.curr_img_in_list = 0
5510 else:
5511 if self.curr_img_in_list != self.loaded_img_in_list:
5512 # Ensure that the user is looking at the correct "last" image before
5513 # they are asked the wrap question:
5514 if location == "PREV":
5515 self.load_new_image(True, False, True, True, True, True)
5516 else:
5517 self.load_new_image(False, False, True, True, True, True)
5518 self.set_go_navigation_sensitivities(False)
5519 self.thumbpane_select(self.curr_img_in_list)
5520 if self.fullscreen_mode:
5521 self.change_cursor(None)
5522 if location == "PREV":
5523 dialog = gtk.MessageDialog(
5524 self.window,
5525 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
5526 gtk.MESSAGE_QUESTION,
5527 gtk.BUTTONS_YES_NO,
5528 _(
5529 "You are viewing the first image in the list. Wrap around to the last image?"
5530 ),
5531 )
5532 elif location == "NEXT":
5533 dialog = gtk.MessageDialog(
5534 self.window,
5535 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
5536 gtk.MESSAGE_QUESTION,
5537 gtk.BUTTONS_YES_NO,
5538 _(
5539 "You are viewing the last image in the list. Wrap around to the first image?"
5540 ),
5541 )
5542 elif location == "RANDOM":
5543 dialog = gtk.MessageDialog(
5544 self.window,
5545 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
5546 gtk.MESSAGE_QUESTION,
5547 gtk.BUTTONS_YES_NO,
5548 _(
5549 "All images have been viewed. Would you like to cycle through the images again?"
5550 ),
5551 )
5552 dialog.set_title(_("Wrap?"))
5553 dialog.label.set_property("can-focus", False)
5554 dialog.set_default_response(gtk.RESPONSE_YES)
5555 self.user_prompt_visible = True
5556 response = dialog.run()
5557 if response == gtk.RESPONSE_YES:
5558 if location == "PREV":
5559 self.curr_img_in_list = len(self.image_list) - 1
5560 elif location == "NEXT":
5561 self.curr_img_in_list = 0
5562 elif location == "RANDOM":
5563 self.reinitialize_randomlist()
5564 dialog.destroy()
5565 self.user_prompt_visible = False
5566 if self.fullscreen_mode:
5567 self.hide_cursor
5568 else:
5569 dialog.destroy()
5570 self.user_prompt_visible = False
5571 if self.fullscreen_mode:
5572 self.hide_cursor
5573 else:
5574 self.change_cursor(None)
5575 if self.slideshow_mode:
5576 self.toggle_slideshow(None)
5577 return
5578 if location == "RANDOM":
5579 # Find random image that hasn't already been chosen:
5580 j = random.randint(0, len(self.image_list) - 1)
5581 while self.randomlist[j]:
5582 j = random.randint(0, len(self.image_list) - 1)
5583 self.curr_img_in_list = j
5584 self.randomlist[j] = True
5585 self.currimg_name = str(self.image_list[self.curr_img_in_list])
5586 if valid_int(location):
5587 prev_img = self.curr_img_in_list
5588 self.curr_img_in_list = int(location)
5589 if not self.fullscreen_mode and (
5590 not self.slideshow_mode or (self.slideshow_mode and action != "ss")
5591 ):
5592 self.change_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
5593 if location == "PREV" or (
5594 valid_int(location) and int(location) == prev_img - 1
5595 ):
5596 self.load_when_idle = gobject.idle_add(
5597 self.load_new_image, True, False, True, True, True, True
5598 )
5599 else:
5600 self.load_when_idle = gobject.idle_add(
5601 self.load_new_image, False, False, True, True, True, True
5602 )
5603 self.set_go_navigation_sensitivities(False)
5604 if self.slideshow_mode:
5605 if self.curr_slideshow_random:
5606 self.timer_delay = gobject.timeout_add(
5607 int(self.curr_slideshow_delay * 1000),
5608 self.goto_random_image,
5609 "ss",
5610 )
5611 else:
5612 self.timer_delay = gobject.timeout_add(
5613 int(self.curr_slideshow_delay * 1000),
5614 self.goto_next_image,
5615 "ss",
5616 )
5617 gobject.idle_add(self.thumbpane_select, self.curr_img_in_list)
5618
5619 def set_go_navigation_sensitivities(self, skip_initial_check):
5620 # setting skip_image_list_check to True is useful when calling from
5621 # expand_filelist_and_load_image() for example, as self.image_list has not
5622 # yet fully populated
5623 if (
5624 not self.image_loaded or len(self.image_list) == 1
5625 ) and not skip_initial_check:
5626 self.set_previous_image_sensitivities(False)
5627 self.set_first_image_sensitivities(False)
5628 self.set_next_image_sensitivities(False)
5629 self.set_last_image_sensitivities(False)
5630 self.set_random_image_sensitivities(False)
5631 elif self.curr_img_in_list == 0:
5632 if self.listwrap_mode == 0:
5633 self.set_previous_image_sensitivities(False)
5634 else:
5635 self.set_previous_image_sensitivities(True)
5636 self.set_first_image_sensitivities(False)
5637 self.set_next_image_sensitivities(True)
5638 self.set_last_image_sensitivities(True)
5639 self.set_random_image_sensitivities(True)
5640 elif self.curr_img_in_list == len(self.image_list) - 1:
5641 self.set_previous_image_sensitivities(True)
5642 self.set_first_image_sensitivities(True)
5643 if self.listwrap_mode == 0:
5644 self.set_next_image_sensitivities(False)
5645 else:
5646 self.set_next_image_sensitivities(True)
5647 self.set_last_image_sensitivities(False)
5648 self.set_random_image_sensitivities(True)
5649 else:
5650 self.set_previous_image_sensitivities(True)
5651 self.set_first_image_sensitivities(True)
5652 self.set_next_image_sensitivities(True)
5653 self.set_last_image_sensitivities(True)
5654 self.set_random_image_sensitivities(True)
5655
5656 def reinitialize_randomlist(self):
5657 self.randomlist = []
5658 for i in range(len(self.image_list)):
5659 self.randomlist.append(False)
5660 self.randomlist[self.curr_img_in_list] = True
5661
5662 def image_load_failed(self, reset_cursor, filename=""):
5663 # If a filename is provided, use it for display:
5664 if len(filename) == 0:
5665 self.currimg_name = str(self.image_list[self.curr_img_in_list])
5666 else:
5667 self.currmg_name = filename
5668 if self.verbose and self.currimg_name != "":
5669 print(_("Loading: %s") % self.currimg_name)
5670 self.update_title()
5671 self.put_error_image_to_window()
5672 self.image_loaded = False
5673 self.currimg_pixbuf_original = None
5674 if reset_cursor:
5675 if not self.fullscreen_mode:
5676 self.change_cursor(None)
5677
5678 def load_new_image_stop_now(self):
5679 try:
5680 gobject.source_remove(self.load_when_idle)
5681 except:
5682 pass
5683 try:
5684 gobject.source_remove(self.preload_when_idle)
5685 except:
5686 pass
5687 try:
5688 gobject.source_remove(self.preload_when_idle2)
5689 except:
5690 pass
5691
5692 def load_new_image(
5693 self,
5694 check_prev_last,
5695 use_current_pixbuf_original,
5696 reset_cursor,
5697 perform_onload_action,
5698 preload_next_image_after,
5699 preload_prev_image_after,
5700 ):
5701 try:
5702 self.load_new_image2(
5703 check_prev_last,
5704 use_current_pixbuf_original,
5705 reset_cursor,
5706 perform_onload_action,
5707 )
5708 except:
5709 self.image_load_failed(True)
5710 if preload_next_image_after:
5711 self.preload_when_idle = gobject.idle_add(self.preload_next_image, False)
5712 if preload_prev_image_after:
5713 self.preload_when_idle2 = gobject.idle_add(self.preload_prev_image, False)
5714
5715 def check_preloadimg_prev_for_existing(
5716 self, prev_index, reset_preloadimg_prev_in_list
5717 ):
5718 # Determines if preloadimg_prev needs to be updated; if so,
5719 # checks if the image is already stored in self.currimg
5720 # or self.preloadimg_next and can be reused.
5721 reset_preloadimg_prev_in_list = False
5722 if prev_index != self.preloadimg_prev_in_list and prev_index != -1:
5723 # Need to update preloadimg_prev:
5724 if (
5725 prev_index == self.loaded_img_in_list
5726 and not self.image_modified
5727 and not self.image_zoomed
5728 ):
5729 self.preloadimg_prev_in_list = self.loaded_img_in_list
5730 self.preloadimg_prev_name = self.currimg_name
5731 self.preloadimg_prev_width = self.currimg_width
5732 self.preloadimg_prev_height = self.currimg_height
5733 self.preloadimg_prev_pixbuf = self.currimg_pixbuf
5734 self.preloadimg_prev_pixbuf_original = self.currimg_pixbuf_original
5735 self.preloadimg_prev_zoomratio = self.currimg_zoomratio
5736 self.preloadimg_prev_is_animation = self.currimg_is_animation
5737 elif prev_index == self.preloadimg_next_in_list:
5738 self.preloadimg_prev_in_list = self.preloadimg_next_in_list
5739 self.preloadimg_prev_name = self.preloadimg_next_name
5740 self.preloadimg_prev_width = self.preloadimg_next_width
5741 self.preloadimg_prev_height = self.preloadimg_next_height
5742 self.preloadimg_prev_pixbuf = self.preloadimg_next_pixbuf
5743 self.preloadimg_prev_pixbuf_original = (
5744 self.preloadimg_next_pixbuf_original
5745 )
5746 self.preloadimg_prev_zoomratio = self.preloadimg_next_zoomratio
5747 self.preloadimg_prev_is_animation = self.preloadimg_next_is_animation
5748 else:
5749 reset_preloadimg_prev_in_list = True
5750 elif prev_index == -1:
5751 reset_preloadimg_prev_in_list = True
5752
5753 def check_preloadimg_next_for_existing(
5754 self, next_index, reset_preloadimg_next_in_list
5755 ):
5756 # Determines if preloadimg_next needs to be updated; if so,
5757 # checks if the image is already stored in self.currimg
5758 # or self.preloadimg_prev and can be reused.
5759 reset_preloadimg_next_in_list = False
5760 if next_index != self.preloadimg_next_in_list and next_index != -1:
5761 # Need to update preloadimg_next:
5762 if (
5763 next_index == self.loaded_img_in_list
5764 and not self.image_modified
5765 and not self.image_zoomed
5766 ):
5767 self.preloadimg_next_in_list = self.loaded_img_in_list
5768 self.preloadimg_next_name = self.currimg_name
5769 self.preloadimg_next_width = self.currimg_width
5770 self.preloadimg_next_height = self.currimg_height
5771 self.preloadimg_next_pixbuf = self.currimg_pixbuf
5772 self.preloadimg_next_pixbuf_original = self.currimg_pixbuf_original
5773 self.preloadimg_next_zoomratio = self.currimg_zoomratio
5774 self.preloadimg_next_is_animation = self.currimg_is_animation
5775 elif next_index == self.preloadimg_prev_in_list:
5776 self.preloadimg_next_in_list = self.preloadimg_prev_in_list
5777 self.preloadimg_next_name = self.preloadimg_prev_name
5778 self.preloadimg_next_width = self.preloadimg_prev_width
5779 self.preloadimg_next_height = self.preloadimg_prev_height
5780 self.preloadimg_next_pixbuf = self.preloadimg_prev_pixbuf
5781 self.preloadimg_next_pixbuf_original = (
5782 self.preloadimg_prev_pixbuf_original
5783 )
5784 self.preloadimg_next_zoomratio = self.preloadimg_prev_zoomratio
5785 self.preloadimg_next_is_animation = self.preloadimg_prev_is_animation
5786 else:
5787 reset_preloadimg_next_in_list = True
5788 elif next_index == -1:
5789 reset_preloadimg_next_in_list = True
5790
5791 def check_currimg_for_existing(self):
5792 # Determines if currimg needs to be updated; if so,
5793 # checks if the image is already stored in self.preloadimg_next
5794 # or self.preloadimg_prev and can be reused (the whole point of
5795 # preloading!)
5796 used_prev = False
5797 used_next = False
5798 if self.curr_img_in_list != self.loaded_img_in_list:
5799 # Need to update currimg:
5800 if self.curr_img_in_list == self.preloadimg_prev_in_list:
5801 # Set preload_prev_image as current image
5802 self.currimg_name = self.preloadimg_prev_name
5803 self.currimg_width = self.preloadimg_prev_width
5804 self.currimg_height = self.preloadimg_prev_height
5805 self.currimg_pixbuf = self.preloadimg_prev_pixbuf
5806 self.currimg_pixbuf_original = self.preloadimg_prev_pixbuf_original
5807 self.currimg_zoomratio = self.preloadimg_prev_zoomratio
5808 self.currimg_is_animation = self.preloadimg_prev_is_animation
5809 used_prev = True
5810 if self.verbose and self.currimg_name != "":
5811 print(_("Loading: %s") % self.currimg_name)
5812 self.put_zoom_image_to_window(True)
5813 if not self.currimg_is_animation:
5814 self.set_image_sensitivities(True)
5815 else:
5816 self.set_image_sensitivities(False)
5817 elif self.curr_img_in_list == self.preloadimg_next_in_list:
5818 # Use preload_next_image as current image
5819 self.currimg_name = self.preloadimg_next_name
5820 self.currimg_width = self.preloadimg_next_width
5821 self.currimg_height = self.preloadimg_next_height
5822 self.currimg_pixbuf = self.preloadimg_next_pixbuf
5823 self.currimg_pixbuf_original = self.preloadimg_next_pixbuf_original
5824 self.currimg_zoomratio = self.preloadimg_next_zoomratio
5825 self.currimg_is_animation = self.preloadimg_next_is_animation
5826 used_next = True
5827 if self.verbose and self.currimg_name != "":
5828 print(_("Loading: %s") % self.currimg_name)
5829 self.put_zoom_image_to_window(True)
5830 if not self.currimg_is_animation:
5831 self.set_image_sensitivities(True)
5832 else:
5833 self.set_image_sensitivities(False)
5834 return used_prev, used_next
5835
5836 def load_new_image2(
5837 self,
5838 check_prev_last,
5839 use_current_pixbuf_original,
5840 reset_cursor,
5841 perform_onload_action,
5842 skip_recentfiles=False,
5843 ):
5844 # check_prev_last is used to determine if we should check whether
5845 # preloadimg_prev can be reused last. This should really only
5846 # be done if the user just clicked the previous image button in
5847 # order to reduce the number of image loads.
5848 # If use_current_pixbuf_original == True, do not reload the
5849 # self.currimg_pixbuf_original from the file; instead, use the existing
5850 # one. This is only currently useful for resizing images.
5851 # Determine the indices in the self.image_list array for the
5852 # previous and next preload images.
5853 next_index = self.curr_img_in_list + 1
5854 if next_index > len(self.image_list) - 1:
5855 if self.listwrap_mode == 0:
5856 next_index = -1
5857 else:
5858 next_index = 0
5859 prev_index = self.curr_img_in_list - 1
5860 if prev_index < 0:
5861 if self.listwrap_mode == 0:
5862 prev_index = -1
5863 else:
5864 prev_index = len(self.image_list) - 1
5865 if self.preloading_images:
5866 reset_preloadimg_next_in_list = False
5867 reset_preloadimg_prev_in_list = False
5868 if check_prev_last:
5869 self.check_preloadimg_next_for_existing(
5870 next_index, reset_preloadimg_next_in_list
5871 )
5872 else:
5873 self.check_preloadimg_prev_for_existing(
5874 prev_index, reset_preloadimg_prev_in_list
5875 )
5876 used_prev, used_next = self.check_currimg_for_existing()
5877 if self.preloading_images:
5878 if check_prev_last:
5879 self.check_preloadimg_prev_for_existing(
5880 prev_index, reset_preloadimg_prev_in_list
5881 )
5882 else:
5883 self.check_preloadimg_next_for_existing(
5884 next_index, reset_preloadimg_next_in_list
5885 )
5886 if reset_preloadimg_prev_in_list:
5887 self.preloadimg_prev_in_list = -1
5888 if reset_preloadimg_next_in_list:
5889 self.preloadimg_next_in_list = -1
5890 if used_prev or used_next:
5891 # If we used a preload image, set the correct boolean variables
5892 if self.open_mode == self.open_mode_smart or (
5893 self.open_mode == self.open_mode_last
5894 and self.last_mode == self.open_mode_smart
5895 ):
5896 self.last_image_action_was_fit = True
5897 self.last_image_action_was_smart_fit = True
5898 elif self.open_mode == self.open_mode_fit or (
5899 self.open_mode == self.open_mode_last
5900 and self.last_mode == self.open_mode_fit
5901 ):
5902 self.last_image_action_was_fit = True
5903 self.last_image_action_was_smart_fit = False
5904 elif self.open_mode == self.open_mode_1to1 or (
5905 self.open_mode == self.open_mode_last
5906 and self.last_mode == self.open_mode_1to1
5907 ):
5908 self.last_image_action_was_fit = False
5909 else:
5910 # Need to load the current image
5911 self.currimg_pixbuf = None
5912 self.currimg_zoomratio = 1
5913 self.currimg_name = str(self.image_list[self.curr_img_in_list])
5914 if self.verbose and self.currimg_name != "":
5915 print(_("Loading: %s") % self.currimg_name)
5916 animtest = gtk.gdk.PixbufAnimation(self.currimg_name)
5917 if animtest.is_static_image() or (
5918 use_current_pixbuf_original and not self.currimg_is_animation
5919 ):
5920 self.currimg_is_animation = False
5921 if not use_current_pixbuf_original:
5922 self.currimg_pixbuf_original = animtest.get_static_image()
5923 self.set_image_sensitivities(True)
5924 if self.open_mode == self.open_mode_smart or (
5925 self.open_mode == self.open_mode_last
5926 and self.last_mode == self.open_mode_smart
5927 ):
5928 self.zoom_to_fit_or_1_to_1(None, False, False)
5929 elif self.open_mode == self.open_mode_fit or (
5930 self.open_mode == self.open_mode_last
5931 and self.last_mode == self.open_mode_fit
5932 ):
5933 self.zoom_to_fit_window(None, False, False)
5934 elif self.open_mode == self.open_mode_1to1 or (
5935 self.open_mode == self.open_mode_last
5936 and self.last_mode == self.open_mode_1to1
5937 ):
5938 self.zoom_1_to_1(None, False, False)
5939 else:
5940 self.currimg_is_animation = True
5941 if not use_current_pixbuf_original:
5942 self.currimg_pixbuf_original = animtest
5943 self.zoom_1_to_1(None, False, False)
5944 self.set_image_sensitivities(False)
5945 if self.onload_cmd != None and perform_onload_action:
5946 self.parse_action_command(self.onload_cmd, False)
5947 self.update_statusbar()
5948 self.update_title()
5949 self.image_loaded = True
5950 self.image_modified = False
5951 self.image_zoomed = False
5952 self.set_slideshow_sensitivities()
5953 if not skip_recentfiles:
5954 self.register_file_with_recent_docs(self.currimg_name)
5955 if reset_cursor:
5956 if not self.fullscreen_mode:
5957 self.change_cursor(None)
5958
5959 def preload_next_image(self, use_existing_image):
5960 try:
5961 if self.preloading_images and len(self.image_list) > 1:
5962 if not use_existing_image:
5963 next_index = self.curr_img_in_list + 1
5964 if next_index > len(self.image_list) - 1:
5965 if self.listwrap_mode == 0:
5966 self.preloadimg_next_in_list == -1
5967 return
5968 else:
5969 next_index = 0
5970 if next_index == self.preloadimg_next_in_list:
5971 return
5972 self.preloadimg_next_in_list = next_index
5973 self.preloadimg_next_name = str(self.image_list[next_index])
5974 pre_animtest = gtk.gdk.PixbufAnimation(self.preloadimg_next_name)
5975 if pre_animtest.is_static_image():
5976 self.preloadimg_next_is_animation = False
5977 self.preloadimg_next_pixbuf_original = (
5978 pre_animtest.get_static_image()
5979 )
5980 else:
5981 self.preloadimg_next_is_animation = True
5982 self.preloadimg_next_pixbuf_original = pre_animtest
5983 if self.preloadimg_next_in_list == -1:
5984 return
5985 # Determine self.preloadimg_next_zoomratio
5986 if self.open_mode == self.open_mode_smart or (
5987 self.open_mode == self.open_mode_last
5988 and self.last_mode == self.open_mode_smart
5989 ):
5990 self.zoom_to_fit_or_1_to_1(None, True, False)
5991 elif self.open_mode == self.open_mode_fit or (
5992 self.open_mode == self.open_mode_last
5993 and self.last_mode == self.open_mode_fit
5994 ):
5995 self.zoom_to_fit_window(None, True, False)
5996 elif self.open_mode == self.open_mode_1to1 or (
5997 self.open_mode == self.open_mode_last
5998 and self.last_mode == self.open_mode_1to1
5999 ):
6000 self.zoom_1_to_1(None, True, False)
6001 # Always start with the original image to preserve quality!
6002 # Calculate image size:
6003 self.preloadimg_next_width = int(
6004 self.preloadimg_next_pixbuf_original.get_width()
6005 * self.preloadimg_next_zoomratio
6006 )
6007 self.preloadimg_next_height = int(
6008 self.preloadimg_next_pixbuf_original.get_height()
6009 * self.preloadimg_next_zoomratio
6010 )
6011 if not self.preloadimg_next_is_animation:
6012 # Scale image:
6013 if not self.preloadimg_next_pixbuf_original.get_has_alpha():
6014 self.preloadimg_next_pixbuf = self.preloadimg_next_pixbuf_original.scale_simple(
6015 self.preloadimg_next_width,
6016 self.preloadimg_next_height,
6017 self.zoom_quality,
6018 )
6019 else:
6020 colormap = self.imageview.get_colormap()
6021 light_grey = colormap.alloc_color("#666666", True, True)
6022 dark_grey = colormap.alloc_color("#999999", True, True)
6023 self.preloadimg_next_pixbuf = self.preloadimg_next_pixbuf_original.composite_color_simple(
6024 self.preloadimg_next_width,
6025 self.preloadimg_next_height,
6026 self.zoom_quality,
6027 255,
6028 8,
6029 light_grey.pixel,
6030 dark_grey.pixel,
6031 )
6032 else:
6033 self.preloadimg_next_pixbuf = self.preloadimg_next_pixbuf_original
6034 gc.collect()
6035 if self.verbose:
6036 print(_("Preloading: %s") % self.preloadimg_next_name)
6037 except:
6038 self.preloadimg_next_in_list = -1
6039
6040 def preload_prev_image(self, use_existing_image):
6041 try:
6042 if self.preloading_images and len(self.image_list) > 1:
6043 if not use_existing_image:
6044 prev_index = self.curr_img_in_list - 1
6045 if prev_index < 0:
6046 if self.listwrap_mode == 0:
6047 self.preloadimg_prev_in_list == -1
6048 return
6049 else:
6050 prev_index = len(self.image_list) - 1
6051 if prev_index == self.preloadimg_prev_in_list:
6052 return
6053 self.preloadimg_prev_in_list = prev_index
6054 self.preloadimg_prev_name = str(self.image_list[prev_index])
6055 pre_animtest = gtk.gdk.PixbufAnimation(self.preloadimg_prev_name)
6056 if pre_animtest.is_static_image():
6057 self.preloadimg_prev_is_animation = False
6058 self.preloadimg_prev_pixbuf_original = (
6059 pre_animtest.get_static_image()
6060 )
6061 else:
6062 self.preloadimg_prev_is_animation = True
6063 self.preloadimg_prev_pixbuf_original = pre_animtest
6064 if self.preloadimg_prev_in_list == -1:
6065 return
6066 # Determine self.preloadimg_prev_zoomratio
6067 if self.open_mode == self.open_mode_smart or (
6068 self.open_mode == self.open_mode_last
6069 and self.last_mode == self.open_mode_smart
6070 ):
6071 self.zoom_to_fit_or_1_to_1(None, False, True)
6072 elif self.open_mode == self.open_mode_fit or (
6073 self.open_mode == self.open_mode_last
6074 and self.last_mode == self.open_mode_fit
6075 ):
6076 self.zoom_to_fit_window(None, False, True)
6077 elif self.open_mode == self.open_mode_1to1 or (
6078 self.open_mode == self.open_mode_last
6079 and self.last_mode == self.open_mode_1to1
6080 ):
6081 self.zoom_1_to_1(None, False, True)
6082 # Always start with the original image to preserve quality!
6083 # Calculate image size:
6084 self.preloadimg_prev_width = int(
6085 self.preloadimg_prev_pixbuf_original.get_width()
6086 * self.preloadimg_prev_zoomratio
6087 )
6088 self.preloadimg_prev_height = int(
6089 self.preloadimg_prev_pixbuf_original.get_height()
6090 * self.preloadimg_prev_zoomratio
6091 )
6092 if not self.preloadimg_prev_is_animation:
6093 # Scale image:
6094 if not self.preloadimg_prev_pixbuf_original.get_has_alpha():
6095 self.preloadimg_prev_pixbuf = self.preloadimg_prev_pixbuf_original.scale_simple(
6096 self.preloadimg_prev_width,
6097 self.preloadimg_prev_height,
6098 self.zoom_quality,
6099 )
6100 else:
6101 colormap = self.imageview.get_colormap()
6102 light_grey = colormap.alloc_color("#666666", True, True)
6103 dark_grey = colormap.alloc_color("#999999", True, True)
6104 self.preloadimg_prev_pixbuf = self.preloadimg_prev_pixbuf_original.composite_color_simple(
6105 self.preloadimg_prev_width,
6106 self.preloadimg_prev_height,
6107 self.zoom_quality,
6108 255,
6109 8,
6110 light_grey.pixel,
6111 dark_grey.pixel,
6112 )
6113 else:
6114 self.preloadimg_prev_pixbuf = self.preloadimg_prev_pixbuf_original
6115 gc.collect()
6116 if self.verbose:
6117 print(_("Preloading: %s") % self.preloadimg_prev_name)
6118 except:
6119 self.preloadimg_prev_in_list = -1
6120
6121 def change_cursor(self, type):
6122 for i in gtk.gdk.window_get_toplevels():
6123 if (
6124 i.get_window_type() != gtk.gdk.WINDOW_TEMP
6125 and i.get_window_type() != gtk.gdk.WINDOW_CHILD
6126 ):
6127 i.set_cursor(type)
6128 self.layout.window.set_cursor(type)
6129
6130 def expand_filelist_and_load_image(self, inputlist):
6131 # Takes the current list (i.e. ["pic.jpg", "pic2.gif", "../images"]) and
6132 # expands it into a list of all pictures found
6133 self.thumblist.clear()
6134 first_image_loaded_successfully = False
6135 self.images_found = 0
6136 self.stop_now = True # Make sure that any previous search process is stopped
6137 self.change_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
6138 # Reset preload images:
6139 self.preloadimg_next_in_list = -1
6140 self.preloadimg_prev_in_list = -1
6141 # If any directories were passed, display "Searching..." in statusbar:
6142 self.searching_for_images = False
6143 for item in inputlist:
6144 if os.path.isdir(item):
6145 self.searching_for_images = True
6146 self.update_statusbar()
6147 if not self.closing_app:
6148 while gtk.events_pending():
6149 gtk.main_iteration()
6150 first_image = ""
6151 first_image_found = False
6152 first_image_loaded = False
6153 second_image = ""
6154 second_image_found = False
6155 second_image_preloaded = False
6156 self.randomlist = []
6157 folderlist = []
6158 self.image_list = []
6159 self.curr_img_in_list = 0
6160 go_buttons_enabled = False
6161 self.set_go_sensitivities(False)
6162 # Clean up list (remove preceding "file://" or "file:" and trailing "/")
6163 for itemnum in range(len(inputlist)):
6164 # Strip off preceding file..
6165 if inputlist[itemnum].startswith("file://"):
6166 inputlist[itemnum] = inputlist[itemnum][7:]
6167 elif inputlist[itemnum].startswith("file:"):
6168 inputlist[itemnum] = inputlist[itemnum][5:]
6169 # Strip off trailing "/" if it exists:
6170 if inputlist[itemnum][len(inputlist[itemnum]) - 1] == "/":
6171 inputlist[itemnum] = inputlist[itemnum][: (len(inputlist[itemnum]) - 1)]
6172 if not (
6173 inputlist[itemnum].startswith("http://")
6174 or inputlist[itemnum].startswith("ftp://")
6175 ):
6176 inputlist[itemnum] = os.path.abspath(inputlist[itemnum])
6177 else:
6178 try:
6179 # Remote file. Save as /tmp/mirage-<random>/filename.ext
6180 tmpdir = tempfile.mkdtemp(prefix="mirage-") + "/"
6181 tmpfile = tmpdir + os.path.basename(inputlist[itemnum])
6182 socket.setdefaulttimeout(5)
6183 urllib.request.urlretrieve(inputlist[itemnum], tmpfile)
6184 inputlist[itemnum] = tmpfile
6185 except:
6186 pass
6187 # Remove hidden files from list:
6188 if not self.open_hidden_files:
6189 tmplist = []
6190 for item in inputlist:
6191 if os.path.basename(item)[0] != ".":
6192 tmplist.append(item)
6193 elif self.verbose:
6194 print(_("Skipping: %s") % item)
6195 inputlist = tmplist
6196 if len(inputlist) == 0:
6197 # All files/dirs were hidden, exit..
6198 self.currimg_name = ""
6199 self.searching_for_images = False
6200 self.set_go_navigation_sensitivities(False)
6201 self.set_slideshow_sensitivities()
6202 if not self.closing_app:
6203 self.change_cursor(None)
6204 self.recursive = False
6205 self.put_error_image_to_window()
6206 self.update_title()
6207 return
6208 init_image = os.path.abspath(inputlist[0])
6209 self.stop_now = False
6210 # If open all images in dir...
6211 if self.open_all_images:
6212 temp = inputlist
6213 inputlist = []
6214 for item in temp:
6215 if os.path.isfile(item):
6216 itempath = os.path.dirname(os.path.abspath(item))
6217 temp = self.recursive
6218 self.recursive = False
6219 self.stop_now = False
6220 self.expand_directory(
6221 itempath, False, go_buttons_enabled, False, False
6222 )
6223 self.recursive = temp
6224 else:
6225 inputlist.append(item)
6226 for item in self.image_list:
6227 inputlist.append(item)
6228 if first_image_found and not second_image_found:
6229 second_image_found = True
6230 second_image = item
6231 second_image_came_from_dir = False
6232 if item == init_image:
6233 first_image_found = True
6234 first_image = item
6235 first_image_came_from_dir = False
6236 self.curr_img_in_list = len(inputlist) - 1
6237 self.image_list = []
6238 for item in inputlist:
6239 if not self.closing_app:
6240 if os.path.isfile(item):
6241 if self.valid_image(item):
6242 if not second_image_found and first_image_found:
6243 second_image_found = True
6244 second_image = item
6245 second_image_came_from_dir = False
6246 if not first_image_found:
6247 first_image_found = True
6248 first_image = item
6249 first_image_came_from_dir = False
6250 self.image_list.append(item)
6251 if self.verbose:
6252 self.images_found += 1
6253 print(
6254 _("Found: %(item)s [%(number)i]")
6255 % {"item": item, "number": self.images_found}
6256 )
6257 else:
6258 # If it's a directory that was explicitly selected or passed to
6259 # the program, get all the files in the dir.
6260 # Retrieve only images in the top directory specified by the user
6261 # unless explicitly told to recurse (via -R or in Settings>Preferences)
6262 folderlist.append(item)
6263 if not second_image_found:
6264 # See if we can find an image in this directory:
6265 self.stop_now = False
6266 self.expand_directory(
6267 item, True, go_buttons_enabled, False, False
6268 )
6269 itemnum = 0
6270 while itemnum < len(self.image_list) and not second_image_found:
6271 if os.path.isfile(self.image_list[itemnum]):
6272 if not second_image_found and first_image_found:
6273 second_image_found = True
6274 second_image_came_from_dir = True
6275 second_image = self.image_list[itemnum]
6276 self.set_go_navigation_sensitivities(True)
6277 go_buttons_enabled = True
6278 while gtk.events_pending():
6279 gtk.main_iteration(True)
6280 if not first_image_found:
6281 first_image_found = True
6282 first_image = self.image_list[itemnum]
6283 first_image_came_from_dir = True
6284 itemnum += 1
6285 # Load first image and display:
6286 if (
6287 first_image_found
6288 and not first_image_loaded
6289 and self.curr_img_in_list <= len(self.image_list) - 1
6290 ):
6291 first_image_loaded = True
6292 if self.slideshow_mode:
6293 self.toggle_slideshow(None)
6294 if self.verbose and self.currimg_name != "":
6295 print(_("Loading: %s") % self.currimg_name)
6296 try:
6297 self.load_new_image2(False, False, True, True)
6298 # Calling load_new_image2 will reset the following two vars
6299 # to 0, so ensure they are -1 again (no images preloaded)
6300 self.preloadimg_prev_in_list = -1
6301 self.preloadimg_next_in_list = -1
6302 if not self.currimg_is_animation:
6303 self.previmg_width = self.currimg_pixbuf.get_width()
6304 else:
6305 self.previmg_width = (
6306 self.currimg_pixbuf.get_static_image().get_width()
6307 )
6308 self.image_loaded = True
6309 first_image_loaded_successfully = True
6310 if not self.closing_app:
6311 while gtk.events_pending():
6312 gtk.main_iteration(True)
6313 except:
6314 pass
6315 if first_image_came_from_dir:
6316 self.image_list = []
6317 # Pre-load second image:
6318 if (
6319 second_image_found
6320 and not second_image_preloaded
6321 and (
6322 (
6323 not second_image_came_from_dir
6324 and self.curr_img_in_list + 1 <= len(self.image_list) - 1
6325 )
6326 or second_image_came_from_dir
6327 )
6328 ):
6329 second_image_preloaded = True
6330 temp = self.image_list
6331 self.image_list = []
6332 while len(self.image_list) < self.curr_img_in_list + 1:
6333 self.image_list.append(first_image)
6334 self.image_list.append(second_image)
6335 self.preload_next_image(False)
6336 self.image_list = temp
6337 if first_image_found:
6338 # Sort the filelist and folderlist alphabetically, and recurse into folderlist:
6339 if first_image_came_from_dir:
6340 self.add_folderlist_images(folderlist, go_buttons_enabled)
6341 self.do_image_list_stuff(first_image, second_image)
6342 else:
6343 self.do_image_list_stuff(first_image, second_image)
6344 self.add_folderlist_images(folderlist, go_buttons_enabled)
6345 self.update_title()
6346 if not self.closing_app:
6347 while gtk.events_pending():
6348 gtk.main_iteration(True)
6349 if not first_image_loaded_successfully:
6350 self.image_load_failed(False, init_image)
6351 self.searching_for_images = False
6352 self.update_statusbar()
6353 self.set_go_navigation_sensitivities(False)
6354 self.set_slideshow_sensitivities()
6355 self.thumbpane_update_images(True, self.curr_img_in_list)
6356 if not self.closing_app:
6357 self.change_cursor(None)
6358 self.recursive = False
6359
6360 def add_folderlist_images(self, folderlist, go_buttons_enabled):
6361 if len(folderlist) > 0:
6362 folderlist.sort(locale.strcoll)
6363 folderlist = list(set(folderlist))
6364 for item in folderlist:
6365 if not self.closing_app:
6366 if (
6367 not self.open_hidden_files and os.path.basename(item)[0] != "."
6368 ) or self.open_hidden_files:
6369 self.stop_now = False
6370 self.expand_directory(
6371 item, False, go_buttons_enabled, True, True
6372 )
6373
6374 def do_image_list_stuff(self, first_image, second_image):
6375 if len(self.image_list) > 0:
6376 self.set_go_navigation_sensitivities(True)
6377 self.image_list = list(set(self.image_list))
6378 self.image_list.sort(locale.strcoll)
6379
6380 def expand_directory(
6381 self,
6382 item,
6383 stop_when_second_image_found,
6384 go_buttons_enabled,
6385 update_window_title,
6386 print_found_msg,
6387 ):
6388 if not self.stop_now and not self.closing_app:
6389 folderlist = []
6390 filelist = []
6391 if not os.access(item, os.R_OK):
6392 return False
6393 for item2 in os.listdir(item):
6394 if not self.closing_app and not self.stop_now:
6395 while gtk.events_pending():
6396 gtk.main_iteration(True)
6397 item2 = item + os.sep + item2
6398 item_fullpath2 = os.path.abspath(item2)
6399 if (
6400 not self.open_hidden_files
6401 and os.path.basename(item_fullpath2)[0] != "."
6402 ) or self.open_hidden_files:
6403 if os.path.isfile(item_fullpath2) and self.valid_image(
6404 item_fullpath2
6405 ):
6406 filelist.append(item2)
6407 if self.verbose and print_found_msg:
6408 self.images_found += 1
6409 print(
6410 _("Found: %(fullpath)s [%(number)i]")
6411 % {
6412 "fullpath": item_fullpath2,
6413 "number": self.images_found,
6414 }
6415 )
6416 elif os.path.isdir(item_fullpath2) and self.recursive:
6417 folderlist.append(item_fullpath2)
6418 elif self.verbose:
6419 print(_("Skipping: %s") % item_fullpath2)
6420 if len(self.image_list) > 0 and update_window_title:
6421 self.update_title()
6422 # Sort the filelist and folderlist alphabetically:
6423 if len(filelist) > 0:
6424 filelist.sort(locale.strcoll)
6425 for item2 in filelist:
6426 if not item2 in self.image_list:
6427 self.image_list.append(item2)
6428 if stop_when_second_image_found and len(self.image_list) == 2:
6429 return
6430 if not go_buttons_enabled and len(self.image_list) > 1:
6431 self.set_go_navigation_sensitivities(True)
6432 go_buttons_enabled = True
6433 # Recurse into the folderlist:
6434 if len(folderlist) > 0:
6435 folderlist.sort(locale.strcoll)
6436 for item2 in folderlist:
6437 if not self.stop_now:
6438 self.expand_directory(
6439 item2,
6440 stop_when_second_image_found,
6441 go_buttons_enabled,
6442 update_window_title,
6443 print_found_msg,
6444 )
6445
6446 def register_file_with_recent_docs(self, imgfile):
6447 self.recent_file_add_and_refresh(imgfile)
6448 if os.path.isfile(imgfile) and gtk.check_version(2, 10, 0) == None:
6449 try:
6450 gtk_recent_manager = gtk.recent_manager_get_default()
6451 uri = ""
6452 if imgfile[:7] != "file://":
6453 uri = "file://"
6454 uri = uri + urllib.request.pathname2url(os.path.abspath(imgfile))
6455 gtk_recent_manager.add_item(uri)
6456 except:
6457 # Isnt currently functional on win32
6458 if sys.platform == "win32":
6459 pass
6460 else:
6461 raise
6462
6463 def valid_image(self, file):
6464 test = gtk.gdk.pixbuf_get_file_info(file)
6465 if test == None:
6466 return False
6467 elif test[0]["name"] == "wbmp":
6468 # some regular files are thought to be wbmp for whatever reason,
6469 # so let's check further.. :(
6470 try:
6471 test2 = gtk.gdk.pixbuf_new_from_file(file)
6472 return True
6473 except:
6474 return False
6475 else:
6476 return True
6477
6478 def image_flip(self, old_pix, vertical):
6479 width = old_pix.get_width()
6480 height = old_pix.get_height()
6481 d = None
6482 if vertical:
6483 d, w, h, rws = imgfuncs.vert(
6484 old_pix.get_pixels(),
6485 width,
6486 height,
6487 old_pix.get_rowstride(),
6488 old_pix.get_n_channels(),
6489 )
6490 else:
6491 d, w, h, rws = imgfuncs.horiz(
6492 old_pix.get_pixels(),
6493 width,
6494 height,
6495 old_pix.get_rowstride(),
6496 old_pix.get_n_channels(),
6497 )
6498 if d:
6499 new_pix = gtk.gdk.pixbuf_new_from_data(
6500 d,
6501 old_pix.get_colorspace(),
6502 old_pix.get_has_alpha(),
6503 old_pix.get_bits_per_sample(),
6504 w,
6505 h,
6506 rws,
6507 )
6508 return new_pix
6509 return old_pix
6510
6511 def image_rotate(self, old_pix, full_angle):
6512 width = old_pix.get_width()
6513 height = old_pix.get_height()
6514 angle = full_angle - (int(full_angle) // 360) * 360
6515 if angle:
6516 d = None
6517 if angle % 270 == 0:
6518 d, w, h, rws = imgfuncs.right(
6519 old_pix.get_pixels(),
6520 width,
6521 height,
6522 old_pix.get_rowstride(),
6523 old_pix.get_n_channels(),
6524 )
6525 elif angle % 180 == 0:
6526 d, w, h, rws = imgfuncs.mirror(
6527 old_pix.get_pixels(),
6528 width,
6529 height,
6530 old_pix.get_rowstride(),
6531 old_pix.get_n_channels(),
6532 )
6533 elif angle % 90 == 0:
6534 d, w, h, rws = imgfuncs.left(
6535 old_pix.get_pixels(),
6536 width,
6537 height,
6538 old_pix.get_rowstride(),
6539 old_pix.get_n_channels(),
6540 )
6541 if d:
6542 new_pix = gtk.gdk.pixbuf_new_from_data(
6543 d,
6544 old_pix.get_colorspace(),
6545 old_pix.get_has_alpha(),
6546 old_pix.get_bits_per_sample(),
6547 w,
6548 h,
6549 rws,
6550 )
6551 return new_pix
6552 return old_pix
6553
6554 def toggle_slideshow(self, action):
6555 if len(self.image_list) > 1:
6556 if not self.slideshow_mode:
6557 if self.slideshow_in_fullscreen and not self.fullscreen_mode:
6558 self.enter_fullscreen(None)
6559 self.slideshow_mode = True
6560 self.update_title()
6561 self.set_slideshow_sensitivities()
6562 if not self.curr_slideshow_random:
6563 self.timer_delay = gobject.timeout_add(
6564 int(self.curr_slideshow_delay * 1000),
6565 self.goto_next_image,
6566 "ss",
6567 )
6568 else:
6569 self.reinitialize_randomlist()
6570 self.timer_delay = gobject.timeout_add(
6571 int(self.curr_slideshow_delay * 1000),
6572 self.goto_random_image,
6573 "ss",
6574 )
6575 self.ss_start.hide()
6576 self.ss_stop.show()
6577 timer_screensaver = gobject.timeout_add(
6578 1000, self.disable_screensaver_in_slideshow_mode
6579 )
6580 else:
6581 self.slideshow_mode = False
6582 gobject.source_remove(self.timer_delay)
6583 self.update_title()
6584 self.set_slideshow_sensitivities()
6585 self.set_zoom_sensitivities()
6586 self.ss_stop.hide()
6587 self.ss_start.show()
6588
6589 def update_title(self):
6590 if len(self.image_list) == 0:
6591 title = "Mirage"
6592 else:
6593 title = (
6594 "Mirage - "
6595 + _("[%(current)i of %(total)i]")
6596 % {"current": self.curr_img_in_list + 1, "total": len(self.image_list)}
6597 + " "
6598 + os.path.basename(self.currimg_name)
6599 )
6600 if self.slideshow_mode:
6601 title = title + " - " + _("Slideshow Mode")
6602 self.window.set_title(title)
6603
6604 def slideshow_controls_show(self):
6605 if not self.slideshow_controls_visible and not self.controls_moving:
6606 self.slideshow_controls_visible = True
6607
6608 self.ss_delayspin.set_value(self.curr_slideshow_delay)
6609 self.ss_randomize.set_active(self.curr_slideshow_random)
6610
6611 if self.slideshow_mode:
6612 self.ss_start.set_no_show_all(True)
6613 self.ss_stop.set_no_show_all(False)
6614 else:
6615 self.ss_start.set_no_show_all(False)
6616 self.ss_stop.set_no_show_all(True)
6617
6618 (xpos, ypos) = self.window.get_position()
6619 screen = self.window.get_screen()
6620 self.slideshow_window.set_screen(screen)
6621 self.slideshow_window2.set_screen(screen)
6622
6623 self.slideshow_window.show_all()
6624 self.slideshow_window2.show_all()
6625 if not self.closing_app:
6626 while gtk.events_pending():
6627 gtk.main_iteration()
6628
6629 ss_winheight = self.slideshow_window.allocation.height
6630 ss_win2width = self.slideshow_window2.allocation.width
6631 winheight = self.window.allocation.height
6632 winwidth = self.window.allocation.width
6633 y = -3.0
6634 self.controls_moving = True
6635 while y < ss_winheight:
6636 self.slideshow_window.move(2 + xpos, int(winheight - y - 2))
6637 self.slideshow_window2.move(
6638 winwidth - ss_win2width - 2 + xpos, int(winheight - y - 2)
6639 )
6640 y += 0.05
6641 if not self.closing_app:
6642 while gtk.events_pending():
6643 gtk.main_iteration()
6644 self.controls_moving = False
6645
6646 def slideshow_controls_hide(self):
6647 if self.slideshow_controls_visible and not self.controls_moving:
6648 self.slideshow_controls_visible = False
6649
6650 (xpos, ypos) = self.window.get_position()
6651
6652 ss_winheight = self.slideshow_window.allocation.height
6653 ss_win2width = self.slideshow_window2.allocation.width
6654 winheight = self.window.allocation.height
6655 winwidth = self.window.allocation.width
6656 y = float(self.slideshow_window.allocation.height * 1.0)
6657 self.controls_moving = True
6658 while y > -3:
6659 self.slideshow_window.move(2 + xpos, int(winheight - y - 2))
6660 self.slideshow_window2.move(
6661 winwidth - ss_win2width - 2 + xpos, int(winheight - y - 2)
6662 )
6663 y -= 0.05
6664 if not self.closing_app:
6665 while gtk.events_pending():
6666 gtk.main_iteration()
6667 self.controls_moving = False
6668
6669 def disable_screensaver_in_slideshow_mode(self):
6670 if self.slideshow_mode and self.disable_screensaver:
6671 test = os.spawnlp(
6672 os.P_WAIT,
6673 "/usr/bin/xscreensaver-command",
6674 "xscreensaver-command",
6675 "-deactivate",
6676 )
6677 if test != 127:
6678 timer_screensaver = gobject.timeout_add(
6679 1000, self.disable_screensaver_in_slideshow_mode
6680 )
6681
6682 def main(self):
6683 gtk.main()
6684
45756685
45766686 if __name__ == "__main__":
4577 base = Base()
4578 gtk.gdk.threads_enter()
4579 base.main()
4580 gtk.gdk.threads_leave()
6687 base = Base()
6688 gtk.gdk.threads_enter()
6689 base.main()
6690 gtk.gdk.threads_leave()
33
44 from distutils.core import setup, Extension
55
6
67 def removeall(path):
7 if not os.path.isdir(path):
8 return
8 if not os.path.isdir(path):
9 return
910
10 files=os.listdir(path)
11 files = os.listdir(path)
1112
12 for x in files:
13 fullpath=os.path.join(path, x)
14 if os.path.isfile(fullpath):
15 f=os.remove
16 rmgeneric(fullpath, f)
17 elif os.path.isdir(fullpath):
18 removeall(fullpath)
19 f=os.rmdir
20 rmgeneric(fullpath, f)
13 for x in files:
14 fullpath = os.path.join(path, x)
15 if os.path.isfile(fullpath):
16 f = os.remove
17 rmgeneric(fullpath, f)
18 elif os.path.isdir(fullpath):
19 removeall(fullpath)
20 f = os.rmdir
21 rmgeneric(fullpath, f)
22
2123
2224 def rmgeneric(path, __func__):
23 try:
24 __func__(path)
25 except OSError:
26 pass
25 try:
26 __func__(path)
27 except OSError:
28 pass
29
2730
2831 # Create mo files:
2932 if not os.path.exists("mo/"):
30 os.mkdir("mo/")
31 for lang in ('it', 'de', 'pl', 'es', 'fr', 'ru', 'hu', 'cs', 'pt_BR', 'zh_CN', 'nl', 'ua'):
32 pofile = "po/" + lang + ".po"
33 mofile = "mo/" + lang + "/mirage.mo"
34 if not os.path.exists("mo/" + lang + "/"):
35 os.mkdir("mo/" + lang + "/")
36 print("generating", mofile)
37 os.system("msgfmt %s -o %s" % (pofile, mofile))
33 os.mkdir("mo/")
34 for lang in (
35 "it",
36 "de",
37 "pl",
38 "es",
39 "fr",
40 "ru",
41 "hu",
42 "cs",
43 "pt_BR",
44 "zh_CN",
45 "nl",
46 "ua",
47 ):
48 pofile = "po/" + lang + ".po"
49 mofile = "mo/" + lang + "/mirage.mo"
50 if not os.path.exists("mo/" + lang + "/"):
51 os.mkdir("mo/" + lang + "/")
52 print("generating", mofile)
53 os.system("msgfmt %s -o %s" % (pofile, mofile))
3854
39 setup(name='Mirage',
40 version='0.9.5.2',
41 description='A fast GTK+ image viewer',
42 author='Scott Horowitz',
43 author_email='stonecrest@gmail.com',
44 maintainer= 'Fredric Johansson',
45 maintainer_email='fredric.miscmail@gmail.com',
46 url='http://mirageiv.berlios.de',
47 classifiers=[
48 'Environment :: X11 Applications',
49 'Intended Audience :: End Users/Desktop',
50 'License :: GNU General Public License (GPL)',
51 'Operating System :: Linux',
52 'Programming Language :: Python',
53 'Topic :: Multimedia :: Graphics :: Viewers'
54 ],
55 py_modules = ['mirage'],
56 ext_modules = [Extension(name='imgfuncs', sources=['imgfuncs.c']),
57 Extension(name='xmouse', sources=['xmouse.c'], libraries=['X11'])],
58 scripts = ['mirage'],
59 data_files=[('share/mirage', ['README', 'COPYING', 'CHANGELOG', 'TODO', 'TRANSLATORS', 'stock_shuffle.png', 'stock_leave-fullscreen.png', 'stock_fullscreen.png', 'mirage_blank.png']),
60 ('share/applications', ['mirage.desktop']),
61 ('share/pixmaps', ['mirage.png']),
62 ('share/locale/ru/LC_MESSAGES', ['mo/ru/mirage.mo']),
63 ('share/locale/pl/LC_MESSAGES', ['mo/pl/mirage.mo']),
64 ('share/locale/fr/LC_MESSAGES', ['mo/fr/mirage.mo']),
65 ('share/locale/es/LC_MESSAGES', ['mo/es/mirage.mo']),
66 ('share/locale/de/LC_MESSAGES', ['mo/de/mirage.mo']),
67 ('share/locale/hu/LC_MESSAGES', ['mo/hu/mirage.mo']),
68 ('share/locale/cs/LC_MESSAGES', ['mo/cs/mirage.mo']),
69 ('share/locale/nl/LC_MESSAGES', ['mo/nl/mirage.mo']),
70 ('share/locale/pt_BR/LC_MESSAGES', ['mo/pt_BR/mirage.mo']),
71 ('share/locale/zh_CN/LC_MESSAGES', ['mo/zh_CN/mirage.mo']),
72 ('share/locale/ua/LC_MESSAGES', ['mo/ua/mirage.mo']),
73 ('share/locale/it/LC_MESSAGES', ['mo/it/mirage.mo'])],
74 )
55 setup(
56 name="Mirage",
57 version="0.9.5.2",
58 description="A fast GTK+ image viewer",
59 author="Scott Horowitz",
60 author_email="stonecrest@gmail.com",
61 maintainer="Fredric Johansson",
62 maintainer_email="fredric.miscmail@gmail.com",
63 url="http://mirageiv.berlios.de",
64 classifiers=[
65 "Environment :: X11 Applications",
66 "Intended Audience :: End Users/Desktop",
67 "License :: GNU General Public License (GPL)",
68 "Operating System :: Linux",
69 "Programming Language :: Python",
70 "Topic :: Multimedia :: Graphics :: Viewers",
71 ],
72 py_modules=["mirage"],
73 ext_modules=[
74 Extension(name="imgfuncs", sources=["imgfuncs.c"]),
75 Extension(name="xmouse", sources=["xmouse.c"], libraries=["X11"]),
76 ],
77 scripts=["mirage"],
78 data_files=[
79 (
80 "share/mirage",
81 [
82 "README",
83 "COPYING",
84 "CHANGELOG",
85 "TODO",
86 "TRANSLATORS",
87 "stock_shuffle.png",
88 "stock_leave-fullscreen.png",
89 "stock_fullscreen.png",
90 "mirage_blank.png",
91 ],
92 ),
93 ("share/applications", ["mirage.desktop"]),
94 ("share/pixmaps", ["mirage.png"]),
95 ("share/locale/ru/LC_MESSAGES", ["mo/ru/mirage.mo"]),
96 ("share/locale/pl/LC_MESSAGES", ["mo/pl/mirage.mo"]),
97 ("share/locale/fr/LC_MESSAGES", ["mo/fr/mirage.mo"]),
98 ("share/locale/es/LC_MESSAGES", ["mo/es/mirage.mo"]),
99 ("share/locale/de/LC_MESSAGES", ["mo/de/mirage.mo"]),
100 ("share/locale/hu/LC_MESSAGES", ["mo/hu/mirage.mo"]),
101 ("share/locale/cs/LC_MESSAGES", ["mo/cs/mirage.mo"]),
102 ("share/locale/nl/LC_MESSAGES", ["mo/nl/mirage.mo"]),
103 ("share/locale/pt_BR/LC_MESSAGES", ["mo/pt_BR/mirage.mo"]),
104 ("share/locale/zh_CN/LC_MESSAGES", ["mo/zh_CN/mirage.mo"]),
105 ("share/locale/ua/LC_MESSAGES", ["mo/ua/mirage.mo"]),
106 ("share/locale/it/LC_MESSAGES", ["mo/it/mirage.mo"]),
107 ],
108 )
75109
76110 # Cleanup (remove /build, /mo, and *.pyc files:
77111 print("Cleaning up...")
78112 try:
79 removeall("build/")
80 os.rmdir("build/")
113 removeall("build/")
114 os.rmdir("build/")
81115 except:
82 pass
116 pass
83117 try:
84 removeall("mo/")
85 os.rmdir("mo/")
118 removeall("mo/")
119 os.rmdir("mo/")
86120 except:
87 pass
121 pass
88122 try:
89 for f in os.listdir("."):
90 if os.path.isfile(f):
91 if os.path.splitext(os.path.basename(f))[1] == ".pyc":
92 os.remove(f)
123 for f in os.listdir("."):
124 if os.path.isfile(f):
125 if os.path.splitext(os.path.basename(f))[1] == ".pyc":
126 os.remove(f)
93127 except:
94 pass
128 pass