示例#1
0
 def _show(self):
     '''Create a scrollable region and fill it with the install log'''
     self.center_win.border_size = (0, 0)
     self.scroll_area = WindowArea(self.win_size_y, self.win_size_x, 0, 0,
                                   len(self.get_log_data()))
     log = ScrollWindow(self.scroll_area, window=self.center_win)
     log.add_paragraph(self.get_log_data(), 0, 2)
     self.center_win.activate_object(log)
示例#2
0
 def _show(self):
     '''Prepare a text summary from the install_profile and display it
     to the user in a ScrollWindow
     
     '''
     y_loc = 1
     y_loc += self.center_win.add_paragraph(SummaryScreen.PARAGRAPH, y_loc)
     
     y_loc += 1
     summary_text = self.build_summary()
     # Wrap the summary text, accounting for the INDENT (used below in
     # the call to add_paragraph)
     max_chars = self.win_size_x - SummaryScreen.INDENT - 1
     summary_text = convert_paragraph(summary_text, max_chars)
     area = WindowArea(x_loc=0, y_loc=y_loc,
                       scrollable_lines=(len(summary_text)+1))
     area.lines = self.win_size_y - y_loc
     area.columns = self.win_size_x
     scroll_region = ScrollWindow(area, window=self.center_win)
     scroll_region.add_paragraph(summary_text, start_x=SummaryScreen.INDENT)
     
     self.center_win.activate_object(scroll_region)
