summaryrefslogtreecommitdiff
path: root/rapid
diff options
context:
space:
mode:
Diffstat (limited to 'rapid')
-rw-r--r--rapid/ChangeLog71
-rw-r--r--rapid/INSTALL2
-rw-r--r--rapid/backupfile.py24
-rw-r--r--rapid/config.py2
-rw-r--r--rapid/copyfiles.py59
-rw-r--r--rapid/filemodify.py138
-rw-r--r--rapid/generatename.py42
-rw-r--r--rapid/generatenameconfig.py31
-rw-r--r--rapid/glade3/about.ui1
-rw-r--r--rapid/glade3/prefs.ui689
-rw-r--r--rapid/glade3/rapid.ui153
-rwxr-xr-xrapid/metadataexiftool.py225
-rw-r--r--[-rwxr-xr-x]rapid/metadataphoto.py27
-rw-r--r--rapid/metadatavideo.py21
-rwxr-xr-xrapid/metadataxmp.py198
-rw-r--r--rapid/misc.py2
-rw-r--r--rapid/preferencesdialog.py42
-rw-r--r--rapid/prefsrapid.py1
-rwxr-xr-xrapid/rapid.py484
-rw-r--r--rapid/rpdfile.py92
-rwxr-xr-xrapid/scan.py38
-rw-r--r--rapid/subfolderfile.py80
-rw-r--r--rapid/thumbnail.py70
23 files changed, 1640 insertions, 852 deletions
diff --git a/rapid/ChangeLog b/rapid/ChangeLog
index 35cd727..775d535 100644
--- a/rapid/ChangeLog
+++ b/rapid/ChangeLog
@@ -1,3 +1,74 @@
+Version 0.4.3
+-------------
+
+2012-01-07
+
+ExifTool is now a required dependency for Rapid Photo Downloader. ExifTool
+can be used to help download videos on Linux distributions that have not
+packaged hachoir-metadata, such as Fedora.
+
+Exiftran is another new dependency. It is used to automatically rotate
+JPEG images.
+
+Fixed bug #704482: Delete photos option should be easily accessible -
+
+Added a toolbar at the top of the main program window, which gives immediate
+access to the most commonly changed configuration options: where files will
+be transferred from, whether they will be copied or moved, and where they will
+be transferred to.
+
+Please when the move option is chosen, all files in the download from a device
+are first copied before any are deleted. In other words, only once all
+source files have been successfully copied from a device to their destination
+are the source files deleted from that device.
+
+Fixed bug #754531: extract Exif.CanonFi.FileNumber metadata -
+
+Added FileNumber metadata renaming option, which is a Canon-specific Exif value
+in the form xxx-yyyy, where xxx is the folder number and yyyy is the image
+number. Uses ExifTool. Thanks go to Etieene Charlier for researching the fix
+and contributing code to get it implemented.
+
+Fixed bug #695517: Added functionality to download MTS video files. There is
+currently no python based library to read metadata from MTS files, but ExifTool
+works.
+
+Fixed bug #859998: Download THM video thumbnail files -
+
+Some video files have THM video thumbnail files associated with them. Rapid
+Photo Downloader now downloads them and renames them to match the name of the
+video it is associated with.
+
+Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
+transfer -
+
+There is now an option to automatically rotate JPEG photos as they are
+downloaded. The program exiftran is used to do the rotation. The feature is
+turned on default.
+
+Fixed bug #859012: Confirm if really want to download from /home, /media or / -
+
+It is possible for the program's preferences to be set to download from /home,
+/media or / (the root of the file system). This can result in the program
+scanning a very large number of files, possibly causing the system to become
+unresponsive. The program now queries the user before commencing this scan to
+confirm if this is really what they want to do.
+
+Fixed bug #792228: clear all thumbnails when refresh command issued.
+
+Fixed a bug where the device progress bar would occasionally disappear when
+the download device was changed.
+
+Fixed a bug where the file extensions the program downloads could not be
+displayed from the command line.
+
+Fixed a bug where the program would crash when trying to convert a malformed
+thumbnail from one image mode to another.
+
+Updated Czech, Danish, Dutch, French, German, Hungarian, Italian, Norwegian,
+Polish, Serbian, Slovak, Spanish and Swedish translations.
+
+
Version 0.4.2
-------------
diff --git a/rapid/INSTALL b/rapid/INSTALL
index 8cde587..dd0ac2b 100644
--- a/rapid/INSTALL
+++ b/rapid/INSTALL
@@ -8,6 +8,8 @@ Rapid Photo Downloader requires the following software:
* python-notify 0.1.1 or higher
* python-imaging 1.1.7 or higher
* librsvg2-common 2.26 or higher
+* exiftool
+* exiftran
To run Rapid Photo Downloader you will need all the software mentioned above.
diff --git a/rapid/backupfile.py b/rapid/backupfile.py
index 40039bc..39efb53 100644
--- a/rapid/backupfile.py
+++ b/rapid/backupfile.py
@@ -85,7 +85,21 @@ class BackupFiles(multiprocessing.Process):
def progress_callback(self, amount_downloaded, total):
self.update_progress(amount_downloaded, total)
-
+ def progress_callback_no_update(self, amount_downloaded, total):
+ """called when copying very small files"""
+ pass
+
+ def backup_additional_file(self, dest_dir, full_file_name):
+ """Backs up small files like XMP or THM files"""
+ source = gio.File(full_file_name)
+ dest_name = os.path.join(dest_dir, os.path.split(full_file_name)[1])
+ logger.debug("Backing up %s", dest_name)
+ dest=gio.File(dest_name)
+ try:
+ source.copy(dest, self.progress_callback_no_update, cancellable=None)
+ except gio.Error, inst:
+ logger.error("Failed to backup file %s", full_file_name)
+
def run(self):
self.cancel_copy = gio.Cancellable()
@@ -174,6 +188,14 @@ class BackupFiles(multiprocessing.Process):
rpd_file.status = config.STATUS_DOWNLOAD_AND_BACKUP_FAILED
else:
rpd_file.status = config.STATUS_BACKUP_PROBLEM
+ else:
+ # backup any THM or XMP files
+ if rpd_file.download_thm_full_name:
+ self.backup_additional_file(dest_dir,
+ rpd_file.download_thm_full_name)
+ if rpd_file.download_xmp_full_name:
+ self.backup_additional_file(dest_dir,
+ rpd_file.download_xmp_full_name)
self.total_downloaded += rpd_file.size
bytes_not_downloaded = rpd_file.size - self.amount_downloaded
diff --git a/rapid/config.py b/rapid/config.py
index 47ecfff..74802ec 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.4.2'
+version = '0.4.3'
GCONF_KEY="/apps/rapid-photo-downloader"
diff --git a/rapid/copyfiles.py b/rapid/copyfiles.py
index 65a6c6d..f6ff073 100644
--- a/rapid/copyfiles.py
+++ b/rapid/copyfiles.py
@@ -20,6 +20,8 @@
import multiprocessing
import tempfile
import os
+import random
+import string
import gio
@@ -37,7 +39,11 @@ from gettext import gettext as _
class CopyFiles(multiprocessing.Process):
+ """
+ Copies files from source to temporary directory, giving them a random name
+ """
def __init__(self, photo_download_folder, video_download_folder, files,
+ modify_files_during_download, modify_pipe,
scan_pid,
batch_size_MB, results_pipe, terminate_queue,
run_event):
@@ -48,6 +54,8 @@ class CopyFiles(multiprocessing.Process):
self.photo_download_folder = photo_download_folder
self.video_download_folder = video_download_folder
self.files = files
+ self.modify_files_during_download = modify_files_during_download
+ self.modify_pipe = modify_pipe
self.scan_pid = scan_pid
self.no_files= len(self.files)
self.run_event = run_event
@@ -85,10 +93,17 @@ class CopyFiles(multiprocessing.Process):
def progress_callback(self, amount_downloaded, total):
self.update_progress(amount_downloaded, total)
+ def thm_progress_callback(self, amount_downloaded, total):
+ # we don't care about tracking download progress for tiny THM files!
+ pass
+
def run(self):
"""start the actual copying of files"""
+ #characters used to generate temporary filenames
+ filename_characters = string.letters + string.digits
+
self.bytes_downloaded = 0
self.total_downloaded = 0
@@ -107,8 +122,8 @@ class CopyFiles(multiprocessing.Process):
if self.photo_temp_dir or self.video_temp_dir:
self.thumbnail_maker = tn.Thumbnail()
-
- for i in range(len(self.files)):
+
+ for i in range(self.no_files):
rpd_file = self.files[i]
self.total_reached = False
@@ -119,9 +134,13 @@ class CopyFiles(multiprocessing.Process):
return None
source = gio.File(path=rpd_file.full_file_name)
+
+ #generate temporary name 5 digits long, no extension
+ temp_name = ''.join(random.choice(filename_characters) for i in xrange(5))
+
temp_full_file_name = os.path.join(
self._get_dest_dir(rpd_file.file_type),
- rpd_file.name)
+ temp_name)
rpd_file.temp_full_file_name = temp_full_file_name
dest = gio.File(path=temp_full_file_name)
@@ -152,9 +171,26 @@ class CopyFiles(multiprocessing.Process):
# succeeded or not. It's neccessary to keep the user informed.
self.total_downloaded += rpd_file.size
+ # copy THM (video thumbnail file) if there is one
+ if copy_succeeded and rpd_file.thm_full_name:
+ source = gio.File(path=rpd_file.thm_full_name)
+ # reuse video's file name
+ temp_thm_full_name = temp_full_file_name + '__rpd__thm'
+ dest = gio.File(path=temp_thm_full_name)
+ try:
+ source.copy(dest, self.thm_progress_callback, cancellable=self.cancel_copy)
+ rpd_file.temp_thm_full_name = temp_thm_full_name
+ logger.debug("Copied video THM file %s", rpd_file.temp_thm_full_name)
+ except gio.Error, inst:
+ logger.error("Failed to download video THM file: %s", rpd_file.thm_full_name)
+ else:
+ temp_thm_full_name = None
+
+
if copy_succeeded and rpd_file.generate_thumbnail:
thumbnail, thumbnail_icon = self.thumbnail_maker.get_thumbnail(
temp_full_file_name,
+ temp_thm_full_name,
rpd_file.file_type,
(160, 120), (100,100))
else:
@@ -163,10 +199,19 @@ class CopyFiles(multiprocessing.Process):
if rpd_file.metadata is not None:
rpd_file.metadata = None
-
- self.results_pipe.send((rpdmp.CONN_PARTIAL, (rpdmp.MSG_FILE,
- (copy_succeeded, rpd_file, i + 1, temp_full_file_name,
- thumbnail_icon, thumbnail))))
+
+
+ download_count = i + 1
+ if self.modify_files_during_download and copy_succeeded:
+ copy_finished = download_count == self.no_files
+
+ self.modify_pipe.send((rpd_file, download_count, temp_full_file_name,
+ thumbnail_icon, thumbnail, copy_finished))
+ else:
+ self.results_pipe.send((rpdmp.CONN_PARTIAL, (rpdmp.MSG_FILE,
+ (copy_succeeded, rpd_file, download_count,
+ temp_full_file_name,
+ thumbnail_icon, thumbnail))))
self.results_pipe.send((rpdmp.CONN_COMPLETE, None))
diff --git a/rapid/filemodify.py b/rapid/filemodify.py
new file mode 100644
index 0000000..3f4f2be
--- /dev/null
+++ b/rapid/filemodify.py
@@ -0,0 +1,138 @@
+#!/usr/bin/python
+# -*- coding: latin1 -*-
+
+### Copyright (C) 2011 Damon Lynch <damonlynch@gmail.com>
+
+### This program is free software; you can redistribute it and/or modify
+### it under the terms of the GNU General Public License as published by
+### the Free Software Foundation; either version 2 of the License, or
+### (at your option) any later version.
+
+### This program is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+### GNU General Public License for more details.
+
+### You should have received a copy of the GNU General Public License
+### along with this program; if not, write to the Free Software
+### Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import os.path, fractions
+import subprocess
+import multiprocessing
+import logging
+logger = multiprocessing.get_logger()
+
+import rpdmultiprocessing as rpdmp
+import rpdfile
+import metadataxmp as mxmp
+import subfolderfile
+
+WRITE_XMP_INPLACE = rpdfile.NON_RAW_IMAGE_EXTENSIONS + ['dng']
+
+def lossless_rotate(jpeg):
+ """using exiftran, performs a lossless, inplace translation of a jpeg, preserving time stamps"""
+ try:
+ logger.debug("Auto rotating %s", jpeg)
+ proc = subprocess.Popen(['exiftran', '-a', '-i', '-p', jpeg], stdout=subprocess.PIPE)
+ v = proc.communicate()[0].strip()
+ except OSError:
+ v = None
+ return v
+
+class FileModify(multiprocessing.Process):
+ def __init__(self, auto_rotate_jpeg, focal_length, results_pipe, terminate_queue,
+ run_event):
+ multiprocessing.Process.__init__(self)
+ self.results_pipe = results_pipe
+ self.terminate_queue = terminate_queue
+ self.run_event = run_event
+
+ self.auto_rotate_jpeg = auto_rotate_jpeg
+ self.focal_length = focal_length
+
+ def check_termination_request(self):
+ """
+ Check to see this process has not been requested to immediately terminate
+ """
+ if not self.terminate_queue.empty():
+ x = self.terminate_queue.get()
+ # terminate immediately
+ return True
+ return False
+
+ def create_rational(self, value):
+ return '%s/%s' % (value.numerator, value.denominator)
+
+ def run(self):
+
+ download_count = 0
+ copy_finished = False
+ while not copy_finished:
+ logger.debug("Finished %s. Getting next task.", download_count)
+
+ rpd_file, download_count, temp_full_file_name, thumbnail_icon, thumbnail, copy_finished = self.results_pipe.recv()
+ if rpd_file is None:
+ # this is a termination signal
+ logger.info("Terminating file modify via pipe")
+ return None
+ # pause if instructed by the caller
+ self.run_event.wait()
+
+ if self.check_termination_request():
+ return None
+
+ if self.auto_rotate_jpeg and rpd_file.file_type == rpdfile.FILE_TYPE_PHOTO:
+ if rpd_file.extension in rpdfile.JPEG_EXTENSIONS:
+ lossless_rotate(rpd_file.temp_full_file_name)
+
+ xmp_sidecar = None
+ # check to see if focal length and aperture data should be manipulated
+ if self.focal_length is not None and rpd_file.file_type == rpdfile.FILE_TYPE_PHOTO:
+ if subfolderfile.load_metadata(rpd_file, temp_file=True):
+ a = rpd_file.metadata.aperture()
+ if a == '0.0':
+ logger.info("Adjusting focal length and aperture for %s (%s)", rpd_file.temp_full_file_name, rpd_file.name)
+
+ new_focal_length = fractions.Fraction(self.focal_length,1)
+ new_aperture = fractions.Fraction(8,1)
+ if rpd_file.extension in WRITE_XMP_INPLACE:
+ try:
+ rpd_file.metadata["Exif.Photo.FocalLength"] = new_focal_length
+ rpd_file.metadata["Exif.Photo.FNumber"] = new_aperture
+ rpd_file.metadata.write(preserve_timestamps=True)
+ logger.debug("Wrote new focal length and aperture to %s (%s)", rpd_file.temp_full_file_name, rpd_file.name)
+ except:
+ logger.error("failed to write new focal length and aperture to %s (%s)!", rpd_file.temp_full_file_name, rpd_file.name)
+ else:
+ # write to xmp sidecar
+ xmp_sidecar = mxmp.XmpMetadataSidecar(rpd_file.temp_full_file_name)
+ xmp_sidecar.set_exif_value('FocalLength', self.create_rational(new_focal_length))
+ xmp_sidecar.set_exif_value('FNumber', self.create_rational(new_aperture))
+ # store values in rpd_file, so they can be used in the subfolderfile process
+ rpd_file.new_focal_length = new_focal_length
+ rpd_file.new_aperture = new_aperture
+
+ if False:
+ xmp_sidecar.set_contact_url('http://www.website.net')
+ xmp_sidecar.set_contact_email('user@email.com')
+
+ if xmp_sidecar is not None:
+ # need to write out xmp sidecar
+ o = xmp_sidecar.write_xmp_sidecar()
+ logger.debug("Wrote XMP sidecar file")
+ logger.debug("exiv2 output: %s", o)
+ rpd_file.temp_xmp_full_name = rpd_file.temp_full_file_name + '.xmp'
+
+
+ copy_succeeded = True
+ rpd_file.metadata = None #purge metadata, as it cannot be pickled
+
+ self.results_pipe.send((rpdmp.CONN_PARTIAL,
+ (copy_succeeded, rpd_file, download_count,
+ temp_full_file_name,
+ thumbnail_icon, thumbnail)))
+
+ self.results_pipe.send((rpdmp.CONN_COMPLETE, None))
+
+
diff --git a/rapid/generatename.py b/rapid/generatename.py
index 1c95478..99d7287 100644
--- a/rapid/generatename.py
+++ b/rapid/generatename.py
@@ -112,6 +112,39 @@ class PhotoName:
logger.error("Both file modification time and metadata date & time are invalid for file %s", self.rpd_file.full_file_name)
return ''
+ def _get_thm_extension(self):
+ """
+ Generates THM extension with correct capitalization, if needed
+ """
+ if self.rpd_file.thm_full_name:
+ thm_extension = os.path.splitext(self.rpd_file.thm_full_name)[1]
+ if self.L2 == UPPERCASE:
+ thm_extension = thm_extension.upper()
+ elif self.L2 == LOWERCASE:
+ thm_extension = thm_extension.lower()
+ self.rpd_file.thm_extension = thm_extension
+ else:
+ self.rpd_file.thm_extension = None
+
+ def _get_xmp_extension(self, extension):
+ """
+ Generates XMP extension with correct capitalization, if needed.
+ """
+ if self.rpd_file.temp_xmp_full_name:
+ if self.L2 == UPPERCASE:
+ self.rpd_file.xmp_extension = '.XMP'
+ elif self.L2 == LOWERCASE:
+ self.rpd_file.xmp_extension = '.xmp'
+ else:
+ # mimic capitalization of extension
+ if extension.isupper():
+ self.rpd_file.xmp_extension = '.XMP'
+ else:
+ self.rpd_file.xmp_extension = '.xmp'
+ else:
+ self.rpd_file.xmp_extension = None
+
+
def _get_filename_component(self):
"""
Returns portion of new file / subfolder name based on the file name
@@ -121,9 +154,13 @@ class PhotoName:
if self.L1 == NAME_EXTENSION:
filename = self.rpd_file.name
+ self._get_thm_extension()
+ self._get_xmp_extension(extension)
elif self.L1 == NAME:
filename = name
elif self.L1 == EXTENSION:
+ self._get_thm_extension()
+ self._get_xmp_extension(extension)
if extension:
if not self.strip_initial_period_from_extension:
# keep the period / dot of the extension, so the user does not
@@ -196,7 +233,10 @@ class PhotoName:
padding = LIST_SHUTTER_COUNT_L2.index(self.L2) + 3
formatter = '%0' + str(padding) + "i"
v = formatter % v
-
+ elif self.L1 == FILE_NUMBER:
+ v = self.rpd_file.metadata.file_number()
+ if v and self.L2 == FILE_NUMBER_FOLDER:
+ v = v[:3]
elif self.L1 == OWNER_NAME:
v = self.rpd_file.metadata.owner_name()
elif self.L1 == ARTIST:
diff --git a/rapid/generatenameconfig.py b/rapid/generatenameconfig.py
index 4d1f75c..0fbeb38 100644
--- a/rapid/generatenameconfig.py
+++ b/rapid/generatenameconfig.py
@@ -31,7 +31,7 @@ ORDER_KEY = "__order__"
# is to have them put into the translation template. If you change the values below
# then you MUST change the value in class i18TranslateMeThanks as well!!
-# *** Level 0
+# *** Level 0, i.e. first column of values presented to user
DATE_TIME = 'Date time'
TEXT = 'Text'
FILENAME = 'Filename'
@@ -41,7 +41,7 @@ JOB_CODE = 'Job code'
SEPARATOR = os.sep
-# *** Level 1
+# *** Level 1, i.e. second column of values presented to user
# Date time
IMAGE_DATE = 'Image date'
@@ -68,6 +68,9 @@ SHORT_CAMERA_MODEL = 'Short camera model'
SHORT_CAMERA_MODEL_HYPHEN = 'Hyphenated short camera model'
SERIAL_NUMBER = 'Serial number'
SHUTTER_COUNT = 'Shutter count'
+#Currently the only file number is Exif.CanonFi.FileNumber,
+#which is in the format xxx-yyyy, where xxx is the folder and yyyy the image
+FILE_NUMBER = 'File number'
OWNER_NAME = 'Owner name'
COPYRIGHT = 'Copyright'
ARTIST = 'Artist'
@@ -84,12 +87,11 @@ DOWNLOAD_SEQ_NUMBER = 'Downloads today'
SESSION_SEQ_NUMBER = 'Session number'
SUBFOLDER_SEQ_NUMBER = 'Subfolder number'
STORED_SEQ_NUMBER = 'Stored number'
-
SEQUENCE_LETTER = 'Sequence letter'
-# *** Level 2
+# *** Level 2, i.e. third and final column of values presented to user
# Image number
IMAGE_NUMBER_ALL = 'All digits'
@@ -113,6 +115,9 @@ SEQUENCE_NUMBER_5 = "Five digits"
SEQUENCE_NUMBER_6 = "Six digits"
SEQUENCE_NUMBER_7 = "Seven digits"
+#File number
+FILE_NUMBER_FOLDER = "Folder only"
+FILE_NUMBER_ALL = "Folder and file"
# Now, define dictionaries and lists of valid combinations of preferences.
@@ -184,6 +189,12 @@ class i18TranslateMeThanks:
_('Serial number')
# Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamemetadata
_('Shutter count')
+ #File number currently refers to the Exif value Exif.Canon.FileNumber
+ _('File number')
+ #Only the folder component of the Exif.Canon.FileNumber value
+ _('Folder only')
+ #The folder and file component of the Exif.Canon.FileNumber value
+ _('Folder and file')
# Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamemetadata
_('Owner name')
_('Codec')
@@ -310,8 +321,12 @@ LIST_SHUTTER_COUNT_L2 = [
SEQUENCE_NUMBER_3,
SEQUENCE_NUMBER_4,
SEQUENCE_NUMBER_5,
- SEQUENCE_NUMBER_6,
+ SEQUENCE_NUMBER_6,
]
+FILE_NUMBER_L2 = [
+ FILE_NUMBER_FOLDER,
+ FILE_NUMBER_ALL
+ ]
# Level 1
LIST_DATE_TIME_L1 = [IMAGE_DATE, TODAY, YESTERDAY, DOWNLOAD_TIME]
@@ -367,7 +382,8 @@ LIST_METADATA_L1 = [APERTURE, ISO, EXPOSURE_TIME, FOCAL_LENGTH,
SHORT_CAMERA_MODEL,
SHORT_CAMERA_MODEL_HYPHEN,
SERIAL_NUMBER,
- SHUTTER_COUNT,
+ SHUTTER_COUNT,
+ FILE_NUMBER,
OWNER_NAME,
ARTIST,
COPYRIGHT]
@@ -384,7 +400,8 @@ DICT_METADATA_L1 = {
SHORT_CAMERA_MODEL: LIST_CASE_L2,
SHORT_CAMERA_MODEL_HYPHEN: LIST_CASE_L2,
SERIAL_NUMBER: None,
- SHUTTER_COUNT: LIST_SHUTTER_COUNT_L2,
+ SHUTTER_COUNT: LIST_SHUTTER_COUNT_L2,
+ FILE_NUMBER: FILE_NUMBER_L2,
OWNER_NAME: LIST_CASE_L2,
ARTIST: LIST_CASE_L2,
COPYRIGHT: LIST_CASE_L2,
diff --git a/rapid/glade3/about.ui b/rapid/glade3/about.ui
index 3e59698..7e10e37 100644
--- a/rapid/glade3/about.ui
+++ b/rapid/glade3/about.ui
@@ -55,6 +55,7 @@ Aron Xu &lt;happyaron.xu@gmail.com&gt;
<object class="GtkBox" id="dialog-vbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
<property name="spacing">2</property>
<child internal-child="action_area">
<object class="GtkButtonBox" id="dialog-action_area1">
diff --git a/rapid/glade3/prefs.ui b/rapid/glade3/prefs.ui
index 0a13978..2e0e53c 100644
--- a/rapid/glade3/prefs.ui
+++ b/rapid/glade3/prefs.ui
@@ -1,7 +1,7 @@
-<?xml version="1.0" encoding="UTF-8"?>
+<?xml version="1.0"?>
<interface>
<requires lib="gtk+" version="2.16"/>
- <!-- interface-naming-policy project-wide -->
+ <!-- interface-naming-policy toplevel-contextual -->
<object class="GtkAdjustment" id="hour_adjustment">
<property name="upper">23</property>
<property name="step_increment">1</property>
@@ -13,7 +13,6 @@
<property name="page_increment">10</property>
</object>
<object class="GtkDialog" id="preferencesdialog">
- <property name="can_focus">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="border_width">5</property>
<property name="title" translatable="yes">Preferences: Rapid Photo Downloader</property>
@@ -22,69 +21,21 @@
<property name="default_height">500</property>
<property name="icon">rapid-photo-downloader.svg</property>
<property name="type_hint">dialog</property>
- <signal name="destroy" handler="on_preferencesdialog_destroy" swapped="no"/>
- <signal name="response" handler="on_preferencesdialog_response" swapped="no"/>
+ <signal name="destroy" handler="on_preferencesdialog_destroy"/>
+ <signal name="response" handler="on_preferencesdialog_response"/>
<child internal-child="vbox">
<object class="GtkVBox" id="dialog-vbox2">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">2</property>
- <child internal-child="action_area">
- <object class="GtkHButtonBox" id="dialog-action_area2">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="layout_style">end</property>
- <child>
- <object class="GtkButton" id="help_button1">
- <property name="label">gtk-help</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="use_action_appearance">False</property>
- <property name="use_stock">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- <property name="secondary">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="close_button">
- <property name="label">gtk-close</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="use_action_appearance">False</property>
- <property name="use_stock">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="pack_type">end</property>
- <property name="position">0</property>
- </packing>
- </child>
<child>
<object class="GtkHBox" id="hbox7">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">2</property>
<child>
<object class="GtkScrolledWindow" id="scrolled_window">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="resize_mode">queue</property>
- <property name="hscrollbar_policy">automatic</property>
- <property name="vscrollbar_policy">automatic</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTreeView" id="treeview">
@@ -93,13 +44,11 @@
<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="headers_visible">False</property>
- <signal name="cursor-changed" handler="on_treeview_cursor_changed" swapped="no"/>
+ <signal name="cursor_changed" handler="on_treeview_cursor_changed"/>
</object>
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="padding">5</property>
<property name="position">0</property>
</packing>
@@ -112,33 +61,27 @@
<child>
<object class="GtkVBox" id="folder_tab">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkVBox" id="vbox6">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<object class="GtkHBox" id="hbox8">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkImage" id="image2">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="stock">gtk-directory</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label4">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;span weight="bold" size="x-large"&gt;Photo Download Folders&lt;/span&gt;</property>
<property name="use_markup">True</property>
</object>
@@ -150,19 +93,15 @@
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator1">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
@@ -176,29 +115,24 @@
<child>
<object class="GtkHBox" id="hbox9">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="label16">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkTable" id="download_folder_table">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="n_rows">7</property>
<property name="n_columns">3</property>
<child>
<object class="GtkLabel" id="example_photo_download_path_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">&lt;i&gt;Example: /home/user/Pictures&lt;/i&gt;</property>
<property name="use_markup">True</property>
@@ -214,7 +148,6 @@
<child>
<object class="GtkLabel" id="label8">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">&lt;b&gt;Download Subfolders&lt;/b&gt;</property>
<property name="use_markup">True</property>
@@ -229,7 +162,6 @@
<child>
<object class="GtkLabel" id="lblPhotos1">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Download folder:</property>
</object>
@@ -245,7 +177,6 @@
<child>
<object class="GtkLabel" id="label7">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="ypad">12</property>
<property name="label" translatable="yes">Choose the download folder. Subfolders for the downloaded photos will be automatically created in this folder using the structure specified below.</property>
@@ -263,7 +194,6 @@
<child>
<object class="GtkLabel" id="label5">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">&lt;b&gt;Download Folder&lt;/b&gt;</property>
<property name="use_markup">True</property>
@@ -276,7 +206,6 @@
<child>
<object class="GtkLabel" id="photo_subfolder_warning_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
<property name="wrap">True</property>
@@ -293,7 +222,6 @@
<child>
<object class="GtkVBox" id="subfolder_vbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<placeholder/>
</child>
@@ -326,26 +254,20 @@
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label23">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="padding">12</property>
<property name="position">1</property>
</packing>
@@ -358,7 +280,6 @@
<child type="tab">
<object class="GtkLabel" id="download_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">Photo Folders</property>
</object>
<packing>
@@ -368,57 +289,46 @@
<child>
<object class="GtkVBox" id="rename_tab">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkVBox" id="vbox7">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<object class="GtkHBox" id="hbox10">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkImage" id="image3">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="stock">gtk-convert</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label6">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;span weight="bold" size="x-large"&gt;Photo Rename&lt;/span&gt; </property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator2">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
@@ -432,34 +342,28 @@
<child>
<object class="GtkHBox" id="hbox12">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="label24">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkVBox" id="rename_vbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="label42">
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">&lt;b&gt;Photo Rename&lt;/b&gt;</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
@@ -467,19 +371,15 @@
<object class="GtkScrolledWindow" id="rename_scrolledwindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="hscrollbar_policy">automatic</property>
- <property name="vscrollbar_policy">automatic</property>
<property name="window_placement_set">True</property>
<child>
<object class="GtkViewport" id="viewport2">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="resize_mode">queue</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkVBox" id="rename_table_vbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<placeholder/>
</child>
@@ -489,21 +389,17 @@
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkTable" id="rename_example_table">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="n_rows">3</property>
<property name="n_columns">3</property>
<child>
<object class="GtkLabel" id="label17">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes"> </property>
</object>
<packing>
@@ -515,7 +411,6 @@
<child>
<object class="GtkLabel" id="label15">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes"> </property>
</object>
<packing>
@@ -528,7 +423,6 @@
<child>
<object class="GtkLabel" id="new_name_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
<property name="label">translators please ignore this</property>
@@ -545,7 +439,6 @@
<child>
<object class="GtkLabel" id="original_name_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label">translators please ignore this</property>
</object>
@@ -560,7 +453,6 @@
<child>
<object class="GtkLabel" id="label21">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
<property name="label" translatable="yes">&lt;i&gt;New:&lt;/i&gt;</property>
@@ -578,7 +470,6 @@
<child>
<object class="GtkLabel" id="label20">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">&lt;i&gt;Original:&lt;/i&gt;</property>
<property name="use_markup">True</property>
@@ -595,7 +486,6 @@
<child>
<object class="GtkLabel" id="label14">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">&lt;b&gt;Example&lt;/b&gt;</property>
<property name="use_markup">True</property>
@@ -614,26 +504,20 @@
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label25">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="padding">12</property>
<property name="position">1</property>
</packing>
@@ -647,7 +531,6 @@
<child type="tab">
<object class="GtkLabel" id="rename_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">Photo Rename</property>
</object>
<packing>
@@ -658,33 +541,27 @@
<child>
<object class="GtkVBox" id="folder_tab1">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkVBox" id="vbox12">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<object class="GtkHBox" id="hbox11">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkImage" id="image5">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="stock">gtk-directory</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label46">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;span weight="bold" size="x-large"&gt;Video Download Folders&lt;/span&gt;</property>
<property name="use_markup">True</property>
</object>
@@ -696,19 +573,15 @@
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator8">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
@@ -721,20 +594,16 @@
</child>
<child>
<object class="GtkHBox" id="folder_videos_cannot_be_downloaded_hbox">
- <property name="can_focus">False</property>
<child>
<object class="GtkLabel" id="folder_videos_cannot_be_downloaded_label">
- <property name="can_focus">False</property>
<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;hachoir metadata&lt;/i&gt; and &lt;i&gt;kaa metadata&lt;/i&gt; packages for python.</property>
+ <property name="label" translatable="yes">Sorry, video downloading functionality disabled. To download videos, please install either the &lt;i&gt;hachoir metadata&lt;/i&gt; and &lt;i&gt;kaa metadata&lt;/i&gt; packages for python, or &lt;i&gt;exiftool&lt;/i&gt;.</property>
<property name="use_markup">True</property>
<property name="wrap">True</property>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
@@ -748,29 +617,24 @@
<child>
<object class="GtkHBox" id="video_folders_hbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="label57">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkTable" id="video_download_folder_table">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="n_rows">7</property>
<property name="n_columns">3</property>
<child>
<object class="GtkLabel" id="lblPhotos2">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Download folder:</property>
</object>
@@ -786,7 +650,6 @@
<child>
<object class="GtkLabel" id="label59">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="ypad">12</property>
<property name="label" translatable="yes">Choose the download folder. Subfolders for the downloaded videos will be automatically created in this folder using the structure specified below.</property>
@@ -804,7 +667,6 @@
<child>
<object class="GtkLabel" id="label61">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">&lt;b&gt;Download Folder&lt;/b&gt;</property>
<property name="use_markup">True</property>
@@ -817,7 +679,6 @@
<child>
<object class="GtkLabel" id="label62">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">&lt;b&gt;Download Subfolders&lt;/b&gt;</property>
<property name="use_markup">True</property>
@@ -832,7 +693,6 @@
<child>
<object class="GtkLabel" id="example_video_download_path_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">&lt;i&gt;Example: /home/user/Pictures&lt;/i&gt;</property>
<property name="use_markup">True</property>
@@ -848,7 +708,6 @@
<child>
<object class="GtkLabel" id="video_subfolder_warning_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
<property name="wrap">True</property>
@@ -865,7 +724,6 @@
<child>
<object class="GtkVBox" id="video_subfolder_vbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<placeholder/>
</child>
@@ -898,26 +756,20 @@
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label63">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="padding">12</property>
<property name="position">2</property>
</packing>
@@ -930,7 +782,6 @@
<child type="tab">
<object class="GtkLabel" id="video_download_folder">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">Video Folders</property>
</object>
<packing>
@@ -941,57 +792,46 @@
<child>
<object class="GtkVBox" id="video_rename_tab">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkVBox" id="vbox10">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<object class="GtkHBox" id="hbox13">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkImage" id="image1">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="stock">gtk-convert</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label32">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;span weight="bold" size="x-large"&gt;Video Rename&lt;/span&gt; </property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator4">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
@@ -1005,29 +845,24 @@
<child>
<object class="GtkHBox" id="hbox14">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="label44">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkVBox" id="rename_vbox1">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="videos_cannot_be_downloaded_label">
- <property name="can_focus">False</property>
<property name="xalign">0</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="label" translatable="yes">Sorry, video downloading functionality disabled. To download videos, please install either the &lt;i&gt;hachoir metadata&lt;/i&gt; and &lt;i&gt;kaa metadata&lt;/i&gt; packages for python, or &lt;i&gt;exiftool&lt;/i&gt;.</property>
<property name="use_markup">True</property>
<property name="wrap">True</property>
</object>
@@ -1041,19 +876,15 @@
<object class="GtkScrolledWindow" id="video_rename_scrolledwindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="hscrollbar_policy">automatic</property>
- <property name="vscrollbar_policy">automatic</property>
<property name="window_placement_set">True</property>
<child>
<object class="GtkViewport" id="viewport1">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="resize_mode">queue</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkVBox" id="video_rename_table_vbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<placeholder/>
</child>
@@ -1063,21 +894,17 @@
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkTable" id="video_rename_example_table">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="n_rows">3</property>
<property name="n_columns">3</property>
<child>
<object class="GtkLabel" id="label55">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes"> </property>
</object>
<packing>
@@ -1089,7 +916,6 @@
<child>
<object class="GtkLabel" id="label56">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes"> </property>
</object>
<packing>
@@ -1102,7 +928,6 @@
<child>
<object class="GtkLabel" id="video_new_name_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
<property name="label">translators please ignore this</property>
@@ -1119,7 +944,6 @@
<child>
<object class="GtkLabel" id="video_original_name_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label">translators please ignore this</property>
</object>
@@ -1134,7 +958,6 @@
<child>
<object class="GtkLabel" id="new_video_filename_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
<property name="label" translatable="yes">&lt;i&gt;New:&lt;/i&gt;</property>
@@ -1152,7 +975,6 @@
<child>
<object class="GtkLabel" id="original_video_filename_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">&lt;i&gt;Original:&lt;/i&gt;</property>
<property name="use_markup">True</property>
@@ -1169,7 +991,6 @@
<child>
<object class="GtkLabel" id="example_video_filename_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">&lt;b&gt;Example&lt;/b&gt;</property>
<property name="use_markup">True</property>
@@ -1188,26 +1009,20 @@
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label60">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="padding">12</property>
<property name="position">1</property>
</packing>
@@ -1220,7 +1035,6 @@
<child type="tab">
<object class="GtkLabel" id="video_rename_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">Video Rename</property>
</object>
<packing>
@@ -1231,57 +1045,46 @@
<child>
<object class="GtkVBox" id="rename_options_tab">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkVBox" id="vbox14">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<object class="GtkHBox" id="hbox18">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkImage" id="image7">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="icon_name">input-keyboard</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label10">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;span weight="bold" size="x-large"&gt;Rename Options&lt;/span&gt;</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator5">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
@@ -1295,11 +1098,9 @@
<child>
<object class="GtkVBox" id="reame_options_vbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<object class="GtkLabel" id="sequence_number_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="xpad">12</property>
<property name="label" translatable="yes">&lt;b&gt;Sequence Numbers&lt;/b&gt;</property>
@@ -1307,140 +1108,112 @@
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="sequence_number_hbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="spacer_seq_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xpad">12</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkVBox" id="seq_vbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="label47">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Specify the time in 24 hour format at which the &lt;i&gt;Downloads today&lt;/i&gt; sequence number should be reset.</property>
<property name="use_markup">True</property>
<property name="wrap">True</property>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox23">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<object class="GtkVBox" id="vbox3">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="label49">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Day start:</property>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label51">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Downloads today:</property>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label52">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Stored number:</property>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label54">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xpad">6</property>
<property name="label" translatable="yes"> </property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkVBox" id="sequence_vbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkHBox" id="hbox22">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<object class="GtkSpinButton" id="hour_spinbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="max_length">2</property>
- <property name="invisible_char">•</property>
+ <property name="invisible_char">&#x2022;</property>
<property name="width_chars">2</property>
<property name="xalign">1</property>
<property name="truncate_multiline">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
- <property name="primary_icon_sensitive">True</property>
- <property name="secondary_icon_sensitive">True</property>
<property name="adjustment">hour_adjustment</property>
<property name="numeric">True</property>
- <signal name="value-changed" handler="on_hour_spinbutton_value_changed" swapped="no"/>
+ <signal name="value_changed" handler="on_hour_spinbutton_value_changed"/>
</object>
<packing>
<property name="expand">False</property>
@@ -1451,7 +1224,6 @@
<child>
<object class="GtkLabel" id="label50">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">:</property>
</object>
<packing>
@@ -1465,17 +1237,13 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="max_length">2</property>
- <property name="invisible_char">•</property>
+ <property name="invisible_char">&#x2022;</property>
<property name="width_chars">2</property>
<property name="xalign">1</property>
<property name="truncate_multiline">True</property>
- <property name="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
- <property name="primary_icon_sensitive">True</property>
- <property name="secondary_icon_sensitive">True</property>
<property name="adjustment">minute_adjustment</property>
<property name="numeric">True</property>
- <signal name="value-changed" handler="on_minute_spinbutton_value_changed" swapped="no"/>
+ <signal name="value_changed" handler="on_minute_spinbutton_value_changed"/>
</object>
<packing>
<property name="expand">False</property>
@@ -1486,20 +1254,17 @@
<child>
<object class="GtkLabel" id="label53">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes"> hh:mm</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
@@ -1512,14 +1277,11 @@
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
@@ -1529,38 +1291,30 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
- <signal name="toggled" handler="on_synchronize_raw_jpg_checkbutton_toggled" swapped="no"/>
+ <signal name="toggled" handler="on_synchronize_raw_jpg_checkbutton_toggled"/>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label48">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="padding">12</property>
<property name="position">1</property>
</packing>
@@ -1568,7 +1322,6 @@
<child>
<object class="GtkLabel" id="compatibility_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="xpad">12</property>
<property name="label" translatable="yes">&lt;b&gt;Compatibility with Other Operating Systems&lt;/b&gt;</property>
@@ -1576,37 +1329,31 @@
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="compatibility_hbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="compatibility_spacer_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xpad">12</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkTable" id="compatibility_table">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="n_rows">2</property>
<property name="n_columns">2</property>
<child>
<object class="GtkLabel" id="label9">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Specify whether photo, video and folder names should have any characters removed that are not allowed by other operating systems.</property>
<property name="wrap">True</property>
@@ -1621,10 +1368,9 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
- <signal name="toggled" handler="on_strip_characters_checkbutton_toggled" swapped="no"/>
+ <signal name="toggled" handler="on_strip_characters_checkbutton_toggled"/>
</object>
<packing>
<property name="right_attach">2</property>
@@ -1634,34 +1380,27 @@
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label33">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="padding">12</property>
<property name="position">4</property>
</packing>
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="padding">12</property>
<property name="position">1</property>
</packing>
@@ -1674,7 +1413,6 @@
<child type="tab">
<object class="GtkLabel" id="rename_options_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">Rename Options</property>
</object>
<packing>
@@ -1685,57 +1423,46 @@
<child>
<object class="GtkVBox" id="job_codes_tab">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkVBox" id="job_codes_header_vbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<object class="GtkHBox" id="hbox188">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkImage" id="image77">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="stock">rapid-photo-downloader-jobcode</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label1340">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;span weight="bold" size="x-large"&gt;Job Codes&lt;/span&gt;</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator44">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
@@ -1749,14 +1476,11 @@
<child>
<object class="GtkVBox" id="job_codes_vbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<object class="GtkVBox" id="job_code_vbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<object class="GtkLabel" id="job_code_label">
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="xpad">12</property>
<property name="label" translatable="yes">&lt;b&gt;Job Codes&lt;/b&gt;</property>
@@ -1764,23 +1488,19 @@
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="job_code_hbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="job_code_spacer_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
@@ -1788,8 +1508,6 @@
<object class="GtkScrolledWindow" id="job_code_scrolledwindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="hscrollbar_policy">automatic</property>
- <property name="vscrollbar_policy">automatic</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTreeView" id="job_code_treeview">
@@ -1803,18 +1521,15 @@
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="job_code_button_hbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<object class="GtkVButtonBox" id="job_code_vbuttonbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<property name="layout_style">start</property>
<child>
@@ -1823,9 +1538,8 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
- <signal name="clicked" handler="on_add_job_code_button_clicked" swapped="no"/>
+ <signal name="clicked" handler="on_add_job_code_button_clicked"/>
</object>
<packing>
<property name="expand">False</property>
@@ -1839,9 +1553,8 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="use_action_appearance">False</property>
<property name="use_stock">True</property>
- <signal name="clicked" handler="on_remove_job_code_button_clicked" swapped="no"/>
+ <signal name="clicked" handler="on_remove_job_code_button_clicked"/>
</object>
<packing>
<property name="expand">False</property>
@@ -1855,9 +1568,8 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
- <signal name="clicked" handler="on_remove_all_job_code_button_clicked" swapped="no"/>
+ <signal name="clicked" handler="on_remove_all_job_code_button_clicked"/>
</object>
<packing>
<property name="expand">False</property>
@@ -1867,22 +1579,17 @@
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
@@ -1891,15 +1598,11 @@
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
@@ -1911,7 +1614,6 @@
<child type="tab">
<object class="GtkLabel" id="job_codes_tab_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">Job Codes</property>
</object>
<packing>
@@ -1922,57 +1624,46 @@
<child>
<object class="GtkVBox" id="device_tab">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkVBox" id="vbox4">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<object class="GtkHBox" id="hbox15">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkImage" id="image6">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">media-flash</property>
+ <property name="icon_name">drive-removable-media</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label22">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;span weight="bold" size="x-large"&gt;Devices&lt;/span&gt;</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator6">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
@@ -1986,11 +1677,9 @@
<child>
<object class="GtkVBox" id="vbox5">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="label41">
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="xpad">12</property>
<property name="label" translatable="yes">Devices</property>
@@ -2000,14 +1689,12 @@
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label18">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="xpad">12</property>
<property name="label" translatable="yes">Devices are from where to download photos and videos, such as cameras, memory cards or Portable Storage Devices.
@@ -2019,31 +1706,25 @@ You can download from multiple devices simultaneously, or you can specify a loca
<property name="wrap">True</property>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox16">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<object class="GtkLabel" id="label26">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xpad">3</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkTable" id="devices_table">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="n_rows">3</property>
<property name="n_columns">2</property>
<property name="row_spacing">3</property>
@@ -2053,9 +1734,8 @@ You can download from multiple devices simultaneously, or you can specify a loca
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
- <signal name="toggled" handler="on_autodetect_psd_checkbutton_toggled" swapped="no"/>
+ <signal name="toggled" handler="on_autodetect_psd_checkbutton_toggled"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -2071,9 +1751,8 @@ You can download from multiple devices simultaneously, or you can specify a loca
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
- <signal name="toggled" handler="on_autodetect_device_checkbutton_toggled" swapped="no"/>
+ <signal name="toggled" handler="on_autodetect_device_checkbutton_toggled"/>
</object>
<packing>
<property name="right_attach">2</property>
@@ -2082,7 +1761,6 @@ You can download from multiple devices simultaneously, or you can specify a loca
<child>
<object class="GtkLabel" id="autodetect_image_devices_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="ypad">6</property>
<property name="label" translatable="yes">If you enable automatic detection of Portable Storage Devices, the entire device will be scanned. On large devices, this could take some time.
@@ -2105,44 +1783,36 @@ When this option is enabled, and a potential device is detected, you will be pro
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label28">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox17">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkTable" id="devices2_table">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="n_rows">2</property>
<property name="n_columns">2</property>
<child>
<object class="GtkLabel" id="device_location_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="xpad">12</property>
<property name="label" translatable="yes">Location:</property>
@@ -2157,7 +1827,6 @@ When this option is enabled, and a potential device is detected, you will be pro
<child>
<object class="GtkLabel" id="device_location_explanation_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="xalign">0</property>
<property name="xpad">12</property>
@@ -2174,8 +1843,6 @@ When this option is enabled, and a potential device is detected, you will be pro
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
@@ -2185,25 +1852,21 @@ When this option is enabled, and a potential device is detected, you will be pro
<child>
<object class="GtkLabel" id="label30">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="padding">12</property>
<property name="position">1</property>
</packing>
@@ -2216,7 +1879,6 @@ When this option is enabled, and a potential device is detected, you will be pro
<child type="tab">
<object class="GtkLabel" id="device_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">Devices</property>
</object>
<packing>
@@ -2227,69 +1889,57 @@ When this option is enabled, and a potential device is detected, you will be pro
<child>
<object class="GtkVBox" id="device_options_tab">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<object class="GtkHBox" id="exclusions_header_hbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkImage" id="image10">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="stock">gtk-preferences</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="exclusionlabel">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;span weight="bold" size="x-large"&gt;Device Options&lt;/span&gt;</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator3">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkVBox" id="vbox2">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkVBox" id="vbox15">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="remebered_devices_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="xpad">12</property>
<property name="label" translatable="yes">Remembered Paths</property>
@@ -2299,14 +1949,12 @@ When this option is enabled, and a potential device is detected, you will be pro
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="remembered_devices_explanation_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="xpad">12</property>
<property name="label" translatable="yes">Remembered paths are those associated with devices that you have chosen to always scan or ignore when automatic detection of Portable Storage Devices is enabled.</property>
@@ -2314,23 +1962,19 @@ When this option is enabled, and a potential device is detected, you will be pro
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="remembered_devices_hbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="remembered_devices_spacer_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
@@ -2338,8 +1982,6 @@ When this option is enabled, and a potential device is detected, you will be pro
<object class="GtkScrolledWindow" id="remembered_devices_scrolledwindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="hscrollbar_policy">automatic</property>
- <property name="vscrollbar_policy">automatic</property>
<child>
<object class="GtkTreeView" id="remembered_devices_treeview">
<property name="width_request">250</property>
@@ -2351,18 +1993,15 @@ When this option is enabled, and a potential device is detected, you will be pro
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="remembered_devices_button_hbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<object class="GtkVButtonBox" id="remembered_devices_vbuttonbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<property name="layout_style">start</property>
<child>
@@ -2371,9 +2010,8 @@ When this option is enabled, and a potential device is detected, you will be pro
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="use_action_appearance">False</property>
<property name="use_stock">True</property>
- <signal name="clicked" handler="on_remove_remembered_device_button_clicked" swapped="no"/>
+ <signal name="clicked" handler="on_remove_remembered_device_button_clicked"/>
</object>
<packing>
<property name="expand">False</property>
@@ -2387,9 +2025,8 @@ When this option is enabled, and a potential device is detected, you will be pro
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
- <signal name="clicked" handler="on_remove_all_remembered_device_button_clicked" swapped="no"/>
+ <signal name="clicked" handler="on_remove_all_remembered_device_button_clicked"/>
</object>
<packing>
<property name="expand">False</property>
@@ -2399,29 +2036,22 @@ When this option is enabled, and a potential device is detected, you will be pro
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="padding">12</property>
<property name="position">0</property>
</packing>
@@ -2429,12 +2059,10 @@ When this option is enabled, and a potential device is detected, you will be pro
<child>
<object class="GtkVBox" id="vbox1">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="ignored_paths_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="xpad">12</property>
<property name="label" translatable="yes">Ignored Paths</property>
@@ -2444,14 +2072,12 @@ When this option is enabled, and a potential device is detected, you will be pro
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="ignored_paths_explanation_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="xpad">12</property>
<property name="label" translatable="yes">Specify the ending portion of any paths you want ignored when scanning devices for photos or videos. Any path ending with the values below will not be scanned.</property>
@@ -2459,23 +2085,19 @@ When this option is enabled, and a potential device is detected, you will be pro
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="ignored_paths_hbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="ignored_paths_spacer_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
@@ -2483,8 +2105,6 @@ When this option is enabled, and a potential device is detected, you will be pro
<object class="GtkScrolledWindow" id="ignored_paths_scrolledwindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="hscrollbar_policy">automatic</property>
- <property name="vscrollbar_policy">automatic</property>
<child>
<object class="GtkTreeView" id="ignored_paths_treeview">
<property name="width_request">250</property>
@@ -2496,18 +2116,15 @@ When this option is enabled, and a potential device is detected, you will be pro
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="ignored_paths_button_hbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<object class="GtkVButtonBox" id="ignored_paths_vbuttonbox">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<property name="layout_style">start</property>
<child>
@@ -2516,9 +2133,8 @@ When this option is enabled, and a potential device is detected, you will be pro
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
- <signal name="clicked" handler="on_add_ignored_path_button_clicked" swapped="no"/>
+ <signal name="clicked" handler="on_add_ignored_path_button_clicked"/>
</object>
<packing>
<property name="expand">False</property>
@@ -2532,9 +2148,8 @@ When this option is enabled, and a potential device is detected, you will be pro
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="use_action_appearance">False</property>
<property name="use_stock">True</property>
- <signal name="clicked" handler="on_remove_ignored_path_button_clicked" swapped="no"/>
+ <signal name="clicked" handler="on_remove_ignored_path_button_clicked"/>
</object>
<packing>
<property name="expand">False</property>
@@ -2548,9 +2163,8 @@ When this option is enabled, and a potential device is detected, you will be pro
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
- <signal name="clicked" handler="on_remove_all_ignored_paths_button_clicked" swapped="no"/>
+ <signal name="clicked" handler="on_remove_all_ignored_paths_button_clicked"/>
</object>
<packing>
<property name="expand">False</property>
@@ -2560,43 +2174,35 @@ When this option is enabled, and a potential device is detected, you will be pro
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox1">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<object class="GtkCheckButton" id="ignored_paths_use_re_checkbutton">
<property name="label" translatable="yes">Use _python-style regular expressions</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
- <signal name="toggled" handler="on_ignored_paths_use_re_checkbutton_toggled" swapped="no"/>
+ <signal name="toggled" handler="on_ignored_paths_use_re_checkbutton_toggled"/>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="padding">12</property>
<property name="position">0</property>
</packing>
@@ -2604,21 +2210,16 @@ When this option is enabled, and a potential device is detected, you will be pro
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
@@ -2630,7 +2231,6 @@ When this option is enabled, and a potential device is detected, you will be pro
<child type="tab">
<object class="GtkLabel" id="device_options_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">Device Options</property>
</object>
<packing>
@@ -2641,56 +2241,45 @@ When this option is enabled, and a potential device is detected, you will be pro
<child>
<object class="GtkVBox" id="backup_tab">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<object class="GtkVBox" id="vbox8">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<object class="GtkHBox" id="hbox19">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkImage" id="image8">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="icon_name">drive-removable-media</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label27">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;span weight="bold" size="x-large"&gt;Backup&lt;/span&gt; </property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator7">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
@@ -2704,10 +2293,8 @@ When this option is enabled, and a potential device is detected, you will be pro
<child>
<object class="GtkVBox" id="vbox9">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<object class="GtkLabel" id="label43">
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="xpad">12</property>
<property name="label" translatable="yes">&lt;b&gt;Backup&lt;/b&gt;</property>
@@ -2715,36 +2302,30 @@ When this option is enabled, and a potential device is detected, you will be pro
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox20">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="label38">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkTable" id="backup_table">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="n_rows">10</property>
<property name="n_columns">4</property>
<child>
<object class="GtkLabel" id="backup_location_explanation_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="xalign">0</property>
<property name="ypad">12</property>
@@ -2766,9 +2347,8 @@ When this option is enabled, and a potential device is detected, you will be pro
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
- <signal name="toggled" handler="on_auto_detect_backup_checkbutton_toggled" swapped="no"/>
+ <signal name="toggled" handler="on_auto_detect_backup_checkbutton_toggled"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -2781,7 +2361,6 @@ When this option is enabled, and a potential device is detected, you will be pro
<child>
<object class="GtkLabel" id="label11">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="ypad">12</property>
<property name="label" translatable="yes">You can have your photos and videos backed up to multiple locations as they are downloaded, e.g. external hard drives.</property>
@@ -2798,10 +2377,9 @@ When this option is enabled, and a potential device is detected, you will be pro
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
- <signal name="toggled" handler="on_backup_checkbutton_toggled" swapped="no"/>
+ <signal name="toggled" handler="on_backup_checkbutton_toggled"/>
</object>
<packing>
<property name="right_attach">4</property>
@@ -2813,7 +2391,6 @@ When this option is enabled, and a potential device is detected, you will be pro
<child>
<object class="GtkLabel" id="backup_identifier_explanation_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="ypad">6</property>
<property name="label" translatable="yes">Specify the folder in which backups are stored on the device.
@@ -2832,7 +2409,6 @@ When this option is enabled, and a potential device is detected, you will be pro
<child>
<object class="GtkLabel" id="backup_location_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Photo backup location:</property>
</object>
@@ -2848,7 +2424,6 @@ When this option is enabled, and a potential device is detected, you will be pro
<child>
<object class="GtkLabel" id="backup_identifier_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Photo backup folder name:</property>
</object>
@@ -2865,7 +2440,6 @@ When this option is enabled, and a potential device is detected, you will be pro
<child>
<object class="GtkLabel" id="backup_example_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
<property name="ypad">6</property>
@@ -2883,7 +2457,6 @@ When this option is enabled, and a potential device is detected, you will be pro
<child>
<object class="GtkLabel" id="example_backup_path_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
<property name="ypad">6</property>
@@ -2902,12 +2475,8 @@ When this option is enabled, and a potential device is detected, you will be pro
<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="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
- <property name="primary_icon_sensitive">True</property>
- <property name="secondary_icon_sensitive">True</property>
- <signal name="changed" handler="on_backup_identifier_entry_changed" swapped="no"/>
+ <property name="invisible_char">&#x2022;</property>
+ <signal name="changed" handler="on_backup_identifier_entry_changed"/>
</object>
<packing>
<property name="left_attach">3</property>
@@ -2921,7 +2490,6 @@ When this option is enabled, and a potential device is detected, you will be pro
<child>
<object class="GtkLabel" id="video_backup_identifier_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Video backup folder name:</property>
</object>
@@ -2940,12 +2508,8 @@ When this option is enabled, and a potential device is detected, you will be pro
<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="primary_icon_activatable">False</property>
- <property name="secondary_icon_activatable">False</property>
- <property name="primary_icon_sensitive">True</property>
- <property name="secondary_icon_sensitive">True</property>
- <signal name="changed" handler="on_video_backup_identifier_entry_changed" swapped="no"/>
+ <property name="invisible_char">&#x2022;</property>
+ <signal name="changed" handler="on_video_backup_identifier_entry_changed"/>
</object>
<packing>
<property name="left_attach">3</property>
@@ -2959,7 +2523,6 @@ When this option is enabled, and a potential device is detected, you will be pro
<child>
<object class="GtkLabel" id="backup_video_location_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Video backup location:</property>
</object>
@@ -3016,33 +2579,26 @@ When this option is enabled, and a potential device is detected, you will be pro
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label39">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="padding">12</property>
<property name="position">1</property>
</packing>
@@ -3055,7 +2611,6 @@ When this option is enabled, and a potential device is detected, you will be pro
<child type="tab">
<object class="GtkLabel" id="backup_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">Backup</property>
</object>
<packing>
@@ -3066,56 +2621,45 @@ When this option is enabled, and a potential device is detected, you will be pro
<child>
<object class="GtkVBox" id="automation_tab">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<object class="GtkVBox" id="vbox11">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<object class="GtkHBox" id="hbox21">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkImage" id="image4">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="stock">gtk-execute</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label31">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;span weight="bold" size="x-large"&gt;Miscellaneous&lt;/span&gt;</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator9">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
@@ -3129,11 +2673,9 @@ When this option is enabled, and a potential device is detected, you will be pro
<child>
<object class="GtkVBox" id="vbox13">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<object class="GtkLabel" id="label45">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="xpad">12</property>
<property name="label" translatable="yes">&lt;b&gt;Program Automation&lt;/b&gt;</property>
@@ -3141,30 +2683,24 @@ When this option is enabled, and a potential device is detected, you will be pro
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox24">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
- <object class="GtkLabel" id="label34">
- <property name="can_focus">False</property>
- </object>
+ <object class="GtkLabel" id="label34"/>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkTable" id="automation_table">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="n_rows">7</property>
+ <property name="n_rows">6</property>
<property name="n_columns">3</property>
<child>
<object class="GtkCheckButton" id="auto_unmount_checkbutton">
@@ -3173,9 +2709,9 @@ When this option is enabled, and a potential device is detected, you will be pro
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="use_action_appearance">False</property>
+ <property name="xalign">0</property>
<property name="draw_indicator">True</property>
- <signal name="toggled" handler="on_auto_unmount_checkbutton_toggled" swapped="no"/>
+ <signal name="toggled" handler="on_auto_unmount_checkbutton_toggled"/>
</object>
<packing>
<property name="right_attach">3</property>
@@ -3190,9 +2726,9 @@ When this option is enabled, and a potential device is detected, you will be pro
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="use_action_appearance">False</property>
+ <property name="xalign">0</property>
<property name="draw_indicator">True</property>
- <signal name="toggled" handler="on_auto_startup_checkbutton_toggled" swapped="no"/>
+ <signal name="toggled" handler="on_auto_startup_checkbutton_toggled"/>
</object>
<packing>
<property name="right_attach">3</property>
@@ -3205,9 +2741,9 @@ When this option is enabled, and a potential device is detected, you will be pro
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="use_action_appearance">False</property>
+ <property name="xalign">0</property>
<property name="draw_indicator">True</property>
- <signal name="toggled" handler="on_auto_insertion_checkbutton_toggled" swapped="no"/>
+ <signal name="toggled" handler="on_auto_insertion_checkbutton_toggled"/>
</object>
<packing>
<property name="right_attach">3</property>
@@ -3221,9 +2757,9 @@ When this option is enabled, and a potential device is detected, you will be pro
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
+ <property name="xalign">0</property>
<property name="draw_indicator">True</property>
- <signal name="toggled" handler="on_auto_exit_checkbutton_toggled" swapped="no"/>
+ <signal name="toggled" handler="on_auto_exit_checkbutton_toggled"/>
</object>
<packing>
<property name="right_attach">3</property>
@@ -3232,36 +2768,36 @@ When this option is enabled, and a potential device is detected, you will be pro
</packing>
</child>
<child>
- <object class="GtkCheckButton" id="auto_delete_checkbutton">
- <property name="label" translatable="yes">Delete photos and videos from device upon download completion</property>
+ <object class="GtkCheckButton" id="auto_exit_force_checkbutton">
+ <property name="label" translatable="yes">Exit program even if download had warnings or errors</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
+ <property name="xalign">0</property>
<property name="draw_indicator">True</property>
- <signal name="toggled" handler="on_auto_delete_checkbutton_toggled" swapped="no"/>
+ <signal name="toggled" handler="on_auto_exit_force_checkbutton_toggled"/>
</object>
<packing>
- <property name="right_attach">3</property>
- <property name="top_attach">5</property>
- <property name="bottom_attach">6</property>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
</packing>
</child>
<child>
- <object class="GtkCheckButton" id="auto_exit_force_checkbutton">
- <property name="label" translatable="yes">Exit program even if download had warnings or errors</property>
+ <object class="GtkCheckButton" id="auto_rotate_checkbutton">
+ <property name="label" translatable="yes">Automatically rotate JPEG images</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
+ <property name="xalign">0</property>
<property name="draw_indicator">True</property>
- <signal name="toggled" handler="on_auto_exit_force_checkbutton_toggled" swapped="no"/>
+ <signal name="toggled" handler="on_auto_rotate_checkbutton_toggled"/>
</object>
<packing>
- <property name="left_attach">1</property>
- <property name="right_attach">2</property>
- <property name="top_attach">4</property>
- <property name="bottom_attach">5</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
</packing>
</child>
<child>
@@ -3270,19 +2806,8 @@ When this option is enabled, and a potential device is detected, you will be pro
<child>
<placeholder/>
</child>
- <child>
- <placeholder/>
- </child>
- <child>
- <placeholder/>
- </child>
- <child>
- <placeholder/>
- </child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="padding">24</property>
<property name="position">1</property>
</packing>
@@ -3290,18 +2815,15 @@ When this option is enabled, and a potential device is detected, you will be pro
<child>
<object class="GtkLabel" id="label35">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="padding">12</property>
<property name="position">1</property>
</packing>
@@ -3309,7 +2831,6 @@ When this option is enabled, and a potential device is detected, you will be pro
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="xpad">12</property>
<property name="label" translatable="yes">Performance</property>
@@ -3319,29 +2840,23 @@ When this option is enabled, and a potential device is detected, you will be pro
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox25">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
- <object class="GtkLabel" id="label19">
- <property name="can_focus">False</property>
- </object>
+ <object class="GtkLabel" id="label19"/>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkTable" id="table1">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="n_columns">3</property>
<child>
<object class="GtkCheckButton" id="generate_thumbnails_checkbutton">
@@ -3349,9 +2864,8 @@ When this option is enabled, and a potential device is detected, you will be pro
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
- <signal name="toggled" handler="on_generate_thumbnails_checkbutton_toggled" swapped="no"/>
+ <signal name="toggled" handler="on_generate_thumbnails_checkbutton_toggled"/>
</object>
</child>
<child>
@@ -3362,8 +2876,6 @@ When this option is enabled, and a potential device is detected, you will be pro
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="padding">24</property>
<property name="position">1</property>
</packing>
@@ -3371,26 +2883,21 @@ When this option is enabled, and a potential device is detected, you will be pro
<child>
<object class="GtkLabel" id="label29">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="padding">12</property>
<property name="position">3</property>
</packing>
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="padding">12</property>
<property name="position">1</property>
</packing>
@@ -3403,7 +2910,6 @@ When this option is enabled, and a potential device is detected, you will be pro
<child type="tab">
<object class="GtkLabel" id="miscillaneous_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Miscellaneous</property>
</object>
@@ -3415,57 +2921,46 @@ When this option is enabled, and a potential device is detected, you will be pro
<child>
<object class="GtkVBox" id="error_tab">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkVBox" id="vbox16">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<child>
<object class="GtkHBox" id="hbox26">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkImage" id="image9">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="stock">gtk-dialog-error</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label40">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;span weight="bold" size="x-large"&gt;Error Handling&lt;/span&gt;</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator10">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
@@ -3479,29 +2974,24 @@ When this option is enabled, and a potential device is detected, you will be pro
<child>
<object class="GtkHBox" id="hbox27">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="label36">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkTable" id="error_table">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="n_rows">14</property>
<property name="n_columns">2</property>
<child>
<object class="GtkLabel" id="label13">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes"> </property>
</object>
@@ -3515,7 +3005,6 @@ When this option is enabled, and a potential device is detected, you will be pro
<child>
<object class="GtkLabel" id="label37">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">&lt;b&gt;Photo and Video Name Conflicts&lt;/b&gt;</property>
<property name="use_markup">True</property>
@@ -3531,11 +3020,10 @@ When this option is enabled, and a potential device is detected, you will be pro
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
- <signal name="toggled" handler="on_add_identifier_radiobutton_toggled" swapped="no"/>
+ <signal name="toggled" handler="on_add_identifier_radiobutton_toggled"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -3551,7 +3039,6 @@ When this option is enabled, and a potential device is detected, you will be pro
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<property name="group">add_identifier_radiobutton</property>
@@ -3567,7 +3054,6 @@ When this option is enabled, and a potential device is detected, you will be pro
<child>
<object class="GtkLabel" id="label58">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="ypad">12</property>
<property name="label" translatable="yes">When a photo or video of the same name has already been downloaded, choose whether to skip downloading the file, or to add a unique indentifier.</property>
@@ -3584,7 +3070,6 @@ When this option is enabled, and a potential device is detected, you will be pro
<child>
<object class="GtkLabel" id="label64">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="ypad">12</property>
<property name="label" translatable="yes">When backing up, choose whether to overwrite a file on the backup device that has the same name, or skip backing it up.</property>
@@ -3604,10 +3089,9 @@ When this option is enabled, and a potential device is detected, you will be pro
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<property name="draw_indicator">True</property>
<property name="group">backup_duplicate_skip_radiobutton</property>
- <signal name="toggled" handler="on_backup_duplicate_overwrite_radiobutton_toggled" swapped="no"/>
+ <signal name="toggled" handler="on_backup_duplicate_overwrite_radiobutton_toggled"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -3622,10 +3106,9 @@ When this option is enabled, and a potential device is detected, you will be pro
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="use_action_appearance">False</property>
<property name="active">True</property>
<property name="draw_indicator">True</property>
- <signal name="toggled" handler="on_backup_duplicate_skip_radiobutton_toggled" swapped="no"/>
+ <signal name="toggled" handler="on_backup_duplicate_skip_radiobutton_toggled"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -3693,26 +3176,21 @@ When this option is enabled, and a potential device is detected, you will be pro
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label65">
<property name="visible">True</property>
- <property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
- <property name="fill">True</property>
<property name="padding">12</property>
<property name="position">1</property>
</packing>
@@ -3725,7 +3203,6 @@ When this option is enabled, and a potential device is detected, you will be pro
<child type="tab">
<object class="GtkLabel" id="error_label">
<property name="visible">True</property>
- <property name="can_focus">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Error Handling</property>
</object>
@@ -3736,16 +3213,12 @@ When this option is enabled, and a potential device is detected, you will be pro
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="padding">6</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
<property name="padding">5</property>
<property name="position">1</property>
</packing>
@@ -3753,6 +3226,46 @@ When this option is enabled, and a potential device is detected, you will be pro
<child>
<placeholder/>
</child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area2">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="help_button1">
+ <property name="label">gtk-help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="close_button">
+ <property name="label">gtk-close</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
</object>
</child>
<action-widgets>
diff --git a/rapid/glade3/rapid.ui b/rapid/glade3/rapid.ui
index 1e49e4e..3ec67f4 100644
--- a/rapid/glade3/rapid.ui
+++ b/rapid/glade3/rapid.ui
@@ -1,73 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="2.20"/>
- <!-- interface-naming-policy project-wide -->
<object class="GtkAction" id="about_action">
- <property name="label" translatable="yes">About...</property>
+ <property name="label">About...</property>
<property name="stock_id">gtk-about</property>
<signal name="activate" handler="on_about_action_activate" swapped="no"/>
</object>
<object class="GtkAction" id="check_all_action">
- <property name="label" translatable="yes">Check All</property>
+ <property name="label">Check All</property>
<signal name="activate" handler="on_check_all_action_activate" swapped="no"/>
</object>
<object class="GtkAction" id="check_all_photos_action">
- <property name="label" translatable="yes">Check All Photos</property>
+ <property name="label">Check All Photos</property>
<signal name="activate" handler="on_check_all_photos_action_activate" swapped="no"/>
</object>
<object class="GtkAction" id="check_all_videos_action">
- <property name="label" translatable="yes">Check All Videos</property>
+ <property name="label">Check All Videos</property>
<signal name="activate" handler="on_check_all_videos_action_activate" swapped="no"/>
</object>
<object class="GtkAction" id="donate_action">
- <property name="label" translatable="yes">Make a Donation...</property>
+ <property name="label">Make a Donation...</property>
<signal name="activate" handler="on_donate_action_activate" swapped="no"/>
</object>
<object class="GtkAction" id="download_action">
- <property name="label" translatable="yes">Download</property>
+ <property name="label">Download</property>
<property name="icon_name">system-run</property>
<property name="sensitive">False</property>
<signal name="activate" handler="on_download_action_activate" swapped="no"/>
</object>
<object class="GtkAction" id="get_help_action">
- <property name="label" translatable="yes">Get Help Online...</property>
+ <property name="label">Get Help Online...</property>
<property name="stock_id">gtk-help</property>
<signal name="activate" handler="on_get_help_action_activate" swapped="no"/>
</object>
<object class="GtkAction" id="help_action">
- <property name="label" translatable="yes">Help</property>
+ <property name="label">Help</property>
<signal name="activate" handler="on_help_action_activate" swapped="no"/>
</object>
<object class="GtkAction" id="next_image_action">
- <property name="label" translatable="yes">Next File</property>
<property name="tooltip">Next file</property>
- <property name="stock_id">gtk-go-forward</property>
- <property name="always_show_image">True</property>
<signal name="activate" handler="on_next_image_action_activate" swapped="no"/>
</object>
<object class="GtkAction" id="preferences_action">
- <property name="label" translatable="yes">Preferences</property>
+ <property name="label">Preferences</property>
<signal name="activate" handler="on_preferences_action_activate" swapped="no"/>
</object>
<object class="GtkAction" id="prev_image_action">
- <property name="label" translatable="yes">Previous File</property>
<property name="tooltip">Previous file</property>
- <property name="stock_id">gtk-go-back</property>
- <property name="always_show_image">True</property>
<signal name="activate" handler="on_prev_image_action_activate" swapped="no"/>
</object>
<object class="GtkAction" id="quit_action">
- <property name="label" translatable="yes">Quit</property>
+ <property name="label">Quit</property>
<property name="stock_id">gtk-quit</property>
<signal name="activate" handler="on_quit_action_activate" swapped="no"/>
</object>
<object class="GtkAction" id="refresh_action">
- <property name="label" translatable="yes">Refresh</property>
+ <property name="label">Refresh</property>
<property name="stock_id">gtk-refresh</property>
<signal name="activate" handler="on_refresh_action_activate" swapped="no"/>
</object>
<object class="GtkAction" id="report_problem_action">
- <property name="label" translatable="yes">Report a Problem...</property>
+ <property name="label">Report a Problem...</property>
<property name="stock_id">gtk-dialog-warning</property>
<signal name="activate" handler="on_report_problem_action_activate" swapped="no"/>
</object>
@@ -80,11 +73,11 @@
<signal name="activate" handler="on_show_thumbnails_action_activate" swapped="no"/>
</object>
<object class="GtkAction" id="translate_action">
- <property name="label" translatable="yes">Translate this Application...</property>
+ <property name="label">Translate this Application...</property>
<signal name="activate" handler="on_translate_action_activate" swapped="no"/>
</object>
<object class="GtkAction" id="uncheck_all_action">
- <property name="label" translatable="yes">Uncheck All</property>
+ <property name="label">Uncheck All</property>
<signal name="activate" handler="on_uncheck_all_action_activate" swapped="no"/>
</object>
<object class="GtkWindow" id="rapidapp">
@@ -102,7 +95,6 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
- <property name="ubuntu_local">True</property>
<child>
<object class="GtkMenuItem" id="menuitem7">
<property name="visible">True</property>
@@ -113,7 +105,6 @@
<child type="submenu">
<object class="GtkMenu" id="menuitem7_menu">
<property name="can_focus">False</property>
- <property name="ubuntu_local">True</property>
<child>
<object class="GtkImageMenuItem" id="menu_download_pause">
<property name="label" translatable="yes">Download / Pause</property>
@@ -151,7 +142,6 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="related_action">quit_action</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
<accelerator key="q" signal="activate" modifiers="GDK_CONTROL_MASK"/>
@@ -171,7 +161,6 @@
<child type="submenu">
<object class="GtkMenu" id="select_menu">
<property name="can_focus">False</property>
- <property name="ubuntu_local">True</property>
<child>
<object class="GtkMenuItem" id="menu_check_all">
<property name="visible">True</property>
@@ -244,7 +233,6 @@
<child type="submenu">
<object class="GtkMenu" id="menuitem10_menu">
<property name="can_focus">False</property>
- <property name="ubuntu_local">True</property>
<child>
<object class="GtkImageMenuItem" id="menu_zoom_in">
<property name="label">gtk-zoom-in</property>
@@ -253,8 +241,8 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
- <accelerator key="equal" signal="activate" modifiers="GDK_CONTROL_MASK"/>
<accelerator key="plus" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ <accelerator key="equal" signal="activate" modifiers="GDK_CONTROL_MASK"/>
</object>
</child>
<child>
@@ -304,6 +292,8 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="related_action">prev_image_action</property>
+ <property name="use_action_appearance">False</property>
+ <property name="label" translatable="yes">Previous File</property>
<accelerator key="bracketleft" signal="activate"/>
</object>
</child>
@@ -312,6 +302,8 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="related_action">next_image_action</property>
+ <property name="use_action_appearance">False</property>
+ <property name="label" translatable="yes">Next File</property>
<accelerator key="bracketright" signal="activate"/>
</object>
</child>
@@ -329,7 +321,6 @@
<child type="submenu">
<object class="GtkMenu" id="help_menu">
<property name="can_focus">False</property>
- <property name="ubuntu_local">True</property>
<child>
<object class="GtkImageMenuItem" id="menu_get_help_online">
<property name="visible">True</property>
@@ -352,7 +343,6 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="related_action">donate_action</property>
- <property name="use_action_appearance">False</property>
<property name="label" translatable="yes">_Make a Donation...</property>
<property name="use_underline">True</property>
</object>
@@ -362,7 +352,6 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="related_action">translate_action</property>
- <property name="use_action_appearance">False</property>
<property name="label" translatable="yes">_Translate this Application...</property>
<property name="use_underline">True</property>
</object>
@@ -379,7 +368,6 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="related_action">about_action</property>
- <property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
@@ -396,27 +384,102 @@
</packing>
</child>
<child>
+ <object class="GtkHBox" id="toobar_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="homogeneous">True</property>
+ <child>
+ <object class="GtkToolbar" id="from_toolbar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="show_arrow">False</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolbar" id="copy_toolbar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="show_arrow">False</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolbar" id="dest_toolbar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="show_arrow">False</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolbar" id="menu_toolbar">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
<object class="GtkVPaned" id="main_vpaned">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="position">1</property>
<property name="position_set">True</property>
<child>
- <object class="GtkScrolledWindow" id="device_collection_scrolledwindow">
+ <object class="GtkHBox" id="devices_box">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="hscrollbar_policy">never</property>
- <property name="vscrollbar_policy">automatic</property>
+ <property name="can_focus">False</property>
<child>
- <object class="GtkViewport" id="device_collection_viewport">
+ <object class="GtkScrolledWindow" id="device_collection_scrolledwindow">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="resize_mode">queue</property>
- <property name="shadow_type">none</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="shadow_type">etched-out</property>
<child>
- <placeholder/>
+ <object class="GtkViewport" id="device_collection_viewport">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="resize_mode">queue</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
</child>
</object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
</child>
</object>
<packing>
@@ -442,8 +505,6 @@
<object class="GtkScrolledWindow" id="thumbnails_scrolledwindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="hscrollbar_policy">automatic</property>
- <property name="vscrollbar_policy">automatic</property>
<child>
<placeholder/>
</child>
@@ -581,7 +642,6 @@
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="related_action">next_image_action</property>
- <property name="use_action_appearance">False</property>
<property name="relief">none</property>
<property name="focus_on_click">False</property>
<accelerator key="bracketright" signal="activate"/>
@@ -602,7 +662,6 @@
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="related_action">prev_image_action</property>
- <property name="use_action_appearance">False</property>
<property name="relief">none</property>
<property name="focus_on_click">False</property>
<accelerator key="bracketleft" signal="activate"/>
@@ -740,7 +799,7 @@
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
- <property name="position">1</property>
+ <property name="position">2</property>
</packing>
</child>
<child>
@@ -751,7 +810,6 @@
<object class="GtkHButtonBox" id="main_buttonbox">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="layout_style">edge</property>
<child>
<object class="GtkButton" id="help_button">
<property name="label">_Help</property>
@@ -797,7 +855,7 @@
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">6</property>
- <property name="position">2</property>
+ <property name="position">3</property>
</packing>
</child>
<child>
@@ -832,7 +890,6 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">2</property>
- <property name="has_resize_grip">False</property>
</object>
<packing>
<property name="expand">True</property>
@@ -984,7 +1041,7 @@
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack_type">end</property>
- <property name="position">3</property>
+ <property name="position">4</property>
</packing>
</child>
</object>
diff --git a/rapid/metadataexiftool.py b/rapid/metadataexiftool.py
new file mode 100755
index 0000000..d6b61fc
--- /dev/null
+++ b/rapid/metadataexiftool.py
@@ -0,0 +1,225 @@
+#!/usr/bin/python
+# -*- coding: latin1 -*-
+
+### Copyright (C) 2011 Damon Lynch <damonlynch@gmail.com>
+
+### This program is free software; you can redistribute it and/or modify
+### it under the terms of the GNU General Public License as published by
+### the Free Software Foundation; either version 2 of the License, or
+### (at your option) any later version.
+
+### This program is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+### GNU General Public License for more details.
+
+### You should have received a copy of the GNU General Public License
+### along with this program; if not, write to the Free Software
+### Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import subprocess
+import json
+import datetime, time
+import string
+
+import multiprocessing
+import logging
+logger = multiprocessing.get_logger()
+
+def version_info():
+ """returns the version of exiftool being used"""
+ try:
+ # unfortunately subprocess.check_output does not exist on python 2.6
+ proc = subprocess.Popen(['exiftool', '-ver'], stdout=subprocess.PIPE)
+ v = proc.communicate()[0].strip()
+ except OSError:
+ v = None
+ return v
+
+EXIFTOOL_VERSION = version_info()
+
+class ExifToolMetaData:
+ """
+ Class to use when a python based metadata parser fails to correctly load
+ necessary metadata. Calls exiftool as a subprocess. It is therefore slow,
+ but in contrast to exiv2 or kaa metadata, exiftool somtimes gives better
+ output.
+ """
+ def __init__(self, filename):
+ self.filename = filename
+ self.metadata = None
+ self.metadata_string_format = None
+ self.exiftool_error = "Error encountered using exiftool with file %s"
+ self.exiftool_output = "Unexpected output from exiftool with file %s"
+
+ def _get(self, key, missing):
+
+ if key == "VideoStreamType" or "FileNumber":
+ # special case: want exiftool's string formatting
+ if self.metadata_string_format is None:
+ try:
+ proc = subprocess.Popen(['exiftool', '-j', self.filename], stdout=subprocess.PIPE)
+ s = proc.communicate()[0]
+ except:
+ logger.error(self.exiftool_error, self.filename)
+ return missing
+ try:
+ self.metadata_string_format = json.loads(s)
+ except:
+ logger.error(self.exiftool_output, self.filename)
+ return missing
+
+ try:
+ v = self.metadata_string_format[0][key]
+ except:
+ return missing
+ return v
+
+ elif self.metadata is None:
+ # note: exiftool's string formatting is OFF (-n switch)
+ try:
+ proc = subprocess.Popen(['exiftool', '-j', '-n', self.filename], stdout=subprocess.PIPE)
+ s = proc.communicate()[0]
+ except:
+ logger.error(self.exiftool_error, self.filename)
+ return missing
+ try:
+ self.metadata = json.loads(s)
+ except:
+ logger.error(self.exiftool_output, self.filename)
+ return missing
+
+ try:
+ v = self.metadata[0][key]
+ except:
+ return missing
+ return v
+
+
+ def date_time(self, missing=''):
+ """
+ Returns in python datetime format the date and time the image was
+ recorded.
+
+ Trys to get value from key "DateTimeOriginal"
+ If that fails, tries "CreateDate"
+
+ Returns missing either metadata value is not present.
+ """
+ d = self._get('DateTimeOriginal', None)
+ if d is None:
+ d = self._get('CreateDate', None)
+ if d is None:
+ d = self._get('FileModifyDate', None)
+ if d is not None:
+ try:
+ # returned value may or may not have a time offset
+ # strip it if need be
+ dt = d[:19]
+ dt = datetime.datetime.strptime(dt, "%Y:%m:%d %H:%M:%S")
+ except:
+ logger.error("Error reading date metadata with file %s", self.filename)
+ return missing
+
+ return dt
+ else:
+ return missing
+
+ def time_stamp(self, missing=''):
+ """
+ Returns a float value representing the time stamp, if it exists
+ """
+ dt = self.date_time(missing=None)
+ if dt:
+ # convert it to a time stamp (not optimal, but better than nothing!)
+ v = time.mktime(dt.timetuple())
+ else:
+ v = missing
+ return v
+
+ def file_number(self, missing=''):
+ v = self._get("FileNumber", None)
+ if v is not None:
+ return str(v)
+ else:
+ return missing
+
+ def width(self, missing=''):
+ v = self._get('ImageWidth', None)
+ if v is not None:
+ return str(v)
+ else:
+ return missing
+
+ def height(self, missing=''):
+ v = self._get('ImageHeight', None)
+ if v is not None:
+ return str(v)
+ else:
+ return missing
+
+ def length(self, missing=''):
+ """
+ return the duration (length) of the video, rounded to the nearest second, in string format
+ """
+ v = self._get("Duration", None)
+ if v is not None:
+ try:
+ v = float(v)
+ v = "%0.f" % v
+ except:
+ return missing
+ return v
+ else:
+ return missing
+
+ def frames_per_second(self, stream=0, missing=''):
+ """
+ value stream is ignored (kept for compatibilty with code calling kaa)
+ """
+ v = self._get("FrameRate", None)
+ if v is None:
+ v = self._get("VideoFrameRate", None)
+
+ if v is None:
+ return missing
+ try:
+ v = '%.0f' % v
+ except:
+ return missing
+ return v
+
+ def codec(self, stream=0, missing=''):
+ """
+ value stream is ignored (kept for compatibilty with code calling kaa)
+ """
+ v = self._get("VideoStreamType", None)
+ if v is None:
+ v = self._get("VideoCodec", None)
+ if v is not None:
+ return v
+ else:
+ return missing
+
+ def fourcc(self, stream=0, missing=''):
+ """
+ value stream is ignored (kept for compatibilty with code calling kaa)
+ """
+ return self._get("CompressorID", missing)
+
+if __name__ == '__main__':
+ import sys
+
+
+ if (len(sys.argv) != 2):
+ print 'Usage: ' + sys.argv[0] + ' path/to/video/containing/metadata'
+ sys.exit(0)
+
+ else:
+ m = ExifToolMetaData(sys.argv[1])
+ dt = m.date_time()
+ print dt
+ print "%sx%s" % (m.width(), m.height())
+ print m.length()
+ print m.frames_per_second()
+ print m.codec()
diff --git a/rapid/metadataphoto.py b/rapid/metadataphoto.py
index 0442a86..1b36be9 100755..100644
--- a/rapid/metadataphoto.py
+++ b/rapid/metadataphoto.py
@@ -30,7 +30,7 @@ except ImportError:
sys.stderr.write("You need to install pyexiv2, the python binding for exiv2, to run this program.\n" )
sys.exit(1)
-
+import metadataexiftool
def __version_info(version):
if not version:
@@ -39,7 +39,7 @@ def __version_info(version):
v = ''
for i in version:
v += '.%s' % i
- return v[1:]
+ return v[1:]
def pyexiv2_version_info():
return __version_info(pyexiv2.version_info)
@@ -54,6 +54,16 @@ class MetaData(pyexiv2.metadata.ImageMetadata):
"""
+ def __init__(self, full_file_name):
+ pyexiv2.metadata.ImageMetadata.__init__(self, full_file_name)
+ self.rpd_metadata_exiftool = None
+ self.rpd_full_file_name = full_file_name
+
+ def _load_exiftool(self):
+ if self.rpd_metadata_exiftool is None:
+ self.rpd_metadata_exiftool = metadataexiftool.ExifToolMetaData(self.rpd_full_file_name)
+
+
def aperture(self, missing=''):
"""
Returns in string format the floating point value of the image's aperture.
@@ -211,6 +221,19 @@ class MetaData(pyexiv2.metadata.ImageMetadata):
except:
return missing
+ def file_number(self, missing=''):
+ """returns Exif.CanonFi.FileNumber, not to be confused with
+ Exif.Canon.FileNumber"""
+ try:
+ if 'Exif.CanonFi.FileNumber' in self.exif_keys:
+ self._load_exiftool()
+ return self.rpd_metadata_exiftool.file_number(missing)
+ else:
+ return missing
+ except:
+ return missing
+
+
def owner_name(self, missing=''):
""" returns camera name recorded by select Canon cameras"""
try:
diff --git a/rapid/metadatavideo.py b/rapid/metadatavideo.py
index 7b6bc6c..5519143 100644
--- a/rapid/metadatavideo.py
+++ b/rapid/metadatavideo.py
@@ -17,13 +17,13 @@
### along with this program; if not, write to the Free Software
### Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+HAVE_HACHOIR = True
DOWNLOAD_VIDEO = True
import os
import datetime
import time
import subprocess
-import tempfile
import multiprocessing
import logging
@@ -33,15 +33,30 @@ import gtk
import paths
import rpdfile
+import metadataexiftool
+
+from gettext import gettext as _
try:
from hachoir_core.cmd_line import unicodeFilename
from hachoir_parser import createParser
from hachoir_metadata import extractMetadata
except ImportError:
- DOWNLOAD_VIDEO = False
+ HAVE_HACHOIR = False
+
+if not HAVE_HACHOIR:
+ v = metadataexiftool.version_info()
+ if v is None:
+ DOWNLOAD_VIDEO = False
+
+def file_types_to_download():
+ """Returns a string with the types of file to download, to display to the user"""
+ if DOWNLOAD_VIDEO:
+ return _("photos and videos")
+ else:
+ return _("photos")
-if DOWNLOAD_VIDEO:
+if HAVE_HACHOIR:
def version_info():
from hachoir_metadata.version import VERSION
diff --git a/rapid/metadataxmp.py b/rapid/metadataxmp.py
new file mode 100755
index 0000000..47321e3
--- /dev/null
+++ b/rapid/metadataxmp.py
@@ -0,0 +1,198 @@
+#!/usr/bin/python
+# -*- coding: latin1 -*-
+
+### Copyright (C) 2011-12 Damon Lynch <damonlynch@gmail.com>
+
+### This program is free software; you can redistribute it and/or modify
+### it under the terms of the GNU General Public License as published by
+### the Free Software Foundation; either version 2 of the License, or
+### (at your option) any later version.
+
+### This program is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+### GNU General Public License for more details.
+
+### You should have received a copy of the GNU General Public License
+### along with this program; if not, write to the Free Software
+### Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import subprocess
+
+import multiprocessing, logging
+logger = multiprocessing.get_logger()
+
+class XmpMetadataSidecar:
+
+ def __init__(self, filename):
+ self.filename = filename
+ self.keys = []
+
+ def _add_pair(self, key_value_pair):
+ self.keys.append(key_value_pair)
+ logger.debug(key_value_pair)
+
+ def _generate_exiv2_command_line(self):
+ # -f option: overwrites any existing xmp file
+ return ['exiv2', '-f'] + self.keys + ['-exX', self.filename]
+
+ def _generate_exiv2_contact_info(self, key, value):
+ return "-M set Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:%s %s" % (key, value)
+
+ def _generate_exiv2_dc(self, key, value):
+ return "-M set Xmp.dc.%s %s" % (key, value)
+
+ def _generate_exiv2_photoshop(self, key, value):
+ return "-M set Xmp.photoshop.%s %s" % (key, value)
+
+ def _generate_exiv2_rights(self, key, value):
+ return "-M set Xmp.xmpRights.%s %s" % (key, value)
+
+ def _generate_exiv2_iptc(self, key, value):
+ return "-M set Xmp.iptc.%s %s" % (key, value)
+
+ def _generate_exiv2_exif(self, key, value):
+ return "-M set Xmp.exif.%s %s" % (key, value)
+
+ def set_location(self, location):
+ self._add_pair(self._generate_exiv2_iptc('Location', location))
+
+ def set_city(self, city):
+ self._add_pair(self._generate_exiv2_photoshop('City', city))
+
+ def set_state_province(self, state):
+ self._add_pair(self._generate_exiv2_photoshop('State', state))
+
+ def set_country(self, country):
+ self._add_pair(self._generate_exiv2_photoshop('Country', country))
+
+ def set_country_code(self, country_code):
+ self._add_pair(self._generate_exiv2_iptc('CountryCode', country_code))
+
+ def set_headline(self, headline):
+ self._add_pair(self._generate_exiv2_photoshop('Headline', headline))
+
+ def set_description_writer(self, writer):
+ """
+ Synonym: Caption writer
+ """
+ self._add_pair(self._generate_exiv2_photoshop('CaptionWriter', writer))
+
+ def set_description(self, description):
+ """A synonym for this in some older programs is 'Caption'"""
+ self._add_pair(self._generate_exiv2_dc('description', description))
+
+ def set_subject(self, subject):
+ """
+ You can call this more than once, to add multiple subjects
+
+ A synonym is 'Keywords'
+ """
+ self._add_pair(self._generate_exiv2_dc('subject', subject))
+
+ def set_creator(self, creator):
+ """
+ Sets the author (creator) field. Photo Mechanic calls this 'Photographer'.
+ """
+ self._add_pair(self._generate_exiv2_dc('creator', creator))
+
+ def set_creator_job_title(self, job_title):
+ self._add_pair(self._generate_exiv2_photoshop('AuthorsPosition', job_title))
+
+ def set_credit_line(self, credit_line):
+ self._add_pair(self._generate_exiv2_photoshop('Credit', credit_line))
+
+ def set_source(self, source):
+ """
+ original owner or copyright holder of the photograph
+ """
+ self._add_pair(self._generate_exiv2_photoshop('Source', source))
+
+ def set_copyright(self, copyright):
+ self._add_pair(self._generate_exiv2_dc('rights', copyright))
+
+ def set_copyright_url(self, copyright_url):
+ self._add_pair(self._generate_exiv2_rights('WebStatement', copyright_url))
+
+ def set_contact_city(self, city):
+ self._add_pair(self._generate_exiv2_contact_info('CiAdrCity', city))
+
+ def set_contact_country(self, country):
+ self._add_pair(self._generate_exiv2_contact_info('CiAdrCtry', country))
+
+ def set_contact_address(self, address):
+ """The contact information address part.
+ Comprises an optional company name and all required information
+ to locate the building or postbox to which mail should be sent."""
+ self._add_pair(self._generate_exiv2_contact_info('CiAdrExtadr', address))
+
+ def set_contact_postal_code(self, postal_code):
+ self._add_pair(self._generate_exiv2_contact_info('CiAdrPcode', postal_code))
+
+ def set_contact_region(self, region):
+ """State/Province"""
+ self._add_pair(self._generate_exiv2_contact_info('CiAdrRegion', region))
+
+ def set_contact_email(self, email):
+ """Multiple email addresses can be given, separated by a comma."""
+ self._add_pair(self._generate_exiv2_contact_info('CiEmailWork', email))
+
+ def set_contact_telephone(self, telephone):
+ """Multiple numbers can be given, separated by a comma."""
+ self._add_pair(self._generate_exiv2_contact_info('CiTelWork', telephone))
+
+ def set_contact_url(self, url):
+ """Multiple URLs can be given, separated by a comma."""
+ self._add_pair(self._generate_exiv2_contact_info('CiUrlWork', url))
+
+ def set_exif_value(self, key, value):
+ self._add_pair(self._generate_exiv2_exif(key, value))
+
+ def write_xmp_sidecar(self):
+ cmd = self._generate_exiv2_command_line()
+ if logger.getEffectiveLevel() == logging.DEBUG:
+ cmd_line = ''
+ for c in cmd:
+ cmd_line += c + ' '
+ cmd_line = cmd_line.strip()
+ logger.debug("XMP write command: %s", cmd_line)
+ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+ return proc.communicate()[0].strip()
+
+
+if __name__ == '__main__':
+ import sys
+
+
+ if (len(sys.argv) != 2):
+ print 'Usage: ' + sys.argv[0] + ' path/to/photo/containing/metadata'
+
+ else:
+ x = XmpMetadataSidecar(sys.argv[1])
+ x.set_description("This is image is just a sample and is nothing serious. I used to test out writing XMP files in Rapid Photo Downloader.")
+ x.set_description_writer("Damon Lynch wrote caption")
+ x.set_headline("Sample image to test XMP")
+ x.set_subject("Keyword 1")
+ x.set_subject("Keyword 2")
+ x.set_city("Minneapolis")
+ x.set_location("University of Minnesota")
+ x.set_state_province("Minnesota")
+ x.set_country("United States of America")
+ x.set_country_code("USA")
+ x.set_creator("Damon Lynch")
+ x.set_creator_job_title("Photographer")
+ x.set_credit_line("Contact Damon for permission")
+ x.set_source("Damon Lynch is the original photographer")
+ x.set_copyright("© 2011 Damon Lynch, all rights reserved.")
+ x.set_copyright_url("http://www.damonlynch.net/license")
+ x.set_contact_address("Contact house number, street, apartment")
+ x.set_contact_city('Contact City')
+ x.set_contact_region('Contact State')
+ x.set_contact_postal_code('Contact Post code')
+ x.set_contact_telephone('+1 111 111 1111')
+ x.set_contact_country('Contact Country')
+ x.set_contact_address('Address\nApartment')
+ x.set_contact_url('http://www.sample.net')
+ x.set_contact_email('name@email1.com, name@email2.com')
+
+ x.write_xmp_sidecar()
diff --git a/rapid/misc.py b/rapid/misc.py
index 1e023ad..e748dc3 100644
--- a/rapid/misc.py
+++ b/rapid/misc.py
@@ -35,7 +35,7 @@ def run_dialog( text, secondarytext=None, parent=None, messagetype=gtk.MESSAGE_
text
)
if parent:
- d.set_transient_for(parent.widget.get_toplevel())
+ d.set_transient_for(parent.get_toplevel())
for b,rid in extrabuttons:
d.add_button(b,rid)
d.vbox.set_spacing(12)
diff --git a/rapid/preferencesdialog.py b/rapid/preferencesdialog.py
index 1509630..bc9e0b7 100644
--- a/rapid/preferencesdialog.py
+++ b/rapid/preferencesdialog.py
@@ -36,6 +36,7 @@ import rpdfile
import higdefaults as hd
import metadataphoto
import metadatavideo
+
import tableplusminus as tpm
import utilities
@@ -385,7 +386,8 @@ class VideoSubfolderPrefs(PhotoSubfolderPrefs):
pref_list = pref_list)
class QuestionDialog(gtk.Dialog):
- def __init__(self, parent_window, title, question, post_choice_callback):
+ def __init__(self, parent_window, title, question, use_markup=False,
+ default_to_yes=True, post_choice_callback=None):
gtk.Dialog.__init__(self, title, None,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_NO, gtk.RESPONSE_CANCEL,
@@ -404,6 +406,7 @@ class QuestionDialog(gtk.Dialog):
prompt_hbox.pack_start(image, False, False, padding = 6)
prompt_label = gtk.Label(question)
+ prompt_label.set_use_markup(use_markup)
prompt_label.set_line_wrap(True)
prompt_hbox.pack_start(prompt_label, False, False, padding=6)
@@ -412,16 +415,18 @@ class QuestionDialog(gtk.Dialog):
self.set_border_width(6)
self.set_has_separator(False)
- self.set_default_response(gtk.RESPONSE_OK)
+ if default_to_yes:
+ self.set_default_response(gtk.RESPONSE_OK)
+ else:
+ self.set_default_response(gtk.RESPONSE_CANCEL)
-
self.set_transient_for(parent_window)
self.show_all()
-
- self.connect('response', self.on_response)
+ if post_choice_callback:
+ self.connect('response', self.on_response)
- def on_response(self, device_dialog, response):
+ def on_response(self, device_dialog, response):
user_selected = response == gtk.RESPONSE_OK
self.post_choice_callback(self, user_selected)
@@ -430,21 +435,21 @@ class RemoveAllJobCodeDialog(QuestionDialog):
QuestionDialog.__init__(self, parent_window,
_('Remove all Job Codes?'),
_('Should all Job Codes be removed?'),
- post_choice_callback)
+ post_choice_callback=post_choice_callback)
class RemoveAllRemeberedDevicesDialog(QuestionDialog):
def __init__(self, parent_window, post_choice_callback):
QuestionDialog.__init__(self, parent_window,
_('Remove all Remembered Paths?'),
_('Should all remembered paths be removed?'),
- post_choice_callback)
+ post_choice_callback=post_choice_callback)
class RemoveAllIgnoredPathsDialog(QuestionDialog):
def __init__(self, parent_window, post_choice_callback):
QuestionDialog.__init__(self, parent_window,
_('Remove all Ignored Paths?'),
_('Should all ignored paths be removed?'),
- post_choice_callback)
+ post_choice_callback=post_choice_callback)
class PhotoRenameTable(tpm.TablePlusMinus):
@@ -467,8 +472,7 @@ class PhotoRenameTable(tpm.TablePlusMinus):
self.connect("add", self.size_adjustment)
self.connect("remove", self.size_adjustment)
- # get scrollbar thickness from parent app scrollbar - very hackish, but what to do??
- self.bump = 16# self.preferencesdialog.parentApp.image_scrolledwindow.get_hscrollbar().allocation.height
+ self.bump = 16
self.have_vertical_scrollbar = False
@@ -887,10 +891,7 @@ class PreferencesDialog():
self._setup_control_spacing()
- if metadatavideo.DOWNLOAD_VIDEO:
- self.file_types = _("photos and videos")
- else:
- self.file_types = _("photos")
+ self.file_types = metadatavideo.file_types_to_download()
self._setup_sample_names()
@@ -1418,10 +1419,10 @@ class PreferencesDialog():
self.prefs.auto_exit)
self.auto_exit_force_checkbutton.set_active(
self.prefs.auto_exit_force)
- self.auto_delete_checkbutton.set_active(
- self.prefs.auto_delete)
self.generate_thumbnails_checkbutton.set_active(
self.prefs.generate_thumbnails)
+ self.auto_rotate_checkbutton.set_active(
+ self.prefs.auto_rotate_jpeg)
self.update_misc_controls()
@@ -1758,9 +1759,9 @@ class PreferencesDialog():
def on_auto_unmount_checkbutton_toggled(self, checkbutton):
self.prefs.auto_unmount = checkbutton.get_active()
-
- def on_auto_delete_checkbutton_toggled(self, checkbutton):
- self.prefs.auto_delete = checkbutton.get_active()
+
+ def on_auto_rotate_checkbutton_toggled(self, checkbutton):
+ self.prefs.auto_rotate_jpeg = checkbutton.get_active()
def on_auto_exit_checkbutton_toggled(self, checkbutton):
active = checkbutton.get_active()
@@ -1918,7 +1919,6 @@ class PreferencesDialog():
def on_backup_identifier_entry_changed(self, widget):
self.update_backup_example()
- #~ self.prefs.
def on_video_backup_identifier_entry_changed(self, widget):
self.update_backup_example()
diff --git a/rapid/prefsrapid.py b/rapid/prefsrapid.py
index 080e6f9..efd06b5 100644
--- a/rapid/prefsrapid.py
+++ b/rapid/prefsrapid.py
@@ -136,6 +136,7 @@ class RapidPreferences(prefs.Preferences):
"show_warning_downloading_from_camera": prefs.Value(prefs.BOOL, True),
#~ "preview_zoom": prefs.Value(prefs.INT, zoom),
"generate_thumbnails": prefs.Value(prefs.BOOL, True),
+ "auto_rotate_jpeg": prefs.Value(prefs.BOOL, True),
}
def __init__(self):
diff --git a/rapid/rapid.py b/rapid/rapid.py
index db5bdc6..9c423c2 100755
--- a/rapid/rapid.py
+++ b/rapid/rapid.py
@@ -60,9 +60,12 @@ import generatename as gn
import downloadtracker
-from metadatavideo import DOWNLOAD_VIDEO
+import filemodify
+
+from metadatavideo import DOWNLOAD_VIDEO, file_types_to_download
import metadataphoto
import metadatavideo
+import metadataexiftool
import scan as scan_process
import copyfiles
@@ -143,12 +146,14 @@ class DeviceCollection(gtk.TreeView):
# make it impossible to select a row
selection = self.get_selection()
selection.set_mode(gtk.SELECTION_NONE)
+ self.set_headers_visible(False)
# Device refers to a thing like a camera, memory card in its reader,
# external hard drive, Portable Storage Device, etc.
column0 = gtk.TreeViewColumn(_("Device"))
pixbuf_renderer = gtk.CellRendererPixbuf()
+ pixbuf_renderer.set_padding(2, 0)
text_renderer = gtk.CellRendererText()
text_renderer.props.ellipsize = pango.ELLIPSIZE_MIDDLE
text_renderer.set_fixed_size(160, -1)
@@ -216,7 +221,7 @@ class DeviceCollection(gtk.TreeView):
# (with one card at startup), it could be 21
# must account for header row at the top
row_height = self.get_background_area(0, self.get_column(0))[3] + 1
- height = (len(self.map_process_to_row) + 1) * row_height
+ height = max(((len(self.map_process_to_row) + 1) * row_height), 24)
self.parent_app.device_collection_scrolledwindow.set_size_request(-1, height)
def update_device(self, process_id, total_size_files):
@@ -683,9 +688,13 @@ class ThumbnailDisplay(gtk.IconView):
#check if preview should be from a downloaded file, or the source
if rpd_file.status in DOWNLOADED:
file_location = rpd_file.download_full_file_name
+ thm_file_name = rpd_file.download_thm_full_name
else:
file_location = rpd_file.full_file_name
+ thm_file_name = rpd_file.thm_full_name
+
self.preview_manager.get_preview(unique_id, file_location,
+ thm_file_name,
rpd_file.file_type, size_max=None,)
self.previews_being_fetched.add(unique_id)
@@ -1188,10 +1197,14 @@ class CopyFilesManager(TaskManager):
video_download_folder = task[1]
scan_pid = task[2]
files = task[3]
+ modify_files_during_download = task[4]
+ modify_pipe = task[5]
copy_files = copyfiles.CopyFiles(photo_download_folder,
video_download_folder,
files,
+ modify_files_during_download,
+ modify_pipe,
scan_pid, self.batch_size,
task_process_conn, terminate_queue, run_event)
copy_files.start()
@@ -1209,12 +1222,51 @@ class ThumbnailManager(TaskManager):
generator.start()
self._processes.append((generator, terminate_queue, run_event))
return generator.pid
+
+class FileModifyManager(TaskManager):
+ """Handles the modification of downloaded files before they are renamed
+ Duplex, multiprocess, similar to BackupFilesManager
+ """
+ def __init__(self, results_callback):
+ TaskManager.__init__(self, results_callback=results_callback,
+ batch_size=0)
+ self.file_modify_by_scan_pid = {}
+
+ def _initiate_task(self, task, task_results_conn, task_process_conn,
+ terminate_queue, run_event):
+ scan_pid = task[0]
+ auto_rotate_jpeg = task[1]
+ focal_length = task[2]
+
+ file_modify = filemodify.FileModify(auto_rotate_jpeg, focal_length,
+ task_process_conn, terminate_queue,
+ run_event)
+ file_modify.start()
+ self._processes.append((file_modify, terminate_queue, run_event,
+ task_results_conn))
+
+ self.file_modify_by_scan_pid[scan_pid] = (task_results_conn, file_modify.pid)
+
+ return file_modify.pid
+
+ def _setup_pipe(self):
+ return Pipe(duplex=True)
+
+ def _send_termination_msg(self, p):
+ p[1].put(None)
+ p[3].send((None, None))
+
+ def get_modify_pipe(self, scan_pid):
+ return self.file_modify_by_scan_pid[scan_pid][0]
+
class BackupFilesManager(TaskManager):
"""
- Handles backup processes. This is a little different from other Task
+ Handles backup processes. This is a little different from some other Task
Manager classes in that its pipe is Duplex, and the work done by it
is not pre-assigned when the process is started.
+
+ Duplex, multiprocess.
"""
def __init__(self, results_callback, batch_size):
TaskManager.__init__(self, results_callback, batch_size)
@@ -1300,8 +1352,8 @@ class PreviewManager(SingleInstanceTaskManager):
self._get_preview = tn.GetPreviewImage(self.task_process_conn)
self._get_preview.start()
- def get_preview(self, unique_id, full_file_name, file_type, size_max):
- self.task_results_conn.send((unique_id, full_file_name, file_type, size_max))
+ def get_preview(self, unique_id, full_file_name, thm_file_name, file_type, size_max):
+ self.task_results_conn.send((unique_id, full_file_name, thm_file_name, file_type, size_max))
def task_results(self, source, condition):
unique_id, preview_full_size, preview_small = self.task_results_conn.recv()
@@ -1312,10 +1364,10 @@ class SubfolderFileManager(SingleInstanceTaskManager):
"""
Manages the daemon process that renames files and creates subfolders
"""
- def __init__(self, results_callback, sequence_values, focal_length):
+ def __init__(self, results_callback, sequence_values):
SingleInstanceTaskManager.__init__(self, results_callback)
self._subfolder_file = subfolderfile.SubfolderFile(self.task_process_conn,
- sequence_values, focal_length)
+ sequence_values)
self._subfolder_file.start()
logger.debug("SubfolderFile PID: %s", self._subfolder_file.pid)
@@ -1440,7 +1492,6 @@ class PreviewImage:
def update_preview_image(self, unique_id, pil_image):
if unique_id == self.unique_id:
self.set_preview_image(unique_id, pil_image)
-
class RapidApp(dbus.service.Object):
@@ -1525,6 +1576,7 @@ class RapidApp(dbus.service.Object):
scan_termination_requested = self.scan_manager.request_termination()
thumbnails_termination_requested = self.thumbnails.thumbnail_manager.request_termination()
backup_termination_requested = self.backup_manager.request_termination()
+ file_modify_termination_requested = self.file_modify_manager.request_termination()
if terminate_file_copies:
copy_files_termination_requested = self.copy_files_manager.request_termination()
@@ -1532,17 +1584,19 @@ class RapidApp(dbus.service.Object):
copy_files_termination_requested = False
if (scan_termination_requested or thumbnails_termination_requested or
- backup_termination_requested):
+ backup_termination_requested or file_modify_termination_requested):
time.sleep(1)
if (self.scan_manager.get_no_active_processes() > 0 or
self.thumbnails.thumbnail_manager.get_no_active_processes() > 0 or
- self.backup_manager.get_no_active_processes() > 0):
+ self.backup_manager.get_no_active_processes() > 0 or
+ self.file_modify_manager.get_no_active_processes() > 0):
time.sleep(1)
# must try again, just in case a new scan has meanwhile started!
self.scan_manager.request_termination()
self.thumbnails.thumbnail_manager.terminate_forcefully()
self.scan_manager.terminate_forcefully()
self.backup_manager.terminate_forcefully()
+ self.file_modify_manager.terminate_forcefully()
if terminate_file_copies and copy_files_termination_requested:
time.sleep(1)
@@ -1591,6 +1645,7 @@ class RapidApp(dbus.service.Object):
self.on_rapidapp_destroy(widget=self.rapidapp, data=None)
def on_refresh_action_activate(self, action):
+ self.thumbnails.clear_all()
self.setup_devices(on_startup=False, on_preference_change=False,
block_auto_start=True)
@@ -1602,7 +1657,7 @@ class RapidApp(dbus.service.Object):
self.about.set_property("version", utilities.human_readable_version(
__version__))
self.about.run()
- self.about.destroy()
+ self.about.hide()
def on_report_problem_action_activate(self, action):
webbrowser.open("https://bugs.launchpad.net/rapid")
@@ -1675,6 +1730,34 @@ class RapidApp(dbus.service.Object):
self.prefs.ignored_paths,
self.prefs.use_re_ignored_paths])
+ def confirm_manual_location(self):
+ """
+ Queries the user to ask if they really want to download from locations
+ that could take a very long time to scan. They can choose yes or no.
+
+ Returns True if yes or there was no need to ask the user, False if the
+ user said no.
+ """
+ l = self.prefs.device_location
+ if l in ['/media', os.path.expanduser('~'), '/']:
+ logger.info("Prompting whether to download from %s", l)
+ if l == '/':
+ #this location is a human readable explanation for /, and is inserted into Downloading from %(location)s
+ l = _('the root of the file system')
+ c = preferencesdialog.QuestionDialog(parent_window=self.rapidapp,
+ title=_('Rapid Photo Downloader'),
+ #message in dialog box which asks the user if they really want to be downloading from this location
+ question="<b>" + _("Downloading from %(location)s.") % {'location': l} + "</b>\n\n" +
+ _("Do you really want to download from here? On some systems, scanning this location can take a very long time."),
+ default_to_yes=False,
+ use_markup=True)
+ response = c.run()
+ user_confirmed = response == gtk.RESPONSE_OK
+ c.destroy()
+ if not user_confirmed:
+ return False
+ return True
+
def setup_devices(self, on_startup, on_preference_change, block_auto_start):
"""
@@ -1697,10 +1780,12 @@ class RapidApp(dbus.service.Object):
if self.using_volume_monitor():
self.start_volume_monitor()
-
self.clear_non_running_downloads()
-
+ if not self.prefs.device_autodetection:
+ if not self.confirm_manual_location():
+ return
+
mounts = []
self.backup_devices = {}
@@ -2175,6 +2260,10 @@ class RapidApp(dbus.service.Object):
# Track which downloads are running
self.download_active_by_scan_pid = []
+
+ def modify_files_during_download(self):
+ """ Returns True if there is a need to modify files during download"""
+ return self.prefs.auto_rotate_jpeg or (self.focal_length is not None)
def start_download(self, scan_pid=None):
@@ -2286,11 +2375,20 @@ class RapidApp(dbus.service.Object):
if self.auto_start_is_on and self.prefs.generate_thumbnails:
for rpd_file in files:
rpd_file.generate_thumbnail = True
+
+ modify_files_during_download = self.modify_files_during_download()
+ if modify_files_during_download:
+ self.file_modify_manager.add_task((scan_pid, self.prefs.auto_rotate_jpeg, self.focal_length))
+ modify_pipe = self.file_modify_manager.get_modify_pipe(scan_pid)
+ else:
+ modify_pipe = None
+
# Initiate copy files process
self.copy_files_manager.add_task((photo_download_folder,
video_download_folder, scan_pid,
- files))
+ files, modify_files_during_download,
+ modify_pipe))
def copy_files_results(self, source, condition):
"""
@@ -2315,33 +2413,7 @@ class RapidApp(dbus.service.Object):
None, None)
self.time_remaining.update(scan_pid, bytes_downloaded=chunk_downloaded)
elif msg_type == rpdmp.MSG_FILE:
- download_succeeded, rpd_file, download_count, temp_full_file_name, thumbnail_icon, thumbnail = data
-
- if thumbnail is not None or thumbnail_icon is not None:
- self.thumbnails.update_thumbnail((rpd_file.unique_id,
- thumbnail_icon,
- thumbnail))
-
- self.download_tracker.set_download_count_for_file(
- rpd_file.unique_id, download_count)
- self.download_tracker.set_download_count(
- rpd_file.scan_pid, download_count)
- rpd_file.download_start_time = self.download_start_time
-
- if download_succeeded:
- # Insert preference values needed for name generation
- rpd_file = prefsrapid.insert_pref_lists(self.prefs, rpd_file)
- rpd_file.strip_characters = self.prefs.strip_characters
- rpd_file.download_folder = self.prefs.get_download_folder_for_file_type(rpd_file.file_type)
- rpd_file.download_conflict_resolution = self.prefs.download_conflict_resolution
- rpd_file.synchronize_raw_jpg = self.prefs.must_synchronize_raw_jpg()
- rpd_file.job_code = self.job_code
-
- self.subfolder_file_manager.rename_file_and_move_to_subfolder(
- download_succeeded,
- download_count,
- rpd_file
- )
+ self.copy_file_results_single_file(data)
return True
else:
@@ -2350,6 +2422,63 @@ class RapidApp(dbus.service.Object):
return False
+ def copy_file_results_single_file(self, data):
+ """
+ Handles results from one of two processes:
+ 1. copy_files
+ 2. file_modify
+
+ Operates after a single file has been copied from the download device
+ to the local folder.
+
+ Calls the process to rename files and create subfolders (subfolderfile)
+ """
+
+ download_succeeded, rpd_file, download_count, temp_full_file_name, thumbnail_icon, thumbnail = data
+
+ if thumbnail is not None or thumbnail_icon is not None:
+ self.thumbnails.update_thumbnail((rpd_file.unique_id,
+ thumbnail_icon,
+ thumbnail))
+
+ self.download_tracker.set_download_count_for_file(
+ rpd_file.unique_id, download_count)
+ self.download_tracker.set_download_count(
+ rpd_file.scan_pid, download_count)
+ rpd_file.download_start_time = self.download_start_time
+
+ if download_succeeded:
+ # Insert preference values needed for name generation
+ rpd_file = prefsrapid.insert_pref_lists(self.prefs, rpd_file)
+ rpd_file.strip_characters = self.prefs.strip_characters
+ rpd_file.download_folder = self.prefs.get_download_folder_for_file_type(rpd_file.file_type)
+ rpd_file.download_conflict_resolution = self.prefs.download_conflict_resolution
+ rpd_file.synchronize_raw_jpg = self.prefs.must_synchronize_raw_jpg()
+ rpd_file.job_code = self.job_code
+
+ self.subfolder_file_manager.rename_file_and_move_to_subfolder(
+ download_succeeded,
+ download_count,
+ rpd_file
+ )
+ def file_modify_results(self, source, condition):
+ """
+ 'file modify' is a process that runs immediately after 'copy files',
+ meaning there can be more than one at one time.
+
+ It runs before the renaming process.
+ """
+ connection = self.file_modify_manager.get_pipe(source)
+
+ conn_type, data = connection.recv()
+ if conn_type == rpdmp.CONN_PARTIAL:
+ self.copy_file_results_single_file(data)
+ return True
+ else:
+ # Process is complete, i.e. conn_type == rpdmp.CONN_COMPLETE
+ connection.close()
+ return False
+
def download_is_occurring(self):
"""Returns True if a file is currently being downloaded, renamed or
@@ -2840,8 +2969,10 @@ class RapidApp(dbus.service.Object):
'device_location', 'ignored_paths',
'use_re_ignored_paths', 'device_blacklist']:
self.rerun_setup_available_image_and_video_media = True
+ self._set_from_toolbar_state()
if not self.preferences_dialog_displayed:
self.post_preference_change()
+
elif key in ['backup_images', 'backup_device_autodetection',
'backup_location', 'backup_video_location',
@@ -2868,6 +2999,7 @@ class RapidApp(dbus.service.Object):
self._check_for_sequence_value_use()
elif key in ['download_folder', 'video_download_folder']:
+ self._set_to_toolbar_values()
self.display_free_space()
def post_preference_change(self):
@@ -2945,9 +3077,14 @@ class RapidApp(dbus.service.Object):
self.builder = builder
builder.add_from_file(paths.share_dir("glade3/rapid.ui"))
self.rapidapp = builder.get_object("rapidapp")
+ self.from_toolbar = builder.get_object("from_toolbar")
+ self.copy_toolbar = builder.get_object("copy_toolbar")
+ self.dest_toolbar = builder.get_object("dest_toolbar")
+ self.menu_toolbar = builder.get_object("menu_toolbar")
self.main_vpaned = builder.get_object("main_vpaned")
self.main_notebook = builder.get_object("main_notebook")
self.download_action = builder.get_object("download_action")
+ self.download_button = builder.get_object("download_button")
self.download_progressbar = builder.get_object("download_progressbar")
self.rapid_statusbar = builder.get_object("rapid_statusbar")
@@ -2962,7 +3099,9 @@ class RapidApp(dbus.service.Object):
# Only enable this action when actually displaying a preview
self.next_image_action.set_sensitive(False)
- self.prev_image_action.set_sensitive(False)
+ self.prev_image_action.set_sensitive(False)
+
+ self._init_toolbars()
# About dialog
builder.add_from_file(paths.share_dir("glade3/about.ui"))
@@ -3003,6 +3142,227 @@ class RapidApp(dbus.service.Object):
self.time_remaining = downloadtracker.TimeRemaining()
self.time_check = downloadtracker.TimeCheck()
+ def _init_toolbars(self):
+ """ Setup the 3 vertical toolbars on the main screen """
+ self._setup_from_toolbar()
+ self._setup_copy_move_toolbar()
+ self._setup_dest_toolbar()
+
+ # size label widths so they are equal, or else the left border of the file chooser will not match
+ self.photo_dest_label.realize()
+ self._make_widget_widths_equal(self.photo_dest_label, self.video_dest_label)
+ self.photo_dest_label.set_alignment(xalign=0.0, yalign=0.5)
+ self.video_dest_label.set_alignment(xalign=0.0, yalign=0.5)
+
+ # size copy / move buttons so they are equal in length, so arrows align
+ self._make_widget_widths_equal(self.copy_button, self.move_button)
+
+ def _setup_from_toolbar(self):
+ self.from_toolbar.set_style(gtk.TOOLBAR_TEXT)
+ self.from_toolbar.set_border_width(5)
+
+ from_label = gtk.Label()
+ from_label.set_markup("<i>" + _("From") + "</i>")
+ self.from_toolbar_label = gtk.ToolItem()
+ self.from_toolbar_label.add(from_label)
+ self.from_toolbar_label.set_is_important(True)
+ self.from_toolbar.insert(self.from_toolbar_label, 0)
+
+ self.auto_detect_button = gtk.ToggleToolButton()
+ self.auto_detect_button.set_is_important(True)
+ self.auto_detect_button.set_label(_("Auto Detect"))
+ self.from_toolbar.insert(self.auto_detect_button, 1)
+
+ self.from_filechooser_button = gtk.FileChooserButton(
+ _("Select a folder containing %(file_types)s") % {'file_types':file_types_to_download()})
+ self.from_filechooser_button.set_action(
+ gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
+
+ self.from_filechooser = gtk.ToolItem()
+ self.from_filechooser.set_is_important(True)
+ self.from_filechooser.add(self.from_filechooser_button)
+ self.from_filechooser.set_expand(True)
+ self.from_toolbar.insert(self.from_filechooser, 2)
+
+ self._set_from_toolbar_state()
+
+ #set events after having initialized the values
+ self.auto_detect_button.connect("toggled", self.on_auto_detect_button_toggled_event)
+ self.from_filechooser_button.connect("selection-changed",
+ self.on_from_filechooser_button_selection_changed)
+
+ self.from_toolbar.show_all()
+
+ def _setup_copy_move_toolbar(self):
+ self.copy_toolbar.set_style(gtk.TOOLBAR_TEXT)
+ self.copy_toolbar.set_border_width(5)
+
+ copy_move_label = gtk.Label(" ")
+ self.copy_move_toolbar_label = gtk.ToolItem()
+ self.copy_move_toolbar_label.add(copy_move_label)
+ self.copy_move_toolbar_label.set_is_important(True)
+ self.copy_toolbar.insert(self.copy_move_toolbar_label, 0)
+
+ self.copy_hbox = gtk.HBox()
+ self.move_hbox = gtk.HBox()
+ self.forward_image = gtk.image_new_from_stock(gtk.STOCK_GO_FORWARD, gtk.ICON_SIZE_SMALL_TOOLBAR)
+ self.forward_image2 = gtk.image_new_from_stock(gtk.STOCK_GO_FORWARD, gtk.ICON_SIZE_SMALL_TOOLBAR)
+ self.forward_image3 = gtk.image_new_from_stock(gtk.STOCK_GO_FORWARD, gtk.ICON_SIZE_SMALL_TOOLBAR)
+ self.forward_image4 = gtk.image_new_from_stock(gtk.STOCK_GO_FORWARD, gtk.ICON_SIZE_SMALL_TOOLBAR)
+ self.forward_label = gtk.Label(" ")
+ self.forward_label2 = gtk.Label(" ")
+ self.forward_label3 = gtk.Label(" ")
+ self.forward_label4 = gtk.Label(" ")
+
+ self.copy_button = gtk.RadioToolButton()
+ self.copy_button.set_label(_("Copy"))
+ self.copy_button.set_is_important(True)
+
+ self.copy_hbox.pack_start(self.forward_label)
+ self.copy_hbox.pack_start(self.forward_image)
+ self.copy_hbox.pack_start(self.copy_button, expand=False, fill=False)
+ self.copy_hbox.pack_start(self.forward_image2)
+ self.copy_hbox.pack_start(self.forward_label2)
+ copy_box = gtk.ToolItem()
+ copy_box.add(self.copy_hbox)
+ self.copy_toolbar.insert(copy_box, 1)
+
+ self.move_button = gtk.RadioToolButton(self.copy_button)
+ self.move_button.set_label(_("Move"))
+ self.move_button.set_is_important(True)
+ self.move_hbox.pack_start(self.forward_label3)
+ self.move_hbox.pack_start(self.forward_image3)
+ self.move_hbox.pack_start(self.move_button, expand=False, fill=False)
+ self.move_hbox.pack_start(self.forward_image4)
+ self.move_hbox.pack_start(self.forward_label4)
+ move_box = gtk.ToolItem()
+ move_box.add(self.move_hbox)
+ self.copy_toolbar.insert(move_box, 2)
+
+ self.move_button.set_active(self.prefs.auto_delete)
+ self.copy_button.connect("toggled", self.on_copy_button_toggle_event)
+
+ self.copy_toolbar.show_all()
+ self._set_copy_toolbar_active_arrows()
+
+ def _setup_dest_toolbar(self):
+ #Destination Toolbar
+ self.dest_toolbar.set_border_width(5)
+
+ dest_label = gtk.Label()
+ dest_label.set_markup("<i>" + _("To") + "</i>")
+ self.dest_toolbar_label = gtk.ToolItem()
+ self.dest_toolbar_label.add(dest_label)
+ self.dest_toolbar_label.set_is_important(True)
+ self.dest_toolbar.insert(self.dest_toolbar_label, 0)
+
+ photo_dest_hbox = gtk.HBox()
+ self.photo_dest_label = gtk.Label(_("Photos:"))
+
+ self.to_photo_filechooser_button = gtk.FileChooserButton(
+ _("Select a folder to download photos to"))
+ self.to_photo_filechooser_button.set_action(
+ gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
+ photo_dest_hbox.pack_start(self.photo_dest_label, expand=False, fill=False, padding=6)
+ photo_dest_hbox.pack_start(self.to_photo_filechooser_button)
+ self.to_photo_filechooser = gtk.ToolItem()
+ self.to_photo_filechooser.set_is_important(True)
+ self.to_photo_filechooser.set_expand(True)
+ self.to_photo_filechooser.add(photo_dest_hbox)
+ self.dest_toolbar.insert(self.to_photo_filechooser, 1)
+
+ video_dest_hbox = gtk.HBox()
+ self.video_dest_label = gtk.Label(_("Videos:"))
+ self.to_video_filechooser_button = gtk.FileChooserButton(
+ _("Select a folder to download videos to"))
+ self.to_video_filechooser_button.set_action(
+ gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
+ video_dest_hbox.pack_start(self.video_dest_label, expand=False, fill=False, padding=6)
+ video_dest_hbox.pack_start(self.to_video_filechooser_button)
+ self.to_video_filechooser = gtk.ToolItem()
+ self.to_video_filechooser.set_is_important(True)
+ self.to_video_filechooser.set_expand(True)
+ self.to_video_filechooser.add(video_dest_hbox)
+ self.dest_toolbar.insert(self.to_video_filechooser, 2)
+
+ self._set_to_toolbar_values()
+ self.to_photo_filechooser_button.connect("selection-changed",
+ self.on_to_photo_filechooser_button_selection_changed)
+ self.to_video_filechooser_button.connect("selection-changed",
+ self.on_to_video_filechooser_button_selection_changed)
+ self.dest_toolbar.show_all()
+
+ def _make_widget_widths_equal(self, widget1, widget2):
+ """takes two widgets and sets a width for both equal to widest one"""
+
+ x1, y1, w1, h1 = widget1.get_allocation()
+ x2, y2, w2, h2 = widget2.get_allocation()
+ w = max(w1, w2)
+ h = max(h1, h2)
+ widget1.set_size_request(w,h)
+ widget2.set_size_request(w,h)
+
+ def _set_copy_toolbar_active_arrows(self):
+ if self.copy_button.get_active():
+ self.forward_image.set_visible(True)
+ self.forward_image2.set_visible(True)
+ self.forward_image3.set_visible(False)
+ self.forward_image4.set_visible(False)
+ self.forward_label.set_visible(False)
+ self.forward_label2.set_visible(False)
+ self.forward_label3.set_visible(True)
+ self.forward_label4.set_visible(True)
+ else:
+ self.forward_image.set_visible(False)
+ self.forward_image2.set_visible(False)
+ self.forward_image3.set_visible(True)
+ self.forward_image4.set_visible(True)
+ self.forward_label.set_visible(True)
+ self.forward_label2.set_visible(True)
+ self.forward_label3.set_visible(False)
+ self.forward_label4.set_visible(False)
+
+ def on_copy_button_toggle_event(self, radio_button):
+ self._set_copy_toolbar_active_arrows()
+ self.prefs.auto_delete = not self.copy_button.get_active()
+
+ def _set_from_toolbar_state(self):
+ logger.debug("_set_from_toolbar_state")
+ self.auto_detect_button.set_active(self.prefs.device_autodetection)
+ if self.prefs.device_autodetection:
+ self.from_filechooser_button.set_sensitive(False)
+ self.from_filechooser_button.set_current_folder(self.prefs.device_location)
+
+ def on_auto_detect_button_toggled_event(self, button):
+ logger.debug("on_auto_detect_button_toggled_event")
+ self.from_filechooser_button.set_sensitive(not button.get_active())
+ if not self.rerun_setup_available_image_and_video_media:
+ self.prefs.device_autodetection = button.get_active()
+
+ def on_from_filechooser_button_selection_changed(self, filechooserbutton):
+ logger.debug("on_from_filechooser_button_selection_changed")
+ path = filechooserbutton.get_current_folder()
+ if path and not self.rerun_setup_available_image_and_video_media:
+ self.prefs.device_location = path
+
+ def on_to_photo_filechooser_button_selection_changed(self, filechooserbutton):
+ path = filechooserbutton.get_current_folder()
+ if path:
+ self.prefs.download_folder = path
+
+ def on_to_video_filechooser_button_selection_changed(self, filechooserbutton):
+ path = filechooserbutton.get_current_folder()
+ if path:
+ self.prefs.video_download_folder = path
+
+ def _set_to_toolbar_values(self):
+ self.to_photo_filechooser_button.set_current_folder(self.prefs.download_folder)
+ self.to_video_filechooser_button.set_current_folder(self.prefs.video_download_folder)
+
+ def toolbar_event(self, widget, toolbar):
+ pass
+
+
def _set_window_size(self):
"""
@@ -3027,7 +3387,7 @@ class RapidApp(dbus.service.Object):
Set the size of the device collection scrolled window widget
"""
if self.device_collection.map_process_to_row:
- height = self.device_collection_viewport.size_request()[1]
+ height = max(self.device_collection_viewport.size_request()[1], 24)
self.device_collection_scrolledwindow.set_size_request(-1, height)
self.main_vpaned.set_position(height)
else:
@@ -3407,7 +3767,7 @@ class RapidApp(dbus.service.Object):
Set up process managers.
A task such as scanning a device or copying files is handled in its
- own process.
+ own process.
"""
self.batch_size = 10
@@ -3421,19 +3781,27 @@ class RapidApp(dbus.service.Object):
self.uses_stored_sequence_no_value,
self.uses_session_sequece_no_value,
self.uses_sequence_letter_value)
-
+
+ # daemon process to rename files and create subfolders
self.subfolder_file_manager = SubfolderFileManager(
self.subfolder_file_results,
- sequence_values,
- self.focal_length)
-
+ sequence_values)
+ # process to scan source devices / paths
self.scan_manager = ScanManager(self.scan_results, self.batch_size,
self.device_collection.add_device)
+
+ #process to copy files from source to destination
self.copy_files_manager = CopyFilesManager(self.copy_files_results,
self.batch_size_MB)
+
+ #process to back files up
self.backup_manager = BackupFilesManager(self.backup_results,
self.batch_size_MB)
+
+ #process to enhance files after they've been copied and before they're
+ #renamed
+ self.file_modify_manager = FileModifyManager(self.file_modify_results)
def scan_results(self, source, condition):
@@ -3468,6 +3836,7 @@ class RapidApp(dbus.service.Object):
self.start_download(scan_pid=scan_pid)
self.set_thumbnail_sort()
+ self.download_button.grab_focus()
# signal that no more data is coming, finishing io watch for this pipe
return False
@@ -3482,7 +3851,6 @@ class RapidApp(dbus.service.Object):
# must return True for this method to be called again
return True
-
@dbus.service.method (config.DBUS_NAME,
in_signature='', out_signature='b')
@@ -3511,7 +3879,7 @@ def start():
parser.add_option("-q", "--quiet", action="store_false", dest="verbose", help=_("only output errors to the command line"))
# image file extensions are recognized RAW files plus TIFF and JPG
parser.add_option("-e", "--extensions", action="store_true", dest="extensions", help=_("list photo and video file extensions the program recognizes and exit"))
- parser.add_option("--focal-length", type=int, dest="focal_length", help="If an aperture value of 0.0 is encountered, for file renaming purposes the metadata for that photo will temporarily have its focal length set to the number passed, and its aperture to f8")
+ parser.add_option("--focal-length", type=int, dest="focal_length", help="If an aperture value of 0.0 is encountered, the focal length metadata will be set to the number passed, and its aperture metadata to f8")
parser.add_option("--reset-settings", action="store_true", dest="reset", help=_("reset all program settings and preferences and exit"))
(options, args) = parser.parse_args()
@@ -3525,7 +3893,7 @@ def start():
logger.setLevel(logging_level)
if options.extensions:
- extensions = ((rpdfile.RAW_FILE_EXTENSIONS + rpdfile.NON_RAW_IMAGE_FILE_EXTENSIONS, _("Photos:")), (rpdfile.VIDEO_FILE_EXTENSIONS, _("Videos:")))
+ extensions = ((rpdfile.PHOTO_EXTENSIONS, _("Photos:")), (rpdfile.VIDEO_EXTENSIONS, _("Videos:")))
for exts, file_type in extensions:
v = ''
for e in exts[:-1]:
@@ -3549,14 +3917,16 @@ def start():
logger.info("Rapid Photo Downloader %s", utilities.human_readable_version(config.version))
logger.info("Using pyexiv2 %s", metadataphoto.pyexiv2_version_info())
logger.info("Using exiv2 %s", metadataphoto.exiv2_version_info())
-
+ if metadataexiftool.EXIFTOOL_VERSION is None:
+ logger.info("Exiftool not detected")
+ else:
+ logger.info("Using exiftool %s", metadataexiftool.EXIFTOOL_VERSION)
+ if metadatavideo.HAVE_HACHOIR:
+ logger.info("Using hachoir %s", metadatavideo.version_info())
+
+
if focal_length:
logger.info("Focal length of %s will be used when an aperture of 0.0 is encountered", focal_length)
-
- if DOWNLOAD_VIDEO:
- logger.info("Using hachoir %s", metadatavideo.version_info())
- else:
- logger.info(_("Video downloading functionality disabled.\nTo download videos, please install the hachoir metadata and kaa metadata packages for python."))
bus = dbus.SessionBus ()
request = bus.request_name (config.DBUS_NAME, dbus.bus.NAME_FLAG_DO_NOT_QUEUE)
diff --git a/rapid/rpdfile.py b/rapid/rpdfile.py
index d9454d7..8818182 100644
--- a/rapid/rpdfile.py
+++ b/rapid/rpdfile.py
@@ -34,6 +34,7 @@ from gettext import gettext as _
import config
import metadataphoto
import metadatavideo
+import metadataexiftool
import problemnotification as pn
@@ -43,7 +44,9 @@ import thumbnail as tn
RAW_EXTENSIONS = ['arw', 'dcr', 'cr2', 'crw', 'dng', 'mos', 'mef', 'mrw',
'nef', 'orf', 'pef', 'raf', 'raw', 'rw2', 'sr2', 'srw']
-NON_RAW_IMAGE_EXTENSIONS = ['jpg', 'jpe', 'jpeg', 'tif', 'tiff']
+JPEG_EXTENSIONS = ['jpg', 'jpe', 'jpeg']
+
+NON_RAW_IMAGE_EXTENSIONS = JPEG_EXTENSIONS + ['tif', 'tiff']
PHOTO_EXTENSIONS = RAW_EXTENSIONS + NON_RAW_IMAGE_EXTENSIONS
@@ -52,7 +55,9 @@ if metadatavideo.DOWNLOAD_VIDEO:
# needs to be able to download videos
VIDEO_EXTENSIONS = ['3gp', 'avi', 'm2t', 'mov', 'mp4', 'mpeg','mpg', 'mod',
'tod']
- VIDEO_THUMBNAIL_EXTENSIONS = ['thm']
+ if metadataexiftool.EXIFTOOL_VERSION is not None:
+ VIDEO_EXTENSIONS += ['mts']
+ VIDEO_THUMBNAIL_EXTENSIONS = ['thm']
else:
VIDEO_EXTENSIONS = []
VIDEO_THUMBNAIL_EXTENSIONS = []
@@ -75,18 +80,18 @@ def file_type(file_extension):
return None
def get_rpdfile(extension, name, display_name, path, size,
- file_system_modification_time,
- scan_pid, file_id):
+ file_system_modification_time, thm_full_name,
+ scan_pid, file_id, file_type):
- if extension in VIDEO_EXTENSIONS:
+ if file_type == FILE_TYPE_VIDEO:
return Video(name, display_name, path, size,
- file_system_modification_time,
+ file_system_modification_time, thm_full_name,
scan_pid, file_id)
else:
# assume it's a photo - no check for performance reasons (this will be
# called many times)
return Photo(name, display_name, path, size,
- file_system_modification_time,
+ file_system_modification_time, thm_full_name,
scan_pid, file_id)
class FileTypeCounter:
@@ -144,7 +149,7 @@ class RPDFile:
"""
def __init__(self, name, display_name, path, size,
- file_system_modification_time,
+ file_system_modification_time, thm_full_name,
scan_pid, file_id):
self.path = path
@@ -153,11 +158,15 @@ class RPDFile:
self.display_name = display_name
self.full_file_name = os.path.join(path, name)
+ self.extension = os.path.splitext(name)[1][1:].lower()
self.size = size # type int
self.modification_time = file_system_modification_time
+ #full path and name of thumbnail file that is associated with some videos
+ self.thm_full_name = thm_full_name
+
self.status = config.STATUS_NOT_DOWNLOADED
self.problem = None # class Problem in problemnotifcation.py
@@ -177,12 +186,18 @@ class RPDFile:
# generated values
self.temp_full_file_name = ''
+ self.temp_thm_full_name = ''
+ self.temp_xmp_full_name = ''
+
self.download_start_time = None
self.download_subfolder = ''
self.download_path = ''
self.download_name = ''
- self.download_full_file_name = ''
+ self.download_full_file_name = '' #file name with path
+ self.download_full_base_name = '' #file name with path but no extension
+ self.download_thm_full_name = '' #name of THM (thumbnail) file with path
+ self.download_xmp_full_name = '' #name of XMP sidecar with path
self.metadata = None
@@ -194,11 +209,24 @@ class RPDFile:
#self.subfolder_pref_list = []
#self.name_pref_list = []
#strip_characters = False
+ #self.thm_extension = ''
+ #self.xmp_extension = ''
+
+ #these values are set only if they were written to an xmp sidecar
+ #in the filemodify process
+ #self.new_aperture = ''
+ #self.new_focal_length = ''
def _assign_file_type(self):
self.file_type = None
+ def _load_file_for_metadata(self, temp_file):
+ if temp_file:
+ return self.temp_full_file_name
+ else:
+ return self.full_file_name
+
def initialize_problem(self):
self.problem = pn.Problem()
# these next values are used to display in the error log window
@@ -218,32 +246,6 @@ class RPDFile:
def add_extra_detail(self, extra_detail, *args):
self.problem.add_extra_detail(extra_detail, *args)
-
-
-
-#~ exif_tags_needed = ('Exif.Photo.FNumber',
- #~ 'Exif.Photo.ISOSpeedRatings',
- #~ 'Exif.Photo.ExposureTime',
- #~ 'Exif.Photo.FocalLength',
- #~ 'Exif.Image.Make',
- #~ 'Exif.Image.Model',
- #~ 'Exif.Canon.SerialNumber',
- #~ 'Exif.Nikon3.SerialNumber'
- #~ 'Exif.OlympusEq.SerialNumber',
- #~ 'Exif.Olympus.SerialNumber',
- #~ 'Exif.Olympus.SerialNumber2',
- #~ 'Exif.Panasonic.SerialNumber',
- #~ 'Exif.Fujifilm.SerialNumber',
- #~ 'Exif.Image.CameraSerialNumber',
- #~ 'Exif.Nikon3.ShutterCount',
- #~ 'Exif.Canon.FileNumber',
- #~ 'Exif.Canon.ImageNumber',
- #~ 'Exif.Canon.OwnerName',
- #~ 'Exif.Photo.DateTimeOriginal',
- #~ 'Exif.Image.DateTime',
- #~ 'Exif.Photo.SubSecTimeOriginal',
- #~ 'Exif.Image.Orientation'
- #~ )
class Photo(RPDFile):
@@ -253,9 +255,8 @@ class Photo(RPDFile):
def _assign_file_type(self):
self.file_type = FILE_TYPE_PHOTO
- def load_metadata(self):
-
- self.metadata = metadataphoto.MetaData(self.full_file_name)
+ def load_metadata(self, temp_file=False):
+ self.metadata = metadataphoto.MetaData(self._load_file_for_metadata(temp_file))
try:
self.metadata.read()
except:
@@ -273,8 +274,13 @@ class Video(RPDFile):
def _assign_file_type(self):
self.file_type = FILE_TYPE_VIDEO
- def load_metadata(self):
- self.metadata = metadatavideo.VideoMetaData(self.full_file_name)
+ def load_metadata(self, temp_file=False):
+ if self.extension == 'mts' or not metadatavideo.HAVE_HACHOIR:
+ if metadatavideo.HAVE_HACHOIR:
+ logger.debug("Using ExifTool parser")
+ self.metadata = metadataexiftool.ExifToolMetaData(self._load_file_for_metadata(temp_file))
+ else:
+ self.metadata = metadatavideo.VideoMetaData(self._load_file_for_metadata(temp_file))
return True
class SamplePhoto(Photo):
@@ -285,7 +291,8 @@ class SamplePhoto(Photo):
size=23516764,
file_system_modification_time=time.time(),
scan_pid=2033,
- file_id='9873afe')
+ file_id='9873afe',
+ thm_full_name=None)
self.sequences = sequences
self.metadata = metadataphoto.DummyMetaData()
self.download_start_time = datetime.datetime.now()
@@ -298,7 +305,8 @@ class SampleVideo(Video):
size=823513764,
file_system_modification_time=time.time(),
scan_pid=2033,
- file_id='9873qrsfe')
+ file_id='9873qrsfe',
+ thm_full_name=None)
self.sequences = sequences
self.metadata = metadatavideo.DummyMetaData(filename=sample_name)
self.download_start_time = datetime.datetime.now()
diff --git a/rapid/scan.py b/rapid/scan.py
index 0d7182c..02902ec 100755
--- a/rapid/scan.py
+++ b/rapid/scan.py
@@ -41,6 +41,24 @@ file_attributes = "standard::name,standard::display-name,\
standard::type,standard::size,time::modified,access::can-read,id::file"
+def get_video_THM_file(full_file_name_no_ext):
+ """
+ Checks to see if a thumbnail file (THM) is in the same directory as the
+ file. Expects a full path to be part of the file name.
+
+ Returns the filename, including path, if found, else returns None.
+ """
+
+ f = None
+ for e in rpdfile.VIDEO_THUMBNAIL_EXTENSIONS:
+ if os.path.exists(full_file_name_no_ext + '.' + e):
+ f = full_file_name_no_ext + '.' + e
+ break
+ if os.path.exists(full_file_name_no_ext + '.' + e.upper()):
+ f = full_file_name_no_ext + '.' + e.upper()
+ break
+
+ return f
class Scan(multiprocessing.Process):
@@ -120,7 +138,8 @@ class Scan(multiprocessing.Process):
return None
elif file_type == gio.FILE_TYPE_REGULAR:
- ext = os.path.splitext(name)[1].lower()[1:]
+ base_name, ext = os.path.splitext(name)
+ ext = ext.lower()[1:]
file_type = rpdfile.file_type(ext)
if file_type is not None:
@@ -134,15 +153,24 @@ class Scan(multiprocessing.Process):
display_name = child.get_display_name()
size = child.get_size()
modification_time = child.get_modification_time()
-
+ path_name = path.get_path()
+
+ # look for thumbnail file for videos
+ if file_type == rpdfile.FILE_TYPE_VIDEO:
+ thm_full_name = get_video_THM_file(os.path.join(path_name, base_name))
+ else:
+ thm_full_name = None
+
scanned_file = rpdfile.get_rpdfile(ext,
name,
display_name,
- path.get_path(),
+ path_name,
size,
- modification_time,
+ modification_time,
+ thm_full_name,
self.pid,
- file_id)
+ file_id,
+ file_type)
self.files.append(scanned_file)
diff --git a/rapid/subfolderfile.py b/rapid/subfolderfile.py
index a80bc84..345edce 100644
--- a/rapid/subfolderfile.py
+++ b/rapid/subfolderfile.py
@@ -20,10 +20,10 @@
"""
Generates names for files and folders.
-Runs a daemon process.
+Runs as a daemon process.
"""
-import os, datetime, collections, fractions
+import os, datetime, collections
import gio
import multiprocessing
@@ -82,13 +82,17 @@ def time_subseconds_human_readable(date, subseconds):
'second':date.strftime("%S"),
'subsecond': subseconds}
-def load_metadata(rpd_file):
+def load_metadata(rpd_file, temp_file=True):
"""
Loads the metadata for the file. Returns True if operation succeeded, false
otherwise
+
+ If temp_file is true, the the metadata from the temporary file rather than
+ the original source file is used. This is important, because the metadata
+ can be modified by the filemodify process.
"""
if rpd_file.metadata is None:
- if not rpd_file.load_metadata():
+ if not rpd_file.load_metadata(temp_file):
# Error in reading metadata
rpd_file.add_problem(None, pn.CANNOT_DOWNLOAD_BAD_METADATA, {'filetype': rpd_file.title_capitalized})
return False
@@ -133,10 +137,10 @@ def generate_name(rpd_file):
rpd_file.download_name = _generate_name(generator, rpd_file)
return rpd_file
-
+
class SubfolderFile(multiprocessing.Process):
- def __init__(self, results_pipe, sequence_values, focal_length):
+ def __init__(self, results_pipe, sequence_values):
multiprocessing.Process.__init__(self)
self.daemon = True
self.results_pipe = results_pipe
@@ -150,8 +154,6 @@ class SubfolderFile(multiprocessing.Process):
self.uses_session_sequece_no = sequence_values[6]
self.uses_sequence_letter = sequence_values[7]
- self.focal_length = focal_length
-
logger.debug("Start of day is set to %s", self.day_start.value)
def progress_callback_no_update(self, amount_downloaded, total):
@@ -237,6 +239,7 @@ class SubfolderFile(multiprocessing.Process):
"""
Get subfolder and name.
Attempt to move the file from it's temporary directory.
+ Move video THM file if there is one.
If successful, increment sequence values.
Report any success or failure.
"""
@@ -265,10 +268,8 @@ class SubfolderFile(multiprocessing.Process):
while True:
logger.debug("Finished %s. Getting next task.", download_count)
- task = self.results_pipe.recv()
-
# rename file and move to generated subfolder
- download_succeeded, download_count, rpd_file = task
+ download_succeeded, download_count, rpd_file = self.results_pipe.recv()
move_succeeded = False
@@ -311,22 +312,16 @@ class SubfolderFile(multiprocessing.Process):
# Generate subfolder name and new file name
generation_succeeded = True
- # check to see if focal length and aperture data should be manipulated
- if self.focal_length is not None and rpd_file.file_type == rpdfile.FILE_TYPE_PHOTO:
- if load_metadata(rpd_file):
- a = rpd_file.metadata.aperture()
- if a == '0.0':
- fl = rpd_file.metadata["Exif.Photo.FocalLength"].value
- logger.info("Adjusting focal length and aperture for %s", rpd_file.full_file_name)
- #~ try:
- rpd_file.metadata["Exif.Photo.FocalLength"] = fractions.Fraction(self.focal_length,1)
- rpd_file.metadata["Exif.Photo.FNumber"] = fractions.Fraction(8,1)
- #~ rpd_file.metadata.write(preserve_timestamps=True)
- #~ logger.info("...wrote new value")
- #~ except:
- #~ logger.error("failed to write value!")
-
-
+ if rpd_file.file_type == rpdfile.FILE_TYPE_PHOTO:
+ if hasattr(rpd_file, 'new_focal_length'):
+ # A RAW file has had its focal length and aperture adjusted.
+ # These have been written out to an XMP sidecar, but they won't
+ # be picked up by pyexiv2. So temporarily change the values inplace here,
+ # without saving them.
+ if load_metadata(rpd_file):
+ rpd_file.metadata["Exif.Photo.FocalLength"] = rpd_file.new_focal_length
+ rpd_file.metadata["Exif.Photo.FNumber"] = rpd_file.new_aperture
+
rpd_file = generate_subfolder(rpd_file)
if rpd_file.download_subfolder:
@@ -374,6 +369,7 @@ class SubfolderFile(multiprocessing.Process):
if generation_succeeded:
rpd_file.download_path = os.path.join(rpd_file.download_folder, rpd_file.download_subfolder)
rpd_file.download_full_file_name = os.path.join(rpd_file.download_path, rpd_file.download_name)
+ rpd_file.download_full_base_name = os.path.splitext(rpd_file.download_full_file_name)[0]
subfolder = gio.File(path=rpd_file.download_path)
@@ -468,6 +464,36 @@ class SubfolderFile(multiprocessing.Process):
self.downloads_today_tracker.increment_downloads_today()
self.downloads_today.value = self.downloads_today_tracker.get_raw_downloads_today()
self.downloads_today_date.value = self.downloads_today_tracker.get_raw_downloads_today_date()
+
+ if rpd_file.temp_thm_full_name:
+ # copy and rename THM video file
+ source = gio.File(path=rpd_file.temp_thm_full_name)
+ ext = None
+ if hasattr(rpd_file, 'thm_extension'):
+ if rpd_file.thm_extension:
+ ext = rpd_file.thm_extension
+ if ext is None:
+ ext = '.THM'
+ download_thm_full_name = rpd_file.download_full_base_name + ext
+ dest = gio.File(path=download_thm_full_name)
+ try:
+ source.move(dest, self.progress_callback_no_update, cancellable=None)
+ rpd_file.download_thm_full_name = download_thm_full_name
+ except gio.Error, inst:
+ logger.error("Failed to move video THM file %s", download_thm_full_name)
+
+ if rpd_file.temp_xmp_full_name:
+ # copy and rename XMP sidecar file
+ source = gio.File(path=rpd_file.temp_xmp_full_name)
+ # generate_name() has generated xmp extension with correct capitalization
+ download_xmp_full_name = rpd_file.download_full_base_name + rpd_file.xmp_extension
+ dest = gio.File(path=download_xmp_full_name)
+ try:
+ source.move(dest, self.progress_callback_no_update, cancellable=None)
+ rpd_file.download_xmp_full_name = download_xmp_full_name
+ except gio.Error, inst:
+ logger.error("Failed to move XMP sidecar file %s", download_xmp_full_name)
+
if not move_succeeded:
logger.error("%s: %s - %s", rpd_file.full_file_name,
diff --git a/rapid/thumbnail.py b/rapid/thumbnail.py
index c987018..404c18b 100644
--- a/rapid/thumbnail.py
+++ b/rapid/thumbnail.py
@@ -1,5 +1,4 @@
#!/usr/bin/python
-#!/usr/bin/python
# -*- coding: latin1 -*-
### Copyright (C) 2011 Damon Lynch <damonlynch@gmail.com>
@@ -137,26 +136,6 @@ class PicklablePIL:
def get_pixbuf(self):
return image_to_pixbuf(self.get_image())
-def get_video_THM_file(fullFileName):
- """
- Checks to see if a thumbnail file (THM) is in the same directory as the
- file. Expects a full path to be part of the file name.
-
- Returns the filename, including path, if found, else returns None.
- """
-
- f = None
- name, ext = os.path.splitext(fullFileName)
- for e in rpdfile.VIDEO_THUMBNAIL_EXTENSIONS:
- if os.path.exists(name + '.' + e):
- f = name + '.' + e
- break
- if os.path.exists(name + '.' + e.upper()):
- f = name + '.' + e.upper()
- break
-
- return f
-
class Thumbnail:
# file types from which to remove letterboxing (black bands in the thumbnail
@@ -187,17 +166,24 @@ class Thumbnail:
return (thumbnail.data, lowrez)
def _process_thumbnail(self, image, size_reduced):
+ image_ok = True
if image.mode <> "RGBA":
- image = image.convert("RGBA")
+ try:
+ image = image.convert("RGBA")
+ except:
+ logger.error("Image thumbnail is corrupt")
+ image_ok = False
- thumbnail = PicklablePIL(image)
- if size_reduced is not None:
- thumbnail_icon = image.copy()
- downsize_pil(thumbnail_icon, size_reduced, fit=False)
- thumbnail_icon = PicklablePIL(thumbnail_icon)
+ if image_ok:
+ thumbnail = PicklablePIL(image)
+ if size_reduced is not None:
+ thumbnail_icon = image.copy()
+ downsize_pil(thumbnail_icon, size_reduced, fit=False)
+ thumbnail_icon = PicklablePIL(thumbnail_icon)
+ else:
+ thumbnail_icon = None
else:
- thumbnail_icon = None
-
+ thumbnail = thumbnail_icon = None
return (thumbnail, thumbnail_icon)
def _get_photo_thumbnail(self, full_file_name, size_max, size_reduced):
@@ -263,7 +249,7 @@ class Thumbnail:
logger.debug("...got thumbnail for %s", full_file_name)
return (thumbnail, thumbnail_icon)
- def _get_video_thumbnail(self, full_file_name, size_max, size_reduced):
+ def _get_video_thumbnail(self, full_file_name, thm_full_name, size_max, size_reduced):
thumbnail = None
thumbnail_icon = None
if size_max is None:
@@ -272,14 +258,16 @@ class Thumbnail:
size = max(size_max[0], size_max[1])
image = None
if size > 0 and size <= 160:
- thm = get_video_THM_file(full_file_name)
- if thm:
+ if thm_full_name:
try:
- thumbnail = gtk.gdk.pixbuf_new_from_file(thm)
+ thumbnail = gtk.gdk.pixbuf_new_from_file(thm_full_name)
except:
- logger.warning("Could not open THM file for %s", full_file_name)
- thumbnail = add_filmstrip(thumbnail)
- image = pixbuf_to_image(thumbnail)
+ logger.error("Could not open THM file for %s", full_file_name)
+ logger.error("Thumbnail file is %s", thm_full_name)
+ image = None
+ else:
+ thumbnail = add_filmstrip(thumbnail)
+ image = pixbuf_to_image(thumbnail)
if image is None:
try:
@@ -299,13 +287,13 @@ class Thumbnail:
logger.debug("...got thumbnail for %s", full_file_name)
return (thumbnail, thumbnail_icon)
- def get_thumbnail(self, full_file_name, file_type, size_max=None, size_reduced=None):
+ def get_thumbnail(self, full_file_name, thm_full_name, file_type, size_max=None, size_reduced=None):
logger.debug("Getting thumbnail for %s...", full_file_name)
if file_type == rpdfile.FILE_TYPE_PHOTO:
logger.debug("file type is photo")
return self._get_photo_thumbnail(full_file_name, size_max, size_reduced)
else:
- return self._get_video_thumbnail(full_file_name, size_max, size_reduced)
+ return self._get_video_thumbnail(full_file_name, thm_full_name, size_max, size_reduced)
class GetPreviewImage(multiprocessing.Process):
@@ -332,8 +320,8 @@ class GetPreviewImage(multiprocessing.Process):
def run(self):
while True:
- unique_id, full_file_name, file_type, size_max = self.results_pipe.recv()
- full_size_preview, reduced_size_preview = self.thumbnail_maker.get_thumbnail(full_file_name, file_type, size_max=size_max, size_reduced=(100,100))
+ unique_id, full_file_name, thm_full_name, file_type, size_max = self.results_pipe.recv()
+ full_size_preview, reduced_size_preview = self.thumbnail_maker.get_thumbnail(full_file_name, thm_full_name, file_type, size_max=size_max, size_reduced=(100,100))
if full_size_preview is None:
full_size_preview = self.get_stock_image(file_type)
self.results_pipe.send((unique_id, full_size_preview, reduced_size_preview))
@@ -369,9 +357,9 @@ class GenerateThumbnails(multiprocessing.Process):
logger.info("Terminating thumbnailing")
return None
-
thumbnail, thumbnail_icon = self.thumbnail_maker.get_thumbnail(
f.full_file_name,
+ f.thm_full_name,
f.file_type,
(160, 120), (100,100))