~dlynch3/rapid/trunk

311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
1
#!/usr/bin/python
321 by Damon Lynch
Preliminary work to integrate device collection treeview
2
# -*- coding: latin1 -*-
3
459 by Damon Lynch
Purge GIO code from subfolder.py and backupfile.py. Update copyright notices.
4
### Copyright (C) 2011-2014 Damon Lynch <damonlynch@gmail.com>
321 by Damon Lynch
Preliminary work to integrate device collection treeview
5
6
### This program is free software; you can redistribute it and/or modify
7
### it under the terms of the GNU General Public License as published by
8
### the Free Software Foundation; either version 2 of the License, or
9
### (at your option) any later version.
10
11
### This program is distributed in the hope that it will be useful,
12
### but WITHOUT ANY WARRANTY; without even the implied warranty of
13
### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
### GNU General Public License for more details.
15
16
### You should have received a copy of the GNU General Public License
17
### along with this program; if not, write to the Free Software
431.1.3 by Damon Lynch
Fixed bug #996613: Updated Free Software Foundation address.
18
### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19
### USA
321 by Damon Lynch
Preliminary work to integrate device collection treeview
20
446 by Damon Lynch
Debugging backup code - partial and switched off. Fixed bug #1093330: Photo rename ignores SubSeconds when 00.
21
use_pynotify = True
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
22
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
23
import tempfile
24
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
25
import dbus
26
import dbus.bus
27
import dbus.service
28
from dbus.mainloop.glib import DBusGMainLoop
29
DBusGMainLoop(set_as_default=True)
30
354 by Damon Lynch
Add command line options and re-enable single instance detection
31
from optparse import OptionParser
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
32
33
import gtk
34
import gtk.gdk as gdk
35
431.1.11 by Damon Lynch
Fix bug #1014203: create new liststore when clearing the old one, thereby removing the sort, which is super slow.
36
from gobject.constants import G_MAXINT
37
337 by Damon Lynch
Place checkbutton beneath thumbnail text; preliminary functionality for download action sensitivity
38
import webbrowser
39
354 by Damon Lynch
Add command line options and re-enable single instance detection
40
import sys, time, types, os, datetime
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
41
335 by Damon Lynch
check all / uncheck all button functionality; preliminary device mounting support, icon in device treeview display
42
import gobject, pango, cairo, array, pangocairo, gio
446 by Damon Lynch
Debugging backup code - partial and switched off. Fixed bug #1093330: Photo rename ignores SubSeconds when 00.
43
44
if use_pynotify:
45
    import pynotify
329 by Damon Lynch
Display thumbnails correctly (ellipsize & positioning); remove left and right panes; show preview image in main window
46
347 by Damon Lynch
Downloads today sequence implemented using tracking class and 3 shared memory values. Stored sequence number implemented using 1 shared memory value. Session sequence no and letter only partially implemented. kaa metadata loaded only when needed.
47
from multiprocessing import Process, Pipe, Queue, Event, Value, Array, current_process, log_to_stderr
48
from ctypes import c_int, c_bool, c_char
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
49
50
import logging
331 by Damon Lynch
Place checkbox in upper left of thumbnail display; add debugging information to understand cause of PIL core dump
51
logger = log_to_stderr()
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
52
337 by Damon Lynch
Place checkbutton beneath thumbnail text; preliminary functionality for download action sensitivity
53
# Rapid Photo Downloader modules
54
346 by Damon Lynch
move reading prefs extraction out of name generation process and into main process
55
import rpdfile
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
56
455 by Damon Lynch
Fixed bug #1242119: choosing a bookmarked folder in a file choose button does not work under Ubuntu 13.10
57
from misc import get_folder_selection
58
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
59
import problemnotification as pn
313 by Damon Lynch
Fix pixbuf problem using numpy array, and preliminary & incomplete thumbnail generator work
60
import thumbnail as tn
61
import rpdmultiprocessing as rpdmp
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
62
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
63
import preferencesdialog
64
import prefsrapid
65
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
66
import tableplusminus as tpm
347 by Damon Lynch
Downloads today sequence implemented using tracking class and 3 shared memory values. Stored sequence number implemented using 1 shared memory value. Session sequence no and letter only partially implemented. kaa metadata loaded only when needed.
67
import generatename as gn
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
68
367 by Damon Lynch
Use downloadtracker class and adjust percent complete by including time taken to rename files.
69
import downloadtracker
70
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
71
import filemodify
72
416 by Damon Lynch
preliminary toolbar code
73
from metadatavideo import DOWNLOAD_VIDEO, file_types_to_download
354 by Damon Lynch
Add command line options and re-enable single instance detection
74
import metadataphoto
75
import metadatavideo
417 by Damon Lynch
Allow video downloading with ExifTool if Hachoir metadata is not installed; use ExifTool for FileNumber.
76
import metadataexiftool
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
77
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
78
import scan as scan_process
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
79
import copyfiles
341 by Damon Lynch
Create base class DaemonTaskManager; preliminary infrstructure work on subfolder creation and file renaming.
80
import subfolderfile
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
81
import backupfile
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
82
from backupfile import PHOTO_BACKUP, VIDEO_BACKUP, PHOTO_VIDEO_BACKUP
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
83
366 by Damon Lynch
Add error log window. Allow cancellation of file copy in case of early termination.
84
import errorlog
85
335 by Damon Lynch
check all / uncheck all button functionality; preliminary device mounting support, icon in device treeview display
86
import device as dv
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
87
import utilities
335 by Damon Lynch
check all / uncheck all button functionality; preliminary device mounting support, icon in device treeview display
88
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
89
import config
90
__version__ = config.version
91
92
import paths
93
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
94
import gettext
95
gettext.bindtextdomain(config.APP_NAME)
96
gettext.textdomain(config.APP_NAME)
97
389 by Damon Lynch
make all buttons translatable
98
_ = gettext.gettext
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
99
100
101
from utilities import format_size_for_user
102
from utilities import register_iconsets
103
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
104
105
from config import  STATUS_CANNOT_DOWNLOAD, STATUS_DOWNLOADED, \
106
                    STATUS_DOWNLOADED_WITH_WARNING, \
107
                    STATUS_DOWNLOAD_FAILED, \
108
                    STATUS_DOWNLOAD_PENDING, \
109
                    STATUS_BACKUP_PROBLEM, \
110
                    STATUS_NOT_DOWNLOADED, \
111
                    STATUS_DOWNLOAD_AND_BACKUP_FAILED, \
112
                    STATUS_WARNING
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
113
359 by Damon Lynch
If necessary, get thumbnail from downloaded file. Indicate download is pending.
114
DOWNLOADED = [STATUS_DOWNLOADED, STATUS_DOWNLOADED_WITH_WARNING, STATUS_BACKUP_PROBLEM]
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
115
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
116
#Translators: if neccessary, for guidance in how to translate this program, you may see http://damonlynch.net/translate.html
352 by Damon Lynch
enable menus
117
PROGRAM_NAME = _('Rapid Photo Downloader')
118
__version__ = config.version
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
119
120
def date_time_human_readable(date, with_line_break=True):
121
    if with_line_break:
122
        return _("%(date)s\n%(time)s") % {'date':date.strftime("%x"), 'time':date.strftime("%X")}
123
    else:
124
        return _("%(date)s %(time)s") % {'date':date.strftime("%x"), 'time':date.strftime("%X")}
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
125
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
126
def date_time_subseconds_human_readable(date, subseconds):
127
    return _("%(date)s %(hour)s:%(minute)s:%(second)s:%(subsecond)s") % \
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
128
            {'date':date.strftime("%x"),
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
129
             'hour':date.strftime("%H"),
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
130
             'minute':date.strftime("%M"),
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
131
             'second':date.strftime("%S"),
132
             'subsecond': subseconds}
133
134
321 by Damon Lynch
Preliminary work to integrate device collection treeview
135
class DeviceCollection(gtk.TreeView):
136
    """
137
    TreeView display of devices and how many files have been copied, shown
138
    immediately under the menu in the main application window.
139
    """
140
    def __init__(self, parent_app):
141
142
        self.parent_app = parent_app
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
143
        # device icon & name, size of images on the device (human readable),
374 by Damon Lynch
unmount button in device collection
144
        # copy progress (%), copy text, eject button (None if irrelevant),
431.1.11 by Damon Lynch
Fix bug #1014203: create new liststore when clearing the old one, thereby removing the sort, which is super slow.
145
        # process id, pulse
374 by Damon Lynch
unmount button in device collection
146
        self.liststore = gtk.ListStore(gtk.gdk.Pixbuf, str, str, float, str,
431.1.11 by Damon Lynch
Fix bug #1014203: create new liststore when clearing the old one, thereby removing the sort, which is super slow.
147
                                       gtk.gdk.Pixbuf, int, int)
321 by Damon Lynch
Preliminary work to integrate device collection treeview
148
        self.map_process_to_row = {}
374 by Damon Lynch
unmount button in device collection
149
        self.devices_by_scan_pid = {}
321 by Damon Lynch
Preliminary work to integrate device collection treeview
150
151
        gtk.TreeView.__init__(self, self.liststore)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
152
321 by Damon Lynch
Preliminary work to integrate device collection treeview
153
        self.props.enable_search = False
154
        # make it impossible to select a row
155
        selection = self.get_selection()
156
        selection.set_mode(gtk.SELECTION_NONE)
416 by Damon Lynch
preliminary toolbar code
157
        self.set_headers_visible(False)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
158
159
160
        # Device refers to a thing like a camera, memory card in its reader,
335 by Damon Lynch
check all / uncheck all button functionality; preliminary device mounting support, icon in device treeview display
161
        # external hard drive, Portable Storage Device, etc.
162
        column0 = gtk.TreeViewColumn(_("Device"))
163
        pixbuf_renderer = gtk.CellRendererPixbuf()
416 by Damon Lynch
preliminary toolbar code
164
        pixbuf_renderer.set_padding(2, 0)
335 by Damon Lynch
check all / uncheck all button functionality; preliminary device mounting support, icon in device treeview display
165
        text_renderer = gtk.CellRendererText()
166
        text_renderer.props.ellipsize = pango.ELLIPSIZE_MIDDLE
374 by Damon Lynch
unmount button in device collection
167
        text_renderer.set_fixed_size(160, -1)
168
        eject_renderer = gtk.CellRendererPixbuf()
335 by Damon Lynch
check all / uncheck all button functionality; preliminary device mounting support, icon in device treeview display
169
        column0.pack_start(pixbuf_renderer, expand=False)
170
        column0.pack_start(text_renderer, expand=True)
374 by Damon Lynch
unmount button in device collection
171
        column0.pack_end(eject_renderer, expand=False)
335 by Damon Lynch
check all / uncheck all button functionality; preliminary device mounting support, icon in device treeview display
172
        column0.add_attribute(pixbuf_renderer, 'pixbuf', 0)
173
        column0.add_attribute(text_renderer, 'text', 1)
374 by Damon Lynch
unmount button in device collection
174
        column0.add_attribute(eject_renderer, 'pixbuf', 5)
321 by Damon Lynch
Preliminary work to integrate device collection treeview
175
        self.append_column(column0)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
176
177
335 by Damon Lynch
check all / uncheck all button functionality; preliminary device mounting support, icon in device treeview display
178
        # Size refers to the total size of images on the device, typically in
179
        # MB or GB
180
        column1 = gtk.TreeViewColumn(_("Size"), gtk.CellRendererText(), text=2)
321 by Damon Lynch
Preliminary work to integrate device collection treeview
181
        self.append_column(column1)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
182
183
        column2 = gtk.TreeViewColumn(_("Download Progress"),
335 by Damon Lynch
check all / uncheck all button functionality; preliminary device mounting support, icon in device treeview display
184
                                    gtk.CellRendererProgress(),
185
                                    value=3,
431.1.11 by Damon Lynch
Fix bug #1014203: create new liststore when clearing the old one, thereby removing the sort, which is super slow.
186
                                    text=4,
187
                                    pulse=7)
321 by Damon Lynch
Preliminary work to integrate device collection treeview
188
        self.append_column(column2)
189
        self.show_all()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
190
374 by Damon Lynch
unmount button in device collection
191
        icontheme = gtk.icon_theme_get_default()
192
        try:
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
193
            self.eject_pixbuf = icontheme.load_icon('media-eject', 16,
374 by Damon Lynch
unmount button in device collection
194
                                                gtk.ICON_LOOKUP_USE_BUILTIN)
195
        except:
196
            self.eject_pixbuf = gtk.gdk.pixbuf_new_from_file(
197
                                    paths.share_dir('glade3/media-eject.png'))
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
198
374 by Damon Lynch
unmount button in device collection
199
        self.add_events(gtk.gdk.BUTTON_PRESS_MASK)
200
        self.connect('button-press-event', self.button_clicked)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
201
374 by Damon Lynch
unmount button in device collection
202
335 by Damon Lynch
check all / uncheck all button functionality; preliminary device mounting support, icon in device treeview display
203
    def add_device(self, process_id, device, progress_bar_text = ''):
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
204
321 by Damon Lynch
Preliminary work to integrate device collection treeview
205
        # add the row, and get a temporary pointer to the row
206
        size_files = ''
207
        progress = 0.0
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
208
374 by Damon Lynch
unmount button in device collection
209
        if device.mount is None:
210
            eject = None
211
        else:
212
            eject = self.eject_pixbuf
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
213
374 by Damon Lynch
unmount button in device collection
214
        self.devices_by_scan_pid[process_id] = device
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
215
335 by Damon Lynch
check all / uncheck all button functionality; preliminary device mounting support, icon in device treeview display
216
        iter = self.liststore.append((device.get_icon(),
217
                                      device.get_name(),
218
                                      size_files,
219
                                      progress,
374 by Damon Lynch
unmount button in device collection
220
                                      progress_bar_text,
221
                                      eject,
431.1.11 by Damon Lynch
Fix bug #1014203: create new liststore when clearing the old one, thereby removing the sort, which is super slow.
222
                                      process_id,
223
                                      -1))
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
224
321 by Damon Lynch
Preliminary work to integrate device collection treeview
225
        self._set_process_map(process_id, iter)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
226
373 by Damon Lynch
auto start download on startup and on device insertion; adjust vertical pane when device is inserted; when auto start is enabled, delay making checkboxes invisible
227
        # adjust scrolled window height, based on row height and number of ready to start downloads
321 by Damon Lynch
Preliminary work to integrate device collection treeview
228
373 by Damon Lynch
auto start download on startup and on device insertion; adjust vertical pane when device is inserted; when auto start is enabled, delay making checkboxes invisible
229
        # please note, at program startup, self.row_height() will be less than it will be when already running
230
        # e.g. when starting with 3 cards, it could be 18, but when adding 2 cards to the already running program
231
        # (with one card at startup), it could be 21
384 by Damon Lynch
set main vpaned position to match height of device collection
232
        # must account for header row at the top
373 by Damon Lynch
auto start download on startup and on device insertion; adjust vertical pane when device is inserted; when auto start is enabled, delay making checkboxes invisible
233
        row_height = self.get_background_area(0, self.get_column(0))[3] + 1
416 by Damon Lynch
preliminary toolbar code
234
        height = max(((len(self.map_process_to_row) + 1) * row_height), 24)
373 by Damon Lynch
auto start download on startup and on device insertion; adjust vertical pane when device is inserted; when auto start is enabled, delay making checkboxes invisible
235
        self.parent_app.device_collection_scrolledwindow.set_size_request(-1, height)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
236
445 by Damon Lynch
Fixed bug #1083756: Application shows duplicate sources
237
321 by Damon Lynch
Preliminary work to integrate device collection treeview
238
    def update_device(self, process_id, total_size_files):
239
        """
240
        Updates the size of the photos and videos on the device, displayed to the user
241
        """
242
        if process_id in self.map_process_to_row:
243
            iter = self._get_process_map(process_id)
335 by Damon Lynch
check all / uncheck all button functionality; preliminary device mounting support, icon in device treeview display
244
            self.liststore.set_value(iter, 2, total_size_files)
321 by Damon Lynch
Preliminary work to integrate device collection treeview
245
        else:
374 by Damon Lynch
unmount button in device collection
246
            logger.critical("This device is unknown")
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
247
375 by Damon Lynch
auto unmount and system notifications on download completion
248
    def get_device(self, process_id):
249
        return self.devices_by_scan_pid.get(process_id)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
250
321 by Damon Lynch
Preliminary work to integrate device collection treeview
251
    def remove_device(self, process_id):
252
        if process_id in self.map_process_to_row:
253
            iter = self._get_process_map(process_id)
254
            self.liststore.remove(iter)
255
            del self.map_process_to_row[process_id]
374 by Damon Lynch
unmount button in device collection
256
            del self.devices_by_scan_pid[process_id]
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
257
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
258
    def get_all_displayed_processes(self):
259
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
260
        returns a list of the processes currently being displayed to the user
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
261
        """
349 by Damon Lynch
Implement mount and unmount callbacks and fix use device dialog
262
        return self.map_process_to_row.keys()
321 by Damon Lynch
Preliminary work to integrate device collection treeview
263
264
265
    def _set_process_map(self, process_id, iter):
266
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
267
        convert the temporary iter into a tree reference, which is
321 by Damon Lynch
Preliminary work to integrate device collection treeview
268
        permanent
269
        """
270
271
        path = self.liststore.get_path(iter)
272
        treerowref = gtk.TreeRowReference(self.liststore, path)
273
        self.map_process_to_row[process_id] = treerowref
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
274
321 by Damon Lynch
Preliminary work to integrate device collection treeview
275
    def _get_process_map(self, process_id):
276
        """
277
        return the tree iter for this process
278
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
279
321 by Damon Lynch
Preliminary work to integrate device collection treeview
280
        if process_id in self.map_process_to_row:
281
            treerowref = self.map_process_to_row[process_id]
282
            path = treerowref.get_path()
283
            iter = self.liststore.get_iter(path)
284
            return iter
285
        else:
286
            return None
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
287
431.1.11 by Damon Lynch
Fix bug #1014203: create new liststore when clearing the old one, thereby removing the sort, which is super slow.
288
    def update_progress(self, scan_pid, percent_complete, progress_bar_text, bytes_downloaded, pulse=None):
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
289
367 by Damon Lynch
Use downloadtracker class and adjust percent complete by including time taken to rename files.
290
        iter = self._get_process_map(scan_pid)
321 by Damon Lynch
Preliminary work to integrate device collection treeview
291
        if iter:
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
292
            if percent_complete:
293
                self.liststore.set_value(iter, 3, percent_complete)
321 by Damon Lynch
Preliminary work to integrate device collection treeview
294
            if progress_bar_text:
335 by Damon Lynch
check all / uncheck all button functionality; preliminary device mounting support, icon in device treeview display
295
                self.liststore.set_value(iter, 4, progress_bar_text)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
296
431.1.11 by Damon Lynch
Fix bug #1014203: create new liststore when clearing the old one, thereby removing the sort, which is super slow.
297
            if pulse is not None:
298
                if pulse:
299
                    # Make the bar pulse
300
                    self.liststore.set_value(iter, 7, self.liststore.get_value(iter, 7) + 1)
301
                else:
302
                    # Set to finished state
303
                    self.liststore.set_value(iter, 7, G_MAXINT)
304
            else:
305
                # Reset to allow fraction to be set
306
                self.liststore.set_value(iter, 7, -1)
307
308
321 by Damon Lynch
Preliminary work to integrate device collection treeview
309
374 by Damon Lynch
unmount button in device collection
310
    def button_clicked(self, widget, event):
311
        """
312
        Look for left single click on eject button
313
        """
314
        if event.button == 1:
381 by Damon Lynch
when user clicks download and thumbnail generation is still occuring, generate the thumbmnails in the copy file process
315
            if len (self.liststore):
316
                x = int(event.x)
317
                y = int(event.y)
318
                path, column, cell_x, cell_y = self.get_path_at_pos(x, y)
319
                if path is not None:
320
                    if column == self.get_column(0):
321
                        if cell_x >= column.get_width() - self.eject_pixbuf.get_width():
322
                            iter = self.liststore.get_iter(path)
323
                            if self.liststore.get_value(iter, 5) is not None:
324
                                self.unmount(process_id = self.liststore.get_value(iter, 6))
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
325
374 by Damon Lynch
unmount button in device collection
326
    def unmount(self, process_id):
327
        device = self.devices_by_scan_pid[process_id]
375 by Damon Lynch
auto unmount and system notifications on download completion
328
        if device.mount is not None:
329
            logger.debug("Unmounting device with scan pid %s", process_id)
330
            device.mount.unmount(self.unmount_callback)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
331
332
374 by Damon Lynch
unmount button in device collection
333
    def unmount_callback(self, mount, result):
377 by Damon Lynch
time remaining and download speed
334
        name = mount.get_name()
335
374 by Damon Lynch
unmount button in device collection
336
        try:
337
            mount.unmount_finish(result)
377 by Damon Lynch
time remaining and download speed
338
            logger.debug("%s successfully unmounted" % name)
374 by Damon Lynch
unmount button in device collection
339
        except gio.Error, inst:
377 by Damon Lynch
time remaining and download speed
340
            logger.error("%s did not unmount: %s", name, inst)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
341
446 by Damon Lynch
Debugging backup code - partial and switched off. Fixed bug #1093330: Photo rename ignores SubSeconds when 00.
342
            if use_pynotify:
343
                title = _("%(device)s did not unmount") % {'device': name}
344
                message = '%s' % inst
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
345
446 by Damon Lynch
Debugging backup code - partial and switched off. Fixed bug #1093330: Photo rename ignores SubSeconds when 00.
346
                n = pynotify.Notification(title, message)
347
                n.set_icon_from_pixbuf(self.parent_app.application_icon)
450 by Damon Lynch
Don't crash when the sending a system notfication message and the message returns an error
348
                try:
349
                    n.show()
350
                except:
351
                    logger.error("Unable to display message using notification system")
374 by Damon Lynch
unmount button in device collection
352
321 by Damon Lynch
Preliminary work to integrate device collection treeview
353
330 by Damon Lynch
make preview image resizable
354
def create_cairo_image_surface(pil_image, image_width, image_height):
481 by Damon Lynch
Release 0.4.11 to fix critical bug when using Pillow>=3.0. Update translations too.
355
        imgd = pil_image.tobytes("raw","BGRA", 0, 1)
330 by Damon Lynch
make preview image resizable
356
        data = array.array('B',imgd)
357
        stride = image_width * 4
358
        image = cairo.ImageSurface.create_for_data(data, cairo.FORMAT_ARGB32,
359
                                            image_width, image_height, stride)
360
        return image
321 by Damon Lynch
Preliminary work to integrate device collection treeview
361
328 by Damon Lynch
Use PIL image instead of pixbuf for cairo custom cell renderer
362
class ThumbnailCellRenderer(gtk.CellRenderer):
363
    __gproperties__ = {
364
        "image": (gobject.TYPE_PYOBJECT, "Image",
365
        "Image", gobject.PARAM_READWRITE),
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
366
367
        "filename": (gobject.TYPE_STRING, "Filename",
328 by Damon Lynch
Use PIL image instead of pixbuf for cairo custom cell renderer
368
        "Filename", '', gobject.PARAM_READWRITE),
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
369
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
370
        "status": (gtk.gdk.Pixbuf, "Status",
371
        "Status", gobject.PARAM_READWRITE),
328 by Damon Lynch
Use PIL image instead of pixbuf for cairo custom cell renderer
372
    }
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
373
331 by Damon Lynch
Place checkbox in upper left of thumbnail display; add debugging information to understand cause of PIL core dump
374
    def __init__(self, checkbutton_height):
328 by Damon Lynch
Use PIL image instead of pixbuf for cairo custom cell renderer
375
        gtk.CellRenderer.__init__(self)
376
        self.image = None
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
377
329 by Damon Lynch
Display thumbnails correctly (ellipsize & positioning); remove left and right panes; show preview image in main window
378
        self.image_area_size = 100
331 by Damon Lynch
Place checkbox in upper left of thumbnail display; add debugging information to understand cause of PIL core dump
379
        self.text_area_size = 30
380
        self.padding = 6
381
        self.checkbutton_height = checkbutton_height
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
382
        self.icon_width = 20
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
383
328 by Damon Lynch
Use PIL image instead of pixbuf for cairo custom cell renderer
384
    def do_set_property(self, pspec, value):
385
        setattr(self, pspec.name, value)
386
387
    def do_get_property(self, pspec):
331 by Damon Lynch
Place checkbox in upper left of thumbnail display; add debugging information to understand cause of PIL core dump
388
        return getattr(self, pspec.name)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
389
327 by Damon Lynch
Preliminary code with custom cell renderer using cairo
390
    def do_render(self, window, widget, background_area, cell_area, expose_area, flags):
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
391
327 by Damon Lynch
Preliminary code with custom cell renderer using cairo
392
        cairo_context = window.cairo_create()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
393
331 by Damon Lynch
Place checkbox in upper left of thumbnail display; add debugging information to understand cause of PIL core dump
394
        x = cell_area.x
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
395
        y = cell_area.y + self.checkbutton_height - 8
327 by Damon Lynch
Preliminary code with custom cell renderer using cairo
396
        w = cell_area.width
328 by Damon Lynch
Use PIL image instead of pixbuf for cairo custom cell renderer
397
        h = cell_area.height
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
398
399
        #constrain operations to cell area, allowing for a 1 pixel border
328 by Damon Lynch
Use PIL image instead of pixbuf for cairo custom cell renderer
400
        #either side
331 by Damon Lynch
Place checkbox in upper left of thumbnail display; add debugging information to understand cause of PIL core dump
401
        #~ cairo_context.rectangle(x-1, y-1, w+2, h+2)
402
        #~ cairo_context.clip()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
403
331 by Damon Lynch
Place checkbox in upper left of thumbnail display; add debugging information to understand cause of PIL core dump
404
        #fill in the background with dark grey
405
        #this ensures that a selected cell's fill does not make
406
        #the text impossible to read
407
        #~ cairo_context.rectangle(x, y, w, h)
408
        #~ cairo_context.set_source_rgb(0.267, 0.267, 0.267)
409
        #~ cairo_context.fill()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
410
328 by Damon Lynch
Use PIL image instead of pixbuf for cairo custom cell renderer
411
        #image width and height
412
        image_w = self.image.size[0]
413
        image_h = self.image.size[1]
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
414
329 by Damon Lynch
Display thumbnails correctly (ellipsize & positioning); remove left and right panes; show preview image in main window
415
        #center the image horizontally
416
        #bottom align vertically
417
        #top left and right corners for the image:
328 by Damon Lynch
Use PIL image instead of pixbuf for cairo custom cell renderer
418
        image_x = x + ((w - image_w) / 2)
329 by Damon Lynch
Display thumbnails correctly (ellipsize & positioning); remove left and right panes; show preview image in main window
419
        image_y = y + self.image_area_size - image_h
328 by Damon Lynch
Use PIL image instead of pixbuf for cairo custom cell renderer
420
421
        #convert PIL image to format suitable for cairo
330 by Damon Lynch
make preview image resizable
422
        image = create_cairo_image_surface(self.image, image_w, image_h)
327 by Damon Lynch
Preliminary code with custom cell renderer using cairo
423
331 by Damon Lynch
Place checkbox in upper left of thumbnail display; add debugging information to understand cause of PIL core dump
424
        # draw a light grey border of 1px around the image
425
        cairo_context.set_source_rgb(0.66, 0.66, 0.66) #light grey, #a9a9a9
327 by Damon Lynch
Preliminary code with custom cell renderer using cairo
426
        cairo_context.set_line_width(1)
328 by Damon Lynch
Use PIL image instead of pixbuf for cairo custom cell renderer
427
        cairo_context.rectangle(image_x-.5, image_y-.5, image_w+1, image_h+1)
331 by Damon Lynch
Place checkbox in upper left of thumbnail display; add debugging information to understand cause of PIL core dump
428
        cairo_context.stroke()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
429
331 by Damon Lynch
Place checkbox in upper left of thumbnail display; add debugging information to understand cause of PIL core dump
430
        # draw a thin border around each cell
373 by Damon Lynch
auto start download on startup and on device insertion; adjust vertical pane when device is inserted; when auto start is enabled, delay making checkboxes invisible
431
        #~ cairo_context.set_source_rgb(0.33,0.33,0.33)
432
        #~ cairo_context.rectangle(x, y, w, h)
337 by Damon Lynch
Place checkbutton beneath thumbnail text; preliminary functionality for download action sensitivity
433
        #~ cairo_context.stroke()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
434
329 by Damon Lynch
Display thumbnails correctly (ellipsize & positioning); remove left and right panes; show preview image in main window
435
        #place the image
328 by Damon Lynch
Use PIL image instead of pixbuf for cairo custom cell renderer
436
        cairo_context.set_source_surface(image, image_x, image_y)
327 by Damon Lynch
Preliminary code with custom cell renderer using cairo
437
        cairo_context.paint()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
438
329 by Damon Lynch
Display thumbnails correctly (ellipsize & positioning); remove left and right panes; show preview image in main window
439
        #text
440
        context = pangocairo.CairoContext(cairo_context)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
441
329 by Damon Lynch
Display thumbnails correctly (ellipsize & positioning); remove left and right panes; show preview image in main window
442
        text_y = y + self.image_area_size + 10
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
443
        text_w = w - self.icon_width
444
        text_x = x + self.icon_width
445
        #~ context.rectangle(text_x, text_y, text_w, 15)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
446
        #~ context.clip()
447
329 by Damon Lynch
Display thumbnails correctly (ellipsize & positioning); remove left and right panes; show preview image in main window
448
        layout = context.create_layout()
449
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
450
        width = text_w * pango.SCALE
329 by Damon Lynch
Display thumbnails correctly (ellipsize & positioning); remove left and right panes; show preview image in main window
451
        layout.set_width(width)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
452
329 by Damon Lynch
Display thumbnails correctly (ellipsize & positioning); remove left and right panes; show preview image in main window
453
        layout.set_alignment(pango.ALIGN_CENTER)
454
        layout.set_ellipsize(pango.ELLIPSIZE_END)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
455
329 by Damon Lynch
Display thumbnails correctly (ellipsize & positioning); remove left and right panes; show preview image in main window
456
        #font color and size
457
        fg_color = pango.AttrForeground(65535, 65535, 65535, 0, -1)
458
        font_size = pango.AttrSize(8192, 0, -1) # 8 * 1024 = 8192
459
        font_family = pango.AttrFamily('sans', 0, -1)
460
        attr = pango.AttrList()
461
        attr.insert(fg_color)
462
        attr.insert(font_size)
463
        attr.insert(font_family)
464
        layout.set_attributes(attr)
465
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
466
        layout.set_text(self.filename)
329 by Damon Lynch
Display thumbnails correctly (ellipsize & positioning); remove left and right panes; show preview image in main window
467
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
468
        context.move_to(text_x, text_y)
329 by Damon Lynch
Display thumbnails correctly (ellipsize & positioning); remove left and right panes; show preview image in main window
469
        context.show_layout(layout)
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
470
471
        #status
472
        cairo_context.set_source_pixbuf(self.status, x, y + self.image_area_size + 10)
473
        cairo_context.paint()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
474
328 by Damon Lynch
Use PIL image instead of pixbuf for cairo custom cell renderer
475
    def do_get_size(self, widget, cell_area):
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
476
        return (0, 0, self.image_area_size, self.image_area_size + self.text_area_size - self.checkbutton_height + 4)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
477
327 by Damon Lynch
Preliminary code with custom cell renderer using cairo
478
328 by Damon Lynch
Use PIL image instead of pixbuf for cairo custom cell renderer
479
gobject.type_register(ThumbnailCellRenderer)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
480
327 by Damon Lynch
Preliminary code with custom cell renderer using cairo
481
326 by Damon Lynch
experiment with gtk.iconview
482
class ThumbnailDisplay(gtk.IconView):
483
    def __init__(self, parent_app):
484
        gtk.IconView.__init__(self)
373 by Damon Lynch
auto start download on startup and on device insertion; adjust vertical pane when device is inserted; when auto start is enabled, delay making checkboxes invisible
485
        self.set_spacing(0)
486
        self.set_row_spacing(5)
487
        self.set_margin(25)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
488
390 by Damon Lynch
multiple selection of files in main window
489
        self.set_selection_mode(gtk.SELECTION_MULTIPLE)
490
        self.connect('selection-changed', self.on_selection_changed)
491
        self._selected_items = []
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
492
329 by Damon Lynch
Display thumbnails correctly (ellipsize & positioning); remove left and right panes; show preview image in main window
493
        self.rapid_app = parent_app
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
494
326 by Damon Lynch
experiment with gtk.iconview
495
        self.batch_size = 10
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
496
326 by Damon Lynch
experiment with gtk.iconview
497
        self.thumbnail_manager = ThumbnailManager(self.thumbnail_results, self.batch_size)
498
        self.preview_manager = PreviewManager(self.preview_results)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
499
500
        self.treerow_index = {}
501
        self.process_index = {}
502
326 by Damon Lynch
experiment with gtk.iconview
503
        self.rpd_files = {}
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
504
376 by Damon Lynch
adjust vertical pane position when additional devices are inserted after preference change; properly track how many thumbnails need to be generated when auto start is on
505
        self.total_thumbs_to_generate = 0
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
506
        self.thumbnails_generated = 0
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
507
381 by Damon Lynch
when user clicks download and thumbnail generation is still occuring, generate the thumbmnails in the copy file process
508
        # dict of scan_pids that are having thumbnails generated
509
        # value is the thumbnail process id
510
        # this is needed when terminating thumbnailing early such as when
511
        # user clicks download before the thumbnailing is finished
512
        self.generating_thumbnails = {}
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
513
326 by Damon Lynch
experiment with gtk.iconview
514
        self.thumbnails = {}
515
        self.previews = {}
336 by Damon Lynch
generate previews for before and next image when user views preview, increasing responsiveness
516
        self.previews_being_fetched = set()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
517
326 by Damon Lynch
experiment with gtk.iconview
518
        self.stock_photo_thumbnails = tn.PhotoIcons()
519
        self.stock_video_thumbnails = tn.VideoIcons()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
520
331 by Damon Lynch
Place checkbox in upper left of thumbnail display; add debugging information to understand cause of PIL core dump
521
        self.SELECTED_COL = 1
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
522
        self.UNIQUE_ID_COL = 2
331 by Damon Lynch
Place checkbox in upper left of thumbnail display; add debugging information to understand cause of PIL core dump
523
        self.TIMESTAMP_COL = 4
352 by Damon Lynch
enable menus
524
        self.FILETYPE_COL = 5
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
525
        self.CHECKBUTTON_VISIBLE_COL = 6
526
        self.DOWNLOAD_STATUS_COL = 7
527
        self.STATUS_ICON_COL = 8
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
528
431.1.11 by Damon Lynch
Fix bug #1014203: create new liststore when clearing the old one, thereby removing the sort, which is super slow.
529
        self._create_liststore()
328 by Damon Lynch
Use PIL image instead of pixbuf for cairo custom cell renderer
530
331 by Damon Lynch
Place checkbox in upper left of thumbnail display; add debugging information to understand cause of PIL core dump
531
        self.clear()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
532
331 by Damon Lynch
Place checkbox in upper left of thumbnail display; add debugging information to understand cause of PIL core dump
533
        checkbutton = gtk.CellRendererToggle()
534
        checkbutton.set_radio(False)
535
        checkbutton.props.activatable = True
536
        checkbutton.props.xalign = 0.0
537
        checkbutton.connect('toggled', self.on_checkbutton_toggled)
337 by Damon Lynch
Place checkbutton beneath thumbnail text; preliminary functionality for download action sensitivity
538
        self.pack_end(checkbutton, expand=False)
373 by Damon Lynch
auto start download on startup and on device insertion; adjust vertical pane when device is inserted; when auto start is enabled, delay making checkboxes invisible
539
331 by Damon Lynch
Place checkbox in upper left of thumbnail display; add debugging information to understand cause of PIL core dump
540
        self.add_attribute(checkbutton, "active", 1)
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
541
        self.add_attribute(checkbutton, "visible", 6)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
542
331 by Damon Lynch
Place checkbox in upper left of thumbnail display; add debugging information to understand cause of PIL core dump
543
        checkbutton_size = checkbutton.get_size(self, None)
544
        checkbutton_height = checkbutton_size[3]
545
        checkbutton_width = checkbutton_size[2]
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
546
331 by Damon Lynch
Place checkbox in upper left of thumbnail display; add debugging information to understand cause of PIL core dump
547
        image = ThumbnailCellRenderer(checkbutton_height)
326 by Damon Lynch
experiment with gtk.iconview
548
        self.pack_start(image, expand=True)
328 by Damon Lynch
Use PIL image instead of pixbuf for cairo custom cell renderer
549
        self.add_attribute(image, "image", 0)
550
        self.add_attribute(image, "filename", 3)
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
551
        self.add_attribute(image, "status", 8)
331 by Damon Lynch
Place checkbox in upper left of thumbnail display; add debugging information to understand cause of PIL core dump
552
553
        #set the background color to a darkish grey
327 by Damon Lynch
Preliminary code with custom cell renderer using cairo
554
        self.modify_base(gtk.STATE_NORMAL, gtk.gdk.Color('#444444'))
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
555
373 by Damon Lynch
auto start download on startup and on device insertion; adjust vertical pane when device is inserted; when auto start is enabled, delay making checkboxes invisible
556
        self.show_all()
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
557
        self._setup_icons()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
558
338 by Damon Lynch
Delay thumbnail sort until scan is complete (much faster)
559
        self.connect('item-activated', self.on_item_activated)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
560
431.1.11 by Damon Lynch
Fix bug #1014203: create new liststore when clearing the old one, thereby removing the sort, which is super slow.
561
    def _create_liststore(self):
562
        """
