<gh_stars>0
"""
###############################################################################
a "mixin" class for other frames: common methods for canned dialogs,
spawning programs, simple text viewers, etc; this class must be mixed
with a Frame (or a subclass derived from Frame) for its quit method
###############################################################################
"""

import glob
from pathlib import Path
from typing import Tuple
import tkinter.scrolledtext as tk_st
from tkinter.messagebox import *
from tkinter.filedialog import *


class GuiMixin:
    def infobox(self, title, text, *args):
        return showinfo(title, text)

    def errorbox(self, text):
        showerror("Errore!", text)

    def question(self, title, text, *args):
        return askyesno(title, text)  # return True or False

    def notdone(self):
        showerror("Non implementato", "Opzione non disponibile")

    def quit(self):
        ans = self.question("Verify quit", "Are you sure you want to quit?")
        if ans:
            Frame.quit(self)  # quit not recursive!

    def help(self):
        self.infobox("RTFM", "See figure 1...")

    def handbook(self, file_name: str) -> None:
        title: str = "Guida"
        self.browser(file_name, title)

    def select_openfile(self, file="", dir=".") -> str:
        return askopenfilename(initialdir=dir, initialfile=file)

    def select_savefile(self, file="", dir="."):
        return asksaveasfilename(initialfile=file, initialdir=dir)

    def select_folder(self, title="Seleziona una cartella", initialdir=os.getcwd()):
        return askdirectory(title=title, initialdir=initialdir)

    def clone(self, args=()):  # optional constructor args
        new = Toplevel()  # make new in-process version of me
        myclass = self.__class__  # instance's (lowest) class object
        myclass(new, *args)  # attach/run instance to new window

    def browser(self, filename: str, title: str) -> None:
        new = Toplevel()
        new.title(title)
        text = tk_st.ScrolledText(new, wrap=WORD, height=30, width=85)
        # text.config(font=14)
        text.pack(expand=YES, fill=BOTH)
        new.iconname("browser")
        text.insert("0.0", open(filename, "r", encoding="utf-8").read())
        text.config(state=DISABLED)

    def enter_openfile(self) -> Tuple[Path, Path]:
        win = Toplevel()
        win.geometry("+%d+%d" % (self.winfo_rootx() + 200, self.winfo_rooty() + 200))
        win.title("Seleziona sorgente e destinazione")
        file_label = "file di testo (CVS)"
        folder_label = "cartella di destinazione"
        label_width = max(len(file_label), len(folder_label))
        file_selection = self._form_row(
            win, label=file_label, open_function=self.select_openfile, width=label_width
        )
        folder_selection = self._form_row(
            win, label=folder_label, open_function=self.select_folder, width=label_width
        )
        Button(win, text="OK", command=win.destroy).pack()
        # TODO addo Abort button
        win.grab_set()
        win.focus_set()  # go modal: mouse grab, keyboard focus, wait
        win.wait_window()  # wait till destroy; else returns now
        return Path(file_selection.get()), Path(folder_selection.get())

    def _form_row(
        self, parent, label, open_function, width=15, browse=True, extend=False
    ):
        var = StringVar()
        row = Frame(parent)
        lab = Label(row, text=label, relief=RIDGE, width=width)
        ent = Entry(row, relief=SUNKEN, textvariable=var)
        row.pack(fill=X)  # uses packed row frames
        lab.pack(side=LEFT)  # and fixed-width labels
        ent.pack(side=LEFT, expand=YES, fill=X)  # or use grid(row, col)
        if browse:
            btn = Button(row, text="sfoglia ...")
            btn.pack(side=RIGHT)
            if not extend:
                btn.config(command=lambda: var.set(open_function() or var.get()))
            else:
                btn.config(command=lambda: var.set(var.get() + " " + open_function()))
        return var


"""
###############################################################################
Classes that encapsulate top-level interfaces.
Allows same GUI to be main, pop-up, or attached; content classes may inherit
from these directly, or be mixed together with them per usage mode; may also
be called directly without a subclass; designed to be mixed in after (further
to the right than) app-specific classes: else, subclass gets methods here
(destroy, okayToQuit), instead of from app-specific classes--can't redefine.
###############################################################################
"""


