1 # -*- coding: utf-8 -*-
3 # (c) Copyright 2001-2009 Hewlett-Packard Development Company, L.P.
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 from base import utils, magic
25 from pcard import photocard
26 from ui_utils import load_pixmap
30 from scrollview import ScrollView, PixmapLabelButton
31 from imagepropertiesdlg import ImagePropertiesDlg
32 #from waitform import WaitForm
37 class IconViewItem(QIconViewItem):
38 def __init__(self, parent, dirname, fname, path, pixmap, mime_type, mime_subtype, size, exif_info={}):
39 QIconViewItem.__init__(self, parent, fname, pixmap)
40 self.mime_type = mime_type
41 self.mime_subtype = mime_subtype
43 self.dirname = dirname
45 self.exif_info = exif_info
47 self.thumbnail_set = False
50 class ScrollUnloadView(ScrollView):
51 def __init__(self, service, parent=None, form=None, name=None, fl=0):
52 ScrollView.__init__(self, service, parent, name, fl)
55 self.progress_dlg = None
56 self.unload_dir = os.path.normpath(os.path.expanduser('~'))
58 self.image_icon_map = {'tiff' : 'tif',
62 'unknown' : 'unknown',
65 self.video_icon_map = {'unknown' : 'movie',
69 QTimer.singleShot(0, self.fillControls)
72 def fillControls(self):
73 ScrollView.fillControls(self)
76 self.addGroupHeading("error", self.__tr("ERROR: Photo Card Failed to Mount Properly. Please check device and card and try again."))
78 self.addGroupHeading("files_to_unload", self.__tr("Select File(s) to Unload from Photo Card"))
81 self.addGroupHeading("folder", self.__tr("Unload Folder"))
84 self.removal_option = 1 # remove files (default)
86 self.addGroupHeading("options", self.__tr("Unload Options"))
89 self.addGroupHeading("space1", "")
91 self.unloadButton = self.addActionButton("bottom_nav", self.__tr("Unload File(s)"),
92 self.unloadButton_clicked, 'download.png', 'download.png',
93 self.__tr("Close"), self.funcButton_clicked)
95 self.unloadButton.setEnabled(False)
97 self.maximizeControl()
100 def onDeviceChange(self, cur_device=None):
101 if cur_device is not None:
102 log.debug("ScrollUnloadView.onDeviceChange(%s)" % cur_device.device_uri)
103 self.cur_device = cur_device
105 log.debug("ScrollUnloadView.onDeviceChange(None)")
107 # TODO: Print a message telling users to use USB mass storage if possible
108 QTimer.singleShot(0, self.mountCard)
114 if self.cur_device is not None and self.cur_device.supported:
116 QApplication.setOverrideCursor(QApplication.waitCursor)
119 self.pc = photocard.PhotoCard(None, self.cur_device.device_uri, self.cur_printer)
121 QApplication.restoreOverrideCursor()
122 self.form.FailureUI(self.__tr("An error occured: %s" % e[0]))
123 self.cleanup(EVENT_PCARD_UNABLE_TO_MOUNT)
126 if self.pc.device.device_uri is None and self.cur_printer:
127 QApplication.restoreOverrideCursor()
128 self.form.FailureUI(self.__tr("Printer '%s' not found." % self.cur_printer))
129 self.cleanup(EVENT_PCARD_JOB_FAIL)
132 if self.pc.device.device_uri is None and self.cur_device.device_uri:
133 QApplication.restoreOverrideCursor()
134 self.form.FailureUI(self.__tr("Malformed/invalid device-uri: %s" % self.device_uri))
135 self.cleanup(EVENT_PCARD_JOB_FAIL)
142 QApplication.restoreOverrideCursor()
143 self.form.FailureUI(self.__tr("<b>Unable to mount photo card on device.</b><p>Check that device is powered on and photo card is correctly inserted."))
145 self.cleanup(EVENT_PCARD_UNABLE_TO_MOUNT)
148 self.device_uri = self.pc.device.device_uri
149 user_conf.set('last_used', 'device_uri', self.device_uri)
152 #self.pc.device.sendEvent(EVENT_START_PCARD_JOB)
154 disk_info = self.pc.info()
155 self.pc.write_protect = disk_info[8]
157 if self.pc.write_protect:
158 log.warning("Photo card is write protected.")
160 log.info("Photocard on device %s mounted" % self.pc.device_uri)
162 if not self.pc.write_protect:
163 log.info("DO NOT REMOVE PHOTO CARD UNTIL YOU EXIT THIS PROGRAM")
165 self.unload_dir = user_conf.workingDirectory()
168 os.chdir(self.unload_dir)
172 self.UnloadDirectoryEdit.setText(self.unload_dir)
174 self.unload_list = self.pc.get_unload_list()
176 self.total_number = 0
179 self.updateSelectionStatus()
181 if self.pc.write_protect:
182 self.removal_option = 0 # leave all files on card
184 # Item map disambiguates between files of the same
185 # name that are on the pcard in more than one location
188 QApplication.restoreOverrideCursor()
191 log.debug("Unsupported device")
198 QTimer.singleShot(0, self.updateIconView)
200 self.display_update_timer = QTimer(self, "DisplayUpdateTimer")
201 self.connect(self.display_update_timer, SIGNAL('timeout()'), self.updateDisplay)
203 self.display_update_timer.start(1000)
209 def addIconList(self):
210 widget = self.getWidget()
212 layout32 = QGridLayout(widget,1,1,5,10,"layout32")
214 spacer34 = QSpacerItem(20,20,QSizePolicy.Expanding,QSizePolicy.Minimum)
215 layout32.addItem(spacer34,2,2)
217 self.selectAllPushButton = PixmapLabelButton(widget, 'ok.png', None)
219 layout32.addWidget(self.selectAllPushButton,2,0)
221 self.ShowThumbnailsButton = PixmapLabelButton(widget, 'thumbnail.png', None)
223 layout32.addWidget(self.ShowThumbnailsButton,2,3)
225 self.IconView = QIconView(widget,"IconView")
226 self.IconView.setResizePolicy(QIconView.AutoOneFit)
227 self.IconView.setSelectionMode(QIconView.Multi)
228 self.IconView.setResizeMode(QIconView.Adjust)
229 self.IconView.setMaxItemWidth(200)
230 self.IconView.setAutoArrange(0)
231 self.IconView.setItemsMovable(1)
233 layout32.addMultiCellWidget(self.IconView,0,0,0,3)
235 self.selectNonePushButton = PixmapLabelButton(widget, 'folder_remove.png', None)
237 layout32.addWidget(self.selectNonePushButton,2,1)
239 self.selectionStatusText = QLabel(widget,"selectionStatusText")
240 layout32.addMultiCellWidget(self.selectionStatusText,1,1,0,3)
241 self.selectAllPushButton.setText(self.__tr("Select All"))
242 self.selectNonePushButton.setText(self.__tr("Select None"))
243 self.ShowThumbnailsButton.setText(self.__tr("Show Thumbnails"))
245 self.connect(self.selectAllPushButton,SIGNAL("clicked()"),self.selectAllPushButton_clicked)
246 self.connect(self.selectNonePushButton,SIGNAL("clicked()"),self.selectNonePushButton_clicked)
247 self.connect(self.IconView,SIGNAL("doubleClicked(QIconViewItem*)"),self.IconView_doubleClicked)
248 self.connect(self.IconView,SIGNAL("rightButtonClicked(QIconViewItem*,const QPoint&)"),self.IconView_rightButtonClicked)
249 self.connect(self.ShowThumbnailsButton,SIGNAL("clicked()"),self.ShowThumbnailsButton_clicked)
251 self.addWidget(widget, "file_list", maximize=True)
254 def selectAllPushButton_clicked(self):
255 self.IconView.selectAll(1)
257 def selectNonePushButton_clicked(self):
258 self.IconView.selectAll(0)
260 def IconView_doubleClicked(self, a0):
261 return self.PopupProperties()
263 def IconView_rightButtonClicked(self, item, pos):
264 popup = QPopupMenu(self)
265 popup.insertItem(self.__tr("Properties"), self.PopupProperties)
267 if item is not None and \
268 item.mime_type == 'image' and \
269 item.mime_subtype == 'jpeg' and \
270 self.pc.get_exif_path(item.path) and \
271 not item.thumbnail_set:
273 popup.insertItem(self.__tr("Show Thumbnail"), self.showThumbNail)
277 def ShowThumbnailsButton_clicked(self):
278 self.ShowThumbnailsButton.setEnabled(False)
279 self.updateIconView(first_load=False)
281 def updateDisplay(self):
283 self.total_number = 0
285 i = self.IconView.firstItem()
290 self.total_number += 1
291 self.total_size += i.size
295 self.updateSelectionStatus()
297 self.updateUnloadButton()
299 def updateUnloadButton(self):
300 self.unloadButton.setEnabled(not self.busy and self.total_number and os.path.exists(self.unload_dir))
301 #qApp.processEvents()
303 def updateSelectionStatus(self):
304 if self.total_number == 0:
305 s = self.__tr("No files selected")
307 elif self.total_number == 1:
308 s = self.__tr("1 file selected, %1").arg(utils.format_bytes(self.total_size, True))
311 s = self.__tr("%1 files selected, %2").arg(self.total_number).arg(utils.format_bytes(self.total_size, True))
313 self.selectionStatusText.setText(s)
316 def PopupDisplay(self):
317 self.Display(self.IconView.currentItem())
319 def PopupProperties(self):
320 self.Properties(self.IconView.currentItem())
322 def showThumbNail(self):
323 item = self.IconView.currentItem()
324 exif_info = self.pc.get_exif_path(item.path)
326 if len(exif_info) > 0:
327 if 'JPEGThumbnail' in exif_info:
329 pixmap.loadFromData(exif_info['JPEGThumbnail'], "JPEG")
330 self.resizePixmap(pixmap)
331 del exif_info['JPEGThumbnail']
332 item.setPixmap(pixmap)
334 self.IconView.adjustItems()
337 self.form.FailureUI(self.__tr("<p><b>No thumbnail found in image.</b>"))
339 item.thumbnail_set = True
341 def Properties(self, item):
343 if not item.exif_info:
344 item.exif_info = self.pc.get_exif_path(item.path)
346 ImagePropertiesDlg(item.filename, item.dirname,
347 '/'.join([item.mime_type, item.mime_subtype]),
348 utils.format_bytes(item.size, True),
349 item.exif_info, self).exec_loop()
352 def updateIconView(self, first_load=True):
353 QApplication.setOverrideCursor(QApplication.waitCursor)
355 self.first_load = first_load
359 self.IconView.clear()
361 self.num_items = len(self.unload_list)
363 self.progress_dlg = QProgressDialog(self.__tr("Loading..."), \
364 self.__tr("Cancel"), self.num_items, self, "progress", 0)
365 self.progress_dlg.setCaption(self.__tr("HP Device Manager"))
367 self.progress_dlg.setMinimumDuration(0)
368 self.progress_dlg.setProgress(0)
370 self.load_timer = QTimer(self, "LoadTimer")
371 self.connect(self.load_timer, SIGNAL('timeout()'), self.continueLoadIconView)
372 self.load_timer.start(0)
375 def continueLoadIconView(self):
376 if self.item_num == self.num_items:
377 self.load_timer.stop()
378 self.disconnect(self.load_timer, SIGNAL('timeout()'), self.continueLoadIconView)
379 self.load_timer = None
382 self.progress_dlg.close()
383 self.progress_dlg = None
385 self.IconView.adjustItems()
387 QApplication.restoreOverrideCursor()
390 self.progress_dlg.setProgress(self.item_num)
392 f = self.unload_list[self.item_num]
396 path, size = f[0], f[1]
398 typ, subtyp = self.pc.classify_file(path).split('/')
400 if not self.first_load and typ == 'image' and subtyp == 'jpeg':
402 exif_info = self.pc.get_exif_path(path)
403 if len(exif_info) > 0:
405 if 'JPEGThumbnail' in exif_info:
407 pixmap.loadFromData(exif_info['JPEGThumbnail'], "JPEG")
409 self.resizePixmap(pixmap)
411 del exif_info['JPEGThumbnail']
412 dname, fname=os.path.split(path)
413 x = self.item_map[fname]
416 item = self.IconView.findItem(fname, 0)
420 item = self.IconView.findItem(fname, 0)
422 item = self.IconView.findItem(fname + " (%d)" % (i+1), 0)
425 item.setPixmap(pixmap)
426 item.thumbnail_set = True
430 elif self.first_load:
432 f = self.image_icon_map.get(subtyp, 'unknown')
435 f = self.video_icon_map.get(subtyp, 'movie')
443 dirname, fname=os.path.split(path)
448 self.item_map[fname] = [path]
450 self.item_map[fname].append(path)
451 num = len(self.item_map[fname])
454 IconViewItem(self.IconView, dirname, fname, path,
455 load_pixmap(f, '128x128'), typ, subtyp, size)
457 IconViewItem(self.IconView, dirname, fname + " (%d)" % num,
458 path,load_pixmap(f, '128x128'), typ, subtyp, size)
460 def resizePixmap(self, pixmap):
461 w, h = pixmap.width(), pixmap.height()
463 if h > 128 or w > 128:
464 ww, hh = w - 128, h - 128
466 pixmap.resize(128, int(float((w-ww))/w*h))
468 pixmap.resize(int(float((h-hh))/h*w), 128)
471 widget = self.getWidget()
472 layout38 = QGridLayout(widget,1,1,5,10,"layout38")
474 self.UnloadDirectoryEdit = QLineEdit(widget,"UnloadDirectoryEdit")
475 layout38.addWidget(self.UnloadDirectoryEdit,0,0)
477 self.UnloadDirectoryBrowseButton = PixmapLabelButton(widget, 'folder_open.png', None)
478 layout38.addWidget(self.UnloadDirectoryBrowseButton,0,1)
480 self.UnloadDirectoryBrowseButton.setText(self.__tr("Browse..."))
481 self.connect(self.UnloadDirectoryBrowseButton,SIGNAL("clicked()"),self.UnloadDirectoryBrowseButton_clicked)
482 self.connect(self.UnloadDirectoryEdit,SIGNAL("textChanged(const QString&)"),self.UnloadDirectoryEdit_textChanged)
484 self.bg = self.UnloadDirectoryEdit.paletteBackgroundColor()
486 self.addWidget(widget, "folder")
488 def UnloadDirectoryEdit_textChanged(self, dir):
489 self.unload_dir = unicode(dir)
491 if not os.path.exists(self.unload_dir):
492 self.UnloadDirectoryEdit.setPaletteBackgroundColor(QColor(0xff, 0x99, 0x99))
494 self.UnloadDirectoryEdit.setPaletteBackgroundColor(self.bg)
496 def UnloadDirectoryBrowseButton_clicked(self):
497 old_dir = self.unload_dir
498 self.unload_dir = unicode(QFileDialog.getExistingDirectory(self.unload_dir, self))
500 if not len(self.unload_dir):
503 elif not utils.is_path_writable(self.unload_dir):
504 self.form.FailureUI(self.__tr("<p><b>The unload directory path you entered is not valid.</b><p>The directory must exist and you must have write permissions."))
505 self.unload_dir = old_dir
508 self.UnloadDirectoryEdit.setText(self.unload_dir)
509 os.chdir(self.unload_dir)
510 user_conf.setWorkingDirectory(self.unload_dir)
512 def addOptions(self):
513 widget = self.getWidget()
515 layout34 = QHBoxLayout(widget,5,10,"layout34")
517 self.textLabel5_4 = QLabel(widget,"textLabel5_4")
518 layout34.addWidget(self.textLabel5_4)
519 spacer20_4 = QSpacerItem(20,20,QSizePolicy.Expanding,QSizePolicy.Minimum)
520 layout34.addItem(spacer20_4)
522 self.optionComboBox = QComboBox(0,widget,"optionsComboBox")
523 layout34.addWidget(self.optionComboBox)
525 self.textLabel5_4.setText(self.__tr("File removal:"))
526 self.optionComboBox.clear()
527 self.optionComboBox.insertItem(self.__tr("Leave unloaded files on photo card")) # 0
528 self.optionComboBox.insertItem(self.__tr("Remove all unloaded files from photo card")) # 1
529 self.optionComboBox.setCurrentItem(self.removal_option)
531 self.connect(self.optionComboBox, SIGNAL("activated(int)"), self.optionComboBox_activated)
533 self.addWidget(widget, "option")
535 def optionComboBox_activated(self, i):
536 self.removal_option = i
539 def unloadButton_clicked(self):
540 was_cancelled = False
542 self.unloadButton.setEnabled(False)
543 self.unload_dir = unicode(self.UnloadDirectoryEdit.text())
548 os.chdir(self.unload_dir)
550 log.error("Directory not found: %s" % self.unload_dir)
553 if dir_error or not utils.is_path_writable(self.unload_dir):
554 self.form.FailureUI(self.__tr("<p><b>The unload directory path is not valid.</b><p>Please enter a new path and try again."))
558 i = self.IconView.firstItem()
563 unload_list.append((i.path, i.size, i.mime_type, i.mime_subtype))
564 self.total_size += i.size
567 if self.total_size == 0:
568 self.form.FailureUI(self.__tr("<p><b>No files are selected to unload.</b><p>Please select one or more files to unload and try again."))
571 self.total_complete = 0
572 self.progress_dlg = QProgressDialog(self.__tr("Unloading card..."), \
573 self.__tr("Cancel"), 100, self, "progress", 1)
574 self.progress_dlg.setCaption(self.__tr("HP Device Manager"))
575 self.progress_dlg.setMinimumDuration(0)
576 self.progress_dlg.setProgress(0)
579 if self.removal_option == 0: # Leave files
580 total_size, total_time, was_cancelled = \
581 self.pc.unload(unload_list, self.updateStatusProgressBar, None, True)
583 elif self.removal_option == 1: # remove selected
584 total_size, total_time, was_cancelled = \
585 self.pc.unload(unload_list, self.updateStatusProgressBar, None, False)
588 total_size, total_time, was_cancelled = \
589 self.pc.unload(unload_list, self.updateStatusProgressBar, None, False)
590 # TODO: Remove remainder of files
592 self.progress_dlg.close()
593 self.progress_dlg = None
596 #self.pc.device.sendEvent(EVENT_PCARD_FILES_TRANSFERED)
598 if self.removal_option != 0: # remove selected or remove all
599 self.unload_list = self.pc.get_unload_list()
600 self.total_number = 0
603 self.total_complete = 0
604 self.updateIconView()
607 self.form.FailureUI(self.__tr("<b>Unload cancelled at user request.</b>"))
614 def updateStatusProgressBar(self, src, trg, size):
616 self.total_complete += size
617 percent = int(100.0 * self.total_complete/self.total_size)
618 self.progress_dlg.setProgress(percent)
621 if self.progress_dlg.wasCanceled():
627 def cleanup(self, error=0):
628 if self.pc is not None:
631 #self.pc.device.sendEvent(error, typ='error')
636 self.pc.device.close()
640 def funcButton_clicked(self):
641 if self.pc is not None:
643 self.pc.device.close()
647 def __tr(self,s,c = None):
648 return qApp.translate("ScrollUnloadView",s,c)