563
        Creates the default list store to hold the icons
564
        """
565
        self.liststore = gtk.ListStore(
566
             gobject.TYPE_PYOBJECT, # 0 PIL thumbnail
567
             gobject.TYPE_BOOLEAN,  # 1 selected or not
568
             str,                   # 2 unique id
569
             str,                   # 3 file name
570
             int,                   # 4 timestamp for sorting, converted float
571
             int,                   # 5 file type i.e. photo or video
572
             gobject.TYPE_BOOLEAN,  # 6 visibility of checkbutton
573
             int,                   # 7 status of download
574
             gtk.gdk.Pixbuf,        # 8 status icon
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
575
             )
576
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
577
    def _setup_icons(self):
578
        # icons to be displayed in status column
579
580
        size = 16
581
        # standard icons
582
        failed = self.render_icon(gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_MENU)
583
        self.download_failed_icon = failed.scale_simple(size, size, gtk.gdk.INTERP_HYPER)
584
        error = self.render_icon(gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_MENU)
585
        self.error_icon = error.scale_simple(size, size, gtk.gdk.INTERP_HYPER)
586
        warning = self.render_icon(gtk.STOCK_DIALOG_WARNING, gtk.ICON_SIZE_MENU)
587
        self.warning_icon = warning.scale_simple(size, size, gtk.gdk.INTERP_HYPER)
588
589
        # Rapid Photo Downloader specific icons
590
        self.downloaded_icon = gtk.gdk.pixbuf_new_from_file_at_size(
591
               paths.share_dir('glade3/rapid-photo-downloader-downloaded.svg'),
592
               size, size)
593
        self.download_pending_icon = gtk.gdk.pixbuf_new_from_file_at_size(
373 by Damon Lynch
auto start download on startup and on device insertion; adjust vertical pane when device is inserted; when auto start is enabled, delay making checkboxes invisible
594
               paths.share_dir('glade3/rapid-photo-downloader-download-pending.png'),
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
595
               size, size)
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
596
        self.downloaded_with_warning_icon = gtk.gdk.pixbuf_new_from_file_at_size(
597
               paths.share_dir('glade3/rapid-photo-downloader-downloaded-with-warning.svg'),
598
               size, size)
599
        self.downloaded_with_error_icon = gtk.gdk.pixbuf_new_from_file_at_size(
600
               paths.share_dir('glade3/rapid-photo-downloader-downloaded-with-error.svg'),
601
               size, size)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
602
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
603
        # make the not yet downloaded icon a transparent square
604
        self.not_downloaded_icon = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, 16, 16)
605
        self.not_downloaded_icon.fill(0xffffffff)
606
        self.not_downloaded_icon = self.not_downloaded_icon.add_alpha(True, chr(255), chr(255), chr(255))
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
607
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
608
    def get_status_icon(self, status):
609
        """
610
        Returns the correct icon, based on the status
611
        """
612
        if status == STATUS_WARNING:
613
            status_icon = self.warning_icon
614
        elif status == STATUS_CANNOT_DOWNLOAD:
615
            status_icon = self.error_icon
616
        elif status == STATUS_DOWNLOADED:
617
            status_icon =  self.downloaded_icon
618
        elif status == STATUS_NOT_DOWNLOADED:
619
            status_icon = self.not_downloaded_icon
620
        elif status in [STATUS_DOWNLOADED_WITH_WARNING, STATUS_BACKUP_PROBLEM]:
621
            status_icon = self.downloaded_with_warning_icon
622
        elif status in [STATUS_DOWNLOAD_FAILED, STATUS_DOWNLOAD_AND_BACKUP_FAILED]:
623
            status_icon = self.downloaded_with_error_icon
624
        elif status == STATUS_DOWNLOAD_PENDING:
625
            status_icon = self.download_pending_icon
626
        else:
627
            logger.critical("FIXME: unknown status: %s", status)
628
            status_icon = self.not_downloaded_icon
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
629
        return status_icon
630
338 by Damon Lynch
Delay thumbnail sort until scan is complete (much faster)
631
    def sort_by_timestamp(self):
632
        self.liststore.set_sort_column_id(self.TIMESTAMP_COL, gtk.SORT_ASCENDING)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
633
390 by Damon Lynch
multiple selection of files in main window
634
    def on_selection_changed(self, iconview):
452 by Damon Lynch
Fixed bug #909405: Allow selections by row (and not GTK default by square) when user is dragging the mouse or using the keyboard to select.
635
        """
636
        Allow selections by row (and not GTK default by square) when user is
637
        dragging the mouse or using the keyboard to select
638
        """
639
        selections = self.get_selected_items()
640
        if len(selections) > 1:
641
            previous_sel = selections[0][0] + 1
642
            # seleted items list always starts with the highest selected item
643
            for selection in selections:
644
                current_sel = selection[0]
645
                if current_sel <> (previous_sel-1):
646
                    for i in range(previous_sel-1, current_sel, -1):
647
                        self.select_path(i)
648
                previous_sel = current_sel
390 by Damon Lynch
multiple selection of files in main window
649
        self._selected_items = self.get_selected_items()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
650
331 by Damon Lynch
Place checkbox in upper left of thumbnail display; add debugging information to understand cause of PIL core dump
651
    def on_checkbutton_toggled(self, cellrenderertoggle, path):
391 by Damon Lynch
0.4.0 release
652
        paths = [p[0] for p in self._selected_items]
653
        if int(path) not in paths:
390 by Damon Lynch
multiple selection of files in main window
654
            self._selected_items = [path,]
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
655
390 by Damon Lynch
multiple selection of files in main window
656
        for path in self._selected_items:
657
            iter = self.liststore.get_iter(path)
391 by Damon Lynch
0.4.0 release
658
            status = self.liststore.get_value(iter, self.DOWNLOAD_STATUS_COL)
659
            if status == STATUS_NOT_DOWNLOADED:
660
                self.liststore.set_value(iter, self.SELECTED_COL, not cellrenderertoggle.get_active())
390 by Damon Lynch
multiple selection of files in main window
661
            self.select_path(path)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
662
337 by Damon Lynch
Place checkbutton beneath thumbnail text; preliminary functionality for download action sensitivity
663
        self.rapid_app.set_download_action_sensitivity()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
664
665
333 by Damon Lynch
Make "Include in download" checkbutton functional
666
    def set_selected(self, unique_id, value):
667
        iter = self.get_iter_from_unique_id(unique_id)
668
        self.liststore.set_value(iter, self.SELECTED_COL, value)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
669
376 by Damon Lynch
adjust vertical pane position when additional devices are inserted after preference change; properly track how many thumbnails need to be generated when auto start is on
670
    def add_file(self, rpd_file, generate_thumbnail):
326 by Damon Lynch
experiment with gtk.iconview
671
672
        thumbnail_icon = self.get_stock_icon(rpd_file.file_type)
673
        unique_id = rpd_file.unique_id
674
        scan_pid = rpd_file.scan_pid
675
331 by Damon Lynch
Place checkbox in upper left of thumbnail display; add debugging information to understand cause of PIL core dump
676
        timestamp = int(rpd_file.modification_time)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
677
326 by Damon Lynch
experiment with gtk.iconview
678
        iter = self.liststore.append((thumbnail_icon,
331 by Damon Lynch
Place checkbox in upper left of thumbnail display; add debugging information to understand cause of PIL core dump
679
                                      True,
328 by Damon Lynch
Use PIL image instead of pixbuf for cairo custom cell renderer
680
                                      unique_id,
331 by Damon Lynch
Place checkbox in upper left of thumbnail display; add debugging information to understand cause of PIL core dump
681
                                      rpd_file.display_name,
352 by Damon Lynch
enable menus
682
                                      timestamp,
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
683
                                      rpd_file.file_type,
684
                                      True,
685
                                      STATUS_NOT_DOWNLOADED,
686
                                      self.not_downloaded_icon
328 by Damon Lynch
Use PIL image instead of pixbuf for cairo custom cell renderer
687
                                      ))
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
688
326 by Damon Lynch
experiment with gtk.iconview
689
        path = self.liststore.get_path(iter)
690
        treerowref = gtk.TreeRowReference(self.liststore, path)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
691
326 by Damon Lynch
experiment with gtk.iconview
692
        if scan_pid in self.process_index:
359 by Damon Lynch
If necessary, get thumbnail from downloaded file. Indicate download is pending.
693
            self.process_index[scan_pid].append(unique_id)
326 by Damon Lynch
experiment with gtk.iconview
694
        else:
359 by Damon Lynch
If necessary, get thumbnail from downloaded file. Indicate download is pending.
695
            self.process_index[scan_pid] = [unique_id,]
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
696
326 by Damon Lynch
experiment with gtk.iconview
697
        self.treerow_index[unique_id] = treerowref
698
        self.rpd_files[unique_id] = rpd_file
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
699
376 by Damon Lynch
adjust vertical pane position when additional devices are inserted after preference change; properly track how many thumbnails need to be generated when auto start is on
700
        if generate_thumbnail:
701
            self.total_thumbs_to_generate += 1
326 by Damon Lynch
experiment with gtk.iconview
702
361 by Damon Lynch
Sample file names and subfolders are now displayed in the preferences dialog
703
    def get_sample_file(self, file_type):
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
704
        """Returns an rpd_file for of a given file type, or None if it does
361 by Damon Lynch
Sample file names and subfolders are now displayed in the preferences dialog
705
        not exist"""
706
        for unique_id, rpd_file in self.rpd_files.iteritems():
707
            if rpd_file.file_type == file_type:
708
                if rpd_file.status <> STATUS_CANNOT_DOWNLOAD:
709
                    return rpd_file
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
710
361 by Damon Lynch
Sample file names and subfolders are now displayed in the preferences dialog
711
        return None
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
712
334 by Damon Lynch
Next and previous image controls
713
    def get_unique_id_from_iter(self, iter):
333 by Damon Lynch
Make "Include in download" checkbutton functional
714
        return self.liststore.get_value(iter, 2)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
715
333 by Damon Lynch
Make "Include in download" checkbutton functional
716
    def get_iter_from_unique_id(self, unique_id):
717
        treerowref = self.treerow_index[unique_id]
718
        path = treerowref.get_path()
719
        return self.liststore.get_iter(path)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
720
721
    def on_item_activated(self, iconview, path):
329 by Damon Lynch
Display thumbnails correctly (ellipsize & positioning); remove left and right panes; show preview image in main window
722
        """
723
        """
724
        iter = self.liststore.get_iter(path)
334 by Damon Lynch
Next and previous image controls
725
        self.show_preview(iter=iter)
336 by Damon Lynch
generate previews for before and next image when user views preview, increasing responsiveness
726
        self.advance_get_preview_image(iter)
326 by Damon Lynch
experiment with gtk.iconview
727
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
728
336 by Damon Lynch
generate previews for before and next image when user views preview, increasing responsiveness
729
    def _get_preview(self, unique_id, rpd_file):
730
        if unique_id not in self.previews_being_fetched:
359 by Damon Lynch
If necessary, get thumbnail from downloaded file. Indicate download is pending.
731
            #check if preview should be from a downloaded file, or the source
732
            if rpd_file.status in DOWNLOADED:
733
                file_location = rpd_file.download_full_file_name
420 by Damon Lynch
Download THM video thumbnail files
734
                thm_file_name = rpd_file.download_thm_full_name
359 by Damon Lynch
If necessary, get thumbnail from downloaded file. Indicate download is pending.
735
            else:
736
                file_location = rpd_file.full_file_name
420 by Damon Lynch
Download THM video thumbnail files
737
                thm_file_name = rpd_file.thm_full_name
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
738
359 by Damon Lynch
If necessary, get thumbnail from downloaded file. Indicate download is pending.
739
            self.preview_manager.get_preview(unique_id, file_location,
420 by Damon Lynch
Download THM video thumbnail files
740
                                            thm_file_name,
336 by Damon Lynch
generate previews for before and next image when user views preview, increasing responsiveness
741
                                            rpd_file.file_type, size_max=None,)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
742
336 by Damon Lynch
generate previews for before and next image when user views preview, increasing responsiveness
743
            self.previews_being_fetched.add(unique_id)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
744
334 by Damon Lynch
Next and previous image controls
745
    def show_preview(self, unique_id=None, iter=None):
457 by Damon Lynch
Added support for MPO files (3D) and fixed some untranslated strings in the GUI
746
        if len(self.liststore):
747
            if unique_id is not None:
748
                iter = self.get_iter_from_unique_id(unique_id)
749
            elif iter is not None:
750
                unique_id = self.get_unique_id_from_iter(iter)
751
            else:
752
                # neither an iter or a unique_id were passed
753
                # use iter from first selected file
754
                # if none is selected, choose the first file
755
                selected = self.get_selected_items()
756
                if selected:
757
                    path = selected[0]
758
                else:
759
                    path = 0
760
                iter = self.liststore.get_iter(path)
761
                unique_id = self.get_unique_id_from_iter(iter)
762
763
764
            rpd_file = self.rpd_files[unique_id]
765
766
            if unique_id in self.previews:
767
                preview_image = self.previews[unique_id]
768
            else:
769
                # request daemon process to get a full size thumbnail
770
                self._get_preview(unique_id, rpd_file)
771
                if unique_id in self.thumbnails:
772
                    preview_image = self.thumbnails[unique_id]
773
                else:
774
                    preview_image = self.get_stock_icon(rpd_file.file_type)
775
776
            checked = self.liststore.get_value(iter, self.SELECTED_COL)
777
            include_checkbutton_visible = rpd_file.status == STATUS_NOT_DOWNLOADED
778
            self.rapid_app.show_preview_image(unique_id, preview_image,
779
                                                include_checkbutton_visible, checked)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
780
336 by Damon Lynch
generate previews for before and next image when user views preview, increasing responsiveness
781
    def _get_next_iter(self, iter):
334 by Damon Lynch
Next and previous image controls
782
        iter = self.liststore.iter_next(iter)
783
        if iter is None:
784
            iter = self.liststore.get_iter_first()
336 by Damon Lynch
generate previews for before and next image when user views preview, increasing responsiveness
785
        return iter
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
786
336 by Damon Lynch
generate previews for before and next image when user views preview, increasing responsiveness
787
    def _get_prev_iter(self, iter):
334 by Damon Lynch
Next and previous image controls
788
        row = self.liststore.get_path(iter)[0]
789
        if row == 0:
790
            row = len(self.liststore)-1
791
        else:
792
            row -= 1
793
        iter = self.liststore.get_iter(row)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
794
        return iter
795
336 by Damon Lynch
generate previews for before and next image when user views preview, increasing responsiveness
796
    def show_next_image(self, unique_id):
797
        iter = self.get_iter_from_unique_id(unique_id)
798
        iter = self._get_next_iter(iter)
799
800
        if iter is not None:
801
            self.show_preview(iter=iter)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
802
336 by Damon Lynch
generate previews for before and next image when user views preview, increasing responsiveness
803
            # cache next image
804
            self.advance_get_preview_image(iter, prev=False, next=True)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
805
336 by Damon Lynch
generate previews for before and next image when user views preview, increasing responsiveness
806
    def show_prev_image(self, unique_id):
807
        iter = self.get_iter_from_unique_id(unique_id)
808
        iter = self._get_prev_iter(iter)
809
810
        if iter is not None:
811
            self.show_preview(iter=iter)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
812
336 by Damon Lynch
generate previews for before and next image when user views preview, increasing responsiveness
813
            # cache next image
814
            self.advance_get_preview_image(iter, prev=True, next=False)
815
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
816
336 by Damon Lynch
generate previews for before and next image when user views preview, increasing responsiveness
817
    def advance_get_preview_image(self, iter, prev=True, next=True):
818
        unique_ids = []
819
        if next:
820
            next_iter = self._get_next_iter(iter)
821
            unique_ids.append(self.get_unique_id_from_iter(next_iter))
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
822
336 by Damon Lynch
generate previews for before and next image when user views preview, increasing responsiveness
823
        if prev:
824
            prev_iter = self._get_prev_iter(iter)
825
            unique_ids.append(self.get_unique_id_from_iter(prev_iter))
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
826
336 by Damon Lynch
generate previews for before and next image when user views preview, increasing responsiveness
827
        for unique_id in unique_ids:
828
            if not unique_id in self.previews:
829
                rpd_file = self.rpd_files[unique_id]
830
                self._get_preview(unique_id, rpd_file)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
831
352 by Damon Lynch
enable menus
832
    def check_all(self, check_all, file_type=None):
335 by Damon Lynch
check all / uncheck all button functionality; preliminary device mounting support, icon in device treeview display
833
        for row in self.liststore:
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
834
            if row[self.CHECKBUTTON_VISIBLE_COL]:
835
                if file_type is not None:
836
                    if row[self.FILETYPE_COL] == file_type:
837
                        row[self.SELECTED_COL] = check_all
838
                else:
352 by Damon Lynch
enable menus
839
                    row[self.SELECTED_COL] = check_all
337 by Damon Lynch
Place checkbutton beneath thumbnail text; preliminary functionality for download action sensitivity
840
        self.rapid_app.set_download_action_sensitivity()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
841
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
842
    def files_are_checked_to_download(self):
337 by Damon Lynch
Place checkbutton beneath thumbnail text; preliminary functionality for download action sensitivity
843
        """
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
844
        Returns True if there is any file that the user has indicated they
845
        intend to download, else returns False.
337 by Damon Lynch
Place checkbutton beneath thumbnail text; preliminary functionality for download action sensitivity
846
        """
847
        for row in self.liststore:
848
            if row[self.SELECTED_COL]:
359 by Damon Lynch
If necessary, get thumbnail from downloaded file. Indicate download is pending.
849
                rpd_file = self.rpd_files[row[self.UNIQUE_ID_COL]]
850
                if rpd_file.status not in DOWNLOADED:
851
                    return True
337 by Damon Lynch
Place checkbutton beneath thumbnail text; preliminary functionality for download action sensitivity
852
        return False
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
853
373 by Damon Lynch
auto start download on startup and on device insertion; adjust vertical pane when device is inserted; when auto start is enabled, delay making checkboxes invisible
854
    def get_files_checked_for_download(self, scan_pid):
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
855
        """
856
        Returns a dict of scan ids and associated files the user has indicated
857
        they want to download
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
858
373 by Damon Lynch
auto start download on startup and on device insertion; adjust vertical pane when device is inserted; when auto start is enabled, delay making checkboxes invisible
859
        If scan_pid is not None, then returns only those files from that scan_pid
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
860
        """
861
        files = dict()
373 by Damon Lynch
auto start download on startup and on device insertion; adjust vertical pane when device is inserted; when auto start is enabled, delay making checkboxes invisible
862
        if scan_pid is None:
863
            for row in self.liststore:
864
                if row[self.SELECTED_COL]:
865
                    rpd_file = self.rpd_files[row[self.UNIQUE_ID_COL]]
866
                    if rpd_file.status not in DOWNLOADED:
867
                        scan_pid = rpd_file.scan_pid
868
                        if scan_pid in files:
869
                            files[scan_pid].append(rpd_file)
870
                        else:
871
                            files[scan_pid] = [rpd_file,]
872
        else:
873
            files[scan_pid] = []
874
            for unique_id in self.process_index[scan_pid]:
875
                rpd_file = self.rpd_files[unique_id]
359 by Damon Lynch
If necessary, get thumbnail from downloaded file. Indicate download is pending.
876
                if rpd_file.status not in DOWNLOADED:
373 by Damon Lynch
auto start download on startup and on device insertion; adjust vertical pane when device is inserted; when auto start is enabled, delay making checkboxes invisible
877
                    iter = self.get_iter_from_unique_id(unique_id)
878
                    if self.liststore.get_value(iter, self.SELECTED_COL):
359 by Damon Lynch
If necessary, get thumbnail from downloaded file. Indicate download is pending.
879
                        files[scan_pid].append(rpd_file)
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
880
        return files
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
881
375 by Damon Lynch
auto unmount and system notifications on download completion
882
    def get_no_files_remaining(self, scan_pid):
883
        """
884
        Returns the number of files that have not yet been downloaded for the
885
        scan_pid
886
        """
887
        i = 0
888
        for unique_id in self.process_index[scan_pid]:
889
            rpd_file = self.rpd_files[unique_id]
890
            if rpd_file.status == STATUS_NOT_DOWNLOADED:
891
                i += 1
892
        return i
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
893
375 by Damon Lynch
auto unmount and system notifications on download completion
894
    def files_remain_to_download(self):
895
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
896
        Returns True if any files remain that are not downloaded, else returns
375 by Damon Lynch
auto unmount and system notifications on download completion
897
        False
898
        """
899
        for row in self.liststore:
900
            if row[self.DOWNLOAD_STATUS_COL] == STATUS_NOT_DOWNLOADED:
901
                return True
902
        return False
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
903
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
904
359 by Damon Lynch
If necessary, get thumbnail from downloaded file. Indicate download is pending.
905
    def mark_download_pending(self, files_by_scan_pid):
906
        """
907
        Sets status to download pending and updates thumbnails display
908
        """
909
        for scan_pid in files_by_scan_pid:
910
            for rpd_file in files_by_scan_pid[scan_pid]:
911
                unique_id = rpd_file.unique_id
912
                self.rpd_files[unique_id].status = STATUS_DOWNLOAD_PENDING
913
                iter = self.get_iter_from_unique_id(unique_id)
373 by Damon Lynch
auto start download on startup and on device insertion; adjust vertical pane when device is inserted; when auto start is enabled, delay making checkboxes invisible
914
                if not self.rapid_app.auto_start_is_on:
915
                    # don't make the checkbox invisible immediately when on auto start
916
                    # otherwise the box can be rendred at the wrong size, as it is
917
                    # realized after the checkbox has already been made invisible
918
                    self.liststore.set_value(iter, self.CHECKBUTTON_VISIBLE_COL, False)
359 by Damon Lynch
If necessary, get thumbnail from downloaded file. Indicate download is pending.
919
                self.liststore.set_value(iter, self.SELECTED_COL, False)
920
                self.liststore.set_value(iter, self.DOWNLOAD_STATUS_COL, STATUS_DOWNLOAD_PENDING)
921
                icon = self.get_status_icon(STATUS_DOWNLOAD_PENDING)
922
                self.liststore.set_value(iter, self.STATUS_ICON_COL, icon)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
923
334 by Damon Lynch
Next and previous image controls
924
    def select_image(self, unique_id):
925
        iter = self.get_iter_from_unique_id(unique_id)
926
        path = self.liststore.get_path(iter)
927
        self.select_path(path)
335 by Damon Lynch
check all / uncheck all button functionality; preliminary device mounting support, icon in device treeview display
928
        self.scroll_to_path(path, use_align=False, row_align=0.5, col_align=0.5)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
929
326 by Damon Lynch
experiment with gtk.iconview
930
    def get_stock_icon(self, file_type):
931
        if file_type == rpdfile.FILE_TYPE_PHOTO:
932
            return self.stock_photo_thumbnails.stock_thumbnail_image_icon
933
        else:
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
934
            return self.stock_video_thumbnails.stock_thumbnail_image_icon
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
935
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
936
    def update_status_post_download(self, rpd_file):
937
        iter = self.get_iter_from_unique_id(rpd_file.unique_id)
359 by Damon Lynch
If necessary, get thumbnail from downloaded file. Indicate download is pending.
938
        self.liststore.set_value(iter, self.DOWNLOAD_STATUS_COL, rpd_file.status)
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
939
        icon = self.get_status_icon(rpd_file.status)
940
        self.liststore.set_value(iter, self.STATUS_ICON_COL, icon)
373 by Damon Lynch
auto start download on startup and on device insertion; adjust vertical pane when device is inserted; when auto start is enabled, delay making checkboxes invisible
941
        self.liststore.set_value(iter, self.CHECKBUTTON_VISIBLE_COL, False)
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
942
        self.rpd_files[rpd_file.unique_id] = rpd_file
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
943
326 by Damon Lynch
experiment with gtk.iconview
944
    def generate_thumbnails(self, scan_pid):
945
        """Initiate thumbnail generation for files scanned in one process
946
        """
410 by Damon Lynch
Fixed crash when generating thumbnail images, reported by Red Hat
947
        if scan_pid in self.process_index:
948
            rpd_files = [self.rpd_files[unique_id] for unique_id in self.process_index[scan_pid]]
949
            thumbnail_pid = self.thumbnail_manager.add_task((scan_pid, rpd_files))
950
            self.generating_thumbnails[scan_pid] = thumbnail_pid
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
951
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
952
    def _set_thumbnail(self, unique_id, icon):
953
        treerowref = self.treerow_index[unique_id]
954
        path = treerowref.get_path()
955
        iter = self.liststore.get_iter(path)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
956
        self.liststore.set(iter, 0, icon)
957
326 by Damon Lynch
experiment with gtk.iconview
958
    def update_thumbnail(self, thumbnail_data):
959
        """
335 by Damon Lynch
check all / uncheck all button functionality; preliminary device mounting support, icon in device treeview display
960
        Takes the generated thumbnail and updates the display
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
961
335 by Damon Lynch
check all / uncheck all button functionality; preliminary device mounting support, icon in device treeview display
962
        If the thumbnail_data includes a second image, that is used to
963
        update the thumbnail list using the unique_id
326 by Damon Lynch
experiment with gtk.iconview
964
        """
965
        unique_id = thumbnail_data[0]
966
        thumbnail_icon = thumbnail_data[1]
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
967
326 by Damon Lynch
experiment with gtk.iconview
968
        if thumbnail_icon is not None:
335 by Damon Lynch
check all / uncheck all button functionality; preliminary device mounting support, icon in device treeview display
969
            # get the thumbnail icon in PIL format
328 by Damon Lynch
Use PIL image instead of pixbuf for cairo custom cell renderer
970
            thumbnail_icon = thumbnail_icon.get_image()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
971
326 by Damon Lynch
experiment with gtk.iconview
972
            if thumbnail_icon:
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
973
                self._set_thumbnail(unique_id, thumbnail_icon)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
974
326 by Damon Lynch
experiment with gtk.iconview
975
            if len(thumbnail_data) > 2:
976
                # get the 2nd image in PIL format
977
                self.thumbnails[unique_id] = thumbnail_data[2].get_image()
978
381 by Damon Lynch
when user clicks download and thumbnail generation is still occuring, generate the thumbmnails in the copy file process
979
    def terminate_thumbnail_generation(self, scan_pid):
980
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
981
        Terminates thumbnail generation if thumbnails are currently
381 by Damon Lynch
when user clicks download and thumbnail generation is still occuring, generate the thumbmnails in the copy file process
982
        being generated for this scan_pid
983
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
984
381 by Damon Lynch
when user clicks download and thumbnail generation is still occuring, generate the thumbmnails in the copy file process
985
        if scan_pid in self.generating_thumbnails:
986
            terminated = True
987
            self.thumbnail_manager.terminate_process(
988
                                        self.generating_thumbnails[scan_pid])
989
            del self.generating_thumbnails[scan_pid]
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
990
381 by Damon Lynch
when user clicks download and thumbnail generation is still occuring, generate the thumbmnails in the copy file process
991
            if len(self.generating_thumbnails) == 0:
