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
30 from base.codes import *
31 from base import utils
35 from PyQt4.QtCore import *
36 from PyQt4.QtGui import *
38 pat_html_remove = re.compile("(?is)<.*?>", re.I)
40 # databaseChanged signal values (for FABWindow)
41 FAB_NAME_ADD = 0 # s1 - new name
42 FAB_NAME_RENAME = 1 # s1 - old name, s2 - new name
43 FAB_NAME_REMOVE = 2 # s1 - removed name
44 FAB_NAME_DETAILS_CHANGED = 3 # s1 - name
45 FAB_GROUP_ADD = 4 # s1 - new group
46 FAB_GROUP_RENAME = 5 # s1 - old group, s2 - new group
47 FAB_GROUP_REMOVE = 6 # s1 - removed group
48 FAB_GROUP_MEMBERSHIP_CHANGED = 7 # s1 - group
52 return QApplication.translate("ui_utils", t, None, QApplication.UnicodeUTF8)
55 def beginWaitCursor():
56 QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
60 QApplication.restoreOverrideCursor()
64 def load_pixmap(name, subdir=None, resize_to=None):
65 name = ''.join([os.path.splitext(name)[0], '.png'])
69 ldir = os.path.join(os.getcwd(), 'data', 'images')
71 dir = os.path.join(prop.image_dir, subdir)
72 ldir = os.path.join(os.getcwd(), 'data', 'images', subdir)
75 f = os.path.join(d, name)
77 if resize_to is not None:
80 return QPixmap.fromImage(img.scaled(x, y, Qt.IgnoreAspectRatio, Qt.SmoothTransformation))
84 for w in utils.walkFiles(dir, recurse=True, abs_paths=True, return_folders=False, pattern=name):
85 if resize_to is not None:
88 return QPixmap.fromImage(img.scaled(x, y, Qt.IgnoreAspectRatio, Qt.SmoothTransformation))
92 log.error("Pixmap '%s' not found!" % name)
95 loadPixmap = load_pixmap
98 def getPynotifyIcon(name, subdir='32x32'):
99 name = ''.join([os.path.splitext(name)[0], '.png'])
100 return "file://" + os.path.join(prop.image_dir, subdir, name)
104 class UserSettings(QSettings):
106 if prop.user_dir is None:
107 QSettings.__init__(self)
109 QSettings.__init__(self, os.path.join(prop.user_dir, 'hplip.conf'), QSettings.IniFormat)
111 self.systray_visible = SYSTRAY_VISIBLE_SHOW_ALWAYS
112 self.systray_messages = SYSTRAY_MESSAGES_SHOW_ALL
113 self.last_used_device_uri = ''
114 self.last_used_printer = ''
117 self.auto_refresh = False
118 self.auto_refresh_rate = 30
119 self.auto_refresh_type = 1
120 self.polling_interval = 5
122 self.device_list = []
123 self.working_dir = '.'
124 self.voice_phone = ''
125 self.email_address = ''
126 self.upgrade_notify=True
127 self.upgrade_last_update_time=0
128 self.upgrade_pending_update_time=0
129 self.latest_available_version=""
133 def __setup(self, cmds):
135 basename = c.split()[0]
136 path = utils.which(basename)
138 return ' '.join([os.path.join(path, basename), ' '.join(c.split()[1:])])
143 def loadDefaults(self):
144 self.cmd_scan = self.__setup(['xsane -V %SANE_URI%', 'kooka', 'xscanimage'])
145 self.cmd_fab = self.__setup(['hp-fab'])
149 log.debug("Loading user settings...")
152 self.beginGroup("settings")
153 i, ok = self.value("systray_visible").toInt()
155 self.systray_visible = i
157 i, ok = self.value("systray_messages").toInt()
159 self.systray_messages = i
163 self.beginGroup("last_used")
164 self.last_used_device_uri = unicode(self.value("device_uri").toString()) or self.last_used_device_uri
165 self.last_used_printer = unicode(self.value("printer_name").toString()) or self.last_used_printer
166 self.working_dir = unicode(self.value("working_dir").toString()) or self.working_dir
169 self.beginGroup("commands")
170 self.cmd_scan = unicode(self.value("scan").toString()) or self.cmd_scan
173 self.beginGroup("refresh")
174 self.auto_refresh_rate = int(self.value("rate").toString() or self.auto_refresh_rate)
175 self.auto_refresh = bool(self.value("enable").toBool())
176 self.auto_refresh_type = int(self.value("type").toString() or self.auto_refresh_type)
179 self.beginGroup("installation")
180 self.version = unicode(self.value("version").toString())
181 self.date_time = unicode(self.value("date_time").toString())
184 self.beginGroup("polling")
185 self.polling = bool(self.value("enable").toBool())
186 self.polling_interval = int(self.value("interval").toString() or self.polling_interval)
187 self.polling_device_list = unicode(self.value("device_list").toString() or '').split(u',')
190 self.beginGroup("fax")
191 self.voice_phone = unicode(self.value("voice_phone").toString())
192 self.email_address = unicode(self.value("email_address").toString())
195 self.beginGroup("upgrade")
196 self.upgrade_notify= bool(self.value("notify_upgrade").toBool())
197 self.latest_available_version=str(self.value("latest_available_version").toString())
199 i, Ok = self.value("last_upgraded_time").toInt()
201 self.upgrade_last_update_time =i
203 self.upgrade_last_update_time = 0
205 i, Ok = self.value("pending_upgrade_time").toInt()
207 self.upgrade_pending_update_time = i
209 self.upgrade_pending_update_time = 0
215 log.debug("Saving user settings...")
217 self.beginGroup("settings")
218 self.setValue("systray_visible", QVariant(self.systray_visible))
219 self.setValue("systray_messages", QVariant(self.systray_messages))
222 self.beginGroup("last_used")
223 self.setValue("device_uri", QVariant(self.last_used_device_uri))
224 self.setValue("printer_name", QVariant(self.last_used_printer))
225 self.setValue("working_dir", QVariant(self.working_dir))
228 self.beginGroup("commands")
229 self.setValue("scan", QVariant(self.cmd_scan))
232 self.beginGroup("refresh")
233 self.setValue("rate", QVariant(self.auto_refresh_rate))
234 self.setValue("enable", QVariant(self.auto_refresh))
235 self.setValue("type", QVariant(self.auto_refresh_type))
238 self.beginGroup("polling")
239 self.setValue("enable", QVariant(self.polling))
240 self.setValue("interval", QVariant(self.polling_interval))
241 self.setValue("device_list", QVariant(u','.join(self.polling_device_list)))
244 self.beginGroup("fax")
245 self.setValue("voice_phone", QVariant(self.voice_phone))
246 self.setValue("email_address", QVariant(self.email_address))
249 self.beginGroup("upgrade")
250 self.setValue("notify_upgrade", QVariant(self.upgrade_notify))
251 if self.upgrade_last_update_time <1:
252 self.upgrade_last_update_time = time.time() # <---Need to verify code once
254 self.setValue("last_upgraded_time", QVariant(self.upgrade_last_update_time))
255 self.setValue("pending_upgrade_time", QVariant(self.upgrade_pending_update_time))
263 log.debug("FAB command: %s" % self.cmd_fab)
264 log.debug("Scan command: %s" % self.cmd_scan)
265 log.debug("Auto refresh: %s" % self.auto_refresh)
266 log.debug("Auto refresh rate: %s" % self.auto_refresh_rate)
267 log.debug("Auto refresh type: %s" % self.auto_refresh_type)
268 log.debug("Systray visible: %d" % self.systray_visible)
269 log.debug("Systray messages: %d" % self.systray_messages)
270 log.debug("Last used device URI: %s" % self.last_used_device_uri)
271 log.debug("Last used printer: %s" % self.last_used_printer)
272 log.debug("Working directory: %s" % self.working_dir)
275 DEFAULT_TITLE = __translate("HP Device Manager")
278 def FailureUI(parent, error_text, title_text=None):
279 log.error(pat_html_remove.sub(' ', unicode(error_text)))
281 if title_text is None:
282 if parent is not None:
283 title_text = parent.windowTitle()
285 title_text = DEFAULT_TITLE
287 QMessageBox.critical(parent,
291 QMessageBox.NoButton,
292 QMessageBox.NoButton)
294 showFailureUi = FailureUI
297 def WarningUI(parent, warn_text, title_text=None):
298 log.warn(pat_html_remove.sub(' ', unicode(warn_text)))
300 if title_text is None:
301 if parent is not None:
302 title_text = parent.windowTitle()
304 title_text = DEFAULT_TITLE
307 QMessageBox.warning(parent,
311 QMessageBox.NoButton,
312 QMessageBox.NoButton)
314 showWarningUi = WarningUI
317 def SuccessUI(parent, text, title_text=None):
318 log.info(pat_html_remove.sub(' ', unicode(text)))
320 if title_text is None:
321 if parent is not None:
322 title_text = parent.windowTitle()
324 title_text = DEFAULT_TITLE
327 QMessageBox.information(parent,
331 QMessageBox.NoButton,
332 QMessageBox.NoButton)
334 showSuccessUi = SuccessUI
337 def CheckDeviceUI(parent, title_text=None):
338 text = __translate("<b>Unable to communicate with device or device is in an error state.</b><p>Please check device setup and try again.</p>")
339 return FailureUI(parent, text, title_text)
341 checkDeviceUi = CheckDeviceUI
344 class PrinterNameValidator(QValidator):
345 def __init__(self, parent=None):
346 QValidator.__init__(self, parent)
348 def validate(self, input, pos):
349 input = unicode(input)
352 return QValidator.Acceptable, pos
354 if input[pos-1] in cups.INVALID_PRINTER_NAME_CHARS:
355 return QValidator.Invalid, pos
357 # TODO: How to determine if unicode char is "printable" and acceptable
359 #elif input != utils.printable(input):
360 # return QValidator.Invalid, pos
362 return QValidator.Acceptable, pos
366 class PhoneNumValidator(QValidator):
367 def __init__(self, parent=None):
368 QValidator.__init__(self, parent)
370 def validate(self, input, pos):
371 input = unicode(input)
374 return QValidator.Acceptable, pos
376 if input[pos-1] not in u'0123456789-(+).,#* ':
377 return QValidator.Invalid, pos
379 return QValidator.Acceptable, pos
382 class AddressBookNameValidator(QValidator):
383 def __init__(self, db, parent=None):
384 QValidator.__init__(self, parent)
387 def validate(self, input, pos):
388 input = unicode(input)
391 return QValidator.Acceptable, pos
393 if input in self.db.get_all_names():
394 return QValidator.Invalid, pos
396 if input[pos-1] in u'''|\\/"''': # | is the drag 'n drop separator
397 return QValidator.Invalid, pos
399 return QValidator.Acceptable, pos
405 "application/pdf" : (__translate("PDF Document"), '.pdf'),
406 "application/postscript" : (__translate("Postscript Document"), '.ps'),
407 "application/vnd.hp-HPGL" : (__translate("HP Graphics Language File"), '.hgl, .hpg, .plt, .prn'),
408 "application/x-cshell" : (__translate("C Shell Script"), '.csh, .sh'),
409 "application/x-csource" : (__translate("C Source Code"), '.c'),
410 "text/cpp": (__translate("C/C++ Source Code"), '.c, .cpp, .cxx'),
411 "application/x-perl" : (__translate("Perl Script"), '.pl'),
412 "application/x-python" : (__translate("Python Program"), '.py'),
413 "application/x-shell" : (__translate("Shell Script"), '.sh'),
414 "application/x-sh" : (__translate("Shell Script"), '.sh'),
415 "text/plain" : (__translate("Plain Text"), '.txt, .log'),
416 "text/html" : (__translate("HTML Dcoument"), '.htm, .html'),
417 "image/gif" : (__translate("GIF Image"), '.gif'),
418 "image/png" : (__translate("PNG Image"), '.png'),
419 "image/jpeg" : (__translate("JPEG Image"), '.jpg, .jpeg'),
420 "image/tiff" : (__translate("TIFF Image"), '.tif, .tiff'),
421 "image/x-bitmap" : (__translate("Bitmap (BMP) Image"), '.bmp'),
422 "image/x-bmp" : (__translate("Bitmap (BMP) Image"), '.bmp'),
423 "image/x-photocd" : (__translate("Photo CD Image"), '.pcd'),
424 "image/x-portable-anymap" : (__translate("Portable Image (PNM)"), '.pnm'),
425 "image/x-portable-bitmap" : (__translate("Portable B&W Image (PBM)"), '.pbm'),
426 "image/x-portable-graymap" : (__translate("Portable Grayscale Image (PGM)"), '.pgm'),
427 "image/x-portable-pixmap" : (__translate("Portable Color Image (PPM)"), '.ppm'),
428 "image/x-sgi-rgb" : (__translate("SGI RGB"), '.rgb'),
429 "image/x-xbitmap" : (__translate("X11 Bitmap (XBM)"), '.xbm'),
430 "image/x-xpixmap" : (__translate("X11 Pixmap (XPM)"), '.xpm'),
431 "image/x-sun-raster" : (__translate("Sun Raster Format"), '.ras'),
432 "application/hplip-fax" : (__translate("HPLIP Fax File"), '.g3, .g4'),
435 # pixmaps for status list(s) (inkjet, laserjet)
438 def getStatusListIcon(error_state):
440 if status_icons is None:
442 ERROR_STATE_CLEAR : (load_pixmap('idle', '16x16'), load_pixmap('idle', '16x16')),
443 ERROR_STATE_BUSY : (load_pixmap('busy', '16x16'), load_pixmap('busy', '16x16')),
444 ERROR_STATE_ERROR : (load_pixmap('error', '16x16'), load_pixmap('error', '16x16')),
445 ERROR_STATE_LOW_SUPPLIES : (load_pixmap('inkdrop', '16x16'), load_pixmap('toner', '16x16')),
446 ERROR_STATE_OK : (load_pixmap('ok', '16x16'), load_pixmap('ok', '16x16')),
447 ERROR_STATE_WARNING : (load_pixmap('warning', '16x16'), load_pixmap('warning', '16x16')),
448 ERROR_STATE_LOW_PAPER: (load_pixmap('paper', '16x16'), load_pixmap('paper', '16x16')),
449 ERROR_STATE_PRINTING : (load_pixmap("print", '16x16'), load_pixmap("print", '16x16')),
450 ERROR_STATE_SCANNING : (load_pixmap("scan", '16x16'), load_pixmap("scan", '16x16')),
451 ERROR_STATE_PHOTOCARD : (load_pixmap("pcard", '16x16'), load_pixmap("pcard", '16x16')),
452 ERROR_STATE_FAXING : (load_pixmap("fax", '16x16'), load_pixmap("fax", '16x16')),
453 ERROR_STATE_COPYING : (load_pixmap("makecopies", '16x16'), load_pixmap("makecopies", '16x16')),
456 return status_icons.get(error_state, status_icons[ERROR_STATE_CLEAR])
458 # pixmaps for device icons (inkjet, laserjet)
461 def getStatusOverlayIcon(error_state):
463 if overlay_icons is None:
465 ERROR_STATE_CLEAR : (None, None),
466 ERROR_STATE_BUSY : (load_pixmap('busy', '16x16'), load_pixmap('busy', '16x16')),
467 ERROR_STATE_ERROR : (load_pixmap('error', '16x16'), load_pixmap('error', '16x16')),
468 ERROR_STATE_LOW_SUPPLIES : (load_pixmap('inkdrop', '16x16'), load_pixmap('toner', '16x16')),
469 ERROR_STATE_OK : (load_pixmap('ok', '16x16'), load_pixmap('ok', '16x16')),
470 ERROR_STATE_WARNING : (load_pixmap('warning', '16x16'), load_pixmap('warning', '16x16')),
471 ERROR_STATE_LOW_PAPER: (load_pixmap('paper', '16x16'), load_pixmap('paper', '16x16')),
472 ERROR_STATE_PRINTING : (load_pixmap('busy', '16x16'), load_pixmap('busy', '16x16')),
473 ERROR_STATE_SCANNING : (load_pixmap('busy', '16x16'), load_pixmap('busy', '16x16')),
474 ERROR_STATE_PHOTOCARD : (load_pixmap('busy', '16x16'), load_pixmap('busy', '16x16')),
475 ERROR_STATE_FAXING : (load_pixmap('busy', '16x16'), load_pixmap('busy', '16x16')),
476 ERROR_STATE_COPYING : (load_pixmap('busy', '16x16'), load_pixmap('busy', '16x16')),
477 ERROR_STATE_REFRESHING : (load_pixmap('refresh1', '16x16'), load_pixmap('refresh1', '16x16')),
480 return overlay_icons.get(error_state, overlay_icons[ERROR_STATE_CLEAR])
484 1 : __translate("one"),
485 2 : __translate("two"),
486 3 : __translate("three"),
487 4 : __translate("four"),
488 5 : __translate("five"),
489 6 : __translate("six"),
490 7 : __translate("seven"),
491 8 : __translate("eight"),
492 9 : __translate("nine"),
493 10 : __translate("ten"),
494 11 : __translate("eleven"),
495 12 : __translate("twelve")
499 "year" : (__translate("year"), __translate("years")),
500 "month" : (__translate("month"), __translate("months")),
501 "week" : (__translate("week"), __translate("weeks")),
502 "day" : (__translate("day"), __translate("days")),
503 "hour" : (__translate("hour"), __translate("hours")),
504 "minute" : (__translate("minute"), __translate("minutes")),
505 "second" : (__translate("second"), __translate("seconds")),
509 def getTimeDeltaDesc(past):
511 t1.setTime_t(int(past))
512 t2 = QDateTime.currentDateTime()
513 delta = t1.secsTo(t2)
514 return __translate("(%1 ago)").arg(stringify(delta))
517 # "Nicely readable timedelta"
518 # Credit: Bjorn Lindqvist
519 # ASPN Python Recipe 498062
520 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/498062
521 # Note: Modified from recipe
522 def getSecondsInUnits(seconds):
523 unit_limits = [("year", 31536000),
530 for unit_name, limit in unit_limits:
532 amount = int(round(float(seconds) / limit))
533 return amount, unit_name
535 return seconds, "second"
538 def stringify(seconds):
539 amount, unit_name = getSecondsInUnits(seconds)
542 i18n_amount = NUM_REPRS[amount]
544 i18n_amount = unicode(amount)
547 i18n_unit = UNIT_NAMES[unit_name][0]
549 i18n_unit = UNIT_NAMES[unit_name][1]
551 return QString("%1 %2").arg(i18n_amount).arg(i18n_unit)