From 1af159274f836fe17d7480f3eec20523bad766cd Mon Sep 17 00:00:00 2001 From: "T. H. Wright" Date: Tue, 20 Aug 2024 21:12:25 -0400 Subject: [PATCH] Continued work on deps install - Fix up msg.logos_continue_question. - Add Curses ConfirmDialog - Allow Curses to wrap text with newlines - Properly detect if user cancels pkexec - Handle TUI manual installation required --- installer.py | 3 ++ main.py | 5 ++- msg.py | 4 +- system.py | 111 +++++++++++++++++++++++++++++++++++++------------- tui_app.py | 78 +++++++++++++++++++++++++---------- tui_curses.py | 14 +++++-- tui_dialog.py | 39 ++++++++++++------ tui_screen.py | 40 +++++++++++++++++- 8 files changed, 224 insertions(+), 70 deletions(-) diff --git a/installer.py b/installer.py index 7313f7d9..53b22876 100644 --- a/installer.py +++ b/installer.py @@ -271,6 +271,9 @@ def ensure_sys_deps(app=None): if not config.SKIP_DEPENDENCIES: utils.check_dependencies(app) + if app: + if config.DIALOG == "curses": + app.installdeps_e.wait() logging.debug("> Done.") else: logging.debug("> Skipped.") diff --git a/main.py b/main.py index 92157bba..37adbe83 100755 --- a/main.py +++ b/main.py @@ -391,10 +391,11 @@ def main(): # Check for AppImageLauncher if shutil.which('AppImageLauncher'): - question_text = "Remove AppImageLauncher?" + question_text = "Remove AppImageLauncher? A reboot will be required." secondary = ( + "Your system currently has AppImageLauncher installed.\n" "LogosLinuxInstaller is not compatible with AppImageLauncher.\n" - "Remove AppImageLauncher now? A reboot will be required." + "For more information, see: https://github.com/FaithLife-Community/LogosLinuxInstaller/issues/114" ) no_text = "User declined to remove AppImageLauncher." msg.logos_continue_question(question_text, no_text, secondary) diff --git a/msg.py b/msg.py index 74e8c416..7762f93c 100644 --- a/msg.py +++ b/msg.py @@ -12,6 +12,7 @@ import config from gui import ask_question from gui import show_error +import utils logging.console_log = [] @@ -144,6 +145,7 @@ def logos_warn(message): logos_msg(message) +#TODO: I think detail is doing the same thing as secondary. def logos_error(message, secondary=None, detail=None, app=None, parent=None): if detail is None: message = f"{message}\n{detail}" @@ -229,7 +231,7 @@ def logos_continue_question(question_text, no_text, secondary, app=None): elif app is None: cli_continue_question(question_text, no_text, secondary) elif config.DIALOG == 'curses': - pass + app.screen_q.put(app.stack_confirm(16, app.confirm_q, app.confirm_e, question_text, no_text, secondary, dialog=config.use_python_dialog)) else: logos_error(f"Unhandled question: {question_text}") diff --git a/system.py b/system.py index 64a58418..8792257a 100644 --- a/system.py +++ b/system.py @@ -52,7 +52,7 @@ def run_command(command, retries=1, delay=0, **kwargs): ) return result except subprocess.CalledProcessError as e: - logging.error(f"Error occurred while executing \"{command}\": {e}") + logging.error(f"Error occurred in run_command() while executing \"{command}\": {e}") if "lock" in str(e): logging.debug(f"Database appears to be locked. Retrying in {delay} seconds…") # noqa: E501 time.sleep(delay) @@ -332,8 +332,11 @@ def remove_appimagelauncher(app=None): logging.debug(f"Running command: {cmd}") run_command(cmd) except subprocess.CalledProcessError as e: - logging.error(f"An error occurred: {e}") - logging.error(f"Command output: {e.output}") + if e.returncode == 127: + logging.error("User cancelled appimagelauncher removal.") + else: + logging.error(f"An error occurred: {e}") + logging.error(f"Command output: {e.output}") msg.logos_error("Failed to uninstall AppImageLauncher.") sys.exit(1) logging.info("System reboot is required.") @@ -388,6 +391,8 @@ def install_dependencies(packages, bad_packages, logos9_packages=None, app=None) if config.SKIP_DEPENDENCIES: return + install_deps_failed = False + manual_install_required = False command = [] preinstall_command = [] install_command = [] @@ -420,17 +425,32 @@ def install_dependencies(packages, bad_packages, logos9_packages=None, app=None) ) if config.PACKAGE_MANAGER_COMMAND_INSTALL: - if missing_packages and conflicting_packages: - message = f"Your {config.OS_NAME} computer requires installing and removing some software. To continue, the program will attempt to install the following package(s) by using '{config.PACKAGE_MANAGER_COMMAND_INSTALL}':\n{missing_packages}\nand will remove the following package(s) by using '{config.PACKAGE_MANAGER_COMMAND_REMOVE}':\n{conflicting_packages}\nProceed?" # noqa: E501 - # logging.critical(message) + if config.OS_NAME in ['fedora', 'arch']: + message = False + elif missing_packages and conflicting_packages: + message = f"Your {config.OS_NAME} computer requires installing and removing some software.\nProceed?" # noqa: E501 + no_message = "User refused to install and remove software via the application" # noqa: E501 + secondary = f"To continue, the program will attempt to install the following package(s) by using '{config.PACKAGE_MANAGER_COMMAND_INSTALL}':\n{missing_packages}\nand will remove the following package(s) by using '{config.PACKAGE_MANAGER_COMMAND_REMOVE}':\n{conflicting_packages}" # noqa: E501 elif missing_packages: - message = f"Your {config.OS_NAME} computer requires installing some software. To continue, the program will attempt to install the following package(s) by using '{config.PACKAGE_MANAGER_COMMAND_INSTALL}':\n{missing_packages}\nProceed?" # noqa: E501 - # logging.critical(message) + message = f"Your {config.OS_NAME} computer requires installing some software.\nProceed?" # noqa: E501 + no_message = "User refused to install software via the application." # noqa: E501 + secondary = f"To continue, the program will attempt to install the following package(s) by using '{config.PACKAGE_MANAGER_COMMAND_INSTALL}':\n{missing_packages}" # noqa: E501 elif conflicting_packages: - message = f"Your {config.OS_NAME} computer requires removing some software. To continue, the program will attempt to remove the following package(s) by using '{config.PACKAGE_MANAGER_COMMAND_REMOVE}':\n{conflicting_packages}\nProceed?" # noqa: E501 - # logging.critical(message) + message = f"Your {config.OS_NAME} computer requires removing some software.\nProceed?" # noqa: E501 + no_message = "User refused to remove software via the application." # noqa: E501 + secondary = f"To continue, the program will attempt to remove the following package(s) by using '{config.PACKAGE_MANAGER_COMMAND_REMOVE}':\n{conflicting_packages}" # noqa: E501 else: + message = None + + if message is None: logging.debug("No missing or conflicting dependencies found.") + elif not message: + logging.error("Your distro requires manual dependency installation.") + else: + msg.logos_continue_question(message, no_message, secondary, app) + if app: + if config.DIALOG == "curses": + app.confirm_e.wait() # TODO: Need to send continue question to user based on DIALOG. # All we do above is create a message that we never send. @@ -492,40 +512,75 @@ def install_dependencies(packages, bad_packages, logos9_packages=None, app=None) command_str = ' '.join(final_command) # TODO: Fix fedora/arch handling. if config.OS_NAME in ['fedora', 'arch']: + manual_install_required = True sudo_command = command_str.replace("pkexec", "sudo") - message = "The system needs to install/remove packages." + message = "The system needs to install/remove packages, but it requires manual intervention." detail = ( "Please run the following command in a terminal, then restart " f"LogosLinuxInstaller:\n{sudo_command}\n" ) - if hasattr(app, 'root'): - detail += "\nThe command has been copied to the clipboard." - app.root.clipboard_clear() - app.root.clipboard_append(sudo_command) - app.root.update() - msg.logos_error( - message, - detail=detail, - app=app, - parent='installer_win' - ) - else: + if app: + if config.DIALOG == "tk": + if hasattr(app, 'root'): + detail += "\nThe command has been copied to the clipboard." + app.root.clipboard_clear() + app.root.clipboard_append(sudo_command) + app.root.update() + msg.logos_error( + message, + detail=detail, + app=app, + parent='installer_win' + ) + install_deps_failed = True + else: + msg.logos_error(message + "\n" + detail) + install_deps_failed = True + + if manual_install_required and app and config.DIALOG == "curses": + app.screen_q.put(app.stack_confirm(17, app.todo_q, app.todo_e, + f"Please run the following command in a terminal, then select \"Continue\" when finished.\n\nLogosLinuxInstaller:\n{sudo_command}\n", # noqa: E501 + "User cancelled dependency installation.", # noqa: E501 + message, # noqa: E501 + options=["Continue", "Return to Main Menu"], dialog=config.use_python_dialog)) + app.manualinstall_e.wait() + + + if not install_deps_failed and not manual_install_required: try: logging.debug(f"Attempting to run this command: {command_str}") run_command(command_str, shell=True) except subprocess.CalledProcessError as e: - logging.error(f"An error occurred: {e}") - logging.error(f"Command output: {e.output}") + if e.returncode == 127: + logging.error("User cancelled dependency installation.") + else: + logging.error(f"An error occurred in install_dependencies(): {e}") + logging.error(f"Command output: {e.output}") + install_deps_failed = True else: msg.logos_error( f"The script could not determine your {config.OS_NAME} install's package manager or it is unsupported. " # noqa: E501 f"Your computer is missing the command(s) {missing_packages}. " f"Please install your distro's package(s) associated with {missing_packages} for {config.OS_NAME}.") # noqa: E501 - # TODO: Verify with user before executing if config.REBOOT_REQUIRED: - pass - # reboot() + question = "Should the program reboot the host now?" # noqa: E501 + no_text = "The user has chosen not to reboot." + secondary = "The system has installed or removed a package that requires a reboot." + if msg.logos_continue_question(question, no_text, secondary): + reboot() + else: + logging.error("Cannot proceed until reboot. Exiting.") + sys.exit(1) + + if install_deps_failed: + if app: + if config.DIALOG == "curses": + app.choice_q.put("Return to Main Menu") + else: + if app: + if config.DIALOG == "curses": + app.installdeps_e.set() def have_lib(library, ld_library_path): diff --git a/tui_app.py b/tui_app.py index a3a75af2..3b5689ed 100644 --- a/tui_app.py +++ b/tui_app.py @@ -60,6 +60,10 @@ def __init__(self, stdscr): self.releases_e = threading.Event() self.release_q = Queue() self.release_e = threading.Event() + self.manualinstall_q = Queue() + self.manualinstall_e = threading.Event() + self.installdeps_q = Queue() + self.installdeps_e = threading.Event() self.installdir_q = Queue() self.installdir_e = threading.Event() self.wine_q = Queue() @@ -72,6 +76,8 @@ def __init__(self, stdscr): self.finished_e = threading.Event() self.config_q = Queue() self.config_e = threading.Event() + self.confirm_q = Queue() + self.confirm_e = threading.Event() self.password_q = Queue() self.password_e = threading.Event() self.appimage_q = Queue() @@ -275,8 +281,6 @@ def task_processor(self, evt=None, task=None): self.menu_screen.set_options(self.set_tui_menu_options(dialog=False)) #self.menu_screen.set_options(self.set_tui_menu_options(dialog=True)) self.switch_q.put(1) - # elif task == 'PASSWORD': - # utils.start_thread(self.get_password(config.use_python_dialog)) def choice_processor(self, stdscr, screen_id, choice): if screen_id != 0 and (choice == "Return to Main Menu" or choice == "Exit"): @@ -345,48 +349,54 @@ def choice_processor(self, stdscr, screen_id, choice): appimage_filename = choice config.SELECTED_APPIMAGE_FILENAME = appimage_filename utils.set_appimage_symlink() + self.menu_screen.choice = "Processing" self.appimage_q.put(config.SELECTED_APPIMAGE_FILENAME) self.appimage_e.set() elif screen_id == 2: - if str(choice).startswith("Logos"): - config.FLPRODUCT = "Logos" - self.product_q.put(config.FLPRODUCT) - self.product_e.set() - elif str(choice).startswith("Verbum"): - config.FLPRODUCT = "Verbum" + if choice: + if str(choice).startswith("Logos"): + config.FLPRODUCT = "Logos" + elif str(choice).startswith("Verbum"): + config.FLPRODUCT = "Verbum" + self.menu_screen.choice = "Processing" self.product_q.put(config.FLPRODUCT) self.product_e.set() elif screen_id == 3: - if "10" in choice: - config.TARGETVERSION = "10" - self.version_q.put(config.TARGETVERSION) - self.version_e.set() - elif "9" in choice: - config.TARGETVERSION = "9" + if choice: + if "10" in choice: + config.TARGETVERSION = "10" + elif "9" in choice: + config.TARGETVERSION = "9" + self.menu_screen.choice = "Processing" self.version_q.put(config.TARGETVERSION) self.version_e.set() elif screen_id == 4: if choice: config.TARGET_RELEASE_VERSION = choice + self.menu_screen.choice = "Processing" self.release_q.put(config.TARGET_RELEASE_VERSION) self.release_e.set() elif screen_id == 5: if choice: config.INSTALLDIR = choice config.APPDIR_BINDIR = f"{config.INSTALLDIR}/data/bin" + self.menu_screen.choice = "Processing" self.installdir_q.put(config.INSTALLDIR) self.installdir_e.set() elif screen_id == 6: config.WINE_EXE = choice if choice: + self.menu_screen.choice = "Processing" self.wine_q.put(config.WINE_EXE) self.wine_e.set() elif screen_id == 7: winetricks_options = utils.get_winetricks_options() if choice.startswith("Download"): + self.menu_screen.choice = "Processing" self.tricksbin_q.put("Download") self.tricksbin_e.set() else: + self.menu_screen.choice = "Processing" config.WINETRICKSBIN = winetricks_options[0] self.tricksbin_q.put(config.WINETRICKSBIN) self.tricksbin_e.set() @@ -399,6 +409,7 @@ def choice_processor(self, stdscr, screen_id, choice): utils.write_config(config.CONFIG_FILE) else: logging.info("Config file left unchanged.") + self.menu_screen.choice = "Processing" self.config_q.put(True) self.config_e.set() self.screen_q.put(self.stack_text(13, self.todo_q, self.todo_e, "Finishing install…", dialog=config.use_python_dialog)) @@ -408,6 +419,7 @@ def choice_processor(self, stdscr, screen_id, choice): pass elif screen_id == 12: if choice: + self.menu_screen.choice = "Processing" wine.run_logos(self) self.switch_q.put(1) elif screen_id == 13: @@ -416,8 +428,28 @@ def choice_processor(self, stdscr, screen_id, choice): pass elif screen_id == 15: if choice: + self.menu_screen.choice = "Processing" self.password_q.put(choice) self.password_e.set() + elif screen_id == 16: + if choice == "No": + self.menu_screen.choice = "Processing" + self.choice_q.put("Return to Main Menu") + else: + self.menu_screen.choice = "Processing" + self.confirm_e.set() + self.screen_q.put(self.stack_text(13, self.todo_q, self.todo_e, + "Installing dependencies…\n", wait=True, + dialog=config.use_python_dialog)) + elif screen_id == 17: + if choice == "Yes": + self.menu_screen.choice = "Processing" + self.manualinstall_e.set() + self.screen_q.put(self.stack_text(13, self.todo_q, self.todo_e, + "Installing dependencies…\n", wait=True, + dialog=config.use_python_dialog)) + else: + self.llirunning = False def switch_screen(self, dialog): if self.active_screen is not None and self.active_screen != self.menu_screen and len(self.tui_screens) > 0: @@ -497,14 +529,15 @@ def get_waiting(self, dialog, screen_id=8): text = ["Install is running…\n"] processed_text = utils.str_array_to_string(text) percent = installer.get_progress_pct(config.INSTALL_STEP, config.INSTALL_STEPS_COUNT) - self.screen_q.put(self.stack_text(screen_id, self.status_q, self.status_e, processed_text, wait=True, percent=percent, - dialog=dialog)) + self.screen_q.put(self.stack_text(screen_id, self.status_q, self.status_e, processed_text, + wait=True, percent=percent, dialog=dialog)) def get_config(self, dialog): question = f"Update config file at {config.CONFIG_FILE}?" labels = ["Yes", "No"] options = self.which_dialog_options(labels, dialog) self.menu_options = options + #TODO: Switch to msg.logos_continue_message self.screen_q.put(self.stack_menu(9, self.config_q, self.config_e, question, options, dialog=dialog)) # def get_password(self, dialog): @@ -598,13 +631,16 @@ def stack_password(self, screen_id, queue, event, question, default="", dialog=F utils.append_unique(self.tui_screens, tui_screen.PasswordScreen(self, screen_id, queue, event, question, default)) - def stack_confirm(self, screen_id, queue, event, question, options, dialog=False): + def stack_confirm(self, screen_id, queue, event, question, no_text, secondary, options=["Yes", "No"], dialog=False): if dialog: - utils.append_unique(self.tui_screens, - tui_screen.ConfirmDialog(self, screen_id, queue, event, question, options)) + yes_label = options[0] + no_label = options[1] + utils.append_unique(self.tui_screens, tui_screen.ConfirmDialog(self, screen_id, queue, event, + question, no_text, secondary, + yes_label=yes_label, no_label=no_label)) else: - #TODO: curses version - pass + utils.append_unique(self.tui_screens, tui_screen.ConfirmScreen(self, screen_id, queue, event, + question, no_text, secondary, options)) def stack_text(self, screen_id, queue, event, text, wait=False, percent=None, dialog=False): if dialog: diff --git a/tui_curses.py b/tui_curses.py index 78ffe239..31f58819 100644 --- a/tui_curses.py +++ b/tui_curses.py @@ -10,8 +10,13 @@ def wrap_text(app, text): # Turn text into wrapped text, line by line, centered - wrapped_text = textwrap.fill(text, app.window_width - 4) - lines = wrapped_text.split('\n') + if "\n" in text: + lines = text.splitlines() + wrapped_lines = [textwrap.fill(line, app.window_width - 4) for line in lines] + lines = '\n'.join(wrapped_lines) + else: + wrapped_text = textwrap.fill(text, app.window_width - 4) + lines = wrapped_text.split('\n') return lines @@ -30,7 +35,10 @@ def title(app, title_text, title_start_y_adj): def text_centered(app, text, start_y=0): stdscr = app.get_menu_window() - text_lines = wrap_text(app, text) + if "\n" in text: + text_lines = wrap_text(app, text).splitlines() + else: + text_lines = wrap_text(app, text) text_start_y = start_y text_width = max(len(line) for line in text_lines) for i, line in enumerate(text_lines): diff --git a/tui_dialog.py b/tui_dialog.py index 8955d0f9..121d2d17 100644 --- a/tui_dialog.py +++ b/tui_dialog.py @@ -7,7 +7,8 @@ def text(app, text, height=None, width=None, title=None, backtitle=None, colors=True): - d = Dialog() + dialog = Dialog() + dialog.autowidgetsize = True options = {'colors': colors} if height is not None: options['height'] = height @@ -17,11 +18,12 @@ def text(app, text, height=None, width=None, title=None, backtitle=None, colors= options['title'] = title if backtitle is not None: options['backtitle'] = backtitle - d.infobox(text, **options) + dialog.infobox(text, **options) def progress_bar(app, text, percent, height=None, width=None, title=None, backtitle=None, colors=True): - d = Dialog() + dialog = Dialog() + dialog.autowidgetsize = True options = {'colors': colors} if height is not None: options['height'] = height @@ -32,22 +34,25 @@ def progress_bar(app, text, percent, height=None, width=None, title=None, backti if backtitle is not None: options['backtitle'] = backtitle - d.gauge_start(text=text, percent=percent, **options) + dialog.gauge_start(text=text, percent=percent, **options) #FIXME: Not working. See tui_screen.py#262. def update_progress_bar(app, percent, text='', update_text=False): - d = Dialog() - d.gauge_update(percent, text, update_text) + dialog = Dialog() + dialog.autowidgetsize = True + dialog.gauge_update(percent, text, update_text) def stop_progress_bar(app): - d = Dialog() - d.gauge_stop() + dialog = Dialog() + dialog.autowidgetsize = True + dialog.gauge_stop() def tasklist_progress_bar(app, text, percent, elements, height=None, width=None, title=None, backtitle=None, colors=None): - d = Dialog() + dialog = Dialog() + dialog.autowidgetsize = True options = {'colors': colors} if height is not None: options['height'] = height @@ -63,7 +68,7 @@ def tasklist_progress_bar(app, text, percent, elements, height=None, width=None, elements_list = [(k, v) for k, v in elements.items()] try: - d.mixedgauge(text=text, percent=percent, elements=elements_list, **options) + dialog.mixedgauge(text=text, percent=percent, elements=elements_list, **options) except Exception as e: logging.debug(f"Error in mixedgauge: {e}") raise @@ -71,6 +76,7 @@ def tasklist_progress_bar(app, text, percent, elements, height=None, width=None, def input(app, question_text, height=None, width=None, init="", title=None, backtitle=None, colors=True): dialog = Dialog() + dialog.autowidgetsize = True options = {'colors': colors} if height is not None: options['height'] = height @@ -86,6 +92,7 @@ def input(app, question_text, height=None, width=None, init="", title=None, bac def password(app, question_text, height=None, width=None, init="", title=None, backtitle=None, colors=True): dialog = Dialog() + dialog.autowidgetsize = True options = {'colors': colors} if height is not None: options['height'] = height @@ -99,8 +106,10 @@ def password(app, question_text, height=None, width=None, init="", title=None, return code, password -def confirm(app, question_text, height=None, width=None, title=None, backtitle=None, colors=True): +def confirm(app, question_text, yes_label="Yes", no_label="No", + height=None, width=None, title=None, backtitle=None, colors=True): dialog = Dialog() + dialog.autowidgetsize = True options = {'colors': colors} if height is not None: options['height'] = height @@ -110,8 +119,8 @@ def confirm(app, question_text, height=None, width=None, title=None, backtitle=N options['title'] = title if backtitle is not None: options['backtitle'] = backtitle - check = dialog.yesno(question_text, **options) - return check + check = dialog.yesno(question_text, height, width, yes_label=yes_label, no_label=no_label, **options) + return check # Returns "ok" or "cancel" def directory_picker(app, path_dir, height=None, width=None, title=None, backtitle=None, colors=True): @@ -119,6 +128,7 @@ def directory_picker(app, path_dir, height=None, width=None, title=None, backtit try: dialog = Dialog() + dialog.autowidgetsize = True options = {'colors': colors} if height is not None: options['height'] = height @@ -141,6 +151,7 @@ def directory_picker(app, path_dir, height=None, width=None, title=None, backtit def menu(app, question_text, choices, height=None, width=None, menu_height=8, title=None, backtitle=None, colors=True): tag_to_description = {tag: description for tag, description in choices} dialog = Dialog(dialog="dialog") + dialog.autowidgetsize = True options = {'colors': colors} if title is not None: options['title'] = title @@ -160,6 +171,7 @@ def menu(app, question_text, choices, height=None, width=None, menu_height=8, ti def buildlist(app, text, items=[], height=None, width=None, list_height=None, title=None, backtitle=None, colors=True): # items is an interable of (tag, item, status) dialog = Dialog(dialog="dialog") + dialog.autowidgetsize = True options = {'colors': colors} if height is not None: options['height'] = height @@ -181,6 +193,7 @@ def buildlist(app, text, items=[], height=None, width=None, list_height=None, ti def checklist(app, text, items=[], height=None, width=None, list_height=None, title=None, backtitle=None, colors=True): # items is an iterable of (tag, item, status) dialog = Dialog(dialog="dialog") + dialog.autowidgetsize = True options = {'colors': colors} if height is not None: options['height'] = height diff --git a/tui_screen.py b/tui_screen.py index b3c70dd2..cae55bb3 100644 --- a/tui_screen.py +++ b/tui_screen.py @@ -130,6 +130,33 @@ def set_options(self, new_options): self.app.menu_options = new_options +class ConfirmScreen(MenuScreen): + def __init__(self, app, screen_id, queue, event, question, no_text, secondary, options=["Yes", "No"]): + super().__init__(app, screen_id, queue, event, question, options, + height=None, width=None, menu_height=8) + self.no_text = no_text + self.secondary = secondary + + def __str__(self): + return f"Curses Confirm Screen" + + def display(self): + self.stdscr.erase() + self.choice = tui_curses.MenuDialog( + self.app, + self.secondary + "\n" + self.question, + self.options + ).run() + if self.choice is not None and not self.choice == "" and not self.choice == "Processing": + config.current_option = 0 + config.current_page = 0 + if self.choice == "No": + logging.critical(self.no_text) + self.submit_choice_to_queue() + self.stdscr.noutrefresh() + curses.doupdate() + + class InputScreen(CursesScreen): def __init__(self, app, screen_id, queue, event, question, default): super().__init__(app, screen_id, queue, event) @@ -272,11 +299,14 @@ def display(self): self.submit_choice_to_queue() utils.send_task(self.app, "INSTALLING_PW") + class ConfirmDialog(DialogScreen): - def __init__(self, app, screen_id, queue, event, question, yes_label="Yes", no_label="No"): + def __init__(self, app, screen_id, queue, event, question, no_text, secondary, yes_label="Yes", no_label="No"): super().__init__(app, screen_id, queue, event) self.stdscr = self.app.get_menu_window() self.question = question + self.no_text = no_text + self.secondary = secondary self.yes_label = yes_label self.no_label = no_label @@ -286,7 +316,13 @@ def __str__(self): def display(self): if self.running == 0: self.running = 1 - _, _, self.choice = tui_dialog.confirm(self.app, self.question, self.yes_label, self.no_label) + self.choice = tui_dialog.confirm(self.app, self.secondary + self.question, + self.yes_label, self.no_label) + if self.choice == "cancel": + self.choice = self.no_label + logging.critical(self.no_text) + else: + self.choice = self.yes_label self.submit_choice_to_queue() def get_question(self):