示例#1
0
    def get_disk_summary(self):
        '''Return a string summary of the disk selection'''
        disk = self.install_profile.disk
        
        solaris_data = disk.get_solaris_data()
        if isinstance(solaris_data, SliceInfo):
            slice_data = solaris_data
            part_data = None
        elif isinstance(solaris_data, PartitionInfo):
            part_data = solaris_data
            slice_data = part_data.get_solaris_data()
	else:
            part_data = None
            slice_data = None
        
        format_dict = {}
        disk_string = [_("Disk: %(disk-size).1fGB %(disk-type)s")]
        format_dict['disk-size'] = disk.size.size_as("gb")
        format_dict['disk-type'] = disk.type
        
        if part_data is not None:
            disk_string.append(_("Partition: %(part-size).1fGB %(part-type)s"))
            format_dict['part-size'] = part_data.size.size_as("gb")
            format_dict['part-type'] = part_data.get_description()

        if not slice_data is None:
	        if part_data is None or not part_data.use_whole_segment:
	            disk_string.append(_("Slice %(slice-num)i: %(slice-size).1fGB"
	                                 " %(pool)s"))
	            format_dict['slice-num'] = slice_data.number
	            format_dict['slice-size'] = slice_data.size.size_as("gb")
	            format_dict['pool'] = slice_data.type[1]
        
        return "\n".join(disk_string) % format_dict
    def validate(self):
        '''Ensure the Solaris partition or ZFS Root exists and is large
        enough
        
        '''
        disk_info = self.disk_win.disk_info
        if self.is_x86 and not self.x86_slice_mode:
            min_size_text = _("The Solaris2 partition must be at least"
                              " %(size).1fGB")
            missing_part = _("There must be exactly one Solaris2 partition.")
        else:
            min_size_text = _("The size of %(pool)s must be at least"
                              " %(size).1fGB")
            missing_part = _("There must be one ZFS root pool, '%(pool)s.'")
        min_size = round(get_minimum_size().size_as("gb"), 1)
        format_dict = {'pool': SliceInfo.DEFAULT_POOL, 'size': min_size}

        try:
            part = disk_info.get_solaris_data(check_multiples=True)
        except ValueError:
            part = None

        if part is None:
            raise UIMessage(missing_part % format_dict)

        # When comparing sizes, check only to the first decimal place,
        # as that is all the user sees. (Rounding errors that could
        # cause the partition/slice layout to be invalid get cleaned up
        # prior to target instantiation)
        part_size = round(part.size.size_as("gb"), 1)
        if part_size < min_size:
            raise UIMessage(min_size_text % format_dict)
示例#3
0
    def build_summary(self):
        '''Build a textual summary from the install_profile'''
        lines = []

        lines.append(_("Software: %s") % self.get_release())
        lines.append("")
        lines.append(self.get_zfs_summary())
        lines.append("")
        lines.append(self.get_disk_summary())
        lines.append("")
        lines.append(self.get_tz_summary())
        lines.append("")
        lines.append(
            _("Language: *The following can be changed when "
              "logging in."))
        if self.install_profile.system.locale is None:
            self.install_profile.system.determine_locale()
        lines.append(
            _("  Default language: %s") %
            self.install_profile.system.actual_lang)
        lines.append("")
        lines.append(_("Users:"))
        lines.extend(self.get_users())
        lines.append("")
        lines.append(_("Network:"))
        lines.extend(self.get_networks())

        return "\n".join(lines)
 def validate(self):
     '''Ensure the Solaris partition or ZFS Root exists and is large
     enough
     
     '''
     disk_info = self.disk_win.disk_info
     if self.is_x86 and not self.x86_slice_mode:
         min_size_text = _("The Solaris2 partition must be at least"
                           " %(size).1fGB")
         missing_part = _("There must be exactly one Solaris2 partition.")
     else:
         min_size_text = _("The size of %(pool)s must be at least"
                           " %(size).1fGB")
         missing_part = _("There must be one ZFS root pool, '%(pool)s.'")
     min_size = round(get_minimum_size().size_as("gb"), 1)
     format_dict = {'pool' : SliceInfo.DEFAULT_POOL,
                    'size': min_size}
     
     try:
         part = disk_info.get_solaris_data(check_multiples=True)
     except ValueError:
         part = None
     
     if part is None:
         raise UIMessage(missing_part % format_dict)
     
     # When comparing sizes, check only to the first decimal place,
     # as that is all the user sees. (Rounding errors that could
     # cause the partition/slice layout to be invalid get cleaned up
     # prior to target instantiation)
     part_size = round(part.size.size_as("gb"), 1)
     if part_size < min_size:
         raise UIMessage(min_size_text % format_dict)
