summaryrefslogtreecommitdiff
path: root/rapid
diff options
context:
space:
mode:
Diffstat (limited to 'rapid')
-rw-r--r--rapid/ChangeLog31
-rw-r--r--rapid/INSTALL6
-rw-r--r--rapid/config.py7
-rwxr-xr-xrapid/dropshadow.py19
-rw-r--r--rapid/glade3/photo.pngbin6135 -> 17234 bytes
-rw-r--r--rapid/glade3/photo_shadow.pngbin6659 -> 0 bytes
-rw-r--r--rapid/glade3/rapid.glade51
-rw-r--r--rapid/glade3/video.pngbin6683 -> 13148 bytes
-rw-r--r--rapid/glade3/video_shadow.pngbin7317 -> 0 bytes
-rw-r--r--rapid/glade3/video_small_shadow.pngbin2647 -> 2556 bytes
-rw-r--r--rapid/glade3/zoom-in.pngbin0 -> 1458 bytes
-rw-r--r--rapid/glade3/zoom-out.pngbin0 -> 1245 bytes
-rwxr-xr-xrapid/media.py8
-rwxr-xr-xrapid/metadata.py3
-rwxr-xr-xrapid/rapid.py171
-rw-r--r--rapid/renamesubfolderprefs.py7
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
index 87cfc2a..b8bd550 100644
--- a/rapid/glade3/photo.png
+++ b/rapid/glade3/photo.png
Binary files differ
diff --git a/rapid/glade3/photo_shadow.png b/rapid/glade3/photo_shadow.png
deleted file mode 100644
index 0053ba0..0000000
--- a/rapid/glade3/photo_shadow.png
+++ /dev/null
Binary files differ
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 &lt;i&gt;kaa metadata&lt;/i&gt; package for python.</property>
+ <property name="label" translatable="yes">Sorry, video downloading functionality disabled. To download videos, please install the &lt;i&gt;hachoir metadata&lt;/i&gt; and &lt;i&gt;kaa metadata&lt;/i&gt; 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 &lt;i&gt;kaa metadata&lt;/i&gt; package for python.</property>
+ <property name="label" translatable="yes">Sorry, video downloading functionality disabled. To download videos, please install the &lt;i&gt;hachoir metadata&lt;/i&gt; and &lt;i&gt;kaa metadata&lt;/i&gt; 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">&#x25CF;</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 &lt;damonlynch@gmail.com&gt;</property>
<property name="translator_credits">Anton Alyab'ev &lt;subeditor@dolgopa.org&gt;
-L&#x151;rincz Andr&#xE1;s &lt;level.andrasnak@gmail.com&gt;
+Lőrincz András &lt;level.andrasnak@gmail.com&gt;
Michel Ange &lt;michelange@wanadoo.fr&gt;
Alain J. Baudrez &lt;a.baudrez@gmail.com&gt;
+Martin Dahl Moe
Martin Egger &lt;martin.egger@gmx.net&gt;
-Nicol&#xE1;s M. Zahlut &lt;nzahlut@live.com&gt;
+Miroslav Matejaš &lt;silverspace@ubuntu-hr.org&gt;
+Nicolás M. Zahlut &lt;nzahlut@live.com&gt;
+Erik M
Jose Luis Navarro &lt;jlnavarro111@gmail.com&gt;
Tomas Novak &lt;kuvaly@seznam.cz&gt;
Abel O'Rian &lt;abel.orian@gmail.com&gt;
Balazs Oveges &lt;ovegesb@freemail.hu&gt;
Daniel Paessler &lt;daniel@paessler.org&gt;
-Milo&#x161; Popovi&#x107; &lt;gpopac@gmail.com&gt;
+Miloš Popović &lt;gpopac@gmail.com&gt;
Michal Predotka &lt;mpredotka@googlemail.com&gt;
Ye Qing &lt;allen19920930@gmail.com&gt;
Luca Reverberi &lt;thereve@gmail.com&gt;
@@ -2856,11 +2858,11 @@ Mikko Ruohola &lt;polarfox@polarfox.net&gt;
Sergiy Gavrylov &lt;sergiovana@bigmir.net&gt;
Sergei Sedov &lt;sedov@webmail.perm.ru&gt;
Marco Solari &lt;marcosolari@gmail.com&gt;
-Toni L&#xE4;hdekorpi &lt;toni@lygon.net&gt;
-Ulf Urd&#xE9;n &lt;ulf.urden@purplescout.com&gt;
+Toni Lähdekorpi &lt;toni@lygon.net&gt;
+Ulf Urdén &lt;ulf.urden@purplescout.com&gt;
Julien Valroff &lt;julien@kirya.net&gt;
Aron Xu &lt;happyaron.xu@gmail.com&gt;
-&#x6881;&#x5176;&#x5B66; &lt;yalongbay@gmail.com&gt;</property>
+梁其学 &lt;yalongbay@gmail.com&gt;</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 &lt;happyaron.xu@gmail.com&gt;
</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 &lt;happyaron.xu@gmail.com&gt;
<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
index ac1086b..2dfc666 100644
--- a/rapid/glade3/video.png
+++ b/rapid/glade3/video.png
Binary files differ
diff --git a/rapid/glade3/video_shadow.png b/rapid/glade3/video_shadow.png
deleted file mode 100644
index 9b66480..0000000
--- a/rapid/glade3/video_shadow.png
+++ /dev/null
Binary files differ
diff --git a/rapid/glade3/video_small_shadow.png b/rapid/glade3/video_small_shadow.png
index eb42d92..bf39c21 100644
--- a/rapid/glade3/video_small_shadow.png
+++ b/rapid/glade3/video_small_shadow.png
Binary files differ
diff --git a/rapid/glade3/zoom-in.png b/rapid/glade3/zoom-in.png
new file mode 100644
index 0000000..af84614
--- /dev/null
+++ b/rapid/glade3/zoom-in.png
Binary files differ
diff --git a/rapid/glade3/zoom-out.png b/rapid/glade3/zoom-out.png
new file mode 100644
index 0000000..6c674df
--- /dev/null
+++ b/rapid/glade3/zoom-out.png
Binary files differ
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