|
0 |
# $HeadURL: http://svn.berlios.de/svnroot/repos/mirageiv/branches/mirage-0.9.x/mirage.py $
|
|
1 |
# $Id: mirage.py 337 2011-02-13 22:40:05Z fredricj $
|
|
2 |
|
|
3 |
__version__ = "0.9.5.2"
|
|
4 |
|
|
5 |
__license__ = """
|
|
6 |
Mirage, a fast GTK+ Image Viewer
|
|
7 |
Copyright 2007 Scott Horowitz <stonecrest@gmail.com>
|
|
8 |
|
|
9 |
This file is part of Mirage.
|
|
10 |
|
|
11 |
Mirage is free software; you can redistribute it and/or modify
|
|
12 |
it under the terms of the GNU General Public License as published by
|
|
13 |
the Free Software Foundation; either version 3 of the License, or
|
|
14 |
(at your option) any later version.
|
|
15 |
|
|
16 |
Mirage is distributed in the hope that it will be useful,
|
|
17 |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
18 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
19 |
GNU General Public License for more details.
|
|
20 |
|
|
21 |
You should have received a copy of the GNU General Public License
|
|
22 |
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
23 |
"""
|
|
24 |
|
|
25 |
import pygtk
|
|
26 |
pygtk.require('2.0')
|
|
27 |
import gtk
|
|
28 |
import os, sys, getopt, ConfigParser, string, gc
|
|
29 |
import random, urllib, gobject, gettext, locale
|
|
30 |
import stat, time, subprocess, shutil, filecmp
|
|
31 |
import tempfile, socket, threading
|
|
32 |
try:
|
|
33 |
import hashlib
|
|
34 |
HAS_HASHLIB = True
|
|
35 |
except:
|
|
36 |
HAS_HASHLIB= False
|
|
37 |
import md5
|
|
38 |
try:
|
|
39 |
import imgfuncs
|
|
40 |
HAS_IMGFUNCS = True
|
|
41 |
except:
|
|
42 |
HAS_IMGFUNCS = False
|
|
43 |
print "imgfuncs.so module not found, rotating/flipping images will be disabled."
|
|
44 |
try:
|
|
45 |
import xmouse
|
|
46 |
HAS_XMOUSE = True
|
|
47 |
except:
|
|
48 |
HAS_XMOUSE = False
|
|
49 |
print "xmouse.so module not found, some screenshot capabilities will be disabled."
|
|
50 |
try:
|
|
51 |
import gconf
|
|
52 |
except:
|
|
53 |
pass
|
|
54 |
|
|
55 |
if gtk.gtk_version < (2, 10, 0):
|
|
56 |
sys.stderr.write("Mirage requires GTK+ 2.10.0 or newer..\n")
|
|
57 |
sys.exit(1)
|
|
58 |
if gtk.pygtk_version < (2, 12, 0):
|
|
59 |
sys.stderr.write("Mirage requires PyGTK 2.12.0 or newer.\n")
|
|
60 |
sys.exit(1)
|
|
61 |
|
|
62 |
def valid_int(inputstring):
|
|
63 |
try:
|
|
64 |
x = int(inputstring)
|
|
65 |
return True
|
|
66 |
except:
|
|
67 |
return False
|
|
68 |
|
|
69 |
class Base:
|
|
70 |
|
|
71 |
def __init__(self):
|
|
72 |
|
|
73 |
gtk.gdk.threads_init()
|
|
74 |
|
|
75 |
# FIX THIS! Does not work on windows and what happens if mo-files exists
|
|
76 |
# in both dirs?
|
|
77 |
gettext.install('mirage', '/usr/share/locale', unicode=1)
|
|
78 |
gettext.install('mirage', '/usr/local/share/locale', unicode=1)
|
|
79 |
|
|
80 |
# Constants
|
|
81 |
self.open_mode_smart = 0
|
|
82 |
self.open_mode_fit = 1
|
|
83 |
self.open_mode_1to1 = 2
|
|
84 |
self.open_mode_last = 3
|
|
85 |
self.min_zoomratio = 0.02
|
|
86 |
|
|
87 |
# Initialize vars:
|
|
88 |
width=600
|
|
89 |
height=400
|
|
90 |
bgcolor_found = False
|
|
91 |
self.simple_bgcolor = False
|
|
92 |
# Current image:
|
|
93 |
self.curr_img_in_list = 0
|
|
94 |
self.currimg_name = ""
|
|
95 |
self.currimg_width = 0
|
|
96 |
self.currimg_height = 0
|
|
97 |
self.currimg_pixbuf = None
|
|
98 |
self.currimg_pixbuf_original = None
|
|
99 |
self.currimg_zoomratio = 1
|
|
100 |
self.currimg_is_animation = False
|
|
101 |
# This is the actual pixbuf that is loaded in Mirage. This will
|
|
102 |
# usually be the same as self.curr_img_in_list except for scenarios
|
|
103 |
# like when the user presses 'next image' multiple times in a row.
|
|
104 |
# In this case, self.curr_img_in_list will increment while
|
|
105 |
# self.loaded_img_in_list will retain the current loaded image.
|
|
106 |
self.loaded_img_in_list = 0
|
|
107 |
# Next preloaded image:
|
|
108 |
self.preloadimg_next_in_list = -1
|
|
109 |
self.preloadimg_next_name = ""
|
|
110 |
self.preloadimg_next_width = 0
|
|
111 |
self.preloadimg_next_height = 0
|
|
112 |
self.preloadimg_next_pixbuf = None
|
|
113 |
self.preloadimg_next_pixbuf_original = None
|
|
114 |
self.preloadimg_next_zoomratio = 1
|
|
115 |
self.preloadimg_next_is_animation = False
|
|
116 |
# Previous preloaded image:
|
|
117 |
self.preloadimg_prev_in_list = -1
|
|
118 |
self.preloadimg_prev_name = ""
|
|
119 |
self.preloadimg_prev_width = 0
|
|
120 |
self.preloadimg_prev_height = 0
|
|
121 |
self.preloadimg_prev_pixbuf = None
|
|
122 |
self.preloadimg_prev_pixbuf_original = None
|
|
123 |
self.preloadimg_prev_zoomratio = 1
|
|
124 |
self.preloadimg_prev_is_animation = False
|
|
125 |
# Settings, misc:
|
|
126 |
self.toolbar_show = True
|
|
127 |
self.thumbpane_show = True
|
|
128 |
self.statusbar_show = True
|
|
129 |
self.fullscreen_mode = False
|
|
130 |
self.opendialogpath = ""
|
|
131 |
self.zoom_quality = gtk.gdk.INTERP_BILINEAR
|
|
132 |
self.recursive = False
|
|
133 |
self.verbose = False
|
|
134 |
self.image_loaded = False
|
|
135 |
self.open_all_images = True # open all images in the directory(ies)
|
|
136 |
self.use_last_dir = True
|
|
137 |
self.last_dir = os.path.expanduser("~")
|
|
138 |
self.fixed_dir = os.path.expanduser("~")
|
|
139 |
self.image_list = []
|
|
140 |
self.open_mode = self.open_mode_smart
|
|
141 |
self.last_mode = self.open_mode_smart
|
|
142 |
self.listwrap_mode = 0 # 0=no, 1=yes, 2=ask
|
|
143 |
self.user_prompt_visible = False # the "wrap?" prompt
|
|
144 |
self.slideshow_delay = 1 # seconds
|
|
145 |
self.slideshow_mode = False
|
|
146 |
self.slideshow_random = False
|
|
147 |
self.slideshow_controls_visible = False # fullscreen slideshow controls
|
|
148 |
self.controls_moving = False
|
|
149 |
self.zoomvalue = 2
|
|
150 |
self.quality_save = 90
|
|
151 |
self.updating_adjustments = False
|
|
152 |
self.disable_screensaver = False
|
|
153 |
self.slideshow_in_fullscreen = False
|
|
154 |
self.closing_app = False
|
|
155 |
self.confirm_delete = True
|
|
156 |
self.preloading_images = True
|
|
157 |
self.action_names = ["Open in GIMP", "Create Thumbnail", "Create Thumbnails", "Move to Favorites"]
|
|
158 |
self.action_shortcuts = ["<Control>e", "<Alt>t", "<Control><Alt>t", "<Control><Alt>f"]
|
|
159 |
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]"]
|
|
160 |
self.action_batch = [False, False, True, False]
|
|
161 |
self.onload_cmd = None
|
|
162 |
self.searching_for_images = False
|
|
163 |
self.preserve_aspect = True
|
|
164 |
self.ignore_preserve_aspect_callback = False
|
|
165 |
self.savemode = 2
|
|
166 |
self.image_modified = False
|
|
167 |
self.image_zoomed = False
|
|
168 |
self.start_in_fullscreen = False
|
|
169 |
self.running_custom_actions = False
|
|
170 |
self.merge_id = None
|
|
171 |
self.actionGroupCustom = None
|
|
172 |
self.merge_id_recent = None
|
|
173 |
self.actionGroupRecent = None
|
|
174 |
self.open_hidden_files = False
|
|
175 |
self.thumbnail_sizes = ["128", "96", "72", "64", "48", "32"]
|
|
176 |
self.thumbnail_size = 128 # Default to 128 x 128
|
|
177 |
self.thumbnail_loaded = []
|
|
178 |
self.thumbpane_updating = False
|
|
179 |
self.recentfiles = ["", "", "", "", ""]
|
|
180 |
self.screenshot_delay = 2
|
|
181 |
self.thumbpane_bottom_coord_loaded = 0
|
|
182 |
|
|
183 |
# Read any passed options/arguments:
|
|
184 |
try:
|
|
185 |
opts, args = getopt.getopt(sys.argv[1:], "hRvVsfo:", ["help", "version", "recursive", "verbose", "slideshow", "fullscreen", "onload="])
|
|
186 |
except getopt.GetoptError:
|
|
187 |
# print help information and exit:
|
|
188 |
self.print_usage()
|
|
189 |
sys.exit(2)
|
|
190 |
# If options were passed, perform action on them.
|
|
191 |
if opts != []:
|
|
192 |
for o, a in opts:
|
|
193 |
if o in ("-v", "--version"):
|
|
194 |
self.print_version()
|
|
195 |
sys.exit(2)
|
|
196 |
elif o in ("-h", "--help"):
|
|
197 |
self.print_usage()
|
|
198 |
sys.exit(2)
|
|
199 |
elif o in ("-R", "--recursive"):
|
|
200 |
self.recursive = True
|
|
201 |
elif o in ("-V", "--verbose"):
|
|
202 |
self.verbose = True
|
|
203 |
elif o in ("-s", "--slideshow", "-f", "--fullscreen"):
|
|
204 |
#This will be handled later
|
|
205 |
None
|
|
206 |
elif o in ("-o", "--onload"):
|
|
207 |
self.onload_cmd = a
|
|
208 |
else:
|
|
209 |
self.print_usage()
|
|
210 |
sys.exit(2)
|
|
211 |
|
|
212 |
|
|
213 |
# Determine config dir, first try the environment variable XDG_CONFIG_HOME
|
|
214 |
# according to XDG specification and as a fallback use ~/.config/mirage
|
|
215 |
self.config_dir = (os.getenv('XDG_CONFIG_HOME') or os.path.expanduser('~/.config')) + '/mirage'
|
|
216 |
# Load config from disk:
|
|
217 |
conf = ConfigParser.ConfigParser()
|
|
218 |
if os.path.isfile(self.config_dir + '/miragerc'):
|
|
219 |
conf.read(self.config_dir + '/miragerc')
|
|
220 |
if conf.has_option('window', 'w'):
|
|
221 |
width = conf.getint('window', 'w')
|
|
222 |
if conf.has_option('window', 'h'):
|
|
223 |
height = conf.getint('window', 'h')
|
|
224 |
if conf.has_option('window', 'toolbar'):
|
|
225 |
self.toolbar_show = conf.getboolean('window', 'toolbar')
|
|
226 |
if conf.has_option('window', 'statusbar'):
|
|
227 |
self.statusbar_show = conf.getboolean('window', 'statusbar')
|
|
228 |
if conf.has_option('window', 'thumbpane'):
|
|
229 |
self.thumbpane_show = conf.getboolean('window', 'thumbpane')
|
|
230 |
if conf.has_option('prefs', 'simple-bgcolor'):
|
|
231 |
self.simple_bgcolor = conf.getboolean('prefs', 'simple-bgcolor')
|
|
232 |
if conf.has_option('prefs', 'bgcolor-red'):
|
|
233 |
bgr = conf.getint('prefs', 'bgcolor-red')
|
|
234 |
bgg = conf.getint('prefs', 'bgcolor-green')
|
|
235 |
bgb = conf.getint('prefs', 'bgcolor-blue')
|
|
236 |
bgcolor_found = True
|
|
237 |
self.bgcolor = gtk.gdk.Color(red=bgr, green=bgg, blue=bgb)
|
|
238 |
if conf.has_option('prefs', 'use_last_dir'):
|
|
239 |
self.use_last_dir = conf.getboolean('prefs', 'use_last_dir')
|
|
240 |
if conf.has_option('prefs', 'last_dir'):
|
|
241 |
self.last_dir = conf.get('prefs', 'last_dir')
|
|
242 |
if conf.has_option('prefs', 'fixed_dir'):
|
|
243 |
self.fixed_dir = conf.get('prefs', 'fixed_dir')
|
|
244 |
if conf.has_option('prefs', 'open_all'):
|
|
245 |
self.open_all_images = conf.getboolean('prefs', 'open_all')
|
|
246 |
if conf.has_option('prefs', 'hidden'):
|
|
247 |
self.open_hidden_files = conf.getboolean('prefs', 'hidden')
|
|
248 |
if conf.has_option('prefs', 'open_mode'):
|
|
249 |
self.open_mode = conf.getint('prefs', 'open_mode')
|
|
250 |
if conf.has_option('prefs', 'last_mode'):
|
|
251 |
self.last_mode = conf.getint('prefs', 'last_mode')
|
|
252 |
if conf.has_option('prefs', 'listwrap_mode'):
|
|
253 |
self.listwrap_mode = conf.getint('prefs', 'listwrap_mode')
|
|
254 |
if conf.has_option('prefs', 'slideshow_delay'):
|
|
255 |
self.slideshow_delay = conf.getint('prefs', 'slideshow_delay')
|
|
256 |
if conf.has_option('prefs', 'slideshow_random'):
|
|
257 |
self.slideshow_random = conf.getboolean('prefs', 'slideshow_random')
|
|
258 |
if conf.has_option('prefs', 'zoomquality'):
|
|
259 |
self.zoomvalue = conf.getint('prefs', 'zoomquality')
|
|
260 |
if int(round(self.zoomvalue, 0)) == 0:
|
|
261 |
self.zoom_quality = gtk.gdk.INTERP_NEAREST
|
|
262 |
elif int(round(self.zoomvalue, 0)) == 1:
|
|
263 |
self.zoom_quality = gtk.gdk.INTERP_TILES
|
|
264 |
elif int(round(self.zoomvalue, 0)) == 2:
|
|
265 |
self.zoom_quality = gtk.gdk.INTERP_BILINEAR
|
|
266 |
elif int(round(self.zoomvalue, 0)) == 3:
|
|
267 |
self.zoom_quality = gtk.gdk.INTERP_HYPER
|
|
268 |
if conf.has_option('prefs', 'quality_save'):
|
|
269 |
self.quality_save = conf.getint('prefs', 'quality_save')
|
|
270 |
if conf.has_option('prefs', 'disable_screensaver'):
|
|
271 |
self.disable_screensaver = conf.getboolean('prefs', 'disable_screensaver')
|
|
272 |
if conf.has_option('prefs', 'slideshow_in_fullscreen'):
|
|
273 |
self.slideshow_in_fullscreen = conf.getboolean('prefs', 'slideshow_in_fullscreen')
|
|
274 |
if conf.has_option('prefs', 'preloading_images'):
|
|
275 |
self.preloading_images = conf.getboolean('prefs', 'preloading_images')
|
|
276 |
if conf.has_option('prefs', 'thumbsize'):
|
|
277 |
self.thumbnail_size = conf.getint('prefs', 'thumbsize')
|
|
278 |
if conf.has_option('prefs', 'screenshot_delay'):
|
|
279 |
self.screenshot_delay = conf.getint('prefs', 'screenshot_delay')
|
|
280 |
if conf.has_option('actions', 'num_actions'):
|
|
281 |
num_actions = conf.getint('actions', 'num_actions')
|
|
282 |
self.action_names = []
|
|
283 |
self.action_commands = []
|
|
284 |
self.action_shortcuts = []
|
|
285 |
self.action_batch = []
|
|
286 |
for i in range(num_actions):
|
|
287 |
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) + ']'):
|
|
288 |
self.action_names.append(conf.get('actions', 'names[' + str(i) + ']'))
|
|
289 |
self.action_commands.append(conf.get('actions', 'commands[' + str(i) + ']'))
|
|
290 |
self.action_shortcuts.append(conf.get('actions', 'shortcuts[' + str(i) + ']'))
|
|
291 |
self.action_batch.append(conf.getboolean('actions', 'batch[' + str(i) + ']'))
|
|
292 |
if conf.has_option('prefs', 'savemode'):
|
|
293 |
self.savemode = conf.getint('prefs', 'savemode')
|
|
294 |
if conf.has_option('prefs', 'start_in_fullscreen'):
|
|
295 |
self.start_in_fullscreen = conf.getboolean('prefs', 'start_in_fullscreen')
|
|
296 |
if conf.has_option('prefs', 'confirm_delete'):
|
|
297 |
self.confirm_delete = conf.getboolean('prefs', 'confirm_delete')
|
|
298 |
self.recentfiles = []
|
|
299 |
if conf.has_option('recent', 'num_recent'):
|
|
300 |
num_recent = conf.getint('recent', 'num_recent')
|
|
301 |
for i in range(num_recent):
|
|
302 |
self.recentfiles.append('')
|
|
303 |
if conf.has_option('recent', 'urls[' + str(i) + ',0]'):
|
|
304 |
self.recentfiles[i] = conf.get('recent', 'urls[' + str(i) + ',0]')
|
|
305 |
# slideshow_delay is the user's preference, whereas curr_slideshow_delay is
|
|
306 |
# the current delay (which can be changed without affecting the 'default')
|
|
307 |
self.curr_slideshow_delay = self.slideshow_delay
|
|
308 |
# Same for randomization:
|
|
309 |
self.curr_slideshow_random = self.slideshow_random
|
|
310 |
|
|
311 |
# Read accel_map file, if it exists
|
|
312 |
if os.path.isfile(self.config_dir + '/accel_map'):
|
|
313 |
gtk.accel_map_load(self.config_dir + '/accel_map')
|
|
314 |
|
|
315 |
# Directory/ies in which to find application images/pixmaps
|
|
316 |
self.resource_path_list = False
|
|
317 |
|
|
318 |
self.blank_image = gtk.gdk.pixbuf_new_from_file(self.find_path("mirage_blank.png"))
|
|
319 |
|
|
320 |
# Define the main menubar and toolbar:
|
|
321 |
factory = gtk.IconFactory()
|
|
322 |
iconname = 'stock_leave-fullscreen.png'
|
|
323 |
iconname2 = 'stock_fullscreen.png'
|
|
324 |
leave_fullscreen_icon_path = self.find_path(iconname)
|
|
325 |
pixbuf = gtk.gdk.pixbuf_new_from_file(leave_fullscreen_icon_path)
|
|
326 |
iconset = gtk.IconSet(pixbuf)
|
|
327 |
factory.add('leave-fullscreen', iconset)
|
|
328 |
factory.add_default()
|
|
329 |
fullscreen_icon_path = self.find_path(iconname2)
|
|
330 |
pixbuf = gtk.gdk.pixbuf_new_from_file(fullscreen_icon_path)
|
|
331 |
iconset = gtk.IconSet(pixbuf)
|
|
332 |
factory.add('fullscreen', iconset)
|
|
333 |
factory.add_default()
|
|
334 |
try:
|
|
335 |
test = gtk.Button("", gtk.STOCK_LEAVE_FULLSCREEN)
|
|
336 |
leave_fullscreen_icon = gtk.STOCK_LEAVE_FULLSCREEN
|
|
337 |
fullscreen_icon = gtk.STOCK_FULLSCREEN
|
|
338 |
except:
|
|
339 |
# This will allow gtk 2.6 users to run Mirage
|
|
340 |
leave_fullscreen_icon = 'leave-fullscreen'
|
|
341 |
fullscreen_icon = 'fullscreen'
|
|
342 |
actions = (
|
|
343 |
('FileMenu', None, _('_File')),
|
|
344 |
('EditMenu', None, _('_Edit')),
|
|
345 |
('ViewMenu', None, _('_View')),
|
|
346 |
('GoMenu', None, _('_Go')),
|
|
347 |
('HelpMenu', None, _('_Help')),
|
|
348 |
('ActionSubMenu', None, _('Custom _Actions')),
|
|
349 |
('Open Image', gtk.STOCK_FILE, _('_Open Image...'), '<Ctrl>O', _('Open Image'), self.open_file),
|
|
350 |
('Open Remote Image', gtk.STOCK_NETWORK, _('Open _Remote image...'), None, _('Open Remote Image'), self.open_file_remote),
|
|
351 |
('Open Folder', gtk.STOCK_DIRECTORY, _('Open _Folder...'), '<Ctrl>F', _('Open Folder'), self.open_folder),
|
|
352 |
('Save', gtk.STOCK_SAVE, _('_Save Image'), '<Ctrl>S', _('Save Image'), self.save_image),
|
|
353 |
('Save As', gtk.STOCK_SAVE, _('Save Image _As...'), '<Shift><Ctrl>S', _('Save Image As'), self.save_image_as),
|
|
354 |
('Crop', None, _('_Crop...'), None, _('Crop Image'), self.crop_image),
|
|
355 |
('Resize', None, _('R_esize...'), None, _('Resize Image'), self.resize_image),
|
|
356 |
('Saturation', None, _('_Saturation...'), None, _('Modify saturation'), self.saturation),
|
|
357 |
('Quit', gtk.STOCK_QUIT, _('_Quit'), '<Ctrl>Q', _('Quit'), self.exit_app),
|
|
358 |
('Previous Image', gtk.STOCK_GO_BACK, _('_Previous Image'), 'Left', _('Previous Image'), self.goto_prev_image),
|
|
359 |
('Next Image', gtk.STOCK_GO_FORWARD, _('_Next Image'), 'Right', _('Next Image'), self.goto_next_image),
|
|
360 |
('Previous2', gtk.STOCK_GO_BACK, _('_Previous'), 'Left', _('Previous'), self.goto_prev_image),
|
|
361 |
('Next2', gtk.STOCK_GO_FORWARD, _('_Next'), 'Right', _('Next'), self.goto_next_image),
|
|
362 |
('Random Image', None, _('_Random Image'), 'R', _('Random Image'), self.goto_random_image),
|
|
363 |
('First Image', gtk.STOCK_GOTO_FIRST, _('_First Image'), 'Home', _('First Image'), self.goto_first_image),
|
|
364 |
('Last Image', gtk.STOCK_GOTO_LAST, _('_Last Image'), 'End', _('Last Image'), self.goto_last_image),
|
|
365 |
('In', gtk.STOCK_ZOOM_IN, _('Zoom _In'), '<Ctrl>Up', _('Zoom In'), self.zoom_in),
|
|
366 |
('Out', gtk.STOCK_ZOOM_OUT, _('Zoom _Out'), '<Ctrl>Down', _('Zoom Out'), self.zoom_out),
|
|
367 |
('Fit', gtk.STOCK_ZOOM_FIT, _('Zoom To _Fit'), '<Ctrl>0', _('Fit'), self.zoom_to_fit_window_action),
|
|
368 |
('1:1', gtk.STOCK_ZOOM_100, _('_1:1'), '<Ctrl>1', _('1:1'), self.zoom_1_to_1_action),
|
|
369 |
('Rotate Left', None, _('Rotate _Left'), '<Ctrl>Left', _('Rotate Left'), self.rotate_left),
|
|
370 |
('Rotate Right', None, _('Rotate _Right'), '<Ctrl>Right', _('Rotate Right'), self.rotate_right),
|
|
371 |
('Flip Vertically', None, _('Flip _Vertically'), '<Ctrl>V', _('Flip Vertically'), self.flip_image_vert),
|
|
372 |
('Flip Horizontally', None, _('Flip _Horizontally'), '<Ctrl>H', _('Flip Horizontally'), self.flip_image_horiz),
|
|
373 |
('About', gtk.STOCK_ABOUT, _('_About'), None, _('About'), self.show_about),
|
|
374 |
('Contents', gtk.STOCK_HELP, _('_Contents'), 'F1', _('Contents'), self.show_help),
|
|
375 |
('Preferences', gtk.STOCK_PREFERENCES, _('_Preferences...'), '<Ctrl>P', _('Preferences'), self.show_prefs),
|
|
376 |
('Full Screen', fullscreen_icon, _('_Full Screen'), 'F11', _('Full Screen'), self.enter_fullscreen),
|
|
377 |
('Exit Full Screen', leave_fullscreen_icon, _('E_xit Full Screen'), None, _('Exit Full Screen'), self.leave_fullscreen),
|
|
378 |
('Start Slideshow', gtk.STOCK_MEDIA_PLAY, _('_Start Slideshow'), 'F5', _('Start Slideshow'), self.toggle_slideshow),
|
|
379 |
('Stop Slideshow', gtk.STOCK_MEDIA_STOP, _('_Stop Slideshow'), 'F5', _('Stop Slideshow'), self.toggle_slideshow),
|
|
380 |
('Delete Image', gtk.STOCK_DELETE, _('_Delete...'), 'Delete', _('Delete Image'), self.delete_image),
|
|
381 |
('Rename Image', None, _('Re_name...'), 'F2', _('Rename Image'), self.rename_image),
|
|
382 |
('Take Screenshot', None, _('_Take Screenshot...'), None, _('Take Screenshot'), self.screenshot),
|
|
383 |
('Properties', gtk.STOCK_PROPERTIES, _('_Properties...'), None, _('Properties'), self.show_properties),
|
|
384 |
('Custom Actions', None, _('_Configure...'), None, _('Custom Actions'), self.show_custom_actions),
|
|
385 |
('MiscKeysMenuHidden', None, 'Keys'),
|
|
386 |
('Escape', None, '', 'Escape', _('Exit Full Screen'), self.leave_fullscreen),
|
|
387 |
('Minus', None, '', 'minus', _('Zoom Out'), self.zoom_out),
|
|
388 |
('Plus', None, '', 'plus', _('Zoom In'), self.zoom_in),
|
|
389 |
('Equal', None, '', 'equal', _('Zoom In'), self.zoom_in),
|
|
390 |
('Space', None, '', 'space', _('Next Image'), self.goto_next_image),
|
|
391 |
('Ctrl-KP_Insert', None, '', '<Ctrl>KP_Insert', _('Fit'), self.zoom_to_fit_window_action),
|
|
392 |
('Ctrl-KP_End', None, '', '<Ctrl>KP_End', _('1:1'), self.zoom_1_to_1_action),
|
|
393 |
('Ctrl-KP_Subtract', None, '', '<Ctrl>KP_Subtract', _('Zoom Out'), self.zoom_out),
|
|
394 |
('Ctrl-KP_Add', None, '', '<Ctrl>KP_Add', _('Zoom In'), self.zoom_in),
|
|
395 |
('Ctrl-KP_0', None, '', '<Ctrl>KP_0', _('Fit'), self.zoom_to_fit_window_action),
|
|
396 |
('Ctrl-KP_1', None, '', '<Ctrl>KP_1', _('1:1'), self.zoom_1_to_1_action),
|
|
397 |
('Full Screen Key', None, '', '<Shift>Return', None, self.enter_fullscreen),
|
|
398 |
('Prev', None, '', 'Up', _('Previous Image'), self.goto_prev_image),
|
|
399 |
('Next', None, '', 'Down', _('Next Image'), self.goto_next_image),
|
|
400 |
('PgUp', None, '', 'Page_Up', _('Previous Image'), self.goto_prev_image),
|
|
401 |
('PgDn', None, '', 'Page_Down', _('Next Image'), self.goto_next_image),
|
|
402 |
('BackSpace', None, '', 'BackSpace', _('Previous Image'), self.goto_prev_image),
|
|
403 |
('OriginalSize', None, '', '1', _('1:1'), self.zoom_1_to_1_action),
|
|
404 |
('ZoomIn', None, '', 'KP_Add', _('Zoom In'), self.zoom_in),
|
|
405 |
('ZoomOut', None, '', 'KP_Subtract', _('Zoom Out'), self.zoom_out)
|
|
406 |
)
|
|
407 |
toggle_actions = (
|
|
408 |
('Status Bar', None, _('_Status Bar'), None, _('Status Bar'), self.toggle_status_bar, self.statusbar_show),
|
|
409 |
('Toolbar', None, _('_Toolbar'), None, _('Toolbar'), self.toggle_toolbar, self.toolbar_show),
|
|
410 |
('Thumbnails Pane', None, _('Thumbnails _Pane'), None, _('Thumbnails Pane'), self.toggle_thumbpane, self.thumbpane_show)
|
|
411 |
)
|
|
412 |
|
|
413 |
# Populate keys[]:
|
|
414 |
self.keys=[]
|
|
415 |
for i in range(len(actions)):
|
|
416 |
if len(actions[i]) > 3:
|
|
417 |
if actions[i][3] != None:
|
|
418 |
self.keys.append([actions[i][4], actions[i][3]])
|
|
419 |
|
|
420 |
uiDescription = """
|
|
421 |
<ui>
|
|
422 |
<popup name="Popup">
|
|
423 |
<menuitem action="Next Image"/>
|
|
424 |
<menuitem action="Previous Image"/>
|
|
425 |
<separator name="FM1"/>
|
|
426 |
<menuitem action="Out"/>
|
|
427 |
<menuitem action="In"/>
|
|
428 |
<menuitem action="1:1"/>
|
|
429 |
<menuitem action="Fit"/>
|
|
430 |
<separator name="FM4"/>
|
|
431 |
<menuitem action="Start Slideshow"/>
|
|
432 |
<menuitem action="Stop Slideshow"/>
|
|
433 |
<separator name="FM3"/>
|
|
434 |
<menuitem action="Exit Full Screen"/>
|
|
435 |
<menuitem action="Full Screen"/>
|
|
436 |
</popup>
|
|
437 |
<menubar name="MainMenu">
|
|
438 |
<menu action="FileMenu">
|
|
439 |
<menuitem action="Open Image"/>
|
|
440 |
<menuitem action="Open Folder"/>
|
|
441 |
<menuitem action="Open Remote Image"/>
|
|
442 |
<separator name="FM1"/>
|
|
443 |
<menuitem action="Save"/>
|
|
444 |
<menuitem action="Save As"/>
|
|
445 |
<separator name="FM2"/>
|
|
446 |
<menuitem action="Take Screenshot"/>
|
|
447 |
<separator name="FM3"/>
|
|
448 |
<menuitem action="Properties"/>
|
|
449 |
<separator name="FM4"/>
|
|
450 |
<placeholder name="Recent Files">
|
|
451 |
</placeholder>
|
|
452 |
<separator name="FM5"/>
|
|
453 |
<menuitem action="Quit"/>
|
|
454 |
</menu>
|
|
455 |
<menu action="EditMenu">
|
|
456 |
<menuitem action="Rotate Left"/>
|
|
457 |
<menuitem action="Rotate Right"/>
|
|
458 |
<menuitem action="Flip Vertically"/>
|
|
459 |
<menuitem action="Flip Horizontally"/>
|
|
460 |
<separator name="FM1"/>
|
|
461 |
<menuitem action="Crop"/>
|
|
462 |
<menuitem action="Resize"/>
|
|
463 |
<menuitem action="Saturation"/>
|
|
464 |
<separator name="FM2"/>
|
|
465 |
<menuitem action="Rename Image"/>
|
|
466 |
<menuitem action="Delete Image"/>
|
|
467 |
<separator name="FM3"/>
|
|
468 |
<menu action="ActionSubMenu">
|
|
469 |
<separator name="FM4" position="bot"/>
|
|
470 |
<menuitem action="Custom Actions" position="bot"/>
|
|
471 |
</menu>
|
|
472 |
<menuitem action="Preferences"/>
|
|
473 |
</menu>
|
|
474 |
<menu action="ViewMenu">
|
|
475 |
<menuitem action="Out"/>
|
|
476 |
<menuitem action="In"/>
|
|
477 |
<menuitem action="1:1"/>
|
|
478 |
<menuitem action="Fit"/>
|
|
479 |
<separator name="FM2"/>
|
|
480 |
<menuitem action="Toolbar"/>
|
|
481 |
<menuitem action="Thumbnails Pane"/>
|
|
482 |
<menuitem action="Status Bar"/>
|
|
483 |
<separator name="FM1"/>
|
|
484 |
<menuitem action="Full Screen"/>
|
|
485 |
</menu>
|
|
486 |
<menu action="GoMenu">
|
|
487 |
<menuitem action="Next Image"/>
|
|
488 |
<menuitem action="Previous Image"/>
|
|
489 |
<menuitem action="Random Image"/>
|
|
490 |
<separator name="FM1"/>
|
|
491 |
<menuitem action="First Image"/>
|
|
492 |
<menuitem action="Last Image"/>
|
|
493 |
<separator name="FM2"/>
|
|
494 |
<menuitem action="Start Slideshow"/>
|
|
495 |
<menuitem action="Stop Slideshow"/>
|
|
496 |
</menu>
|
|
497 |
<menu action="HelpMenu">
|
|
498 |
<menuitem action="Contents"/>
|
|
499 |
<menuitem action="About"/>
|
|
500 |
</menu>
|
|
501 |
<menu action="MiscKeysMenuHidden">
|
|
502 |
<menuitem action="Minus"/>
|
|
503 |
<menuitem action="Escape"/>
|
|
504 |
<menuitem action="Plus"/>
|
|
505 |
<menuitem action="Equal"/>
|
|
506 |
<menuitem action="Space"/>
|
|
507 |
<menuitem action="Ctrl-KP_Insert"/>
|
|
508 |
<menuitem action="Ctrl-KP_End"/>
|
|
509 |
<menuitem action="Ctrl-KP_Subtract"/>
|
|
510 |
<menuitem action="Ctrl-KP_Add"/>
|
|
511 |
<menuitem action="Ctrl-KP_0"/>
|
|
512 |
<menuitem action="Ctrl-KP_1"/>
|
|
513 |
<menuitem action="Full Screen Key"/>
|
|
514 |
<menuitem action="Prev"/>
|
|
515 |
<menuitem action="Next"/>
|
|
516 |
<menuitem action="PgUp"/>
|
|
517 |
<menuitem action="PgDn"/>
|
|
518 |
<menuitem action="OriginalSize"/>
|
|
519 |
<menuitem action="BackSpace"/>
|
|
520 |
<menuitem action="ZoomIn"/>
|
|
521 |
<menuitem action="ZoomOut"/>
|
|
522 |
</menu>
|
|
523 |
</menubar>
|
|
524 |
<toolbar name="MainToolbar">
|
|
525 |
<toolitem action="Open Image"/>
|
|
526 |
<separator name="FM1"/>
|
|
527 |
<toolitem action="Previous2"/>
|
|
528 |
<toolitem action="Next2"/>
|
|
529 |
<separator name="FM2"/>
|
|
530 |
<toolitem action="Out"/>
|
|
531 |
<toolitem action="In"/>
|
|
532 |
<toolitem action="1:1"/>
|
|
533 |
<toolitem action="Fit"/>
|
|
534 |
</toolbar>
|
|
535 |
</ui>
|
|
536 |
"""
|
|
537 |
|
|
538 |
# Create interface
|
|
539 |
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
|
|
540 |
self.update_title()
|
|
541 |
icon_path = self.find_path('mirage.png')
|
|
542 |
try:
|
|
543 |
gtk.window_set_default_icon_from_file(icon_path)
|
|
544 |
except:
|
|
545 |
pass
|
|
546 |
vbox = gtk.VBox(False, 0)
|
|
547 |
self.UIManager = gtk.UIManager()
|
|
548 |
actionGroup = gtk.ActionGroup('Actions')
|
|
549 |
actionGroup.add_actions(actions)
|
|
550 |
actionGroup.add_toggle_actions(toggle_actions)
|
|
551 |
self.UIManager.insert_action_group(actionGroup, 0)
|
|
552 |
self.UIManager.add_ui_from_string(uiDescription)
|
|
553 |
self.refresh_custom_actions_menu()
|
|
554 |
self.refresh_recent_files_menu()
|
|
555 |
self.window.add_accel_group(self.UIManager.get_accel_group())
|
|
556 |
self.menubar = self.UIManager.get_widget('/MainMenu')
|
|
557 |
vbox.pack_start(self.menubar, False, False, 0)
|
|
558 |
self.toolbar = self.UIManager.get_widget('/MainToolbar')
|
|
559 |
vbox.pack_start(self.toolbar, False, False, 0)
|
|
560 |
self.layout = gtk.Layout()
|
|
561 |
self.vscroll = gtk.VScrollbar(None)
|
|
562 |
self.vscroll.set_adjustment(self.layout.get_vadjustment())
|
|
563 |
self.hscroll = gtk.HScrollbar(None)
|
|
564 |
self.hscroll.set_adjustment(self.layout.get_hadjustment())
|
|
565 |
self.table = gtk.Table(3, 2, False)
|
|
566 |
|
|
567 |
self.thumblist = gtk.ListStore(gtk.gdk.Pixbuf)
|
|
568 |
self.thumbpane = gtk.TreeView(self.thumblist)
|
|
569 |
self.thumbcolumn = gtk.TreeViewColumn(None)
|
|
570 |
self.thumbcell = gtk.CellRendererPixbuf()
|
|
571 |
self.thumbcolumn.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
|
|
572 |
self.thumbpane_set_size()
|
|
573 |
self.thumbpane.append_column(self.thumbcolumn)
|
|
574 |
self.thumbcolumn.pack_start(self.thumbcell, True)
|
|
575 |
self.thumbcolumn.set_attributes(self.thumbcell, pixbuf=0)
|
|
576 |
self.thumbpane.get_selection().set_mode(gtk.SELECTION_SINGLE)
|
|
577 |
self.thumbpane.set_headers_visible(False)
|
|
578 |
self.thumbpane.set_property('can-focus', False)
|
|
579 |
self.thumbscroll = gtk.ScrolledWindow()
|
|
580 |
self.thumbscroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
|
|
581 |
self.thumbscroll.add(self.thumbpane)
|
|
582 |
|
|
583 |
self.table.attach(self.thumbscroll, 0, 1, 0, 1, 0, gtk.FILL|gtk.EXPAND, 0, 0)
|
|
584 |
self.table.attach(self.layout, 1, 2, 0, 1, gtk.FILL|gtk.EXPAND, gtk.FILL|gtk.EXPAND, 0, 0)
|
|
585 |
self.table.attach(self.hscroll, 1, 2, 1, 2, gtk.FILL|gtk.SHRINK, gtk.FILL|gtk.SHRINK, 0, 0)
|
|
586 |
self.table.attach(self.vscroll, 2, 3, 0, 1, gtk.FILL|gtk.SHRINK, gtk.FILL|gtk.SHRINK, 0, 0)
|
|
587 |
vbox.pack_start(self.table, True, True, 0)
|
|
588 |
if not bgcolor_found:
|
|
589 |
self.bgcolor = gtk.gdk.Color(0, 0, 0) # Default to black
|
|
590 |
if self.simple_bgcolor:
|
|
591 |
self.layout.modify_bg(gtk.STATE_NORMAL, None)
|
|
592 |
else:
|
|
593 |
self.layout.modify_bg(gtk.STATE_NORMAL, self.bgcolor)
|
|
594 |
self.imageview = gtk.Image()
|
|
595 |
self.layout.add(self.imageview)
|
|
596 |
|
|
597 |
self.statusbar = gtk.Statusbar()
|
|
598 |
self.statusbar2 = gtk.Statusbar()
|
|
599 |
self.statusbar.set_has_resize_grip(False)
|
|
600 |
self.statusbar2.set_has_resize_grip(True)
|
|
601 |
self.statusbar2.set_size_request(200, -1)
|
|
602 |
hbox_statusbar = gtk.HBox()
|
|
603 |
hbox_statusbar.pack_start(self.statusbar, expand=True)
|
|
604 |
hbox_statusbar.pack_start(self.statusbar2, expand=False)
|
|
605 |
vbox.pack_start(hbox_statusbar, False, False, 0)
|
|
606 |
self.window.add(vbox)
|
|
607 |
self.window.set_property('allow-shrink', False)
|
|
608 |
self.window.set_default_size(width,height)
|
|
609 |
|
|
610 |
# Slideshow control:
|
|
611 |
self.slideshow_window = gtk.Window(gtk.WINDOW_POPUP)
|
|
612 |
self.slideshow_controls = gtk.HBox()
|
|
613 |
self.ss_back = gtk.Button()
|
|
614 |
self.ss_back.add(gtk.image_new_from_stock(gtk.STOCK_GO_BACK, gtk.ICON_SIZE_BUTTON))
|
|
615 |
self.ss_back.set_property('can-focus', False)
|
|
616 |
self.ss_back.connect('clicked', self.goto_prev_image)
|
|
617 |
self.ss_start = gtk.Button("", gtk.STOCK_MEDIA_PLAY)
|
|
618 |
self.ss_start.get_child().get_child().get_children()[1].set_text('')
|
|
619 |
self.ss_start.set_property('can-focus', False)
|
|
620 |
self.ss_start.connect('clicked', self.toggle_slideshow)
|
|
621 |
self.ss_stop = gtk.Button("", gtk.STOCK_MEDIA_STOP)
|
|
622 |
self.ss_stop.get_child().get_child().get_children()[1].set_text('')
|
|
623 |
self.ss_stop.set_property('can-focus', False)
|
|
624 |
self.ss_stop.connect('clicked', self.toggle_slideshow)
|
|
625 |
self.ss_forward = gtk.Button("", gtk.STOCK_GO_FORWARD)
|
|
626 |
self.ss_forward.get_child().get_child().get_children()[1].set_text('')
|
|
627 |
self.ss_forward.set_property('can-focus', False)
|
|
628 |
self.ss_forward.connect('clicked', self.goto_next_image)
|
|
629 |
self.slideshow_controls.pack_start(self.ss_back, False, False, 0)
|
|
630 |
self.slideshow_controls.pack_start(self.ss_start, False, False, 0)
|
|
631 |
self.slideshow_controls.pack_start(self.ss_stop, False, False, 0)
|
|
632 |
self.slideshow_controls.pack_start(self.ss_forward, False, False, 0)
|
|
633 |
self.slideshow_window.add(self.slideshow_controls)
|
|
634 |
if self.simple_bgcolor:
|
|
635 |
self.slideshow_window.modify_bg(gtk.STATE_NORMAL, None)
|
|
636 |
else:
|
|
637 |
self.slideshow_window.modify_bg(gtk.STATE_NORMAL, self.bgcolor)
|
|
638 |
self.slideshow_window2 = gtk.Window(gtk.WINDOW_POPUP)
|
|
639 |
self.slideshow_controls2 = gtk.HBox()
|
|
640 |
try:
|
|
641 |
self.ss_exit = gtk.Button("", gtk.STOCK_LEAVE_FULLSCREEN)
|
|
642 |
self.ss_exit.get_child().get_child().get_children()[1].set_text('')
|
|
643 |
except:
|
|
644 |
self.ss_exit = gtk.Button()
|
|
645 |
self.ss_exit.set_image(gtk.image_new_from_stock('leave-fullscreen', gtk.ICON_SIZE_MENU))
|
|
646 |
self.ss_exit.set_property('can-focus', False)
|
|
647 |
self.ss_exit.connect('clicked', self.leave_fullscreen)
|
|
648 |
self.ss_randomize = gtk.ToggleButton()
|
|
649 |
icon_path = self.find_path('stock_shuffle.png')
|
|
650 |
try:
|
|
651 |
pixbuf = gtk.gdk.pixbuf_new_from_file(icon_path)
|
|
652 |
iconset = gtk.IconSet(pixbuf)
|
|
653 |
factory.add('stock-shuffle', iconset)
|
|
654 |
factory.add_default()
|
|
655 |
self.ss_randomize.set_image(gtk.image_new_from_stock('stock-shuffle', gtk.ICON_SIZE_MENU))
|
|
656 |
except:
|
|
657 |
self.ss_randomize.set_label("Rand")
|
|
658 |
self.ss_randomize.connect('toggled', self.random_changed)
|
|
659 |
|
|
660 |
spin_adj = gtk.Adjustment(self.slideshow_delay, 0, 50000, 1,100, 0)
|
|
661 |
self.ss_delayspin = gtk.SpinButton(spin_adj, 1.0, 0)
|
|
662 |
self.ss_delayspin.set_numeric(True)
|
|
663 |
self.ss_delayspin.connect('changed', self.delay_changed)
|
|
664 |
self.slideshow_controls2.pack_start(self.ss_randomize, False, False, 0)
|
|
665 |
self.slideshow_controls2.pack_start(self.ss_delayspin, False, False, 0)
|
|
666 |
self.slideshow_controls2.pack_start(self.ss_exit, False, False, 0)
|
|
667 |
self.slideshow_window2.add(self.slideshow_controls2)
|
|
668 |
if self.simple_bgcolor:
|
|
669 |
self.slideshow_window2.modify_bg(gtk.STATE_NORMAL, None)
|
|
670 |
else:
|
|
671 |
self.slideshow_window2.modify_bg(gtk.STATE_NORMAL, self.bgcolor)
|
|
672 |
|
|
673 |
# Connect signals
|
|
674 |
self.window.connect("delete_event", self.delete_event)
|
|
675 |
self.window.connect("destroy", self.destroy)
|
|
676 |
self.window.connect("size-allocate", self.window_resized)
|
|
677 |
self.window.connect('key-press-event', self.topwindow_keypress)
|
|
678 |
self.toolbar.connect('focus', self.toolbar_focused)
|
|
679 |
self.layout.drag_dest_set(gtk.DEST_DEFAULT_HIGHLIGHT | gtk.DEST_DEFAULT_DROP, [("text/uri-list", 0, 80)], gtk.gdk.ACTION_DEFAULT)
|
|
680 |
self.layout.connect('drag_motion', self.motion_cb)
|
|
681 |
self.layout.connect('drag_data_received', self.drop_cb)
|
|
682 |
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)
|
|
683 |
self.layout.connect("scroll-event", self.mousewheel_scrolled)
|
|
684 |
self.layout.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.KEY_PRESS_MASK)
|
|
685 |
self.layout.connect("button_press_event", self.button_pressed)
|
|
686 |
self.layout.add_events(gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.POINTER_MOTION_HINT_MASK | gtk.gdk.BUTTON_RELEASE_MASK)
|
|
687 |
self.layout.connect("motion-notify-event", self.mouse_moved)
|
|
688 |
self.layout.connect("button-release-event", self.button_released)
|
|
689 |
self.imageview.connect("expose-event", self.expose_event)
|
|
690 |
self.thumb_sel_handler = self.thumbpane.get_selection().connect('changed', self.thumbpane_selection_changed)
|
|
691 |
self.thumb_scroll_handler = self.thumbscroll.get_vscrollbar().connect("value-changed", self.thumbpane_scrolled)
|
|
692 |
|
|
693 |
# Since GNOME does its own thing for the toolbar style...
|
|
694 |
# Requires gnome-python installed to work (but optional)
|
|
695 |
try:
|
|
696 |
client = gconf.client_get_default()
|
|
697 |
style = client.get_string('/desktop/gnome/interface/toolbar_style')
|
|
698 |
if style == "both":
|
|
699 |
self.toolbar.set_style(gtk.TOOLBAR_BOTH)
|
|
700 |
elif style == "both-horiz":
|
|
701 |
self.toolbar.set_style(gtk.TOOLBAR_BOTH_HORIZ)
|
|
702 |
elif style == "icons":
|
|
703 |
self.toolbar.set_style(gtk.TOOLBAR_ICONS)
|
|
704 |
elif style == "text":
|
|
705 |
self.toolbar.set_style(gtk.TOOLBAR_TEXT)
|
|
706 |
client.add_dir("/desktop/gnome/interface", gconf.CLIENT_PRELOAD_NONE)
|
|
707 |
client.notify_add("/desktop/gnome/interface/toolbar_style", self.gconf_key_changed)
|
|
708 |
except:
|
|
709 |
pass
|
|
710 |
|
|
711 |
# Show GUI:
|
|
712 |
if not self.toolbar_show:
|
|
713 |
self.toolbar.set_property('visible', False)
|
|
714 |
self.toolbar.set_no_show_all(True)
|
|
715 |
if not self.statusbar_show:
|
|
716 |
self.statusbar.set_property('visible', False)
|
|
717 |
self.statusbar.set_no_show_all(True)
|
|
718 |
self.statusbar2.set_property('visible', False)
|
|
719 |
self.statusbar2.set_no_show_all(True)
|
|
720 |
if not self.thumbpane_show:
|
|
721 |
self.thumbscroll.set_property('visible', False)
|
|
722 |
self.thumbscroll.set_no_show_all(True)
|
|
723 |
self.hscroll.set_no_show_all(True)
|
|
724 |
self.vscroll.set_no_show_all(True)
|
|
725 |
go_into_fullscreen = False
|
|
726 |
if opts != []:
|
|
727 |
for o, a in opts:
|
|
728 |
if (o in ("-f", "--fullscreen")) or ((o in ("-s", "--slideshow")) and self.slideshow_in_fullscreen):
|
|
729 |
go_into_fullscreen = True
|
|
730 |
if go_into_fullscreen or self.start_in_fullscreen:
|
|
731 |
self.enter_fullscreen(None)
|
|
732 |
self.statusbar.set_no_show_all(True)
|
|
733 |
self.statusbar2.set_no_show_all(True)
|
|
734 |
self.toolbar.set_no_show_all(True)
|
|
735 |
self.menubar.set_no_show_all(True)
|
|
736 |
self.thumbscroll.set_no_show_all(True)
|
|
737 |
self.window.show_all()
|
|
738 |
self.ss_exit.set_size_request(self.ss_start.size_request()[0], self.ss_stop.size_request()[1])
|
|
739 |
self.ss_randomize.set_size_request(self.ss_start.size_request()[0], -1)
|
|
740 |
self.ss_start.set_size_request(self.ss_start.size_request()[0]*2, -1)
|
|
741 |
self.ss_stop.set_size_request(self.ss_stop.size_request()[0]*2, -1)
|
|
742 |
self.UIManager.get_widget('/Popup/Exit Full Screen').hide()
|
|
743 |
self.layout.set_flags(gtk.CAN_FOCUS)
|
|
744 |
self.window.set_focus(self.layout)
|
|
745 |
|
|
746 |
#sets the visibility of some menu entries
|
|
747 |
self.set_slideshow_sensitivities()
|
|
748 |
self.UIManager.get_widget('/MainMenu/MiscKeysMenuHidden').set_property('visible', False)
|
|
749 |
if opts != []:
|
|
750 |
for o, a in opts:
|
|
751 |
if o in ("-f", "--fullscreen"):
|
|
752 |
self.UIManager.get_widget('/Popup/Exit Full Screen').show()
|
|
753 |
|
|
754 |
# If arguments (filenames) were passed, try to open them:
|
|
755 |
self.image_list = []
|
|
756 |
if args != []:
|
|
757 |
for i in range(len(args)):
|
|
758 |
args[i] = urllib.url2pathname(args[i])
|
|
759 |
self.expand_filelist_and_load_image(args)
|
|
760 |
else:
|
|
761 |
self.set_go_sensitivities(False)
|
|
762 |
self.set_image_sensitivities(False)
|
|
763 |
|
|
764 |
if opts != []:
|
|
765 |
for o, a in opts:
|
|
766 |
if o in ("-s", "--slideshow"):
|
|
767 |
self.toggle_slideshow(None)
|
|
768 |
|
|
769 |
def refresh_recent_files_menu(self):
|
|
770 |
if self.merge_id_recent:
|
|
771 |
self.UIManager.remove_ui(self.merge_id_recent)
|
|
772 |
if self.actionGroupRecent:
|
|
773 |
self.UIManager.remove_action_group(self.actionGroupRecent)
|
|
774 |
self.actionGroupRecent = None
|
|
775 |
self.actionGroupRecent = gtk.ActionGroup('RecentFiles')
|
|
776 |
self.UIManager.ensure_update()
|
|
777 |
for i in range(len(self.recentfiles)):
|
|
778 |
if len(self.recentfiles[i]) > 0:
|
|
779 |
filename = self.recentfiles[i].split("/")[-1]
|
|
780 |
if len(filename) > 0:
|
|
781 |
if len(filename) > 27:
|
|
782 |
# Replace end of file name (excluding extension) with ..
|
|
783 |
try:
|
|
784 |
menu_name = filename[:25] + '..' + os.path.splitext(filename)[1]
|
|
785 |
except:
|
|
786 |
menu_name = filename[0]
|
|
787 |
else:
|
|
788 |
menu_name = filename
|
|
789 |
menu_name = menu_name.replace('_','__')
|
|
790 |
action = [(str(i), None, menu_name, '<Alt>' + str(i+1), None, self.recent_action_click)]
|
|
791 |
self.actionGroupRecent.add_actions(action)
|
|
792 |
uiDescription = """
|
|
793 |
<ui>
|
|
794 |
<menubar name="MainMenu">
|
|
795 |
<menu action="FileMenu">
|
|
796 |
<placeholder name="Recent Files">
|
|
797 |
"""
|
|
798 |
for i in range(len(self.recentfiles)):
|
|
799 |
if len(self.recentfiles[i]) > 0:
|
|
800 |
uiDescription = uiDescription + """<menuitem action=\"""" + str(i) + """\"/>"""
|
|
801 |
uiDescription = uiDescription + """</placeholder></menu></menubar></ui>"""
|
|
802 |
self.merge_id_recent = self.UIManager.add_ui_from_string(uiDescription)
|
|
803 |
self.UIManager.insert_action_group(self.actionGroupRecent, 0)
|
|
804 |
self.UIManager.get_widget('/MainMenu/MiscKeysMenuHidden').set_property('visible', False)
|
|
805 |
|
|
806 |
def refresh_custom_actions_menu(self):
|
|
807 |
if self.merge_id:
|
|
808 |
self.UIManager.remove_ui(self.merge_id)
|
|
809 |
if self.actionGroupCustom:
|
|
810 |
self.UIManager.remove_action_group(self.actionGroupCustom)
|
|
811 |
self.actionGroupCustom = None
|
|
812 |
self.actionGroupCustom = gtk.ActionGroup('CustomActions')
|
|
813 |
self.UIManager.ensure_update()
|
|
814 |
for i in range(len(self.action_names)):
|
|
815 |
action = [(self.action_names[i], None, self.action_names[i], self.action_shortcuts[i], None, self.custom_action_click)]
|
|
816 |
self.actionGroupCustom.add_actions(action)
|
|
817 |
uiDescription = """
|
|
818 |
<ui>
|
|
819 |
<menubar name="MainMenu">
|
|
820 |
<menu action="EditMenu">
|
|
821 |
<menu action="ActionSubMenu">
|
|
822 |
"""
|
|
823 |
for i in range(len(self.action_names)):
|
|
824 |
uiDescription = uiDescription + """<menuitem action=\"""" + self.action_names[len(self.action_names)-i-1].replace('&','&') + """\" position="top"/>"""
|
|
825 |
uiDescription = uiDescription + """</menu></menu></menubar></ui>"""
|
|
826 |
self.merge_id = self.UIManager.add_ui_from_string(uiDescription)
|
|
827 |
self.UIManager.insert_action_group(self.actionGroupCustom, 0)
|
|
828 |
self.UIManager.get_widget('/MainMenu/MiscKeysMenuHidden').set_property('visible', False)
|
|
829 |
|
|
830 |
def thumbpane_update_images(self, clear_first=False, force_upto_imgnum=-1):
|
|
831 |
self.stop_now = False
|
|
832 |
# When first populating the thumbpane, make sure we go up to at least
|
|
833 |
# force_upto_imgnum so that we can show this image selected:
|
|
834 |
if clear_first:
|
|
835 |
self.thumbpane_clear_list()
|
|
836 |
# Load all images up to the bottom ofo the visible thumbpane rect:
|
|
837 |
rect = self.thumbpane.get_visible_rect()
|
|
838 |
bottom_coord = rect.y + rect.height + self.thumbnail_size
|
|
839 |
if bottom_coord > self.thumbpane_bottom_coord_loaded:
|
|
840 |
self.thumbpane_bottom_coord_loaded = bottom_coord
|
|
841 |
# update images:
|
|
842 |
if not self.thumbpane_updating:
|
|
843 |
thread = threading.Thread(target=self.thumbpane_update_pending_images, args=(force_upto_imgnum, None))
|
|
844 |
thread.setDaemon(True)
|
|
845 |
thread.start()
|
|
846 |
|
|
847 |
def thumbpane_create_dir(self):
|
|
848 |
if not os.path.exists(os.path.expanduser('~/.thumbnails/')):
|
|
849 |
os.mkdir(os.path.expanduser('~/.thumbnails/'))
|
|
850 |
if not os.path.exists(os.path.expanduser('~/.thumbnails/normal/')):
|
|
851 |
os.mkdir(os.path.expanduser('~/.thumbnails/normal/'))
|
|
852 |
|
|
853 |
def thumbpane_update_pending_images(self, force_upto_imgnum, foo):
|
|
854 |
self.thumbpane_updating = True
|
|
855 |
self.thumbpane_create_dir()
|
|
856 |
# Check to see if any images need their thumbnails generated.
|
|
857 |
curr_coord = 0
|
|
858 |
imgnum = 0
|
|
859 |
while curr_coord < self.thumbpane_bottom_coord_loaded or imgnum <= force_upto_imgnum:
|
|
860 |
if self.closing_app or self.stop_now or not self.thumbpane_show:
|
|
861 |
break
|
|
862 |
if imgnum >= len(self.image_list):
|
|
863 |
break
|
|
864 |
self.thumbpane_set_image(self.image_list[imgnum], imgnum)
|
|
865 |
curr_coord += self.thumbpane.get_background_area((imgnum,),self.thumbcolumn).height
|
|
866 |
if force_upto_imgnum == imgnum:
|
|
867 |
# Verify that the user hasn't switched images while we're loading thumbnails:
|
|
868 |
if force_upto_imgnum == self.curr_img_in_list:
|
|
869 |
gobject.idle_add(self.thumbpane_select, force_upto_imgnum)
|
|
870 |
imgnum += 1
|
|
871 |
self.thumbpane_updating = False
|
|
872 |
|
|
873 |
def thumbpane_clear_list(self):
|
|
874 |
self.thumbpane_bottom_coord_loaded = 0
|
|
875 |
self.thumbscroll.get_vscrollbar().handler_block(self.thumb_scroll_handler)
|
|
876 |
self.thumblist.clear()
|
|
877 |
self.thumbscroll.get_vscrollbar().handler_unblock(self.thumb_scroll_handler)
|
|
878 |
for image in self.image_list:
|
|
879 |
blank_pix = self.get_blank_pix_for_image(image)
|
|
880 |
self.thumblist.append([blank_pix])
|
|
881 |
self.thumbnail_loaded = [False]*len(self.image_list)
|
|
882 |
|
|
883 |
def thumbpane_set_image(self, image_name, imgnum, force_update=False):
|
|
884 |
if self.thumbpane_show:
|
|
885 |
if not self.thumbnail_loaded[imgnum] or force_update:
|
|
886 |
filename, thumbfile = self.thumbnail_get_name(image_name)
|
|
887 |
pix = self.thumbpane_get_pixbuf(thumbfile, filename, force_update)
|
|
888 |
if pix:
|
|
889 |
if self.thumbnail_size != 128:
|
|
890 |
# 128 is the size of the saved thumbnail, so convert if different:
|
|
891 |
pix, image_width, image_height = self.get_pixbuf_of_size(pix, self.thumbnail_size, gtk.gdk.INTERP_TILES)
|
|
892 |
self.thumbnail_loaded[imgnum] = True
|
|
893 |
self.thumbscroll.get_vscrollbar().handler_block(self.thumb_scroll_handler)
|
|
894 |
pix = self.pixbuf_add_border(pix)
|
|
895 |
try:
|
|
896 |
self.thumblist[imgnum] = [pix]
|
|
897 |
except:
|
|
898 |
pass
|
|
899 |
self.thumbscroll.get_vscrollbar().handler_unblock(self.thumb_scroll_handler)
|
|
900 |
|
|
901 |
def thumbnail_get_name(self, image_name):
|
|
902 |
filename = os.path.expanduser('file://' + image_name)
|
|
903 |
uriname = os.path.expanduser('file://' + urllib.pathname2url(image_name))
|
|
904 |
if HAS_HASHLIB:
|
|
905 |
m = hashlib.md5()
|
|
906 |
else:
|
|
907 |
m = md5.new()
|
|
908 |
m.update(uriname)
|
|
909 |
mhex = m.hexdigest()
|
|
910 |
mhex_filename = os.path.expanduser('~/.thumbnails/normal/' + mhex + '.png')
|
|
911 |
return filename, mhex_filename
|
|
912 |
|
|
913 |
def thumbpane_get_pixbuf(self, thumb_url, image_url, force_generation):
|
|
914 |
# Returns a valid pixbuf or None if a pixbuf cannot be generated. Tries to re-use
|
|
915 |
# a thumbnail from ~/.thumbails/normal/, otherwise generates one with the
|
|
916 |
# XDG filename: md5(file:///full/path/to/image).png
|
|
917 |
imgfile = image_url
|
|
918 |
if imgfile[:7] == 'file://':
|
|
919 |
imgfile = imgfile[7:]
|
|
920 |
try:
|
|
921 |
if os.path.exists(thumb_url) and not force_generation:
|
|
922 |
pix = gtk.gdk.pixbuf_new_from_file(thumb_url)
|
|
923 |
pix_mtime = pix.get_option('tEXt::Thumb::MTime')
|
|
924 |
if pix_mtime:
|
|
925 |
st = os.stat(imgfile)
|
|
926 |
file_mtime = str(st[stat.ST_MTIME])
|
|
927 |
# If the mtimes match, we're good. if not, regenerate the thumbnail..
|
|
928 |
if pix_mtime == file_mtime:
|
|
929 |
return pix
|
|
930 |
# Create the 128x128 thumbnail:
|
|
931 |
uri = 'file://' + urllib.pathname2url(imgfile)
|
|
932 |
pix = gtk.gdk.pixbuf_new_from_file(imgfile)
|
|
933 |
pix, image_width, image_height = self.get_pixbuf_of_size(pix, 128, gtk.gdk.INTERP_TILES)
|
|
934 |
st = os.stat(imgfile)
|
|
935 |
file_mtime = str(st[stat.ST_MTIME])
|
|
936 |
# Save image to .thumbnails:
|
|
937 |
pix.save(thumb_url, "png", {'tEXt::Thumb::URI':uri, 'tEXt::Thumb::MTime':file_mtime, 'tEXt::Software':'Mirage' + __version__})
|
|
938 |
return pix
|
|
939 |
except:
|
|
940 |
return None
|
|
941 |
|
|
942 |
def thumbpane_load_image(self, treeview, imgnum):
|
|
943 |
if imgnum != self.curr_img_in_list:
|
|
944 |
gobject.idle_add(self.goto_image, str(imgnum), None)
|
|
945 |
|
|
946 |
def thumbpane_selection_changed(self, treeview):
|
|
947 |
cancel = self.autosave_image()
|
|
948 |
if cancel:
|
|
949 |
# Revert selection...
|
|
950 |
gobject.idle_add(self.thumbpane_select, self.curr_img_in_list)
|
|
951 |
return True
|
|
952 |
try:
|
|
953 |
model, paths = self.thumbpane.get_selection().get_selected_rows()
|
|
954 |
imgnum = paths[0][0]
|
|
955 |
if not self.thumbnail_loaded[imgnum]:
|
|
956 |
self.thumbpane_set_image(self.image_list[imgnum], imgnum)
|
|
957 |
gobject.idle_add(self.thumbpane_load_image, treeview, imgnum)
|
|
958 |
except:
|
|
959 |
pass
|
|
960 |
|
|
961 |
def thumbpane_select(self, imgnum):
|
|
962 |
if self.thumbpane_show:
|
|
963 |
self.thumbpane.get_selection().handler_block(self.thumb_sel_handler)
|
|
964 |
try:
|
|
965 |
self.thumbpane.get_selection().select_path((imgnum,))
|
|
966 |
self.thumbpane.scroll_to_cell((imgnum,))
|
|
967 |
except:
|
|
968 |
pass
|
|
969 |
self.thumbpane.get_selection().handler_unblock(self.thumb_sel_handler)
|
|
970 |
|
|
971 |
def thumbpane_set_size(self):
|
|
972 |
self.thumbcolumn.set_fixed_width(self.thumbpane_get_size())
|
|
973 |
self.window_resized(None, self.window.allocation, True)
|
|
974 |
|
|
975 |
def thumbpane_get_size(self):
|
|
976 |
return int(self.thumbnail_size * 1.3)
|
|
977 |
|
|
978 |
def thumbpane_scrolled(self, range):
|
|
979 |
self.thumbpane_update_images()
|
|
980 |
|
|
981 |
def get_blank_pix_for_image(self, image):
|
|
982 |
# Sizes the "blank image" icon for the thumbpane. This will ensure that we don't
|
|
983 |
# load a humongous icon for a small pix, for example, and will keep the thumbnails
|
|
984 |
# from shifting around when they are actually loaded.
|
|
985 |
try:
|
|
986 |
info = gtk.gdk.pixbuf_get_file_info(image)
|
|
987 |
imgwidth = float(info[1])
|
|
988 |
imgheight = float(info[2])
|
|
989 |
if imgheight > self.thumbnail_size:
|
|
990 |
if imgheight > imgwidth:
|
|
991 |
imgheight = self.thumbnail_size
|
|
992 |
else:
|
|
993 |
imgheight = imgheight/imgwidth * self.thumbnail_size
|
|
994 |
imgheight = 2 + int(imgheight) # Account for border that will be added to thumbnails..
|
|
995 |
imgwidth = self.thumbnail_size
|
|
996 |
except:
|
|
997 |
imgheight = 2 + self.thumbnail_size
|
|
998 |
imgwidth = self.thumbnail_size
|
|
999 |
blank_pix = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, imgwidth, imgheight)
|
|
1000 |
blank_pix.fill(0x00000000)
|
|
1001 |
imgwidth2 = int(imgheight*0.8)
|
|
1002 |
imgheight2 = int(imgheight*0.8)
|
|
1003 |
composite_pix = self.blank_image.scale_simple(imgwidth2, imgheight2, gtk.gdk.INTERP_BILINEAR)
|
|
1004 |
leftcoord = int((imgwidth - imgwidth2)/2)
|
|
1005 |
topcoord = int((imgheight - imgheight2)/2)
|
|
1006 |
composite_pix.copy_area(0, 0, imgwidth2, imgheight2, blank_pix, leftcoord, topcoord)
|
|
1007 |
return blank_pix
|
|
1008 |
|
|
1009 |
def find_path(self, filename, exit_on_fail=True):
|
|
1010 |
""" Find a pixmap or icon by looking through standard dirs.
|
|
1011 |
If the image isn't found exit with error status 1 unless
|
|
1012 |
exit_on_fail is set to False, then return None """
|
|
1013 |
if not self.resource_path_list:
|
|
1014 |
#If executed from mirage in bin this points to the basedir
|
|
1015 |
basedir_mirage = os.path.split(sys.path[0])[0]
|
|
1016 |
#If executed from mirage.py module in python lib this points to the basedir
|
|
1017 |
f0 = os.path.split(__file__)[0].split('/lib')[0]
|
|
1018 |
self.resource_path_list = list(set(filter(os.path.isdir, [
|
|
1019 |
os.path.join(basedir_mirage, 'share', 'mirage'),
|
|
1020 |
os.path.join(basedir_mirage, 'share', 'pixmaps'),
|
|
1021 |
os.path.join(sys.prefix, 'share', 'mirage'),
|
|
1022 |
os.path.join(sys.prefix, 'share', 'pixmaps'),
|
|
1023 |
os.path.join(sys.prefix, 'local', 'share', 'mirage'),
|
|
1024 |
os.path.join(sys.prefix, 'local', 'share', 'pixmaps'),
|
|
1025 |
sys.path[0], #If it's run non-installed
|
|
1026 |
os.path.join(f0, 'share', 'mirage'),
|
|
1027 |
os.path.join(f0, 'share', 'pixmaps'),
|
|
1028 |
])))
|
|
1029 |
for path in self.resource_path_list:
|
|
1030 |
pix = os.path.join(path, filename)
|
|
1031 |
if os.path.exists(pix):
|
|
1032 |
return pix
|
|
1033 |
# If we reached here, we didn't find the pixmap
|
|
1034 |
if exit_on_fail:
|
|
1035 |
print _("Couldn't find the image %s. Please check your installation.") % filename
|
|
1036 |
sys.exit(1)
|
|
1037 |
else:
|
|
1038 |
return None
|
|
1039 |
|
|
1040 |
def gconf_key_changed(self, client, cnxn_id, entry, label):
|
|
1041 |
if entry.value.type == gconf.VALUE_STRING:
|
|
1042 |
style = entry.value.to_string()
|
|
1043 |
if style == "both":
|
|
1044 |
self.toolbar.set_style(gtk.TOOLBAR_BOTH)
|
|
1045 |
elif style == "both-horiz":
|
|
1046 |
self.toolbar.set_style(gtk.TOOLBAR_BOTH_HORIZ)
|
|
1047 |
elif style == "icons":
|
|
1048 |
self.toolbar.set_style(gtk.TOOLBAR_ICONS)
|
|
1049 |
elif style == "text":
|
|
1050 |
self.toolbar.set_style(gtk.TOOLBAR_TEXT)
|
|
1051 |
if self.image_loaded and self.last_image_action_was_fit:
|
|
1052 |
if self.last_image_action_was_smart_fit:
|
|
1053 |
self.zoom_to_fit_or_1_to_1(None, False, False)
|
|
1054 |
else:
|
|
1055 |
self.zoom_to_fit_window(None, False, False)
|
|
1056 |
|
|
1057 |
def toolbar_focused(self, widget, direction):
|
|
1058 |
self.layout.grab_focus()
|
|
1059 |
return True
|
|
1060 |
|
|
1061 |
def topwindow_keypress(self, widget, event):
|
|
1062 |
# For whatever reason, 'Left' and 'Right' cannot be used as menu
|
|
1063 |
# accelerators so we will manually check for them here:
|
|
1064 |
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):
|
|
1065 |
if event.keyval == gtk.gdk.keyval_from_name('Left') or event.keyval == gtk.gdk.keyval_from_name('Up'):
|
|
1066 |
self.goto_prev_image(None)
|
|
1067 |
return
|
|
1068 |
elif event.keyval == gtk.gdk.keyval_from_name('Right') or event.keyval == gtk.gdk.keyval_from_name('Down'):
|
|
1069 |
self.goto_next_image(None)
|
|
1070 |
return
|
|
1071 |
shortcut = gtk.accelerator_name(event.keyval, event.state)
|
|
1072 |
if "Escape" in shortcut:
|
|
1073 |
self.stop_now = True
|
|
1074 |
self.searching_for_images = False
|
|
1075 |
while gtk.events_pending():
|
|
1076 |
gtk.main_iteration()
|
|
1077 |
self.update_title()
|
|
1078 |
return
|
|
1079 |
|
|
1080 |
def parse_action_command(self, command, batchmode):
|
|
1081 |
self.running_custom_actions = True
|
|
1082 |
self.change_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
|
|
1083 |
while gtk.events_pending():
|
|
1084 |
gtk.main_iteration()
|
|
1085 |
self.curr_custom_action = 0
|
|
1086 |
if batchmode:
|
|
1087 |
self.num_custom_actions = len(self.image_list)
|
|
1088 |
for i in range(self.num_custom_actions):
|
|
1089 |
self.curr_custom_action += 1
|
|
1090 |
self.update_statusbar()
|
|
1091 |
while gtk.events_pending():
|
|
1092 |
gtk.main_iteration()
|
|
1093 |
imagename = self.image_list[i]
|
|
1094 |
self.parse_action_command2(command, imagename)
|
|
1095 |
else:
|
|
1096 |
self.num_custom_actions = 1
|
|
1097 |
self.curr_custom_action = 1
|
|
1098 |
self.update_statusbar()
|
|
1099 |
while gtk.events_pending():
|
|
1100 |
gtk.main_iteration()
|
|
1101 |
self.parse_action_command2(command, self.currimg_name)
|
|
1102 |
gc.collect()
|
|
1103 |
self.change_cursor(None)
|
|
1104 |
# Refresh the current image or any preloaded needed if they have changed:
|
|
1105 |
if not os.path.exists(self.currimg_name):
|
|
1106 |
self.currimg_pixbuf_original = None
|
|
1107 |
self.image_load_failed(False)
|
|
1108 |
else:
|
|
1109 |
animtest = gtk.gdk.PixbufAnimation(self.currimg_name)
|
|
1110 |
if animtest.is_static_image():
|
|
1111 |
if self.images_are_different(animtest.get_static_image(), self.currimg_pixbuf_original):
|
|
1112 |
self.load_new_image2(False, False, True, False)
|
|
1113 |
else:
|
|
1114 |
if self.images_are_different(animtest, self.currimg_pixbuf_original):
|
|
1115 |
self.load_new_image2(False, False, True, False)
|
|
1116 |
self.running_custom_actions = False
|
|
1117 |
self.update_statusbar()
|
|
1118 |
while gtk.events_pending():
|
|
1119 |
gtk.main_iteration()
|
|
1120 |
if not os.path.exists(self.preloadimg_prev_name):
|
|
1121 |
self.preloadimg_prev_in_list = -1
|
|
1122 |
else:
|
|
1123 |
animtest = gtk.gdk.PixbufAnimation(self.preloadimg_prev_name)
|
|
1124 |
if animtest.is_static_image():
|
|
1125 |
if self.images_are_different(animtest.get_static_image(), self.preloadimg_prev_pixbuf_original):
|
|
1126 |
self.preloadimg_prev_in_list = -1
|
|
1127 |
self.preload_when_idle = gobject.idle_add(self.preload_prev_image, False)
|
|
1128 |
else:
|
|
1129 |
if self.images_are_different(animtest, self.preloadimg_prev_pixbuf_original):
|
|
1130 |
self.preloadimg_prev_in_list = -1
|
|
1131 |
self.preload_when_idle = gobject.idle_add(self.preload_prev_image, False)
|
|
1132 |
if not os.path.exists(self.preloadimg_next_name):
|
|
1133 |
self.preloadimg_next_in_list = -1
|
|
1134 |
else:
|
|
1135 |
animtest = gtk.gdk.PixbufAnimation(self.preloadimg_next_name)
|
|
1136 |
if animtest.is_static_image():
|
|
1137 |
if self.images_are_different(animtest.get_static_image(), self.preloadimg_next_pixbuf_original):
|
|
1138 |
self.preloadimg_next_in_list = -1
|
|
1139 |
self.preload_when_idle = gobject.idle_add(self.preload_next_image, False)
|
|
1140 |
else:
|
|
1141 |
if self.images_are_different(animtest, self.preloadimg_next_pixbuf_original):
|
|
1142 |
self.preloadimg_next_in_list = -1
|
|
1143 |
self.preload_when_idle = gobject.idle_add(self.preload_next_image, False)
|
|
1144 |
self.stop_now = False
|
|
1145 |
if batchmode:
|
|
1146 |
# Update all thumbnails:
|
|
1147 |
gobject.idle_add(self.thumbpane_update_images, True, self.curr_img_in_list)
|
|
1148 |
else:
|
|
1149 |
# Update only the current thumbnail:
|
|
1150 |
gobject.idle_add(self.thumbpane_set_image, self.image_list[self.curr_img_in_list], self.curr_img_in_list, True)
|
|
1151 |
|
|
1152 |
def images_are_different(self, pixbuf1, pixbuf2):
|
|
1153 |
if pixbuf1.get_pixels() == pixbuf2.get_pixels():
|
|
1154 |
return False
|
|
1155 |
else:
|
|
1156 |
return True
|
|
1157 |
|
|
1158 |
def recent_action_click(self, action):
|
|
1159 |
self.stop_now = True
|
|
1160 |
while gtk.events_pending():
|
|
1161 |
gtk.main_iteration()
|
|
1162 |
cancel = self.autosave_image()
|
|
1163 |
if cancel:
|
|
1164 |
return
|
|
1165 |
index = int(action.get_name())
|
|
1166 |
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://'):
|
|
1167 |
self.expand_filelist_and_load_image([self.recentfiles[index]])
|
|
1168 |
else:
|
|
1169 |
self.image_list = []
|
|
1170 |
self.curr_img_in_list = 0
|
|
1171 |
self.image_list.append(self.recentfiles[index])
|
|
1172 |
self.image_load_failed(False)
|
|
1173 |
self.recent_file_remove_and_refresh(index)
|
|
1174 |
|
|
1175 |
def recent_file_remove_and_refresh_name(self, rmfile):
|
|
1176 |
index_num = 0
|
|
1177 |
for imgfile in self.recentfiles:
|
|
1178 |
if imgfile == rmfile:
|
|
1179 |
self.recent_file_remove_and_refresh(index_num)
|
|
1180 |
break
|
|
1181 |
index_num += index_num
|
|
1182 |
|
|
1183 |
def recent_file_remove_and_refresh(self, index_num):
|
|
1184 |
i = index_num
|
|
1185 |
while i < len(self.recentfiles)-1:
|
|
1186 |
self.recentfiles[i] = self.recentfiles[i+1]
|
|
1187 |
i = i + 1
|
|
1188 |
# Set last item empty:
|
|
1189 |
self.recentfiles[len(self.recentfiles)-1] = ''
|
|
1190 |
self.refresh_recent_files_menu()
|
|
1191 |
|
|
1192 |
def recent_file_add_and_refresh(self, addfile):
|
|
1193 |
# First check if the filename is already in the list:
|
|
1194 |
for i in range(len(self.recentfiles)):
|
|
1195 |
if len(self.recentfiles[i]) > 0:
|
|
1196 |
if addfile == self.recentfiles[i]:
|
|
1197 |
# If found in list, put to position 1 and decrement the rest:
|
|
1198 |
j = i
|
|
1199 |
while j > 0:
|
|
1200 |
self.recentfiles[j] = self.recentfiles[j-1]
|
|
1201 |
j = j - 1
|
|
1202 |
self.recentfiles[0] = addfile
|
|
1203 |
self.refresh_recent_files_menu()
|
|
1204 |
return
|
|
1205 |
# If not found, put to position 1, decrement the rest:
|
|
1206 |
j = len(self.recentfiles)-1
|
|
1207 |
while j > 0:
|
|
1208 |
self.recentfiles[j] = self.recentfiles[j-1]
|
|
1209 |
j = j - 1
|
|
1210 |
if len(self.recentfiles) > 0:
|
|
1211 |
self.recentfiles[0] = addfile
|
|
1212 |
self.refresh_recent_files_menu()
|
|
1213 |
|
|
1214 |
def custom_action_click(self, action):
|
|
1215 |
if self.UIManager.get_widget('/MainMenu/EditMenu/ActionSubMenu/' + action.get_name()).get_property('sensitive'):
|
|
1216 |
for i in range(len(self.action_shortcuts)):
|
|
1217 |
try:
|
|
1218 |
if action.get_name() == self.action_names[i]:
|
|
1219 |
self.parse_action_command(self.action_commands[i], self.action_batch[i])
|
|
1220 |
except:
|
|
1221 |
pass
|
|
1222 |
|
|
1223 |
|
|
1224 |
def parse_action_command2(self, cmd, imagename):
|
|
1225 |
# Executes the given command using ``os.system``, substituting "%"-macros approprately.
|
|
1226 |
def sh_esc(s):
|
|
1227 |
import re
|
|
1228 |
return re.sub(r'[^/._a-zA-Z0-9-]', lambda c: '\\'+c.group(), s)
|
|
1229 |
cmd = cmd.strip()
|
|
1230 |
# [NEXT] and [PREV] are only valid alone or at the end of the command
|
|
1231 |
if cmd == "[NEXT]":
|
|
1232 |
self.goto_next_image(None)
|
|
1233 |
return
|
|
1234 |
elif cmd == "[PREV]":
|
|
1235 |
self.goto_prev_image(None)
|
|
1236 |
return
|
|
1237 |
# -1=go to previous, 1=go to next, 0=don't change
|
|
1238 |
prev_or_next=0
|
|
1239 |
if cmd[-6:] == "[NEXT]":
|
|
1240 |
prev_or_next=1
|
|
1241 |
cmd = cmd[:-6]
|
|
1242 |
elif cmd[-6:] == "[PREV]":
|
|
1243 |
prev_or_next=-1
|
|
1244 |
cmd = cmd[:-6]
|
|
1245 |
if "%F" in cmd:
|
|
1246 |
cmd = cmd.replace("%F", sh_esc(imagename))
|
|
1247 |
if "%N" in cmd:
|
|
1248 |
cmd = cmd.replace("%N", sh_esc(os.path.splitext(os.path.basename(imagename))[0]))
|
|
1249 |
if "%P" in cmd:
|
|
1250 |
cmd = cmd.replace("%P", sh_esc(os.path.dirname(imagename) + "/"))
|
|
1251 |
if "%E" in cmd:
|
|
1252 |
cmd = cmd.replace("%E", sh_esc(os.path.splitext(os.path.basename(imagename))[1]))
|
|
1253 |
if "%L" in cmd:
|
|
1254 |
cmd = cmd.replace("%L", " ".join([sh_esc(s) for s in self.image_list]))
|
|
1255 |
if self.verbose:
|
|
1256 |
print _("Action: %s") % cmd
|
|
1257 |
shell_rc = os.system(cmd) >> 8
|
|
1258 |
if self.verbose:
|
|
1259 |
print _("Action return code: %s") % shell_rc
|
|
1260 |
if shell_rc != 0:
|
|
1261 |
msg = _('Unable to launch \"%s\". Please specify a valid command from Edit > Custom Actions.') % cmd
|
|
1262 |
error_dialog = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_WARNING, gtk.BUTTONS_CLOSE, msg)
|
|
1263 |
error_dialog.set_title(_("Invalid Custom Action"))
|
|
1264 |
error_dialog.run()
|
|
1265 |
error_dialog.destroy()
|
|
1266 |
elif prev_or_next == 1:
|
|
1267 |
self.goto_next_image(None)
|
|
1268 |
elif prev_or_next == -1:
|
|
1269 |
self.goto_prev_image(None)
|
|
1270 |
self.running_custom_actions = False
|
|
1271 |
|
|
1272 |
def set_go_sensitivities(self, enable):
|
|
1273 |
self.UIManager.get_widget('/MainMenu/GoMenu/Previous Image').set_sensitive(enable)
|
|
1274 |
self.UIManager.get_widget('/MainMenu/GoMenu/Next Image').set_sensitive(enable)
|
|
1275 |
self.UIManager.get_widget('/MainMenu/GoMenu/Random Image').set_sensitive(enable)
|
|
1276 |
self.UIManager.get_widget('/MainMenu/GoMenu/First Image').set_sensitive(enable)
|
|
1277 |
self.UIManager.get_widget('/MainMenu/GoMenu/Last Image').set_sensitive(enable)
|
|
1278 |
self.UIManager.get_widget('/Popup/Previous Image').set_sensitive(enable)
|
|
1279 |
self.UIManager.get_widget('/Popup/Next Image').set_sensitive(enable)
|
|
1280 |
self.UIManager.get_widget('/MainToolbar/Previous2').set_sensitive(enable)
|
|
1281 |
self.UIManager.get_widget('/MainToolbar/Next2').set_sensitive(enable)
|
|
1282 |
self.ss_forward.set_sensitive(enable)
|
|
1283 |
self.ss_back.set_sensitive(enable)
|
|
1284 |
|
|
1285 |
def set_image_sensitivities(self, enable):
|
|
1286 |
self.set_zoom_in_sensitivities(enable)
|
|
1287 |
self.set_zoom_out_sensitivities(enable)
|
|
1288 |
self.UIManager.get_widget('/MainMenu/ViewMenu/1:1').set_sensitive(enable)
|
|
1289 |
self.UIManager.get_widget('/MainMenu/ViewMenu/Fit').set_sensitive(enable)
|
|
1290 |
self.UIManager.get_widget('/MainMenu/EditMenu/Delete Image').set_sensitive(enable)
|
|
1291 |
self.UIManager.get_widget('/MainMenu/EditMenu/Rename Image').set_sensitive(enable)
|
|
1292 |
self.UIManager.get_widget('/MainMenu/EditMenu/Crop').set_sensitive(enable)
|
|
1293 |
self.UIManager.get_widget('/MainMenu/EditMenu/Resize').set_sensitive(enable)
|
|
1294 |
self.UIManager.get_widget('/MainMenu/EditMenu/Saturation').set_sensitive(enable)
|
|
1295 |
self.UIManager.get_widget('/MainToolbar/1:1').set_sensitive(enable)
|
|
1296 |
self.UIManager.get_widget('/MainToolbar/Fit').set_sensitive(enable)
|
|
1297 |
self.UIManager.get_widget('/Popup/1:1').set_sensitive(enable)
|
|
1298 |
self.UIManager.get_widget('/Popup/Fit').set_sensitive(enable)
|
|
1299 |
self.UIManager.get_widget('/MainMenu/FileMenu/Save As').set_sensitive(enable)
|
|
1300 |
self.UIManager.get_widget('/MainMenu/FileMenu/Save').set_sensitive(False)
|
|
1301 |
self.UIManager.get_widget('/MainMenu/FileMenu/Properties').set_sensitive(False)
|
|
1302 |
# Only jpeg, png, and bmp images are currently supported for saving
|
|
1303 |
if len(self.image_list) > 0:
|
|
1304 |
try:
|
|
1305 |
filetype = gtk.gdk.pixbuf_get_file_info(self.currimg_name)[0]['name']
|
|
1306 |
self.UIManager.get_widget('/MainMenu/FileMenu/Properties').set_sensitive(True)
|
|
1307 |
if self.filetype_is_writable(filetype):
|
|
1308 |
self.UIManager.get_widget('/MainMenu/FileMenu/Save').set_sensitive(enable)
|
|
1309 |
except:
|
|
1310 |
self.UIManager.get_widget('/MainMenu/FileMenu/Save').set_sensitive(False)
|
|
1311 |
if self.actionGroupCustom:
|
|
1312 |
for action in self.action_names:
|
|
1313 |
self.UIManager.get_widget('/MainMenu/EditMenu/ActionSubMenu/' + action).set_sensitive(enable)
|
|
1314 |
if not HAS_IMGFUNCS:
|
|
1315 |
enable = False
|
|
1316 |
self.UIManager.get_widget('/MainMenu/EditMenu/Rotate Left').set_sensitive(enable)
|
|
1317 |
self.UIManager.get_widget('/MainMenu/EditMenu/Rotate Right').set_sensitive(enable)
|
|
1318 |
self.UIManager.get_widget('/MainMenu/EditMenu/Flip Vertically').set_sensitive(enable)
|
|
1319 |
self.UIManager.get_widget('/MainMenu/EditMenu/Flip Horizontally').set_sensitive(enable)
|
|
1320 |
|
|
1321 |
def set_zoom_in_sensitivities(self, enable):
|
|
1322 |
self.UIManager.get_widget('/MainMenu/ViewMenu/In').set_sensitive(enable)
|
|
1323 |
self.UIManager.get_widget('/MainToolbar/In').set_sensitive(enable)
|
|
1324 |
self.UIManager.get_widget('/Popup/In').set_sensitive(enable)
|
|
1325 |
|
|
1326 |
def set_zoom_out_sensitivities(self, enable):
|
|
1327 |
self.UIManager.get_widget('/MainMenu/ViewMenu/Out').set_sensitive(enable)
|
|
1328 |
self.UIManager.get_widget('/MainToolbar/Out').set_sensitive(enable)
|
|
1329 |
self.UIManager.get_widget('/Popup/Out').set_sensitive(enable)
|
|
1330 |
|
|
1331 |
def set_next_image_sensitivities(self, enable):
|
|
1332 |
self.UIManager.get_widget('/MainToolbar/Next2').set_sensitive(enable)
|
|
1333 |
self.UIManager.get_widget('/MainMenu/GoMenu/Next Image').set_sensitive(enable)
|
|
1334 |
self.UIManager.get_widget('/Popup/Next Image').set_sensitive(enable)
|
|
1335 |
self.ss_forward.set_sensitive(enable)
|
|
1336 |
|
|
1337 |
def set_previous_image_sensitivities(self, enable):
|
|
1338 |
self.UIManager.get_widget('/MainToolbar/Previous2').set_sensitive(enable)
|
|
1339 |
self.UIManager.get_widget('/MainMenu/GoMenu/Previous Image').set_sensitive(enable)
|
|
1340 |
self.UIManager.get_widget('/Popup/Previous Image').set_sensitive(enable)
|
|
1341 |
self.ss_back.set_sensitive(enable)
|
|
1342 |
|
|
1343 |
def set_first_image_sensitivities(self, enable):
|
|
1344 |
self.UIManager.get_widget('/MainMenu/GoMenu/First Image').set_sensitive(enable)
|
|
1345 |
|
|
1346 |
def set_last_image_sensitivities(self, enable):
|
|
1347 |
self.UIManager.get_widget('/MainMenu/GoMenu/Last Image').set_sensitive(enable)
|
|
1348 |
|
|
1349 |
def set_random_image_sensitivities(self, enable):
|
|
1350 |
self.UIManager.get_widget('/MainMenu/GoMenu/Random Image').set_sensitive(enable)
|
|
1351 |
|
|
1352 |
def set_slideshow_sensitivities(self):
|
|
1353 |
if len(self.image_list) <=1:
|
|
1354 |
self.UIManager.get_widget('/MainMenu/GoMenu/Start Slideshow').show()
|
|
1355 |
self.UIManager.get_widget('/MainMenu/GoMenu/Start Slideshow').set_sensitive(False)
|
|
1356 |
self.UIManager.get_widget('/MainMenu/GoMenu/Stop Slideshow').hide()
|
|
1357 |
self.UIManager.get_widget('/MainMenu/GoMenu/Stop Slideshow').set_sensitive(False)
|
|
1358 |
elif self.slideshow_mode:
|
|
1359 |
self.UIManager.get_widget('/MainMenu/GoMenu/Start Slideshow').hide()
|
|
1360 |
self.UIManager.get_widget('/MainMenu/GoMenu/Start Slideshow').set_sensitive(False)
|
|
1361 |
self.UIManager.get_widget('/MainMenu/GoMenu/Stop Slideshow').show()
|
|
1362 |
self.UIManager.get_widget('/MainMenu/GoMenu/Stop Slideshow').set_sensitive(True)
|
|
1363 |
else:
|
|
1364 |
self.UIManager.get_widget('/MainMenu/GoMenu/Start Slideshow').show()
|
|
1365 |
self.UIManager.get_widget('/MainMenu/GoMenu/Start Slideshow').set_sensitive(True)
|
|
1366 |
self.UIManager.get_widget('/MainMenu/GoMenu/Stop Slideshow').hide()
|
|
1367 |
self.UIManager.get_widget('/MainMenu/GoMenu/Stop Slideshow').set_sensitive(False)
|
|
1368 |
if self.slideshow_mode:
|
|
1369 |
self.UIManager.get_widget('/Popup/Start Slideshow').hide()
|
|
1370 |
self.UIManager.get_widget('/Popup/Stop Slideshow').show()
|
|
1371 |
else:
|
|
1372 |
self.UIManager.get_widget('/Popup/Start Slideshow').show()
|
|
1373 |
self.UIManager.get_widget('/Popup/Stop Slideshow').hide()
|
|
1374 |
if len(self.image_list) <=1:
|
|
1375 |
self.UIManager.get_widget('/Popup/Start Slideshow').set_sensitive(False)
|
|
1376 |
else:
|
|
1377 |
self.UIManager.get_widget('/Popup/Start Slideshow').set_sensitive(True)
|
|
1378 |
|
|
1379 |
def set_zoom_sensitivities(self):
|
|
1380 |
if not self.currimg_is_animation:
|
|
1381 |
self.set_zoom_out_sensitivities(True)
|
|
1382 |
self.set_zoom_in_sensitivities(True)
|
|
1383 |
else:
|
|
1384 |
self.set_zoom_out_sensitivities(False)
|
|
1385 |
self.set_zoom_in_sensitivities(False)
|
|
1386 |
|
|
1387 |
def print_version(self):
|
|
1388 |
print _("Version: Mirage"), __version__
|
|
1389 |
print _("Website: http://mirageiv.berlios.de")
|
|
1390 |
|
|
1391 |
def print_usage(self):
|
|
1392 |
self.print_version()
|
|
1393 |
print ""
|
|
1394 |
print _("Usage: mirage [OPTION]... FILES|FOLDERS...")
|
|
1395 |
print ""
|
|
1396 |
print _("Options") + ":"
|
|
1397 |
print " -h, --help " + _("Show this help and exit")
|
|
1398 |
print " -v, --version " + _("Show version information and exit")
|
|
1399 |
print " -V, --verbose " + _("Show more detailed information")
|
|
1400 |
print " -R, --recursive " + _("Recursively include all images found in")
|
|
1401 |
print " " + _("subdirectories of FOLDERS")
|
|
1402 |
print " -s, --slideshow " + _("Start in slideshow mode")
|
|
1403 |
print " -f, --fullscreen " + _("Start in fullscreen mode")
|
|
1404 |
print " -o, --onload 'cmd' " + _("Execute 'cmd' when an image is loaded")
|
|
1405 |
print " " + _("uses same syntax as custom actions,\n")
|
|
1406 |
print " " + _("i.e. mirage -o 'echo file is %F'")
|
|
1407 |
|
|
1408 |
def delay_changed(self, action):
|
|
1409 |
self.curr_slideshow_delay = self.ss_delayspin.get_value()
|
|
1410 |
if self.slideshow_mode:
|
|
1411 |
gobject.source_remove(self.timer_delay)
|
|
1412 |
if self.curr_slideshow_random:
|
|
1413 |
self.timer_delay = gobject.timeout_add(int(self.curr_slideshow_delay*1000), self.goto_random_image, "ss")
|
|
1414 |
else:
|
|
1415 |
self.timer_delay = gobject.timeout_add((self.curr_slideshow_delay*1000), self.goto_next_image, "ss")
|
|
1416 |
self.window.set_focus(self.layout)
|
|
1417 |
|
|
1418 |
def random_changed(self, action):
|
|
1419 |
self.curr_slideshow_random = self.ss_randomize.get_active()
|
|
1420 |
|
|
1421 |
def motion_cb(self, widget, context, x, y, time):
|
|
1422 |
context.drag_status(gtk.gdk.ACTION_COPY, time)
|
|
1423 |
return True
|
|
1424 |
|
|
1425 |
def drop_cb(self, widget, context, x, y, selection, info, time):
|
|
1426 |
uri = selection.data.strip()
|
|
1427 |
path = urllib.url2pathname(uri)
|
|
1428 |
paths = path.rsplit('\n')
|
|
1429 |
for i, path in enumerate(paths):
|
|
1430 |
paths[i] = path.rstrip('\r')
|
|
1431 |
self.expand_filelist_and_load_image(paths)
|
|
1432 |
|
|
1433 |
def put_error_image_to_window(self):
|
|
1434 |
self.imageview.set_from_stock(gtk.STOCK_MISSING_IMAGE, gtk.ICON_SIZE_LARGE_TOOLBAR)
|
|
1435 |
self.currimg_width = self.imageview.size_request()[0]
|
|
1436 |
self.currimg_height = self.imageview.size_request()[1]
|
|
1437 |
self.center_image()
|
|
1438 |
self.set_go_sensitivities(False)
|
|
1439 |
self.set_image_sensitivities(False)
|
|
1440 |
self.update_statusbar()
|
|
1441 |
self.loaded_img_in_list = -1
|
|
1442 |
return
|
|
1443 |
|
|
1444 |
def expose_event(self, widget, event):
|
|
1445 |
if self.updating_adjustments:
|
|
1446 |
return
|
|
1447 |
self.updating_adjustments = True
|
|
1448 |
if self.hscroll.get_property('visible'):
|
|
1449 |
try:
|
|
1450 |
zoomratio = float(self.currimg_width)/self.previmg_width
|
|
1451 |
newvalue = abs(self.layout.get_hadjustment().get_value() * zoomratio + (self.available_image_width()) * (zoomratio - 1) / 2)
|
|
1452 |
if newvalue >= self.layout.get_hadjustment().lower and newvalue <= (self.layout.get_hadjustment().upper - self.layout.get_hadjustment().page_size):
|
|
1453 |
self.layout.get_hadjustment().set_value(newvalue)
|
|
1454 |
except:
|
|
1455 |
pass
|
|
1456 |
if self.vscroll.get_property('visible'):
|
|
1457 |
try:
|
|
1458 |
newvalue = abs(self.layout.get_vadjustment().get_value() * zoomratio + (self.available_image_height()) * (zoomratio - 1) / 2)
|
|
1459 |
if newvalue >= self.layout.get_vadjustment().lower and newvalue <= (self.layout.get_vadjustment().upper - self.layout.get_vadjustment().page_size):
|
|
1460 |
self.layout.get_vadjustment().set_value(newvalue)
|
|
1461 |
self.previmg_width = self.currimg_width
|
|
1462 |
except:
|
|
1463 |
pass
|
|
1464 |
self.updating_adjustments = False
|
|
1465 |
|
|
1466 |
def window_resized(self, widget, allocation, force_update=False):
|
|
1467 |
# Update the image size on window resize if the current image was last fit:
|
|
1468 |
if self.image_loaded:
|
|
1469 |
if force_update or allocation.width != self.prevwinwidth or allocation.height != self.prevwinheight:
|
|
1470 |
if self.last_image_action_was_fit:
|
|
1471 |
if self.last_image_action_was_smart_fit:
|
|
1472 |
self.zoom_to_fit_or_1_to_1(None, False, False)
|
|
1473 |
else:
|
|
1474 |
self.zoom_to_fit_window(None, False, False)
|
|
1475 |
else:
|
|
1476 |
self.center_image()
|
|
1477 |
self.load_new_image_stop_now()
|
|
1478 |
self.show_scrollbars_if_needed()
|
|
1479 |
# Also, regenerate preloaded image for new window size:
|
|
1480 |
self.preload_when_idle = gobject.idle_add(self.preload_next_image, True)
|
|
1481 |
self.preload_when_idle2 = gobject.idle_add(self.preload_prev_image, True)
|
|
1482 |
self.prevwinwidth = allocation.width
|
|
1483 |
self.prevwinheight = allocation.height
|
|
1484 |
return
|
|
1485 |
|
|
1486 |
def save_settings(self):
|
|
1487 |
conf = ConfigParser.ConfigParser()
|
|
1488 |
conf.add_section('window')
|
|
1489 |
conf.set('window', 'w', self.window.get_allocation().width)
|
|
1490 |
conf.set('window', 'h', self.window.get_allocation().height)
|
|
1491 |
conf.set('window', 'toolbar', self.toolbar_show)
|
|
1492 |
conf.set('window', 'statusbar', self.statusbar_show)
|
|
1493 |
conf.set('window', 'thumbpane', self.thumbpane_show)
|
|
1494 |
conf.add_section('prefs')
|
|
1495 |
conf.set('prefs', 'simple-bgcolor', self.simple_bgcolor)
|
|
1496 |
conf.set('prefs', 'bgcolor-red', self.bgcolor.red)
|
|
1497 |
conf.set('prefs', 'bgcolor-green', self.bgcolor.green)
|
|
1498 |
conf.set('prefs', 'bgcolor-blue', self.bgcolor.blue)
|
|
1499 |
conf.set('prefs', 'open_all', self.open_all_images)
|
|
1500 |
conf.set('prefs', 'hidden', self.open_hidden_files)
|
|
1501 |
conf.set('prefs', 'use_last_dir', self.use_last_dir)
|
|
1502 |
conf.set('prefs', 'last_dir', self.last_dir)
|
|
1503 |
conf.set('prefs', 'fixed_dir', self.fixed_dir)
|
|
1504 |
conf.set('prefs', 'open_mode', self.open_mode)
|
|
1505 |
conf.set('prefs', 'last_mode', self.last_mode)
|
|
1506 |
conf.set('prefs', 'listwrap_mode', self.listwrap_mode)
|
|
1507 |
conf.set('prefs', 'slideshow_delay', int(self.slideshow_delay))
|
|
1508 |
conf.set('prefs', 'slideshow_random', self.slideshow_random)
|
|
1509 |
conf.set('prefs', 'zoomquality', self.zoomvalue)
|
|
1510 |
conf.set('prefs', 'quality_save', int(self.quality_save))
|
|
1511 |
conf.set('prefs', 'disable_screensaver', self.disable_screensaver)
|
|
1512 |
conf.set('prefs', 'slideshow_in_fullscreen', self.slideshow_in_fullscreen)
|
|
1513 |
conf.set('prefs', 'confirm_delete', self.confirm_delete)
|
|
1514 |
conf.set('prefs', 'preloading_images', self.preloading_images)
|
|
1515 |
conf.set('prefs', 'savemode', self.savemode)
|
|
1516 |
conf.set('prefs', 'start_in_fullscreen', self.start_in_fullscreen)
|
|
1517 |
conf.set('prefs', 'thumbsize', self.thumbnail_size)
|
|
1518 |
conf.set('prefs', 'screenshot_delay', self.screenshot_delay)
|
|
1519 |
conf.add_section('actions')
|
|
1520 |
conf.set('actions', 'num_actions', len(self.action_names))
|
|
1521 |
for i in range(len(self.action_names)):
|
|
1522 |
conf.set('actions', 'names[' + str(i) + ']', self.action_names[i])
|
|
1523 |
conf.set('actions', 'commands[' + str(i) + ']', self.action_commands[i])
|
|
1524 |
conf.set('actions', 'shortcuts[' + str(i) + ']', self.action_shortcuts[i])
|
|
1525 |
conf.set('actions', 'batch[' + str(i) + ']', self.action_batch[i])
|
|
1526 |
conf.add_section('recent')
|
|
1527 |
conf.set('recent', 'num_recent', len(self.recentfiles))
|
|
1528 |
for i in range(len(self.recentfiles)):
|
|
1529 |
conf.set('recent', 'num[' + str(i) + ']', len(self.recentfiles[i]))
|
|
1530 |
conf.set('recent', 'urls[' + str(i) + ',0]', self.recentfiles[i])
|
|
1531 |
if not os.path.exists(self.config_dir):
|
|
1532 |
os.makedirs(self.config_dir)
|
|
1533 |
conf.write(file(self.config_dir + '/miragerc', 'w'))
|
|
1534 |
|
|
1535 |
# Also, save accel_map:
|
|
1536 |
gtk.accel_map_save(self.config_dir + '/accel_map')
|
|
1537 |
|
|
1538 |
return
|
|
1539 |
|
|
1540 |
def delete_event(self, widget, event, data=None):
|
|
1541 |
cancel = self.autosave_image()
|
|
1542 |
if cancel:
|
|
1543 |
return True
|
|
1544 |
self.stop_now = True
|
|
1545 |
self.closing_app = True
|
|
1546 |
self.save_settings()
|
|
1547 |
sys.exit(0)
|
|
1548 |
|
|
1549 |
def destroy(self, event, data=None):
|
|
1550 |
cancel = self.autosave_image()
|
|
1551 |
if cancel:
|
|
1552 |
return True
|
|
1553 |
self.stop_now = True
|
|
1554 |
self.closing_app = True
|
|
1555 |
self.save_settings()
|
|
1556 |
|
|
1557 |
def exit_app(self, action):
|
|
1558 |
cancel = self.autosave_image()
|
|
1559 |
if cancel:
|
|
1560 |
return True
|
|
1561 |
self.stop_now = True
|
|
1562 |
self.closing_app = True
|
|
1563 |
self.save_settings()
|
|
1564 |
sys.exit(0)
|
|
1565 |
|
|
1566 |
def put_zoom_image_to_window(self, currimg_preloaded):
|
|
1567 |
self.window.window.freeze_updates()
|
|
1568 |
if not currimg_preloaded:
|
|
1569 |
# Always start with the original image to preserve quality!
|
|
1570 |
# Calculate image size:
|
|
1571 |
finalimg_width = int(self.currimg_pixbuf_original.get_width() * self.currimg_zoomratio)
|
|
1572 |
finalimg_height = int(self.currimg_pixbuf_original.get_height() * self.currimg_zoomratio)
|
|
1573 |
if not self.currimg_is_animation:
|
|
1574 |
# Scale image:
|
|
1575 |
if not self.currimg_pixbuf_original.get_has_alpha():
|
|
1576 |
self.currimg_pixbuf = self.currimg_pixbuf_original.scale_simple(finalimg_width, finalimg_height, self.zoom_quality)
|
|
1577 |
else:
|
|
1578 |
colormap = self.imageview.get_colormap()
|
|
1579 |
light_grey = colormap.alloc_color('#666666', True, True)
|
|
1580 |
dark_grey = colormap.alloc_color('#999999', True, True)
|
|
1581 |
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)
|
|
1582 |
else:
|
|
1583 |
self.currimg_pixbuf = self.currimg_pixbuf_original
|
|
1584 |
self.currimg_width, self.currimg_height = finalimg_width, finalimg_height
|
|
1585 |
self.layout.set_size(self.currimg_width, self.currimg_height)
|
|
1586 |
self.center_image()
|
|
1587 |
self.show_scrollbars_if_needed()
|
|
1588 |
if not self.currimg_is_animation:
|
|
1589 |
self.imageview.set_from_pixbuf(self.currimg_pixbuf)
|
|
1590 |
self.previmage_is_animation = False
|
|
1591 |
else:
|
|
1592 |
self.imageview.set_from_animation(self.currimg_pixbuf)
|
|
1593 |
self.previmage_is_animation = True
|
|
1594 |
# Clean up (free memory) because I'm lazy
|
|
1595 |
gc.collect()
|
|
1596 |
self.window.window.thaw_updates()
|
|
1597 |
self.loaded_img_in_list = self.curr_img_in_list
|
|
1598 |
|
|
1599 |
def show_scrollbars_if_needed(self):
|
|
1600 |
if self.currimg_width > self.available_image_width():
|
|
1601 |
self.hscroll.show()
|
|
1602 |
else:
|
|
1603 |
self.hscroll.hide()
|
|
1604 |
if self.currimg_height > self.available_image_height():
|
|
1605 |
self.vscroll.show()
|
|
1606 |
else:
|
|
1607 |
self.vscroll.hide()
|
|
1608 |
|
|
1609 |
def center_image(self):
|
|
1610 |
x_shift = int((self.available_image_width() - self.currimg_width)/2)
|
|
1611 |
if x_shift < 0:
|
|
1612 |
x_shift = 0
|
|
1613 |
y_shift = int((self.available_image_height() - self.currimg_height)/2)
|
|
1614 |
if y_shift < 0:
|
|
1615 |
y_shift = 0
|
|
1616 |
self.layout.move(self.imageview, x_shift, y_shift)
|
|
1617 |
|
|
1618 |
def available_image_width(self):
|
|
1619 |
width = self.window.get_size()[0]
|
|
1620 |
if not self.fullscreen_mode:
|
|
1621 |
if self.thumbpane_show:
|
|
1622 |
width -= self.thumbscroll.size_request()[0]
|
|
1623 |
return width
|
|
1624 |
|
|
1625 |
def available_image_height(self):
|
|
1626 |
height = self.window.get_size()[1]
|
|
1627 |
if not self.fullscreen_mode:
|
|
1628 |
height -= self.menubar.size_request()[1]
|
|
1629 |
if self.toolbar_show:
|
|
1630 |
height -= self.toolbar.size_request()[1]
|
|
1631 |
if self.statusbar_show:
|
|
1632 |
height -= self.statusbar.size_request()[1]
|
|
1633 |
return height
|
|
1634 |
|
|
1635 |
def save_image(self, action):
|
|
1636 |
if self.UIManager.get_widget('/MainMenu/FileMenu/Save').get_property('sensitive'):
|
|
1637 |
self.save_image_now(self.currimg_name, gtk.gdk.pixbuf_get_file_info(self.currimg_name)[0]['name'])
|
|
1638 |
|
|
1639 |
def save_image_as(self, action):
|
|
1640 |
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))
|
|
1641 |
dialog.set_default_response(gtk.RESPONSE_OK)
|
|
1642 |
filename = os.path.basename(self.currimg_name)
|
|
1643 |
filetype = None
|
|
1644 |
dialog.set_current_folder(os.path.dirname(self.currimg_name))
|
|
1645 |
dialog.set_current_name(filename)
|
|
1646 |
dialog.set_do_overwrite_confirmation(True)
|
|
1647 |
response = dialog.run()
|
|
1648 |
if response == gtk.RESPONSE_OK:
|
|
1649 |
prev_name = self.currimg_name
|
|
1650 |
filename = dialog.get_filename()
|
|
1651 |
dialog.destroy()
|
|
1652 |
fileext = os.path.splitext(os.path.basename(filename))[1].lower()
|
|
1653 |
if len(fileext) > 0:
|
|
1654 |
fileext = fileext[1:]
|
|
1655 |
# Override filetype if user typed a filename with a different extension:
|
|
1656 |
for i in gtk.gdk.pixbuf_get_formats():
|
|
1657 |
if fileext in i['extensions']:
|
|
1658 |
filetype = i['name']
|
|
1659 |
self.save_image_now(filename, filetype)
|
|
1660 |
self.register_file_with_recent_docs(filename)
|
|
1661 |
else:
|
|
1662 |
dialog.destroy()
|
|
1663 |
|
|
1664 |
def save_image_now(self, dest_name, filetype):
|
|
1665 |
try:
|
|
1666 |
self.change_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
|
|
1667 |
while gtk.events_pending():
|
|
1668 |
gtk.main_iteration()
|
|
1669 |
if filetype == None:
|
|
1670 |
filetype = gtk.gdk.pixbuf_get_file_info(self.currimg_name)[0]['name']
|
|
1671 |
if self.filetype_is_writable(filetype):
|
|
1672 |
self.currimg_pixbuf_original.save(dest_name, filetype, {'quality': str(self.quality_save)})
|
|
1673 |
self.currimg_name = dest_name
|
|
1674 |
self.image_list[self.curr_img_in_list] = dest_name
|
|
1675 |
self.update_title()
|
|
1676 |
self.update_statusbar()
|
|
1677 |
# Update thumbnail:
|
|
1678 |
gobject.idle_add(self.thumbpane_set_image, dest_name, self.curr_img_in_list, True)
|
|
1679 |
self.image_modified = False
|
|
1680 |
else:
|
|
1681 |
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)
|
|
1682 |
error_dialog.set_title(_("Save"))
|
|
1683 |
response = error_dialog.run()
|
|
1684 |
if response == gtk.RESPONSE_YES:
|
|
1685 |
error_dialog.destroy()
|
|
1686 |
while gtk.events_pending():
|
|
1687 |
gtk.main_iteration()
|
|
1688 |
self.save_image_as(None)
|
|
1689 |
else:
|
|
1690 |
error_dialog.destroy()
|
|
1691 |
except:
|
|
1692 |
error_dialog = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_WARNING, gtk.BUTTONS_CLOSE, _('Unable to save %s') % dest_name)
|
|
1693 |
error_dialog.set_title(_("Save"))
|
|
1694 |
error_dialog.run()
|
|
1695 |
error_dialog.destroy()
|
|
1696 |
self.change_cursor(None)
|
|
1697 |
|
|
1698 |
def autosave_image(self):
|
|
1699 |
# Returns True if the user has canceled out of the dialog
|
|
1700 |
# Never call this function from an idle or timeout loop! That will cause
|
|
1701 |
# the app to freeze.
|
|
1702 |
if self.image_modified:
|
|
1703 |
if self.savemode == 1:
|
|
1704 |
temp = self.UIManager.get_widget('/MainMenu/FileMenu/Save').get_property('sensitive')
|
|
1705 |
self.UIManager.get_widget('/MainMenu/FileMenu/Save').set_property('sensitive', True)
|
|
1706 |
self.save_image(None)
|
|
1707 |
self.UIManager.get_widget('/MainMenu/FileMenu/Save').set_property('sensitive', temp)
|
|
1708 |
elif self.savemode == 2:
|
|
1709 |
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?"))
|
|
1710 |
dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
|
|
1711 |
dialog.add_button(gtk.STOCK_NO, gtk.RESPONSE_NO)
|
|
1712 |
dialog.add_button(gtk.STOCK_SAVE, gtk.RESPONSE_YES)
|
|
1713 |
dialog.set_title(_("Save?"))
|
|
1714 |
dialog.set_default_response(gtk.RESPONSE_YES)
|
|
1715 |
response = dialog.run()
|
|
1716 |
dialog.destroy()
|
|
1717 |
if response == gtk.RESPONSE_YES:
|
|
1718 |
temp = self.UIManager.get_widget('/MainMenu/FileMenu/Save').get_property('sensitive')
|
|
1719 |
self.UIManager.get_widget('/MainMenu/FileMenu/Save').set_property('sensitive', True)
|
|
1720 |
self.save_image(None)
|
|
1721 |
self.UIManager.get_widget('/MainMenu/FileMenu/Save').set_property('sensitive', temp)
|
|
1722 |
self.image_modified = False
|
|
1723 |
elif response == gtk.RESPONSE_NO:
|
|
1724 |
self.image_modified = False
|
|
1725 |
# Ensures that we don't use the current pixbuf for any preload pixbufs if we are in
|
|
1726 |
# the process of loading the previous or next image in the list:
|
|
1727 |
self.currimg_pixbuf = self.currimg_pixbuf_original
|
|
1728 |
self.preloadimg_next_in_list = -1
|
|
1729 |
self.preloadimg_prev_in_list = -1
|
|
1730 |
self.loaded_img_in_list = -1
|
|
1731 |
else:
|
|
1732 |
return True
|
|
1733 |
|
|
1734 |
def filetype_is_writable(self, filetype):
|
|
1735 |
# Determine if filetype is a writable format
|
|
1736 |
filetype_is_writable = True
|
|
1737 |
for i in gtk.gdk.pixbuf_get_formats():
|
|
1738 |
if filetype in i['extensions']:
|
|
1739 |
if i['is_writable']:
|
|
1740 |
return True
|
|
1741 |
return False
|
|
1742 |
|
|
1743 |
def open_file(self, action):
|
|
1744 |
self.stop_now = True
|
|
1745 |
while gtk.events_pending():
|
|
1746 |
gtk.main_iteration()
|
|
1747 |
self.open_file_or_folder(action, True)
|
|
1748 |
|
|
1749 |
def open_file_remote(self, action):
|
|
1750 |
|