示例#5
0
    def build_summary(self):
        '''Build a textual summary from the install_profile'''
        lines = []

        lines.append(_("Software: %s") % self.get_release())
        lines.append("")
        lines.append(self.get_zfs_summary())
        lines.append("")
        lines.append(self.get_disk_summary())
        lines.append("")
        lines.append(self.get_tz_summary())
        lines.append("")
        lines.append(
            _("Language: *The following can be changed when "
              "logging in."))
        if self.install_profile.system.locale is None:
            self.install_profile.system.determine_locale()
        lines.append(
            _("  Default language: %s") %
            self.install_profile.system.actual_lang)
        lines.append("")
        lines.append(_("Users:"))
        if SummaryScreen.SONICLE_USER:
            lines.append(
                " privileged user sonicle created automatically (password sonicle)"
            )
        else:
            lines.extend(self.get_users())
        lines.append("")
        lines.append(_("Network:"))
        lines.extend(self.get_networks())

        return "\n".join(lines)
示例#6
0
    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)
示例#7
0
 def validate(self):
     '''Verify each of the edit fields'''
     year_value = self.year_edit.get_text()
     month_value = self.month_edit.get_text()
     day_value = self.day_edit.get_text()
     hour_value = self.hour_edit.get_text()
     minute_value = self.minute_edit.get_text()
     logging.debug("year_value=%s", year_value)
     logging.debug("month_value=%s", month_value)
     logging.debug("day_value=%s", day_value)
     logging.debug("hour_value=%s", hour_value)
     logging.debug("minute_value=%s", minute_value)
     had_err = False
     if not self.year_edit.run_on_exit():
         had_err = True
     if not self.month_edit.run_on_exit():
         had_err = True
     if not self.day_edit.run_on_exit():
         had_err = True
     if not self.hour_edit.run_on_exit():
         had_err = True
     if not self.minute_edit.run_on_exit():
         had_err = True
     if had_err:
         raise UIMessage, _("Invalid date/time. See errors above.")
示例#8
0
def hour_on_exit(hour_edit):
    '''Check hour when exiting field'''
    hour_str = hour_edit.get_text()
    logging.debug("hour_on_exit, =%s=", hour_str)
    if not hour_str:
        raise UIMessage, _("Hour out of range")
    hour_valid(hour_edit)
    return True
示例#9
0
def username_valid(edit_field):
    '''Ensure the username is not "root" or "jack"'''
    user_str = edit_field.get_text()
    if user_str == "root":
        raise UIMessage, _("'root' cannot be used")
    elif user_str == "jack":
        raise UIMessage, _("'jack' cannot be used")
    return True
示例#10
0
def minute_on_exit(minute_edit):
    '''Check minute when exiting field'''
    minute_str = minute_edit.get_text()
    logging.debug("minute_on_exit, =%s=", minute_str)
    if not minute_str:
        raise UIMessage, _("Minute out of range")
    minute_valid(minute_edit)
    return True
示例#11
0
def pass_match(edit_field, linked_win=None):
    '''Make sure passwords match'''
    confirm_pass = edit_field.get_text()
    if linked_win is None or linked_win.get_text() == confirm_pass:
        return True
    else:
        edit_field.clear_text()
        linked_win.clear_text()
        raise UIMessage, _("Passwords don't match")