992
                self._reset_thumbnail_tracking_and_display()
993
        else:
994
            terminated = False
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
995
381 by Damon Lynch
when user clicks download and thumbnail generation is still occuring, generate the thumbmnails in the copy file process
996
        return terminated
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
997
381 by Damon Lynch
when user clicks download and thumbnail generation is still occuring, generate the thumbmnails in the copy file process
998
    def mark_thumbnails_needed(self, rpd_files):
999
        for rpd_file in rpd_files:
1000
            if rpd_file.unique_id not in self.thumbnails:
1001
                rpd_file.generate_thumbnail = True
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1002
381 by Damon Lynch
when user clicks download and thumbnail generation is still occuring, generate the thumbmnails in the copy file process
1003
    def _reset_thumbnail_tracking_and_display(self):
1004
        self.rapid_app.download_progressbar.set_fraction(0.0)
1005
        self.rapid_app.download_progressbar.set_text('')
1006
        self.thumbnails_generated = 0
1007
        self.total_thumbs_to_generate = 0
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1008
326 by Damon Lynch
experiment with gtk.iconview
1009
    def thumbnail_results(self, source, condition):
1010
        connection = self.thumbnail_manager.get_pipe(source)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1011
326 by Damon Lynch
experiment with gtk.iconview
1012
        conn_type, data = connection.recv()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1013
326 by Damon Lynch
experiment with gtk.iconview
1014
        if conn_type == rpdmp.CONN_COMPLETE:
381 by Damon Lynch
when user clicks download and thumbnail generation is still occuring, generate the thumbmnails in the copy file process
1015
            scan_pid = data
1016
            del self.generating_thumbnails[scan_pid]
326 by Damon Lynch
experiment with gtk.iconview
1017
            connection.close()
1018
            return False
1019
        else:
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1020
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
1021
            for thumbnail_data in data:
1022
                self.update_thumbnail(thumbnail_data)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1023
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
1024
            self.thumbnails_generated += len(data)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1025
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
1026
            # clear progress bar information if all thumbnails have been
1027
            # extracted
376 by Damon Lynch
adjust vertical pane position when additional devices are inserted after preference change; properly track how many thumbnails need to be generated when auto start is on
1028
            if self.thumbnails_generated == self.total_thumbs_to_generate:
381 by Damon Lynch
when user clicks download and thumbnail generation is still occuring, generate the thumbmnails in the copy file process
1029
                self._reset_thumbnail_tracking_and_display()
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
1030
            else:
381 by Damon Lynch
when user clicks download and thumbnail generation is still occuring, generate the thumbmnails in the copy file process
1031
                if self.total_thumbs_to_generate:
1032
                    self.rapid_app.download_progressbar.set_fraction(
1033
                        float(self.thumbnails_generated) / self.total_thumbs_to_generate)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1034
1035
326 by Damon Lynch
experiment with gtk.iconview
1036
        return True
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1037
326 by Damon Lynch
experiment with gtk.iconview
1038
    def preview_results(self, unique_id, preview_full_size, preview_small):
329 by Damon Lynch
Display thumbnails correctly (ellipsize & positioning); remove left and right panes; show preview image in main window
1039
        """
1040
        Receive a full size preview image and update
1041
        """
336 by Damon Lynch
generate previews for before and next image when user views preview, increasing responsiveness
1042
        self.previews_being_fetched.remove(unique_id)
1043
        if preview_full_size:
1044
            preview_image = preview_full_size.get_image()
1045
            self.previews[unique_id] = preview_image
1046
            self.rapid_app.update_preview_image(unique_id, preview_image)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1047
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1048
            # user can turn off option for thumbnail generation after a scan
409 by Damon Lynch
Fixed crash when displaying a preview of file without an extracted
1049
            if unique_id not in self.thumbnails and preview_small is not None:
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1050
                self._set_thumbnail(unique_id, preview_small.get_image())
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1051
1052
359 by Damon Lynch
If necessary, get thumbnail from downloaded file. Indicate download is pending.
1053
    def clear_all(self, scan_pid=None, keep_downloaded_files=False):
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1054
        """
1055
        Removes files from display and internal tracking.
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1056
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1057
        If scan_pid is not None, then only files matching that scan_pid will
1058
        be removed. Otherwise, everything will be removed.
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1059
359 by Damon Lynch
If necessary, get thumbnail from downloaded file. Indicate download is pending.
1060
        If keep_downloaded_files is True, files will not be removed if they
1061
        have been downloaded.
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1062
        """
359 by Damon Lynch
If necessary, get thumbnail from downloaded file. Indicate download is pending.
1063
        if scan_pid is None and not keep_downloaded_files:
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1064
431.1.11 by Damon Lynch
Fix bug #1014203: create new liststore when clearing the old one, thereby removing the sort, which is super slow.
1065
            # Here it is critically important to create a brand new liststore,
1066
            # because the old one is set to be sorted, which is extremely slow.
431.1.13 by Damon Lynch
Updated translations
1067
            logger.debug("Creating new thumbnails model")
431.1.11 by Damon Lynch
Fix bug #1014203: create new liststore when clearing the old one, thereby removing the sort, which is super slow.
1068
            self.set_model(None)
1069
            self._create_liststore()
1070
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1071
            self.treerow_index = {}
1072
            self.process_index = {}
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1073
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1074
            self.rpd_files = {}
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
1075
        else:
349 by Damon Lynch
Implement mount and unmount callbacks and fix use device dialog
1076
            if scan_pid in self.process_index:
359 by Damon Lynch
If necessary, get thumbnail from downloaded file. Indicate download is pending.
1077
                for unique_id in self.process_index[scan_pid]:
1078
                    rpd_file = self.rpd_files[unique_id]
1079
                    if not keep_downloaded_files or not rpd_file.status in DOWNLOADED:
1080
                        treerowref = self.treerow_index[rpd_file.unique_id]
1081
                        path = treerowref.get_path()
1082
                        iter = self.liststore.get_iter(path)
1083
                        self.liststore.remove(iter)
1084
                        del self.treerow_index[rpd_file.unique_id]
1085
                        del self.rpd_files[rpd_file.unique_id]
1086
                if not keep_downloaded_files or not len(self.process_index[scan_pid]):
1087
                    del self.process_index[scan_pid]
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1088
431.1.11 by Damon Lynch
Fix bug #1014203: create new liststore when clearing the old one, thereby removing the sort, which is super slow.
1089
    def display_thumbnails(self):
1090
        self.set_model(self.liststore)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1091
313 by Damon Lynch
Fix pixbuf problem using numpy array, and preliminary & incomplete thumbnail generator work
1092
class TaskManager:
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
1093
    def __init__(self, results_callback, batch_size):
1094
        self.results_callback = results_callback
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1095
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1096
        # List of actual process, it's terminate_queue, and it's run_event
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
1097
        self._processes = []
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1098
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
1099
        self._pipes = {}
1100
        self.batch_size = batch_size
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1101
350 by Damon Lynch
Add pause functionality, and bug fixes in early termination of processes.
1102
        self.paused = False
379 by Damon Lynch
check if scans are occuring by using manual counter, not no active processes (they can take a while to finish)
1103
        self.no_tasks = 0
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1104
1105
313 by Damon Lynch
Fix pixbuf problem using numpy array, and preliminary & incomplete thumbnail generator work
1106
    def add_task(self, task):
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1107
        pid = self._setup_task(task)
362 by Damon Lynch
Leave file preview mode when download devices are changed in the
1108
        logger.debug("TaskManager PID: %s", pid)
379 by Damon Lynch
check if scans are occuring by using manual counter, not no active processes (they can take a while to finish)
1109
        self.no_tasks += 1
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1110
        return pid
313 by Damon Lynch
Fix pixbuf problem using numpy array, and preliminary & incomplete thumbnail generator work
1111
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1112
313 by Damon Lynch
Fix pixbuf problem using numpy array, and preliminary & incomplete thumbnail generator work
1113
    def _setup_task(self, task):
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1114
        task_results_conn, task_process_conn = self._setup_pipe()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1115
313 by Damon Lynch
Fix pixbuf problem using numpy array, and preliminary & incomplete thumbnail generator work
1116
        source = task_results_conn.fileno()
1117
        self._pipes[source] = task_results_conn
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
1118
        gobject.io_add_watch(source, gobject.IO_IN, self.results_callback)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1119
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
1120
        terminate_queue = Queue()
1121
        run_event = Event()
1122
        run_event.set()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1123
1124
        return self._initiate_task(task, task_results_conn, task_process_conn,
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1125
                                   terminate_queue, run_event)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1126
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1127
    def _setup_pipe(self):
1128
        return Pipe(duplex=False)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1129
313 by Damon Lynch
Fix pixbuf problem using numpy array, and preliminary & incomplete thumbnail generator work
1130
    def _initiate_task(self, task, task_process_conn, terminate_queue, run_event):
321 by Damon Lynch
Preliminary work to integrate device collection treeview
1131
        logger.error("Implement child class method!")
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1132
1133
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
1134
    def processes(self):
1135
        for i in range(len(self._processes)):
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1136
            yield self._processes[i]
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1137
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
1138
    def start(self):
350 by Damon Lynch
Add pause functionality, and bug fixes in early termination of processes.
1139
        self.paused = False
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
1140
        for scan in self.processes():
1141
            run_event = scan[2]
1142
            if not run_event.is_set():
1143
                run_event.set()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1144
350 by Damon Lynch
Add pause functionality, and bug fixes in early termination of processes.
1145
    def pause(self):
1146
        self.paused = True
1147
        for scan in self.processes():
1148
            run_event = scan[2]
1149
            if run_event.is_set():
1150
                run_event.clear()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1151
381 by Damon Lynch
when user clicks download and thumbnail generation is still occuring, generate the thumbmnails in the copy file process
1152
    def _terminate_process(self, p):
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1153
        self._send_termination_msg(p)
381 by Damon Lynch
when user clicks download and thumbnail generation is still occuring, generate the thumbmnails in the copy file process
1154
        # The process might be paused: let it run
1155
        run_event = p[2]
1156
        if not run_event.is_set():
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1157
            run_event.set()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1158
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1159
    def _send_termination_msg(self, p):
1160
        p[1].put(None)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1161
381 by Damon Lynch
when user clicks download and thumbnail generation is still occuring, generate the thumbmnails in the copy file process
1162
    def terminate_process(self, process_id):
1163
        """
1164
        Send a signal to process with matching process_id that it should
1165
        immediately terminate
1166
        """
1167
        for p in self.processes():
1168
            if p[0].pid == process_id:
1169
                if p[0].is_alive():
1170
                    self._terminate_process(p)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1171
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1172
    def request_termination(self):
1173
        """
1174
        Send a signal to processes that they should immediately terminate
1175
        """
1176
        requested = False
337 by Damon Lynch
Place checkbutton beneath thumbnail text; preliminary functionality for download action sensitivity
1177
        for p in self.processes():
1178
            if p[0].is_alive():
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1179
                requested = True
381 by Damon Lynch
when user clicks download and thumbnail generation is still occuring, generate the thumbmnails in the copy file process
1180
                self._terminate_process(p)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1181
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1182
        return requested
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1183
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1184
    def terminate_forcefully(self):
1185
        """
1186
        Forcefully terminates any running processes. Use with great caution.
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1187
        No cleanup action is performed.
1188
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1189
        As python essential reference (4th edition) says, if the process
1190
        'holds a lock or is involved with interprocess communication,
1191
        terminating it might cause a deadlock or corrupted I/O.'
1192
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1193
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1194
        for p in self.processes():
1195
            if p[0].is_alive():
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1196
                logger.info("Forcefully terminating %s in %s" , p[0].name,
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1197
                                                self.__class__.__name__)
350 by Damon Lynch
Add pause functionality, and bug fixes in early termination of processes.
1198
                p[0].terminate()
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1199
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1200
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
1201
    def get_pipe(self, source):
1202
        return self._pipes[source]
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1203
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1204
    def get_no_active_processes(self):
1205
        """
1206
        Returns how many processes are currently active, i.e. running
1207
        """
1208
        i = 0
1209
        for p in self.processes():
1210
            if p[0].is_alive():
1211
                i += 1
1212
        return i
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
1213
313 by Damon Lynch
Fix pixbuf problem using numpy array, and preliminary & incomplete thumbnail generator work
1214
1215
class ScanManager(TaskManager):
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1216
1217
    def __init__(self, results_callback, batch_size,
326 by Damon Lynch
experiment with gtk.iconview
1218
                 add_device_function):
321 by Damon Lynch
Preliminary work to integrate device collection treeview
1219
        TaskManager.__init__(self, results_callback, batch_size)
1220
        self.add_device_function = add_device_function
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1221
1222
    def _initiate_task(self, task, task_results_conn, task_process_conn,
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1223
                       terminate_queue, run_event):
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1224
402 by Damon Lynch
add ignore paths option; add remove remembered devices to prefs window; add focal_length command line option; updated i18n
1225
        device = task[0]
1226
        ignored_paths = task[1]
1227
        use_re_ignored_paths = task[2]
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1228
402 by Damon Lynch
add ignore paths option; add remove remembered devices to prefs window; add focal_length command line option; updated i18n
1229
        scan = scan_process.Scan(device.get_path(),
1230
                                ignored_paths,
1231
                                use_re_ignored_paths,
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1232
                                self.batch_size,
326 by Damon Lynch
experiment with gtk.iconview
1233
                                task_process_conn, terminate_queue, run_event)
313 by Damon Lynch
Fix pixbuf problem using numpy array, and preliminary & incomplete thumbnail generator work
1234
        scan.start()
1235
        self._processes.append((scan, terminate_queue, run_event))
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1236
        self.add_device_function(scan.pid, device,
321 by Damon Lynch
Preliminary work to integrate device collection treeview
1237
            # This refers to when a device like a hard drive is having its contents scanned,
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1238
            # looking for photos or videos. It is visible initially in the progress bar for each device
321 by Damon Lynch
Preliminary work to integrate device collection treeview
1239
            # (which normally holds "x photos and videos").
1240
            # It maybe displayed only briefly if the contents of the device being scanned is small.
1241
            progress_bar_text=_('scanning...'))
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1242
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1243
        return scan.pid
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1244
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
1245
class CopyFilesManager(TaskManager):
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1246
1247
    def _initiate_task(self, task, task_results_conn,
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1248
                       task_process_conn, terminate_queue, run_event):
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
1249
        photo_download_folder = task[0]
1250
        video_download_folder = task[1]
340 by Damon Lynch
Move download tracking functionality from copyfile process to main program process, in preparation for coming file renaming and backup functionality
1251
        scan_pid = task[2]
1252
        files = task[3]
470 by Damon Lynch
Implementation of file verification for downloaded file, but not yet not the backup
1253
        verify_files = task[4]
1254
        modify_files_during_download = task[5]
1255
        modify_pipe = task[6]
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1256
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
1257
        copy_files = copyfiles.CopyFiles(photo_download_folder,
1258
                                video_download_folder,
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1259
                                files,
470 by Damon Lynch
Implementation of file verification for downloaded file, but not yet not the backup
1260
                                verify_files,
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
1261
                                modify_files_during_download,
1262
                                modify_pipe,
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1263
                                scan_pid, self.batch_size,
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
1264
                                task_process_conn, terminate_queue, run_event)
1265
        copy_files.start()
1266
        self._processes.append((copy_files, terminate_queue, run_event))
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1267
        return copy_files.pid
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1268
313 by Damon Lynch
Fix pixbuf problem using numpy array, and preliminary & incomplete thumbnail generator work
1269
class ThumbnailManager(TaskManager):
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1270
    def _initiate_task(self, task, task_results_conn,
1271
                       task_process_conn, terminate_queue, run_event):
381 by Damon Lynch
when user clicks download and thumbnail generation is still occuring, generate the thumbmnails in the copy file process
1272
        scan_pid = task[0]
1273
        files = task[1]
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1274
        generator = tn.GenerateThumbnails(scan_pid, files, self.batch_size,
1275
                                          task_process_conn, terminate_queue,
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1276
                                          run_event)
313 by Damon Lynch
Fix pixbuf problem using numpy array, and preliminary & incomplete thumbnail generator work
1277
        generator.start()
1278
        self._processes.append((generator, terminate_queue, run_event))
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1279
        return generator.pid
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
1280
1281
class FileModifyManager(TaskManager):
476 by Damon Lynch
Updated Catalan and Spanish translations. Fixed occasional incorrect use of term "backup".
1282
    """Handles the modification or verification of of downloaded files before they are renamed
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
1283
    Duplex, multiprocess, similar to BackupFilesManager
1284
    """
1285
    def __init__(self, results_callback):
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1286
        TaskManager.__init__(self, results_callback=results_callback,
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
1287
                            batch_size=0)
1288
        self.file_modify_by_scan_pid = {}
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1289
1290
    def _initiate_task(self, task, task_results_conn, task_process_conn,
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
1291
                       terminate_queue, run_event):
1292
        scan_pid = task[0]
1293
        auto_rotate_jpeg = task[1]
430 by Damon Lynch
Move aperture and focal length modifications to filemodify.py, writing out exif changes to about-to-be renamed temporary file or to XMP sidecar.
1294
        focal_length = task[2]
470 by Damon Lynch
Implementation of file verification for downloaded file, but not yet not the backup
1295
        verify_file = task[3]
1296
        refresh_md5_on_file_change = task[4]
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1297
430 by Damon Lynch
Move aperture and focal length modifications to filemodify.py, writing out exif changes to about-to-be renamed temporary file or to XMP sidecar.
1298
        file_modify = filemodify.FileModify(auto_rotate_jpeg, focal_length,
470 by Damon Lynch
Implementation of file verification for downloaded file, but not yet not the backup
1299
                                        verify_file, refresh_md5_on_file_change,
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1300
                                        task_process_conn, terminate_queue,
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
1301
                                        run_event)
1302
        file_modify.start()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1303
        self._processes.append((file_modify, terminate_queue, run_event,
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
1304
                                task_results_conn))
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1305
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
1306
        self.file_modify_by_scan_pid[scan_pid] = (task_results_conn, file_modify.pid)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1307
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
1308
        return file_modify.pid
1309
1310
    def _setup_pipe(self):
1311
        return Pipe(duplex=True)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1312
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
1313
    def _send_termination_msg(self, p):
1314
        p[1].put(None)
1315
        p[3].send((None, None))
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1316
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
1317
    def get_modify_pipe(self, scan_pid):
1318
        return self.file_modify_by_scan_pid[scan_pid][0]
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1319
1320
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1321
class BackupFilesManager(TaskManager):
1322
    """
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
1323
    Handles backup processes. This is a little different from some other Task
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1324
    Manager classes in that its pipe is Duplex, and the work done by it
1325
    is not pre-assigned when the process is started.
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1326
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
1327
    Duplex, multiprocess.
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1328
    """
1329
    def __init__(self, results_callback, batch_size):
1330
        TaskManager.__init__(self, results_callback, batch_size)
1331
        self.backup_devices_by_path = {}
1332
1333
    def _setup_pipe(self):
1334
        return Pipe(duplex=True)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1335
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1336
    def _send_termination_msg(self, p):
1337
        p[1].put(None)
471 by Damon Lynch
Added file verification on backups too.
1338
        p[3].send((None, None, None, None, None, None, None))
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1339
1340
    def _initiate_task(self, task, task_results_conn, task_process_conn,
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1341
                       terminate_queue, run_event):
1342
        path = task[0]
1343
        name = task[1]
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
1344
        backup_type = task[2]
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1345
        backup_files = backupfile.BackupFiles(path, name, self.batch_size,
1346
                                        task_process_conn, terminate_queue,
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1347
                                        run_event)
1348
        backup_files.start()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1349
        self._processes.append((backup_files, terminate_queue, run_event,
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1350
                                task_results_conn))
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1351
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
1352
        self.backup_devices_by_path[path] = (task_results_conn, backup_files.pid,
1353
                                            backup_type)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1354
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1355
        return backup_files.pid
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1356
1357
    def backup_file(self, move_succeeded, rpd_file, path_suffix,
446 by Damon Lynch
Debugging backup code - partial and switched off. Fixed bug #1093330: Photo rename ignores SubSeconds when 00.
1358
                                                backup_duplicate_overwrite,
471 by Damon Lynch
Added file verification on backups too.
1359
                                                verify_file,
446 by Damon Lynch
Debugging backup code - partial and switched off. Fixed bug #1093330: Photo rename ignores SubSeconds when 00.
1360
                                                download_count):
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
1361
1362
        if rpd_file.file_type == rpdfile.FILE_TYPE_PHOTO:
1363
            logger.debug("Backing up photo %s", rpd_file.download_name)
1364
        else:
1365
            logger.debug("Backing up video %s", rpd_file.download_name)
1366
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1367
        for path in self.backup_devices_by_path:
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
1368
            backup_type = self.backup_devices_by_path[path][2]
465 by Damon Lynch
Fixed bug where missing backup folder on auto detected devices could cause the download to fail
1369
            do_backup = ((backup_type == PHOTO_VIDEO_BACKUP) or
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
1370
                    (rpd_file.file_type == rpdfile.FILE_TYPE_PHOTO and backup_type == PHOTO_BACKUP) or
465 by Damon Lynch
Fixed bug where missing backup folder on auto detected devices could cause the download to fail
1371
                    (rpd_file.file_type == rpdfile.FILE_TYPE_VIDEO and backup_type == VIDEO_BACKUP))
1372
            if do_backup:
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
1373
                logger.debug("Backing up to %s", path)
1374
            else:
1375
                logger.debug("Not backing up to %s", path)
465 by Damon Lynch
Fixed bug where missing backup folder on auto detected devices could cause the download to fail
1376
            # Even if not going to backup to this device, need to send it anyway so
1377
            # progress bar can be updated. Not this most efficient but the
1378
            # code is much more simple
1379
            task_results_conn = self.backup_devices_by_path[path][0]
1380
            task_results_conn.send((move_succeeded, do_backup, rpd_file,
1381
                                path_suffix,
471 by Damon Lynch
Added file verification on backups too.
1382
                                backup_duplicate_overwrite,
1383
                                verify_file, download_count))
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1384
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
1385
    def add_device(self, path, name, backup_type):
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1386
        """
1387
        Convenience function to setup adding a backup device
1388
        """
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
1389
        return self.add_task((path, name, backup_type))
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1390
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1391
    def remove_device(self, path):
1392
        pid = self.backup_devices_by_path[path][1]
1393
        self.terminate_process(pid)
1394
        del self.backup_devices_by_path[path]
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1395
1396
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1397
class SingleInstanceTaskManager:
341 by Damon Lynch
Create base class DaemonTaskManager; preliminary infrstructure work on subfolder creation and file renaming.
1398
    """
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1399
    Base class to manage single instance processes. Examples are daemon
1400
    processes, but also a non-daemon process that has one simple task.
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1401
341 by Damon Lynch
Create base class DaemonTaskManager; preliminary infrstructure work on subfolder creation and file renaming.
1402
    Core (infrastructure) functionality is implemented in this class.
1403
    Derived classes should implemented functionality to actually implement
1404
    specific tasks.
1405
    """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1406
    def __init__(self, results_callback):
319 by Damon Lynch
Move preview widgets into glade file and automatically resize preview images
1407
        self.results_callback = results_callback
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1408
343 by Damon Lynch
Use only a pipe for communication with daemon processes, as using a queue for one direction and a pipe for another leads to a strange problem reading from the queue.
1409
        self.task_results_conn, self.task_process_conn = Pipe(duplex=True)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1410
319 by Damon Lynch
Move preview widgets into glade file and automatically resize preview images
1411
        source = self.task_results_conn.fileno()
341 by Damon Lynch
Create base class DaemonTaskManager; preliminary infrstructure work on subfolder creation and file renaming.
1412
        gobject.io_add_watch(source, gobject.IO_IN, self.task_results)
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1413
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1414
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1415
class PreviewManager(SingleInstanceTaskManager):
341 by Damon Lynch
Create base class DaemonTaskManager; preliminary infrstructure work on subfolder creation and file renaming.
1416
    def __init__(self, results_callback):
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1417
        SingleInstanceTaskManager.__init__(self, results_callback)
343 by Damon Lynch
Use only a pipe for communication with daemon processes, as using a queue for one direction and a pipe for another leads to a strange problem reading from the queue.
1418
        self._get_preview = tn.GetPreviewImage(self.task_process_conn)
341 by Damon Lynch
Create base class DaemonTaskManager; preliminary infrstructure work on subfolder creation and file renaming.
1419
        self._get_preview.start()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1420
420 by Damon Lynch
Download THM video thumbnail files
1421
    def get_preview(self, unique_id, full_file_name, thm_file_name, file_type, size_max):
1422
        self.task_results_conn.send((unique_id, full_file_name, thm_file_name, file_type, size_max))
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1423
341 by Damon Lynch
Create base class DaemonTaskManager; preliminary infrstructure work on subfolder creation and file renaming.
1424
    def task_results(self, source, condition):
319 by Damon Lynch
Move preview widgets into glade file and automatically resize preview images
1425
        unique_id, preview_full_size, preview_small = self.task_results_conn.recv()
1426
        self.results_callback(unique_id, preview_full_size, preview_small)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1427
        return True
1428
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1429
class SubfolderFileManager(SingleInstanceTaskManager):
1430
    """
1431
    Manages the daemon process that renames files and creates subfolders
1432
    """
430 by Damon Lynch
Move aperture and focal length modifications to filemodify.py, writing out exif changes to about-to-be renamed temporary file or to XMP sidecar.
1433
    def __init__(self, results_callback, sequence_values):
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1434
        SingleInstanceTaskManager.__init__(self, results_callback)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1435
        self._subfolder_file = subfolderfile.SubfolderFile(self.task_process_conn,
430 by Damon Lynch
Move aperture and focal length modifications to filemodify.py, writing out exif changes to about-to-be renamed temporary file or to XMP sidecar.
1436
                                                sequence_values)
341 by Damon Lynch
Create base class DaemonTaskManager; preliminary infrstructure work on subfolder creation and file renaming.
1437
        self._subfolder_file.start()
362 by Damon Lynch
Leave file preview mode when download devices are changed in the
1438
        logger.debug("SubfolderFile PID: %s", self._subfolder_file.pid)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1439
1440
    def rename_file_and_move_to_subfolder(self, download_succeeded,
346 by Damon Lynch
move reading prefs extraction out of name generation process and into main process
1441
            download_count, rpd_file):
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1442
1443
        logger.debug("Sending file for rename: %s.", download_count)
1444
        self.task_results_conn.send((download_succeeded, download_count,
346 by Damon Lynch
move reading prefs extraction out of name generation process and into main process
1445
            rpd_file))
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1446
341 by Damon Lynch
Create base class DaemonTaskManager; preliminary infrstructure work on subfolder creation and file renaming.
1447
1448
    def task_results(self, source, condition):
446 by Damon Lynch
Debugging backup code - partial and switched off. Fixed bug #1093330: Photo rename ignores SubSeconds when 00.
1449
        move_succeeded, rpd_file, download_count = self.task_results_conn.recv()
1450
        self.results_callback(move_succeeded, rpd_file, download_count)
341 by Damon Lynch
Create base class DaemonTaskManager; preliminary infrstructure work on subfolder creation and file renaming.
1451
        return True
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1452
330 by Damon Lynch
make preview image resizable
1453
class ResizblePilImage(gtk.DrawingArea):
1454
    def __init__(self, bg_color=None):
1455
        gtk.DrawingArea.__init__(self)
1456
        self.base_image = None
1457
        self.bg_color = bg_color
1458
        self.connect('expose_event', self.expose)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1459
330 by Damon Lynch
make preview image resizable
1460
    def set_image(self, image):
1461
        self.base_image = image
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1462
330 by Damon Lynch
make preview image resizable
1463
        #set up sizes and ratio used for drawing the derived image
1464
        self.base_image_w = self.base_image.size[0]
1465
        self.base_image_h = self.base_image.size[1]
1466
        self.base_image_aspect = float(self.base_image_w) / self.base_image_h
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1467
330 by Damon Lynch
make preview image resizable
1468
        self.queue_draw()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1469
330 by Damon Lynch
make preview image resizable
1470
    def expose(self, widget, event):
1471
1472
        cairo_context = self.window.cairo_create()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1473
1474
        x = event.area.x
1475
        y = event.area.y
330 by Damon Lynch
make preview image resizable
1476
        w = event.area.width
1477
        h = event.area.height
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1478
1479
        #constrain operations to event area
330 by Damon Lynch
make preview image resizable
1480
        cairo_context.rectangle(x, y, w, h)
1481
        cairo_context.clip_preserve()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1482
330 by Damon Lynch
make preview image resizable
1483
        #set background color, if needed
1484
        if self.bg_color:
1485
            cairo_context.set_source_rgb(*self.bg_color)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1486
            cairo_context.fill_preserve()
330 by Damon Lynch
make preview image resizable
1487
1488
        if not self.base_image:
1489
            return False
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1490
330 by Damon Lynch
make preview image resizable
1491
        frame_aspect = float(w) / h
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1492
330 by Damon Lynch
make preview image resizable
1493
        if frame_aspect > self.base_image_aspect:
1494
            # Frame is wider than image
1495
            height = h
1496
            width = int(height * self.base_image_aspect)
1497
        else:
1498
            # Frame is taller than image
1499
            width = w
1500
            height = int(width / self.base_image_aspect)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1501
330 by Damon Lynch
make preview image resizable
1502
        #resize image
1503
        pil_image = self.base_image.copy()
1504
        if self.base_image_w < width or self.base_image_h < height:
331 by Damon Lynch
Place checkbox in upper left of thumbnail display; add debugging information to understand cause of PIL core dump
1505
            logger.debug("Upsizing image")
330 by Damon Lynch
make preview image resizable
1506
            pil_image = tn.upsize_pil(pil_image, (width, height))
1507
        else:
331 by Damon Lynch
Place checkbox in upper left of thumbnail display; add debugging information to understand cause of PIL core dump
1508
            logger.debug("Downsizing image")
330 by Damon Lynch
make preview image resizable
1509
            tn.downsize_pil(pil_image, (width, height))
1510
1511
        #image width and height
1512
        image_w = pil_image.size[0]
1513
        image_h = pil_image.size[1]
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1514
330 by Damon Lynch
make preview image resizable
1515
        #center the image horizontally and vertically
1516
        #top left and right corners for the image:
1517
        image_x = x + ((w - image_w) / 2)
1518
        image_y = y + ((h - image_h) / 2)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1519
330 by Damon Lynch
make preview image resizable
1520
        image = create_cairo_image_surface(pil_image, image_w, image_h)
1521
        cairo_context.set_source_surface(image, image_x, image_y)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1522
        cairo_context.paint()
1523
1524
        return False
1525
1526
330 by Damon Lynch
make preview image resizable
1527
329 by Damon Lynch
Display thumbnails correctly (ellipsize & positioning); remove left and right panes; show preview image in main window
1528
class PreviewImage:
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1529
329 by Damon Lynch
Display thumbnails correctly (ellipsize & positioning); remove left and right panes; show preview image in main window
1530
    def __init__(self, parent_app, builder):
330 by Damon Lynch
make preview image resizable
1531
        #set background color to equivalent of '#444444
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1532
        self.preview_image = ResizblePilImage(bg_color=(0.267, 0.267, 0.267))
330 by Damon Lynch
make preview image resizable
1533
        self.preview_image_eventbox = builder.get_object("preview_eventbox")
1534
        self.preview_image_eventbox.add(self.preview_image)
1535
        self.preview_image.show()
333 by Damon Lynch
Make "Include in download" checkbutton functional
1536
        self.download_this_checkbutton = builder.get_object("download_this_checkbutton")
329 by Damon Lynch
Display thumbnails correctly (ellipsize & positioning); remove left and right panes; show preview image in main window
1537
        self.rapid_app = parent_app
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1538
329 by Damon Lynch
Display thumbnails correctly (ellipsize & positioning); remove left and right panes; show preview image in main window
1539
        self.base_preview_image = None # large size image used to scale down from
1540
        self.current_preview_size = (0,0)
1541
        self.preview_image_size_limit = (0,0)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1542
352 by Damon Lynch
enable menus
1543
        self.unique_id = None
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1544
1545
    def set_preview_image(self, unique_id, pil_image, include_checkbutton_visible=None,
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
1546
                          checked=None):
329 by Damon Lynch
Display thumbnails correctly (ellipsize & positioning); remove left and right panes; show preview image in main window
1547
        """
