<reponame>AlanCristhian/colorwheel<filename>__main__.py
import tkinter as tk
from tkinter import ttk, filedialog, messagebox, font
import pathlib
import configparser
import itertools
import sys

import widgets
import file
import toolbar



if sys.platform == "win32":
    try:
        import ctypes
        PROCESS_SYSTEM_DPI_AWARE = 1
        shcore = ctypes.OleDLL("shcore")
        shcore.SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE)
    except (ImportError, AttributeError, OSError):
        pass



COUNTER = itertools.count().__next__
BASE = pathlib.Path()
SETTINGS = configparser.ConfigParser()
if (BASE/".config").exists():
    SETTINGS.read(".config")
else:
    SETTINGS["default"] = {
        "current_directory": str(BASE.home()),
        "opened_files": [],
    }


def wheel_name(text):
    name = pathlib.Path(text).resolve()
    parts = name.parent.parts
    if parts:
        return str(name.name)
    else:
        return "untitled-%s" % COUNTER()


class App(ttk.Frame):
    def __init__(self, master):
        super().__init__(master)
        self.grid(row=0, column=0)
        self.current_directory = SETTINGS["default"]["current_directory"]

        # create the toolbar
        self.toolbar = toolbar.ToolBar(master)
        self.toolbar.button_grid = {"pady": 8, "padx": (8, 0)}
        self.toolbar.append(
            name="new", label="Nuevo", command=self.new_wheel,
            image=BASE/"images"/"new.png")
        self.toolbar.append(
            name="open", label="Abrir", command=self.open_wheel,
            image=BASE/"images"/"open.png")
        self.toolbar.append(
            name="save", label="Guardar", command=self.save_changes,
            image=BASE/"images"/"save.png")
        self.toolbar.append(
            name="save_as", label="Guardar como", command=self.save_wheel,
            image=BASE/"images"/"save_as.png")
        self.toolbar.append(
            name="undo", label="Deshacer", command=self.undo,
            image=BASE/"images"/"undo.png")
        self.toolbar.append(
            name="redo", label="rehacer", command=self.redo,
            image=BASE/"images"/"redo.png")
        self.toolbar.grid(row=0, column=0, sticky=tk.W+tk.E)

        # create an inner frame to center the widgets
        self.notebook = widgets.ClosableNotebook(
            master=master,
            check_unsaved=True,
            confirm_close=lambda data: self.save_changes(index=data))
        self.notebook.grid(row=1, column=0, sticky=tk.W)

        self.set_events()
        if SETTINGS["default"]["opened_files"]:
            for i in SETTINGS["default"]["opened_files"].split(","):
                self.open_wheel(path=i)
        else:
            self.new_wheel()

    def new_wheel(self, event=None, name=None):
        tab_name = "⚫ untitled-%r" % COUNTER() if name is None \
                   else wheel_name(name)
        wheel = file.File(self.notebook)
        if name is None:
            wheel.temporary_name = tab_name
        wheel.grid(row=0, column=0)
        if name:
            wheel.file_path = str(name)
        self.notebook.add(wheel, text=tab_name)
        # set the focus in the new tab
        self.notebook.select(wheel)
        wheel.draw_default_wheel()
        wheel.saved = False
        widgets.update_title(self, wheel)
        return wheel

    def get_wheel_and_tab_id(self, index=None):
        tab_id = None
        if index is None:
            tab_id = self.notebook.select()
            index = self.notebook.index(tab_id)
        key = self.notebook.tabs()[index].split(".")[2]
        return self.notebook.children[key], tab_id

    def save_changes(self, event=None, index=None):
        wheel, tab_id = self.get_wheel_and_tab_id(index)
        if wheel.file_path:
            with open(wheel.file_path, "w") as wheel_file:
                wheel_file.write(
                    f"[wheel]\n"
                    f"number = {wheel.settings.number}\n"
                    f"start = {wheel.settings.start}\n"
                    f"saturation = {wheel.settings.saturation}\n"
                    f"luminosity = {wheel.settings.luminosity}\n"
                    f"background = {wheel.view.background}\n"
                    f"outline = {wheel.view.outline}\n"
                    f"color_space = {wheel.view.space}")
            wheel.saved = True
            text = self.notebook.tab(tab_id)["text"].replace("⚫ ", "")
            self.notebook.tab(tab_id, text=text)
        else:
            self.save_wheel(wheel=wheel, tab_id=tab_id)

    def save_wheel(self, event=None, wheel=None, tab_id=None):
        if wheel is None:
            wheel, tab_id = self.get_wheel_and_tab_id()

        dialog = filedialog.asksaveasfile(
            mode="w",
            defaultextension=".wheel",
            initialdir=self.current_directory,
            filetypes=[("color wheel", ".wheel")])

        if dialog is not None:
            dialog.write(
                f"[wheel]\n"
                f"number = {wheel.settings.number}\n"
                f"start = {wheel.settings.start}\n"
                f"saturation = {wheel.settings.saturation}\n"
                f"luminosity = {wheel.settings.luminosity}\n"
                f"background = {wheel.view.background}\n"
                f"outline = {wheel.view.outline}\n"
                f"color_space = {wheel.view.space}")
            wheel.saved = True

            wheel_path = pathlib.Path(dialog.name)
            self.current_directory = str(wheel_path.parent)
            SETTINGS["default"]["current_directory"] = self.current_directory
            self.file_path = dialog.name
            dialog.close()
            wheel.file_path = str(wheel_path)
            new_text = wheel_path.name
            wheel.focus_set()
            tab_id = self.notebook.select()
            self.notebook.tab(str(tab_id), text=str(new_text))
            widgets.update_title(self, wheel)

    def open_wheel(self, event=None, path=None):
        if path:
            settings = configparser.ConfigParser()
            settings.read(path)
            wheel = self.new_wheel(name=path)
        else:
            dialog = filedialog.askopenfile(
                mode="r",
                defaultextension=".wheel",
                initialdir=self.current_directory,
                filetypes=[("color wheel", ".wheel")])
            if dialog is not None:
                settings = configparser.ConfigParser()
                settings.read(dialog.name)
                text = wheel_name(dialog.name)
                wheel = self.new_wheel(name=text)
                wheel.file_path = dialog.name
                dialog.close()
            else:
                return
        if "wheel" in settings:
            wheel.settings.number = settings["wheel"]["number"]
            wheel.settings.start = settings["wheel"]["start"]
            wheel.settings.saturation = settings["wheel"]["saturation"]
            wheel.settings.luminosity = settings["wheel"]["luminosity"]
            wheel.view.background = settings["wheel"]["background"]
            wheel.view.outline = settings["wheel"]["outline"]
            wheel.view.space = settings["wheel"]["color_space"]
            wheel.draw_wheel()
            wheel.focus_set()
            tab_id = self.notebook.select()
            text = self.notebook.tab(tab_id)["text"].replace("⚫ ", "")
            self.notebook.tab(tab_id, text=text)
        wheel.saved = True

    def undo(self, event=None):
        wheel, tab_id = self.get_wheel_and_tab_id()
        wheel.history.prev()
        data = wheel.history[wheel.history.cursor]
        wheel.settings.number = data.number
        wheel.settings.start = data.start
        wheel.settings.saturation = data.saturation
        wheel.settings.luminosity = data.luminosity
        wheel.view.background = data.background
        wheel.view.outline = data.outline
        wheel.view.space = data.color_space
        wheel.update_history = False
        wheel.draw_wheel()
        wheel.update_history = True

    def redo(self, event=None):
        wheel, tab_id = self.get_wheel_and_tab_id()
        wheel.history.next()
        data = wheel.history[wheel.history.cursor]
        wheel.settings.number = data.number
        wheel.settings.start = data.start
        wheel.settings.saturation = data.saturation
        wheel.settings.luminosity = data.luminosity
        wheel.view.background = data.background
        wheel.view.outline = data.outline
        wheel.view.space = data.color_space
        wheel.update_history = False
        wheel.draw_wheel()
        wheel.update_history = True

    def save_config(self):
        opened = []
        for tab in self.notebook.tabs():
            key = tab.split(".")[2]
            wheel = self.notebook.children[key]
            wheel.focus_set()
            tab_id = self.notebook.select()
            index = self.notebook.index(tab_id)
            if wheel.file_path:
                opened.append(wheel.file_path)
            if not wheel.saved:
                file_id = wheel.file_path if wheel.file_path\
                          else "este documento sin título"
                response = messagebox.askyesnocancel(
                    title="Grupal",
                    message=f"¿Desea guardar los cambios en {file_id} "
                            f"antes de cerrar?",
                    detail="Si cierra sin guardar se "
                           "perderán los cambios realizados",
                    icon="warning")
                if response is None:
                    break
                elif response is True:
                    self.save_changes(index=index)
                    self.notebook.forget(index)
                else:
                    self.notebook.forget(index)
        else:
            SETTINGS["default"]["opened_files"] = ",".join(opened)

            with open(BASE/".config", "w") as settings_file:
                SETTINGS.write(settings_file)
            self.master.destroy()

    def mark_as_unsaved(self, event):
        if not event.widget.saved:
            tab_id = self.notebook.select()
            if tab_id:
                text = self.notebook.tab(tab_id)["text"]
                if not "⚫ " in text:
                    self.notebook.tab(tab_id, text="⚫ " + text)

    def set_events(self):
        self.master.bind_all("<Control-o>", self.open_wheel)
        self.master.bind_all("<Control-n>", self.new_wheel)
        self.master.bind_all("<Control-s>", self.save_changes)
        self.master.bind_all("<Control-Shift-Key-S>", self.save_wheel)
        self.master.bind_all("<Control-Key-z>", self.undo)
        self.master.bind_all("<Control-Shift-Key-Z>", self.redo)
        self.master.bind_all("<Control-Key-y>", self.redo)
        self.master.protocol("WM_DELETE_WINDOW", self.save_config)
        if self.master._windowingsystem == 'x11':
            # Pasting from the clipboard doesn't delete the current selection
            # on X11.
            # See issue #5124.
            for cls in 'Text', 'Entry', 'Spinbox':
                self.master.bind_class(cls, '<<Paste>>',
                                'catch {%W delete sel.first sel.last}\n' +
                                self.master.bind_class(cls, '<<Paste>>'))
        self.master.bind_all("<<WheelDrawed>>", self.mark_as_unsaved)