示例#12
0
def day_on_exit(day_edit):
    '''Check day when exiting field'''
    day_str = day_edit.get_text()
    logging.debug("day_on_exit, =%s=", day_str)
    day_valid(day_edit)
    if (len(day_str) == 0 or int(day_str) == 0):
        logging.debug("on exit day out of range=%s", day_str)
        raise UIMessage, _("Day out of range")
    return True
示例#13
0
def month_on_exit(month_edit):
    '''Check month when exiting field'''
    month_str = month_edit.get_text()
    logging.debug("month_on_exit, =%s=", month_str)
    month_valid(month_edit)
    if (len(month_str) == 0 or int(month_str) == 0):
        logging.debug("on exit month out of range=%s", month_str)
        raise UIMessage, _("Month out of range")
    return True
示例#14
0
def exit_text_installer(logname=None, errcode=0):
    '''Close out the logger and exit with errcode'''
    logging.info("**** END ****")
    logging.shutdown()
    if logname is not None:
        print _("Exiting Text Installer. Log is available at:\n%s") % logname
    if isinstance(errcode, unicode):
        errcode = errcode.encode(get_encoding())
    sys.exit(errcode)
示例#15
0
def minute_valid(minute_edit):
    '''Check validity of minute as each char entered'''
    minute_str = minute_edit.get_text()
    logging.log(LOG_LEVEL_INPUT, "validating minute, text=%s=", minute_str)
    if minute_str and not minute_str.isdigit():
        raise UIMessage, _("Minute must be numeric")
    if len(minute_str) >= 2 and (int(minute_str) > 59):
        raise UIMessage, _("Minute out of range")
    return True
示例#16
0
def year_on_exit(year_edit):
    '''Check year when exiting field'''
    year_str = year_edit.get_text()
    logging.debug("year_on_exit, =%s=", year_str)
    year_valid(year_edit)
    if (len(year_str) != 4):
        logging.debug("on exit year out of range=%s", input)
        raise UIMessage, _("Year out of range")
    return True
示例#17
0
def hour_valid(hour_edit):
    '''Check validity of hour as each char entered'''
    hour_str = hour_edit.get_text()
    logging.log(LOG_LEVEL_INPUT, "validating hour, text=%s=", hour_str)
    if hour_str and not hour_str.isdigit():
        raise UIMessage, _("Hour must be numeric")
    if len(hour_str) >= 2 and (int(hour_str) > 23):
        logging.debug("hour out of range, =%s", hour_str)
        raise UIMessage, _("Hour out of range")
    return True
示例#18
0
class WelcomeScreen(BaseScreen):
    '''First screen of the text installer.
    No special __init__ needed beyond that provided by BaseScreen
    
    '''
    
    HEADER_TEXT = _("Welcome to %(release)s") % RELEASE
    WELCOME_TEXT = _("Thanks for choosing to install %(release)s! This "
                     "installer enables you to install the %(release)s "
                     "Operating System (OS) on SPARC or x86 systems.\n\n"
                     "The installation log will be at %(log)s.\n\n"
                     "How to navigate through this installer:")
    BULLET_ITEMS = [_("Use the function keys listed at the bottom of each "
                 "screen to move from screen to screen and to perform "
                 "other operations."),
               _("Use the up/down arrow keys to change the selection "
                 "or to move between input fields."),
               _("If your keyboard does not have function keys, or "
                 "they do not respond, press ESC; the legend at the "
                 "bottom of the screen will change to show the ESC keys"
                 " for navigation and other functions.")]
    BULLET = "- "
    
    def set_actions(self):
        '''Remove the F3_Back Action from the first screen'''
        self.main_win.actions.pop(self.main_win.back_action.key, None)
        zpool_install_action = Action(curses.KEY_F5, _("InstallToExistingPool"),
                                      self.zpool_install)
        self.main_win.actions[zpool_install_action.key] = zpool_install_action

    def zpool_install(self, dummy):
        '''We write down install_profile.install_to_pool flag
        and show the next screen.
        '''
        self.install_profile.install_to_pool = True
        return self.main_win.screen_list.get_next(self)
    
    def _show(self):
        '''Display the static paragraph WELCOME_TEXT'''
        log_file = self.install_profile.log_location
        y_loc = 1
        fmt = {"log" : log_file}
        fmt.update(RELEASE)
        text = WelcomeScreen.WELCOME_TEXT % fmt
        y_loc += self.center_win.add_paragraph(text, start_y=y_loc)
        x_loc = len(WelcomeScreen.BULLET)
        for bullet in WelcomeScreen.BULLET_ITEMS:
            self.center_win.add_text(WelcomeScreen.BULLET, start_y=y_loc)
            y_loc += self.center_win.add_paragraph(bullet, start_y=y_loc,
                                                   start_x=x_loc)
        # If user returned back to this screen, forget about hitting F5
        if self.install_profile.install_to_pool:
            self.install_profile.install_to_pool = False
            self.install_profile.pool_name = None
            self.install_profile.be_name = InstallProfile.DEFAULT_BE_NAME
