# -*- coding: utf-8 -*- # # (c) Copyright 2003-2009 Hewlett-Packard Development Company, L.P. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Author: Don Welch # # Std Lib import sys import re # Local from g import * import pexpect import utils def enter_yes_no(question, default_value='y', choice_prompt=None): if type(default_value) == type(""): if default_value == 'y': default_value = True else: default_value = False #assert default_value in [True, False] if choice_prompt is None: if default_value: question += " (y=yes*, n=no, q=quit) ? " else: question += " (y=yes, n=no*, q=quit) ? " else: question += choice_prompt while True: try: user_input = raw_input(log.bold(question)).lower().strip() except EOFError: continue if not user_input: return True, default_value if user_input == 'n': return True, False if user_input == 'y': return True, True if user_input in ('q', 'c'): # q -> quit, c -> cancel return False, default_value log.error("Please press or enter 'y', 'n', or 'q'.") def enter_range(question, min_value, max_value, default_value=None): while True: try: user_input = raw_input(log.bold(question)).lower().strip() except EOFError: continue if not user_input: if default_value is not None: return True, default_value if user_input == 'q': return False, default_value try: value_int = int(user_input) except ValueError: log.error('Please enter a number between %d and %d, or "q" to quit.' % (min_value, max_value)) continue if value_int < min_value or value_int > max_value: log.error('Please enter a number between %d and %d, or "q" to quit.' % (min_value, max_value)) continue return True, value_int def enter_choice(question, choices, default_value=None): if 'q' not in choices: choices.append('q') while True: try: user_input = raw_input(log.bold(question)).lower().strip() except EOFError: continue if (not user_input and default_value) or user_input == default_value: if default_value == 'q': return False, default_value else: return True, default_value #print user_input if user_input == 'q': return False, user_input if user_input in choices: return True, user_input log.error("Please enter %s or press for the default of '%s'." % (', '.join(["'%s'" % x for x in choices]), default_value)) def title(text): log.info("") log.info("") log.info(log.bold(text)) log.info(log.bold("-"*len(text))) def header(text): c = len(text) log.info("") log.info("-"*(c+4)) log.info("| "+text+" |") log.info("-"*(c+4)) log.info("") def load_paper_prompt(): return continue_prompt("A page will be printed.\nPlease load plain paper into the printer.") def load_scanner_for_align_prompt(): 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.") def load_photo_paper_prompt(): return continue_prompt("A page will be printed.\nPlease load HP Advanced Photo Paper - Glossy into the printer.") def continue_prompt(prompt=''): while True: try: x = raw_input(log.bold(prompt + " Press to continue or 'q' to quit: ")).lower().strip() except EOFError: continue if not x: return True elif x == 'q': return False log.error("Please press or enter 'q' to quit.") def enter_regex(regex, prompt, pattern, default_value=None): re_obj = re.compile(regex) while True: try: x = raw_input(log.bold(prompt)) except EOFError: continue if not x and default_value is not None: return default_value, x elif x == 'q': return False, default_value match = re_obj.search(x) if not match: log.error("Incorrect input. Please enter correct input.") continue return True, x def ttysize(): try: import commands # TODO: Replace with subprocess (commands is deprecated in Python 3.0) ln1 = commands.getoutput('stty -a').splitlines()[0] vals = {'rows':None, 'columns':None} for ph in ln1.split(';'): x = ph.split() if len(x) == 2: vals[x[0]] = x[1] vals[x[1]] = x[0] return int(vals['rows']), int(vals['columns']) except TypeError: return 40, 64 class ProgressMeter(object): def __init__(self, prompt="Progress:"): self.progress = 0 self.prompt = prompt self.prev_length = 0 self.spinner = "\|/-\|/-*" self.spinner_pos = 0 self.max_size = ttysize()[1] - len(prompt) - 25 self.update(0) def update(self, progress, msg=''): # progress in % self.progress = progress x = self.progress * self.max_size / 100 if x > self.max_size: x = self.max_size if self.progress >= 100: self.spinner_pos = 8 self.progress = 100 sys.stdout.write("\b" * self.prev_length) y = "%s [%s%s%s] %d%% %s " % \ (self.prompt, '*'*(x-1), self.spinner[self.spinner_pos], ' '*(self.max_size-x), self.progress, msg) sys.stdout.write(y) sys.stdout.flush() self.prev_length = len(y) self.spinner_pos = (self.spinner_pos + 1) % 8 class Formatter(object): def __init__(self, margin=2, header=None, min_widths=None, max_widths=None): self.margin = margin # int self.header = header # tuple of strings self.rows = [] # list of tuples self.max_widths = max_widths # tuple of ints self.min_widths = min_widths # tuple of ints def add(self, row_data): # tuple of strings self.rows.append(row_data) def output(self): if self.rows: num_cols = len(self.rows[0]) for r in self.rows: if len(r) != num_cols: log.error("Invalid number of items in row: %s" % r) return if len(self.header) != num_cols: log.error("Invalid number of items in header.") min_calc_widths = [] for c in self.header: header_parts = c.split(' ') max_width = 0 for x in header_parts: max_width = max(max_width, len(x)) min_calc_widths.append(max_width) max_calc_widths = [] for x, c in enumerate(self.header): max_width = 0 for r in self.rows: max_width = max(max_width, len(r[x])) max_calc_widths.append(max_width) max_screen_width = None if self.max_widths is None: max_screen_width = ttysize()[1] def_max = 8*(max_screen_width/num_cols)/10 self.max_widths = [] for c in self.header: self.max_widths.append(def_max) else: if len(self.max_widths) != num_cols: log.error("Invalid number of items in max col widths.") if self.min_widths is None: if max_screen_width is None: max_screen_width = ttysize()[1] def_min = 4*(max_screen_width/num_cols)/10 self.min_widths = [] for c in self.header: self.min_widths.append(def_min) else: if len(self.min_widths) != num_cols: log.error("Invalid number of items in min col widths.") col_widths = [] formats = [] for m1, m2, m3, m4 in zip(self.min_widths, min_calc_widths, self.max_widths, max_calc_widths): col_width = max(max(m1, m2), min(m3, m4)) col_widths.append(col_width) formats.append({'width': col_width, 'margin': self.margin}) formatter = utils.TextFormatter(tuple(formats)) log.info(formatter.compose(self.header)) sep = [] for c in col_widths: sep.append('-'*c) log.info(formatter.compose(tuple(sep))) for r in self.rows: log.info(formatter.compose(r)) else: log.error("No data rows") ALIGN_LEFT = 0 ALIGN_CENTER = 1 ALIGN_RIGHT = 2 def align(line, width=70, alignment=ALIGN_LEFT): space = width - len(line) if alignment == ALIGN_CENTER: return ' '*(space/2) + line + \ ' '*(space/2 + space%2) elif alignment == ALIGN_RIGHT: return ' '*space + line else: return line + ' '*space def format_paragraph(paragraph, width=None, alignment=ALIGN_LEFT): if width is None: width = ttysize()[1] result = [] words = paragraph.split() try: current, words = words[0], words[1:] except IndexError: return [paragraph] for word in words: increment = 1 + len(word) if len(current) + increment > width: result.append(align(current, width, alignment)) current = word else: current = current+" "+word result.append(align(current, width, alignment)) return result def printer_table(printers): header("SELECT PRINTER") last_used_printer_name = user_conf.get('last_used', 'printer_name') ret = None table = Formatter(header=('Num', 'CUPS Printer'), max_widths=(8, 100), min_widths=(8, 20)) default_index = None for x, _ in enumerate(printers): if last_used_printer_name == printers[x]: table.add((str(x) + '*', printers[x])) default_index = x else: table.add((str(x), printers[x])) table.output() if default_index is not None: ok, i = enter_range("\nEnter number 0...%d for printer (q=quit, =default: *%d) ?" % (x, default_index), 0, x, default_index) else: ok, i = enter_range("\nEnter number 0...%d for printer (q=quit) ?" % x, 0, x) if ok: ret = printers[i] return ret def device_table(devices, scan_flag=False): header("SELECT DEVICE") last_used_device_uri = user_conf.get('last_used', 'device_uri') ret = None if scan_flag: table = Formatter(header=('Num', 'Scan device URI'), max_widths=(8, 100), min_widths=(8, 12)) else: table = Formatter(header=('Num', 'Device URI', 'CUPS Printer(s)'), max_widths=(8, 100, 100), min_widths=(8, 12, 12)) default_index = None device_index = {} for x, d in enumerate(devices): device_index[x] = d if last_used_device_uri == d: if scan_flag: table.add((str(x) + "*", d)) else: table.add((str(x) + "*", d, ','.join(devices[d]))) default_index = x else: if scan_flag: table.add((str(x), d)) else: table.add((str(x), d, ','.join(devices[d]))) table.output() if default_index is not None: ok, i = enter_range("\nEnter number 0...%d for device (q=quit, =default: %d*) ?" % (x, default_index), 0, x, default_index) else: ok, i = enter_range("\nEnter number 0...%d for device (q=quit) ?" % x, 0, x) if ok: ret = device_index[i] return ret def connection_table(): ret, ios, x = None, {0: ('usb', "Universal Serial Bus (USB)") }, 1 if prop.net_build: ios[x] = ('net', "Network/Ethernet/Wireless (direct connection or JetDirect)") x += 1 if prop.par_build: ios[x] = ('par', "Parallel Port (LPT:)") x += 1 if len(ios) > 1: header("SELECT CONNECTION (I/O) TYPE") table = Formatter(header=('Num', 'Connection Type', 'Description'), max_widths=(8, 20, 80), min_widths=(8, 10, 40)) for x, data in ios.items(): if x == 0: table.add((str(x) + "*", data[0], data[1])) else: table.add((str(x), data[0], data[1])) table.output() ok, val = enter_range("\nEnter number 0...%d for connection type (q=quit, enter=usb*) ? " % x, 0, x, 0) if ok: ret = [ios[val][0]] else: ret = ['usb'] return ret