#!/usr/bin/python from . import proto_fm_screen_area from . import wgwidget as widget from . import wgbutton as button import weakref from . import npyspmfuncs as pmfuncs #import Menu import curses import _curses from . import npysGlobalOptions from . import wgwidget_proto from . import fm_form_edit_loop as form_edit_loop from . import util_viewhelp from . import npysGlobalOptions as GlobalOptions from .eveventhandler import EventHandler from .globals import DISABLE_RESIZE_SYSTEM class _FormBase(proto_fm_screen_area.ScreenArea, widget.InputHandler, wgwidget_proto._LinePrinter, EventHandler): BLANK_COLUMNS_RIGHT= 2 BLANK_LINES_BASE = 2 OK_BUTTON_TEXT = 'OK' OK_BUTTON_BR_OFFSET = (2,6) OKBUTTON_TYPE = button.MiniButton DEFAULT_X_OFFSET = 2 PRESERVE_SELECTED_WIDGET_DEFAULT = False # Preserve cursor location between displays? FRAMED = True ALLOW_RESIZE = True FIX_MINIMUM_SIZE_WHEN_CREATED = True WRAP_HELP = True def __init__(self, name=None, parentApp=None, framed=None, help=None, color='FORMDEFAULT', widget_list=None, cycle_widgets=False, *args, **keywords): super(_FormBase, self).__init__(*args, **keywords) self.initialize_event_handling() self.preserve_selected_widget = self.__class__.PRESERVE_SELECTED_WIDGET_DEFAULT if parentApp: try: self.parentApp = weakref.proxy(parentApp) except: self.parentApp = parentApp try: self.keypress_timeout = self.parentApp.keypress_timeout_default except AttributeError: pass if framed is None: self.framed = self.__class__.FRAMED else: self.framed = framed self.name=name self.editing = False ## OLD MENU CODE REMOVED self.__menus = [] self._clear_all_widgets() self.help = help self.color = color self.cycle_widgets = cycle_widgets self.set_up_handlers() self.set_up_exit_condition_handlers() if hasattr(self, 'initialWidgets'): self.create_widgets_from_list(self.__class__.initialWidgets) if widget_list: self.create_widgets_from_list(widget_list) self.create() if self.FIX_MINIMUM_SIZE_WHEN_CREATED: self.min_l = self.lines self.min_c = self.columns def resize(self): pass def _clear_all_widgets(self, ): self._widgets__ = [] self._widgets_by_id = {} self._next_w_id = 0 self.nextrely = self.DEFAULT_NEXTRELY self.nextrelx = self.DEFAULT_X_OFFSET self.editw = 0 # Index of widget to edit. def create_widgets_from_list(self, widget_list): # This code is currently experimental, and the API may change in future releases # (npyscreen.TextBox, {'rely': 2, 'relx': 7, 'editable': False}) for line in widget_list: w_type = line[0] keywords = line[1] self.add_widget(w_type, **keywords) def set_value(self, value): self.value = value for _w in self._widgets__: if hasattr(_w, 'when_parent_changes_value'): _w.when_parent_changes_value() def _resize(self, *args): global DISABLE_RESIZE_SYSTEM if DISABLE_RESIZE_SYSTEM: return False if not self.ALLOW_RESIZE: return False if hasattr(self, 'parentApp'): self.parentApp.resize() self._create_screen() self.resize() for w in self._widgets__: w._resize() self.DISPLAY() def create(self): """Programmers should over-ride this in derived classes, creating widgets here""" pass def set_up_handlers(self): self.complex_handlers = [] self.handlers = { curses.KEY_F1: self.h_display_help, "KEY_F(1)": self.h_display_help, "^O": self.h_display_help, "^L": self.h_display, curses.KEY_RESIZE: self._resize, } def set_up_exit_condition_handlers(self): # What happens when widgets exit? # each widget will set it's how_exited value: this should # be used to look up the following table. self.how_exited_handers = { widget.EXITED_DOWN: self.find_next_editable, widget.EXITED_RIGHT: self.find_next_editable, widget.EXITED_UP: self.find_previous_editable, widget.EXITED_LEFT: self.find_previous_editable, widget.EXITED_ESCAPE: self.do_nothing, True: self.find_next_editable, # A default value widget.EXITED_MOUSE: self.get_and_use_mouse_event, False: self.do_nothing, None: self.do_nothing, } def handle_exiting_widgets(self, condition): self.how_exited_handers[condition]() def do_nothing(self, *args, **keywords): pass def exit_editing(self, *args, **keywords): self.editing = False try: self._widgets__[self.editw].entry_widget.editing = False except: pass try: self._widgets__[self.editw].editing = False except: pass def adjust_widgets(self): """This method can be overloaded by derived classes. It is called when editing any widget, as opposed to the while_editing() method, which may only be called when moving between widgets. Since it is called for every keypress, and perhaps more, be careful when selecting what should be done here.""" def while_editing(self, *args, **keywords): """This function gets called during the edit loop, on each iteration of the loop. It does nothing: it is here to make customising the loop as easy as overriding this function. A proxy to the currently selected widget is passed to the function.""" def on_screen(self): # is the widget in editw on sreen at the moment? # if not, alter screen so that it is. w = weakref.proxy(self._widgets__[self.editw]) max_y, max_x = self._max_physical() w_my, w_mx = w.calculate_area_needed() # always try to show the top of the screen. self.show_from_y = 0 self.show_from_x = 0 while w.rely + w_my -1 > self.show_from_y + max_y: self.show_from_y += 1 while w.rely < self.show_from_y: self.show_from_y -= 1 while w.relx + w_mx -1 > self.show_from_x + max_x: self.show_from_x += 1 while w.relx < self.show_from_x: self.show_from_x -= 1 def h_display_help(self, input): if self.help == None: return if self.name: help_name="%s Help" %(self.name) else: help_name=None curses.flushinp() util_viewhelp.view_help(self.help, title=help_name, autowrap=self.WRAP_HELP) #select.ViewText(self.help, name=help_name) self.display() return True def DISPLAY(self): self.curses_pad.redrawwin() self.erase() self.display() self.display(clear=False) if self.editing and self.editw is not None: self._widgets__[self.editw].display() def h_display(self, input): self._resize() self.DISPLAY() def safe_get_mouse_event(self): try: mouse_event = curses.getmouse() return mouse_event except _curses.error: return None def get_and_use_mouse_event(self): mouse_event = self.safe_get_mouse_event() if mouse_event: self.use_mouse_event(mouse_event) def use_mouse_event(self, mouse_event): wg = self.find_mouse_handler(mouse_event) if wg: self.set_editing(wg) if hasattr(wg, 'handle_mouse_event'): wg.handle_mouse_event(mouse_event) else: curses.beep() def find_mouse_handler(self, mouse_event): #mouse_id, x, y, z, bstate = mouse_event for wd in self._widgets__: try: if wd.intersted_in_mouse_event(mouse_event) == True: return wd except AttributeError: pass return None def set_editing(self, wdg): try: self.editw = self._widgets__.index(wdg) except ValueError: pass def find_next_editable(self, *args): if not self.cycle_widgets: r = list(range(self.editw+1, len(self._widgets__))) else: r = list(range(self.editw+1, len(self._widgets__))) + list(range(0, self.editw)) for n in r: if self._widgets__[n].editable and not self._widgets__[n].hidden: self.editw = n break self.display() def find_previous_editable(self, *args): if not self.editw == 0: # remember that xrange does not return the 'last' value, # so go to -1, not 0! (fence post error in reverse) for n in range(self.editw-1, -1, -1 ): if self._widgets__[n].editable and not self._widgets__[n].hidden: self.editw = n break #def widget_useable_space(self, rely=0, relx=0): # #Slightly misreports space available. # mxy, mxx = self.lines-1, self.columns-1 # return (mxy-1-rely, mxx-1-relx) def center_on_display(self): my, mx = self._max_physical() if self.lines < my: self.show_aty = (my - self.lines) // 2 else: self.show_aty = 0 if self.columns < mx: self.show_atx = (mx - self.columns) // 2 else: self.show_atx = 0 def display(self, clear=False): #APPLICATION_THEME_MANAGER.setTheme(self) if curses.has_colors() and not npysGlobalOptions.DISABLE_ALL_COLORS: self.curses_pad.attrset(0) color_attribute = self.theme_manager.findPair(self, self.color) self.curses_pad.bkgdset(' ', color_attribute) self.curses_pad.attron(color_attribute) self.curses_pad.erase() self.draw_form() for w in [wg for wg in self._widgets__ if wg.hidden]: w.clear() for w in [wg for wg in self._widgets__ if not wg.hidden]: w.update(clear=clear) self.refresh() def draw_title_and_help(self): try: if self.name: _title = self.name[:(self.columns-4)] _title = ' ' + str(_title) + ' ' #self.curses_pad.addstr(0,1, ' '+str(_title)+' ') if isinstance(_title, bytes): _title = _title.decode('utf-8', 'replace') self.add_line(0,1, _title, self.make_attributes_list(_title, curses.A_NORMAL), self.columns-4 ) except: pass if self.help and self.editing: try: help_advert = " Help: F1 or ^O " if isinstance(help_advert, bytes): help_advert = help_advert.decode('utf-8', 'replace') self.add_line( 0, self.curses_pad.getmaxyx()[1]-len(help_advert)-2, help_advert, self.make_attributes_list(help_advert, curses.A_NORMAL), len(help_advert) ) except: pass def draw_form(self): if self.framed: if curses.has_colors() and not GlobalOptions.DISABLE_ALL_COLORS: self.curses_pad.attrset(0) self.curses_pad.bkgdset(' ', curses.A_NORMAL | self.theme_manager.findPair(self, self.color)) self.curses_pad.border() self.draw_title_and_help() def add_widget(self, widgetClass, w_id=None, max_height=None, rely=None, relx=None, *args, **keywords): """Add a widget to the form. The form will do its best to decide on placing, unless you override it. The form of this function is add_widget(WidgetClass, ....) with any arguments or keywords supplied to the widget. The wigdet will be added to self._widgets__ It is safe to use the return value of this function to keep hold of the widget, since that is a weak reference proxy, but it is not safe to keep hold of self._widgets__""" if rely is None: rely = self.nextrely if relx is None: relx = self.nextrelx if max_height is False: max_height = self.curses_pad.getmaxyx()[0] - rely - 1 _w = widgetClass(self, rely=rely, relx=relx, max_height=max_height, *args, **keywords) self.nextrely = _w.height + _w.rely self._widgets__.append(_w) w_proxy = weakref.proxy(_w) if not w_id: w_id = self._next_w_id self._next_w_id += 1 self._widgets_by_id[w_id] = w_proxy return w_proxy def get_widget(self, w_id): return self._widgets_by_id[w_id] add = add_widget class FormBaseNew(form_edit_loop.FormNewEditLoop, _FormBase): # use the new-style edit loop. pass class Form(form_edit_loop.FormDefaultEditLoop, _FormBase, ): #use the old-style edit loop pass def resize(self): super(Form, self).resize() self.move_ok_button() class FormBaseNewExpanded(form_edit_loop.FormNewEditLoop, _FormBase): BLANK_LINES_BASE = 1 OK_BUTTON_BR_OFFSET = (1,6) # use the new-style edit loop. pass class FormExpanded(form_edit_loop.FormDefaultEditLoop, _FormBase, ): BLANK_LINES_BASE = 1 OK_BUTTON_BR_OFFSET = (1,6) #use the old-style edit loop pass class TitleForm(Form): """A form without a box, just a title line""" BLANK_LINES_BASE = 1 DEFAULT_X_OFFSET = 1 DEFAULT_NEXTRELY = 1 BLANK_COLUMNS_RIGHT = 0 OK_BUTTON_BR_OFFSET = (1,6) #OKBUTTON_TYPE = button.MiniButton #DEFAULT_X_OFFSET = 1 def draw_form(self): MAXY, MAXX = self.curses_pad.getmaxyx() self.curses_pad.hline(0, 0, curses.ACS_HLINE, MAXX) self.draw_title_and_help() class TitleFooterForm(TitleForm): BLANK_LINES_BASE=1 def draw_form(self): MAXY, MAXX = self.curses_pad.getmaxyx() if self.editing: self.curses_pad.hline(MAXY-1, 0, curses.ACS_HLINE, MAXX - self.__class__.OK_BUTTON_BR_OFFSET[1] - 1) else: self.curses_pad.hline(MAXY-1, 0, curses.ACS_HLINE, MAXX-1) super(TitleFooterForm, self).draw_form() class SplitForm(Form): MOVE_LINE_ON_RESIZE = False """Just the same as the Title Form, but with a horizontal line""" def __init__(self, draw_line_at=None, *args, **keywords): super(SplitForm, self).__init__(*args, **keywords) if not hasattr(self, 'draw_line_at'): if draw_line_at != None: self.draw_line_at = draw_line_at else: self.draw_line_at = self.get_half_way() def draw_form(self,): MAXY, MAXX = self.curses_pad.getmaxyx() super(SplitForm, self).draw_form() self.curses_pad.hline(self.draw_line_at, 1, curses.ACS_HLINE, MAXX-2) def get_half_way(self): return self.curses_pad.getmaxyx()[0] // 2 def resize(self): super(SplitForm, self).resize() if self.MOVE_LINE_ON_RESIZE: self.draw_line_at = self.get_half_way() def blank_terminal(): F = _FormBase(framed=False) F.erase() F.display()