示例#19
0
    def get_networks(self):
        '''Build a summary of the networks in the install_profile,
        returned as a list of strings
        
        '''
        network_summary = []
        network_summary.append(
            _("  Computer name: %s") % self.install_profile.system.hostname)
        nic = self.install_profile.nic

        if nic.type == NetworkInfo.AUTOMATIC:
            network_summary.append(_("  Network Configuration: Automatic"))
        elif nic.type == NetworkInfo.NONE:
            network_summary.append(_("  Network Configuration: None"))
        else:
            network_summary.append(
                _("  Manual Configuration: %s") % nic.nic_name)
            network_summary.append(_("    IP Address: %s") % nic.ip_address)
            network_summary.append(_("    Netmask: %s") % nic.netmask)
            if nic.gateway:
                network_summary.append(_("    Gateway: %s") % nic.gateway)
            if nic.dns_address:
                network_summary.append(_("    DNS: %s") % nic.dns_address)
            if nic.domain:
                network_summary.append(_("    Domain: %s") % nic.domain)
        return network_summary
示例#20
0
 def __init__(self, main_win, screen=None):
     super(TimeZone, self).__init__(main_win)
     self.sys_info = None
     if screen is None:
         self.screen = TimeZone.TIMEZONE
     else:
         self.screen = screen
     self.tz_tuples = None
     self.tz_list = None
     self.cur_timezone_idx = 0
     self.cur_timezone_parent = None
     self.last_timezone_parent = None
     self.cur_continent = None
     self.cur_country = None
     self.scroll_region = None
     self.last_country = None
     self.last_continent = None
     if self.screen == TimeZone.TIMEZONE:
         self.header_text = _("Time Zone")
         self.intro = _("Select your time zone.")
         self.title = _("Time Zones")
     elif self.screen == TimeZone.LOCATIONS:
         self.header_text = _("Time Zone: Locations")
         self.intro = _("Select the location that contains your time zone.")
         self.title = _("Locations")
     else:
         self.header_text = _("Time Zone: Regions")
         self.intro = _("Select the region that contains your time zone.")
         self.title = _("Regions")
