Tizen 2.1 base
[platform/upstream/hplip.git] / ui / scrollunload.py
1 # -*- coding: utf-8 -*-
2 #
3 # (c) Copyright 2001-2009 Hewlett-Packard Development Company, L.P.
4 #
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.
9 #
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.
14 #
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
18 #
19 # Author: Don Welch
20 #
21
22 # Local
23 from base.g import *
24 from base import utils, magic
25 from pcard import photocard
26 from ui_utils import load_pixmap
27
28 # Qt
29 from qt import *
30 from scrollview import ScrollView, PixmapLabelButton
31 from imagepropertiesdlg import ImagePropertiesDlg
32 #from waitform import WaitForm
33
34 # Std Lib
35 import os.path, os
36
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
42         self.path = path
43         self.dirname = dirname
44         self.filename = fname
45         self.exif_info = exif_info
46         self.size = size
47         self.thumbnail_set = False
48
49
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)
53
54         self.form = form
55         self.progress_dlg = None
56         self.unload_dir = os.path.normpath(os.path.expanduser('~'))
57
58         self.image_icon_map = {'tiff' : 'tif',
59                                'bmp'  : 'bmp',
60                                'jpeg' : 'jpg',
61                                'gif'  : 'gif',
62                                'unknown' : 'unknown',
63                                 }
64
65         self.video_icon_map = {'unknown' : 'movie',
66                                 'mpeg'    : 'mpg',
67                                 }
68
69         QTimer.singleShot(0, self.fillControls)
70
71
72     def fillControls(self):
73         ScrollView.fillControls(self)
74
75         if 0:
76             self.addGroupHeading("error", self.__tr("ERROR: Photo Card Failed to Mount Properly. Please check device and card and try again."))
77
78         self.addGroupHeading("files_to_unload", self.__tr("Select File(s) to Unload from Photo Card"))
79         self.addIconList()
80
81         self.addGroupHeading("folder", self.__tr("Unload Folder"))
82         self.addFolder()
83
84         self.removal_option = 1 # remove files (default)
85
86         self.addGroupHeading("options", self.__tr("Unload Options"))
87         self.addOptions()
88
89         self.addGroupHeading("space1", "")
90
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)
94
95         self.unloadButton.setEnabled(False)
96
97         self.maximizeControl()
98
99
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
104         else:
105             log.debug("ScrollUnloadView.onDeviceChange(None)")
106
107         # TODO: Print a message telling users to use USB mass storage if possible
108         QTimer.singleShot(0, self.mountCard)
109
110
111     def mountCard(self):
112         self.pc = None
113
114         if self.cur_device is not None and self.cur_device.supported:
115
116             QApplication.setOverrideCursor(QApplication.waitCursor)
117
118             try:
119                 self.pc = photocard.PhotoCard(None, self.cur_device.device_uri, self.cur_printer)
120             except Error, e:
121                 QApplication.restoreOverrideCursor()
122                 self.form.FailureUI(self.__tr("An error occured: %s" % e[0]))
123                 self.cleanup(EVENT_PCARD_UNABLE_TO_MOUNT)
124                 return False
125
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)
130                 return False
131
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)
136
137                 return False
138             else:
139                 try:
140                     self.pc.mount()
141                 except Error:
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."))
144                     #self.pc.umount()
145                     self.cleanup(EVENT_PCARD_UNABLE_TO_MOUNT)
146                     return
147
148                 self.device_uri = self.pc.device.device_uri
149                 user_conf.set('last_used', 'device_uri', self.device_uri)
150
151                 # TODO:
152                 #self.pc.device.sendEvent(EVENT_START_PCARD_JOB)
153
154                 disk_info = self.pc.info()
155                 self.pc.write_protect = disk_info[8]
156
157                 if self.pc.write_protect:
158                     log.warning("Photo card is write protected.")
159
160                 log.info("Photocard on device %s mounted" % self.pc.device_uri)
161
162                 if not self.pc.write_protect:
163                     log.info("DO NOT REMOVE PHOTO CARD UNTIL YOU EXIT THIS PROGRAM")
164
165                 self.unload_dir = user_conf.workingDirectory()
166
167                 try:
168                     os.chdir(self.unload_dir)
169                 except OSError:
170                     self.unload_dir = ''
171
172                 self.UnloadDirectoryEdit.setText(self.unload_dir)
173
174                 self.unload_list = self.pc.get_unload_list()
175
176                 self.total_number = 0
177                 self.total_size = 0
178
179                 self.updateSelectionStatus()
180
181                 if self.pc.write_protect:
182                     self.removal_option = 0 # leave all files on card
183
184                 # Item map disambiguates between files of the same
185                 # name that are on the pcard in more than one location
186                 self.item_map = {}
187
188                 QApplication.restoreOverrideCursor()
189
190         else:
191             log.debug("Unsupported device")
192             self.y = 0
193             self.clear()
194             return False
195
196         self.busy = False
197
198         QTimer.singleShot(0, self.updateIconView)
199
200         self.display_update_timer = QTimer(self, "DisplayUpdateTimer")
201         self.connect(self.display_update_timer, SIGNAL('timeout()'), self.updateDisplay)
202
203         self.display_update_timer.start(1000)
204
205         return True
206
207
208
209     def addIconList(self):
210         widget = self.getWidget()
211
212         layout32 = QGridLayout(widget,1,1,5,10,"layout32")
213
214         spacer34 = QSpacerItem(20,20,QSizePolicy.Expanding,QSizePolicy.Minimum)
215         layout32.addItem(spacer34,2,2)
216
217         self.selectAllPushButton = PixmapLabelButton(widget, 'ok.png', None)
218
219         layout32.addWidget(self.selectAllPushButton,2,0)
220
221         self.ShowThumbnailsButton = PixmapLabelButton(widget, 'thumbnail.png', None)
222
223         layout32.addWidget(self.ShowThumbnailsButton,2,3)
224
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)
232
233         layout32.addMultiCellWidget(self.IconView,0,0,0,3)
234
235         self.selectNonePushButton = PixmapLabelButton(widget, 'folder_remove.png', None)
236
237         layout32.addWidget(self.selectNonePushButton,2,1)
238
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"))
244
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)
250
251         self.addWidget(widget, "file_list", maximize=True)
252
253
254     def selectAllPushButton_clicked(self):
255         self.IconView.selectAll(1)
256
257     def selectNonePushButton_clicked(self):
258         self.IconView.selectAll(0)
259
260     def IconView_doubleClicked(self, a0):
261         return self.PopupProperties()
262
263     def IconView_rightButtonClicked(self, item, pos):
264         popup = QPopupMenu(self)
265         popup.insertItem(self.__tr("Properties"), self.PopupProperties)
266
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:
272
273             popup.insertItem(self.__tr("Show Thumbnail"), self.showThumbNail)
274
275         popup.popup(pos)
276
277     def ShowThumbnailsButton_clicked(self):
278         self.ShowThumbnailsButton.setEnabled(False)
279         self.updateIconView(first_load=False)
280
281     def updateDisplay(self):
282         if not self.busy:
283             self.total_number = 0
284             self.total_size = 0
285             i = self.IconView.firstItem()
286
287             while i is not None:
288
289                 if i.isSelected():
290                     self.total_number += 1
291                     self.total_size += i.size
292
293                 i = i.nextItem()
294
295             self.updateSelectionStatus()
296
297         self.updateUnloadButton()
298
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()
302
303     def updateSelectionStatus(self):
304         if self.total_number == 0:
305             s = self.__tr("No files selected")
306
307         elif self.total_number == 1:
308             s = self.__tr("1 file selected, %1").arg(utils.format_bytes(self.total_size, True))
309
310         else:
311             s = self.__tr("%1 files selected, %2").arg(self.total_number).arg(utils.format_bytes(self.total_size, True))
312
313         self.selectionStatusText.setText(s)
314
315
316     def PopupDisplay(self):
317         self.Display(self.IconView.currentItem())
318
319     def PopupProperties(self):
320         self.Properties(self.IconView.currentItem())
321
322     def showThumbNail(self):
323         item = self.IconView.currentItem()
324         exif_info = self.pc.get_exif_path(item.path)
325
326         if len(exif_info) > 0:
327             if 'JPEGThumbnail' in exif_info:
328                 pixmap = QPixmap()
329                 pixmap.loadFromData(exif_info['JPEGThumbnail'], "JPEG")
330                 self.resizePixmap(pixmap)
331                 del exif_info['JPEGThumbnail']
332                 item.setPixmap(pixmap)
333
334                 self.IconView.adjustItems()
335
336         else:
337             self.form.FailureUI(self.__tr("<p><b>No thumbnail found in image.</b>"))
338
339         item.thumbnail_set = True
340
341     def Properties(self, item):
342         if item is not None:
343             if not item.exif_info:
344                 item.exif_info = self.pc.get_exif_path(item.path)
345
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()
350
351
352     def updateIconView(self, first_load=True):
353         QApplication.setOverrideCursor(QApplication.waitCursor)
354         self.busy = True
355         self.first_load = first_load
356         self.item_num = 0
357
358         if first_load:
359             self.IconView.clear()
360
361         self.num_items = len(self.unload_list)
362
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"))
366
367         self.progress_dlg.setMinimumDuration(0)
368         self.progress_dlg.setProgress(0)
369
370         self.load_timer = QTimer(self, "LoadTimer")
371         self.connect(self.load_timer, SIGNAL('timeout()'), self.continueLoadIconView)
372         self.load_timer.start(0)
373
374
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
380             del self.load_timer
381
382             self.progress_dlg.close()
383             self.progress_dlg = None
384
385             self.IconView.adjustItems()
386             self.busy = False
387             QApplication.restoreOverrideCursor()
388             return
389
390         self.progress_dlg.setProgress(self.item_num)
391
392         f = self.unload_list[self.item_num]
393         self.item_num += 1
394         log.debug(f)
395
396         path, size = f[0], f[1]
397
398         typ, subtyp = self.pc.classify_file(path).split('/')
399
400         if not self.first_load and typ == 'image' and subtyp == 'jpeg':
401
402             exif_info = self.pc.get_exif_path(path)
403             if len(exif_info) > 0:
404
405                 if 'JPEGThumbnail' in exif_info:
406                     pixmap = QPixmap()
407                     pixmap.loadFromData(exif_info['JPEGThumbnail'], "JPEG")
408
409                     self.resizePixmap(pixmap)
410
411                     del exif_info['JPEGThumbnail']
412                     dname, fname=os.path.split(path)
413                     x = self.item_map[fname]
414
415                     if len(x) == 1:
416                         item = self.IconView.findItem(fname, 0)
417                     else:
418                         i = x.index(path)
419                         if i == 0:
420                             item = self.IconView.findItem(fname, 0)
421                         else:
422                             item = self.IconView.findItem(fname + " (%d)" % (i+1), 0)
423
424                     if item is not None:
425                         item.setPixmap(pixmap)
426                         item.thumbnail_set = True
427
428                     return
429
430         elif self.first_load:
431             if typ == 'image':
432                 f = self.image_icon_map.get(subtyp, 'unknown')
433
434             elif typ == 'video':
435                 f = self.video_icon_map.get(subtyp, 'movie')
436
437             elif typ == 'audio':
438                 f = 'sound'
439
440             else:
441                 f = 'unknown'
442
443             dirname, fname=os.path.split(path)
444             num = 1
445             try:
446                 self.item_map[fname]
447             except:
448                 self.item_map[fname] = [path]
449             else:
450                 self.item_map[fname].append(path)
451                 num = len(self.item_map[fname])
452
453             if num == 1:
454                 IconViewItem(self.IconView, dirname, fname, path,
455                     load_pixmap(f, '128x128'), typ, subtyp, size)
456             else:
457                 IconViewItem(self.IconView, dirname, fname + " (%d)" % num,
458                               path,load_pixmap(f, '128x128'), typ, subtyp, size)
459
460     def resizePixmap(self, pixmap):
461         w, h = pixmap.width(), pixmap.height()
462
463         if h > 128 or w > 128:
464             ww, hh = w - 128, h - 128
465             if ww >= hh:
466                 pixmap.resize(128, int(float((w-ww))/w*h))
467             else:
468                 pixmap.resize(int(float((h-hh))/h*w), 128)
469
470     def addFolder(self):
471         widget = self.getWidget()
472         layout38 = QGridLayout(widget,1,1,5,10,"layout38")
473
474         self.UnloadDirectoryEdit = QLineEdit(widget,"UnloadDirectoryEdit")
475         layout38.addWidget(self.UnloadDirectoryEdit,0,0)
476
477         self.UnloadDirectoryBrowseButton = PixmapLabelButton(widget, 'folder_open.png', None)
478         layout38.addWidget(self.UnloadDirectoryBrowseButton,0,1)
479
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)
483
484         self.bg = self.UnloadDirectoryEdit.paletteBackgroundColor()
485
486         self.addWidget(widget, "folder")
487
488     def UnloadDirectoryEdit_textChanged(self, dir):
489         self.unload_dir = unicode(dir)
490
491         if not os.path.exists(self.unload_dir):
492             self.UnloadDirectoryEdit.setPaletteBackgroundColor(QColor(0xff, 0x99, 0x99))
493         else:
494             self.UnloadDirectoryEdit.setPaletteBackgroundColor(self.bg)
495
496     def UnloadDirectoryBrowseButton_clicked(self):
497         old_dir = self.unload_dir
498         self.unload_dir = unicode(QFileDialog.getExistingDirectory(self.unload_dir, self))
499
500         if not len(self.unload_dir):
501             return
502
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
506
507         else:
508             self.UnloadDirectoryEdit.setText(self.unload_dir)
509             os.chdir(self.unload_dir)
510             user_conf.setWorkingDirectory(self.unload_dir)
511
512     def addOptions(self):
513         widget = self.getWidget()
514
515         layout34 = QHBoxLayout(widget,5,10,"layout34")
516
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)
521
522         self.optionComboBox = QComboBox(0,widget,"optionsComboBox")
523         layout34.addWidget(self.optionComboBox)
524
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)
530
531         self.connect(self.optionComboBox, SIGNAL("activated(int)"), self.optionComboBox_activated)
532
533         self.addWidget(widget, "option")
534
535     def optionComboBox_activated(self, i):
536         self.removal_option = i
537
538
539     def unloadButton_clicked(self):
540         was_cancelled = False
541         self.busy = True
542         self.unloadButton.setEnabled(False)
543         self.unload_dir = unicode(self.UnloadDirectoryEdit.text())
544         dir_error = False
545
546         try:
547             try:
548                 os.chdir(self.unload_dir)
549             except OSError:
550                 log.error("Directory not found: %s" % self.unload_dir)
551                 dir_error = True
552
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."))
555                 return
556
557             unload_list = []
558             i = self.IconView.firstItem()
559             self.total_size = 0
560             while i is not None:
561
562                 if i.isSelected():
563                     unload_list.append((i.path, i.size, i.mime_type, i.mime_subtype))
564                     self.total_size += i.size
565                 i = i.nextItem()
566
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."))
569                 return
570
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)
577             qApp.processEvents()
578
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)
582
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)
586
587             else: # remove all
588                 total_size, total_time, was_cancelled = \
589                     self.pc.unload(unload_list, self.updateStatusProgressBar, None, False)
590                 # TODO: Remove remainder of files
591
592             self.progress_dlg.close()
593             self.progress_dlg = None
594
595             # TODO:
596             #self.pc.device.sendEvent(EVENT_PCARD_FILES_TRANSFERED)
597
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
601                 self.total_size = 0
602                 self.item_map = {}
603                 self.total_complete = 0
604                 self.updateIconView()
605
606             if was_cancelled:
607                 self.form.FailureUI(self.__tr("<b>Unload cancelled at user request.</b>"))
608             else:
609                 pass
610
611         finally:
612             self.busy = False
613
614     def updateStatusProgressBar(self, src, trg, size):
615         qApp.processEvents()
616         self.total_complete += size
617         percent = int(100.0 * self.total_complete/self.total_size)
618         self.progress_dlg.setProgress(percent)
619         qApp.processEvents()
620
621         if self.progress_dlg.wasCanceled():
622             return True
623
624         return False
625
626
627     def cleanup(self, error=0):
628         if self.pc is not None:
629             if error > 0:
630                 # TODO:
631                 #self.pc.device.sendEvent(error, typ='error')
632                 pass
633
634             try:
635                 self.pc.umount()
636                 self.pc.device.close()
637             except Error:
638                 pass
639
640     def funcButton_clicked(self):
641         if self.pc is not None:
642             self.pc.umount()
643             self.pc.device.close()
644
645         self.form.close()
646
647     def __tr(self,s,c = None):
648         return qApp.translate("ScrollUnloadView",s,c)