示例#3
0
class MainWindow(object):
    '''Represent initscr (the whole screen), and break it into a border,
    header, and central region. Map F# keystrokes to Actions
    
    '''
    def __init__(self, initscr, screen_list, theme=None, force_bw=False):
        '''Set the theme, and call reset to initialize the terminal to
        prepare for the first screen.
        
        '''

        if theme is not None:
            self.theme = theme
        else:
            self.theme = ColorTheme(force_bw=force_bw)
        self.screen_list = screen_list
        self.initscr = initscr
        self.default_cursor_pos = (initscr.getmaxyx()[0] - 1, 0)
        self.cursor_pos = self.default_cursor_pos
        self.footer = None
        self.header = None
        self._cur_header_text = None
        self.central_area = None
        self.popup_win = None
        self.error_line = None
        self._active_win = None
        self.continue_action = None
        self.back_action = None
        self.help_action = None
        self.quit_action = None
        self.actions = None
        self.reset()

    def redrawwin(self):
        '''Completely repaint the screen'''
        self.header.redrawwin()
        self.footer.redrawwin()
        self.error_line.redrawwin()
        self.central_area.redrawwin()
        if self._active_win is self.popup_win:
            self.popup_win.redrawwin()

    def do_update(self):
        '''Wrapper to curses.doupdate()'''
        curses.setsyx(*self.get_cursor_loc())
        curses.doupdate()

    def get_cursor_loc(self):
        '''Retrieve the current cursor position from the active UI
        element.
        
        '''
        cursor = self.central_area.get_cursor_loc()
        if cursor is None:
            cursor = self.cursor_pos
        return cursor

    def reset(self):
        '''Create the InnerWindows representing the header, footer/border,
        error line, and main central_area
        
        '''
        window_size = self.initscr.getmaxyx()
        win_size_y = window_size[0]
        win_size_x = window_size[1]
        footer_area = WindowArea(1, win_size_x, win_size_y - 1, 0)
        self.footer = InnerWindow(footer_area,
                                  color_theme=self.theme,
                                  color=self.theme.border)
        top = self.initscr.derwin(1, win_size_x, 0, 0)
        left = self.initscr.derwin(win_size_y - 2, 1, 1, 0)
        right = self.initscr.derwin(win_size_y - 2, 1, 1, win_size_x - 1)
        self.footer.more_windows = [top, left, right]
        self.footer.set_color(self.theme.border)
        header_area = WindowArea(1, win_size_x - 2, 1, 1)
        self.header = InnerWindow(header_area,
                                  color_theme=self.theme,
                                  color=self.theme.header)
        central_win_area = WindowArea(win_size_y - 4, win_size_x - 2, 2, 1)
        self.central_area = InnerWindow(central_win_area,
                                        border_size=(0, 2),
                                        color_theme=self.theme)
        self._active_win = self.central_area
        popup_win_area = WindowArea(central_win_area.lines - 10,
                                    central_win_area.columns - 20, 5, 10,
                                    central_win_area.lines - 10)
        self.popup_win = ScrollWindow(popup_win_area,
                                      window=self.central_area,
                                      color=self.theme.error_msg,
                                      highlight_color=self.theme.error_msg)
        error_area = WindowArea(1, win_size_x - 2, win_size_y - 2, 1)
        self.error_line = ErrorWindow(error_area, color_theme=self.theme)
        self.reset_actions()

    def reset_actions(self):
        '''Reset the actions to the defaults, clearing any custom actions
        registered by individual screens
        
        '''
        self.continue_action = Action(curses.KEY_F2, _("Continue"),
                                      self.screen_list.get_next)
        self.back_action = Action(curses.KEY_F3, _("Back"),
                                  self.screen_list.previous_screen)
        self.help_action = Action(curses.KEY_F6, _("Help"),
                                  self.screen_list.show_help)
        self.quit_action = Action(curses.KEY_F9, _("Quit"),
                                  self.screen_list.quit)
        self.set_default_actions()

    def clear(self):
        '''Clear all InnerWindows and reset_actions()'''
        self.header.clear()
        self.footer.clear()
        self.central_area.clear()
        self.error_line.clear_err()
        self.reset_actions()

    def set_header_text(self, header_text):
        '''Set the header_text'''
        text = center_columns(header_text, self.header.area.columns - 1)
        self.header.add_text(text)
        self._cur_header_text = text

    def set_default_actions(self):
        '''Clear the actions dictionary and add the default actions back
        into it
        
        '''
        self.actions = {}
        self.actions[self.continue_action.key] = self.continue_action
        self.actions[self.back_action.key] = self.back_action
        self.actions[self.help_action.key] = self.help_action
        self.actions[self.quit_action.key] = self.quit_action

    def show_actions(self):
        '''Read through the actions dictionary, displaying all the actions
        descriptive text along the footer (along with a prefix linked to
        its associated keystroke)
        
        '''
        self.footer.window.clear()
        if InnerWindow.USE_ESC:
            prefix = " Esc-"
        else:
            prefix = "  F"
        strings = []
        length = 0
        action_format = "%s%i_%s"
        for key in sorted(self.actions.keys()):
            key_num = key - curses.KEY_F0
            action_text = self.actions[key].text
            action_str = action_format % (prefix, key_num, action_text)
            strings.append(action_str)
        display_str = "".join(strings)
        max_len = self.footer.window.getmaxyx()[1]
        length = textwidth(display_str)
        if not InnerWindow.USE_ESC:
            length += (len(" Esc-") - len("  F")) * len(self.actions)
        if length > max_len:
            raise ValueError("Can't display footer actions - string too long")
        self.footer.window.addstr(display_str.encode(get_encoding()))
        self.footer.window.noutrefresh()

    def getch(self):
        '''Call down into central_area to get a keystroke, and, if necessary,
        update the footer to switch to using the Esc- prefixes
        
        '''
        input_key = self._active_win.getch()
        if input_key == InnerWindow.REPAINT_KEY:
            self.redrawwin()
            input_key = None
        if InnerWindow.UPDATE_FOOTER:
            InnerWindow.UPDATE_FOOTER = False
            self.show_actions()
        return input_key

    def process_input(self, current_screen):
        '''Read input until a keystroke that fires a screen change
        is caught
        
        '''
        input_key = None
        while input_key not in self.actions:
            input_key = self.getch()
            input_key = self.central_area.process(input_key)
            self.do_update()
        return self.actions[input_key].do_action(current_screen)

    def pop_up(self,
               header,
               question,
               left_btn_txt,
               right_btn_txt,
               color=None):
        '''Suspend the current screen, setting the header
        to 'header', presenting the 'question,' and providing two 'buttons'.
        Returns True if the RIGHT button is selected, False if the LEFT is
        selected. The LEFT button is initially selected.
        
        '''

        # Hide the cursor, storing its previous state (visibility) so
        # it can be restored when finished. Then, move the cursor
        # to the default position (in case this terminal type does not support
        # hiding the cursor entirely)
        try:
            old_cursor_state = curses.curs_set(0)
        except curses.error:
            old_cursor_state = 2
        cursor_loc = curses.getsyx()
        curses.setsyx(self.cursor_pos[0], self.cursor_pos[1])

        # Add the header, a border, and the question to the window
        self.popup_win.window.border()
        header_x = (self.popup_win.area.columns - textwidth(header)) / 2
        self.popup_win.add_text(header, 0, header_x)
        y_loc = 2
        y_loc += self.popup_win.add_paragraph(question, y_loc, 2)
        y_loc += 2

        # Set the background color based on the parameter given, or choose
        # a default based on the theme. Set the highlight_color by flipping
        # the A_REVERSE bit of the color
        if color is None:
            color = self.popup_win.color
        self.popup_win.set_color(color)
        highlight_color = color ^ curses.A_REVERSE

        # Create two "buttons" of equal size by finding the larger of the
        # two, and centering them
        max_len = max(textwidth(left_btn_txt), textwidth(right_btn_txt))
        left_btn_txt = " [ %s ]" % left_btn_txt.center(max_len)
        right_btn_txt = " [ %s ]" % right_btn_txt.center(max_len)
        button_len = textwidth(left_btn_txt) + 1
        win_size = self.popup_win.window.getmaxyx()
        left_area = WindowArea(1, button_len, y_loc,
                               (win_size[1] / 2) - (button_len + 2))
        left_button = ListItem(left_area,
                               window=self.popup_win,
                               text=left_btn_txt,
                               color=color,
                               highlight_color=highlight_color)
        right_area = WindowArea(1, button_len, y_loc, win_size[1] / 2 + 2)
        right_button = ListItem(right_area,
                                window=self.popup_win,
                                text=right_btn_txt,
                                color=color,
                                highlight_color=highlight_color)

        # Highlight the left button, clear any errors on the screen,
        # and display the pop up
        self.popup_win.activate_object(left_button)
        self.popup_win.no_ut_refresh()
        self.error_line.clear_err()
        self.do_update()

        self._active_win = self.popup_win
        # Loop until the user selects an option.
        input_key = None
        while input_key != curses.KEY_ENTER:
            input_key = self.getch()
            input_key = self.popup_win.process(input_key)
            if input_key == curses.KEY_LEFT:
                self.popup_win.activate_object(left_button)
            elif input_key == curses.KEY_RIGHT:
                self.popup_win.activate_object(right_button)
            self.do_update()
        self._active_win = self.central_area
        user_selected = (self.popup_win.get_active_object() is right_button)

        # Clear the pop up and restore the previous screen, including the
        # cursor position and visibility
        self.popup_win.clear()
        self.central_area.redrawwin()
        curses.setsyx(cursor_loc[0], cursor_loc[1])
        try:
            curses.curs_set(old_cursor_state)
        except curses.error:
            pass
        self.do_update()

        return user_selected