class _window:
    """
    mixin shared by main and pop-up windows
    """

    foundicon = None
    iconpatt = "*.ico"
    iconmine = "py.ico"

    def configBorders(self, app, kind, iconfile):
        if not iconfile:  # no icon passed?
            iconfile = self.findIcon()  # try curr,tool dirs
        title = app
        if kind:
            title += " - " + kind
        self.title(title)  # on window border
        self.iconname(app)  # when minimized
        if iconfile:
            try:
                self.iconbitmap(iconfile)  # window icon image
            except:  # bad py or platform
                pass
        self.protocol("WM_DELETE_WINDOW", self.quit)  # don't close silent

    def findIcon(self):
        if _window.foundicon:  # already found one?
            return _window.foundicon
        iconfile = None  # try curr dir first
        iconshere = glob.glob(self.iconpatt)  # assume just one
        if iconshere:  # del icon for red Tk
            iconfile = iconshere[0]
        else:  # try tools dir icon
            mymod = __import__(__name__)  # import self for dir
            path = __name__.split(".")  # poss a package path
            for mod in path[1:]:  # follow path to end
                mymod = getattr(mymod, mod)  # only have leftmost
            mydir = os.path.dirname(mymod.__file__)
            myicon = os.path.join(mydir, self.iconmine)  # use myicon, not tk
            if os.path.exists(myicon):
                iconfile = myicon
        _window.foundicon = iconfile  # don't search again
        return iconfile


class MainWindow(Tk, _window, GuiMixin):
    """
    when run in main top-level window
    """

    def __init__(self, app, kind="", iconfile=None):
        self.findIcon()
        Tk.__init__(self)
        self.__app = app
        self.configBorders(app, kind, iconfile)

    def quit(self):
        if self.okayToQuit():  # threads running?
            ans = self.question("Verifica uscita", "Sei sicuro di voler terminare?")
            if ans:
                self.destroy()  # quit whole app
        else:
            self.showinfo(self.__app, "Terminazione non permessa")

    def destroy(self):
        Tk.quit(self)  # redef if exit ops

    def okayToQuit(self):  # redef me if used
        return True  # e.g., thread busy

"""
###############################################################################
a "mixin" class for other frames: common methods for canned dialogs,
spawning programs, simple text viewers, etc; this class must be mixed
with a Frame (or a subclass derived from Frame) for its quit method
###############################################################################
"""

import glob
from pathlib import Path
from typing import Tuple
import tkinter.scrolledtext as tk_st
from tkinter.messagebox import *
from tkinter.filedialog import *


class GuiMixin:
    def infobox(self, title, text, *args):
        return showinfo(title, text)

    def errorbox(self, text):
        showerror("Errore!", text)

    def question(self, title, text, *args):
        return askyesno(title, text)  # return True or False

    def notdone(self):
        showerror("Non implementato", "Opzione non disponibile")

    def quit(self):
        ans = self.question("Verify quit", "Are you sure you want to quit?")
        if ans:
            Frame.quit(self)  # quit not recursive!

    def help(self):
        self.infobox("RTFM", "See figure 1...")

    def handbook(self, file_name: str) -> None:
        title: str = "Guida"
        self.browser(file_name, title)

    def select_openfile(self, file="", dir=".") -> str:
        return askopenfilename(initialdir=dir, initialfile=file)

    def select_savefile(self, file="", dir="."):
        return asksaveasfilename(initialfile=file, initialdir=dir)

    def select_folder(self, title="Seleziona una cartella", initialdir=os.getcwd()):
        return askdirectory(title=title, initialdir=initialdir)

    def clone(self, args=()):  # optional constructor args
        new = Toplevel()  # make new in-process version of me
        myclass = self.__class__  # instance's (lowest) class object
        myclass(new, *args)  # attach/run instance to new window

    def browser(self, filename: str, title: str) -> None:
        new = Toplevel()
        new.title(title)
        text = tk_st.ScrolledText(new, wrap=WORD, height=30, width=85)
        # text.config(font=14)
        text.pack(expand=YES, fill=BOTH)
        new.iconname("browser")
        text.insert("0.0", open(filename, "r", encoding="utf-8").read())
        text.config(state=DISABLED)

    def enter_openfile(self) -> Tuple[Path, Path]:
        win = Toplevel()
        win.geometry("+%d+%d" % (self.winfo_rootx() + 200, self.winfo_rooty() + 200))
        win.title("Seleziona sorgente e destinazione")
        file_label = "file di testo (CVS)"
        folder_label = "cartella di destinazione"
        label_width = max(len(file_label), len(folder_label))
        file_selection = self._form_row(
            win, label=file_label, open_function=self.select_openfile, width=label_width
        )
        folder_selection = self._form_row(
            win, label=folder_label, open_function=self.select_folder, width=label_width
        )
        Button(win, text="OK", command=win.destroy).pack()
        # TODO addo Abort button
        win.grab_set()
        win.focus_set()  # go modal: mouse grab, keyboard focus, wait
        win.wait_window()  # wait till destroy; else returns now
        return Path(file_selection.get()), Path(folder_selection.get())

    def _form_row(
        self, parent, label, open_function, width=15, browse=True, extend=False
    ):
        var = StringVar()
        row = Frame(parent)
        lab = Label(row, text=label, relief=RIDGE, width=width)
        ent = Entry(row, relief=SUNKEN, textvariable=var)
        row.pack(fill=X)  # uses packed row frames
        lab.pack(side=LEFT)  # and fixed-width labels
        ent.pack(side=LEFT, expand=YES, fill=X)  # or use grid(row, col)
        if browse:
            btn = Button(row, text="sfoglia ...")
            btn.pack(side=RIGHT)
            if not extend:
                btn.config(command=lambda: var.set(open_function() or var.get()))
            else:
                btn.config(command=lambda: var.set(var.get() + " " + open_function()))
        return var