1548
        """
330 by Damon Lynch
make preview image resizable
1549
        self.preview_image.set_image(pil_image)
329 by Damon Lynch
Display thumbnails correctly (ellipsize & positioning); remove left and right panes; show preview image in main window
1550
        self.unique_id = unique_id
333 by Damon Lynch
Make "Include in download" checkbutton functional
1551
        if checked is not None:
1552
            self.download_this_checkbutton.set_active(checked)
1553
            self.download_this_checkbutton.grab_focus()
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
1554
1555
        if include_checkbutton_visible is not None:
1556
            self.download_this_checkbutton.props.visible = include_checkbutton_visible
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1557
329 by Damon Lynch
Display thumbnails correctly (ellipsize & positioning); remove left and right panes; show preview image in main window
1558
    def update_preview_image(self, unique_id, pil_image):
1559
        if unique_id == self.unique_id:
1560
            self.set_preview_image(unique_id, pil_image)
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1561
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1562
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1563
class RapidApp(dbus.service.Object):
1564
    """
1565
    The main Rapid Photo Downloader application class.
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1566
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1567
    Contains functionality for main program window, and directs all other
1568
    processes.
1569
    """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1570
431.1.10 by Damon Lynch
Added command line option to specify device detection and location
1571
    def __init__(self,  bus, path, name, taskserver=None, focal_length=None,
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1572
    auto_detect=None, device_location=None):
1573
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
1574
        dbus.service.Object.__init__ (self, bus, path, name)
1575
        self.running = False
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1576
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
1577
        self.taskserver = taskserver
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1578
402 by Damon Lynch
add ignore paths option; add remove remembered devices to prefs window; add focal_length command line option; updated i18n
1579
        self.focal_length = focal_length
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1580
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1581
        # Setup program preferences, and set callback for when they change
431.1.10 by Damon Lynch
Added command line option to specify device detection and location
1582
        self._init_prefs(auto_detect, device_location)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1583
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1584
        # Initialize widgets in the main window, and variables that point to them
1585
        self._init_widgets()
446 by Damon Lynch
Debugging backup code - partial and switched off. Fixed bug #1093330: Photo rename ignores SubSeconds when 00.
1586
1587
        if use_pynotify:
1588
            self._init_pynotify()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1589
346 by Damon Lynch
move reading prefs extraction out of name generation process and into main process
1590
        # Initialize job code handling
1591
        self._init_job_code()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1592
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1593
        # Remember the window size from the last time the program was run, or
1594
        # set a default size
1595
        self._set_window_size()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1596
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1597
        # Setup various widgets
1598
        self._setup_buttons()
1599
        self._setup_error_icons()
356 by Damon Lynch
translation updates and free space display minor fixes
1600
        self._setup_icons()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1601
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1602
        # Show the main window
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
1603
        self.rapidapp.show()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1604
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1605
        # Check program preferences - don't allow auto start if there is a problem
372 by Damon Lynch
notify user if file rename or subfolder preferences are invalid on program startup
1606
        prefs_valid, msg = prefsrapid.check_prefs_for_validity(self.prefs)
1607
        if not prefs_valid:
1608
            self.notify_prefs_are_invalid(details=msg)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1609
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1610
        # Initialize variables with which to track important downloads results
1611
        self._init_download_tracking()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1612
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
1613
        # Set up process managers.
1614
        # A task such as scanning a device or copying files is handled in its
1615
        # own process.
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1616
        self._start_process_managers()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1617
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1618
        # Setup devices from which to download from and backup to
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1619
        self.setup_devices(on_startup=True, on_preference_change=False,
372 by Damon Lynch
notify user if file rename or subfolder preferences are invalid on program startup
1620
                           block_auto_start=not prefs_valid)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1621
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1622
        # Ensure the device collection scrolled window is not too small
1623
        self._set_device_collection_size()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1624
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
1625
    def on_rapidapp_destroy(self, widget, data=None):
315 by Damon Lynch
move thumbnail generation out of rpdfile.py and into thumbnail.py
1626
350 by Damon Lynch
Add pause functionality, and bug fixes in early termination of processes.
1627
        self._terminate_processes(terminate_file_copies = True)
315 by Damon Lynch
move thumbnail generation out of rpdfile.py and into thumbnail.py
1628
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
1629
        # save window and component sizes
319 by Damon Lynch
Move preview widgets into glade file and automatically resize preview images
1630
        self.prefs.vpaned_pos = self.main_vpaned.get_position()
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
1631
332 by Damon Lynch
Proprerly place and align thumbnail and image controls in main window
1632
        x, y, width, height = self.rapidapp.get_allocation()
1633
        self.prefs.main_window_size_x = width
1634
        self.prefs.main_window_size_y = height
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1635
347 by Damon Lynch
Downloads today sequence implemented using tracking class and 3 shared memory values. Stored sequence number implemented using 1 shared memory value. Session sequence no and letter only partially implemented. kaa metadata loaded only when needed.
1636
        self.prefs.set_downloads_today_from_tracker(self.downloads_today_tracker)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1637
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1638
        gtk.main_quit()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1639
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1640
    def _terminate_processes(self, terminate_file_copies=False):
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1641
361 by Damon Lynch
Sample file names and subfolders are now displayed in the preferences dialog
1642
        if terminate_file_copies:
1643
            logger.info("Terminating all processes...")
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1644
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1645
        scan_termination_requested = self.scan_manager.request_termination()
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1646
        thumbnails_termination_requested = self.thumbnails.thumbnail_manager.request_termination()
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1647
        backup_termination_requested = self.backup_manager.request_termination()
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
1648
        file_modify_termination_requested = self.file_modify_manager.request_termination()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1649
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1650
        if terminate_file_copies:
1651
            copy_files_termination_requested = self.copy_files_manager.request_termination()
1652
        else:
1653
            copy_files_termination_requested = False
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1654
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1655
        if (scan_termination_requested or thumbnails_termination_requested or
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
1656
                backup_termination_requested or file_modify_termination_requested):
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1657
            time.sleep(1)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1658
            if (self.scan_manager.get_no_active_processes() > 0 or
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1659
                self.thumbnails.thumbnail_manager.get_no_active_processes() > 0 or
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
1660
                self.backup_manager.get_no_active_processes() > 0 or
1661
                self.file_modify_manager.get_no_active_processes() > 0):
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1662
                time.sleep(1)
1663
                # must try again, just in case a new scan has meanwhile started!
1664
                self.scan_manager.request_termination()
1665
                self.thumbnails.thumbnail_manager.terminate_forcefully()
1666
                self.scan_manager.terminate_forcefully()
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1667
                self.backup_manager.terminate_forcefully()
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
1668
                self.file_modify_manager.terminate_forcefully()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1669
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1670
        if terminate_file_copies and copy_files_termination_requested:
1671
            time.sleep(1)
1672
            self.copy_files_manager.terminate_forcefully()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1673
350 by Damon Lynch
Add pause functionality, and bug fixes in early termination of processes.
1674
        if terminate_file_copies:
1675
            self._clean_all_temp_dirs()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1676
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
1677
    # # #
1678
    # Events and tasks related to displaying preview images and thumbnails
1679
    # # #
330 by Damon Lynch
make preview image resizable
1680
333 by Damon Lynch
Make "Include in download" checkbutton functional
1681
    def on_download_this_checkbutton_toggled(self, checkbutton):
1682
        value = checkbutton.get_active()
1683
        self.thumbnails.set_selected(self.preview_image.unique_id, value)
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
1684
        self.set_download_action_sensitivity()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1685
334 by Damon Lynch
Next and previous image controls
1686
    def on_preview_eventbox_button_press_event(self, widget, event):
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1687
334 by Damon Lynch
Next and previous image controls
1688
        if event.type == gtk.gdk._2BUTTON_PRESS and event.button == 1:
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1689
            self.show_thumbnails()
1690
334 by Damon Lynch
Next and previous image controls
1691
    def on_show_thumbnails_action_activate(self, action):
1692
        logger.debug("on_show_thumbnails_action_activate")
1693
        self.show_thumbnails()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1694
335 by Damon Lynch
check all / uncheck all button functionality; preliminary device mounting support, icon in device treeview display
1695
    def on_show_image_action_activate(self, action):
1696
        logger.debug("on_show_image_action_activate")
1697
        self.thumbnails.show_preview()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1698
335 by Damon Lynch
check all / uncheck all button functionality; preliminary device mounting support, icon in device treeview display
1699
    def on_check_all_action_activate(self, action):
1700
        self.thumbnails.check_all(check_all=True)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1701
335 by Damon Lynch
check all / uncheck all button functionality; preliminary device mounting support, icon in device treeview display
1702
    def on_uncheck_all_action_activate(self, action):
1703
        self.thumbnails.check_all(check_all=False)
352 by Damon Lynch
enable menus
1704
1705
    def on_check_all_photos_action_activate(self, action):
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1706
        self.thumbnails.check_all(check_all=True,
352 by Damon Lynch
enable menus
1707
                                  file_type=rpdfile.FILE_TYPE_PHOTO)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1708
352 by Damon Lynch
enable menus
1709
    def on_check_all_videos_action_activate(self, action):
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1710
        self.thumbnails.check_all(check_all=True,
352 by Damon Lynch
enable menus
1711
                                  file_type=rpdfile.FILE_TYPE_VIDEO)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1712
352 by Damon Lynch
enable menus
1713
    def on_quit_action_activate(self, action):
1714
        self.on_rapidapp_destroy(widget=self.rapidapp, data=None)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1715
352 by Damon Lynch
enable menus
1716
    def on_refresh_action_activate(self, action):
425 by Damon Lynch
Fix bug: previously downloaded images are displayed when new card inserted
1717
        self.thumbnails.clear_all()
352 by Damon Lynch
enable menus
1718
        self.setup_devices(on_startup=False, on_preference_change=False,
372 by Damon Lynch
notify user if file rename or subfolder preferences are invalid on program startup
1719
                           block_auto_start=True)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1720
352 by Damon Lynch
enable menus
1721
    def on_get_help_action_activate(self, action):
1722
        webbrowser.open("http://www.damonlynch.net/rapid/help.html")
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1723
352 by Damon Lynch
enable menus
1724
    def on_about_action_activate(self, action):
1725
        self.about.set_property("name", PROGRAM_NAME)
1726
        self.about.set_property("version", utilities.human_readable_version(
1727
                                                                __version__))
1728
        self.about.run()
418 by Damon Lynch
Fix bug #859012 - warn user about downloading from /, /media and /home.
1729
        self.about.hide()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1730
352 by Damon Lynch
enable menus
1731
    def on_report_problem_action_activate(self, action):
1732
        webbrowser.open("https://bugs.launchpad.net/rapid")
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1733
352 by Damon Lynch
enable menus
1734
    def on_translate_action_activate(self, action):
1735
        webbrowser.open("http://www.damonlynch.net/rapid/translate.html")
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1736
352 by Damon Lynch
enable menus
1737
    def on_donate_action_activate(self, action):
1738
        webbrowser.open("http://www.damonlynch.net/rapid/donate.html")
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1739
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
1740
    def show_preview_image(self, unique_id, image, include_checkbutton_visible, checked):
329 by Damon Lynch
Display thumbnails correctly (ellipsize & positioning); remove left and right panes; show preview image in main window
1741
        if self.main_notebook.get_current_page() == 0: # thumbnails
332 by Damon Lynch
Proprerly place and align thumbnail and image controls in main window
1742
            logger.debug("Switching to preview image display")
329 by Damon Lynch
Display thumbnails correctly (ellipsize & positioning); remove left and right panes; show preview image in main window
1743
            self.main_notebook.set_current_page(1)
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
1744
        self.preview_image.set_preview_image(unique_id, image, include_checkbutton_visible, checked)
352 by Damon Lynch
enable menus
1745
        self.next_image_action.set_sensitive(True)
1746
        self.prev_image_action.set_sensitive(True)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1747
329 by Damon Lynch
Display thumbnails correctly (ellipsize & positioning); remove left and right panes; show preview image in main window
1748
    def update_preview_image(self, unique_id, image):
1749
        self.preview_image.update_preview_image(unique_id, image)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1750
329 by Damon Lynch
Display thumbnails correctly (ellipsize & positioning); remove left and right panes; show preview image in main window
1751
    def show_thumbnails(self):
332 by Damon Lynch
Proprerly place and align thumbnail and image controls in main window
1752
        logger.debug("Switching to thumbnails display")
329 by Damon Lynch
Display thumbnails correctly (ellipsize & positioning); remove left and right panes; show preview image in main window
1753
        self.main_notebook.set_current_page(0)
334 by Damon Lynch
Next and previous image controls
1754
        self.thumbnails.select_image(self.preview_image.unique_id)
352 by Damon Lynch
enable menus
1755
        self.next_image_action.set_sensitive(False)
1756
        self.prev_image_action.set_sensitive(False)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1757
1758
334 by Damon Lynch
Next and previous image controls
1759
    def on_next_image_action_activate(self, action):
352 by Damon Lynch
enable menus
1760
        if self.preview_image.unique_id is not None:
1761
            self.thumbnails.show_next_image(self.preview_image.unique_id)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1762
334 by Damon Lynch
Next and previous image controls
1763
    def on_prev_image_action_activate(self, action):
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1764
        if self.preview_image.unique_id is not None:
352 by Damon Lynch
enable menus
1765
            self.thumbnails.show_prev_image(self.preview_image.unique_id)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1766
431.1.13 by Damon Lynch
Updated translations
1767
    def display_scan_thumbnails(self):
338 by Damon Lynch
Delay thumbnail sort until scan is complete (much faster)
1768
        """
431.1.13 by Damon Lynch
Updated translations
1769
        If all the scans are complete, sets the sort order and displays
1770
        thumbnails in the icon view
338 by Damon Lynch
Delay thumbnail sort until scan is complete (much faster)
1771
        """
393 by Damon Lynch
bug fix: thumbnails occassionally not sorted by file modification time
1772
        if self.scan_manager.no_tasks == 0:
338 by Damon Lynch
Delay thumbnail sort until scan is complete (much faster)
1773
            self.thumbnails.sort_by_timestamp()
431.1.11 by Damon Lynch
Fix bug #1014203: create new liststore when clearing the old one, thereby removing the sort, which is super slow.
1774
            self.thumbnails.display_thumbnails()
338 by Damon Lynch
Delay thumbnail sort until scan is complete (much faster)
1775
332 by Damon Lynch
Proprerly place and align thumbnail and image controls in main window
1776
334 by Damon Lynch
Next and previous image controls
1777
    # # #
335 by Damon Lynch
check all / uncheck all button functionality; preliminary device mounting support, icon in device treeview display
1778
    # Volume management
1779
    # # #
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1780
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1781
    def start_volume_monitor(self):
1782
        if not self.vmonitor:
1783
            self.vmonitor = gio.volume_monitor_get()
1784
            self.vmonitor.connect("mount-added", self.on_mount_added)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1785
            self.vmonitor.connect("mount-removed", self.on_mount_removed)
1786
1787
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1788
    def _backup_device_name(self, path):
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
1789
        if self.backup_devices[path][0] is None:
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1790
            name = path
1791
        else:
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
1792
            name = self.backup_devices[path][0].get_name()
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1793
        return name
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1794
402 by Damon Lynch
add ignore paths option; add remove remembered devices to prefs window; add focal_length command line option; updated i18n
1795
    def start_device_scan(self, device):
1796
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1797
        Commences the scanning of a device using the preference values for
402 by Damon Lynch
add ignore paths option; add remove remembered devices to prefs window; add focal_length command line option; updated i18n
1798
        any paths to ignore while scanning
1799
        """
445 by Damon Lynch
Fixed bug #1083756: Application shows duplicate sources
1800
        logger.debug("Starting a device scan for device %s", device.get_name())
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1801
        return self.scan_manager.add_task([device,
402 by Damon Lynch
add ignore paths option; add remove remembered devices to prefs window; add focal_length command line option; updated i18n
1802
                                          self.prefs.ignored_paths,
1803
                                          self.prefs.use_re_ignored_paths])
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1804
418 by Damon Lynch
Fix bug #859012 - warn user about downloading from /, /media and /home.
1805
    def confirm_manual_location(self):
1806
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1807
        Queries the user to ask if they really want to download from locations
418 by Damon Lynch
Fix bug #859012 - warn user about downloading from /, /media and /home.
1808
        that could take a very long time to scan. They can choose yes or no.
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1809
418 by Damon Lynch
Fix bug #859012 - warn user about downloading from /, /media and /home.
1810
        Returns True if yes or there was no need to ask the user, False if the
1811
        user said no.
1812
        """
1813
        l = self.prefs.device_location
431.1.9 by Damon Lynch
Display path info in debugging log output. Display running totals during scan.
1814
        if l in ['/media', '/run', os.path.expanduser('~'), '/']:
418 by Damon Lynch
Fix bug #859012 - warn user about downloading from /, /media and /home.
1815
            logger.info("Prompting whether to download from %s", l)
1816
            if l == '/':
1817
                #this location is a human readable explanation for /, and is inserted into Downloading from %(location)s
1818
                l = _('the root of the file system')
1819
            c = preferencesdialog.QuestionDialog(parent_window=self.rapidapp,
1820
                    title=_('Rapid Photo Downloader'),
1821
                    #message in dialog box which asks the user if they really want to be downloading from this location
1822
                    question="<b>" + _("Downloading from %(location)s.") %  {'location': l} + "</b>\n\n" +
1823
                    _("Do you really want to download from here? On some systems, scanning this location can take a very long time."),
1824
                    default_to_yes=False,
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1825
                    use_markup=True)
418 by Damon Lynch
Fix bug #859012 - warn user about downloading from /, /media and /home.
1826
            response = c.run()
1827
            user_confirmed = response == gtk.RESPONSE_OK
1828
            c.destroy()
1829
            if not user_confirmed:
1830
                return False
1831
        return True
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1832
372 by Damon Lynch
notify user if file rename or subfolder preferences are invalid on program startup
1833
    def setup_devices(self, on_startup, on_preference_change, block_auto_start):
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1834
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1835
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1836
        Setup devices from which to download from and backup to
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1837
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1838
        Sets up volumes for downloading from and backing up to
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1839
1840
        on_startup should be True if the program is still starting,
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1841
        i.e. this is being called from the program's initialization.
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1842
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1843
        on_preference_change should be True if this is being called as the
1844
        result of a preference being changed
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1845
372 by Damon Lynch
notify user if file rename or subfolder preferences are invalid on program startup
1846
        block_auto_start should be True if automation options to automatically
1847
        start a download should be ignored
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1848
1849
        Removes any image media that are currently not downloaded,
1850
        or finished downloading
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1851
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1852
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1853
        if self.using_volume_monitor():
1854
            self.start_volume_monitor()
1855
1856
        self.clear_non_running_downloads()
418 by Damon Lynch
Fix bug #859012 - warn user about downloading from /, /media and /home.
1857
        if not self.prefs.device_autodetection:
1858
            if not self.confirm_manual_location():
1859
                return
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1860
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1861
        mounts = []
1862
        self.backup_devices = {}
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1863
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1864
        if self.using_volume_monitor():
1865
            # either using automatically detected backup devices
1866
            # or download devices
1867
            for mount in self.vmonitor.get_mounts():
1868
                if not mount.is_shadowed():
1869
                    path = mount.get_root().get_path()
1870
                    if path:
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1871
                        if (path in self.prefs.device_blacklist and
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1872
                                    self.search_for_PSD()):
450 by Damon Lynch
Don't crash when the sending a system notfication message and the message returns an error
1873
                            logger.info("blacklisted device %s ignored", mount.get_name())
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1874
                        else:
1875
                            logger.info("Detected %s", mount.get_name())
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
1876
                            is_backup_mount, backup_file_type = self.check_if_backup_mount(path)
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1877
                            if is_backup_mount:
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
1878
                                self.backup_devices[path] = (mount, backup_file_type)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1879
                            elif (self.prefs.device_autodetection and
1880
                                 (dv.is_DCIM_device(path) or
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1881
                                  self.search_for_PSD())):
399 by Damon Lynch
Changes in prefs.ui - new preference options for ignore paths, remembered devices, and video backup folder
1882
                                logger.debug("Appending %s", mount.get_name())
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1883
                                mounts.append((path, mount))
399 by Damon Lynch
Changes in prefs.ui - new preference options for ignore paths, remembered devices, and video backup folder
1884
                            else:
1885
                                logger.debug("Ignoring %s", mount.get_name())
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1886
1887
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1888
        if not self.prefs.device_autodetection:
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1889
            # user manually specified the path from which to download
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1890
            path = self.prefs.device_location
1891
            if path:
1892
                logger.info("Using manually specified path %s", path)
1893
                if utilities.is_directory(path):
1894
                    mounts.append((path, None))
1895
                else:
1896
                    logger.error("Download path does not exist: %s", path)
1897
1898
        if self.prefs.backup_images:
1899
            if not self.prefs.backup_device_autodetection:
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
1900
                self._setup_manual_backup()
1901
            self._add_backup_devices()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1902
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
1903
        self.update_no_backup_devices()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1904
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1905
        # Display amount of free space in a status bar message
1906
        self.display_free_space()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1907
372 by Damon Lynch
notify user if file rename or subfolder preferences are invalid on program startup
1908
        if block_auto_start:
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1909
            self.auto_start_is_on = False
1910
        else:
1911
            self.auto_start_is_on = ((not on_preference_change) and
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1912
                                    ((self.prefs.auto_download_at_startup and
1913
                                      on_startup) or
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1914
                                      (self.prefs.auto_download_upon_device_insertion and
1915
                                       not on_startup)))
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1916
445 by Damon Lynch
Fixed bug #1083756: Application shows duplicate sources
1917
        logger.debug("Working with %s devices", len(mounts))
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1918
        for m in mounts:
1919
            path, mount = m
1920
            device = dv.Device(path=path, mount=mount)
445 by Damon Lynch
Fixed bug #1083756: Application shows duplicate sources
1921
1922
1923
            if not self._device_already_detected(device):
1924
                if (self.search_for_PSD() and
1925
                        path not in self.prefs.device_whitelist):
1926
                    # prompt user to see if device should be used or not
1927
                    self.get_use_device(device)
1928
                else:
1929
                    scan_pid = self.start_device_scan(device)
1930
                    if mount is not None:
1931
                        self.mounts_by_path[path] = scan_pid
379 by Damon Lynch
check if scans are occuring by using manual counter, not no active processes (they can take a while to finish)
1932
        if not mounts:
1933
            self.set_download_action_sensitivity()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1934
445 by Damon Lynch
Fixed bug #1083756: Application shows duplicate sources
1935
    def _device_already_detected(self, device):
1936
        path = device.get_path()
1937
        if path in self.mounts_by_path:
1938
            logger.debug("Ignoring device %s as already have path %s", device.get_name(), path)
1939
            return True
1940
        else:
1941
            return False
1942
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
1943
    def _setup_manual_backup(self):
1944
        """
1945
        Setup backup devices that the user has manually specified.
1946
        Depending on the folder the user has chosen, the paths for photo and
1947
        video backup will either be the same or they will differ.
1948
        """
1949
        # user manually specified backup locations
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1950
        # will backup to these paths, but don't need any volume info
1951
        # associated with them
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
1952
        self.backup_devices[self.prefs.backup_location] = (None, PHOTO_BACKUP)
1953
        if DOWNLOAD_VIDEO:
1954
            if self.prefs.backup_location <> self.prefs.backup_video_location:
1955
                self.backup_devices[self.prefs.backup_video_location] = (None, VIDEO_BACKUP)
1956
                logger.info("Backing up photos to %s", self.prefs.backup_location)
1957
                logger.info("Backing up videos to %s", self.prefs.backup_video_location)
1958
            else:
1959
                # videos and photos are being backed up to the same location
1960
                self.backup_devices[self.prefs.backup_location] = (None, PHOTO_VIDEO_BACKUP)
1961
                logger.info("Backing up photos and videos to %s", self.prefs.backup_location)
1962
        else:
1963
            logger.info("Backing up photos to %s", self.prefs.backup_location)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1964
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
1965
    def _add_backup_devices(self):
1966
        """
1967
        Add each backup devices / path to backup manager
1968
        """
1969
        for path in self.backup_devices:
1970
            name = self._backup_device_name(path)
1971
            backup_type = self.backup_devices[path][1]
1972
            self.backup_manager.add_device(path, name, backup_type)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1973
1974
1975
    def get_use_device(self, device):
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1976
        """ Prompt user whether or not to download from this device """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1977
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1978
        logger.info("Prompting whether to use %s", device.get_name())
445 by Damon Lynch
Fixed bug #1083756: Application shows duplicate sources
1979
1980
        # On some systems, e.g. Ubuntu 12.10, the GTK/Gnome environment
1981
        # unexpectedly results in a device being added twice and not once.
1982
        # The hack on the next line ensures the user is not prompted twice
1983
        # for the same device.
1984
1985
        self.mounts_by_path[device.get_path()] = "PROMPTING"
1986
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1987
        d = dv.UseDeviceDialog(self.rapidapp, device, self.got_use_device)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1988
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1989
    def got_use_device(self, dialog, user_selected, permanent_choice, device):
1990
        """ User has chosen whether or not to use a device to download from """
1991
        dialog.destroy()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1992
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1993
        path = device.get_path()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
1994
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
1995
        if user_selected:
1996
            if permanent_choice and path not in self.prefs.device_whitelist:
1997
                # do NOT do a list append operation here without the assignment,
1998
                # or the actual preferences will not be updated!!
1999
                if len(self.prefs.device_whitelist):
2000
                    self.prefs.device_whitelist = self.prefs.device_whitelist + [path]
2001
                else:
2002
                    self.prefs.device_whitelist = [path]
402 by Damon Lynch
add ignore paths option; add remove remembered devices to prefs window; add focal_length command line option; updated i18n
2003
            scan_pid = self.start_device_scan(device)
349 by Damon Lynch
Implement mount and unmount callbacks and fix use device dialog
2004
            self.mounts_by_path[path] = scan_pid
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2005
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2006
        elif permanent_choice and path not in self.prefs.device_blacklist:
2007
            # do not do a list append operation here without the assignment, or the preferences will not be updated!
2008
            if len(self.prefs.device_blacklist):
2009
                self.prefs.device_blacklist = self.prefs.device_blacklist + [path]
2010
            else:
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2011
                self.prefs.device_blacklist = [path]
2012
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2013
    def search_for_PSD(self):
2014
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2015
        Check to see if user preferences are to automatically search for
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2016
        Portable Storage Devices or not
2017
        """
2018
        return self.prefs.device_autodetection_psd and self.prefs.device_autodetection
2019
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
2020
    def check_if_backup_mount(self, path):
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2021
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2022
        Checks to see if backups are enabled and path represents a valid backup
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
2023
        location. It must be writeable.
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2024
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2025
        Checks against user preferences.
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2026
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
2027
        Returns a tuple:
2028
        (True, <backup-type> (one of PHOTO_VIDEO_BACKUP, PHOTO_BACKUP, or VIDEO_BACKUP)) or
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2029
        (False, None)
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2030
        """
2031
        if self.prefs.backup_images:
2032
            if self.prefs.backup_device_autodetection:
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2033
                # Determine if the auto-detected backup device is
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
2034
                # to be used to backup only photos, or videos, or both.
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2035
                # Use the presence of a corresponding directory to
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
2036
                # determine this.
2037
                # The directory must be writable.
2038
                photo_path = os.path.join(path, self.prefs.backup_identifier)
2039
                p_backup = os.path.isdir(photo_path) and os.access(photo_path, os.W_OK)
2040
                if DOWNLOAD_VIDEO:
2041
                    video_path = os.path.join(path, self.prefs.video_backup_identifier)
2042
                    v_backup = os.path.isdir(video_path) and os.access(video_path, os.W_OK)
2043
                else:
2044
                    v_backup = False
2045
                if p_backup and v_backup:
2046
                    logger.info("Photos and videos will be backed up to %s", path)
2047
                    return (True, PHOTO_VIDEO_BACKUP)
2048
                elif p_backup:
2049
                    logger.info("Photos will be backed up to %s", path)
2050
                    return (True, PHOTO_BACKUP)
2051
                elif v_backup:
2052
                    logger.info("Videos will be backed up to %s", path)
2053
                    return (True, VIDEO_BACKUP)
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2054
            elif path == self.prefs.backup_location:
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2055
                # user manually specified the path
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
2056
                if os.access(self.prefs.backup_location, os.W_OK):
2057
                    return (True, PHOTO_BACKUP)
2058
            elif path == self.prefs.backup_video_location:
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2059
                # user manually specified the path
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
2060
                if os.access(self.prefs.backup_video_location, os.W_OK):
2061
                    return (True, VIDEO_BACKUP)
2062
        return (False, None)
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2063
2064
    def update_no_backup_devices(self):
408 by Damon Lynch
Fixed display of error messages when using more than one backup device; fixed bug in Swedish translation; fixed multiple spelling errors in ChangeLog
2065
        self.no_photo_backup_devices = 0
2066
        self.no_video_backup_devices = 0
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
2067
        for path, value in self.backup_devices.iteritems():
2068
            backup_type = value[1]
2069
            if backup_type == PHOTO_BACKUP:
408 by Damon Lynch
Fixed display of error messages when using more than one backup device; fixed bug in Swedish translation; fixed multiple spelling errors in ChangeLog
2070
                self.no_photo_backup_devices += 1
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
2071
            elif backup_type == VIDEO_BACKUP:
408 by Damon Lynch
Fixed display of error messages when using more than one backup device; fixed bug in Swedish translation; fixed multiple spelling errors in ChangeLog
2072
                self.no_video_backup_devices += 1
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
2073
            else:
2074
                #both videos and photos are backed up to this device / path
408 by Damon Lynch
Fixed display of error messages when using more than one backup device; fixed bug in Swedish translation; fixed multiple spelling errors in ChangeLog
2075
                self.no_photo_backup_devices += 1
2076
                self.no_video_backup_devices += 1
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2077
        logger.info("# photo backup devices: %s; # video backup devices: %s",
408 by Damon Lynch
Fixed display of error messages when using more than one backup device; fixed bug in Swedish translation; fixed multiple spelling errors in ChangeLog
2078
                    self.no_photo_backup_devices, self.no_video_backup_devices)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2079
        self.download_tracker.set_no_backup_devices(self.no_photo_backup_devices,
408 by Damon Lynch
Fixed display of error messages when using more than one backup device; fixed bug in Swedish translation; fixed multiple spelling errors in ChangeLog
2080
                                                    self.no_video_backup_devices)
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2081
2082
    def refresh_backup_media(self):
2083
        """
2084
        Setup the backup media
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2085
2086
        Assumptions: this is being called after the user has changed their
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2087
        preferences AND download media has already been setup
2088
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2089
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2090
        # terminate any running backup processes
2091
        self.backup_manager.request_termination()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2092
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2093
        self.backup_devices = {}
2094
        if self.prefs.backup_images:
2095
            if not self.prefs.backup_device_autodetection:
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
2096
                self._setup_manual_backup()
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2097
            else:
2098
                for mount in self.vmonitor.get_mounts():
2099
                    if not mount.is_shadowed():
2100
                        path = mount.get_root().get_path()
2101
                        if path:
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
2102
                            is_backup_mount, backup_file_type = self.check_if_backup_mount(path)
2103
                            if is_backup_mount:
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2104
                                # is a backup volume
2105
                                if path not in self.backup_devices:
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
2106
                                    self.backup_devices[path] = (mount, backup_file_type)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2107
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
2108
            self._add_backup_devices()
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2109
2110
        self.update_no_backup_devices()
2111
        self.display_free_space()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2112
335 by Damon Lynch
check all / uncheck all button functionality; preliminary device mounting support, icon in device treeview display
2113
    def using_volume_monitor(self):
2114
        """
2115
        Returns True if programs needs to use gio volume monitor
2116
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2117
2118
        return (self.prefs.device_autodetection or
2119
                (self.prefs.backup_images and
335 by Damon Lynch
check all / uncheck all button functionality; preliminary device mounting support, icon in device treeview display
2120
                self.prefs.backup_device_autodetection
2121
                ))
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2122
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2123
    def on_mount_added(self, vmonitor, mount):
2124
        """
2125
        callback run when gio indicates a new volume
2126
        has been mounted
