; CATALOGUE DOCUMENTAIRE - Version 100% AutoHotkey v1 ; Interface graphique complète avec toutes les fonctionnalités ; Version: 1.0 - Projet complet en AHK v1 #NoEnv #SingleInstance Force #Persistent ; Variables globales CatalogueDir := A_ScriptDir . "\images" DataFile := A_ScriptDir . "\catalogue-data.txt" LogFile := A_ScriptDir . "\catalogue-log.txt" ListsDir := A_ScriptDir . "\lists" GouvernementsFile := ListsDir . "\gouvernements.txt" ClassesLotFile := ListsDir . "\classes_lot.txt" DocumentsKofaxFile := ListsDir . "\documents_kofax.txt" NextId := 1 CurrPreviewPath := "" ; Listes de choix (chargées depuis fichiers) Gouvernements := "" ClassesLot := "" DocumentsKofax := "" ; Variables pour les documents (simulation d'array) DocumentCount := 0 EditingDocId := 0 ; Créer le dossier images FileCreateDir, %CatalogueDir% ; Charger les données et les listes LoadData() LoadLists() ; Créer l'interface principale CreateMainGUI() return ; Sauvegarde à la fermeture du processus OnExit, AppExit AppExit: SaveData() ExitApp ; Créer l'interface graphique principale CreateMainGUI() { global Gouvernements, ClassesLot, DocumentsKofax global DocumentCount, DocumentsList, SearchBox, FilterGouvernement, FilterClasseLot, StatusBar global PreviewPic, PreviewName, CurrPreviewPath global hMain Gui, Main:New, +Resize +MaximizeBox +HwndhMain, Catalogue Documentaire ; Menu principal Menu, FileMenu, Add, &Nouveau Document, NewDocument Menu, FileMenu, Add, &Ouvrir Dossier Images, OpenImagesFolder Menu, FileMenu, Add Menu, FileMenu, Add, &Quitter, GuiClose Menu, EditMenu, Add, &Gestion des Listes, ManageLists Menu, EditMenu, Add, &Rechercher, ShowSearch Menu, HelpMenu, Add, &A Propos, About Menu, MainMenu, Add, &Fichier, :FileMenu Menu, MainMenu, Add, &Edition, :EditMenu Menu, MainMenu, Add, &Aide, :HelpMenu Gui, Main:Menu, MainMenu ; Barre d'outils Gui, Main:Add, Button, x10 y10 w100 h30 gNewDocument, Nouveau Gui, Main:Add, Button, x120 y10 w100 h30 gEditDocument, Modifier Gui, Main:Add, Button, x230 y10 w100 h30 gDeleteDocument, Supprimer Gui, Main:Add, Button, x340 y10 w100 h30 gShowSearch, Rechercher ; Zone de recherche Gui, Main:Add, Text, x10 y50 w80 h20, Recherche: Gui, Main:Add, Edit, x90 y48 w300 h20 vSearchBox gSearchDocuments Gui, Main:Add, Button, x400 y47 w80 h22 gClearSearch, Effacer ; Filtres Gui, Main:Add, Text, x10 y80 w80 h20, Gouvernement: Gui, Main:Add, DropDownList, x90 y78 w120 vFilterGouvernement gApplyFilters, Tous||%Gouvernements% Gui, Main:Add, Text, x220 y80 w80 h20, Classe Lot: Gui, Main:Add, DropDownList, x300 y78 w120 vFilterClasseLot gApplyFilters, Tous||%ClassesLot% ; Liste des documents Gui, Main:Add, ListView, x10 y110 w520 h400 vDocumentsList gDocumentSelect AltSubmit +ReadOnly, ID|Titre|Tag|Modele|Classe Lot|Gouvernement|Document Kofax|Fichier ; Aperçu image Gui, Main:Add, Text, x540 y110 w230 h20 Center, Apercu de l'image Gui, Main:Add, Picture, x540 y130 w230 h300 vPreviewPic gPreviewClick Border Gui, Main:Add, Text, x540 y440 w230 h20 vPreviewName Center, ; Barre de statut Gui, Main:Add, Text, x10 y520 w760 h20 vStatusBar, Pret - %DocumentCount% document(s) ; Initialiser recherche et filtres et afficher SearchBox := "" GuiControl, Main:, SearchBox GuiControl, Main:, FilterGouvernement, Tous GuiControl, Main:, FilterClasseLot, Tous RefreshDocumentsList() ; Afficher la fenêtre Gui, Main:Show, w780 h550 } ; Redimensionnement dynamique de la fenêtre principale MainGuiSize: if (A_EventInfo = 1) ; minimized return ; Marges et calculs margin := 10 ; Lire dimensions actuelles d'abord guiW := A_GuiWidth guiH := A_GuiHeight ; 60% / 40% split availW := guiW - margin*3 leftW := Floor(availW * 0.6) rightW := availW - leftW topListY := 110 bottomH := 40 ; leftW deja calcule if (leftW < 200) leftW := 200 listH := guiH - topListY - bottomH if (listH < 120) listH := 120 ; Déplacer/Redimensionner la liste GuiControl, Main:Move, DocumentsList, x%margin% y%topListY% w%leftW% h%listH% ; Zone d'aperçu à droite prevX := margin + leftW + margin prevW := rightW prevPicH := listH - 60 if (prevPicH < 100) prevPicH := 100 GuiControl, Main:Move, PreviewPic, x%prevX% y%topListY% w%prevW% h%prevPicH% nameY := topListY + prevPicH + 10 GuiControl, Main:Move, PreviewName, x%prevX% y%nameY% w%prevW% h20 ; Barre de statut statusY := guiH - 30 statusW := guiW - margin*2 GuiControl, Main:Move, StatusBar, x%margin% y%statusY% w%statusW% h20 return ; --------- Image utilities (keep aspect ratio) --------- GetImageSize(path, ByRef w, ByRef h) { static token := 0, lib := 0 if (!lib) { lib := DllCall("LoadLibrary", "Str", "gdiplus.dll", "Ptr") } if (!token) { VarSetCapacity(si, 16, 0) NumPut(1, si, 0, "UInt") ; GdiplusStartupInput.GdiplusVersion = 1 if (DllCall("gdiplus\\GdiplusStartup", "PtrP", token, "Ptr", &si, "Ptr", 0, "UInt") != 0) return false } pBitmap := 0 if (DllCall("gdiplus\\GdipCreateBitmapFromFile", "WStr", path, "PtrP", pBitmap, "UInt") != 0 || !pBitmap) return false DllCall("gdiplus\\GdipGetImageWidth", "Ptr", pBitmap, "UIntP", w) DllCall("gdiplus\\GdipGetImageHeight", "Ptr", pBitmap, "UIntP", h) DllCall("gdiplus\\GdipDisposeImage", "Ptr", pBitmap) return true } ShowPreviewImage(path) { global CurrPreviewPath ; Simple approach: just set the image directly GuiControl, Main:, PreviewPic, %path% CurrPreviewPath := path } ; Charger les données depuis le fichier LoadData() { global if (FileExist(DataFile)) { FileRead, FileContent, %DataFile% ; Parser les données ligne par ligne Loop, Parse, FileContent, `n, `r { if (A_LoopField != "") { DocumentCount++ ; Stocker chaque ligne de document Document%DocumentCount% := A_LoopField } } LogMessage("Donnees chargees depuis " . DataFile) } else { ; Créer des données d'exemple CreateExampleData() } } ; Charger les listes depuis des fichiers externes LoadLists() { global ListsDir, GouvernementsFile, ClassesLotFile, DocumentsKofaxFile global Gouvernements, ClassesLot, DocumentsKofax ; Créer le dossier lists s'il n'existe pas FileCreateDir, %ListsDir% ; Charger Gouvernements if (FileExist(GouvernementsFile)) { FileRead, content, %GouvernementsFile% Gouvernements := Trim(FormatList(content)) } else { Gouvernements := "Federal|Provincial|Municipal|International" FileAppend, Federal`nProvincial`nMunicipal`nInternational, %GouvernementsFile% } ; Charger Classes de Lot if (FileExist(ClassesLotFile)) { FileRead, content, %ClassesLotFile% ClassesLot := Trim(FormatList(content)) } else { ClassesLot := "Lot A|Lot B|Lot C|Lot D|Lot Special" FileAppend, Lot A`nLot B`nLot C`nLot D`nLot Special, %ClassesLotFile% } ; Charger Documents Kofax if (FileExist(DocumentsKofaxFile)) { FileRead, content, %DocumentsKofaxFile% DocumentsKofax := Trim(FormatList(content)) } else { DocumentsKofax := "Type 1|Type 2|Type 3|Type 4|Formulaire Standard" FileAppend, Type 1`nType 2`nType 3`nType 4`nFormulaire Standard, %DocumentsKofaxFile% } } ; Utilitaire: convertir un fichier multi-lignes en chaîne DDL séparée par | FormatList(byref text) { list := "" Loop, Parse, text, `n, `r { line := Trim(A_LoopField) if (line != "") { if (list != "") list .= "|" list .= line } } return list } ; Créer des données d'exemple CreateExampleData() { global DocumentCount, NextId DocumentCount := 3 ; Format: ID|Titre|Tag|Modele|ClasseLot|DocumentKofax|Gouvernement|Directives|Keywords|Filename Document1 := "1|Certificat de naissance|CERT001|1|Lot A|Type 1|Provincial|Verifier la signature officielle et le sceau gouvernemental.|naissance certificat etat civil registre|cert_naissance_001.svg" Document2 := "2|Passeport canadien|PASS001|1|Lot B|Type 2|Federal|Verifier la photo, la signature et les elements de securite.|passeport canada voyage identite photo|passeport_001.svg" Document3 := "3|Permis de conduire|PERM001|1|Lot A|Type 1|Provincial|Verifier la classe de permis et la date d'expiration.|permis conduire vehicule licence transport|permis_001.svg" NextId := 4 SaveData() } ; Sauvegarder les données SaveData() { global FileContent := "" Loop, %DocumentCount% { if (Document%A_Index% != "") { FileContent .= Document%A_Index% . "`n" } } FileDelete, %DataFile% FileAppend, %FileContent%, %DataFile% LogMessage("Donnees sauvegardees") } ; Rafraîchir la liste des documents RefreshDocumentsList() { global GuiControl, Main:-Redraw, DocumentsList LV_Delete() Loop, %DocumentCount% { if (Document%A_Index% != "") { ; Parser la ligne de document StringSplit, DocParts, Document%A_Index%, | if (DocParts0 >= 10) { LV_Add("", DocParts1, DocParts2, DocParts3, DocParts4, DocParts5, DocParts7, DocParts6, DocParts10) } } } ; Auto-redimensionner les colonnes Loop, 8 { LV_ModifyCol(A_Index, "AutoHdr") } GuiControl, Main:+Redraw, DocumentsList GuiControl, Main:, StatusBar, Pret - %DocumentCount% document(s) ; Auto-select first item and show its preview if (DocumentCount > 0) { LV_Modify(1, "Select") Gosub, DocumentSelect } } ; Nouveau document NewDocument: CreateDocumentForm() return ; Modifier document EditDocument: SelectedRow := LV_GetNext() if (SelectedRow = 0) { MsgBox, 48, Attention, Veuillez selectionner un document a modifier. return } LV_GetText(DocumentId, SelectedRow, 1) ; Trouver le document Loop, %DocumentCount% { StringSplit, DocParts, Document%A_Index%, | if (DocParts1 = DocumentId) { EditingDocId := DocumentId CreateDocumentForm(A_Index) break } } return ; Supprimer document DeleteDocument: SelectedRow := LV_GetNext() if (SelectedRow = 0) { MsgBox, 48, Attention, Veuillez selectionner un document a supprimer. return } LV_GetText(DocumentId, SelectedRow, 1) LV_GetText(DocumentTitle, SelectedRow, 2) MsgBox, 4, Confirmation, Voulez-vous vraiment supprimer le document "%DocumentTitle%" ? IfMsgBox Yes { ; Supprimer de la liste Loop, %DocumentCount% { StringSplit, DocParts, Document%A_Index%, | if (DocParts1 = DocumentId) { Document%A_Index% := "" break } } SaveData() RefreshDocumentsList() LogMessage("Document supprime: " . DocumentTitle) } return ; Sélection de document DocumentSelect: ; Debug: Show what event we got if (A_GuiEvent = "DoubleClick") { Gosub, EditDocument return } ; For any selection change, update preview SelRow := LV_GetNext() if (SelRow > 0) { LV_GetText(ImgFile, SelRow, 8) if (ImgFile != "") { FullPath := CatalogueDir . "\images\" . ImgFile if (FileExist(FullPath)) { ; Set the image directly GuiControl, Main:, PreviewPic, %FullPath% GuiControl, Main:, PreviewName, %ImgFile% CurrPreviewPath := FullPath } else { ; File not found - clear preview GuiControl, Main:, PreviewPic, GuiControl, Main:, PreviewName, CurrPreviewPath := "" } } else { ; No filename - clear preview GuiControl, Main:, PreviewPic, GuiControl, Main:, PreviewName, CurrPreviewPath := "" } } return ; Clic sur l'aperçu pour afficher en pleine grandeur PreviewClick: if (CurrPreviewPath != "" && FileExist(CurrPreviewPath)) { Gui, Full:New, +Resize +MaximizeBox +MinimizeBox, Apercu ; image en taille réelle; si elle dépasse, l'utilisateur pourra agrandir/maximiser la fenêtre Gui, Full:Add, Picture, vFullPic gFullPicClick, %CurrPreviewPath% Gui, Full:Show, AutoSize } return FullGuiSize: return FullPicClick: ; Fermer sur clic Gui, Full:Destroy return ; Recherche de documents SearchDocuments: Gosub, ApplyFilters return ; Appliquer les filtres ApplyFilters: Gui, Main:Submit, NoHide ; Filtrer les documents GuiControl, Main:-Redraw, DocumentsList LV_Delete() FilteredCount := 0 Loop, %DocumentCount% { if (Document%A_Index% != "") { StringSplit, DocParts, Document%A_Index%, | if (DocParts0 >= 10) { ; Filtre de recherche SearchMatch := true if (SearchBox != "") { SearchMatch := InStr(DocParts2, SearchBox) || InStr(DocParts3, SearchBox) || InStr(DocParts5, SearchBox) || InStr(DocParts7, SearchBox) || InStr(DocParts6, SearchBox) || InStr(DocParts9, SearchBox) } ; Filtre gouvernement GouvernementMatch := (FilterGouvernement = "Tous" || DocParts7 = FilterGouvernement) ; Filtre classe lot ClasseLotMatch := (FilterClasseLot = "Tous" || DocParts5 = FilterClasseLot) if (SearchMatch && GouvernementMatch && ClasseLotMatch) { LV_Add("", DocParts1, DocParts2, DocParts3, DocParts4, DocParts5, DocParts7, DocParts6, DocParts10) FilteredCount++ } } } } ; Auto-redimensionner les colonnes Loop, 8 { LV_ModifyCol(A_Index, "AutoHdr") } GuiControl, Main:+Redraw, DocumentsList GuiControl, Main:, StatusBar, %FilteredCount% document(s) affiche(s) sur %DocumentCount% ; Auto-select first item and show its preview if (FilteredCount > 0) { LV_Modify(1, "Select") Gosub, DocumentSelect } return ; Effacer la recherche ClearSearch: GuiControl, Main:, SearchBox GuiControl, Main:, FilterGouvernement, Tous GuiControl, Main:, FilterClasseLot, Tous RefreshDocumentsList() return ; Créer le formulaire de document CreateDocumentForm(EditIndex := 0) { global ClassesLot, Gouvernements, DocumentsKofax, EditingDocId global Titre, Modele, Tag, ClasseLot, Gouvernement, DocumentKofax, Filename, Directives, Keywords global DocPreviewPic IsEdit := (EditIndex > 0) FormTitle := IsEdit ? "Modifier le Document" : "Nouveau Document" if (hMain) { Gui, DocForm:New, +Owner%hMain% +LastFound, %FormTitle% } else { Gui, DocForm:New, +LastFound, %FormTitle% } ; Titre Gui, DocForm:Add, Text, x10 y10 w100 h20, Titre du document: Gui, DocForm:Add, Edit, x10 y30 w300 h20 vTitre ; Modèle et Tag Gui, DocForm:Add, Text, x10 y60 w50 h20, Modele: Gui, DocForm:Add, Edit, x10 y80 w50 h20 vModele Number Gui, DocForm:Add, Text, x80 y60 w100 h20, Tag (3-10 car): Gui, DocForm:Add, Edit, x80 y80 w100 h20 vTag Limit10 ; Classe de lot Gui, DocForm:Add, Text, x10 y110 w100 h20, Classe de lot: Gui, DocForm:Add, DropDownList, x10 y130 w150 vClasseLot, %ClassesLot% ; Gouvernement Gui, DocForm:Add, Text, x180 y110 w100 h20, Gouvernement: Gui, DocForm:Add, DropDownList, x180 y130 w150 vGouvernement, %Gouvernements% ; Document Kofax Gui, DocForm:Add, Text, x10 y160 w120 h20, Document Kofax: Gui, DocForm:Add, DropDownList, x10 y180 w200 vDocumentKofax, %DocumentsKofax% ; Fichier image Gui, DocForm:Add, Text, x10 y210 w100 h20, Fichier image: Gui, DocForm:Add, Edit, x10 y230 w250 h20 vFilename ReadOnly Gui, DocForm:Add, Button, x270 y229 w60 h22 gSelectImageFile, Parcourir Gui, DocForm:Add, Picture, x340 y210 w60 h40 vDocPreviewPic Border ; Directives Gui, DocForm:Add, Text, x10 y260 w200 h20, Directives de Validation: Gui, DocForm:Add, Edit, x10 y280 w400 h60 vDirectives Multi VScroll ; Keywords Gui, DocForm:Add, Text, x10 y350 w200 h20, Keywords (mots-cles): Gui, DocForm:Add, Edit, x10 y370 w400 h40 vKeywords Multi VScroll ; Boutons Gui, DocForm:Add, Button, x10 y420 w100 h30 gSaveDocument, Sauvegarder Gui, DocForm:Add, Button, x120 y420 w100 h30 gCancelDocument, Annuler ; Si c'est une modification, remplir les champs if (IsEdit) { StringSplit, DocParts, Document%EditIndex%, | if (DocParts0 >= 10) { GuiControl, DocForm:, Titre, %DocParts2% GuiControl, DocForm:, Modele, %DocParts4% GuiControl, DocForm:, Tag, %DocParts3% GuiControl, DocForm:, ClasseLot, %DocParts5% GuiControl, DocForm:, Gouvernement, %DocParts7% GuiControl, DocForm:, DocumentKofax, %DocParts6% GuiControl, DocForm:, Filename, %DocParts10% GuiControl, DocForm:, Directives, %DocParts8% GuiControl, DocForm:, Keywords, %DocParts9% ; Stocker l'ID pour la modification EditingDocId := DocParts1 } } else { GuiControl, DocForm:, Modele, 1 EditingDocId := 0 } Gui, DocForm:Show, w420 h460 } ; Sélectionner fichier image SelectImageFile: global CatalogueDir FileSelectFile, SelectedFile, 1, %CatalogueDir%, Selectionner un fichier image, Images (*.jpg; *.jpeg; *.png; *.gif; *.svg; *.tif; *.tiff; *.bmp; *.webp) if (SelectedFile != "") { ; Extraire juste le nom du fichier SplitPath, SelectedFile, FileName GuiControl, DocForm:, Filename, %FileName% ; Copier le fichier vers le dossier images si nécessaire DestPath := CatalogueDir . "\" . FileName if (SelectedFile != DestPath) { FileCopy, %SelectedFile%, %DestPath% if (ErrorLevel = 0) { LogMessage("Fichier copie: " . FileName) } } } return ; Sauvegarder le document SaveDocument: Gui, DocForm:Submit ; Validation if (Titre = "") { MsgBox, 48, Erreur, Le titre est obligatoire. return } if (Tag = "") { MsgBox, 48, Erreur, Le tag est obligatoire. return } if (ClasseLot = "") { MsgBox, 48, Erreur, La classe de lot est obligatoire. return } if (Gouvernement = "") { MsgBox, 48, Erreur, Le gouvernement est obligatoire. return } if (DocumentKofax = "") { MsgBox, 48, Erreur, Le document Kofax est obligatoire. return } if (Filename = "") { MsgBox, 48, Erreur, Le fichier image est obligatoire. return } ; Créer la ligne de document NewDocLine := EditingDocId . "|" . Titre . "|" . Tag . "|" . Modele . "|" . ClasseLot . "|" . DocumentKofax . "|" . Gouvernement . "|" . Directives . "|" . Keywords . "|" . Filename ; Créer ou modifier le document if (EditingDocId > 0) { ; Modification Loop, %DocumentCount% { StringSplit, DocParts, Document%A_Index%, | if (DocParts1 = EditingDocId) { Document%A_Index% := NewDocLine break } } LogMessage("Document modifie: " . Titre) } else { ; Nouveau document DocumentCount++ NewDocLine := NextId . "|" . Titre . "|" . Tag . "|" . Modele . "|" . ClasseLot . "|" . DocumentKofax . "|" . Gouvernement . "|" . Directives . "|" . Keywords . "|" . Filename Document%DocumentCount% := NewDocLine NextId++ LogMessage("Nouveau document cree: " . Titre) } SaveData() RefreshDocumentsList() Gui, DocForm:Destroy return ; Annuler le formulaire CancelDocument: DocFormClose: Gui, DocForm:Destroy return ; Gestion des listes ManageLists: MsgBox, 64, Gestion des Listes, Fonctionnalite en cours de developpement.`n`nPour modifier les listes, editez directement le script:`n- Gouvernements`n- ClassesLot`n- DocumentsKofax return ; Afficher la recherche ShowSearch: ; Focus sur la zone de recherche GuiControl, Main:Focus, SearchBox return ; Ouvrir le dossier images OpenImagesFolder: Run, explorer.exe "%CatalogueDir%" return ; À propos About: MsgBox, 64, A Propos, Catalogue Documentaire v1.0`n`nSysteme complet de catalogage de documents`nDeveloppe en AutoHotkey v1`n`nFonctionnalites:`n- Gestion complete des documents`n- Recherche et filtrage`n- Gestion des listes de choix`n- Interface graphique native return ; Fonction de logging LogMessage(Message) { FormatTime, CurrentTime, , yyyy-MM-dd HH:mm:ss LogEntry := CurrentTime . " - " . Message . "`n" FileAppend, %LogEntry%, %LogFile% } ; Fermeture de l'application MainGuiClose: GuiClose: ExitApp