"""
###############################################################################
Classes that encapsulate top-level interfaces.
Allows same GUI to be main, pop-up, or attached; content classes may inherit
from these directly, or be mixed together with them per usage mode; may also
be called directly without a subclass; designed to be mixed in after (further
to the right than) app-specific classes: else, subclass gets methods here
(destroy, okayToQuit), instead of from app-specific classes--can't redefine.
###############################################################################
"""


class _window:
    """
    mixin shared by main and pop-up windows
    """

    foundicon = None
    iconpatt = "*.ico"
    iconmine = "py.ico"

    def configBorders(self, app, kind, iconfile):
        if not iconfile:  # no icon passed?
            iconfile = self.findIcon()  # try curr,tool dirs
        title = app
        if kind:
            title += " - " + kind
        self.title(title)  # on window border
        self.iconname(app)  # when minimized
        if iconfile:
            try:
                self.iconbitmap(iconfile)  # window icon image
            except:  # bad py or platform
                pass
        self.protocol("WM_DELETE_WINDOW", self.quit)  # don't close silent

    def findIcon(self):
        if _window.foundicon:  # already found one?
            return _window.foundicon
        iconfile = None  # try curr dir first
        iconshere = glob.glob(self.iconpatt)  # assume just one
        if iconshere:  # del icon for red Tk
            iconfile = iconshere[0]
        else:  # try tools dir icon
            mymod = __import__(__name__)  # import self for dir
            path = __name__.split(".")  # poss a package path
            for mod in path[1:]:  # follow path to end
                mymod = getattr(mymod, mod)  # only have leftmost
            mydir = os.path.dirname(mymod.__file__)
            myicon = os.path.join(mydir, self.iconmine)  # use myicon, not tk
            if os.path.exists(myicon):
                iconfile = myicon
        _window.foundicon = iconfile  # don't search again
        return iconfile


class MainWindow(Tk, _window, GuiMixin):
    """
    when run in main top-level window
    """

    def __init__(self, app, kind="", iconfile=None):
        self.findIcon()
        Tk.__init__(self)
        self.__app = app
        self.configBorders(app, kind, iconfile)

    def quit(self):
        if self.okayToQuit():  # threads running?
            ans = self.question("Verifica uscita", "Sei sicuro di voler terminare?")
            if ans:
                self.destroy()  # quit whole app
        else:
            self.showinfo(self.__app, "Terminazione non permessa")

    def destroy(self):
        Tk.quit(self)  # redef if exit ops

    def okayToQuit(self):  # redef me if used
        return True  # e.g., thread busy
############################################################################### a "mixin" class for other frames: common methods for canned dialogs, spawning programs, simple text viewers, etc; this class must be mixed with a Frame (or a subclass derived from Frame) for its quit method ############################################################################### # return True or False # quit not recursive! # optional constructor args # make new in-process version of me # instance's (lowest) class object # attach/run instance to new window # text.config(font=14) # TODO addo Abort button # go modal: mouse grab, keyboard focus, wait # wait till destroy; else returns now # uses packed row frames # and fixed-width labels # or use grid(row, col) ############################################################################### Classes that encapsulate top-level interfaces. Allows same GUI to be main, pop-up, or attached; content classes may inherit from these directly, or be mixed together with them per usage mode; may also be called directly without a subclass; designed to be mixed in after (further to the right than) app-specific classes: else, subclass gets methods here (destroy, okayToQuit), instead of from app-specific classes--can't redefine. ############################################################################### mixin shared by main and pop-up windows # no icon passed? # try curr,tool dirs # on window border # when minimized # window icon image # bad py or platform # don't close silent # already found one? # try curr dir first # assume just one # del icon for red Tk # try tools dir icon # import self for dir # poss a package path # follow path to end # only have leftmost # use myicon, not tk # don't search again when run in main top-level window # threads running? # quit whole app # redef if exit ops # redef me if used # e.g., thread busy