示例#21
0
 def print_data(self):
     '''Print static (non-editable) data.
     
     Slices - fill the left side, then remaining slices on the right side.
     If for some reason not all slices fit, indicate how many more slices
     there area
     
     Partitions - Put standard partitions on the left, logical partitions
     on the right
     
     '''
     part_index = 0
     if self.has_partition_data:
         max_parts = PartitionInfo.MAX_STANDARD_PARTITIONS
     else:
         max_parts = min(len(self.disk_info.slices),
                             self.left_win.area.lines)
     win = self.left_win
     y_loc = 0
     for next_part in self.disk_info.get_parts():
         if y_loc >= max_parts:
             if win is self.left_win:
                 win = self.right_win
                 y_loc = 0
                 max_parts = win.area.lines
             else:
                 if self.has_partition_data:
                     num_extra = len(self.disk_info.partitions) - part_index
                     more_parts_txt = _("%d more partitions") % num_extra
                 else:
                     num_extra = len(self.disk_info.slices) - part_index
                     more_parts_txt = _("%d more slices") % num_extra
                 win.add_text(more_parts_txt, win.area.lines, 3)
                 break
         x_loc = DiskWindow.SCROLL_PAD
         field = 0
         win.add_text(next_part.get_description(), y_loc, x_loc,
                      self.headers[field][0] - 1)
         x_loc += self.headers[field][0]
         field += 1
         if not self.has_partition_data:
             win.add_text(str(next_part.number), y_loc, x_loc,
                          self.headers[field][0] - 1)
             x_loc += self.headers[field][0]
             field += 1
         win.add_text("%*.1f" % (self.headers[field][0]-1,
                                 next_part.size.size_as("gb")),
                                 y_loc, x_loc,
                                 self.headers[field][0]-1)
         x_loc += self.headers[field][0]
         y_loc += 1
         field += 1
         part_index += 1
     self.right_win.use_vert_scroll_bar = False
     self.no_ut_refresh()
示例#22
0
 def get_zfs_summary(self):
     '''Return a string summary of the root pool configuration'''
     pool_name = SliceInfo.DEFAULT_POOL.data
     pool_type = self.install_profile.zpool_type
     if (pool_type):
         return _("ZFS Pool name: %(name)s, type: %(type)s") % {
             "name": pool_name,
             "type": pool_type
         }
     else:
         return _("ZFS Pool name: %s") % (pool_name)
示例#23
0
 def set_actions(self):
     '''Edit Screens add 'Reset' and 'Change Type' actions. Since these
     do not manipulate screen direction, they are captured during
     processing by adding them to center_win's key_dict.
     
     '''
     super(PartEditScreen, self).set_actions()
     reset_action = Action(curses.KEY_F7, _("Reset"))
     change_action = Action(curses.KEY_F5, _("Change Type"))
     self.main_win.actions[reset_action.key] = reset_action
     self.main_win.actions[change_action.key] = change_action
     self.center_win.key_dict[curses.KEY_F7] = self.on_key_F7
示例#24
0
def username_valid_alphanum(edit_field):
    '''Ensure username is alphanumeric, and does not start with a digit'''
    user_str = edit_field.get_text()
    if not user_str:
        return True
    if user_str[0].isalpha():
        if user_str.isalnum():
            return True
        else:
            raise UIMessage, _("Username must be alphanumeric")
    else:
        raise UIMessage, _("First character must be a-zA-Z")
 def set_actions(self):
     '''Edit Screens add 'Reset' and 'Change Type' actions. Since these
     do not manipulate screen direction, they are captured during
     processing by adding them to center_win's key_dict.
     
     '''
     super(PartEditScreen, self).set_actions()
     reset_action = Action(curses.KEY_F7, _("Reset"))
     change_action = Action(curses.KEY_F5, _("Change Type"))
     self.main_win.actions[reset_action.key] = reset_action
     self.main_win.actions[change_action.key] = change_action
     self.center_win.key_dict[curses.KEY_F7] = self.on_key_F7
