1 # -*- coding: utf-8 -*-
3 # (c) Copyright 2003-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
32 def enter_yes_no(question, default_value='y', choice_prompt=None):
33 if type(default_value) == type(""):
34 if default_value == 'y':
39 #assert default_value in [True, False]
41 if choice_prompt is None:
43 question += " (y=yes*, n=no, q=quit) ? "
45 question += " (y=yes, n=no*, q=quit) ? "
47 question += choice_prompt
51 user_input = raw_input(log.bold(question)).lower().strip()
56 return True, default_value
64 if user_input in ('q', 'c'): # q -> quit, c -> cancel
65 return False, default_value
67 log.error("Please press <enter> or enter 'y', 'n', or 'q'.")
70 def enter_range(question, min_value, max_value, default_value=None):
73 user_input = raw_input(log.bold(question)).lower().strip()
78 if default_value is not None:
79 return True, default_value
82 return False, default_value
85 value_int = int(user_input)
87 log.error('Please enter a number between %d and %d, or "q" to quit.' %
88 (min_value, max_value))
91 if value_int < min_value or value_int > max_value:
92 log.error('Please enter a number between %d and %d, or "q" to quit.' %
93 (min_value, max_value))
96 return True, value_int
99 def enter_choice(question, choices, default_value=None):
100 if 'q' not in choices:
105 user_input = raw_input(log.bold(question)).lower().strip()
110 if (not user_input and default_value) or user_input == default_value:
111 if default_value == 'q':
112 return False, default_value
114 return True, default_value
117 if user_input == 'q':
118 return False, user_input
120 if user_input in choices:
121 return True, user_input
123 log.error("Please enter %s or press <enter> for the default of '%s'." %
124 (', '.join(["'%s'" % x for x in choices]), default_value))
130 log.info(log.bold(text))
131 log.info(log.bold("-"*len(text)))
138 log.info("| "+text+" |")
143 def load_paper_prompt():
144 return continue_prompt("A page will be printed.\nPlease load plain paper into the printer.")
147 def load_scanner_for_align_prompt():
148 return continue_prompt("Load the alignment page on the scanner bed and push the 'Scan' or 'Enter' button on the printer to complete the alignment.")
150 def load_photo_paper_prompt():
151 return continue_prompt("A page will be printed.\nPlease load HP Advanced Photo Paper - Glossy into the printer.")
154 def continue_prompt(prompt=''):
157 x = raw_input(log.bold(prompt + " Press <enter> to continue or 'q' to quit: ")).lower().strip()
167 log.error("Please press <enter> or enter 'q' to quit.")
170 def enter_regex(regex, prompt, pattern, default_value=None):
171 re_obj = re.compile(regex)
174 x = raw_input(log.bold(prompt))
178 if not x and default_value is not None:
179 return default_value, x
182 return False, default_value
184 match = re_obj.search(x)
187 log.error("Incorrect input. Please enter correct input.")
195 import commands # TODO: Replace with subprocess (commands is deprecated in Python 3.0)
196 ln1 = commands.getoutput('stty -a').splitlines()[0]
197 vals = {'rows':None, 'columns':None}
198 for ph in ln1.split(';'):
203 return int(vals['rows']), int(vals['columns'])
208 class ProgressMeter(object):
209 def __init__(self, prompt="Progress:"):
213 self.spinner = "\|/-\|/-*"
215 self.max_size = ttysize()[1] - len(prompt) - 25
218 def update(self, progress, msg=''): # progress in %
219 self.progress = progress
221 x = self.progress * self.max_size / 100
222 if x > self.max_size: x = self.max_size
224 if self.progress >= 100:
228 sys.stdout.write("\b" * self.prev_length)
230 y = "%s [%s%s%s] %d%% %s " % \
231 (self.prompt, '*'*(x-1), self.spinner[self.spinner_pos],
232 ' '*(self.max_size-x), self.progress, msg)
237 self.prev_length = len(y)
238 self.spinner_pos = (self.spinner_pos + 1) % 8
242 class Formatter(object):
243 def __init__(self, margin=2, header=None, min_widths=None, max_widths=None):
244 self.margin = margin # int
245 self.header = header # tuple of strings
246 self.rows = [] # list of tuples
247 self.max_widths = max_widths # tuple of ints
248 self.min_widths = min_widths # tuple of ints
251 def add(self, row_data): # tuple of strings
252 self.rows.append(row_data)
257 num_cols = len(self.rows[0])
259 if len(r) != num_cols:
260 log.error("Invalid number of items in row: %s" % r)
263 if len(self.header) != num_cols:
264 log.error("Invalid number of items in header.")
267 for c in self.header:
268 header_parts = c.split(' ')
270 for x in header_parts:
271 max_width = max(max_width, len(x))
273 min_calc_widths.append(max_width)
276 for x, c in enumerate(self.header):
279 max_width = max(max_width, len(r[x]))
281 max_calc_widths.append(max_width)
283 max_screen_width = None
285 if self.max_widths is None:
286 max_screen_width = ttysize()[1]
287 def_max = 8*(max_screen_width/num_cols)/10
289 for c in self.header:
290 self.max_widths.append(def_max)
292 if len(self.max_widths) != num_cols:
293 log.error("Invalid number of items in max col widths.")
295 if self.min_widths is None:
296 if max_screen_width is None:
297 max_screen_width = ttysize()[1]
298 def_min = 4*(max_screen_width/num_cols)/10
300 for c in self.header:
301 self.min_widths.append(def_min)
303 if len(self.min_widths) != num_cols:
304 log.error("Invalid number of items in min col widths.")
308 for m1, m2, m3, m4 in zip(self.min_widths, min_calc_widths,
309 self.max_widths, max_calc_widths):
310 col_width = max(max(m1, m2), min(m3, m4))
311 col_widths.append(col_width)
312 formats.append({'width': col_width, 'margin': self.margin})
314 formatter = utils.TextFormatter(tuple(formats))
316 log.info(formatter.compose(self.header))
322 log.info(formatter.compose(tuple(sep)))
325 log.info(formatter.compose(r))
328 log.error("No data rows")
337 def align(line, width=70, alignment=ALIGN_LEFT):
338 space = width - len(line)
340 if alignment == ALIGN_CENTER:
341 return ' '*(space/2) + line + \
342 ' '*(space/2 + space%2)
344 elif alignment == ALIGN_RIGHT:
345 return ' '*space + line
348 return line + ' '*space
351 def format_paragraph(paragraph, width=None, alignment=ALIGN_LEFT):
356 words = paragraph.split()
358 current, words = words[0], words[1:]
363 increment = 1 + len(word)
365 if len(current) + increment > width:
366 result.append(align(current, width, alignment))
370 current = current+" "+word
372 result.append(align(current, width, alignment))
376 def printer_table(printers):
377 header("SELECT PRINTER")
378 last_used_printer_name = user_conf.get('last_used', 'printer_name')
381 table = Formatter(header=('Num', 'CUPS Printer'),
382 max_widths=(8, 100), min_widths=(8, 20))
385 for x, _ in enumerate(printers):
386 if last_used_printer_name == printers[x]:
387 table.add((str(x) + '*', printers[x]))
390 table.add((str(x), printers[x]))
394 if default_index is not None:
395 ok, i = enter_range("\nEnter number 0...%d for printer (q=quit, <enter>=default: *%d) ?" % (x, default_index),
398 ok, i = enter_range("\nEnter number 0...%d for printer (q=quit) ?" % x, 0, x)
406 def device_table(devices, scan_flag=False):
407 header("SELECT DEVICE")
408 last_used_device_uri = user_conf.get('last_used', 'device_uri')
412 table = Formatter(header=('Num', 'Scan device URI'),
413 max_widths=(8, 100), min_widths=(8, 12))
415 table = Formatter(header=('Num', 'Device URI', 'CUPS Printer(s)'),
416 max_widths=(8, 100, 100), min_widths=(8, 12, 12))
420 for x, d in enumerate(devices):
422 if last_used_device_uri == d:
424 table.add((str(x) + "*", d))
426 table.add((str(x) + "*", d, ','.join(devices[d])))
430 table.add((str(x), d))
432 table.add((str(x), d, ','.join(devices[d])))
436 if default_index is not None:
437 ok, i = enter_range("\nEnter number 0...%d for device (q=quit, <enter>=default: %d*) ?" % (x, default_index),
440 ok, i = enter_range("\nEnter number 0...%d for device (q=quit) ?" % x, 0, x)
443 ret = device_index[i]
448 def connection_table():
449 ret, ios, x = None, {0: ('usb', "Universal Serial Bus (USB)") }, 1
452 ios[x] = ('net', "Network/Ethernet/Wireless (direct connection or JetDirect)")
456 ios[x] = ('par', "Parallel Port (LPT:)")
460 header("SELECT CONNECTION (I/O) TYPE")
462 table = Formatter(header=('Num', 'Connection Type', 'Description'),
463 max_widths=(8, 20, 80), min_widths=(8, 10, 40))
465 for x, data in ios.items():
467 table.add((str(x) + "*", data[0], data[1]))
469 table.add((str(x), data[0], data[1]))
473 ok, val = enter_range("\nEnter number 0...%d for connection type (q=quit, enter=usb*) ? " % x,