示例#4
0
class HelpScreen(BaseScreen):
    '''Show localized help file pertaining to last traversed screen or
    help topics list from which to choose a desired help topic.
    
    '''

    HELP_HEADER = _("Help Topics")
    HELP_INDEX = _("Help Index")
    INTRO = _("Select a topic and press Continue.")

    def __init__(self, main_win):
        super(HelpScreen, self).__init__(main_win)

        self.locale = locale.setlocale(locale.LC_MESSAGES, "")
        logging.debug("locale=%s", self.locale)

        self.screen = None
        self.screen_last = None
        self.help_info = []
        self.help_dict = None
        self.topics = False
        self.scroll_region = None
        self.cur_help_idx = 0
        self.is_x86 = (platform.processor() == "i386")
        self.setup_help_data()

    def setup_help_data(self):
        '''Setup the help_dict and help_info structures
        
        help_dict contains:
            key: screen name 
            tuple:  (<helpfile_name>, <header for help screen>)
            tuple:  (<helpfile_name>, <header for help screen
                                       and help topics menu entry>)
        
        '''

        self.help_dict = \
            {
            "WelcomeScreen": ("welcome.txt",
                              _("Welcome and Navigation Instructions")),
            "DiskScreen": ("disks.txt", _("Disks")),
            "NetworkTypeScreen": ("network.txt", _("Network")),
            "NICSelect": ("network_manual.txt",
                          _("Manual Network Configuration")),
            "NICConfigure": ("network_manual.txt",
                             _("Manually Configure: NIC")),
            "TimeZone": ("timezone.txt", _("Time Zone")),
            "DateTimeScreen": ("date_time.txt", _("Date and Time")),
            "UserScreen": ("users.txt", _("Users")),
            "SummaryScreen": ("summary.txt", _("Installation Summary"))
            }

        # add x86 and SPARC specific help_dict entries
        if self.is_x86:
            self.help_dict["FDiskPart"] =  \
                ("x86_fdisk_partitions.txt", _("Fdisk Partitions"))
            self.help_dict["PartEditScreen"] = \
                ("x86_fdisk_partitions_select.txt", _("Select Partition"))
            self.help_dict["FDiskPart.slice"] = \
                ("x86_fdisk_slices.txt", _("Solaris Partition Slices"))
            self.help_dict["PartEditScreen.slice"] = \
                ("x86_fdisk_slices_select.txt", _("Select Slice"))
        else:
            self.help_dict["FDiskPart"] = \
                ("sparc_solaris_slices.txt", _("Solaris Slices"))
            self.help_dict["PartEditScreen"] = \
                ("sparc_solaris_slices_select.txt", _("Select Slice"))

        logging.debug("self.help_dict=%s", self.help_dict)

        # help_info contains tuples:
        #    (tuple of screen names, format of text)

        self.help_info = []
        self.help_info.append((("WelcomeScreen", ), "%s"))
        self.help_info.append((("DiskScreen", ), "%s"))

        # add x86 and SPARC specific entries to self.help_info
        if self.is_x86:
            self.help_info.append((("FDiskPart", ), "  %s"))
            self.help_info.append((("PartEditScreen", ), "    %s"))
            self.help_info.append((("FDiskPart.slice", ), "    %s"))
            self.help_info.append((("PartEditScreen.slice", ), "    %s"))
        else:
            self.help_info.append((("FDiskPart", ), "  %s"))
            self.help_info.append((("PartEditScreen", ), "  %s"))
        self.help_info.append((("NetworkTypeScreen", ), "%s"))
        self.help_info.append((("NICSelect", ), "  %s"))
        self.help_info.append((("NICConfigure", ), "  %s"))
        self.help_info.append((("TimeZone", ), "%s"))
        self.help_info.append((("DateTimeScreen", ), "%s"))
        self.help_info.append((("UserScreen", ), "%s"))
        self.help_info.append((("SummaryScreen", ), "%s"))

        logging.debug("self.help_info=%s", self.help_info)

    def set_actions(self):
        '''Remove the continue key for help screen and Help key for
        help topics screen. Redirect F2_Continue to display the selected
        topic, when at the topics list
        
        '''

        logging.debug("in set_actions self.class_name=%s",
                      self.__class__.__name__)

        # change F6 description
        self.main_win.help_action.text = HelpScreen.HELP_INDEX

        # change continue to call continue_action, rather than
        # normal continue. Though we stay on the same screen,
        # we simulate the continue here by changing the screen text.
        #
        help_continue = Action(curses.KEY_F2, _("Continue"),
                               self.continue_action)
        self.main_win.actions[help_continue.key] = help_continue

        if (self.screen == self.__class__.__name__):
            # help topics screen
            self.main_win.actions.pop(self.main_win.help_action.key, None)
        else:
            # help screen
            self.main_win.actions.pop(self.main_win.continue_action.key, None)

    def display_help_topics(self):
        '''Display the help topics screen.'''

        self.main_win.set_header_text(HelpScreen.HELP_HEADER)
        y_loc = 1

        y_loc += self.center_win.add_paragraph(HelpScreen.INTRO,
                                               y_loc,
                                               1,
                                               max_x=(self.win_size_x - 1))
        y_loc += 1

        area = WindowArea(scrollable_lines=(len(self.help_info) + 1),
                          y_loc=y_loc,
                          x_loc=0)
        logging.debug("lines=%s", len(self.help_dict))
        area.lines = self.win_size_y - (y_loc + 1)
        area.columns = self.win_size_x

        self.scroll_region = ScrollWindow(area, window=self.center_win)

        # add the entries to the screen
        logging.debug("range=%s", len(self.help_info))
        for idx, info in enumerate(self.help_info):
            # create ListItem for each help topic
            topic_format = info[1]
            help_topic = self.get_help_topic(info[0])
            help_topic = topic_format % help_topic
            hilite = min(self.win_size_x, textwidth(help_topic) + 1)
            list_item = ListItem(WindowArea(1, hilite, idx, 0),
                                 window=self.scroll_region,
                                 text=help_topic)
            help_screens = info[0]
            logging.debug("help_screens=%s", list(help_screens))
            logging.debug("self.screen_last=%s", self.screen_last)
            if self.screen_last in help_screens:
                logging.debug("Set cur_help_idx = %s", idx)
                self.cur_help_idx = idx
        logging.debug("beg_y=%d, beg_x=%d", *list_item.window.getbegyx())

        self.center_win.activate_object(self.scroll_region)
        self.scroll_region.activate_object(self.cur_help_idx)

    def continue_action(self, dummy=None):
        '''Called when user preses F2 on help topics screen.
        Results in show being called again to display single file help
        of chosen topic.
         
        '''
        logging.debug("continue_action:%s", self.scroll_region.active_object)
        cur_topic = self.scroll_region.active_object
        self.screen = self.help_info[cur_topic][0][0]
        logging.debug("continue_action self.screen=%s", self.screen)
        self.topics = False
        return self

    def get_help_topic(self, name_classes=None):
        '''Get the help topic from the dictionary, given the help class
        tuple passed in. The single file help header in help_dict is
        also the entry used in the help topics menu.
        
        '''
        for key in self.help_dict.keys():
            if key in name_classes:
                return self.help_dict[key][1]
        return ""

    def display_help(self):
        '''Display the single file help screen'''
        # customize header
        help_header = "%s: %s"
        logging.debug("self.screen is =%s", self.screen)
        if self.screen in self.help_dict:
            help_header = help_header % (_("Help"),
                                         self.help_dict[self.screen][1])
            help_text = self.get_help_text(self.help_dict[self.screen][0])
        else:
            help_header = help_header % (_("Help"), _("Not Available"))
            help_text = _("Help for this screen is not available")

        self.main_win.set_header_text(help_header)

        help_text = convert_paragraph(help_text, self.win_size_x - 5)
        logging.debug("help_text #lines=%d, text is \n%s", len(help_text),
                      help_text)
        area = WindowArea(x_loc=0,
                          y_loc=1,
                          scrollable_lines=(len(help_text) + 1))
        area.lines = self.win_size_y - 1
        area.columns = self.win_size_x
        self.scroll_region = ScrollWindow(area, window=self.center_win)
        self.scroll_region.add_paragraph(help_text, start_x=(area.x_loc + 3))
        self.center_win.activate_object(self.scroll_region)

    def _show(self):
        '''Display the screen, either the single file help or help topics.'''

        logging.debug("in show self.screen=%s", self.screen)

        if (self.screen is self.__class__.__name__):
            logging.debug("setting self topics to true:")
            self.topics = True
        else:
            self.topics = False
            self.screen_last = self.screen
            logging.debug("setting self.screen_last to %s", self.screen_last)

        if self.topics:
            self.display_help_topics()
        else:
            self.display_help()

    def get_help_text(self, filename=None):
        '''
        Get the localized help text for the filename passed in.
        First check locid directory. If not there, strip off 
        dot extension (fr_FR.UTF-8 becomes fr_FR). If not there,
        truncate to 2 chars (fr).  If not there, use C.
        '''
        if filename is None:
            return ("")

        help_file = None
        try:
            locid = self.locale
            path = "%s%s/%s"
            full_path = path % (HELP_PATH, locid, filename)
            logging.debug("Accessing help file %s", full_path)
            if (not os.access(full_path, os.R_OK)):
                if (locid.find(".") > 0):
                    locid = locid.split(".")[0]
                    full_path = path % (HELP_PATH, locid, filename)
                    logging.debug("Accessing help file %s", full_path)
                    if (not os.access(full_path, os.R_OK)):
                        if (len(locid) > 1):
                            locid = locid[:2]
                            full_path = path % (HELP_PATH, locid, filename)
                            logging.debug("Accessing help file %s", full_path)
                            if (not os.access(full_path, os.R_OK)):
                                locid = "C"
                                full_path = path % (HELP_PATH, locid, filename)
            logging.debug("Opening help file %s", full_path)
            help_file = open(full_path, 'r')
        except IOError:
            logging.debug("Unable to open help file %s", full_path)
            help_text = _("Help for this screen is not available")
        else:
            help_text = help_file.read()
            logging.debug("Done reading help file %s", full_path)
        if help_file:
            help_file.close()
        return help_text