diff options
Diffstat (limited to 'rapid')
-rw-r--r-- | rapid/ChangeLog | 31 | ||||
-rw-r--r-- | rapid/INSTALL | 6 | ||||
-rw-r--r-- | rapid/config.py | 7 | ||||
-rwxr-xr-x | rapid/dropshadow.py | 19 | ||||
-rw-r--r-- | rapid/glade3/photo.png | bin | 6135 -> 17234 bytes | |||
-rw-r--r-- | rapid/glade3/photo_shadow.png | bin | 6659 -> 0 bytes | |||
-rw-r--r-- | rapid/glade3/rapid.glade | 51 | ||||
-rw-r--r-- | rapid/glade3/video.png | bin | 6683 -> 13148 bytes | |||
-rw-r--r-- | rapid/glade3/video_shadow.png | bin | 7317 -> 0 bytes | |||
-rw-r--r-- | rapid/glade3/video_small_shadow.png | bin | 2647 -> 2556 bytes | |||
-rw-r--r-- | rapid/glade3/zoom-in.png | bin | 0 -> 1458 bytes | |||
-rw-r--r-- | rapid/glade3/zoom-out.png | bin | 0 -> 1245 bytes | |||
-rwxr-xr-x | rapid/media.py | 8 | ||||
-rwxr-xr-x | rapid/metadata.py | 3 | ||||
-rwxr-xr-x | rapid/rapid.py | 171 | ||||
-rw-r--r-- | rapid/renamesubfolderprefs.py | 7 |
16 files changed, 239 insertions, 64 deletions
diff --git a/rapid/ChangeLog b/rapid/ChangeLog index d749497..46a1f3b 100644 --- a/rapid/ChangeLog +++ b/rapid/ChangeLog @@ -1,3 +1,32 @@ +Version 0.3.4 +------------- + +2010-12-31 + +You can now change the size of the preview image by zooming in and out using a +slider. The maximum size is double that of the previous fixed size, which was +160px. On computers with small screens such as netbooks, the maximum preview +image size is the same as the previous fixed size. Please note that Rapid Photo +Downloader only extracts thumbnails of photos; for performance reasons, it does +not create them. This means for some file formats, the thumbnails will contain +jpeg artifacts when scaled up (this is particularly true when using a version of +pyexiv2 < 0.2.0). For users who require larger preview images, this will be of +little consequence. + +When the "Strip compatible characters" feature is enabled in the Preferences +(which is the default), any white space (e.g. spaces) beginning or ending a +folder name will now be removed. + +Bug fix: camera serial numbers are now stripped of any spaces preceding or +following the actual value. + +Fixed bug #685335: inaccurate description of python packages required for +downloading videos. + +Added Croatian translation. Updated French, Norwegian Bokmal, Polish and Russian +translations. + + Version 0.3.3 ------------- @@ -22,7 +51,7 @@ download was started. Fixed bugs related to missing video download directory at program startup. -Added command line option to output to the terminal information usesful for +Added command line option to output to the terminal information useful for debugging. Added Norwegian Bokmal and Portuguese translations. Updated Brazilian diff --git a/rapid/INSTALL b/rapid/INSTALL index de0e15e..8270a20 100644 --- a/rapid/INSTALL +++ b/rapid/INSTALL @@ -20,7 +20,7 @@ Rapid Photo Downloader can determine if it can download additional types of RAW files (some early versions of exiv2 and pyexiv2 segfault on certain RAW file types). -If you want to download videos, you can install: +If you want to download videos, you should install: - python-hachoir-metadata - python-kaa-metadata @@ -28,8 +28,8 @@ If you want to download videos, you can install: hachoir metadata is required to download videos. kaa metadata is used to extract additional metadata from videos. ffmpegthumbnailer is used only to -display thumbnail images as the download occurs. This is a useful feature, and -if you can install it, it is recommended. +display thumbnail images before the download occurs. This is a useful feature, +and if you can install it, it is recommended. hachoir metadata, kaa metadata and ffmpegthumbnailer are optional. The program will run without them. diff --git a/rapid/config.py b/rapid/config.py index c621848..73deb87 100644 --- a/rapid/config.py +++ b/rapid/config.py @@ -15,7 +15,7 @@ ### along with this program; if not, write to the Free Software ### Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -version = '0.3.3' +version = '0.3.4' GCONF_KEY="/apps/rapid-photo-downloader" GLADE_FILE = "glade3/rapid.glade" @@ -45,7 +45,10 @@ SERIOUS_ERROR = 2 WARNING = 3 MAX_LENGTH_DEVICE_NAME = 15 -MAX_THUMBNAIL_SIZE = 160 + +MIN_THUMBNAIL_SIZE = 80 +max_thumbnail_size = 320 # will be overridden when the screen is tiny +THUMBNAIL_INCREMENT = 50 STATUS_DOWNLOAD_PENDING = 0 # going to try to download it STATUS_DOWNLOADED = 1 # downloaded successfully diff --git a/rapid/dropshadow.py b/rapid/dropshadow.py index fd7cf99..68b5398 100755 --- a/rapid/dropshadow.py +++ b/rapid/dropshadow.py @@ -36,9 +36,17 @@ def pixbuf_to_image(pb): dimensions = pb.get_width(), pb.get_height() stride = pb.get_rowstride() pixels = pb.get_pixels() + mode = pb.get_has_alpha() and "RGBA" or "RGB" - return Image.frombuffer(mode, dimensions, pixels, + image = Image.frombuffer(mode, dimensions, pixels, "raw", mode, stride, 1) + + if mode == "RGB": + # convert to having an alpha value, so that the image can + # act as a mask in the drop shadow paste + image = image.convert("RGBA") + + return image class DropShadow(): @@ -73,6 +81,8 @@ class DropShadow(): To make backgrounds transparent, ensure the alpha value of the shadow color is the same as the background color, e.g. if background_color is 0xffffff, shadow's alpha should be 0xff + + The image must be in RGBA format. """ self.backgrounds = {} self.offset = offset @@ -92,10 +102,9 @@ class DropShadow(): else: self.top_spacing = 0 - def dropShadow(self, image): """ - image - The image to overlay on top of the shadow. + image - The image to overlay on top of the shadow. """ dimensions = (image.size[0], image.size[1]) if not dimensions in self.backgrounds: @@ -125,7 +134,7 @@ class DropShadow(): back.paste(self.shadow, [shadowLeft, shadowTop, shadowLeft + image.size[0], shadowTop + image.size[1]] ) - # Apply the filter to blur the edges of the shadow. Since a small kernel + # Apply the filter to blur the edges of the shadow. Since a small kernel # is used, the filter must be applied repeatedly to get a decent blur. n = 0 while n < self.iterations: @@ -139,7 +148,7 @@ class DropShadow(): imageTop = self.top_spacing - min(self.offset[1], 0) back = self.backgrounds[dimensions].copy() - back.paste(image, (imageLeft, imageTop)) + back.paste(image, (imageLeft, imageTop), image) return back diff --git a/rapid/glade3/photo.png b/rapid/glade3/photo.png Binary files differindex 87cfc2a..b8bd550 100644 --- a/rapid/glade3/photo.png +++ b/rapid/glade3/photo.png diff --git a/rapid/glade3/photo_shadow.png b/rapid/glade3/photo_shadow.png Binary files differdeleted file mode 100644 index 0053ba0..0000000 --- a/rapid/glade3/photo_shadow.png +++ /dev/null diff --git a/rapid/glade3/rapid.glade b/rapid/glade3/rapid.glade index 2b65c8b..7975593 100644 --- a/rapid/glade3/rapid.glade +++ b/rapid/glade3/rapid.glade @@ -1,4 +1,4 @@ -<?xml version="1.0"?> +<?xml version="1.0" encoding="UTF-8"?> <glade-interface> <!-- interface-requires gtk+ 2.16 --> <!-- interface-naming-policy toplevel-contextual --> @@ -11,7 +11,6 @@ <property name="default_height">500</property> <property name="icon">rapid-photo-downloader.svg</property> <property name="type_hint">dialog</property> - <property name="has_separator">False</property> <signal name="destroy" handler="on_preferencesdialog_destroy"/> <signal name="response" handler="on_response"/> <child internal-child="vbox"> @@ -596,7 +595,7 @@ <property name="xalign">0</property> <property name="xpad">12</property> <property name="ypad">10</property> - <property name="label" translatable="yes">Sorry, video downloading functionality disabled. To download videos, please install the <i>kaa metadata</i> package for python.</property> + <property name="label" translatable="yes">Sorry, video downloading functionality disabled. To download videos, please install the <i>hachoir metadata</i> and <i>kaa metadata</i> packages for python.</property> <property name="use_markup">True</property> <property name="wrap">True</property> </widget> @@ -860,7 +859,7 @@ <child> <widget class="GtkLabel" id="videos_cannot_be_downloaded_label"> <property name="xalign">0</property> - <property name="label" translatable="yes">Sorry, video downloading functionality disabled. To download videos, please install the <i>kaa metadata</i> package for python.</property> + <property name="label" translatable="yes">Sorry, video downloading functionality disabled. To download videos, please install the <i>hachoir metadata</i> and <i>kaa metadata</i> packages for python.</property> <property name="use_markup">True</property> <property name="wrap">True</property> </widget> @@ -2152,7 +2151,7 @@ You can download photos from multiple devices simultaneously, or you can specify <property name="visible">True</property> <property name="can_focus">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="invisible_char">●</property> + <property name="invisible_char">●</property> <signal name="changed" handler="on_video_backup_identifier_entry_changed"/> </widget> <packing> @@ -2838,17 +2837,20 @@ Rapid Photo Downloader is distributed in the hope that it will be useful, but WI You should have received a copy of the GNU General Public License along with Rapid Photo Downloader; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.</property> <property name="authors">Damon Lynch <damonlynch@gmail.com></property> <property name="translator_credits">Anton Alyab'ev <subeditor@dolgopa.org> -Lőrincz András <level.andrasnak@gmail.com> +Lőrincz András <level.andrasnak@gmail.com> Michel Ange <michelange@wanadoo.fr> Alain J. Baudrez <a.baudrez@gmail.com> +Martin Dahl Moe Martin Egger <martin.egger@gmx.net> -Nicolás M. Zahlut <nzahlut@live.com> +Miroslav Matejaš <silverspace@ubuntu-hr.org> +Nicolás M. Zahlut <nzahlut@live.com> +Erik M Jose Luis Navarro <jlnavarro111@gmail.com> Tomas Novak <kuvaly@seznam.cz> Abel O'Rian <abel.orian@gmail.com> Balazs Oveges <ovegesb@freemail.hu> Daniel Paessler <daniel@paessler.org> -Miloš Popović <gpopac@gmail.com> +Miloš Popović <gpopac@gmail.com> Michal Predotka <mpredotka@googlemail.com> Ye Qing <allen19920930@gmail.com> Luca Reverberi <thereve@gmail.com> @@ -2856,11 +2858,11 @@ Mikko Ruohola <polarfox@polarfox.net> Sergiy Gavrylov <sergiovana@bigmir.net> Sergei Sedov <sedov@webmail.perm.ru> Marco Solari <marcosolari@gmail.com> -Toni Lähdekorpi <toni@lygon.net> -Ulf Urdén <ulf.urden@purplescout.com> +Toni Lähdekorpi <toni@lygon.net> +Ulf Urdén <ulf.urden@purplescout.com> Julien Valroff <julien@kirya.net> Aron Xu <happyaron.xu@gmail.com> -梁其学 <yalongbay@gmail.com></property> +梁其学 <yalongbay@gmail.com></property> <property name="logo">rapid-photo-downloader.svg</property> <property name="wrap_license">True</property> <child internal-child="vbox"> @@ -3099,6 +3101,32 @@ Aron Xu <happyaron.xu@gmail.com> </widget> </child> <child> + <widget class="GtkSeparatorMenuItem" id="seperator40"> + <property name="visible">True</property> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="menu_zoom_in"> + <property name="label">gtk-zoom-in</property> + <property name="visible">True</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + <signal name="activate" handler="on_menu_zoom_in_activate"/> + <accelerator key="equal" signal="activate" modifiers="GDK_CONTROL_MASK"/> + <accelerator key="plus" signal="activate" modifiers="GDK_CONTROL_MASK"/> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="menu_zoom_out"> + <property name="label">gtk-zoom-out</property> + <property name="visible">True</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + <signal name="activate" handler="on_menu_zoom_out_activate"/> + <accelerator key="minus" signal="activate" modifiers="GDK_CONTROL_MASK"/> + </widget> + </child> + <child> <widget class="GtkSeparatorMenuItem" id="seperator20"> <property name="visible">True</property> </widget> @@ -3464,7 +3492,6 @@ Aron Xu <happyaron.xu@gmail.com> <property name="destroy_with_parent">True</property> <property name="icon">rapid-photo-downloader.svg</property> <property name="type_hint">dialog</property> - <property name="has_separator">False</property> <signal name="close" handler="on_logdialog_close"/> <signal name="response" handler="on_logdialog_response"/> <child internal-child="vbox"> diff --git a/rapid/glade3/video.png b/rapid/glade3/video.png Binary files differindex ac1086b..2dfc666 100644 --- a/rapid/glade3/video.png +++ b/rapid/glade3/video.png diff --git a/rapid/glade3/video_shadow.png b/rapid/glade3/video_shadow.png Binary files differdeleted file mode 100644 index 9b66480..0000000 --- a/rapid/glade3/video_shadow.png +++ /dev/null diff --git a/rapid/glade3/video_small_shadow.png b/rapid/glade3/video_small_shadow.png Binary files differindex eb42d92..bf39c21 100644 --- a/rapid/glade3/video_small_shadow.png +++ b/rapid/glade3/video_small_shadow.png diff --git a/rapid/glade3/zoom-in.png b/rapid/glade3/zoom-in.png Binary files differnew file mode 100644 index 0000000..af84614 --- /dev/null +++ b/rapid/glade3/zoom-in.png diff --git a/rapid/glade3/zoom-out.png b/rapid/glade3/zoom-out.png Binary files differnew file mode 100644 index 0000000..6c674df --- /dev/null +++ b/rapid/glade3/zoom-out.png diff --git a/rapid/media.py b/rapid/media.py index 3ad09d2..9819ab7 100755 --- a/rapid/media.py +++ b/rapid/media.py @@ -24,7 +24,7 @@ import datetime import subprocess import config -from config import MAX_THUMBNAIL_SIZE +from config import max_thumbnail_size from config import STATUS_NOT_DOWNLOADED, \ STATUS_DOWNLOAD_PENDING, \ STATUS_CANNOT_DOWNLOAD @@ -197,7 +197,7 @@ class MediaFile: else: if self.isImage: try: - thumbnail = self.metadata.getThumbnailData(MAX_THUMBNAIL_SIZE) + thumbnail = self.metadata.getThumbnailData(max_thumbnail_size) if not isinstance(thumbnail, types.StringType): self.thumbnail = None else: @@ -220,10 +220,10 @@ class MediaFile: else: # get thumbnail of video # it may need to be generated - self.thumbnail = self.metadata.getThumbnailData(MAX_THUMBNAIL_SIZE, tempWorkingDir) + self.thumbnail = self.metadata.getThumbnailData(max_thumbnail_size, tempWorkingDir) if self.thumbnail: # scale to size - self.thumbnail = common.scale2pixbuf(MAX_THUMBNAIL_SIZE, MAX_THUMBNAIL_SIZE, self.thumbnail) + self.thumbnail = common.scale2pixbuf(max_thumbnail_size, max_thumbnail_size, self.thumbnail) diff --git a/rapid/metadata.py b/rapid/metadata.py index 0f3c48d..1a040f3 100755 --- a/rapid/metadata.py +++ b/rapid/metadata.py @@ -280,7 +280,8 @@ class MetaData(baseclass): v = self['Exif.Image.CameraSerialNumber'] else: return missing - return str(v) + v = str(v) + return v.strip() except: return missing diff --git a/rapid/rapid.py b/rapid/rapid.py index ded7324..a69b80a 100755 --- a/rapid/rapid.py +++ b/rapid/rapid.py @@ -61,12 +61,10 @@ from optparse import OptionParser import pynotify -import ValidatedEntry - import idletube as tube import config -from config import MAX_THUMBNAIL_SIZE + from config import STATUS_CANNOT_DOWNLOAD, STATUS_DOWNLOADED, \ STATUS_DOWNLOADED_WITH_WARNING, \ STATUS_DOWNLOAD_FAILED, \ @@ -75,7 +73,7 @@ from config import STATUS_CANNOT_DOWNLOAD, STATUS_DOWNLOADED, \ STATUS_NOT_DOWNLOADED, \ STATUS_DOWNLOAD_AND_BACKUP_FAILED, \ STATUS_WARNING - + import common import misc import higdefaults as hd @@ -84,6 +82,8 @@ from media import getDefaultPhotoLocation, getDefaultVideoLocation, \ getDefaultBackupPhotoIdentifier, \ getDefaultBackupVideoIdentifier +import ValidatedEntry + from media import CardMedia import media @@ -123,7 +123,8 @@ _ = Configi18n._ #Translators: if neccessary, for guidance in how to translate this program, you may see http://damonlynch.net/translate.html PROGRAM_NAME = _('Rapid Photo Downloader') - +TINY_SCREEN = gtk.gdk.screen_height() <= config.TINY_SCREEN_HEIGHT +#~ TINY_SCREEN = True def today(): return datetime.date.today().strftime('%Y-%m-%d') @@ -407,6 +408,11 @@ class ThreadManager: workers = ThreadManager() class RapidPreferences(prefs.Preferences): + if TINY_SCREEN: + zoom = 120 + else: + zoom = config.MIN_THUMBNAIL_SIZE * 2 + defaults = { "program_version": prefs.Value(prefs.STRING, ""), "download_folder": prefs.Value(prefs.STRING, @@ -465,6 +471,7 @@ class RapidPreferences(prefs.Preferences): "main_window_size_y": prefs.Value(prefs.INT, 0), "main_window_maximized": prefs.Value(prefs.INT, 0), "show_warning_downloading_from_camera": prefs.Value(prefs.BOOL, True), + "preview_zoom": prefs.Value(prefs.INT, zoom), } def __init__(self): @@ -1843,12 +1850,8 @@ class CopyPhotos(Thread): # load images to display for when a thumbnail cannot be extracted or created - if DROP_SHADOW: - self.photoThumbnail = gtk.gdk.pixbuf_new_from_file(paths.share_dir('glade3/photo_shadow.png')) - self.videoThumbnail = gtk.gdk.pixbuf_new_from_file(paths.share_dir('glade3/video_shadow.png')) - else: - self.photoThumbnail = gtk.gdk.pixbuf_new_from_file(paths.share_dir('glade3/photo.png')) - self.videoThumbnail = gtk.gdk.pixbuf_new_from_file(paths.share_dir('glade3/video.png')) + self.photoThumbnail = gtk.gdk.pixbuf_new_from_file(paths.share_dir('glade3/photo.png')) + self.videoThumbnail = gtk.gdk.pixbuf_new_from_file(paths.share_dir('glade3/video.png')) imageRenameUsesJobCode = rn.usesJobCode(self.prefs.image_rename) imageSubfolderUsesJobCode = rn.usesJobCode(self.prefs.subfolder) @@ -3603,7 +3606,6 @@ class SelectionTreeView(gtk.TreeView): if DROP_SHADOW: self.iconDropShadow = DropShadow(offset=(3,3), shadow = (0x34, 0x34, 0x34, 0xff), border=6) - self.previewDropShadow = DropShadow(shadow = (0x44, 0x44, 0x44, 0xff), trim_border = True) self.previewed_file_treerowref = None self.icontheme = gtk.icon_theme_get_default() @@ -3732,7 +3734,8 @@ class SelectionTreeView(gtk.TreeView): name = mediaFile.name size = mediaFile.size thumbnail = mediaFile.thumbnail - thumbnail_icon = common.scale2pixbuf(60, 36, mediaFile.thumbnail) + thumbnail_icon = common.scale2pixbuf(60, 36, thumbnail) + #thumbnail_icon = common.scale2pixbuf(80, 48, mediaFile.thumbnail) if DROP_SHADOW: if not mediaFile.genericThumbnail: pil_image = pixbuf_to_image(thumbnail_icon) @@ -4020,12 +4023,8 @@ class SelectionTreeView(gtk.TreeView): self.previewed_file_treerowref = mediaFile.treerowref - thumbnail = mediaFile.thumbnail - - if DROP_SHADOW and not mediaFile.genericThumbnail: - pil_image = pixbuf_to_image(thumbnail) - pil_image = self.previewDropShadow.dropShadow(pil_image) - thumbnail = image_to_pixbuf(pil_image) + self.parentApp.set_base_preview_image(mediaFile.thumbnail) + thumbnail = self.parentApp.scaledPreviewImage() self.parentApp.preview_image.set_from_pixbuf(thumbnail) @@ -4329,7 +4328,9 @@ class SelectionVBox(gtk.VBox): gtk.VBox.__init__(self) self.parentApp = parentApp - tiny_screen = gtk.gdk.screen_height() <= config.TINY_SCREEN_HEIGHT + tiny_screen = TINY_SCREEN + if tiny_screen: + config.max_thumbnail_size = 160 selection_scrolledwindow = gtk.ScrolledWindow() selection_scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) @@ -4351,23 +4352,49 @@ class SelectionVBox(gtk.VBox): #selection_scrolledwindow.set_size_request(350, -1) - #Preview pane + # Preview pane + + # Zoom in and out slider (make the image bigger / smaller) + + # Zoom out (on the left of the slider) + self.zoom_out_eventbox = gtk.EventBox() + self.zoom_out_eventbox.set_events(gtk.gdk.BUTTON_PRESS_MASK) + self.zoom_out_image = gtk.Image() + self.zoom_out_image.set_from_file(paths.share_dir('glade3/zoom-out.png')) + self.zoom_out_eventbox.add(self.zoom_out_image) + self.zoom_out_eventbox.connect("button_press_event", self.zoom_out_0_callback) + + # Zoom in (on the right of the slider) + self.zoom_in_eventbox = gtk.EventBox() + self.zoom_in_eventbox.set_events(gtk.gdk.BUTTON_PRESS_MASK) + self.zoom_in_image = gtk.Image() + self.zoom_in_image.set_from_file(paths.share_dir('glade3/zoom-in.png')) + self.zoom_in_eventbox.add(self.zoom_in_image) + self.zoom_in_eventbox.connect("button_press_event", self.zoom_in_100_callback) + + self.slider_adjustment = gtk.Adjustment(value=self.parentApp.prefs.preview_zoom, + lower=config.MIN_THUMBNAIL_SIZE, upper=config.max_thumbnail_size, + step_incr=1.0, page_incr=config.THUMBNAIL_INCREMENT, page_size=0) + self.slider_adjustment.connect("value_changed", self.resize_image_callback) + self.slider_hscale = gtk.HScale(self.slider_adjustment) + self.slider_hscale.set_draw_value(False) # don't display numeric value + self.slider_hscale.set_size_request(config.MIN_THUMBNAIL_SIZE * 2, -1) - #Preview title - self.preview_title_label = gtk.Label() - self.preview_title_label.set_markup("<b>%s</b>" % _("Preview")) - self.preview_title_label.set_alignment(0, 0.5) - self.preview_title_label.set_padding(12, 0) #Preview image + self.base_preview_image = None # large size image used to scale down from self.preview_image = gtk.Image() + self.preview_image.set_alignment(0, 0.5) #leave room for thumbnail shadow if DROP_SHADOW: - shadow_size = 21 + self.cacheDropShadow() else: - shadow_size = 0 - self.preview_image.set_size_request(MAX_THUMBNAIL_SIZE + shadow_size, MAX_THUMBNAIL_SIZE + shadow_size) + self.shadow_size = 0 + + image_size, shadow_size, offset = self._imageAndShadowSize() + + self.preview_image.set_size_request(image_size, image_size) #labels to display file information @@ -4460,9 +4487,14 @@ class SelectionVBox(gtk.VBox): self.preview_table.attach(right_spacer, 3, 4, 1, 2, xoptions=gtk.SHRINK, yoptions=gtk.SHRINK) row = 0 - if not tiny_screen: - self.preview_table.attach(self.preview_title_label, 0, 3, row, row+1, yoptions=gtk.SHRINK) - row += 1 + zoom_hbox = gtk.HBox() + zoom_hbox.pack_start(self.zoom_out_eventbox, False, False) + zoom_hbox.pack_start(self.slider_hscale, False, False) + zoom_hbox.pack_start(self.zoom_in_eventbox, False, False) + + self.preview_table.attach(zoom_hbox, 1, 3, row, row+1, yoptions=gtk.SHRINK) + + row += 1 self.preview_table.attach(self.preview_image, 1, 3, row, row+1, yoptions=gtk.SHRINK) row += 1 @@ -4504,6 +4536,26 @@ class SelectionVBox(gtk.VBox): self.show_all() + def set_base_preview_image(self, pixbuf): + """ + sets the unscaled pixbuf image to be displayed to the user + the actual image the user will see will depend on the scale + they've set to view it at + """ + self.base_preview_image = pixbuf + + def zoom_in(self): + self.slider_adjustment.set_value(min([config.max_thumbnail_size, int(self.slider_adjustment.get_value()) + config.THUMBNAIL_INCREMENT])) + + def zoom_out(self): + self.slider_adjustment.set_value(max([config.MIN_THUMBNAIL_SIZE, int(self.slider_adjustment.get_value()) - config.THUMBNAIL_INCREMENT])) + + def zoom_in_100_callback(self, widget, value): + self.slider_adjustment.set_value(config.max_thumbnail_size) + + def zoom_out_0_callback(self, widget, value): + self.slider_adjustment.set_value(config.MIN_THUMBNAIL_SIZE) + def set_display_preview_folders(self, value): if value and self.selection_treeview.previewed_file_treerowref: self.preview_destination_expander.show() @@ -4512,6 +4564,51 @@ class SelectionVBox(gtk.VBox): else: self.preview_destination_expander.hide() self.preview_device_expander.hide() + + def cacheDropShadow(self): + i, self.shadow_size, offset_v = self._imageAndShadowSize() + self.drop_shadow = DropShadow(offset=(offset_v,offset_v), shadow = (0x44, 0x44, 0x44, 0xff), border=self.shadow_size, trim_border = True) + + def _imageAndShadowSize(self): + image_size = int(self.slider_adjustment.get_value()) + offset_v = max([image_size / 25, 5]) # realistically size the shadow based on the size of the image + shadow_size = offset_v + 3 + image_size = image_size + offset_v * 2 + 3 + return (image_size, shadow_size, offset_v) + + def resize_image_callback(self, adjustment): + """ + Resize the preview image after the adjustment value has been + changed + """ + size = int(adjustment.value) + self.parentApp.prefs.preview_zoom = size + self.cacheDropShadow() + + pixbuf = self.scaledPreviewImage() + if pixbuf: + self.preview_image.set_from_pixbuf(pixbuf) + size = max([pixbuf.get_width(), pixbuf.get_height()]) + self.preview_image.set_size_request(size, size) + else: + self.preview_image.set_size_request(size + self.shadow_size, size + self.shadow_size) + + def scaledPreviewImage(self): + """ + Generate a scaled version of the preview image + """ + size = int(self.slider_adjustment.get_value()) + if not self.base_preview_image: + return None + else: + pixbuf = common.scale2pixbuf(size, size, self.base_preview_image) + + if DROP_SHADOW: + pil_image = pixbuf_to_image(pixbuf) + pil_image = self.drop_shadow.dropShadow(pil_image) + pixbuf = image_to_pixbuf(pil_image) + + return pixbuf def set_job_code_display(self): """ @@ -4542,8 +4639,6 @@ class SelectionVBox(gtk.VBox): # clear existing entry displayed in entry box self.job_code_entry.set_text('') - - def add_job_code_combo(self): self.job_code_hbox = gtk.HBox(spacing = 12) @@ -5926,6 +6021,12 @@ class RapidApp(gnomeglade.GnomeApp, dbus.service.Object): def on_menu_preview_folders_toggled(self, check_button): self.prefs.display_preview_folders = check_button.get_active() + def on_menu_zoom_out_activate(self, widget): + self.selection_vbox.zoom_out() + + def on_menu_zoom_in_activate(self, widget): + self.selection_vbox.zoom_in() + def on_menu_select_all_activate(self, widget): self.selection_vbox.selection_treeview.select_rows('all') @@ -6376,7 +6477,7 @@ def start (): if DOWNLOAD_VIDEO: cmd_line(_("Using") + " hachoir " + videometadata.version_info()) else: - cmd_line(_("\n" + "Video downloading functionality disabled.\nTo download videos, please install the kaa metadata package for python.") + "\n") + cmd_line(_("\n" + "Video downloading functionality disabled.\nTo download videos, please install the hachoir metadata and kaa metadata packages for python.") + "\n") if using_gio: cmd_line(_("Using") + " GIO") diff --git a/rapid/renamesubfolderprefs.py b/rapid/renamesubfolderprefs.py index 845f809..d46a8c1 100644 --- a/rapid/renamesubfolderprefs.py +++ b/rapid/renamesubfolderprefs.py @@ -64,7 +64,6 @@ _ = Configi18n._ import datetime -import ValidatedEntry import config from common import pythonifyVersion @@ -1456,6 +1455,8 @@ class SubfolderPreferences(ImageRenamePreferences): self.component = pn.SUBFOLDER_COMPONENT ImageRenamePreferences.__init__(self, prefList, parent) + self.stripExtraneousWhiteSpace = re.compile(r'\s*%s\s*' % os.sep) + def generateNameUsingPreferences(self, photo, existingFilename=None, stripCharacters = False, fallback_date = None): """ @@ -1477,6 +1478,10 @@ class SubfolderPreferences(ImageRenamePreferences): if subfolders: if subfolders[0] == os.sep: subfolders = subfolders[1:] + + # remove any spaces before and after a directory name + if subfolders and stripCharacters: + subfolders = self.stripExtraneousWhiteSpace.sub(os.sep, subfolders) return subfolders |