2127
        """
349 by Damon Lynch
Implement mount and unmount callbacks and fix use device dialog
2128
2129
2130
        if mount.is_shadowed():
2131
            # ignore this type of mount
2132
            return
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2133
349 by Damon Lynch
Implement mount and unmount callbacks and fix use device dialog
2134
        path = mount.get_root().get_path()
378 by Damon Lynch
don't crash if path cannot be read when mount added
2135
        if path is not None:
2136
2137
            if path in self.prefs.device_blacklist and self.search_for_PSD():
2138
                logger.info("Device %(device)s (%(path)s) ignored" % {
2139
                            'device': mount.get_name(), 'path': path})
2140
            else:
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
2141
                is_backup_mount, backup_file_type = self.check_if_backup_mount(path)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2142
378 by Damon Lynch
don't crash if path cannot be read when mount added
2143
                if is_backup_mount:
2144
                    if path not in self.backup_devices:
2145
                        self.backup_devices[path] = mount
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2146
                        name = self._backup_device_name(path)
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
2147
                        self.backup_manager.add_device(path, name, backup_file_type)
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2148
                        self.update_no_backup_devices()
378 by Damon Lynch
don't crash if path cannot be read when mount added
2149
                        self.display_free_space()
2150
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2151
                elif self.prefs.device_autodetection and (dv.is_DCIM_device(path) or
378 by Damon Lynch
don't crash if path cannot be read when mount added
2152
                                                            self.search_for_PSD()):
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2153
378 by Damon Lynch
don't crash if path cannot be read when mount added
2154
                    self.auto_start_is_on = self.prefs.auto_download_upon_device_insertion
2155
                    device = dv.Device(path=path, mount=mount)
445 by Damon Lynch
Fixed bug #1083756: Application shows duplicate sources
2156
2157
                    if not self._device_already_detected(device):
2158
                        if self.search_for_PSD() and path not in self.prefs.device_whitelist:
2159
                            # prompt user if device should be used or not
2160
                            self.get_use_device(device)
2161
                        else:
2162
                            scan_pid = self.start_device_scan(device)
2163
                            self.mounts_by_path[path] = scan_pid
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2164
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2165
    def on_mount_removed(self, vmonitor, mount):
2166
        """
2167
        callback run when gio indicates a new volume
2168
        has been mounted
2169
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2170
349 by Damon Lynch
Implement mount and unmount callbacks and fix use device dialog
2171
        path = mount.get_root().get_path()
2172
2173
        # three scenarios -
2174
        # the mount has been scanned but downloading has not yet started
2175
        # files are being downloaded from mount (it must be a messy unmount)
2176
        # files have finished downloading from mount
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2177
349 by Damon Lynch
Implement mount and unmount callbacks and fix use device dialog
2178
        if path in self.mounts_by_path:
2179
            scan_pid = self.mounts_by_path[path]
2180
            del self.mounts_by_path[path]
2181
            # temp directory should be cleaned by finishing of process
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2182
2183
            self.thumbnails.clear_all(scan_pid = scan_pid,
359 by Damon Lynch
If necessary, get thumbnail from downloaded file. Indicate download is pending.
2184
                                      keep_downloaded_files = True)
349 by Damon Lynch
Implement mount and unmount callbacks and fix use device dialog
2185
            self.device_collection.remove_device(scan_pid)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2186
2187
2188
349 by Damon Lynch
Implement mount and unmount callbacks and fix use device dialog
2189
        # remove backup volumes
2190
        elif path in self.backup_devices:
2191
            del self.backup_devices[path]
2192
            self.display_free_space()
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2193
            self.backup_manager.remove_device(path)
2194
            self.update_no_backup_devices()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2195
349 by Damon Lynch
Implement mount and unmount callbacks and fix use device dialog
2196
        # may need to disable download button and menu
2197
        self.set_download_action_sensitivity()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2198
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2199
    def clear_non_running_downloads(self):
2200
        """
2201
        Clears the display of downloads that are currently not running
2202
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2203
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2204
        # Stop any processes currently scanning or creating thumbnails
2205
        self._terminate_processes(terminate_file_copies=False)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2206
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2207
        # Remove them from the user interface
2208
        for scan_pid in self.device_collection.get_all_displayed_processes():
2209
            if scan_pid not in self.download_active_by_scan_pid:
2210
                self.device_collection.remove_device(scan_pid)
2211
                self.thumbnails.clear_all(scan_pid=scan_pid)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2212
2213
2214
2215
335 by Damon Lynch
check all / uncheck all button functionality; preliminary device mounting support, icon in device treeview display
2216
    # # #
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2217
    # Download and help buttons, and menu items
337 by Damon Lynch
Place checkbutton beneath thumbnail text; preliminary functionality for download action sensitivity
2218
    # # #
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2219
337 by Damon Lynch
Place checkbutton beneath thumbnail text; preliminary functionality for download action sensitivity
2220
    def on_download_action_activate(self, action):
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
2221
        """
2222
        Called when a download is activated
2223
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2224
350 by Damon Lynch
Add pause functionality, and bug fixes in early termination of processes.
2225
        if self.copy_files_manager.paused:
2226
            logger.debug("Download resumed")
2227
            self.resume_download()
2228
        else:
2229
            logger.debug("Download activated")
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2230
350 by Damon Lynch
Add pause functionality, and bug fixes in early termination of processes.
2231
            if self.download_action_is_download:
371 by Damon Lynch
finish implementing job codes
2232
                if self.need_job_code_for_naming and not self.prompting_for_job_code:
2233
                    self.get_job_code()
2234
                else:
2235
                    self.start_download()
350 by Damon Lynch
Add pause functionality, and bug fixes in early termination of processes.
2236
            else:
2237
                self.pause_download()
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
2238
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2239
337 by Damon Lynch
Place checkbutton beneath thumbnail text; preliminary functionality for download action sensitivity
2240
    def on_help_action_activate(self, action):
2241
        webbrowser.open("http://www.damonlynch.net/rapid/documentation")
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2242
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2243
    def on_preferences_action_activate(self, action):
2244
2245
        preferencesdialog.PreferencesDialog(self)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2246
337 by Damon Lynch
Place checkbutton beneath thumbnail text; preliminary functionality for download action sensitivity
2247
    def set_download_action_sensitivity(self):
2248
        """
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2249
        Sets sensitivity of Download action to enable or disable it
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2250
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2251
        Affects download button and menu item
337 by Damon Lynch
Place checkbutton beneath thumbnail text; preliminary functionality for download action sensitivity
2252
        """
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
2253
        if not self.download_is_occurring():
2254
            sensitivity = False
379 by Damon Lynch
check if scans are occuring by using manual counter, not no active processes (they can take a while to finish)
2255
            if self.scan_manager.no_tasks == 0:
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
2256
                if self.thumbnails.files_are_checked_to_download():
2257
                    sensitivity = True
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2258
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
2259
            self.download_action.set_sensitive(sensitivity)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2260
350 by Damon Lynch
Add pause functionality, and bug fixes in early termination of processes.
2261
    def set_download_action_label(self, is_download):
2262
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2263
        Toggles label betwen pause and download
350 by Damon Lynch
Add pause functionality, and bug fixes in early termination of processes.
2264
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2265
350 by Damon Lynch
Add pause functionality, and bug fixes in early termination of processes.
2266
        if is_download:
2267
            self.download_action.set_label(_("Download"))
2268
            self.download_action_is_download = True
2269
        else:
2270
            self.download_action.set_label(_("Pause"))
2271
            self.download_action_is_download = False
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2272
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2273
    # # #
2274
    # Job codes
2275
    # # #
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2276
2277
346 by Damon Lynch
move reading prefs extraction out of name generation process and into main process
2278
    def _init_job_code(self):
372 by Damon Lynch
notify user if file rename or subfolder preferences are invalid on program startup
2279
        self.job_code = self.last_chosen_job_code = ''
346 by Damon Lynch
move reading prefs extraction out of name generation process and into main process
2280
        self.need_job_code_for_naming = self.prefs.any_pref_uses_job_code()
371 by Damon Lynch
finish implementing job codes
2281
        self.prompting_for_job_code = False
372 by Damon Lynch
notify user if file rename or subfolder preferences are invalid on program startup
2282
371 by Damon Lynch
finish implementing job codes
2283
    def assign_job_code(self, code):
2284
        """ assign job code (which may be empty) to member variable and update user preferences
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2285
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2286
        Update preferences only if code is not empty. Do not duplicate job code.
2287
        """
371 by Damon Lynch
finish implementing job codes
2288
2289
        self.job_code = code
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2290
371 by Damon Lynch
finish implementing job codes
2291
        if code:
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2292
            #add this value to job codes preferences
2293
            #delete any existing value which is the same
2294
            #(this way it comes to the front, which is where it should be)
2295
            #never modify self.prefs.job_codes in place! (or prefs become screwed up)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2296
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2297
            jcs = self.prefs.job_codes
2298
            while code in jcs:
2299
                jcs.remove(code)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2300
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2301
            self.prefs.job_codes = [code] + jcs
2302
371 by Damon Lynch
finish implementing job codes
2303
    def _get_job_code(self, post_job_code_entry_callback):
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2304
        """ prompt for a job code """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2305
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2306
        if not self.prompting_for_job_code:
2307
            logger.debug("Prompting for Job Code")
2308
            self.prompting_for_job_code = True
2309
            j = preferencesdialog.JobCodeDialog(parent_window = self.rapidapp,
2310
                    job_codes = self.prefs.job_codes,
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2311
                    default_job_code = self.last_chosen_job_code,
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2312
                    post_job_code_entry_callback=post_job_code_entry_callback,
2313
                    entry_only = False)
2314
        else:
2315
            logger.debug("Already prompting for Job Code, do not prompt again")
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2316
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2317
    def get_job_code(self):
2318
        self._get_job_code(self.got_job_code)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2319
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2320
    def got_job_code(self, dialog, user_chose_code, code):
2321
        dialog.destroy()
2322
        self.prompting_for_job_code = False
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2323
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2324
        if user_chose_code:
371 by Damon Lynch
finish implementing job codes
2325
            if code is None:
2326
                code = ''
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2327
            self.assign_job_code(code)
2328
            self.last_chosen_job_code = code
371 by Damon Lynch
finish implementing job codes
2329
            logger.debug("Job Code %s entered", self.job_code)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2330
            self.start_download()
2331
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2332
        else:
2333
            # user cancelled
371 by Damon Lynch
finish implementing job codes
2334
            logger.debug("No Job Code entered")
2335
            self.job_code = ''
373 by Damon Lynch
auto start download on startup and on device insertion; adjust vertical pane when device is inserted; when auto start is enabled, delay making checkboxes invisible
2336
            self.auto_start_is_on = False
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2337
2338
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
2339
    # # #
2340
    # Download
2341
    # # #
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2342
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2343
    def _init_download_tracking(self):
2344
        """
2345
        Initialize variables to track downloads
2346
        """
2347
        # Track download sizes and other values for each device.
2348
        # (Scan id acts as an index to each device. A device could be scanned
2349
        #  more than once).
367 by Damon Lynch
Use downloadtracker class and adjust percent complete by including time taken to rename files.
2350
        self.download_tracker = downloadtracker.DownloadTracker()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2351
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2352
        # Track which temporary directories are created when downloading files
2353
        self.temp_dirs_by_scan_pid = dict()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2354
393 by Damon Lynch
bug fix: thumbnails occassionally not sorted by file modification time
2355
        # Track which downloads are running
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2356
        self.download_active_by_scan_pid = []
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2357
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
2358
    def modify_files_during_download(self):
470 by Damon Lynch
Implementation of file verification for downloaded file, but not yet not the backup
2359
        """ Returns True if there is a need to modify or verify files during download"""
471 by Damon Lynch
Added file verification on backups too.
2360
        return self.prefs.auto_rotate_jpeg or (self.focal_length is not None) or self.prefs.verify_file
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2361
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2362
373 by Damon Lynch
auto start download on startup and on device insertion; adjust vertical pane when device is inserted; when auto start is enabled, delay making checkboxes invisible
2363
    def start_download(self, scan_pid=None):
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
2364
        """
340 by Damon Lynch
Move download tracking functionality from copyfile process to main program process, in preparation for coming file renaming and backup functionality
2365
        Start download, renaming and backup of files.
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2366
373 by Damon Lynch
auto start download on startup and on device insertion; adjust vertical pane when device is inserted; when auto start is enabled, delay making checkboxes invisible
2367
        If scan_pid is specified, only files matching it will be downloaded
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
2368
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2369
373 by Damon Lynch
auto start download on startup and on device insertion; adjust vertical pane when device is inserted; when auto start is enabled, delay making checkboxes invisible
2370
        files_by_scan_pid = self.thumbnails.get_files_checked_for_download(scan_pid)
465 by Damon Lynch
Fixed bug where missing backup folder on auto detected devices could cause the download to fail
2371
        self.check_file_types_to_be_downloaded(files_by_scan_pid)
366 by Damon Lynch
Add error log window. Allow cancellation of file copy in case of early termination.
2372
        folders_valid, invalid_dirs = self.check_download_folder_validity(files_by_scan_pid)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2373
366 by Damon Lynch
Add error log window. Allow cancellation of file copy in case of early termination.
2374
        if not folders_valid:
2375
            if len(invalid_dirs) > 1:
2376
                msg = _("These download folders are invalid:\n%(folder1)s\n%(folder2)s") % {
2377
                        'folder1': invalid_dirs[0], 'folder2': invalid_dirs[1]}
2378
            else:
2379
                msg = _("This download folder is invalid:\n%s") % invalid_dirs[0]
2380
            self.log_error(config.CRITICAL_ERROR, _("Download cannot proceed"),
2381
                msg)
2382
        else:
465 by Damon Lynch
Fixed bug where missing backup folder on auto detected devices could cause the download to fail
2383
            missing_destinations = self.backup_destinations_missing()
2384
            if missing_destinations is not None:
2385
                # Warn user that they have specified that they want to backup a file type, but no such folder exists on backup devices
2386
                if not missing_destinations[0]:
2387
                    logger.warning("No backup device contains a valid folder for backing up photos")
472 by Damon Lynch
Slight modification for i18n
2388
                    msg = _("No backup device contains a valid folder for backing up %(filetype)s") % {'filetype': _('photos')}
465 by Damon Lynch
Fixed bug where missing backup folder on auto detected devices could cause the download to fail
2389
                else:
2390
                    logger.warning("No backup device contains a valid folder for backing up videos")
472 by Damon Lynch
Slight modification for i18n
2391
                    msg = _("No backup device contains a valid folder for backing up %(filetype)s") % {'filetype': _('videos')}
465 by Damon Lynch
Fixed bug where missing backup folder on auto detected devices could cause the download to fail
2392
2393
                self.log_error(config.WARNING, _("Backup problem"), msg)
2394
373 by Damon Lynch
auto start download on startup and on device insertion; adjust vertical pane when device is inserted; when auto start is enabled, delay making checkboxes invisible
2395
            # set time download is starting if it is not already set
2396
            # it is unset when all downloads are completed
2397
            if self.download_start_time is None:
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2398
                self.download_start_time = datetime.datetime.now()
381 by Damon Lynch
when user clicks download and thumbnail generation is still occuring, generate the thumbmnails in the copy file process
2399
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2400
            # Set status to download pending
359 by Damon Lynch
If necessary, get thumbnail from downloaded file. Indicate download is pending.
2401
            self.thumbnails.mark_download_pending(files_by_scan_pid)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2402
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2403
            # disable refresh and preferences change while download is occurring
2404
            self.enable_prefs_and_refresh(enabled=False)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2405
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
2406
            for scan_pid in files_by_scan_pid:
2407
                files = files_by_scan_pid[scan_pid]
381 by Damon Lynch
when user clicks download and thumbnail generation is still occuring, generate the thumbmnails in the copy file process
2408
                # if generating thumbnails for this scan_pid, stop it
2409
                if self.thumbnails.terminate_thumbnail_generation(scan_pid):
2410
                    self.thumbnails.mark_thumbnails_needed(files)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2411
340 by Damon Lynch
Move download tracking functionality from copyfile process to main program process, in preparation for coming file renaming and backup functionality
2412
                self.download_files(files, scan_pid)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2413
366 by Damon Lynch
Add error log window. Allow cancellation of file copy in case of early termination.
2414
            self.set_download_action_label(is_download = False)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2415
350 by Damon Lynch
Add pause functionality, and bug fixes in early termination of processes.
2416
    def pause_download(self):
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2417
350 by Damon Lynch
Add pause functionality, and bug fixes in early termination of processes.
2418
        self.copy_files_manager.pause()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2419
350 by Damon Lynch
Add pause functionality, and bug fixes in early termination of processes.
2420
        # set action to display Download
2421
        if not self.download_action_is_download:
2422
            self.set_download_action_label(is_download = True)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2423
377 by Damon Lynch
time remaining and download speed
2424
        self.time_check.pause()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2425
350 by Damon Lynch
Add pause functionality, and bug fixes in early termination of processes.
2426
    def resume_download(self):
377 by Damon Lynch
time remaining and download speed
2427
        for scan_pid in self.download_active_by_scan_pid:
2428
            self.time_remaining.set_time_mark(scan_pid)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2429
377 by Damon Lynch
time remaining and download speed
2430
        self.time_check.set_download_mark()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2431
350 by Damon Lynch
Add pause functionality, and bug fixes in early termination of processes.
2432
        self.copy_files_manager.start()
340 by Damon Lynch
Move download tracking functionality from copyfile process to main program process, in preparation for coming file renaming and backup functionality
2433
2434
    def download_files(self, files, scan_pid):
2435
        """
342 by Damon Lynch
Move downloaded files from temp dir to destination folders; proper error handling of gio calls, using gio.Error
2436
        Initiate downloading and renaming of files
340 by Damon Lynch
Move download tracking functionality from copyfile process to main program process, in preparation for coming file renaming and backup functionality
2437
        """
2438
        # Check which file types will be downloaded for this particular process
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2439
        no_photos_to_download = self.files_of_type_present(files,
2440
                                                    rpdfile.FILE_TYPE_PHOTO,
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
2441
                                                    return_file_count=True)
2442
        if no_photos_to_download:
340 by Damon Lynch
Move download tracking functionality from copyfile process to main program process, in preparation for coming file renaming and backup functionality
2443
            photo_download_folder = self.prefs.download_folder
2444
        else:
2445
            photo_download_folder = None
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2446
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
2447
        if DOWNLOAD_VIDEO:
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2448
            no_videos_to_download = self.files_of_type_present(files,
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
2449
                                        rpdfile.FILE_TYPE_VIDEO,
2450
                                        return_file_count=True)
2451
            if no_videos_to_download:
2452
                video_download_folder = self.prefs.video_download_folder
2453
            else:
2454
                video_download_folder = None
340 by Damon Lynch
Move download tracking functionality from copyfile process to main program process, in preparation for coming file renaming and backup functionality
2455
        else:
2456
            video_download_folder = None
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
2457
            no_videos_to_download = 0
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2458
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
2459
        photo_download_size, video_download_size = self.size_files_to_be_downloaded(files)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2460
        self.download_tracker.init_stats(scan_pid=scan_pid,
2461
                                photo_size_in_bytes=photo_download_size,
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
2462
                                video_size_in_bytes=video_download_size,
2463
                                no_photos_to_download=no_photos_to_download,
2464
                                no_videos_to_download=no_videos_to_download)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2465
2466
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
2467
        download_size = photo_download_size + video_download_size
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2468
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2469
        if self.prefs.backup_images:
408 by Damon Lynch
Fixed display of error messages when using more than one backup device; fixed bug in Swedish translation; fixed multiple spelling errors in ChangeLog
2470
            download_size = download_size + ((self.no_photo_backup_devices * photo_download_size) +
2471
                                             (self.no_video_backup_devices * video_download_size))
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2472
377 by Damon Lynch
time remaining and download speed
2473
        self.time_remaining.set(scan_pid, download_size)
2474
        self.time_check.set_download_mark()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2475
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2476
        self.download_active_by_scan_pid.append(scan_pid)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2477
2478
375 by Damon Lynch
auto unmount and system notifications on download completion
2479
        if len(self.download_active_by_scan_pid) > 1:
2480
            self.display_summary_notification = True
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2481
382 by Damon Lynch
generate thumbnails preferences option
2482
        if self.auto_start_is_on and self.prefs.generate_thumbnails:
381 by Damon Lynch
when user clicks download and thumbnail generation is still occuring, generate the thumbmnails in the copy file process
2483
            for rpd_file in files:
2484
                rpd_file.generate_thumbnail = True
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
2485
470 by Damon Lynch
Implementation of file verification for downloaded file, but not yet not the backup
2486
        verify_file = self.prefs.verify_file
2487
        if verify_file:
2488
            # since a file might be modified in the file modify process,
2489
            # if it will be backed up, need to refresh the md5 once it has
2490
            # been modified
2491
            refresh_md5_on_file_change = self.prefs.backup_images
2492
        else:
2493
            refresh_md5_on_file_change = False
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
2494
        modify_files_during_download = self.modify_files_during_download()
2495
        if modify_files_during_download:
470 by Damon Lynch
Implementation of file verification for downloaded file, but not yet not the backup
2496
            self.file_modify_manager.add_task((scan_pid, self.prefs.auto_rotate_jpeg, self.focal_length, verify_file, refresh_md5_on_file_change))
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
2497
            modify_pipe = self.file_modify_manager.get_modify_pipe(scan_pid)
2498
        else:
2499
            modify_pipe = None
2500
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2501
340 by Damon Lynch
Move download tracking functionality from copyfile process to main program process, in preparation for coming file renaming and backup functionality
2502
        # Initiate copy files process
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2503
        self.copy_files_manager.add_task((photo_download_folder,
340 by Damon Lynch
Move download tracking functionality from copyfile process to main program process, in preparation for coming file renaming and backup functionality
2504
                              video_download_folder, scan_pid,
470 by Damon Lynch
Implementation of file verification for downloaded file, but not yet not the backup
2505
                              files, verify_file,
2506
                              modify_files_during_download,
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
2507
                              modify_pipe))
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2508
340 by Damon Lynch
Move download tracking functionality from copyfile process to main program process, in preparation for coming file renaming and backup functionality
2509
    def copy_files_results(self, source, condition):
2510
        """
2511
        Handle results from copy files process
2512
        """
342 by Damon Lynch
Move downloaded files from temp dir to destination folders; proper error handling of gio calls, using gio.Error
2513
        #FIXME: must handle early termination / pause of copy files process
340 by Damon Lynch
Move download tracking functionality from copyfile process to main program process, in preparation for coming file renaming and backup functionality
2514
        connection = self.copy_files_manager.get_pipe(source)
2515
        conn_type, msg_data = connection.recv()
2516
        if conn_type == rpdmp.CONN_PARTIAL:
2517
            msg_type, data = msg_data
342 by Damon Lynch
Move downloaded files from temp dir to destination folders; proper error handling of gio calls, using gio.Error
2518
2519
            if msg_type == rpdmp.MSG_TEMP_DIRS:
2520
                scan_pid, photo_temp_dir, video_temp_dir = data
431.1.9 by Damon Lynch
Display path info in debugging log output. Display running totals during scan.
2521
                self.temp_dirs_by_scan_pid[scan_pid] = (photo_temp_dir, video_temp_dir)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2522
431.1.9 by Damon Lynch
Display path info in debugging log output. Display running totals during scan.
2523
                # Report which temporary directories are being used for this
2524
                # download
2525
                if photo_temp_dir and video_temp_dir:
2526
                    logger.debug("Using temp dirs %s (photos) & %s (videos)",
2527
                                photo_temp_dir, video_temp_dir)
2528
                elif photo_temp_dir:
2529
                    logger.debug("Using temp dir %s (photos)",
2530
                                photo_temp_dir)
2531
                else:
2532
                    logger.debug("Using temp dir %s (videos)",
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2533
                                video_temp_dir)
342 by Damon Lynch
Move downloaded files from temp dir to destination folders; proper error handling of gio calls, using gio.Error
2534
            elif msg_type == rpdmp.MSG_BYTES:
377 by Damon Lynch
time remaining and download speed
2535
                scan_pid, total_downloaded, chunk_downloaded = data
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2536
                self.download_tracker.set_total_bytes_copied(scan_pid,
367 by Damon Lynch
Use downloadtracker class and adjust percent complete by including time taken to rename files.
2537
                                                             total_downloaded)
377 by Damon Lynch
time remaining and download speed
2538
                self.time_check.increment(bytes_downloaded=chunk_downloaded)
367 by Damon Lynch
Use downloadtracker class and adjust percent complete by including time taken to rename files.
2539
                percent_complete = self.download_tracker.get_percent_complete(scan_pid)
340 by Damon Lynch
Move download tracking functionality from copyfile process to main program process, in preparation for coming file renaming and backup functionality
2540
                self.device_collection.update_progress(scan_pid, percent_complete,
2541
                                            None, None)
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2542
                self.time_remaining.update(scan_pid, bytes_downloaded=chunk_downloaded)
340 by Damon Lynch
Move download tracking functionality from copyfile process to main program process, in preparation for coming file renaming and backup functionality
2543
            elif msg_type == rpdmp.MSG_FILE:
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
2544
                self.copy_file_results_single_file(data)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2545
340 by Damon Lynch
Move download tracking functionality from copyfile process to main program process, in preparation for coming file renaming and backup functionality
2546
            return True
2547
        else:
341 by Damon Lynch
Create base class DaemonTaskManager; preliminary infrstructure work on subfolder creation and file renaming.
2548
            # Process is complete, i.e. conn_type == rpdmp.CONN_COMPLETE
2549
            connection.close()
2550
            return False
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2551
342 by Damon Lynch
Move downloaded files from temp dir to destination folders; proper error handling of gio calls, using gio.Error
2552
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
2553
    def copy_file_results_single_file(self, data):
2554
        """
2555
        Handles results from one of two processes:
2556
        1. copy_files
2557
        2. file_modify
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2558
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
2559
        Operates after a single file has been copied from the download device
2560
        to the local folder.
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2561
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
2562
        Calls the process to rename files and create subfolders (subfolderfile)
2563
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2564
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
2565
        download_succeeded, rpd_file, download_count, temp_full_file_name, thumbnail_icon, thumbnail = data
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2566
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
2567
        if thumbnail is not None or thumbnail_icon is not None:
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2568
            self.thumbnails.update_thumbnail((rpd_file.unique_id,
2569
                                              thumbnail_icon,
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
2570
                                              thumbnail))
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2571
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
2572
        self.download_tracker.set_download_count_for_file(
2573
                                    rpd_file.unique_id, download_count)
2574
        self.download_tracker.set_download_count(
2575
                                    rpd_file.scan_pid, download_count)
2576
        rpd_file.download_start_time = self.download_start_time
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2577
471 by Damon Lynch
Added file verification on backups too.
2578
        if download_succeeded:
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
2579
            # Insert preference values needed for name generation
2580
            rpd_file = prefsrapid.insert_pref_lists(self.prefs, rpd_file)
2581
            rpd_file.strip_characters = self.prefs.strip_characters
2582
            rpd_file.download_folder = self.prefs.get_download_folder_for_file_type(rpd_file.file_type)
2583
            rpd_file.download_conflict_resolution = self.prefs.download_conflict_resolution
2584
            rpd_file.synchronize_raw_jpg = self.prefs.must_synchronize_raw_jpg()
2585
            rpd_file.job_code = self.job_code
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2586
470 by Damon Lynch
Implementation of file verification for downloaded file, but not yet not the backup
2587
        # Call this even if download did not succeed e.g. file verification error
2588
        self.subfolder_file_manager.rename_file_and_move_to_subfolder(
2589
                download_succeeded,
2590
                download_count,
2591
                rpd_file
2592
                )
2593
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
2594
    def file_modify_results(self, source, condition):
2595
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2596
        'file modify' is a process that runs immediately after 'copy files',
2597
        meaning there can be more than one at one time.
2598
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
2599
        It runs before the renaming process.
2600
        """
2601
        connection = self.file_modify_manager.get_pipe(source)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2602
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
2603
        conn_type, data = connection.recv()
2604
        if conn_type == rpdmp.CONN_PARTIAL:
2605
            self.copy_file_results_single_file(data)
2606
            return True
2607
        else:
2608
            # Process is complete, i.e. conn_type == rpdmp.CONN_COMPLETE
2609
            connection.close()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2610
            return False
2611
2612
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2613
    def download_is_occurring(self):
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2614
        """Returns True if a file is currently being downloaded, renamed or
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2615
           backed up
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2616
        """
381 by Damon Lynch
when user clicks download and thumbnail generation is still occuring, generate the thumbmnails in the copy file process
2617
        return not len(self.download_active_by_scan_pid) == 0
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2618
341 by Damon Lynch
Create base class DaemonTaskManager; preliminary infrstructure work on subfolder creation and file renaming.
2619
    # # #
2620
    # Create folder and file names for downloaded files
2621
    # # #
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2622
446 by Damon Lynch
Debugging backup code - partial and switched off. Fixed bug #1093330: Photo rename ignores SubSeconds when 00.
2623
    def subfolder_file_results(self, move_succeeded, rpd_file, download_count):
341 by Damon Lynch
Create base class DaemonTaskManager; preliminary infrstructure work on subfolder creation and file renaming.
2624
        """
2625
        Handle results of subfolder creation and file renaming
2626
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2627
342 by Damon Lynch
Move downloaded files from temp dir to destination folders; proper error handling of gio calls, using gio.Error
2628
        scan_pid = rpd_file.scan_pid
2629
        unique_id = rpd_file.unique_id
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2630
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2631
        if rpd_file.status == config.STATUS_DOWNLOADED_WITH_WARNING:
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2632
            self.log_error(config.WARNING, rpd_file.error_title,
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2633
                           rpd_file.error_msg, rpd_file.error_extra_detail)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2634
465 by Damon Lynch
Fixed bug where missing backup folder on auto detected devices could cause the download to fail
2635
        if self.prefs.backup_images:
2636
            if self.backup_possible(rpd_file.file_type):
2637
                if self.prefs.backup_device_autodetection:
2638
                    if rpd_file.file_type == rpdfile.FILE_TYPE_PHOTO:
2639
                        path_suffix = self.prefs.backup_identifier
2640
                    else:
2641
                        path_suffix = self.prefs.video_backup_identifier
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2642
                else:
465 by Damon Lynch
Fixed bug where missing backup folder on auto detected devices could cause the download to fail
2643
                    path_suffix = None
2644
2645
                self.backup_manager.backup_file(move_succeeded, rpd_file,
2646
                                        path_suffix,
2647
                                        self.prefs.backup_duplicate_overwrite,
471 by Damon Lynch
Added file verification on backups too.
2648
                                        self.prefs.verify_file,
465 by Damon Lynch
Fixed bug where missing backup folder on auto detected devices could cause the download to fail
2649
                                        download_count)
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2650
            else:
465 by Damon Lynch
Fixed bug where missing backup folder on auto detected devices could cause the download to fail
2651
                if rpd_file.status ==  config.STATUS_DOWNLOAD_FAILED:
2652
                    rpd_file.status = config.STATUS_DOWNLOAD_AND_BACKUP_FAILED
2653
                else:
2654
                    rpd_file.status = config.STATUS_BACKUP_PROBLEM
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2655
465 by Damon Lynch
Fixed bug where missing backup folder on auto detected devices could cause the download to fail
2656
                self.file_download_finished(move_succeeded, rpd_file)
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2657
        else:
2658
            self.file_download_finished(move_succeeded, rpd_file)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2659
465 by Damon Lynch
Fixed bug where missing backup folder on auto detected devices could cause the download to fail
2660
    def backup_possible(self, file_type):
2661
        if file_type == rpdfile.FILE_TYPE_PHOTO:
2662
            return self.no_photo_backup_devices > 0
2663
        elif file_type == rpdfile.FILE_TYPE_VIDEO:
2664
            return self.no_video_backup_devices > 0
2665
        else:
2666
            logger.critical("Unrecognized file type when determining if backup is possible")
2667
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2668
408 by Damon Lynch
Fixed display of error messages when using more than one backup device; fixed bug in Swedish translation; fixed multiple spelling errors in ChangeLog
2669
    def multiple_backup_devices(self, file_type):
