# coding: utf-8
# python 2 only

# Copyright (c) 2026 TormachTips.com. All rights reserved.
# Licensed under the TormachTips Personal Use License.
# Permission is granted only for private personal use and private personal modification.
# No sharing, publication, distribution, resale, sublicensing, screenshots, code excerpts,
# benchmarks, or videos are permitted without prior written permission.
# Requests:         tormach.1100m@gmail.com
# Information page: https://tormachtips.com/plugins.htm

#############################################
##                                         ##
##      No Hidden Notebook Tabs 0.95       ##
##          www.tormachtips.com            ##
##                                         ##
#############################################

# 0.95 - Public beta. Disables PathPilot notebook tab hiding during Cycle Start. - 5/12/2026

import os
import gtk
import glib
import constants
import singletons
from ui_hooks import plugin

CURRENT_VER                  = "0.95"
SCRIPT_NAME                  = "No Hidden Notebook Tabs"
DESCRIPTION                  = "Prevents PathPilot from hiding notebook tabs when Cycle Start is pressed."
ENABLED                      = 1
DEV_MACHINE                  = 1
DEV_MACHINE_FLAG             = "/home/operator/gcode/python/dev_machine.txt"
INSTALL_RETRY_MS             = 500
INSTALL_TIMEOUT_MS           = 30000
WATCHDOG_MS                  = 3000

class UserPlugin(plugin):
    def __init__(self):
        plugin.__init__(self, '%s %s' % (SCRIPT_NAME, CURRENT_VER))
        dev_machine_found = os.path.exists(DEV_MACHINE_FLAG)
        if dev_machine_found:
            plugin_enabled = DEV_MACHINE
        else:
            plugin_enabled = ENABLED
        self.install_elapsed_ms = 0
        self.ui = None
        self._installed_function = None
        if plugin_enabled:
            self.write_status("Loaded. Waiting for PathPilot UI.")
            glib.timeout_add(INSTALL_RETRY_MS, self.try_install)
            return
        if dev_machine_found:
            self.write_status("Dev machine found. Plugin loaded, but disabled by DEV_MACHINE.")
        else:
            self.write_status("Plugin loaded, but disabled.")
            self.write_status("To enable, open script, find ENABLED = 0 and change to ENABLED = 1.")
    
    def write_status(self, message, level=constants.ALARM_LEVEL_QUIET):
        try:
            self.error_handler.write("[%s] %s" % (SCRIPT_NAME, message), level)
        except:
            pass
    
    def try_install(self):
        self.install_elapsed_ms += INSTALL_RETRY_MS
        try:
            ui = getattr(singletons, "g_Machine", None)
            if ui is None:
                if self.install_elapsed_ms >= INSTALL_TIMEOUT_MS:
                    self.write_status("Timed out waiting for PathPilot UI.", constants.ALARM_LEVEL_LOW)
                    return False
                return True
            if not hasattr(ui, "notebook"):
                if self.install_elapsed_ms >= INSTALL_TIMEOUT_MS:
                    self.write_status("PathPilot UI found, but notebook was not available.", constants.ALARM_LEVEL_LOW)
                    return False
                return True
            if not hasattr(ui, "hide_notebook_tabs"):
                self.write_status("PathPilot UI has no hide_notebook_tabs method.", constants.ALARM_LEVEL_LOW)
                return False
            self.install_patch(ui)
            glib.timeout_add(WATCHDOG_MS, self.watchdog)
            return False
        except Exception as e:
            self.write_status("Install error: %s" % str(e), constants.ALARM_LEVEL_LOW)
            return False
    
    def install_patch(self, ui):
        self.ui = ui
        if hasattr(ui, "_beagle_no_hidden_tabs_installed"):
            if ui._beagle_no_hidden_tabs_installed:
                self.write_status("Already installed.")
                return
        try:
            ui._beagle_no_hidden_tabs_original_hide = ui.hide_notebook_tabs
        except:
            pass
        def beagle_hide_notebook_tabs_replacement():
            self.show_notebook_tabs()
        ui.hide_notebook_tabs = beagle_hide_notebook_tabs_replacement
        ui._beagle_no_hidden_tabs_installed = True
        self._installed_function = beagle_hide_notebook_tabs_replacement
        self.show_notebook_tabs()
        self.write_status("Installed. Cycle Start will no longer hide notebook tabs.", constants.ALARM_LEVEL_MEDIUM)
    
    def watchdog(self):
        try:
            ui = self.ui or getattr(singletons, "g_Machine", None)
            if ui is None:
                return True
            self.show_notebook_tabs()
            return True
        except Exception as e:
            self.write_status("Watchdog error: %s" % str(e), constants.ALARM_LEVEL_LOW)
            return True
    
    def show_notebook_tabs(self):
        ui = self.ui or getattr(singletons, "g_Machine", None)
        if ui is None or not hasattr(ui, "notebook"):
            return
        try:
            if hasattr(ui, "show_enabled_notebook_tabs"):
                ui.show_enabled_notebook_tabs()
            else:
                self.show_known_runtime_pages(ui)
        except:
            pass
        try:
            if hasattr(ui, "_always_visible_plugin_pages"):
                for page in ui._always_visible_plugin_pages:
                    try:
                        page.show()
                    except:
                        pass
        except:
            pass
    def show_known_runtime_pages(self, ui):
        known_page_ids = (
            "notebook_main_fixed",
            "notebook_file_util_wrapping_eventbox",
            "notebook_settings_fixed",
            "notebook_offsets_fixed",
            "conversational_fixed",
            "alarms_fixed",
        )
        try:
            page_count = ui.notebook.get_n_pages()
            for i in range(0, page_count):
                page = ui.notebook.get_nth_page(i)
                if not page:
                    continue
                page_id = ""
                try:
                    page_id = gtk.Buildable.get_name(page)
                except:
                    pass
                if page_id in known_page_ids:
                    page.show()
        except:
            pass            

DESCRIPTION_LONG = """Prevents PathPilot from hiding valid notebook tabs after Cycle Start.
    Normally, when a program starts, PathPilot hides most notebook pages and leaves
    only the Main and Status pages visible. This plugin replaces that runtime
    behavior so PathPilot restores the notebook pages it considers enabled for the
    current machine configuration instead.

    The plugin does not edit OEM files. It waits for the PathPilot UI to load, then
    replaces the live hide_notebook_tabs method for the current session. On each
    startup, the plugin installs that runtime replacement again.

    When available, the plugin uses PathPilot's own show_enabled_notebook_tabs method
    so machine-specific pages such as ATC, scanner, injector, probe, rack tool
    changer, lathe, or router pages are only shown when PathPilot itself considers
    them valid for that machine. The fallback list is conservative and is only used
    if that OEM method is not present.

    Runtime plugin pages registered through _always_visible_plugin_pages, such as
    the Load Meter Graph tab, are also kept visible."""