示例#26
0
def decimal_valid(edit_field, disk_win=None):
    '''Check text to see if it is a decimal number of precision no
    greater than the tenths place.
    
    '''
    text = edit_field.get_text().lstrip()
    if text.endswith(" "):
        raise UIMessage(_('Only the digits 0-9 and "." are valid.'))
    vals = text.split(".")
    if len(vals) > 2:
        raise UIMessage(_('A number can only have one "."'))
    try:
        if len(vals[0]) > 0:
            int(vals[0])
        if len(vals) > 1 and len(vals[1]) > 0:
            int(vals[1])
    except ValueError:
        raise UIMessage(_('Only the digits 0-9 and "." are valid.'))
    if len(vals) > 1 and len(vals[1]) > 1:
        raise UIMessage(_("Size can be specified to only one decimal place."))
    if disk_win is not None:
        text = text.rstrip(".")
        if not text:
            text = "0"
        new_size = DiskSpace(text + "gb")
        max_size = edit_field.data_obj.get_max_size(disk_win.disk_info)

        # When comparing sizes, check only to the first decimal place,
        # as that is all the user sees. (Rounding errors that could
        # cause the partition/slice layout to be invalid get cleaned up
        # prior to target instantiation)
        new_size_rounded = round(new_size.size_as("gb"), 1)
        max_size_rounded = round(max_size, 1)
        if new_size_rounded > max_size_rounded:
            raise UIMessage(
                _("The new size (%(size).1f) is greater than "
                  "the available space (%(avail).1f)") % {
                      "size": new_size_rounded,
                      "avail": max_size_rounded
                  })
        size_diff = abs(new_size.size_as("gb") - edit_field.data_obj.orig_size)
        if size_diff > DiskWindow.SIZE_PRECISION:
            edit_field.data_obj.size = new_size
            edit_field.data_obj.adjust_offset(disk_win.disk_info)
        else:
            edit_field.data_obj.size = "%fgb" % edit_field.data_obj.orig_size
        disk_win.update_avail_space()
        disk_win.no_ut_refresh()
        part_field = disk_win.find_part_field(edit_field.data_obj)[1]
        disk_win.mark_if_destroyed(part_field)
    return True
示例#27
0
 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()
示例#28
0
 def validate(self):
     '''Ensure hostname is set and a network type is chosen (unless no
     NICs are present)
     
     '''
     hostname_text = self.hostname.get_text()
     if not hostname_text:
         raise UIMessage(_("A Hostname is required."))
     if len(hostname_text) < 2:
         raise UIMessage(_("A Hostname must be at least two characters."))
     if self.have_nic:
         item_key = self.center_win.get_active_object().item_key
         if item_key not in self.net_type_dict:
             raise UIMessage(_("Select the wired network configuration: "
                                "Automatically or None."))
示例#29
0
 def get_users(self):
     '''Build a summary of the user information, and return it as a list
     of strings
     
     '''
     root = self.install_profile.users[0]
     primary = self.install_profile.users[1]
     user_summary = []
     if not root.password:
         user_summary.append(_("  Warning: No root password set"))
     if primary.login_name:
         user_summary.append(_("  Username: %s") % primary.login_name)
     else:
         user_summary.append(_("  No user account"))
     return user_summary
示例#30
0
 def validate(self):
     '''Verify the syntactical validity of the IP Address fields'''
     ip_fields = [self.ip_field,
                  self.netmask_field,
                  self.gateway_field,
                  self.dns_field]
     for field in ip_fields:
         validate_ip(field)
     
     if not self.ip_field.get_text():
         raise UIMessage(_("IP Address must not be empty"))
     if not self.netmask_field.get_text():
         raise UIMessage(_("Netmask must not be empty"))
     if self.domain_field.get_text() and not self.dns_field.get_text():
         raise UIMessage(_("DNS server required if Domain set"))
示例#31
0
    def set_actions(self):
        '''Remove all actions except Quit, and add actions for rebooting
        and viewing the log.
        
        '''
        self.main_win.actions.pop(curses.KEY_F2)  # Remove F2_Continue
        self.main_win.actions.pop(curses.KEY_F3)  # Remove F3_Back
        self.main_win.actions.pop(curses.KEY_F6)  # Remove F6_Help

        if self.install_profile.install_succeeded:
            reboot_action = Action(curses.KEY_F8, _("Reboot"), reboot_system)
            self.main_win.actions[reboot_action.key] = reboot_action

        log_action = Action(curses.KEY_F4, _("View Log"),
                            self.main_win.screen_list.get_next)
        self.main_win.actions[log_action.key] = log_action
示例#32
0
    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)