2670
        """Returns true if more than one backup device is being used for that
2671
        file type
2672
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2673
        return ((file_type == rpdfile.FILE_TYPE_PHOTO and
408 by Damon Lynch
Fixed display of error messages when using more than one backup device; fixed bug in Swedish translation; fixed multiple spelling errors in ChangeLog
2674
                 self.no_photo_backup_devices > 1) or
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2675
                (file_type == rpdfile.FILE_TYPE_VIDEO and
408 by Damon Lynch
Fixed display of error messages when using more than one backup device; fixed bug in Swedish translation; fixed multiple spelling errors in ChangeLog
2676
                 self.no_video_backup_devices > 1))
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2677
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2678
    def backup_results(self, source, condition):
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
2679
        """
2680
        Handle results sent from backup processes
2681
        """
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2682
        connection = self.backup_manager.get_pipe(source)
2683
        conn_type, msg_data = connection.recv()
2684
        if conn_type == rpdmp.CONN_PARTIAL:
2685
            msg_type, data = msg_data
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2686
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2687
            if msg_type == rpdmp.MSG_BYTES:
2688
                scan_pid, backup_pid, total_downloaded, chunk_downloaded = data
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2689
                self.download_tracker.increment_bytes_backed_up(scan_pid,
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2690
                                                             chunk_downloaded)
2691
                self.time_check.increment(bytes_downloaded=chunk_downloaded)
2692
                percent_complete = self.download_tracker.get_percent_complete(scan_pid)
2693
                self.device_collection.update_progress(scan_pid, percent_complete,
2694
                                            None, None)
2695
                self.time_remaining.update(scan_pid, bytes_downloaded=chunk_downloaded)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2696
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2697
            elif msg_type == rpdmp.MSG_FILE:
465 by Damon Lynch
Fixed bug where missing backup folder on auto detected devices could cause the download to fail
2698
                backup_succeeded, do_backup, rpd_file = data
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2699
408 by Damon Lynch
Fixed display of error messages when using more than one backup device; fixed bug in Swedish translation; fixed multiple spelling errors in ChangeLog
2700
                # Only show an error message if there is more than one device
2701
                # backing up files of this type - if that is the case,
459 by Damon Lynch
Purge GIO code from subfolder.py and backupfile.py. Update copyright notices.
2702
                # do not want to rely on showing an error message in the
408 by Damon Lynch
Fixed display of error messages when using more than one backup device; fixed bug in Swedish translation; fixed multiple spelling errors in ChangeLog
2703
                # function file_download_finished, as it is only called once,
2704
                # when all files have been backed up
465 by Damon Lynch
Fixed bug where missing backup folder on auto detected devices could cause the download to fail
2705
                if not backup_succeeded and self.multiple_backup_devices(rpd_file.file_type) and do_backup:
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2706
                    self.log_error(config.SERIOUS_ERROR,
2707
                        rpd_file.error_title,
408 by Damon Lynch
Fixed display of error messages when using more than one backup device; fixed bug in Swedish translation; fixed multiple spelling errors in ChangeLog
2708
                        rpd_file.error_msg, rpd_file.error_extra_detail)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2709
465 by Damon Lynch
Fixed bug where missing backup folder on auto detected devices could cause the download to fail
2710
                if do_backup:
2711
                    self.download_tracker.file_backed_up(rpd_file.unique_id)
2712
                    if self.download_tracker.all_files_backed_up(rpd_file.unique_id,
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
2713
                                                             rpd_file.file_type):
465 by Damon Lynch
Fixed bug where missing backup folder on auto detected devices could cause the download to fail
2714
                        logger.debug("File %s will not be backed up to any more locations", rpd_file.download_name)
2715
                        self.file_download_finished(backup_succeeded or not do_backup, rpd_file)
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2716
            return True
2717
        else:
2718
            return False
2719
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2720
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2721
    def file_download_finished(self, succeeded, rpd_file):
408 by Damon Lynch
Fixed display of error messages when using more than one backup device; fixed bug in Swedish translation; fixed multiple spelling errors in ChangeLog
2722
        """
2723
        Called when a file has been downloaded i.e. copied, renamed, and backed up
2724
        """
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2725
        scan_pid = rpd_file.scan_pid
2726
        unique_id = rpd_file.unique_id
367 by Damon Lynch
Use downloadtracker class and adjust percent complete by including time taken to rename files.
2727
        # Update error log window if neccessary
408 by Damon Lynch
Fixed display of error messages when using more than one backup device; fixed bug in Swedish translation; fixed multiple spelling errors in ChangeLog
2728
        if not succeeded and not self.multiple_backup_devices(rpd_file.file_type):
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2729
            self.log_error(config.SERIOUS_ERROR, rpd_file.error_title,
366 by Damon Lynch
Add error log window. Allow cancellation of file copy in case of early termination.
2730
                           rpd_file.error_msg, rpd_file.error_extra_detail)
391 by Damon Lynch
0.4.0 release
2731
        elif self.prefs.auto_delete:
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2732
            # record which files to automatically delete when download
391 by Damon Lynch
0.4.0 release
2733
            # completes
2734
            self.download_tracker.add_to_auto_delete(rpd_file)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2735
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2736
        self.thumbnails.update_status_post_download(rpd_file)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2737
        self.download_tracker.file_downloaded_increment(scan_pid,
375 by Damon Lynch
auto unmount and system notifications on download completion
2738
                                                        rpd_file.file_type,
2739
                                                        rpd_file.status)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2740
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
2741
        completed, files_remaining = self._update_file_download_device_progress(scan_pid, unique_id, rpd_file.file_type)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2742
377 by Damon Lynch
time remaining and download speed
2743
        if self.download_is_occurring():
2744
            self.update_time_remaining()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2745
375 by Damon Lynch
auto unmount and system notifications on download completion
2746
        if completed:
2747
            # Last file for this scan pid has been downloaded, so clean temp directory
347 by Damon Lynch
Downloads today sequence implemented using tracking class and 3 shared memory values. Stored sequence number implemented using 1 shared memory value. Session sequence no and letter only partially implemented. kaa metadata loaded only when needed.
2748
            logger.debug("Purging temp directories")
349 by Damon Lynch
Implement mount and unmount callbacks and fix use device dialog
2749
            self._clean_temp_dirs_for_scan_pid(scan_pid)
391 by Damon Lynch
0.4.0 release
2750
            if self.prefs.auto_delete:
2751
                logger.debug("Auto deleting files")
2752
                self.auto_delete(scan_pid)
2753
                self.download_tracker.clear_auto_delete(scan_pid)
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
2754
            self.download_active_by_scan_pid.remove(scan_pid)
377 by Damon Lynch
time remaining and download speed
2755
            self.time_remaining.remove(scan_pid)
375 by Damon Lynch
auto unmount and system notifications on download completion
2756
            self.notify_downloaded_from_device(scan_pid)
2757
            if files_remaining == 0 and self.prefs.auto_unmount:
2758
                self.device_collection.unmount(scan_pid)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2759
2760
377 by Damon Lynch
time remaining and download speed
2761
            if not self.download_is_occurring():
347 by Damon Lynch
Downloads today sequence implemented using tracking class and 3 shared memory values. Stored sequence number implemented using 1 shared memory value. Session sequence no and letter only partially implemented. kaa metadata loaded only when needed.
2762
                logger.debug("Download completed")
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2763
                self.enable_prefs_and_refresh(enabled=True)
375 by Damon Lynch
auto unmount and system notifications on download completion
2764
                self.notify_download_complete()
377 by Damon Lynch
time remaining and download speed
2765
                self.download_progressbar.set_fraction(0.0)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2766
347 by Damon Lynch
Downloads today sequence implemented using tracking class and 3 shared memory values. Stored sequence number implemented using 1 shared memory value. Session sequence no and letter only partially implemented. kaa metadata loaded only when needed.
2767
                self.prefs.stored_sequence_no = self.stored_sequence_value.value
2768
                self.downloads_today_tracker.set_raw_downloads_today_from_int(self.downloads_today_value.value)
2769
                self.downloads_today_tracker.set_raw_downloads_today_date(self.downloads_today_date_value.value)
2770
                self.prefs.set_downloads_today_from_tracker(self.downloads_today_tracker)
375 by Damon Lynch
auto unmount and system notifications on download completion
2771
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2772
                if ((self.prefs.auto_exit and self.download_tracker.no_errors_or_warnings())
375 by Damon Lynch
auto unmount and system notifications on download completion
2773
                                                or self.prefs.auto_exit_force):
2774
                    if not self.thumbnails.files_remain_to_download():
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2775
                        self._terminate_processes()
375 by Damon Lynch
auto unmount and system notifications on download completion
2776
                        gtk.main_quit()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2777
375 by Damon Lynch
auto unmount and system notifications on download completion
2778
                self.download_tracker.purge_all()
377 by Damon Lynch
time remaining and download speed
2779
                self.speed_label.set_label(" ")
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2780
349 by Damon Lynch
Implement mount and unmount callbacks and fix use device dialog
2781
                self.display_free_space()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2782
350 by Damon Lynch
Add pause functionality, and bug fixes in early termination of processes.
2783
                self.set_download_action_label(is_download=True)
353 by Damon Lynch
Show download status in thumbnail section of window; checkbox is now beside filename. "Include in download" checkbox only visible if file not yet downloaded. Togge download action sensitivity based on this checkbox.
2784
                self.set_download_action_sensitivity()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2785
371 by Damon Lynch
finish implementing job codes
2786
                self.job_code = ''
373 by Damon Lynch
auto start download on startup and on device insertion; adjust vertical pane when device is inserted; when auto start is enabled, delay making checkboxes invisible
2787
                self.download_start_time = None
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2788
2789
377 by Damon Lynch
time remaining and download speed
2790
    def update_time_remaining(self):
2791
        update, download_speed = self.time_check.check_for_update()
2792
        if update:
2793
            self.speed_label.set_text(download_speed)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2794
377 by Damon Lynch
time remaining and download speed
2795
            time_remaining = self.time_remaining.time_remaining()
2796
            if time_remaining:
2797
                secs =  int(time_remaining)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2798
377 by Damon Lynch
time remaining and download speed
2799
                if secs == 0:
2800
                    message = ""
2801
                elif secs == 1:
2802
                    message = _("About 1 second remaining")
2803
                elif secs < 60:
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2804
                    message = _("About %i seconds remaining") % secs
377 by Damon Lynch
time remaining and download speed
2805
                elif secs == 60:
2806
                    message = _("About 1 minute remaining")
2807
                else:
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2808
                    # Translators: in the text '%(minutes)i:%(seconds)02i', only the : should be translated, if needed.
377 by Damon Lynch
time remaining and download speed
2809
                    # '%(minutes)i' and '%(seconds)02i' should not be modified or left out. They are used to format and display the amount
2810
                    # of time the download has remainging, e.g. 'About 5:36 minutes remaining'
2811
                    message = _("About %(minutes)i:%(seconds)02i minutes remaining") % {'minutes': secs / 60, 'seconds': secs % 60}
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2812
377 by Damon Lynch
time remaining and download speed
2813
                self.rapid_statusbar.pop(self.statusbar_context_id)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2814
                self.rapid_statusbar.push(self.statusbar_context_id, message)
2815
391 by Damon Lynch
0.4.0 release
2816
    def auto_delete(self, scan_pid):
2817
        """Delete files from download device at completion of download"""
2818
        for file in self.download_tracker.get_files_to_auto_delete(scan_pid):
2819
            f = gio.File(file)
2820
            try:
2821
                f.delete(cancellable=None)
2822
            except gio.Error, inst:
2823
                logger.error("Failure deleting file %s", file)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2824
                logger.error(inst)
2825
375 by Damon Lynch
auto unmount and system notifications on download completion
2826
    def file_types_by_number(self, no_photos, no_videos):
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2827
        """
375 by Damon Lynch
auto unmount and system notifications on download completion
2828
        returns a string to be displayed to the user that can be used
2829
        to show if a value refers to photos or videos or both, or just one
2830
        of each
2831
        """
2832
        if (no_videos > 0) and (no_photos > 0):
2833
            v = _('photos and videos')
2834
        elif (no_videos == 0) and (no_photos == 0):
2835
            v = _('photos or videos')
2836
        elif no_videos > 0:
2837
            if no_videos > 1:
2838
                v = _('videos')
2839
            else:
2840
                v = _('video')
2841
        else:
2842
            if no_photos > 1:
2843
                v = _('photos')
2844
            else:
2845
                v = _('photo')
2846
        return v
2847
2848
    def notify_downloaded_from_device(self, scan_pid):
2849
        device = self.device_collection.get_device(scan_pid)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2850
375 by Damon Lynch
auto unmount and system notifications on download completion
2851
        if device.mount is None:
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2852
            notification_name = PROGRAM_NAME
2853
            icon = self.application_icon
375 by Damon Lynch
auto unmount and system notifications on download completion
2854
        else:
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
2855
            notification_name  = device.get_name()
2856
            icon = device.get_icon(self.notification_icon_size)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2857
377 by Damon Lynch
time remaining and download speed
2858
        no_photos_downloaded = self.download_tracker.get_no_files_downloaded(
2859
                                            scan_pid, rpdfile.FILE_TYPE_PHOTO)
2860
        no_videos_downloaded = self.download_tracker.get_no_files_downloaded(
2861
                                            scan_pid, rpdfile.FILE_TYPE_VIDEO)
2862
        no_photos_failed = self.download_tracker.get_no_files_failed(
2863
                                            scan_pid, rpdfile.FILE_TYPE_PHOTO)
2864
        no_videos_failed = self.download_tracker.get_no_files_failed(
2865
                                            scan_pid, rpdfile.FILE_TYPE_VIDEO)
2866
        no_files_downloaded = no_photos_downloaded + no_videos_downloaded
2867
        no_files_failed = no_photos_failed + no_videos_failed
375 by Damon Lynch
auto unmount and system notifications on download completion
2868
        no_warnings = self.download_tracker.get_no_warnings(scan_pid)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2869
377 by Damon Lynch
time remaining and download speed
2870
        file_types = self.file_types_by_number(no_photos_downloaded, no_videos_downloaded)
2871
        file_types_failed = self.file_types_by_number(no_photos_failed, no_videos_failed)
375 by Damon Lynch
auto unmount and system notifications on download completion
2872
        message = _("%(noFiles)s %(filetypes)s downloaded") % \
377 by Damon Lynch
time remaining and download speed
2873
                   {'noFiles':no_files_downloaded, 'filetypes': file_types}
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2874
377 by Damon Lynch
time remaining and download speed
2875
        if no_files_failed:
2876
            message += "\n" + _("%(noFiles)s %(filetypes)s failed to download") % {'noFiles':no_files_failed, 'filetypes':file_types_failed}
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2877
375 by Damon Lynch
auto unmount and system notifications on download completion
2878
        if no_warnings:
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2879
            message = "%s\n%s " % (message,  no_warnings) + _("warnings")
2880
446 by Damon Lynch
Debugging backup code - partial and switched off. Fixed bug #1093330: Photo rename ignores SubSeconds when 00.
2881
        if use_pynotify:
2882
            n = pynotify.Notification(notification_name, message)
2883
            n.set_icon_from_pixbuf(icon)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2884
450 by Damon Lynch
Don't crash when the sending a system notfication message and the message returns an error
2885
            try:
2886
                n.show()
2887
            except:
2888
                logger.error("Unable to display message using notification system")
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2889
375 by Damon Lynch
auto unmount and system notifications on download completion
2890
    def notify_download_complete(self):
2891
        if self.display_summary_notification:
2892
            message = _("All downloads complete")
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2893
375 by Damon Lynch
auto unmount and system notifications on download completion
2894
            # photo downloads
2895
            photo_downloads = self.download_tracker.total_photos_downloaded
2896
            if photo_downloads:
2897
                filetype = self.file_types_by_number(photo_downloads, 0)
2898
                message += "\n" + _("%(number)s %(numberdownloaded)s") % \
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2899
                            {'number': photo_downloads,
375 by Damon Lynch
auto unmount and system notifications on download completion
2900
                            'numberdownloaded': _("%(filetype)s downloaded") % \
2901
                            {'filetype': filetype}}
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2902
375 by Damon Lynch
auto unmount and system notifications on download completion
2903
            # photo failures
2904
            photo_failures = self.download_tracker.total_photo_failures
2905
            if photo_failures:
2906
                filetype = self.file_types_by_number(photo_failures, 0)
2907
                message += "\n" + _("%(number)s %(numberdownloaded)s") % \
2908
                            {'number': photo_failures,
2909
                            'numberdownloaded': _("%(filetype)s failed to download") % \
2910
                            {'filetype': filetype}}
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2911
375 by Damon Lynch
auto unmount and system notifications on download completion
2912
            # video downloads
2913
            video_downloads = self.download_tracker.total_videos_downloaded
2914
            if video_downloads:
2915
                filetype = self.file_types_by_number(0, video_downloads)
2916
                message += "\n" + _("%(number)s %(numberdownloaded)s") % \
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2917
                            {'number': video_downloads,
375 by Damon Lynch
auto unmount and system notifications on download completion
2918
                            'numberdownloaded': _("%(filetype)s downloaded") % \
2919
                            {'filetype': filetype}}
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2920
375 by Damon Lynch
auto unmount and system notifications on download completion
2921
            # video failures
2922
            video_failures = self.download_tracker.total_video_failures
2923
            if video_failures:
2924
                filetype = self.file_types_by_number(0, video_failures)
2925
                message += "\n" + _("%(number)s %(numberdownloaded)s") % \
2926
                            {'number': video_failures,
2927
                            'numberdownloaded': _("%(filetype)s failed to download") % \
2928
                            {'filetype': filetype}}
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2929
375 by Damon Lynch
auto unmount and system notifications on download completion
2930
            # warnings
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2931
            warnings = self.download_tracker.total_warnings
375 by Damon Lynch
auto unmount and system notifications on download completion
2932
            if warnings:
2933
                message += "\n" + _("%(number)s %(numberdownloaded)s") % \
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2934
                            {'number': warnings,
375 by Damon Lynch
auto unmount and system notifications on download completion
2935
                            'numberdownloaded': _("warnings")}
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2936
446 by Damon Lynch
Debugging backup code - partial and switched off. Fixed bug #1093330: Photo rename ignores SubSeconds when 00.
2937
            if use_pynotify:
2938
                n = pynotify.Notification(PROGRAM_NAME, message)
2939
                n.set_icon_from_pixbuf(self.application_icon)
450 by Damon Lynch
Don't crash when the sending a system notfication message and the message returns an error
2940
                try:
2941
                    n.show()
2942
                except:
2943
                    logger.error("Unable to display message using notification system")
375 by Damon Lynch
auto unmount and system notifications on download completion
2944
            self.display_summary_notification = False # don't show it again unless needed
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2945
2946
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
2947
    def _update_file_download_device_progress(self, scan_pid, unique_id, file_type):
342 by Damon Lynch
Move downloaded files from temp dir to destination folders; proper error handling of gio calls, using gio.Error
2948
        """
2949
        Increments the progress bar for an individual device
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2950
375 by Damon Lynch
auto unmount and system notifications on download completion
2951
        Returns if the download is completed for that scan_pid
2952
        It also returns the number of files remaining for the scan_pid, BUT
2953
        this value is valid ONLY if the download is completed
342 by Damon Lynch
Move downloaded files from temp dir to destination folders; proper error handling of gio calls, using gio.Error
2954
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2955
375 by Damon Lynch
auto unmount and system notifications on download completion
2956
        files_downloaded = self.download_tracker.get_download_count_for_file(unique_id)
2957
        files_to_download = self.download_tracker.get_no_files_in_download(scan_pid)
2958
        file_types = self.download_tracker.get_file_types_present(scan_pid)
2959
        completed = files_downloaded == files_to_download
465 by Damon Lynch
Fixed bug where missing backup folder on auto detected devices could cause the download to fail
2960
        if completed and (self.prefs.backup_images and self.backup_possible(file_type)):
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
2961
            completed = self.download_tracker.all_files_backed_up(unique_id, file_type)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2962
375 by Damon Lynch
auto unmount and system notifications on download completion
2963
        if completed:
2964
            files_remaining = self.thumbnails.get_no_files_remaining(scan_pid)
2965
        else:
2966
            files_remaining = 0
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2967
375 by Damon Lynch
auto unmount and system notifications on download completion
2968
        if completed and files_remaining:
377 by Damon Lynch
time remaining and download speed
2969
            # e.g.: 3 of 205 photos and videos (202 remaining)
375 by Damon Lynch
auto unmount and system notifications on download completion
2970
            progress_bar_text = _("%(number)s of %(total)s %(filetypes)s (%(remaining)s remaining)") % {
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2971
                                  'number':  files_downloaded,
375 by Damon Lynch
auto unmount and system notifications on download completion
2972
                                  'total': files_to_download,
2973
                                  'filetypes': file_types,
2974
                                  'remaining': files_remaining}
2975
        else:
377 by Damon Lynch
time remaining and download speed
2976
            # e.g.: 205 of 205 photos and videos
375 by Damon Lynch
auto unmount and system notifications on download completion
2977
            progress_bar_text = _("%(number)s of %(total)s %(filetypes)s") % \
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2978
                                 {'number':  files_downloaded,
375 by Damon Lynch
auto unmount and system notifications on download completion
2979
                                  'total': files_to_download,
2980
                                  'filetypes': file_types}
367 by Damon Lynch
Use downloadtracker class and adjust percent complete by including time taken to rename files.
2981
        percent_complete = self.download_tracker.get_percent_complete(scan_pid)
2982
        self.device_collection.update_progress(scan_pid=scan_pid,
2983
                                        percent_complete=percent_complete,
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2984
                                        progress_bar_text=progress_bar_text,
375 by Damon Lynch
auto unmount and system notifications on download completion
2985
                                        bytes_downloaded=None)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2986
377 by Damon Lynch
time remaining and download speed
2987
        percent_complete = self.download_tracker.get_overall_percent_complete()
2988
        self.download_progressbar.set_fraction(percent_complete)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2989
375 by Damon Lynch
auto unmount and system notifications on download completion
2990
        return (completed, files_remaining)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
2991
342 by Damon Lynch
Move downloaded files from temp dir to destination folders; proper error handling of gio calls, using gio.Error
2992
350 by Damon Lynch
Add pause functionality, and bug fixes in early termination of processes.
2993
    def _clean_all_temp_dirs(self):
2994
        """
2995
        Cleans all temp dirs if they exist
2996
        """
2997
        for scan_pid in self.temp_dirs_by_scan_pid:
2998
            for temp_dir in self.temp_dirs_by_scan_pid[scan_pid]:
2999
                self._purge_dir(temp_dir)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3000
350 by Damon Lynch
Add pause functionality, and bug fixes in early termination of processes.
3001
        self.temp_dirs_by_scan_pid = {}
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3002
3003
349 by Damon Lynch
Implement mount and unmount callbacks and fix use device dialog
3004
    def _clean_temp_dirs_for_scan_pid(self, scan_pid):
3005
        """
3006
        Deletes temp files and folders used in download
3007
        """
3008
        for temp_dir in self.temp_dirs_by_scan_pid[scan_pid]:
3009
            self._purge_dir(temp_dir)
3010
        del self.temp_dirs_by_scan_pid[scan_pid]
3011
342 by Damon Lynch
Move downloaded files from temp dir to destination folders; proper error handling of gio calls, using gio.Error
3012
    def _purge_dir(self, directory):
3013
        """
3014
        Deletes all files in the directory, and the directory itself.
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3015
342 by Damon Lynch
Move downloaded files from temp dir to destination folders; proper error handling of gio calls, using gio.Error
3016
        Does not recursively traverse any subfolders in the directory.
3017
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3018
342 by Damon Lynch
Move downloaded files from temp dir to destination folders; proper error handling of gio calls, using gio.Error
3019
        if directory:
3020
            try:
3021
                path = gio.File(directory)
3022
                # first delete any files in the temp directory
3023
                # assume there are no directories in the temp directory
3024
                file_attributes = "standard::name,standard::type"
3025
                children = path.enumerate_children(file_attributes)
3026
                for child in children:
3027
                    f = path.get_child(child.get_name())
3028
                    f.delete(cancellable=None)
3029
                path.delete(cancellable=None)
347 by Damon Lynch
Downloads today sequence implemented using tracking class and 3 shared memory values. Stored sequence number implemented using 1 shared memory value. Session sequence no and letter only partially implemented. kaa metadata loaded only when needed.
3030
                logger.debug("Deleted directory %s", directory)
342 by Damon Lynch
Move downloaded files from temp dir to destination folders; proper error handling of gio calls, using gio.Error
3031
            except gio.Error, inst:
3032
                logger.error("Failure deleting temporary folder %s", directory)
3033
                logger.error(inst)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3034
3035
3036
    # # #
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3037
    # Preferences
3038
    # # #
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3039
3040
3041
    def _init_prefs(self, auto_detect, device_location):
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3042
        self.prefs = prefsrapid.RapidPreferences()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3043
431.1.10 by Damon Lynch
Added command line option to specify device detection and location
3044
        # handle device preferences set from the command line
3045
        # do this before preference changes are handled with notify_add
3046
        if auto_detect:
3047
            self.prefs.device_autodetection = True
3048
        elif device_location:
3049
            self.prefs.device_location = device_location
3050
            self.prefs.device_autodetection = False
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3051
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3052
        self.prefs.notify_add(self.on_preference_changed)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3053
3054
        # flag to indicate whether the user changed some preferences that
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3055
        # indicate the image and backup devices should be setup again
3056
        self.rerun_setup_available_image_and_video_media = False
3057
        self.rerun_setup_available_backup_media = False
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3058
3059
        # flag to indicate that the preferences dialog window is being
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3060
        # displayed to the user
347 by Damon Lynch
Downloads today sequence implemented using tracking class and 3 shared memory values. Stored sequence number implemented using 1 shared memory value. Session sequence no and letter only partially implemented. kaa metadata loaded only when needed.
3061
        self.preferences_dialog_displayed = False
3062
3063
        # flag to indicate that the user has modified the download today
3064
        # related values in the preferences dialog window
3065
        self.refresh_downloads_today = False
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3066
3067
        # these values are used to track the number of backup devices /
408 by Damon Lynch
Fixed display of error messages when using more than one backup device; fixed bug in Swedish translation; fixed multiple spelling errors in ChangeLog
3068
        # locations for each file type
3069
        self.no_photo_backup_devices = 0
3070
        self.no_video_backup_devices = 0
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3071
347 by Damon Lynch
Downloads today sequence implemented using tracking class and 3 shared memory values. Stored sequence number implemented using 1 shared memory value. Session sequence no and letter only partially implemented. kaa metadata loaded only when needed.
3072
        self.downloads_today_tracker = self.prefs.get_downloads_today_tracker()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3073
347 by Damon Lynch
Downloads today sequence implemented using tracking class and 3 shared memory values. Stored sequence number implemented using 1 shared memory value. Session sequence no and letter only partially implemented. kaa metadata loaded only when needed.
3074
        downloads_today = self.downloads_today_tracker.get_and_maybe_reset_downloads_today()
3075
        if downloads_today > 0:
3076
            logger.info("Downloads that have occurred so far today: %s", downloads_today)
3077
        else:
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3078
            logger.info("No downloads have occurred so far today")
347 by Damon Lynch
Downloads today sequence implemented using tracking class and 3 shared memory values. Stored sequence number implemented using 1 shared memory value. Session sequence no and letter only partially implemented. kaa metadata loaded only when needed.
3079
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3080
        self.downloads_today_value = Value(c_int,
347 by Damon Lynch
Downloads today sequence implemented using tracking class and 3 shared memory values. Stored sequence number implemented using 1 shared memory value. Session sequence no and letter only partially implemented. kaa metadata loaded only when needed.
3081
                        self.downloads_today_tracker.get_raw_downloads_today())
3082
        self.downloads_today_date_value = Array(c_char,
3083
                        self.downloads_today_tracker.get_raw_downloads_today_date())
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3084
        self.day_start_value = Array(c_char,
347 by Damon Lynch
Downloads today sequence implemented using tracking class and 3 shared memory values. Stored sequence number implemented using 1 shared memory value. Session sequence no and letter only partially implemented. kaa metadata loaded only when needed.
3085
                        self.downloads_today_tracker.get_raw_day_start())
3086
        self.refresh_downloads_today_value = Value(c_bool, False)
3087
        self.stored_sequence_value = Value(c_int, self.prefs.stored_sequence_no)
3088
        self.uses_stored_sequence_no_value = Value(c_bool, self.prefs.any_pref_uses_stored_sequence_no())
348 by Damon Lynch
sequence numbers and letters implemented
3089
        self.uses_session_sequece_no_value = Value(c_bool, self.prefs.any_pref_uses_session_sequece_no())
3090
        self.uses_sequence_letter_value = Value(c_bool, self.prefs.any_pref_uses_sequence_letter_value())
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3091
404 by Damon Lynch
UI changes for manual video backup path
3092
        self.check_prefs_upgrade(__version__)
360 by Damon Lynch
0.4.0 alpha 1 release
3093
        self.prefs.program_version = __version__
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3094
348 by Damon Lynch
sequence numbers and letters implemented
3095
    def _check_for_sequence_value_use(self):
3096
        self.uses_stored_sequence_no_value.value = self.prefs.any_pref_uses_stored_sequence_no()
3097
        self.uses_session_sequece_no_value.value = self.prefs.any_pref_uses_session_sequece_no()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3098
        self.uses_sequence_letter_value.value = self.prefs.any_pref_uses_sequence_letter_value()
3099
404 by Damon Lynch
UI changes for manual video backup path
3100
    def check_prefs_upgrade(self, running_version):
3101
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3102
        Checks if the running version of the program is different from the
404 by Damon Lynch
UI changes for manual video backup path
3103
        version recorded in the preferences.
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3104
404 by Damon Lynch
UI changes for manual video backup path
3105
        If the version is different, the preferences are checked to see
3106
        whether they should be upgraded or not.
3107
        """
3108
        previous_version = self.prefs.program_version
3109
        if len(previous_version) > 0:
3110
            # the program has been run previously for this user
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3111
404 by Damon Lynch
UI changes for manual video backup path
3112
            pv = utilities.pythonify_version(previous_version)
3113
            rv = utilities.pythonify_version(running_version)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3114
404 by Damon Lynch
UI changes for manual video backup path
3115
            if pv <> rv:
3116
                # 0.4.1 and below had only one manual backup location
3117
                # 0.4.2 introduced a distinct video back up location that can be manually set
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3118
                # Therefore must duplicate the previous photo & video manual backup location into the
404 by Damon Lynch
UI changes for manual video backup path
3119
                # new video field, unless it has already been changed already.
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3120
404 by Damon Lynch
UI changes for manual video backup path
3121
                if pv < utilities.pythonify_version('0.4.2'):
3122
                    if self.prefs.backup_video_location == os.path.expanduser('~'):
3123
                        self.prefs.backup_video_location = self.prefs.backup_location
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3124
                        logger.info("Migrated manual backup location preference to videos: %s",
404 by Damon Lynch
UI changes for manual video backup path
3125
                                    self.prefs.backup_video_location)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3126
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3127
    def on_preference_changed(self, key, value):
3128
        """
3129
        Called when user changes the program's preferences
3130
        """
3131
        logger.debug("Preference change detected: %s", key)
3132
3133
        if key == 'show_log_dialog':
3134
            self.menu_log_window.set_active(value)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3135
        elif key in ['device_autodetection', 'device_autodetection_psd',
402 by Damon Lynch
add ignore paths option; add remove remembered devices to prefs window; add focal_length command line option; updated i18n
3136
                     'device_location', 'ignored_paths',
3137
                     'use_re_ignored_paths', 'device_blacklist']:
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3138
            self.rerun_setup_available_image_and_video_media = True
416 by Damon Lynch
preliminary toolbar code
3139
            self._set_from_toolbar_state()
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3140
            if not self.preferences_dialog_displayed:
3141
                self.post_preference_change()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3142
3143
3144
        elif key in ['backup_images', 'backup_device_autodetection',
3145
                     'backup_location', 'backup_video_location',
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
3146
                     'backup_identifier', 'video_backup_identifier']:
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3147
            self.rerun_setup_available_backup_media = True
3148
            if not self.preferences_dialog_displayed:
3149
                self.post_preference_change()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3150
347 by Damon Lynch
Downloads today sequence implemented using tracking class and 3 shared memory values. Stored sequence number implemented using 1 shared memory value. Session sequence no and letter only partially implemented. kaa metadata loaded only when needed.
3151
        # Downloads today and stored sequence numbers are kept in shared memory,