if __name__ == '__main__':
    master = tk.Tk()
    app = App(master)
    master.mainloop()

import tkinter as tk
from tkinter import ttk, filedialog, messagebox, font
import pathlib
import configparser
import itertools
import sys

import widgets
import file
import toolbar



if sys.platform == "win32":
    try:
        import ctypes
        PROCESS_SYSTEM_DPI_AWARE = 1
        shcore = ctypes.OleDLL("shcore")
        shcore.SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE)
    except (ImportError, AttributeError, OSError):
        pass



COUNTER = itertools.count().__next__
BASE = pathlib.Path()
SETTINGS = configparser.ConfigParser()
if (BASE/".config").exists():
    SETTINGS.read(".config")
else:
    SETTINGS["default"] = {
        "current_directory": str(BASE.home()),
        "opened_files": [],
    }


def wheel_name(text):
    name = pathlib.Path(text).resolve()
    parts = name.parent.parts
    if parts:
        return str(name.name)
    else:
        return "untitled-%s" % COUNTER()


class App(ttk.Frame):
    def __init__(self, master):
        super().__init__(master)
        self.grid(row=0, column=0)
        self.current_directory = SETTINGS["default"]["current_directory"]

        # create the toolbar
        self.toolbar = toolbar.ToolBar(master)
        self.toolbar.button_grid = {"pady": 8, "padx": (8, 0)}
        self.toolbar.append(
            name="new", label="Nuevo", command=self.new_wheel,
            image=BASE/"images"/"new.png")
        self.toolbar.append(
            name="open", label="Abrir", command=self.open_wheel,
            image=BASE/"images"/"open.png")
        self.toolbar.append(
            name="save", label="Guardar", command=self.save_changes,
            image=BASE/"images"/"save.png")
        self.toolbar.append(
            name="save_as", label="Guardar como", command=self.save_wheel,
            image=BASE/"images"/"save_as.png")
        self.toolbar.append(
            name="undo", label="Deshacer", command=self.undo,
            image=BASE/"images"/"undo.png")
        self.toolbar.append(
            name="redo", label="rehacer", command=self.redo,
            image=BASE/"images"/"redo.png")
        self.toolbar.grid(row=0, column=0, sticky=tk.W+tk.E)

        # create an inner frame to center the widgets
        self.notebook = widgets.ClosableNotebook(
            master=master,
            check_unsaved=True,
            confirm_close=lambda data: self.save_changes(index=data))
        self.notebook.grid(row=1, column=0, sticky=tk.W)

        self.set_events()
        if SETTINGS["default"]["opened_files"]:
            for i in SETTINGS["default"]["opened_files"].split(","):
                self.open_wheel(path=i)
        else:
            self.new_wheel()

    def new_wheel(self, event=None, name=None):
        tab_name = "⚫ untitled-%r" % COUNTER() if name is None \
                   else wheel_name(name)
        wheel = file.File(self.notebook)
        if name is None:
            wheel.temporary_name = tab_name
        wheel.grid(row=0, column=0)
        if name:
            wheel.file_path = str(name)
        self.notebook.add(wheel, text=tab_name)
        # set the focus in the new tab
        self.notebook.select(wheel)
        wheel.draw_default_wheel()
        wheel.saved = False
        widgets.update_title(self, wheel)
        return wheel

    def get_wheel_and_tab_id(self, index=None):
        tab_id = None
        if index is None:
            tab_id = self.notebook.select()
            index = self.notebook.index(tab_id)
        key = self.notebook.tabs()[index].split(".")[2]
        return self.notebook.children[key], tab_id

    def save_changes(self, event=None, index=None):
        wheel, tab_id = self.get_wheel_and_tab_id(index)
        if wheel.file_path:
            with open(wheel.file_path, "w") as wheel_file:
                wheel_file.write(
                    f"[wheel]\n"
                    f"number = {wheel.settings.number}\n"
                    f"start = {wheel.settings.start}\n"
                    f"saturation = {wheel.settings.saturation}\n"
                    f"luminosity = {wheel.settings.luminosity}\n"
                    f"background = {wheel.view.background}\n"
                    f"outline = {wheel.view.outline}\n"
                    f"color_space = {wheel.view.space}")
            wheel.saved = True
            text = self.notebook.tab(tab_id)["text"].replace("⚫ ", "")
            self.notebook.tab(tab_id, text=text)
        else:
            self.save_wheel(wheel=wheel, tab_id=tab_id)

    def save_wheel(self, event=None, wheel=None, tab_id=None):
        if wheel is None:
            wheel, tab_id = self.get_wheel_and_tab_id()

        dialog = filedialog.asksaveasfile(
            mode="w",
            defaultextension=".wheel",
            initialdir=self.current_directory,
            filetypes=[("color wheel", ".wheel")])

        if dialog is not None:
            dialog.write(
                f"[wheel]\n"
                f"number = {wheel.settings.number}\n"
                f"start = {wheel.settings.start}\n"
                f"saturation = {wheel.settings.saturation}\n"
                f"luminosity = {wheel.settings.luminosity}\n"
                f"background = {wheel.view.background}\n"
                f"outline = {wheel.view.outline}\n"
                f"color_space = {wheel.view.space}")
            wheel.saved = True

            wheel_path = pathlib.Path(dialog.name)
            self.current_directory = str(wheel_path.parent)
            SETTINGS["default"]["current_directory"] = self.current_directory
            self.file_path = dialog.name
            dialog.close()
            wheel.file_path = str(wheel_path)
            new_text = wheel_path.name
            wheel.focus_set()
            tab_id = self.notebook.select()
            self.notebook.tab(str(tab_id), text=str(new_text))
            widgets.update_title(self, wheel)

    def open_wheel(self, event=None, path=None):
        if path:
            settings = configparser.ConfigParser()
            settings.read(path)
            wheel = self.new_wheel(name=path)
        else:
            dialog = filedialog.askopenfile(
                mode="r",
                defaultextension=".wheel",
                initialdir=self.current_directory,
                filetypes=[("color wheel", ".wheel")])
            if dialog is not None:
                settings = configparser.ConfigParser()
                settings.read(dialog.name)
                text = wheel_name(dialog.name)
                wheel = self.new_wheel(name=text)
                wheel.file_path = dialog.name
                dialog.close()
            else:
                return
        if "wheel" in settings:
            wheel.settings.number = settings["wheel"]["number"]
            wheel.settings.start = settings["wheel"]["start"]
            wheel.settings.saturation = settings["wheel"]["saturation"]
            wheel.settings.luminosity = settings["wheel"]["luminosity"]
            wheel.view.background = settings["wheel"]["background"]
            wheel.view.outline = settings["wheel"]["outline"]
            wheel.view.space = settings["wheel"]["color_space"]
            wheel.draw_wheel()
            wheel.focus_set()
            tab_id = self.notebook.select()
            text = self.notebook.tab(tab_id)["text"].replace("⚫ ", "")
            self.notebook.tab(tab_id, text=text)
        wheel.saved = True

    def undo(self, event=None):
        wheel, tab_id = self.get_wheel_and_tab_id()
        wheel.history.prev()
        data = wheel.history[wheel.history.cursor]
        wheel.settings.number = data.number
        wheel.settings.start = data.start
        wheel.settings.saturation = data.saturation
        wheel.settings.luminosity = data.luminosity
        wheel.view.background = data.background
        wheel.view.outline = data.outline
        wheel.view.space = data.color_space
        wheel.update_history = False
        wheel.draw_wheel()
        wheel.update_history = True

    def redo(self, event=None):
        wheel, tab_id = self.get_wheel_and_tab_id()
        wheel.history.next()
        data = wheel.history[wheel.history.cursor]
        wheel.settings.number = data.number
        wheel.settings.start = data.start
        wheel.settings.saturation = data.saturation
        wheel.settings.luminosity = data.luminosity
        wheel.view.background = data.background
        wheel.view.outline = data.outline
        wheel.view.space = data.color_space
        wheel.update_history = False
        wheel.draw_wheel()
        wheel.update_history = True

    def save_config(self):
        opened = []
        for tab in self.notebook.tabs():
            key = tab.split(".")[2]
            wheel = self.notebook.children[key]
            wheel.focus_set()
            tab_id = self.notebook.select()
            index = self.notebook.index(tab_id)
            if wheel.file_path:
                opened.append(wheel.file_path)
            if not wheel.saved:
                file_id = wheel.file_path if wheel.file_path\
                          else "este documento sin título"
                response = messagebox.askyesnocancel(
                    title="Grupal",
                    message=f"¿Desea guardar los cambios en {file_id} "
                            f"antes de cerrar?",
                    detail="Si cierra sin guardar se "
                           "perderán los cambios realizados",
                    icon="warning")
                if response is None:
                    break
                elif response is True:
                    self.save_changes(index=index)
                    self.notebook.forget(index)
                else:
                    self.notebook.forget(index)
        else:
            SETTINGS["default"]["opened_files"] = ",".join(opened)

            with open(BASE/".config", "w") as settings_file:
                SETTINGS.write(settings_file)
            self.master.destroy()

    def mark_as_unsaved(self, event):
        if not event.widget.saved:
            tab_id = self.notebook.select()
            if tab_id:
                text = self.notebook.tab(tab_id)["text"]
                if not "⚫ " in text:
                    self.notebook.tab(tab_id, text="⚫ " + text)

    def set_events(self):
        self.master.bind_all("<Control-o>", self.open_wheel)
        self.master.bind_all("<Control-n>", self.new_wheel)
        self.master.bind_all("<Control-s>", self.save_changes)
        self.master.bind_all("<Control-Shift-Key-S>", self.save_wheel)
        self.master.bind_all("<Control-Key-z>", self.undo)
        self.master.bind_all("<Control-Shift-Key-Z>", self.redo)
        self.master.bind_all("<Control-Key-y>", self.redo)
        self.master.protocol("WM_DELETE_WINDOW", self.save_config)
        if self.master._windowingsystem == 'x11':
            # Pasting from the clipboard doesn't delete the current selection
            # on X11.
            # See issue #5124.
            for cls in 'Text', 'Entry', 'Spinbox':
                self.master.bind_class(cls, '<<Paste>>',
                                'catch {%W delete sel.first sel.last}\n' +
                                self.master.bind_class(cls, '<<Paste>>'))
        self.master.bind_all("<<WheelDrawed>>", self.mark_as_unsaved)


if __name__ == '__main__':
    master = tk.Tk()
    app = App(master)
    master.mainloop()
# create the toolbar # create an inner frame to center the widgets # set the focus in the new tab # Pasting from the clipboard doesn't delete the current selection # on X11. # See issue #5124.