summaryrefslogtreecommitdiff
path: root/rapid/preferencesdialog.py
diff options
context:
space:
mode:
Diffstat (limited to 'rapid/preferencesdialog.py')
-rw-r--r--rapid/preferencesdialog.py330
1 files changed, 308 insertions, 22 deletions
diff --git a/rapid/preferencesdialog.py b/rapid/preferencesdialog.py
index 7289d0e..1509630 100644
--- a/rapid/preferencesdialog.py
+++ b/rapid/preferencesdialog.py
@@ -18,7 +18,7 @@
### Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-import datetime
+import datetime, re
import gtk
@@ -384,9 +384,9 @@ class VideoSubfolderPrefs(PhotoSubfolderPrefs):
pref_defn_L0 = DICT_VIDEO_SUBFOLDER_L0,
pref_list = pref_list)
-class RemoveAllJobCodeDialog(gtk.Dialog):
- def __init__(self, parent_window, post_choice_callback):
- gtk.Dialog.__init__(self, _('Remove all Job Codes?'), None,
+class QuestionDialog(gtk.Dialog):
+ def __init__(self, parent_window, title, question, post_choice_callback):
+ gtk.Dialog.__init__(self, title, None,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_NO, gtk.RESPONSE_CANCEL,
gtk.STOCK_YES, gtk.RESPONSE_OK))
@@ -403,7 +403,7 @@ class RemoveAllJobCodeDialog(gtk.Dialog):
image.set_from_pixbuf(icon)
prompt_hbox.pack_start(image, False, False, padding = 6)
- prompt_label = gtk.Label(_('Should all Job Codes be removed?'))
+ prompt_label = gtk.Label(question)
prompt_label.set_line_wrap(True)
prompt_hbox.pack_start(prompt_label, False, False, padding=6)
@@ -420,11 +420,32 @@ class RemoveAllJobCodeDialog(gtk.Dialog):
self.connect('response', self.on_response)
-
+
def on_response(self, device_dialog, response):
user_selected = response == gtk.RESPONSE_OK
self.post_choice_callback(self, user_selected)
+class RemoveAllJobCodeDialog(QuestionDialog):
+ def __init__(self, parent_window, post_choice_callback):
+ QuestionDialog.__init__(self, parent_window,
+ _('Remove all Job Codes?'),
+ _('Should all Job Codes be removed?'),
+ 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)
+
+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)
+
class PhotoRenameTable(tpm.TablePlusMinus):
def __init__(self, preferencesdialog, adjust_scroll_window):
@@ -716,7 +737,6 @@ class JobCodeDialog(gtk.Dialog):
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
gtk.STOCK_OK, gtk.RESPONSE_OK))
-
self.set_icon_from_file(paths.share_dir('glade3/rapid-photo-downloader.svg'))
self.post_job_code_entry_callback = post_job_code_entry_callback
@@ -788,6 +808,54 @@ class JobCodeDialog(gtk.Dialog):
logger.debug("Job Code not entered")
self.post_job_code_entry_callback(self, user_chose_code, self.get_job_code())
+class IgnorePathDialog(gtk.Dialog):
+ """ Dialog prompting for a path to ignore when scanning devices"""
+
+ def __init__(self, parent_window, post_entry_callback):
+ gtk.Dialog.__init__(self, _('Enter a Path to Ignore'), None,
+ gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
+ (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+ gtk.STOCK_OK, gtk.RESPONSE_OK))
+
+ self.set_icon_from_file(paths.share_dir('glade3/rapid-photo-downloader.svg'))
+ self.post_entry_callback = post_entry_callback
+
+ self.path_entry = gtk.Entry(max=0)
+
+ self.ignored_path_hbox = gtk.HBox(homogeneous = False)
+
+ task_label = gtk.Label(_('Specify a path that will never be scanned for photos or videos'))
+ task_label.set_line_wrap(True)
+ task_hbox = gtk.HBox()
+ task_hbox.pack_start(task_label, False, False, padding=6)
+
+ label = gtk.Label(_('Path:'))
+ self.ignored_path_hbox.pack_start(label, False, False, padding=6)
+ self.ignored_path_hbox.pack_start(self.path_entry, True, True, padding=6)
+
+ self.set_border_width(6)
+ self.set_has_separator(False)
+
+ # when user hits enter, close the dialog window
+ self.set_default_response(gtk.RESPONSE_OK)
+ self.path_entry.set_activates_default(True)
+
+ self.vbox.pack_start(task_hbox, False, False, padding = 6)
+ self.vbox.pack_start(self.ignored_path_hbox, False, False, padding=12)
+
+ self.set_transient_for(parent_window)
+ self.show_all()
+ self.connect('response', self.on_ignored_path_resp)
+
+ def on_ignored_path_resp(self, ignored_path_dialog, response):
+ user_chose_path = False
+ if response == gtk.RESPONSE_OK:
+ user_chose_path = True
+ logger.debug("Ignored Path entered")
+ else:
+ logger.debug("Ignored Path not entered")
+ self.post_entry_callback(self, user_chose_path, self.path_entry.get_text())
+
class PreferencesDialog():
"""
@@ -834,6 +902,7 @@ class PreferencesDialog():
self._setup_rename_options_tab()
self._setup_job_code_tab()
self._setup_device_tab()
+ self._setup_device_options_tab()
self._setup_backup_tab()
self._setup_miscellaneous_tab()
self._setup_error_tab()
@@ -903,8 +972,101 @@ class PreferencesDialog():
self.prefs.backup_location = widget.get_current_folder()
self.update_backup_example()
+ def on_backup_video_folder_filechooser_button_selection_changed(self, widget):
+ self.prefs.backup_video_location = widget.get_current_folder()
+ self.update_backup_example()
+
def on_device_location_filechooser_button_selection_changed(self, widget):
self.prefs.device_location = widget.get_current_folder()
+
+ def on_add_ignored_path_button_clicked(self, widget):
+ i = IgnorePathDialog(parent_window = self.dialog,
+ post_entry_callback = self.add_ignored_path)
+
+ def add_ignored_path(self, dialog, user_chose_path, path):
+ dialog.destroy()
+ if user_chose_path:
+ if path and path not in self.prefs.ignored_paths:
+ self.ignored_paths_liststore.prepend((path, ))
+ self.update_ignored_paths()
+ selection = self.ignored_paths_treeview.get_selection()
+ selection.unselect_all()
+ selection.select_path((0, ))
+ #scroll to the top
+ adjustment = self.ignored_paths_scrolledwindow.get_vadjustment()
+ adjustment.set_value(adjustment.lower)
+
+ def on_ignored_paths_use_re_checkbutton_toggled(self, checkbutton):
+ self.prefs.use_re_ignored_paths = checkbutton.get_active()
+ if self.prefs.use_re_ignored_paths and not self.pref_dialog_startup:
+ # check for invalid regular expressions
+ self.update_ignored_paths()
+
+ def on_remove_ignored_path_button_clicked(self, button):
+ self._remove_from_treeview(self.ignored_paths_treeview)
+ self.update_ignored_paths()
+
+ def on_remove_all_ignored_paths_button_clicked(self, button):
+ i = RemoveAllIgnoredPathsDialog(self.dialog, self.remove_all_ignored_paths)
+
+ def remove_all_ignored_paths(self, dialog, user_selected):
+ dialog.destroy()
+ if user_selected:
+ self.ignored_paths_liststore.clear()
+ self.update_ignored_paths()
+
+ def on_remove_remembered_device_button_clicked(self, button):
+ """
+ uses remembered devices treeview to delete any removed items from the
+ device_whitelist and device_blacklist prefs
+ """
+ blacklist = [i for i in self.prefs.device_blacklist if i]
+ whitelist = [i for i in self.prefs.device_whitelist if i]
+ selection = self.remembered_devices_treeview.get_selection()
+ model, selected = selection.get_selected_rows()
+ iters = [model.get_iter(path) for path in selected]
+ # only delete if a value is selected
+ if iters:
+ no = len(iters)
+ path = None
+ for i in range(0, no):
+ iter = iters[i]
+ if i == no - 1:
+ path = model.get_path(iter)
+ v = self.remembered_devices_liststore.get_value(iter, 0)
+ if v in blacklist:
+ blacklist.remove(v)
+ elif v in whitelist:
+ whitelist.remove(v)
+ else:
+ logger.debug("Unknown remembered device %s", v)
+ model.remove(iter)
+
+ # now that we removed the selection, play nice with
+ # the user and select the next item
+ selection.select_path(path)
+
+ # if there was no selection that meant the user
+ # removed the last entry, so we try to select the
+ # last item
+ if not selection.path_is_selected(path):
+ row = path[0]-1
+ # test case for empty lists
+ if row >= 0:
+ selection.select_path((row,))
+
+ self.prefs.device_blacklist = blacklist
+ self.prefs.device_whitelist = whitelist
+
+ def on_remove_all_remembered_device_button_clicked(self, button):
+ r = RemoveAllRemeberedDevicesDialog(self.dialog, self.remove_all_remembered_devices)
+
+ def remove_all_remembered_devices(self, dialog, user_selected):
+ dialog.destroy()
+ if user_selected:
+ self.remembered_devices_liststore.clear()
+ self.prefs.device_blacklist = []
+ self.prefs.device_whitelist = []
def _setup_sample_names(self, use_dummy_data = False):
"""
@@ -1085,7 +1247,7 @@ class PreferencesDialog():
column = gtk.TreeViewColumn()
rentext = gtk.CellRendererText()
rentext.connect('edited', self.on_job_code_edited)
- rentext .set_property('editable', True)
+ rentext.set_property('editable', True)
column.pack_start(rentext, expand=0)
column.set_attributes(rentext, text=0)
@@ -1101,6 +1263,60 @@ class PreferencesDialog():
self.remove_all_job_code_button.set_image(gtk.image_new_from_stock(
gtk.STOCK_CLEAR,
gtk.ICON_SIZE_BUTTON))
+ def _setup_device_options_tab(self):
+ """
+ Setup ignored paths and remembered devices tab in prefs dialog
+ """
+
+ self.ignored_paths_use_re_checkbutton.set_active(
+ self.prefs.use_re_ignored_paths)
+
+ self.ignored_paths_liststore = gtk.ListStore(str)
+ column = gtk.TreeViewColumn()
+ rentext = gtk.CellRendererText()
+ rentext.connect('edited', self.on_ignored_path_edited)
+ rentext.set_property('editable', True)
+
+ column.pack_start(rentext, expand=0)
+ column.set_attributes(rentext, text=0)
+ self.ignored_paths_treeview_column = column
+ self.ignored_paths_treeview.append_column(column)
+ self.ignored_paths_treeview.props.model = self.ignored_paths_liststore
+ for path in self.prefs.ignored_paths:
+ self.ignored_paths_liststore.append((path, ))
+
+ self.ignored_paths_treeview.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
+ self.remove_all_ignored_paths_button.set_image(gtk.image_new_from_stock(
+ gtk.STOCK_CLEAR,
+ gtk.ICON_SIZE_BUTTON))
+
+ # Remembered devices are a little different in that they cannot be
+ # edited, and they can only added when the user is prompted by the
+ # program. Moreover, the list the user sees is a combination of two
+ # lists: device_whitelist and device_blacklist
+
+ self.remembered_devices_liststore = gtk.ListStore(str)
+ column = gtk.TreeViewColumn()
+ rentext = gtk.CellRendererText()
+ rentext.set_property('editable', False)
+
+ column.pack_start(rentext, expand=0)
+ column.set_attributes(rentext, text=0)
+ self.remembered_devices_treeview_column = column
+ self.remembered_devices_treeview.append_column(column)
+ self.remembered_devices_treeview.props.model = self.remembered_devices_liststore
+ for device in self.prefs.device_whitelist:
+ if device:
+ self.remembered_devices_liststore.append((device, ))
+ for device in self.prefs.device_blacklist:
+ if device:
+ self.remembered_devices_liststore.append((device, ))
+
+ self.remembered_devices_treeview.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
+ self.remove_all_remembered_device_button.set_image(gtk.image_new_from_stock(
+ gtk.STOCK_CLEAR,
+ gtk.ICON_SIZE_BUTTON))
+
def _setup_device_tab(self):
self.device_location_filechooser_button = gtk.FileChooserButton(
@@ -1125,8 +1341,12 @@ class PreferencesDialog():
def _setup_backup_tab(self):
+ """
+ Setup and configure backup tab
+ """
+ #Manual backup location for photos file chooser
self.backup_folder_filechooser_button = gtk.FileChooserButton(
- _("Select a folder in which to backup %(file_types)s") % {'file_types':self.file_types})
+ _("Select a folder in which to backup photos"))
self.backup_folder_filechooser_button.set_current_folder(
self.prefs.backup_location)
self.backup_folder_filechooser_button.set_action(
@@ -1136,6 +1356,20 @@ class PreferencesDialog():
self.backup_table.attach(self.backup_folder_filechooser_button,
3, 4, 8, 9, yoptions = gtk.SHRINK)
self.backup_folder_filechooser_button.show()
+
+ #Manual backup location for videos file chooser
+ self.backup_video_folder_filechooser_button = gtk.FileChooserButton(
+ _("Select a folder in which to backup videos"))
+ self.backup_video_folder_filechooser_button.set_current_folder(
+ self.prefs.backup_video_location)
+ self.backup_video_folder_filechooser_button.set_action(
+ gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
+ self.backup_video_folder_filechooser_button.connect("selection-changed",
+ self.on_backup_video_folder_filechooser_button_selection_changed)
+ self.backup_table.attach(self.backup_video_folder_filechooser_button,
+ 3, 4, 9, 10, yoptions = gtk.SHRINK)
+ self.backup_video_folder_filechooser_button.show()
+
self.backup_identifier_entry.set_text(self.prefs.backup_identifier)
self.video_backup_identifier_entry.set_text(self.prefs.video_backup_identifier)
@@ -1149,6 +1383,14 @@ class PreferencesDialog():
self._backup_controls2 = [self.backup_location_label,
self.backup_folder_filechooser_button,
self.backup_location_explanation_label]
+
+ if metadatavideo.DOWNLOAD_VIDEO:
+ self._backup_controls2 += [self.backup_video_folder_filechooser_button,
+ self.backup_video_location_label]
+ else:
+ self.backup_video_folder_filechooser_button.set_sensitive(False)
+ self.backup_video_location_label.set_sensitive(False)
+
self._backup_controls = self._backup_controls0 + self._backup_controls1 + \
self._backup_controls2
@@ -1400,12 +1642,14 @@ class PreferencesDialog():
adjustment = self.job_code_scrolledwindow.get_vadjustment()
adjustment.set_value(adjustment.lower)
- def on_remove_job_code_button_clicked(self, button):
- """ remove selected job codes (can be multiple selection)"""
- selection = self.job_code_treeview.get_selection()
+ def _remove_from_treeview(self, treeview):
+ """
+ Removes selected items from a treeview, allowing multiple selections
+ """
+ selection = treeview.get_selection()
model, selected = selection.get_selected_rows()
iters = [model.get_iter(path) for path in selected]
- # only delete if a jobe code is selected
+ # only delete if a value is selected
if iters:
no = len(iters)
path = None
@@ -1419,7 +1663,7 @@ class PreferencesDialog():
# the user and select the next item
selection.select_path(path)
- # if there was no selection that meant the user
+ # if there was no selection that meant the user
# removed the last entry, so we try to select the
# last item
if not selection.path_is_selected(path):
@@ -1427,14 +1671,19 @@ class PreferencesDialog():
# test case for empty lists
if row >= 0:
selection.select_path((row,))
-
+
+
+ def on_remove_job_code_button_clicked(self, button):
+ """ remove selected job codes (can be multiple selection)"""
+
+ self._remove_from_treeview(self.job_code_treeview)
self.update_job_codes()
self.update_photo_rename_example()
self.update_video_rename_example()
self.update_photo_download_folder_example()
self.update_video_download_folder_example()
- def on_remove_all_job_code_button_clicked(self, button):
+ def on_remove_all_job_code_button_clicked(self, button):
j = RemoveAllJobCodeDialog(self.dialog, self.remove_all_job_code)
def remove_all_job_code(self, dialog, user_selected):
@@ -1456,13 +1705,51 @@ class PreferencesDialog():
self.update_photo_download_folder_example()
self.update_video_download_folder_example()
+ def _update_prefs_list(self, liststore):
+ replacement_list = []
+ for row in liststore:
+ replacement_list.append(row[0])
+ return replacement_list
+
def update_job_codes(self):
""" update preferences with list of job codes"""
- job_codes = []
- for row in self.job_code_liststore:
- job_codes.append(row[0])
- self.prefs.job_codes = job_codes
-
+ self.prefs.job_codes = self._update_prefs_list(self.job_code_liststore)
+
+ def on_ignored_path_edited(self, widget, path, new_text):
+ iter = self.ignored_paths_liststore.get_iter(path)
+ self.ignored_paths_liststore.set_value(iter, 0, new_text)
+ self.update_ignored_paths()
+
+ def update_ignored_paths(self):
+ ignored_paths = self._update_prefs_list(self.ignored_paths_liststore)
+
+ # remove any trailing slashes
+ ignored_paths = [path.rstrip('/') for path in ignored_paths if path]
+ # remove any blank values from ignored_paths
+ ignored_paths = [path for path in ignored_paths if path]
+
+ if self.prefs.use_re_ignored_paths:
+ ip = []
+ bad_paths = ''
+ for path in ignored_paths:
+ # check for validity
+ try:
+ re.match(path, '')
+ ip.append(path)
+ except:
+ logger.error("Ignoring invalid regular expression: %s", path)
+ bad_paths += path + '\n'
+ ignored_paths = ip
+ if bad_paths:
+ bad_paths = bad_paths[:-1]
+ if bad_paths.find('\n') >= 0:
+ msg = _("The following regular expressions are invalid, and will be removed unless you correct them:\n %s") % bad_paths
+ else:
+ msg = _("This regular expression is invalid, and will be removed unless you correct it:\n %s") % bad_paths
+ misc.run_dialog(_("Invalid regular expression"), msg, self)
+
+ self.prefs.ignored_paths = ignored_paths
+
def on_auto_startup_checkbutton_toggled(self, checkbutton):
self.prefs.auto_download_at_startup = checkbutton.get_active()
@@ -1471,7 +1758,6 @@ 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()