3152
        # so that the subfolderfile daemon process can access and modify them
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3153
347 by Damon Lynch
Downloads today sequence implemented using tracking class and 3 shared memory values. Stored sequence number implemented using 1 shared memory value. Session sequence no and letter only partially implemented. kaa metadata loaded only when needed.
3154
        # Note, totally ignore any changes in downloads today, as it
3155
        # is modified in a special manner via a tracking class
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3156
347 by Damon Lynch
Downloads today sequence implemented using tracking class and 3 shared memory values. Stored sequence number implemented using 1 shared memory value. Session sequence no and letter only partially implemented. kaa metadata loaded only when needed.
3157
        elif key == 'stored_sequence_no':
3158
            if type(value) <> types.IntType:
3159
                logger.critical("Stored sequence number value is malformed")
3160
            else:
3161
                self.stored_sequence_value.value = value
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3162
347 by Damon Lynch
Downloads today sequence implemented using tracking class and 3 shared memory values. Stored sequence number implemented using 1 shared memory value. Session sequence no and letter only partially implemented. kaa metadata loaded only when needed.
3163
        elif key in ['image_rename', 'subfolder', 'video_rename', 'video_subfolder']:
395 by Damon Lynch
Fixed bug #784399: Job code not prompted for after preference change.
3164
            self.need_job_code_for_naming = self.prefs.any_pref_uses_job_code()
347 by Damon Lynch
Downloads today sequence implemented using tracking class and 3 shared memory values. Stored sequence number implemented using 1 shared memory value. Session sequence no and letter only partially implemented. kaa metadata loaded only when needed.
3165
            # Check if stored sequence no is being used
348 by Damon Lynch
sequence numbers and letters implemented
3166
            self._check_for_sequence_value_use()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3167
356 by Damon Lynch
translation updates and free space display minor fixes
3168
        elif key in ['download_folder', 'video_download_folder']:
416 by Damon Lynch
preliminary toolbar code
3169
            self._set_to_toolbar_values()
356 by Damon Lynch
translation updates and free space display minor fixes
3170
            self.display_free_space()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3171
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3172
    def post_preference_change(self):
3173
        if self.rerun_setup_available_image_and_video_media:
377 by Damon Lynch
time remaining and download speed
3174
431.1.11 by Damon Lynch
Fix bug #1014203: create new liststore when clearing the old one, thereby removing the sort, which is super slow.
3175
            logger.info("Download device settings preferences were changed")
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3176
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3177
            self.thumbnails.clear_all()
372 by Damon Lynch
notify user if file rename or subfolder preferences are invalid on program startup
3178
            self.setup_devices(on_startup = False, on_preference_change = True, block_auto_start = True)
376 by Damon Lynch
adjust vertical pane position when additional devices are inserted after preference change; properly track how many thumbnails need to be generated when auto start is on
3179
            self._set_device_collection_size()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3180
362 by Damon Lynch
Leave file preview mode when download devices are changed in the
3181
            if self.main_notebook.get_current_page() == 1: # preview of file
3182
                self.main_notebook.set_current_page(0)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3183
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3184
            self.rerun_setup_available_image_and_video_media = False
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3185
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3186
        if self.rerun_setup_available_backup_media:
3187
            if self.using_volume_monitor():
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3188
                self.start_volume_monitor()
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3189
            logger.info("Backup preferences were changed.")
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3190
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
3191
            self.refresh_backup_media()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3192
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3193
            self.rerun_setup_available_backup_media = False
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3194
347 by Damon Lynch
Downloads today sequence implemented using tracking class and 3 shared memory values. Stored sequence number implemented using 1 shared memory value. Session sequence no and letter only partially implemented. kaa metadata loaded only when needed.
3195
        if self.refresh_downloads_today:
3196
            self.downloads_today_value.value = self.downloads_today_tracker.get_raw_downloads_today()
3197
            self.downloads_today_date_value.value = self.downloads_today_tracker.get_raw_downloads_today_date()
3198
            self.day_start_value.value = self.downloads_today_tracker.get_raw_day_start()
3199
            self.refresh_downloads_today_value.value = True
3200
            self.prefs.set_downloads_today_from_tracker(self.downloads_today_tracker)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3201
3202
3203
337 by Damon Lynch
Place checkbutton beneath thumbnail text; preliminary functionality for download action sensitivity
3204
    # # #
334 by Damon Lynch
Next and previous image controls
3205
    # Main app window management and setup
3206
    # # #
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3207
375 by Damon Lynch
auto unmount and system notifications on download completion
3208
    def _init_pynotify(self):
3209
        """
3210
        Initialize system notification messages
3211
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3212
375 by Damon Lynch
auto unmount and system notifications on download completion
3213
        if not pynotify.init("TestCaps"):
392 by Damon Lynch
modify metadata test code; correct preferences misspelling; relax pynotify startup test
3214
            logger.warning("There might be problems using pynotify.")
375 by Damon Lynch
auto unmount and system notifications on download completion
3215
3216
        do_not_size_icon = False
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3217
        self.notification_icon_size = 48
375 by Damon Lynch
auto unmount and system notifications on download completion
3218
        try:
3219
            info = pynotify.get_server_info()
3220
        except:
3221
            logger.warning("Desktop environment notification server is incorrectly configured.")
3222
        else:
3223
            try:
3224
                if info["name"] == 'notify-osd':
3225
                    do_not_size_icon = True
3226
            except:
3227
                pass
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3228
375 by Damon Lynch
auto unmount and system notifications on download completion
3229
        if do_not_size_icon:
3230
            self.application_icon = gtk.gdk.pixbuf_new_from_file(
3231
                        paths.share_dir('glade3/rapid-photo-downloader.svg'))
3232
        else:
3233
            self.application_icon = gtk.gdk.pixbuf_new_from_file_at_size(
3234
                    paths.share_dir('glade3/rapid-photo-downloader.svg'),
3235
                    self.notification_icon_size, self.notification_icon_size)
3236
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3237
    def _init_widgets(self):
3238
        """
3239
        Initialize widgets in the main window, and variables that point to them
3240
        """
3241
        builder = gtk.Builder()
388 by Damon Lynch
set translation domain for gtkbuilder files
3242
        builder.set_translation_domain(config.APP_NAME)
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3243
        self.builder = builder
3244
        builder.add_from_file(paths.share_dir("glade3/rapid.ui"))
3245
        self.rapidapp = builder.get_object("rapidapp")
416 by Damon Lynch
preliminary toolbar code
3246
        self.from_toolbar = builder.get_object("from_toolbar")
3247
        self.copy_toolbar = builder.get_object("copy_toolbar")
3248
        self.dest_toolbar = builder.get_object("dest_toolbar")
3249
        self.menu_toolbar = builder.get_object("menu_toolbar")
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3250
        self.main_vpaned = builder.get_object("main_vpaned")
3251
        self.main_notebook = builder.get_object("main_notebook")
3252
        self.download_action = builder.get_object("download_action")
416 by Damon Lynch
preliminary toolbar code
3253
        self.download_button = builder.get_object("download_button")
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3254
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3255
        self.download_progressbar = builder.get_object("download_progressbar")
3256
        self.rapid_statusbar = builder.get_object("rapid_statusbar")
3257
        self.statusbar_context_id = self.rapid_statusbar.get_context_id("progress")
3258
        self.device_collection_scrolledwindow = builder.get_object("device_collection_scrolledwindow")
352 by Damon Lynch
enable menus
3259
        self.next_image_action = builder.get_object("next_image_action")
3260
        self.prev_image_action = builder.get_object("prev_image_action")
366 by Damon Lynch
Add error log window. Allow cancellation of file copy in case of early termination.
3261
        self.menu_log_window = builder.get_object("menu_log_window")
377 by Damon Lynch
time remaining and download speed
3262
        self.speed_label = builder.get_object("speed_label")
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
3263
        self.refresh_action = builder.get_object("refresh_action")
3264
        self.preferences_action = builder.get_object("preferences_action")
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3265
352 by Damon Lynch
enable menus
3266
        # Only enable this action when actually displaying a preview
3267
        self.next_image_action.set_sensitive(False)
416 by Damon Lynch
preliminary toolbar code
3268
        self.prev_image_action.set_sensitive(False)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3269
416 by Damon Lynch
preliminary toolbar code
3270
        self._init_toolbars()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3271
352 by Damon Lynch
enable menus
3272
        # About dialog
3273
        builder.add_from_file(paths.share_dir("glade3/about.ui"))
3274
        self.about = builder.get_object("about")
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3275
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3276
        builder.connect_signals(self)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3277
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3278
        self.preview_image = PreviewImage(self, builder)
3279
3280
        thumbnails_scrolledwindow = builder.get_object('thumbnails_scrolledwindow')
3281
        self.thumbnails = ThumbnailDisplay(self)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3282
        thumbnails_scrolledwindow.add(self.thumbnails)
3283
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3284
        #collection of devices from which to download
3285
        self.device_collection_viewport = builder.get_object("device_collection_viewport")
3286
        self.device_collection = DeviceCollection(self)
3287
        self.device_collection_viewport.add(self.device_collection)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3288
366 by Damon Lynch
Add error log window. Allow cancellation of file copy in case of early termination.
3289
        #error log window
3290
        self.error_log = errorlog.ErrorLog(self)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3291
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3292
        # monitor to handle mounts and dismounts
3293
        self.vmonitor = None
349 by Damon Lynch
Implement mount and unmount callbacks and fix use device dialog
3294
        # track scan ids for mount paths - very useful when a device is unmounted
3295
        self.mounts_by_path = {}
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3296
350 by Damon Lynch
Add pause functionality, and bug fixes in early termination of processes.
3297
        # Download action state
3298
        self.download_action_is_download = True
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3299
373 by Damon Lynch
auto start download on startup and on device insertion; adjust vertical pane when device is inserted; when auto start is enabled, delay making checkboxes invisible
3300
        # Track the time a download commences
3301
        self.download_start_time = None
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3302
375 by Damon Lynch
auto unmount and system notifications on download completion
3303
        # Whether a system wide notifcation message should be shown
3304
        # after a download has occurred in parallel
3305
        self.display_summary_notification = False
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3306
377 by Damon Lynch
time remaining and download speed
3307
        # Values used to display how much longer a download will take
3308
        self.time_remaining = downloadtracker.TimeRemaining()
3309
        self.time_check = downloadtracker.TimeCheck()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3310
416 by Damon Lynch
preliminary toolbar code
3311
    def _init_toolbars(self):
3312
        """ Setup the 3 vertical toolbars on the main screen """
3313
        self._setup_from_toolbar()
3314
        self._setup_copy_move_toolbar()
3315
        self._setup_dest_toolbar()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3316
416 by Damon Lynch
preliminary toolbar code
3317
        # size label widths so they are equal, or else the left border of the file chooser will not match
3318
        self.photo_dest_label.realize()
3319
        self._make_widget_widths_equal(self.photo_dest_label, self.video_dest_label)
3320
        self.photo_dest_label.set_alignment(xalign=0.0, yalign=0.5)
3321
        self.video_dest_label.set_alignment(xalign=0.0, yalign=0.5)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3322
416 by Damon Lynch
preliminary toolbar code
3323
        # size copy / move buttons so they are equal in length, so arrows align
3324
        self._make_widget_widths_equal(self.copy_button, self.move_button)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3325
416 by Damon Lynch
preliminary toolbar code
3326
    def _setup_from_toolbar(self):
3327
        self.from_toolbar.set_style(gtk.TOOLBAR_TEXT)
3328
        self.from_toolbar.set_border_width(5)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3329
416 by Damon Lynch
preliminary toolbar code
3330
        from_label = gtk.Label()
3331
        from_label.set_markup("<i>" + _("From") + "</i>")
3332
        self.from_toolbar_label = gtk.ToolItem()
3333
        self.from_toolbar_label.add(from_label)
3334
        self.from_toolbar_label.set_is_important(True)
3335
        self.from_toolbar.insert(self.from_toolbar_label, 0)
3336
3337
        self.auto_detect_button = gtk.ToggleToolButton()
3338
        self.auto_detect_button.set_is_important(True)
3339
        self.auto_detect_button.set_label(_("Auto Detect"))
3340
        self.from_toolbar.insert(self.auto_detect_button, 1)
3341
3342
        self.from_filechooser_button = gtk.FileChooserButton(
3343
            _("Select a folder containing %(file_types)s") % {'file_types':file_types_to_download()})
3344
        self.from_filechooser_button.set_action(
3345
                            gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3346
416 by Damon Lynch
preliminary toolbar code
3347
        self.from_filechooser = gtk.ToolItem()
3348
        self.from_filechooser.set_is_important(True)
3349
        self.from_filechooser.add(self.from_filechooser_button)
3350
        self.from_filechooser.set_expand(True)
3351
        self.from_toolbar.insert(self.from_filechooser, 2)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3352
416 by Damon Lynch
preliminary toolbar code
3353
        self._set_from_toolbar_state()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3354
416 by Damon Lynch
preliminary toolbar code
3355
        #set events after having initialized the values
3356
        self.auto_detect_button.connect("toggled", self.on_auto_detect_button_toggled_event)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3357
        self.from_filechooser_button.connect("selection-changed",
416 by Damon Lynch
preliminary toolbar code
3358
                            self.on_from_filechooser_button_selection_changed)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3359
416 by Damon Lynch
preliminary toolbar code
3360
        self.from_toolbar.show_all()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3361
416 by Damon Lynch
preliminary toolbar code
3362
    def _setup_copy_move_toolbar(self):
3363
        self.copy_toolbar.set_style(gtk.TOOLBAR_TEXT)
3364
        self.copy_toolbar.set_border_width(5)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3365
416 by Damon Lynch
preliminary toolbar code
3366
        copy_move_label = gtk.Label(" ")
3367
        self.copy_move_toolbar_label = gtk.ToolItem()
3368
        self.copy_move_toolbar_label.add(copy_move_label)
3369
        self.copy_move_toolbar_label.set_is_important(True)
3370
        self.copy_toolbar.insert(self.copy_move_toolbar_label, 0)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3371
416 by Damon Lynch
preliminary toolbar code
3372
        self.copy_hbox = gtk.HBox()
3373
        self.move_hbox = gtk.HBox()
3374
        self.forward_image = gtk.image_new_from_stock(gtk.STOCK_GO_FORWARD, gtk.ICON_SIZE_SMALL_TOOLBAR)
3375
        self.forward_image2 = gtk.image_new_from_stock(gtk.STOCK_GO_FORWARD, gtk.ICON_SIZE_SMALL_TOOLBAR)
3376
        self.forward_image3 = gtk.image_new_from_stock(gtk.STOCK_GO_FORWARD, gtk.ICON_SIZE_SMALL_TOOLBAR)
3377
        self.forward_image4 = gtk.image_new_from_stock(gtk.STOCK_GO_FORWARD, gtk.ICON_SIZE_SMALL_TOOLBAR)
3378
        self.forward_label = gtk.Label(" ")
3379
        self.forward_label2 = gtk.Label(" ")
3380
        self.forward_label3 = gtk.Label(" ")
3381
        self.forward_label4 = gtk.Label(" ")
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3382
416 by Damon Lynch
preliminary toolbar code
3383
        self.copy_button = gtk.RadioToolButton()
3384
        self.copy_button.set_label(_("Copy"))
3385
        self.copy_button.set_is_important(True)
3386
3387
        self.copy_hbox.pack_start(self.forward_label)
3388
        self.copy_hbox.pack_start(self.forward_image)
3389
        self.copy_hbox.pack_start(self.copy_button, expand=False, fill=False)
3390
        self.copy_hbox.pack_start(self.forward_image2)
3391
        self.copy_hbox.pack_start(self.forward_label2)
3392
        copy_box = gtk.ToolItem()
3393
        copy_box.add(self.copy_hbox)
3394
        self.copy_toolbar.insert(copy_box, 1)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3395
416 by Damon Lynch
preliminary toolbar code
3396
        self.move_button = gtk.RadioToolButton(self.copy_button)
3397
        self.move_button.set_label(_("Move"))
3398
        self.move_button.set_is_important(True)
3399
        self.move_hbox.pack_start(self.forward_label3)
3400
        self.move_hbox.pack_start(self.forward_image3)
3401
        self.move_hbox.pack_start(self.move_button, expand=False, fill=False)
3402
        self.move_hbox.pack_start(self.forward_image4)
3403
        self.move_hbox.pack_start(self.forward_label4)
3404
        move_box = gtk.ToolItem()
3405
        move_box.add(self.move_hbox)
3406
        self.copy_toolbar.insert(move_box, 2)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3407
416 by Damon Lynch
preliminary toolbar code
3408
        self.move_button.set_active(self.prefs.auto_delete)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3409
        self.copy_button.connect("toggled", self.on_copy_button_toggle_event)
3410
416 by Damon Lynch
preliminary toolbar code
3411
        self.copy_toolbar.show_all()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3412
        self._set_copy_toolbar_active_arrows()
3413
416 by Damon Lynch
preliminary toolbar code
3414
    def _setup_dest_toolbar(self):
3415
        #Destination Toolbar
3416
        self.dest_toolbar.set_border_width(5)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3417
416 by Damon Lynch
preliminary toolbar code
3418
        dest_label = gtk.Label()
3419
        dest_label.set_markup("<i>" + _("To") + "</i>")
3420
        self.dest_toolbar_label = gtk.ToolItem()
3421
        self.dest_toolbar_label.add(dest_label)
3422
        self.dest_toolbar_label.set_is_important(True)
3423
        self.dest_toolbar.insert(self.dest_toolbar_label, 0)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3424
416 by Damon Lynch
preliminary toolbar code
3425
        photo_dest_hbox = gtk.HBox()
3426
        self.photo_dest_label = gtk.Label(_("Photos:"))
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3427
416 by Damon Lynch
preliminary toolbar code
3428
        self.to_photo_filechooser_button = gtk.FileChooserButton(
3429
                _("Select a folder to download photos to"))
3430
        self.to_photo_filechooser_button.set_action(
3431
                            gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
3432
        photo_dest_hbox.pack_start(self.photo_dest_label, expand=False, fill=False, padding=6)
3433
        photo_dest_hbox.pack_start(self.to_photo_filechooser_button)
3434
        self.to_photo_filechooser = gtk.ToolItem()
3435
        self.to_photo_filechooser.set_is_important(True)
3436
        self.to_photo_filechooser.set_expand(True)
3437
        self.to_photo_filechooser.add(photo_dest_hbox)
3438
        self.dest_toolbar.insert(self.to_photo_filechooser, 1)
3439
3440
        video_dest_hbox = gtk.HBox()
3441
        self.video_dest_label = gtk.Label(_("Videos:"))
3442
        self.to_video_filechooser_button = gtk.FileChooserButton(
3443
                _("Select a folder to download videos to"))
3444
        self.to_video_filechooser_button.set_action(
3445
                        gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
3446
        video_dest_hbox.pack_start(self.video_dest_label, expand=False, fill=False, padding=6)
3447
        video_dest_hbox.pack_start(self.to_video_filechooser_button)
3448
        self.to_video_filechooser = gtk.ToolItem()
3449
        self.to_video_filechooser.set_is_important(True)
3450
        self.to_video_filechooser.set_expand(True)
3451
        self.to_video_filechooser.add(video_dest_hbox)
3452
        self.dest_toolbar.insert(self.to_video_filechooser, 2)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3453
416 by Damon Lynch
preliminary toolbar code
3454
        self._set_to_toolbar_values()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3455
        self.to_photo_filechooser_button.connect("selection-changed",
416 by Damon Lynch
preliminary toolbar code
3456
                        self.on_to_photo_filechooser_button_selection_changed)
455 by Damon Lynch
Fixed bug #1242119: choosing a bookmarked folder in a file choose button does not work under Ubuntu 13.10
3457
        #~ self.to_photo_filechooser_button.connect("file-set",
3458
                        #~ self.on_to_photo_filechooser_button_file_set)
3459
        #~ self.to_photo_filechooser_button.connect("current-folder-changed",
3460
                        #~ self.on_to_photo_filechooser_button_current_folder_changed)
416 by Damon Lynch
preliminary toolbar code
3461
        self.to_video_filechooser_button.connect("selection-changed",
3462
                        self.on_to_video_filechooser_button_selection_changed)
3463
        self.dest_toolbar.show_all()
3464
3465
    def _make_widget_widths_equal(self, widget1, widget2):
3466
        """takes two widgets and sets a width for both equal to widest one"""
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3467
416 by Damon Lynch
preliminary toolbar code
3468
        x1, y1, w1, h1 = widget1.get_allocation()
3469
        x2, y2, w2, h2 = widget2.get_allocation()
3470
        w = max(w1, w2)
3471
        h = max(h1, h2)
3472
        widget1.set_size_request(w,h)
3473
        widget2.set_size_request(w,h)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3474
416 by Damon Lynch
preliminary toolbar code
3475
    def _set_copy_toolbar_active_arrows(self):
3476
        if self.copy_button.get_active():
3477
            self.forward_image.set_visible(True)
3478
            self.forward_image2.set_visible(True)
3479
            self.forward_image3.set_visible(False)
3480
            self.forward_image4.set_visible(False)
3481
            self.forward_label.set_visible(False)
3482
            self.forward_label2.set_visible(False)
3483
            self.forward_label3.set_visible(True)
3484
            self.forward_label4.set_visible(True)
3485
        else:
3486
            self.forward_image.set_visible(False)
3487
            self.forward_image2.set_visible(False)
3488
            self.forward_image3.set_visible(True)
3489
            self.forward_image4.set_visible(True)
3490
            self.forward_label.set_visible(True)
3491
            self.forward_label2.set_visible(True)
3492
            self.forward_label3.set_visible(False)
3493
            self.forward_label4.set_visible(False)
3494
3495
    def on_copy_button_toggle_event(self, radio_button):
3496
        self._set_copy_toolbar_active_arrows()
3497
        self.prefs.auto_delete = not self.copy_button.get_active()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3498
416 by Damon Lynch
preliminary toolbar code
3499
    def _set_from_toolbar_state(self):
3500
        logger.debug("_set_from_toolbar_state")
3501
        self.auto_detect_button.set_active(self.prefs.device_autodetection)
3502
        if self.prefs.device_autodetection:
3503
            self.from_filechooser_button.set_sensitive(False)
3504
        self.from_filechooser_button.set_current_folder(self.prefs.device_location)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3505
416 by Damon Lynch
preliminary toolbar code
3506
    def on_auto_detect_button_toggled_event(self, button):
418 by Damon Lynch
Fix bug #859012 - warn user about downloading from /, /media and /home.
3507
        logger.debug("on_auto_detect_button_toggled_event")
416 by Damon Lynch
preliminary toolbar code
3508
        self.from_filechooser_button.set_sensitive(not button.get_active())
3509
        if not self.rerun_setup_available_image_and_video_media:
3510
            self.prefs.device_autodetection = button.get_active()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3511
416 by Damon Lynch
preliminary toolbar code
3512
    def on_from_filechooser_button_selection_changed(self, filechooserbutton):
3513
        logger.debug("on_from_filechooser_button_selection_changed")
455 by Damon Lynch
Fixed bug #1242119: choosing a bookmarked folder in a file choose button does not work under Ubuntu 13.10
3514
        path = get_folder_selection(filechooserbutton)
416 by Damon Lynch
preliminary toolbar code
3515
        if path and not self.rerun_setup_available_image_and_video_media:
3516
            self.prefs.device_location = path
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3517
455 by Damon Lynch
Fixed bug #1242119: choosing a bookmarked folder in a file choose button does not work under Ubuntu 13.10
3518
    def on_from_filechooser_button_file_set(self, button):
3519
        logger.debug("on_from_filechooser_button_file_set")
3520
3521
    def on_to_photo_filechooser_button_file_set(self, filechooserbutton):
3522
        logger.debug("on_to_filechooser_button_file_set")
3523
416 by Damon Lynch
preliminary toolbar code
3524
    def on_to_photo_filechooser_button_selection_changed(self, filechooserbutton):
455 by Damon Lynch
Fixed bug #1242119: choosing a bookmarked folder in a file choose button does not work under Ubuntu 13.10
3525
        logger.debug("on_to_filechooser_button_selection_changed")
3526
        path = get_folder_selection(filechooserbutton)
3527
        #~ logger.debug("Path: %s", path)
416 by Damon Lynch
preliminary toolbar code
3528
        if path:
3529
            self.prefs.download_folder = path
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3530
455 by Damon Lynch
Fixed bug #1242119: choosing a bookmarked folder in a file choose button does not work under Ubuntu 13.10
3531
    def on_to_photo_filechooser_button_current_folder_changed(self, filechooserbutton):
3532
        logger.debug("on_to_photo_filechooser_button_current_folder_changed")
3533
416 by Damon Lynch
preliminary toolbar code
3534
    def on_to_video_filechooser_button_selection_changed(self, filechooserbutton):
455 by Damon Lynch
Fixed bug #1242119: choosing a bookmarked folder in a file choose button does not work under Ubuntu 13.10
3535
        path = get_folder_selection(filechooserbutton)
416 by Damon Lynch
preliminary toolbar code
3536
        if path:
3537
            self.prefs.video_download_folder = path
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3538
416 by Damon Lynch
preliminary toolbar code
3539
    def _set_to_toolbar_values(self):
3540
        self.to_photo_filechooser_button.set_current_folder(self.prefs.download_folder)
3541
        self.to_video_filechooser_button.set_current_folder(self.prefs.video_download_folder)
3542
3543
    def toolbar_event(self, widget, toolbar):
3544
        pass
3545
3546
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3547
3548
    def _set_window_size(self):
3549
        """
3550
        Remember the window size from the last time the program was run, or
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3551
        set a default size
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3552
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3553
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3554
        if self.prefs.main_window_maximized:
3555
            self.rapidapp.maximize()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3556
            self.rapidapp.set_default_size(config.DEFAULT_WINDOW_WIDTH,
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3557
                                           config.DEFAULT_WINDOW_HEIGHT)
3558
        elif self.prefs.main_window_size_x > 0:
3559
            self.rapidapp.set_default_size(self.prefs.main_window_size_x, self.prefs.main_window_size_y)
3560
        else:
3561
            # set a default size
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3562
            self.rapidapp.set_default_size(config.DEFAULT_WINDOW_WIDTH,
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3563
                                           config.DEFAULT_WINDOW_HEIGHT)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3564
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3565
3566
    def _set_device_collection_size(self):
3567
        """
3568
        Set the size of the device collection scrolled window widget
3569
        """
3570
        if self.device_collection.map_process_to_row:
416 by Damon Lynch
preliminary toolbar code
3571
            height = max(self.device_collection_viewport.size_request()[1], 24)
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3572
            self.device_collection_scrolledwindow.set_size_request(-1,  height)
384 by Damon Lynch
set main vpaned position to match height of device collection
3573
            self.main_vpaned.set_position(height)
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3574
        else:
3575
            # don't allow the media collection to be absolutely empty
3576
            self.device_collection_scrolledwindow.set_size_request(-1, 47)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3577
3578
332 by Damon Lynch
Proprerly place and align thumbnail and image controls in main window
3579
    def on_rapidapp_window_state_event(self, widget, event):
3580
        """ Records the window maximization state in the preferences."""
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3581
332 by Damon Lynch
Proprerly place and align thumbnail and image controls in main window
3582
        if event.changed_mask & gdk.WINDOW_STATE_MAXIMIZED:
3583
            self.prefs.main_window_maximized = event.new_window_state & gdk.WINDOW_STATE_MAXIMIZED
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3584
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3585
    def _setup_buttons(self):
3586
        thumbnails_button = self.builder.get_object("thumbnails_button")
332 by Damon Lynch
Proprerly place and align thumbnail and image controls in main window
3587
        image = gtk.image_new_from_file(paths.share_dir('glade3/thumbnails_icon.png'))
3588
        thumbnails_button.set_image(image)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3589
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3590
        preview_button = self.builder.get_object("preview_button")
332 by Damon Lynch
Proprerly place and align thumbnail and image controls in main window
3591
        image = gtk.image_new_from_file(paths.share_dir('glade3/photo_icon.png'))
3592
        preview_button.set_image(image)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3593
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3594
        next_image_button = self.builder.get_object("next_image_button")
332 by Damon Lynch
Proprerly place and align thumbnail and image controls in main window
3595
        image = gtk.image_new_from_stock(gtk.STOCK_GO_FORWARD, gtk.ICON_SIZE_BUTTON)
3596
        next_image_button.set_image(image)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3597
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3598
        prev_image_button = self.builder.get_object("prev_image_button")
332 by Damon Lynch
Proprerly place and align thumbnail and image controls in main window
3599
        image = gtk.image_new_from_stock(gtk.STOCK_GO_BACK, gtk.ICON_SIZE_BUTTON)
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3600
        prev_image_button.set_image(image)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3601
356 by Damon Lynch
translation updates and free space display minor fixes
3602
    def _setup_icons(self):
3603
        icons = ['rapid-photo-downloader-jobcode',]
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3604
        icon_list = [(icon, paths.share_dir('glade3/%s.svg' % icon)) for icon in icons]
356 by Damon Lynch
translation updates and free space display minor fixes
3605
        register_iconsets(icon_list)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3606
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3607
    def _setup_error_icons(self):
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3608
        """
3609
        hide display of warning and error symbols in the taskbar until they
3610
        are needed
3611
        """
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3612
        self.error_image = self.builder.get_object("error_image")
3613
        self.warning_image = self.builder.get_object("warning_image")
3614
        self.warning_vseparator = self.builder.get_object("warning_vseparator")
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3615
        self.error_image.hide()
3616
        self.warning_image.hide()
3617
        self.warning_vseparator.hide()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3618
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
3619
    def enable_prefs_and_refresh(self, enabled):
3620
        """
3621
        If enable is true, then the user is able to activate the preferences
3622
        or refresh command.
3623
        The intention is to be able to disable this during a download
3624
        """
3625
        self.refresh_action.set_sensitive(enabled)
3626
        self.preferences_action.set_sensitive(enabled)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3627
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3628
    def statusbar_message(self, msg):
3629
        self.rapid_statusbar.push(self.statusbar_context_id, msg)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3630
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3631
    def statusbar_message_remove(self):
3632
        self.rapid_statusbar.pop(self.statusbar_context_id)
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
3633
3634
    def display_backup_mounts(self):
3635
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3636
        Create a message to be displayed to the user showing which backup
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
3637
        mounts will be used
3638
        """
3639
        message =  ''
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3640
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
3641
        paths = self.backup_devices.keys()
3642
        i = 0
3643
        v = len(paths)
3644
        prefix = ''
3645
        for b in paths:
3646
            if v > 1:
3647
                if i < (v -1)  and i > 0:
3648
                    prefix = ', '
3649
                elif i == (v - 1) :
3650
                    prefix = " " + _("and")  + " "
3651
            i += 1
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
3652
            message = "%s%s'%s'" % (message,  prefix, self.backup_devices[b][0].get_name())
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3653
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
3654
        if v > 1:
3655
            message = _("Using backup devices") + " %s" % message
3656
        elif v == 1:
3657
            message = _("Using backup device") + " %s"  % message
3658
        else:
3659
            message = _("No backup devices detected")
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3660
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
3661
        return message
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3662
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3663
    def display_free_space(self):
3664
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3665
        Displays the amount of space free on the filesystem the files will be
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3666
        downloaded to.
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3667
3668
        Also displays backup volumes / path being used.
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3669
        """
366 by Damon Lynch
Add error log window. Allow cancellation of file copy in case of early termination.
3670
        photo_dir = self.is_valid_download_dir(path=self.prefs.download_folder, is_photo_dir=True, show_error_in_log=True)
3671
        video_dir = self.is_valid_download_dir(path=self.prefs.video_download_folder, is_photo_dir=False, show_error_in_log=True)
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3672
        if photo_dir and video_dir:
3673
            same_file_system = self.same_file_system(self.prefs.download_folder,
3674
                                            self.prefs.video_download_folder)
342 by Damon Lynch
Move downloaded files from temp dir to destination folders; proper error handling of gio calls, using gio.Error
3675
        else:
3676
            same_file_system = False
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3677
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3678
        dirs = []
3679
        if photo_dir:
3680
            dirs.append((self.prefs.download_folder, _("photos")))
3681
        if video_dir and not same_file_system:
3682
            dirs.append((self.prefs.video_download_folder, _("videos")))
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3683
361 by Damon Lynch
Sample file names and subfolders are now displayed in the preferences dialog
3684
        msg = ''
