# Annexe : Application - Bloc-notes

## Rôle et utilité
L'application `apps/blocnotes/app.py` est l'équivalent du célèbre "Notepad" sous Windows. C'est un éditeur de texte pur, minimaliste, permettant à l'utilisateur de prendre des notes rapides, de copier/coller du texte ou de lire de petits fichiers sans la lourdeur d'un traitement de texte complet.

## Implémentation technique

*   **Composant `tk.Text`** : Le cœur de l'application repose entièrement sur le composant multi-lignes standard de Tkinter. Il est configuré pour occuper tout l'espace disponible (`fill="both", expand=True`).
*   **Barre de défilement (Scrollbar)** : L'application instancie une `tk.Scrollbar` qu'elle lie manuellement au widget de texte (`yscrollcommand=scrollbar.set`), démontrant la façon standard de créer une zone défilante en Tkinter.
*   **Menu Fichier** : Contrairement aux applications modernes avec des barres de boutons massives, le bloc-notes utilise un `tk.Menu` classique (Fichier > Nouveau, Ouvrir, Enregistrer, Quitter), ce qui lui confère son aspect délicieusement rétro.
*   **Dialogue système** : Pour ouvrir et sauvegarder, l'application s'appuie sur `tkinter.filedialog`, ce qui permet d'utiliser la fenêtre de navigation native sans avoir à recoder un explorateur de fichiers interne.

## Pistes de modification

*   **Sauvegarde automatique** : Un développeur pourrait ajouter un mécanisme (via `window.after`) qui écrit discrètement le contenu du texte dans un fichier temporaire `/tmp/autosave.txt` toutes les minutes pour éviter les pertes de données.
*   **Statistiques en temps réel** : L'ajout d'une petite barre d'état (`tk.Label`) en bas de la fenêtre indiquant le nombre de mots et de caractères tapés transformerait cet outil basique en un outil plus orienté vers la rédaction.


## Code Source