356 by Damon Lynch
translation updates and free space display minor fixes
3685
        if len(dirs) > 1:
3686
            msg = ' ' + _('Free space:') + ' '
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3687
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3688
        for i in range(len(dirs)):
3689
            dir_info = dirs[i]
3690
            folder = gio.File(dir_info[0])
3691
            file_info = folder.query_filesystem_info(gio.FILE_ATTRIBUTE_FILESYSTEM_FREE)
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3692
            size = file_info.get_attribute_uint64(gio.FILE_ATTRIBUTE_FILESYSTEM_FREE)
3693
            free = format_size_for_user(bytes=size)
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3694
            if len(dirs) > 1:
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3695
                #(videos) or (photos) will be appended to the free space message displayed to the
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3696
                #user in the status bar.
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3697
                #you should only translate this if your language does not use parantheses
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3698
                file_type = _("(%(file_type)s)") % {'file_type': dir_info[1]}
3699
3700
                #Freespace available on the filesystem for downloading to
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3701
                #Displayed in status bar message on main window
356 by Damon Lynch
translation updates and free space display minor fixes
3702
                msg += _("%(free)s %(file_type)s") % {'free': free, 'file_type': file_type}
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3703
                if i == 0:
3704
                    #Inserted in the middle of the statusbar message concerning the amount of freespace
3705
                    #Used to differentiate between two different file systems
356 by Damon Lynch
translation updates and free space display minor fixes
3706
                    #e.g. Free space: 21.3GB (photos); 14.7GB (videos).
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3707
                    msg += _("; ")
387 by Damon Lynch
don't crash when completing download with backups enabled no backup devices detected.
3708
                elif not self.prefs.backup_images:
356 by Damon Lynch
translation updates and free space display minor fixes
3709
                    #Inserted at the end of the statusbar message concerning the amount of freespace
3710
                    #Used to differentiate between two different file systems
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3711
                    #e.g. Free space: 21.3GB (photos); 14.7GB (videos).
356 by Damon Lynch
translation updates and free space display minor fixes
3712
                    msg += _(".")
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3713
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3714
            else:
3715
                #Freespace available on the filesystem for downloading to
3716
                #Displayed in status bar message on main window
3717
                #e.g. 14.7GB available
356 by Damon Lynch
translation updates and free space display minor fixes
3718
                msg = " " + _("%(free)s free") % {'free': free}
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3719
3720
3721
        if self.prefs.backup_images:
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3722
            if not self.prefs.backup_device_autodetection:
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
3723
                if self.prefs.backup_location == self.prefs.backup_video_location:
3724
                    if DOWNLOAD_VIDEO:
3725
                        # user manually specified the same location for photos and video backups
3726
                        msg2 = _('Backing up photos and videos to %(path)s') % {'path':self.prefs.backup_location}
3727
                    else:
3728
                        # user manually specified backup location
3729
                        msg2 = _('Backing up to %(path)s') % {'path':self.prefs.backup_location}
3730
                else:
3731
                    # user manually specified different locations for photo and video backups
3732
                    msg2 = _('Backing up photos to %(path)s and videos to %(path2)s') % {
3733
                             'path':self.prefs.backup_location,
3734
                             'path2': self.prefs.backup_video_location}
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3735
            else:
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3736
                msg2 = self.display_backup_mounts()
3737
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3738
            if msg:
3739
                msg = _("%(freespace)s. %(backuppaths)s.") % {'freespace': msg, 'backuppaths': msg2}
3740
            else:
3741
                msg = msg2
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3742
356 by Damon Lynch
translation updates and free space display minor fixes
3743
        msg = msg.rstrip()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3744
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3745
        self.statusbar_message(msg)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3746
366 by Damon Lynch
Add error log window. Allow cancellation of file copy in case of early termination.
3747
    def log_error(self, severity, problem, details, extra_detail=None):
3748
        """
3749
        Display error and warning messages to user in log window
3750
        """
3751
        self.error_log.add_message(severity, problem, details, extra_detail)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3752
3753
366 by Damon Lynch
Add error log window. Allow cancellation of file copy in case of early termination.
3754
    def on_error_eventbox_button_press_event(self, widget, event):
3755
        self.prefs.show_log_dialog = True
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3756
        self.error_log.widget.show()
3757
3758
366 by Damon Lynch
Add error log window. Allow cancellation of file copy in case of early termination.
3759
    def on_menu_log_window_toggled(self, widget):
3760
        active = widget.get_active()
3761
        self.prefs.show_log_dialog = active
3762
        if active:
3763
            self.error_log.widget.show()
3764
        else:
3765
            self.error_log.widget.hide()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3766
372 by Damon Lynch
notify user if file rename or subfolder preferences are invalid on program startup
3767
    def notify_prefs_are_invalid(self, details):
3768
        title = _("Program preferences are invalid")
373 by Damon Lynch
auto start download on startup and on device insertion; adjust vertical pane when device is inserted; when auto start is enabled, delay making checkboxes invisible
3769
        logger.critical(title)
372 by Damon Lynch
notify user if file rename or subfolder preferences are invalid on program startup
3770
        self.log_error(severity=config.CRITICAL_ERROR, problem=title,
3771
                       details=details)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3772
3773
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3774
    # # #
3775
    # Utility functions
3776
    # # #
3777
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
3778
    def files_of_type_present(self, files, file_type, return_file_count=False):
340 by Damon Lynch
Move download tracking functionality from copyfile process to main program process, in preparation for coming file renaming and backup functionality
3779
        """
3780
        Returns true if there is at least one instance of the file_type
3781
        in the list of files to be copied
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3782
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
3783
        If return_file_count is True, then the number of files of that type
3784
        will be counted and returned instead of True or False
340 by Damon Lynch
Move download tracking functionality from copyfile process to main program process, in preparation for coming file renaming and backup functionality
3785
        """
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
3786
        i = 0
340 by Damon Lynch
Move download tracking functionality from copyfile process to main program process, in preparation for coming file renaming and backup functionality
3787
        for rpd_file in files:
3788
            if rpd_file.file_type == file_type:
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
3789
                if return_file_count:
3790
                    i += 1
3791
                else:
3792
                    return True
3793
        if not return_file_count:
3794
            return False
3795
        else:
3796
            return i
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3797
340 by Damon Lynch
Move download tracking functionality from copyfile process to main program process, in preparation for coming file renaming and backup functionality
3798
    def size_files_to_be_downloaded(self, files):
3799
        """
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
3800
        Returns the total sizes of the photos and videos to be downloaded in bytes
340 by Damon Lynch
Move download tracking functionality from copyfile process to main program process, in preparation for coming file renaming and backup functionality
3801
        """
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
3802
        photo_size = 0
3803
        video_size = 0
3804
        for rpd_file in files:
3805
            if rpd_file.file_type == rpdfile.FILE_TYPE_PHOTO:
3806
                photo_size += rpd_file.size
3807
            else:
3808
                video_size += rpd_file.size
340 by Damon Lynch
Move download tracking functionality from copyfile process to main program process, in preparation for coming file renaming and backup functionality
3809
405 by Damon Lynch
Enable backups of photos and videos to different paths / devices based on file type
3810
        return (photo_size, video_size)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3811
465 by Damon Lynch
Fixed bug where missing backup folder on auto detected devices could cause the download to fail
3812
    def check_file_types_to_be_downloaded(self, files_by_scan_pid):
3813
        """Determines what types of files need to be downloaded, setting
3814
        self.downloading_photos and self.downloading_videos accordingly"""
3815
        self.downloading_photos = False
3816
        self.downloading_videos = False
3817
        while not self.downloading_photos and not self.downloading_videos:
3818
            for scan_pid in files_by_scan_pid:
3819
                files = files_by_scan_pid[scan_pid]
3820
                if not self.downloading_photos:
3821
                    if self.files_of_type_present(files, rpdfile.FILE_TYPE_PHOTO):
3822
                        self.downloading_photos = True
3823
                if not self.downloading_videos:
3824
                    if self.files_of_type_present(files, rpdfile.FILE_TYPE_VIDEO):
3825
                        self.downloading_videos = True
3826
3827
340 by Damon Lynch
Move download tracking functionality from copyfile process to main program process, in preparation for coming file renaming and backup functionality
3828
    def check_download_folder_validity(self, files_by_scan_pid):
3829
        """
3830
        Checks validity of download folders based on the file types the user
3831
        is attempting to download.
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3832
366 by Damon Lynch
Add error log window. Allow cancellation of file copy in case of early termination.
3833
        If valid, returns a tuple of True and an empty list.
3834
        If invalid, returns a tuple of False and a list of the invalid directores.
340 by Damon Lynch
Move download tracking functionality from copyfile process to main program process, in preparation for coming file renaming and backup functionality
3835
        """
3836
        valid = True
366 by Damon Lynch
Add error log window. Allow cancellation of file copy in case of early termination.
3837
        invalid_dirs = []
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3838
465 by Damon Lynch
Fixed bug where missing backup folder on auto detected devices could cause the download to fail
3839
        if self.downloading_photos:
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3840
            if not self.is_valid_download_dir(self.prefs.download_folder,
366 by Damon Lynch
Add error log window. Allow cancellation of file copy in case of early termination.
3841
                                                        is_photo_dir=True):
340 by Damon Lynch
Move download tracking functionality from copyfile process to main program process, in preparation for coming file renaming and backup functionality
3842
                valid = False
366 by Damon Lynch
Add error log window. Allow cancellation of file copy in case of early termination.
3843
                invalid_dirs.append(self.prefs.download_folder)
431.1.9 by Damon Lynch
Display path info in debugging log output. Display running totals during scan.
3844
            else:
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3845
                logger.debug("Photo download folder is valid: %s",
431.1.9 by Damon Lynch
Display path info in debugging log output. Display running totals during scan.
3846
                        self.prefs.download_folder)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3847
465 by Damon Lynch
Fixed bug where missing backup folder on auto detected devices could cause the download to fail
3848
        if self.downloading_videos:
366 by Damon Lynch
Add error log window. Allow cancellation of file copy in case of early termination.
3849
            if not self.is_valid_download_dir(self.prefs.video_download_folder,
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3850
                                                        is_photo_dir=False):
340 by Damon Lynch
Move download tracking functionality from copyfile process to main program process, in preparation for coming file renaming and backup functionality
3851
                valid = False
366 by Damon Lynch
Add error log window. Allow cancellation of file copy in case of early termination.
3852
                invalid_dirs.append(self.prefs.video_download_folder)
431.1.9 by Damon Lynch
Display path info in debugging log output. Display running totals during scan.
3853
            else:
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3854
                logger.debug("Video download folder is valid: %s",
431.1.9 by Damon Lynch
Display path info in debugging log output. Display running totals during scan.
3855
                        self.prefs.video_download_folder)
3856
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3857
366 by Damon Lynch
Add error log window. Allow cancellation of file copy in case of early termination.
3858
        return (valid, invalid_dirs)
340 by Damon Lynch
Move download tracking functionality from copyfile process to main program process, in preparation for coming file renaming and backup functionality
3859
465 by Damon Lynch
Fixed bug where missing backup folder on auto detected devices could cause the download to fail
3860
    def backup_destinations_missing(self):
3861
        if self.prefs.backup_images and self.prefs.backup_device_autodetection:
3862
            photo_backup_ok = video_backup_ok = True
3863
            if self.downloading_photos and not self.backup_possible(rpdfile.FILE_TYPE_PHOTO):
3864
                photo_backup_ok = False
3865
            if self.downloading_videos and not self.backup_possible(rpdfile.FILE_TYPE_VIDEO):
3866
                video_backup_ok = False
3867
            if photo_backup_ok and video_backup_ok:
3868
                return None
3869
            else:
3870
                return (photo_backup_ok, video_backup_ok)
3871
        return None
3872
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3873
    def same_file_system(self, file1, file2):
3874
        """Returns True if the files / diretories are on the same file system
3875
        """
3876
        f1 = gio.File(file1)
3877
        f2 = gio.File(file2)
3878
        f1_info = f1.query_info(gio.FILE_ATTRIBUTE_ID_FILESYSTEM)
3879
        f1_id = f1_info.get_attribute_string(gio.FILE_ATTRIBUTE_ID_FILESYSTEM)
3880
        f2_info = f2.query_info(gio.FILE_ATTRIBUTE_ID_FILESYSTEM)
3881
        f2_id = f2_info.get_attribute_string(gio.FILE_ATTRIBUTE_ID_FILESYSTEM)
3882
        return f1_id == f2_id
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3883
3884
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3885
    def same_file(self, file1, file2):
3886
        """Returns True if the files / directories are the same
3887
        """
3888
        f1 = gio.File(file1)
3889
        f2 = gio.File(file2)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3890
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3891
        file_attributes = "id::file"
3892
        f1_info = f1.query_filesystem_info(file_attributes)
3893
        f1_id = f1_info.get_attribute_string(gio.FILE_ATTRIBUTE_ID_FILE)
3894
        f2_info = f2.query_filesystem_info(file_attributes)
3895
        f2_id = f2_info.get_attribute_string(gio.FILE_ATTRIBUTE_ID_FILE)
3896
        return f1_id == f2_id
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3897
366 by Damon Lynch
Add error log window. Allow cancellation of file copy in case of early termination.
3898
    def is_valid_download_dir(self, path, is_photo_dir, show_error_in_log=False):
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3899
        """
3900
        Checks the following conditions:
3901
        Does the directory exist?
3902
        Is it writable?
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3903
366 by Damon Lynch
Add error log window. Allow cancellation of file copy in case of early termination.
3904
        if show_error_in_log is True, then display warning in log window, using
3905
        is_photo_dir, which if true means the download directory is for photos,
3906
        if false, for Videos
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3907
        """
3908
        valid = False
366 by Damon Lynch
Add error log window. Allow cancellation of file copy in case of early termination.
3909
        if is_photo_dir:
3910
            download_folder_type = _("Photo")
3911
        else:
3912
            download_folder_type = _("Video")
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3913
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3914
        try:
3915
            d = gio.File(path)
342 by Damon Lynch
Move downloaded files from temp dir to destination folders; proper error handling of gio calls, using gio.Error
3916
            if not d.query_exists(cancellable=None):
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3917
                logger.error("%s download folder does not exist: %s",
366 by Damon Lynch
Add error log window. Allow cancellation of file copy in case of early termination.
3918
                             download_folder_type, path)
3919
                if show_error_in_log:
3920
                    severity = config.WARNING
3921
                    problem = _("%(file_type)s download folder does not exist") % {
3922
                                'file_type': download_folder_type}
3923
                    details = _("Folder: %s") % path
3924
                    self.log_error(severity, problem, details)
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3925
            else:
342 by Damon Lynch
Move downloaded files from temp dir to destination folders; proper error handling of gio calls, using gio.Error
3926
                file_attributes = "standard::type,access::can-read,access::can-write"
3927
                file_info = d.query_filesystem_info(file_attributes)
3928
                file_type = file_info.get_file_type()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3929
342 by Damon Lynch
Move downloaded files from temp dir to destination folders; proper error handling of gio calls, using gio.Error
3930
                if file_type != gio.FILE_TYPE_DIRECTORY and file_type != gio.FILE_TYPE_UNKNOWN:
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3931
                    logger.error("%s download folder is invalid: %s",
366 by Damon Lynch
Add error log window. Allow cancellation of file copy in case of early termination.
3932
                                 download_folder_type, path)
3933
                    if show_error_in_log:
3934
                        severity = config.WARNING
3935
                        problem = _("%(file_type)s download folder is invalid") % {
3936
                                    'file_type': download_folder_type}
3937
                        details = _("Folder: %s") % path
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3938
                        self.log_error(severity, problem, details)
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3939
                else:
342 by Damon Lynch
Move downloaded files from temp dir to destination folders; proper error handling of gio calls, using gio.Error
3940
                    # is the directory writable?
3941
                    try:
3942
                        temp_dir = tempfile.mkdtemp(prefix="rpd-tmp", dir=path)
3943
                        valid = True
3944
                    except:
3945
                        logger.error("%s is not writable", path)
366 by Damon Lynch
Add error log window. Allow cancellation of file copy in case of early termination.
3946
                        if show_error_in_log:
3947
                            severity = config.WARNING
3948
                            problem = _("%(file_type)s download folder is not writable") % {
3949
                                        'file_type': download_folder_type}
3950
                            details = _("Folder: %s") % path
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3951
                            self.log_error(severity, problem, details)
342 by Damon Lynch
Move downloaded files from temp dir to destination folders; proper error handling of gio calls, using gio.Error
3952
                    else:
3953
                        f = gio.File(temp_dir)
3954
                        f.delete(cancellable=None)
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3955
342 by Damon Lynch
Move downloaded files from temp dir to destination folders; proper error handling of gio calls, using gio.Error
3956
        except gio.Error, inst:
3957
            logger.error("Error checking download directory %s", path)
3958
            logger.error(inst)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3959
339 by Damon Lynch
Preliminary file copying; check download directories at time of download; display free space in main window; add status bar icons, progress bar and text
3960
        return valid
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3961
3962
3963
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3964
    # # #
3965
    #  Process results and management
3966
    # # #
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3967
3968
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3969
    def _start_process_managers(self):
3970
        """
3971
        Set up process managers.
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3972
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3973
        A task such as scanning a device or copying files is handled in its
423 by Damon Lynch
include THM thumbnails in backups
3974
        own process.
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3975
        """
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3976
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
3977
        self.batch_size = 10
3978
        self.batch_size_MB = 2
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3979
347 by Damon Lynch
Downloads today sequence implemented using tracking class and 3 shared memory values. Stored sequence number implemented using 1 shared memory value. Session sequence no and letter only partially implemented. kaa metadata loaded only when needed.
3980
        sequence_values = (self.downloads_today_value,
3981
                           self.downloads_today_date_value,
3982
                           self.day_start_value,
3983
                           self.refresh_downloads_today_value,
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3984
                           self.stored_sequence_value,
348 by Damon Lynch
sequence numbers and letters implemented
3985
                           self.uses_stored_sequence_no_value,
3986
                           self.uses_session_sequece_no_value,
3987
                           self.uses_sequence_letter_value)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3988
3989
        # daemon process to rename files and create subfolders
347 by Damon Lynch
Downloads today sequence implemented using tracking class and 3 shared memory values. Stored sequence number implemented using 1 shared memory value. Session sequence no and letter only partially implemented. kaa metadata loaded only when needed.
3990
        self.subfolder_file_manager = SubfolderFileManager(
3991
                                        self.subfolder_file_results,
430 by Damon Lynch
Move aperture and focal length modifications to filemodify.py, writing out exif changes to about-to-be renamed temporary file or to XMP sidecar.
3992
                                        sequence_values)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3993
423 by Damon Lynch
include THM thumbnails in backups
3994
        # process to scan source devices / paths
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3995
        self.scan_manager = ScanManager(self.scan_results, self.batch_size,
402 by Damon Lynch
add ignore paths option; add remove remembered devices to prefs window; add focal_length command line option; updated i18n
3996
                                        self.device_collection.add_device)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3997
423 by Damon Lynch
include THM thumbnails in backups
3998
        #process to copy files from source to destination
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
3999
        self.copy_files_manager = CopyFilesManager(self.copy_files_results,
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
4000
                                                   self.batch_size_MB)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
4001
423 by Damon Lynch
include THM thumbnails in backups
4002
        #process to back files up
383 by Damon Lynch
backup file functionality; more accurate bytes downloaded tracking; when thumbnails not automatically generated, when user shows preview, grab thumbnails; disable preference and refresh when download is occurring
4003
        self.backup_manager = BackupFilesManager(self.backup_results,
4004
                                                 self.batch_size_MB)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
4005
424 by Damon Lynch
Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture
4006
        #process to enhance files after they've been copied and before they're
4007
        #renamed
4008
        self.file_modify_manager = FileModifyManager(self.file_modify_results)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
4009
4010
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
4011
    def scan_results(self, source, condition):
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
4012
        """
4013
        Receive results from scan processes
4014
        """
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
4015
        connection = self.scan_manager.get_pipe(source)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
4016
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
4017
        conn_type, data = connection.recv()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
4018
313 by Damon Lynch
Fix pixbuf problem using numpy array, and preliminary & incomplete thumbnail generator work
4019
        if conn_type == rpdmp.CONN_COMPLETE:
317 by Damon Lynch
search for bug in pipe size and thumbnail image corruption
4020
            connection.close()
379 by Damon Lynch
check if scans are occuring by using manual counter, not no active processes (they can take a while to finish)
4021
            self.scan_manager.no_tasks -= 1
322 by Damon Lynch
display file count in progress bar after scan is complete
4022
            size, file_type_counter, scan_pid = data
345 by Damon Lynch
preliminary renaming & subfolder creation; preliminary prefs dialog (sample names not working); preliminary volume management, rapid.ui and prefs.ui created; video icon changed; photo metadata access now pyexiv > 0.2.x only; purge configi18n class; better termination code; get high resolution stock thumbnail from SVG
4023
            size = format_size_for_user(bytes=size)
340 by Damon Lynch
Move download tracking functionality from copyfile process to main program process, in preparation for coming file renaming and backup functionality
4024
            results_summary, file_types_present = file_type_counter.summarize_file_count()
367 by Damon Lynch
Use downloadtracker class and adjust percent complete by including time taken to rename files.
4025
            self.download_tracker.set_file_types_present(scan_pid, file_types_present)
322 by Damon Lynch
display file count in progress bar after scan is complete
4026
            logger.info('Found %s' % results_summary)
321 by Damon Lynch
Preliminary work to integrate device collection treeview
4027
            logger.info('Files total %s' % size)
4028
            self.device_collection.update_device(scan_pid, size)
431.1.11 by Damon Lynch
Fix bug #1014203: create new liststore when clearing the old one, thereby removing the sort, which is super slow.
4029
            self.device_collection.update_progress(scan_pid, 0.0, results_summary, 0, pulse=False)
373 by Damon Lynch
auto start download on startup and on device insertion; adjust vertical pane when device is inserted; when auto start is enabled, delay making checkboxes invisible
4030
            self.set_download_action_sensitivity()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
4031
385 by Damon Lynch
remove testing auto exit functionality; report backup problems in system notifications
4032
            if (not self.auto_start_is_on and
4033
                self.prefs.generate_thumbnails):
4034
                self.download_progressbar.set_text(_("Thumbnails"))
4035
                self.thumbnails.generate_thumbnails(scan_pid)
4036
            elif self.auto_start_is_on:
4037
                if self.need_job_code_for_naming and not self.job_code:
4038
                    self.get_job_code()
4039
                else:
4040
                    self.start_download(scan_pid=scan_pid)
373 by Damon Lynch
auto start download on startup and on device insertion; adjust vertical pane when device is inserted; when auto start is enabled, delay making checkboxes invisible
4041
431.1.13 by Damon Lynch
Updated translations
4042
            logger.debug("Turning on display of thumbnails")
4043
            self.display_scan_thumbnails()
416 by Damon Lynch
preliminary toolbar code
4044
            self.download_button.grab_focus()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
4045
317 by Damon Lynch
search for bug in pipe size and thumbnail image corruption
4046
            # signal that no more data is coming, finishing io watch for this pipe
4047
            return False
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
4048
        else:
431.1.9 by Damon Lynch
Display path info in debugging log output. Display running totals during scan.
4049
            # partial results
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
4050
            if len(data) > self.batch_size:
366 by Damon Lynch
Add error log window. Allow cancellation of file copy in case of early termination.
4051
                logger.critical("incoming pipe length is unexpectedly long: %s" % len(data))
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
4052
            else:
431.1.9 by Damon Lynch
Display path info in debugging log output. Display running totals during scan.
4053
                size, file_type_counter, scan_pid, rpd_files = data
4054
                size = format_size_for_user(bytes=size)
4055
                scanning_progress = file_type_counter.running_file_count()
4056
                self.device_collection.update_device(scan_pid, size)
431.1.11 by Damon Lynch
Fix bug #1014203: create new liststore when clearing the old one, thereby removing the sort, which is super slow.
4057
                self.device_collection.update_progress(scan_pid, 0.0, scanning_progress, 0, pulse=True)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
4058
431.1.9 by Damon Lynch
Display path info in debugging log output. Display running totals during scan.
4059
                for rpd_file in rpd_files:
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
4060
                    self.thumbnails.add_file(rpd_file=rpd_file,
376 by Damon Lynch
adjust vertical pane position when additional devices are inserted after preference change; properly track how many thumbnails need to be generated when auto start is on
4061
                                        generate_thumbnail = not self.auto_start_is_on)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
4062
313 by Damon Lynch
Fix pixbuf problem using numpy array, and preliminary & incomplete thumbnail generator work
4063
        # must return True for this method to be called again
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
4064
        return True
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
4065
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
4066
4067
    @dbus.service.method (config.DBUS_NAME,
4068
                           in_signature='', out_signature='b')
4069
    def is_running (self):
4070
        return self.running
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
4071
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
4072
    @dbus.service.method (config.DBUS_NAME,
4073
                            in_signature='', out_signature='')
4074
    def start (self):
4075
        if self.is_running():
354 by Damon Lynch
Add command line options and re-enable single instance detection
4076
            self.rapidapp.present()
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
4077
        else:
4078
            self.running = True
4079
            gtk.main()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
4080
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
4081
def start():
4082
354 by Damon Lynch
Add command line options and re-enable single instance detection
4083
    is_beta = config.version.find('~') > 0
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
4084
354 by Damon Lynch
Add command line options and re-enable single instance detection
4085
    parser = OptionParser(version= "%%prog %s" % utilities.human_readable_version(config.version))
4086
    parser.set_defaults(verbose=is_beta,  extensions=False)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
4087
    # Translators: this text is displayed to the user when they request information on the command line options.
354 by Damon Lynch
Add command line options and re-enable single instance detection
4088
    # The text %default should not be modified or left out.
4089
    parser.add_option("-v",  "--verbose",  action="store_true", dest="verbose",  help=_("display program information on the command line as the program runs (default: %default)"))
4090
    parser.add_option("-d", "--debug", action="store_true", dest="debug", help=_('display debugging information when run from the command line'))
4091
    parser.add_option("-q", "--quiet",  action="store_false", dest="verbose",  help=_("only output errors to the command line"))
4092
    # image file extensions are recognized RAW files plus TIFF and JPG
4093
    parser.add_option("-e",  "--extensions", action="store_true", dest="extensions", help=_("list photo and video file extensions the program recognizes and exit"))
431.1.15 by Damon Lynch
0.4.5 release
4094
    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 f/8")
431.1.10 by Damon Lynch
Added command line option to specify device detection and location
4095
    parser.add_option("-a", "--auto-detect", action="store_true", dest="auto_detect", help=_("automatically detect devices from which to download, overwriting existing program preferences"))
4096
    parser.add_option("-l", "--device-location", type="string", metavar="PATH", dest="device_location", help=_("manually specify the PATH of the device from which to download, overwriting existing program preferences"))
354 by Damon Lynch
Add command line options and re-enable single instance detection
4097
    parser.add_option("--reset-settings", action="store_true", dest="reset", help=_("reset all program settings and preferences and exit"))
4098
    (options, args) = parser.parse_args()
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
4099
354 by Damon Lynch
Add command line options and re-enable single instance detection
4100
    if options.debug:
4101
        logging_level = logging.DEBUG
4102
    elif options.verbose:
4103
        logging_level = logging.INFO
4104
    else:
4105
        logging_level = logging.ERROR
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
4106
354 by Damon Lynch
Add command line options and re-enable single instance detection
4107
    logger.setLevel(logging_level)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
4108
431.1.10 by Damon Lynch
Added command line option to specify device detection and location
4109
    if options.auto_detect and options.device_location:
431.1.14 by Damon Lynch
Correct spelling error
4110
        logger.info(_("Error: specify device auto-detection or manually specify a device's path from which to download, but do not do both."))
431.1.10 by Damon Lynch
Added command line option to specify device detection and location
4111
        sys.exit(1)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
4112
431.1.10 by Damon Lynch
Added command line option to specify device detection and location
4113
    if options.auto_detect:
4114
        auto_detect=True
4115
        logger.info("Device auto detection set from command line")
4116
    else:
4117
        auto_detect=None
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
4118
431.1.10 by Damon Lynch
Added command line option to specify device detection and location
4119
    if options.device_location:
4120
        device_location=options.device_location
431.1.15 by Damon Lynch
0.4.5 release
4121
        if device_location[-1]=='/':
4122
            device_location = device_location[:-1]
431.1.10 by Damon Lynch
Added command line option to specify device detection and location
4123
        logger.info("Device location set from command line: %s", device_location)
4124
    else:
4125
        device_location=None
354 by Damon Lynch
Add command line options and re-enable single instance detection
4126
4127
    if options.extensions:
417 by Damon Lynch
Allow video downloading with ExifTool if Hachoir metadata is not installed; use ExifTool for FileNumber.
4128
        extensions = ((rpdfile.PHOTO_EXTENSIONS, _("Photos:")), (rpdfile.VIDEO_EXTENSIONS, _("Videos:")))
354 by Damon Lynch
Add command line options and re-enable single instance detection
4129
        for exts, file_type in extensions:
4130
            v = ''
4131
            for e in exts[:-1]:
4132
                v += '%s, ' % e.upper()
4133
            v = file_type + " " + v[:-1] + ' '+ (_('and %s') % exts[-1].upper())
4134
            print v
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
4135
354 by Damon Lynch
Add command line options and re-enable single instance detection
4136
        sys.exit(0)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
4137
354 by Damon Lynch
Add command line options and re-enable single instance detection
4138
    if options.reset:
400 by Damon Lynch
Fix bug #789995 : crash when reset-settings is run
4139
        prefs = prefsrapid.RapidPreferences()
354 by Damon Lynch
Add command line options and re-enable single instance detection
4140
        prefs.reset()
4141
        print _("All settings and preferences have been reset")
4142
        sys.exit(0)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
4143
402 by Damon Lynch
add ignore paths option; add remove remembered devices to prefs window; add focal_length command line option; updated i18n
4144
    if options.focal_length:
4145
        focal_length = options.focal_length
4146
    else:
4147
        focal_length = None
354 by Damon Lynch
Add command line options and re-enable single instance detection
4148
4149
    logger.info("Rapid Photo Downloader %s", utilities.human_readable_version(config.version))
4150
    logger.info("Using pyexiv2 %s", metadataphoto.pyexiv2_version_info())
4151
    logger.info("Using exiv2 %s", metadataphoto.exiv2_version_info())
417 by Damon Lynch
Allow video downloading with ExifTool if Hachoir metadata is not installed; use ExifTool for FileNumber.
4152
    if metadataexiftool.EXIFTOOL_VERSION is None:
4153
        logger.info("Exiftool not detected")
4154
    else:
4155
        logger.info("Using exiftool %s", metadataexiftool.EXIFTOOL_VERSION)
4156
    if metadatavideo.HAVE_HACHOIR:
4157
        logger.info("Using hachoir %s", metadatavideo.version_info())
4158
4159
402 by Damon Lynch
add ignore paths option; add remove remembered devices to prefs window; add focal_length command line option; updated i18n
4160
    if focal_length:
4161
        logger.info("Focal length of %s will be used when an aperture of 0.0 is encountered", focal_length)
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
4162
4163
    bus = dbus.SessionBus ()
4164
    request = bus.request_name (config.DBUS_NAME, dbus.bus.NAME_FLAG_DO_NOT_QUEUE)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
4165
    if request != dbus.bus.REQUEST_NAME_REPLY_EXISTS:
431.1.10 by Damon Lynch
Added command line option to specify device detection and location
4166
        app = RapidApp(bus, '/', config.DBUS_NAME, focal_length=focal_length,
4167
        auto_detect=auto_detect, device_location=device_location)
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
4168
    else:
4169
        # this application is already running
354 by Damon Lynch
Add command line options and re-enable single instance detection
4170
        print "Rapid Photo Downloader is already running"
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
4171
        object = bus.get_object (config.DBUS_NAME, "/")
4172
        app = dbus.Interface (object, config.DBUS_NAME)
442 by Damon Lynch
Add extra debugging output to track down cause of bug #1025908
4173
4174
    app.start()
311 by Damon Lynch
Hacks on version 0.3.4 rapid.py code to have option to disable preview generation, and initial cut of multiprocessing prototype, doing only scans.
4175
4176
if __name__ == "__main__":
4177
    start()