```python
import tkinter as tk
from tkinter import filedialog, messagebox

import os

def start(window, app_manager=None, **kwargs):
    text_area = tk.Text(window, wrap="word", undo=True)
    text_area.pack(fill="both", expand=True)

    menu_frame = tk.Frame(window, bg="lightgray")
    menu_frame.pack(side="top", fill="x", before=text_area)
    
    current_filepath = kwargs.get("filepath")
    
    def update_title(modified=False):
        if hasattr(window, 'master') and window.master.__class__.__name__ == 'Window':
            title_text = current_filepath if current_filepath else "Sans titre"
            prefix = "* " if modified else ""
            window.master.title_label.config(text=f"Bloc-notes - {prefix}{title_text}")
            
    update_title()
    
    def on_modify(event=None):
        update_title(modified=text_area.edit_modified())
        
    text_area.bind("<<Modified>>", on_modify)
    
    def set_clean():
        text_area.edit_modified(False)
        
    if current_filepath and os.path.exists(current_filepath):
        try:
            with open(current_filepath, "r", encoding="utf-8") as f:
                text_area.insert(tk.END, f.read())
            set_clean()
        except Exception as e:
            messagebox.showerror("Erreur", f"Impossible d'ouvrir le fichier :\n{e}")

    def check_unsaved():
        if text_area.edit_modified():
            ans = messagebox.askyesnocancel("Attention", "Voulez-vous enregistrer les modifications ?")
            if ans is True:
                return save_file()
            elif ans is False:
                return True
            else:
                return False
        return True

    def new_file():
        if not check_unsaved(): return
        nonlocal current_filepath
        current_filepath = None
        text_area.delete(1.0, tk.END)
        set_clean()
        update_title()

    def open_file():
        if not check_unsaved(): return
        nonlocal current_filepath
        init_dir = os.path.dirname(current_filepath) if current_filepath else os.path.expanduser("~")
        filepath = filedialog.askopenfilename(initialdir=init_dir, defaultextension=".txt", filetypes=[("All Files", "*.*"), ("Text Files", "*.txt")])
        if not filepath:
            return
        try:
            with open(filepath, "r", encoding="utf-8") as f:
                content = f.read()
            text_area.delete(1.0, tk.END)
            text_area.insert(tk.END, content)
            current_filepath = filepath
            set_clean()
            update_title()
        except Exception as e:
            messagebox.showerror("Erreur", f"Impossible d'ouvrir :\n{e}")

    def save_file():
        nonlocal current_filepath
        if not current_filepath:
            return save_file_as()
        try:
            with open(current_filepath, "w", encoding="utf-8") as f:
                f.write(text_area.get(1.0, "end-1c"))
            set_clean()
            update_title()
            return True
        except Exception as e:
            messagebox.showerror("Erreur", f"Impossible de sauvegarder :\n{e}")
            return False

    def save_file_as():
        nonlocal current_filepath
        init_dir = os.path.dirname(current_filepath) if current_filepath else os.path.expanduser("~")
        filepath = filedialog.asksaveasfilename(initialdir=init_dir, defaultextension=".txt", filetypes=[("All Files", "*.*"), ("Text Files", "*.txt")])
        if not filepath:
            return False
        current_filepath = filepath
        return save_file()

    icon_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "icons")
    window.icons = {}
    for iname in ['menu_new_file', 'menu_open', 'menu_save', 'menu_save_as']:
        ipath = os.path.join(icon_dir, iname + ".png")
        if os.path.exists(ipath):
            window.icons[iname] = tk.PhotoImage(file=ipath)

    btn_new = tk.Button(menu_frame, text=" Nouveau", image=window.icons.get('menu_new_file'), compound="left", command=new_file, relief="flat")
    btn_new.pack(side="left", padx=2, pady=2)
    
    btn_open = tk.Button(menu_frame, text=" Ouvrir", image=window.icons.get('menu_open'), compound="left", command=open_file, relief="flat")
    btn_open.pack(side="left", padx=2, pady=2)
    
    btn_save = tk.Button(menu_frame, text=" Enregistrer", image=window.icons.get('menu_save'), compound="left", command=save_file, relief="flat")
    btn_save.pack(side="left", padx=2, pady=2)
    
    def show_help():
        help_text = (
            "Aide du Bloc-notes\n\n"
            "Raccourcis clavier disponibles :\n"
            "• Ctrl + N : Nouveau fichier\n"
            "• Ctrl + O : Ouvrir un fichier\n"
            "• Ctrl + S : Enregistrer\n"
            "• Ctrl + Shift + S : Enregistrer sous...\n"
            "• Ctrl + C : Copier la sélection\n"
            "• Ctrl + X : Couper la sélection\n"
            "• Ctrl + V : Coller\n"
            "• Ctrl + Z : Annuler la modification\n"
            "• Ctrl + Y : Refaire la modification\n\n"
            "Indicateur visuel :\n"
            "L'étoile (*) dans le titre signifie que le fichier contient des modifications non enregistrées."
        )
        messagebox.showinfo("Aide", help_text)

    btn_save_as = tk.Button(menu_frame, text=" Enregistrer sous...", image=window.icons.get('menu_save_as'), compound="left", command=save_file_as, relief="flat")
    btn_save_as.pack(side="left", padx=2, pady=2)
    
    btn_help = tk.Button(menu_frame, text=" ? Aide ", command=show_help, relief="flat", bg="lightblue")
    btn_help.pack(side="right", padx=5, pady=2)
    
    text_area.focus_set()
    
    text_area.bind("<Control-n>", lambda e: new_file() or "break")
    text_area.bind("<Control-o>", lambda e: open_file() or "break")
    text_area.bind("<Control-s>", lambda e: save_file() or "break")
    text_area.bind("<Control-S>", lambda e: save_file_as() or "break")
    
    # Lier le gestionnaire de fermeture si possible
    if hasattr(window, 'master') and window.master.__class__.__name__ == 'Window':
        window.master.on_close_callback = check_unsaved
```
