From 6efa8ecf5c523176e9781a2fe7d3edda8608a6d3 Mon Sep 17 00:00:00 2001 From: Thyphoon Date: Wed, 27 Aug 2025 22:28:12 +0200 Subject: [PATCH] new version --- main.pb | 33 +- main2.pb | 1008 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1023 insertions(+), 18 deletions(-) create mode 100644 main2.pb diff --git a/main.pb b/main.pb index 7e02e3d..a9d49a0 100644 --- a/main.pb +++ b/main.pb @@ -2465,7 +2465,7 @@ Procedure InitGadget() ; Le bouton "Enregistrer" actif si au moins un champ non vide DisableGadget(#GdtBtnSaveCfg, Bool(userName$ = "" And userEmail$ = "")) - + EndProcedure Macro RightGadget(GDT) @@ -2499,7 +2499,7 @@ Procedure OpenGUI() End EndIf ; --- Dimensions générales --- - #WinW = 950 + #WinW = 1280 #WinH = 720 ; plus haut pour la zone d’aide #PanelW = 920 #InPad = 10 ; marge interne au Panel @@ -2551,11 +2551,12 @@ Procedure OpenGUI() TextGadget(#GdtLblAction, #InPad, DownGadget(#GdtLblRemoteStatus)+#InMargin, 50, 20, "Action :") TextGadget(#GdtTxtAction, RightGadget(#GdtLblAction), GadgetY(#GdtLblAction), 300, 20, "-", #PB_Text_Border) CloseGadgetList() + ; ---- Cadre "Fichiers & modifications" ---- Define yFiles = DownGadget(#GdtFrmRemote)+ #InPad Define hFiles = PanelH - yFiles - #InPad FrameGadget(#GdtFrmFiles, #InPad, yFiles, #PanelW - 2*#InPad, hFiles, T("GdtFrmFiles","Fichiers & modifications"),#PB_Frame_Container) - ListIconGadget(#GdtListStatus, #InPad, #InFrmY, GadgetWidth(#GdtFrmFiles)-2*#InPad, 120, T("GdtListStatus-Path","Path"), 300, #PB_ListIcon_CheckBoxes | #PB_ListIcon_MultiSelect | #PB_ListIcon_FullRowSelect |#PB_ListIcon_AlwaysShowSelection) + ListIconGadget(#GdtListStatus, #InPad, #InFrmY, GadgetWidth(#GdtFrmFiles)-2*#InPad, hFiles-#BtnH*2-#InMargin*4-#InFrmY, T("GdtListStatus-Path","Path"), 300, #PB_ListIcon_CheckBoxes | #PB_ListIcon_MultiSelect | #PB_ListIcon_FullRowSelect |#PB_ListIcon_AlwaysShowSelection) AddGadgetColumn(#GdtListStatus, 1, T("GdtListStatus-Status","Status"), 50) AddGadgetColumn(#GdtListStatus, 2, T("GdtListStatus-Desc","Description"), 300) @@ -2567,7 +2568,7 @@ Procedure OpenGUI() ButtonGadget(#GdtBtnIgnore, #InPad + 370, yLocalActions, 110, #BtnH, T("GdtBtnIgnore","Ignorer")) ; Message de commit - Define yMsg = yLocalActions + #BtnH + 10 + Define yMsg = DownGadget(#GdtBtnRestore)+#InMargin TextGadget(#GdtLblMessage, #InPad + 10, yMsg + 4, 80, 22, T("GdtLblMessage","Message :")) StringGadget(#GdtFieldMessage, #InPad + 95, yMsg, #PanelW - 2*#InPad - 95 - 110, #BtnH, "") ButtonGadget(#GdtBtnCommit, #PanelW - #InPad - 100, yMsg - 2, 100, #BtnH, T("GdtBtnCommit","Commit")) @@ -2617,7 +2618,7 @@ Procedure OpenGUI() ; ============================================================ AddGadgetItem(#gdtPnl, -1, T("#gdtPnl-Config","Config")) - FrameGadget(#GdtFrmConfig, #InPad, #InPad, #PanelW - 2*#InPad, 170, T("GdtFrmConfig","Configuration Git")) + FrameGadget(#GdtFrmConfig, #InPad, #InPad, #PanelW - 2*#InPad, 170, T("GdtFrmConfig","Configuration Git"),#PB_Frame_Container) TextGadget(#GdtLblUserName, #InPad + 10, #InPad + 35, 90, 22, "user.name") StringGadget(#GdtFieldUserName, #InPad + 110, #InPad + 33, #PanelW - 2*#InPad - 120, #BtnH, "") TextGadget(#GdtLblUserEmail, #InPad + 10, #InPad + 70, 90, 22, "user.email") @@ -2635,26 +2636,22 @@ Procedure OpenGUI() CloseGadgetList() ; ============================================================ - ; Aide à droite du Panel (sans splitter) + ; HELP ; ============================================================ - ; On garde #PanelW / #PanelH tels quels - Define helpGap = 10 ; espace entre panel et aide - Define helpX = #InPad + #PanelW + helpGap ; à droite du panel + Define helpX = #InPad*2 + #PanelW ; à droite du panel Define helpY = #InPad Define helpW = WindowWidth(#WinMain)-GadgetWidth(#gdtPnl)- #InPad*4 ; reste de largeur jusqu'à la marge droite - Define helpH = PanelH ; même hauteur que le panel - - ; (Optionnel) borne minimale si besoin - If helpW < 100 : helpW = 100 : EndIf + Define helpH = GadgetHeight(#gdtPnl) ; même hauteur que le panel + ; Soit en direct : WebViewGadget(#GdtHelp, helpX, helpY, helpW, helpH) ; — ou, si tu veux un cadre : - ; FrameGadget(#GdtFrmHelp, helpX, helpY, helpW, helpH, T("GdtFrmHelp","Aide")) - ; WebViewGadget(#GdtHelp, helpX + 10, helpY + 25, helpW - 20, helpH - 35) - + FrameGadget(#GdtFrmHelp, helpX, helpY, helpW, helpH, T("GdtFrmHelp","Aide"),#PB_Frame_Container) + WebViewGadget(#GdtHelp, helpX + #InMargin, helpY + #InFrmY, helpW - #InMargin*2, helpH - #InMargin-#InFrmY) + CloseGadgetList() SetGadgetText(#GgtFieldRepo,main\GitCall\workdir) CreateThread(@RefreshFileList(),0) @@ -2781,8 +2778,8 @@ OpenGUI() ; IDE Options = PureBasic 6.21 (Windows - x64) -; CursorPosition = 2645 -; FirstLine = 2641 +; CursorPosition = 2467 +; FirstLine = 2371 ; Folding = ----------- ; Optimizer ; EnableThread diff --git a/main2.pb b/main2.pb new file mode 100644 index 0000000..1fb2430 --- /dev/null +++ b/main2.pb @@ -0,0 +1,1008 @@ +EnableExplicit + +; ============================================================================= +;-MODULE TRANSLATE +; ============================================================================= +DeclareModule Translate + Declare.s T(key.s, DefaultLng.s = "") + Declare LoadLanguage(filename.s) + Declare SaveLanguage(filename.s = "DefaultLanguage.ini") ; ← valeur par défaut +EndDeclareModule + +Module Translate + ; =============================================== + ; Système Multilingue Minimaliste pour PureBasic + ; =============================================== + Global NewMap Translations.s() + + Procedure.s T(key.s, DefaultLng.s = "") + If FindMapElement(Translations(), key) + ProcedureReturn Translations(key) + Else + If DefaultLng <> "" + Translations(key) = DefaultLng + Else + Translations(key) = "!!!" + key + EndIf + EndIf + ProcedureReturn Translations(key) + EndProcedure + + ; =============================================== + ; Chargement d'un fichier de langue (clé=valeur) + ; =============================================== + Procedure LoadLanguage(filename.s) + Protected file, line$, pos, key$, value$ + + file = ReadFile(#PB_Any, filename) + If file + While Eof(file) = 0 + line$ = Trim(ReadString(file)) + + If line$ <> "" And Left(line$, 1) <> ";" And Left(line$, 1) <> "#" + pos = FindString(line$, "=", 1) + If pos > 0 + key$ = Trim(Left(line$, pos - 1)) + value$ = Trim(Mid(line$, pos + 1)) + + value$ = ReplaceString(value$, "\n", Chr(10)) + value$ = ReplaceString(value$, "\t", Chr(9)) + + Translations(key$) = value$ + EndIf + EndIf + Wend + CloseFile(file) + ProcedureReturn #True + EndIf + ProcedureReturn #False + EndProcedure + + ; --------------------------------------------------------------------------- + ; ---------------------- UTILITAIRES INTERNES ------------------------------ + ; --------------------------------------------------------------------------- + + ; Supprime le commentaire ';' en fin de ligne (en respectant les chaînes "...") + Procedure.s _StripLineComment(line$) + Protected out$, i, c$, inString = #False, q$ = Chr(34) + + For i = 1 To Len(line$) + c$ = Mid(line$, i, 1) + + If c$ = q$ + ; Gérer "" (guillemet échappé style BASIC) + If inString And i < Len(line$) And Mid(line$, i + 1, 1) = q$ + out$ + q$ + q$ + i + 1 + Continue + EndIf + inString ! 1 + out$ + c$ + ElseIf c$ = ";" And inString = #False + Break ; commentaire atteint + Else + out$ + c$ + EndIf + Next + + ProcedureReturn out$ + EndProcedure + + ; Détecte un chemin absolu rudimentaire (Windows / Unix-like) + Procedure.i _IsAbsolutePath(path$) + If Len(path$) = 0 : ProcedureReturn #False : EndIf + If Mid(path$, 2, 1) = ":" : ProcedureReturn #True : EndIf ; "C:\..." + If Left(path$, 1) = "\" Or Left(path$, 1) = "/" : ProcedureReturn #True : EndIf + ProcedureReturn #False + EndProcedure + + ; Résout un include relatif par rapport au dossier du fichier courant + Procedure.s _ResolveInclude(baseFile$, include$) + Protected baseDir$ = GetPathPart(baseFile$) + If _IsAbsolutePath(include$) + ProcedureReturn include$ + Else + ProcedureReturn baseDir$+#PS$ + include$ + EndIf + EndProcedure + + ; Analyse un fichier source : récupère T("KEY","TEXT") et suit les Include* + Procedure _ScanSourceFile(file$, Map outPairs.s(), Map visited.b()) + Protected fileID, all$, line$, clean$, tmp$, pos1, pos2, inc$, q$ = Chr(34) + + If FindMapElement(visited(), UCase(file$)) : ProcedureReturn : EndIf + If FileSize(file$) < 0 : ProcedureReturn : EndIf + + visited(UCase(file$)) = 1 + + fileID = ReadFile(#PB_Any, file$) + If fileID = 0 : ProcedureReturn : EndIf + + While Eof(fileID) = 0 + line$ = ReadString(fileID) + clean$ = _StripLineComment(line$) + all$ + clean$ + #CRLF$ + + ; Détection IncludeFile / XIncludeFile "xxx" + tmp$ = LCase(Trim(clean$)) + If Left(tmp$, 11) = "includefile" Or Left(tmp$, 12) = "xincludefile" + pos1 = FindString(clean$, q$, 1) + If pos1 + pos2 = FindString(clean$, q$, pos1 + 1) + If pos2 > pos1 + inc$ = Mid(clean$, pos1 + 1, pos2 - pos1 - 1) + inc$ = _ResolveInclude(file$, inc$) + _ScanSourceFile(inc$, outPairs(), visited()) + EndIf + EndIf + EndIf + Wend + CloseFile(fileID) + + ; Extraction des T("KEY","TEXT") + ; - sensible aux guillemets échappés par "" (gérés plus haut partiellement) + If CreateRegularExpression(1, ~"(?i)T\\s*\\(\\s*\"([^\"]+)\"\\s*,\\s*\"([^\"]*)\"\\s*\\)") + If ExamineRegularExpression(1, all$) + Protected k$, v$ + While NextRegularExpressionMatch(1) + k$ = RegularExpressionGroup(1, 1) + v$ = RegularExpressionGroup(1, 2) + ; Si la clé n'existe pas encore, on la retient (évite doublons) + If FindMapElement(outPairs(), k$) = 0 + outPairs(k$) = v$ + EndIf + Wend + EndIf + FreeRegularExpression(1) + EndIf + EndProcedure + + ; =============================================== + ; Sauvegarde des T("KEY","TEXT") -> DefaultLanguage.ini + ; =============================================== + Procedure SaveLanguage(filename.s = "DefaultLanguage.ini") + Protected outFile$ = filename + If Trim(outFile$) = "" : outFile$ = "DefaultLanguage.ini" : EndIf + + ; Point de départ : fichier compilé (si module inclus, appeler depuis le main) + Protected mainSource$ = #PB_Compiler_File + + NewMap pairs.s() + NewMap visited.b() + _ScanSourceFile(mainSource$, pairs(), visited()) + + If MapSize(pairs()) = 0 + ; Rien trouvé -> on retourne #False pour signaler l'absence d'extractions + ProcedureReturn #False + EndIf + + ; Tri des clés pour un INI stable + NewList keys.s() + ForEach pairs() + AddElement(keys()) + keys() = MapKey(pairs()) + Next + SortList(keys(), #PB_Sort_Ascending | #PB_Sort_NoCase) + + Protected f = CreateFile(#PB_Any, outFile$) + If f + WriteStringN(f, "; Fichier de langue généré automatiquement", #PB_UTF8) + WriteStringN(f, "; Source analysée: " + mainSource$, #PB_UTF8) + WriteStringN(f, "; Format: Key = TEXT", #PB_UTF8) + WriteStringN(f, "", #PB_UTF8) + + ForEach keys() + WriteStringN(f, keys() + " = " + pairs(keys()), #PB_UTF8) + Next + + CloseFile(f) + ProcedureReturn #True + EndIf + + ProcedureReturn #False + EndProcedure + +EndModule + + +UseModule Translate +SaveLanguage() + +; ============================================================================= +;-STRUCTURES / STRUCTURES +; ============================================================================= + +; To use with RunExe() Helper +Structure runProgramCallStruct + exec.s ; program to execute / Programme à executer + args.s ; Command arguments / Arguments de la commande + workdir.s ; Working directory / Répertoire de travail + output.s ; Standard output / Sortie standard + errors.s ; Error output / Sortie d'erreur + exitcode.i ; Exit code / Code de sortie +EndStructure + +Structure FilesStruct + name.s + status.s + statusDescription.s + importance.i +EndStructure + +Structure mainStruct + gitCall.runProgramCallStruct + List Files.FilesStruct() + ;Helpers Info + gitVersion$ + IsRepository.b +EndStructure + +Global main.mainStruct + +; ============================================================================= +;-Helper +; ============================================================================= +Procedure Max(nb1, nb2) + Protected Result.l + If nb1 > nb2 + Result = nb1 + Else + Result = nb2 + EndIf + + ProcedureReturn Result +EndProcedure + +Procedure Min(nb1, nb2) + Protected Result.l + If nb1 < nb2 + Result = nb1 + Else + Result = nb2 + EndIf + + ProcedureReturn Result +EndProcedure + +Procedure.s SupTrim(text.s) + If text = "" : ProcedureReturn "" : EndIf + + Protected i.i = 1 + Protected j.i = Len(text) + Protected c.l + + ; Trim gauche : avancer tant que <= 32 + While i <= j + c = Asc(Mid(text, i, 1)) + If c <= 32 + i + 1 + Else + Break + EndIf + Wend + + ; Trim droite : reculer tant que <= 32 + While j >= i + c = Asc(Mid(text, j, 1)) + If c <= 32 + j - 1 + Else + Break + EndIf + Wend + + If j < i + ProcedureReturn "" + Else + ProcedureReturn Mid(text, i, j - i + 1) + EndIf +EndProcedure + +Procedure.i RunExe(*call.runProgramCallStruct) + Protected prg.i, line$, lineError$, out$, err$ + + If FileSize(*call\workdir)<>-2 ; Correct bad git detect if bad workdir + *call\workdir=GetCurrentDirectory() + EndIf + Debug "[RunExe] "+*call\exec+" "+*call\args + prg = RunProgram(*call\exec, *call\args, *call\workdir, #PB_Program_Open | #PB_Program_Read | #PB_Program_Error | #PB_Program_Hide) + If prg = 0 + *call\output = "" + *call\errors = "Impossible de lancer '" + *call\exec+"'." + *call\exitcode = -1 + ProcedureReturn *call\exitcode + EndIf + + ; Read output while program is running / Lire la sortie pendant l'exécution + While ProgramRunning(prg) + While AvailableProgramOutput(prg) + line$ = ReadProgramString(prg) + If line$ <> "" : out$ + line$ + #LF$ : EndIf + lineError$ = ReadProgramError(prg) + If lineError$ <> "" : err$ + lineError$ + #LF$ : EndIf + Wend + Delay(5) + Wend + + ; Read remaining output / Lire le reste de la sortie + While AvailableProgramOutput(prg) + line$ = ReadProgramString(prg) + If line$ <> "" : out$ + line$ + #LF$ : EndIf + Wend + + ; Read remaining errors / Lire le reste des erreurs + Repeat + line$ = ReadProgramError(prg) + If line$ = "" : Break : EndIf + err$ + line$ + #LF$ + ForEver + + *call\output = out$ + *call\errors = err$ + *call\exitcode = ProgramExitCode(prg) + CloseProgram(prg) + + Debug "[Exec] code=" + Str(*call\exitcode) + If *call\output <> "" : Debug "[std]" + *call\output : EndIf + If *call\errors <> "" : Debug "[stderr] " + *call\errors : EndIf + + ProcedureReturn *call\exitcode +EndProcedure + + + +; ============================================================================= +;-GUI +; ============================================================================= + +; ----------------------------------------------------------------------------- +; UI CONSTANTS / CONSTANTES UI +; ----------------------------------------------------------------------------- +#AppTitle$ = "Git Companion" + +#UI_WinStartW = 1280 +#UI_WinStartH = 720 +#UI_WinMinW = 980 +#UI_WinMinH = 600 + +#UI_Margin = 10 ; outer margin / marge extérieure +#UI_Inset = 10 ; inner padding / marge interne +#UI_RowH = 30 ; default row height / hauteur ligne +#UI_BtnW = 100 +#UI_PanelStartW = 920 ; initial left panel width / largeur initiale du panel de gauche +#UI_PanelMinW = 740 ; minimum left panel width / largeur mini du panel +#UI_HelpMinW = 240 ; minimum help area width / largeur mini de l’aide + +#UI_FrameHeaderH = 30 ; frame header area (title) / bandeau titre du frame + +; ----------------------------------------------------------------------------- +; ENUMS: WINDOWS & GADGETS / ÉNUMÉRATIONS : FENÊTRES & GADGETS +; ----------------------------------------------------------------------------- +Enumeration WindowIDs + #WinMain +EndEnumeration + +Enumeration GadgetsIDs + ; Core layout + #GID_Panel + #GID_HelpFrame + #GID_HelpWeb + + ; === TAB 1: REPO (Dépôt) === + #GID_Tab_Repo + + ; Local frame + #GID_FrmLocal + #GID_LblRepo + #GID_EdRepo + #GID_BtnBrowseRepo + #GID_BtnInit + #GID_BtnRefresh + + ; Remote frame + #GID_FrmRemote + #GID_LblRemote + #GID_EdRemote + #GID_LblBranch + #GID_CbBranch + #GID_BtnNewBranch + #GID_BtnClone + #GID_BtnPull + #GID_BtnPush + #GID_LblRemoteStatus + #GID_TxtRemoteStatus + #GID_LblLastFetch + #GID_TxtLastFetch + #GID_BtnVerify + #GID_LblAction + #GID_TxtAction + + ; Files frame + #GID_FrmFiles + #GID_ListStatus + #GID_BtnRestore + #GID_BtnRename + #GID_BtnDelete + #GID_BtnIgnore + #GID_LblMessage + #GID_EdMessage + #GID_BtnCommit + + ; === TAB 2: HISTORY === + #GID_Tab_History + #GID_ListHistory + #GID_BtnRestoreCommit + #GID_TxtCommitInfo + + ; === TAB 3: .gitignore === + #GID_Tab_Gitignore + #GID_TxtGitIgnore + #GID_BtnSaveGitIgnore + + ; === TAB 4: CONFIG === + #GID_Tab_Config + #GID_FrmConfig + #GID_LblUserName + #GID_EdUserName + #GID_LblUserEmail + #GID_EdUserEmail + #GID_LblScope + #GID_CbScope + #GID_BtnSaveCfg +EndEnumeration + +; ----------------------------------------------------------------------------- +; SMALL HELPERS / AIDES UTILITAIRES +; ----------------------------------------------------------------------------- +Macro RightOf(g) : GadgetX(g) + GadgetWidth(g) : EndMacro +Macro BottomOf(g) : GadgetY(g) + GadgetHeight(g) : EndMacro + +; ----------------------------------------------------------------------------- +; LAYOUT COMPUTATION / CALCUL DE MISE EN PAGE +; ----------------------------------------------------------------------------- +Procedure ResizeGUI() + Protected winW = WindowWidth(#WinMain) + Protected winH = WindowHeight(#WinMain) + + If winW < #UI_WinMinW : winW = #UI_WinMinW : EndIf + If winH < #UI_WinMinH : winH = #UI_WinMinH : EndIf + + ; Left panel width adapts, but keeps at least minimum + Protected panelW = #UI_PanelStartW + panelW = Min(panelW, winW - #UI_HelpMinW - #UI_Margin*4) + panelW = Max(panelW, #UI_PanelMinW) + + Protected panelH = winH - #UI_Margin*2 + ResizeGadget(#GID_Panel, #UI_Margin, #UI_Margin, panelW, panelH) + + ; Help / Aide area on the right fills the remaining space + Protected helpX = #UI_Margin*2 + panelW + Protected helpW = winW - helpX - #UI_Margin + Protected helpH = panelH + + ResizeGadget(#GID_HelpFrame, helpX, #UI_Margin, helpW, helpH) + ; WebView inside frame (with padding and header) + Protected innerX = helpX + #UI_Inset + Protected innerY = #UI_Margin + #UI_FrameHeaderH + Protected innerW = helpW - #UI_Inset*2 + Protected innerH = helpH - #UI_Inset - #UI_FrameHeaderH + ResizeGadget(#GID_HelpWeb, innerX, innerY, innerW, innerH) + + ; --- Resize essentials inside tabs (keep it simple & robust) --- + ; TAB 1: Files list should expand with panel height + Protected filesTop = BottomOf(#GID_FrmRemote) + #UI_Inset + Protected filesH = panelH - filesTop - #UI_Inset + ResizeGadget(#GID_FrmFiles, #UI_Inset, filesTop, panelW - #UI_Inset*2, filesH) + + Protected listMargin = #UI_Inset + Protected listH = filesH - #UI_RowH*2 - #UI_Inset*4 - #UI_FrameHeaderH + If listH < 100 : listH = 100 : EndIf + ResizeGadget(#GID_ListStatus, #UI_Inset, #UI_FrameHeaderH, GadgetWidth(#GID_FrmFiles) - #UI_Inset*2, listH) + + ; Buttons row positions under list + Protected btnY = #UI_FrameHeaderH + listH + #UI_Inset + ResizeGadget(#GID_BtnRestore, #UI_Inset + 10, btnY, 110, #UI_RowH) + ResizeGadget(#GID_BtnRename, #UI_Inset + 130, btnY, 110, #UI_RowH) + ResizeGadget(#GID_BtnDelete, #UI_Inset + 250, btnY, 110, #UI_RowH) + ResizeGadget(#GID_BtnIgnore, #UI_Inset + 370, btnY, 110, #UI_RowH) + + ; Commit message line under the buttons + Protected msgY = btnY + #UI_RowH + #UI_Inset + ResizeGadget(#GID_LblMessage, #UI_Inset + 10, msgY + 4, 80, 22) + ResizeGadget(#GID_BtnCommit, GadgetWidth(#GID_FrmFiles) - #UI_Inset - 100, msgY - 2, 100, #UI_RowH) + ResizeGadget(#GID_EdMessage, #UI_Inset + 95, msgY, GadgetX(#GID_BtnCommit) - (#UI_Inset + 95) - #UI_Inset, #UI_RowH) + + ; TAB 2: History List + editor + Protected histListH = panelH - #UI_Margin*2 - #UI_RowH - 10 - 150 - 10 + If histListH < 100 : histListH = 100 : EndIf + ResizeGadget(#GID_ListHistory, #UI_Inset, #UI_Inset, panelW - #UI_Inset*2, histListH) + ResizeGadget(#GID_BtnRestoreCommit, #UI_Inset, #UI_Inset + histListH + #UI_Inset, 180, #UI_RowH) + ResizeGadget(#GID_TxtCommitInfo, #UI_Inset, panelH - #UI_Inset - 150, panelW - #UI_Inset*2, 150) + + ; TAB 3: .gitignore + Protected gitEdH = panelH - #UI_Inset*4 - #UI_RowH + ResizeGadget(#GID_TxtGitIgnore, #UI_Inset, #UI_Inset, panelW - #UI_Inset*2, gitEdH) + ResizeGadget(#GID_BtnSaveGitIgnore, #UI_Inset, #UI_Inset + gitEdH + #UI_Inset, 100, #UI_RowH) + + ; TAB 4: Config frame fits content (kept constant height) + ; (no additional dynamic resize needed here) +EndProcedure + +; ----------------------------------------------------------------------------- +; Tooltips & i18n +; FR: Appeler après la création des gadgets, puis après tout changement de langue. +; EN: Call after creating gadgets, then after any language change. +; ----------------------------------------------------------------------------- + +Procedure ApplyToolTips() + ; --- Dépôt / Local repo --- + GadgetToolTip(#GID_BtnInit, T("tip.init", "Initialiser un dépôt Git ici")) + GadgetToolTip(#GID_BtnRefresh, T("tip.refresh", "Rafraîchir la liste des fichiers et l’état Git")) + GadgetToolTip(#GID_EdRepo, T("tip.repo.path", "Chemin du dossier projet (workdir)")) + + ; --- Remote / Branch --- + GadgetToolTip(#GID_EdRemote, T("tip.remote", "URL du remote (ex.: https://... ou git@host:org/repo.git)")) + GadgetToolTip(#GID_CbBranch, T("tip.branch.select", "Choisir la branche active")) + GadgetToolTip(#GID_BtnNewBranch, T("tip.branch.new", "Créer une nouvelle branche")) + GadgetToolTip(#GID_BtnClone, T("tip.clone", "Cloner depuis l’URL remote")) + GadgetToolTip(#GID_BtnPull, T("tip.pull", "Récupérer et fusionner depuis le remote")) + GadgetToolTip(#GID_BtnPush, T("tip.push", "Envoyer vos commits sur le remote")) + + ; --- Fichiers & actions locales / Files & local actions --- + GadgetToolTip(#GID_ListStatus, T("tip.files.list", "Fichiers du dépôt :\n- cochez pour préparer un commit\n- sélectionnez pour agir")) + GadgetToolTip(#GID_BtnRestore, T("tip.restore", "Restaurer les fichiers sélectionnés")) + GadgetToolTip(#GID_BtnRename, T("tip.rename", "Renommer les fichiers sélectionnés")) + GadgetToolTip(#GID_BtnDelete, T("tip.delete", "Supprimer les fichiers sélectionnés")) + GadgetToolTip(#GID_BtnIgnore, T("tip.ignore", "Ajouter/retirer les fichiers sélectionnés dans .gitignore")) + + ; --- Commit --- + GadgetToolTip(#GID_EdMessage, T("tip.message", "Message du commit")) + GadgetToolTip(#GID_BtnCommit, T("tip.commit", "Committer les fichiers cochés avec le message")) + + ; --- History --- + GadgetToolTip(#GID_ListHistory, T("tip.history", "Historique des commits")) + GadgetToolTip(#GID_BtnRestoreCommit, T("tip.history.restore", "Restaurer / checkout le commit sélectionné")) + + ; --- .gitignore --- + GadgetToolTip(#GID_TxtGitIgnore, T("tip.gitignore.edit", "Éditeur du .gitignore")) + GadgetToolTip(#GID_BtnSaveGitIgnore, T("tip.gitignore.save", "Sauvegarder le .gitignore")) + + ; --- Config --- + GadgetToolTip(#GID_EdUserName, T("tip.cfg.username", "Nom d’utilisateur Git (user.name)")) + GadgetToolTip(#GID_EdUserEmail, T("tip.cfg.useremail", "Email Git (user.email)")) + GadgetToolTip(#GID_CbScope, T("tip.cfg.scope", "Portée de la configuration (Local/Global/System)")) + GadgetToolTip(#GID_BtnSaveCfg, T("tip.cfg.save", "Enregistrer la configuration Git")) + + ; --- Help (optionnel) --- + GadgetToolTip(#GID_HelpWeb, T("tip.help.web", "Zone d’aide (documentation / rendu HTML)")) +EndProcedure + +; ----------------------------------------------------------------------------- +; BUILD GUI / CONSTRUCTION DE L’INTERFACE +; ----------------------------------------------------------------------------- +Procedure OpenGUI() + UseModule Translate + Define repoDir$ = GetCurrentDirectory() + + If OpenWindow(#WinMain, 0, 0, #UI_WinStartW, #UI_WinStartH, #AppTitle$, #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_SizeGadget) + Protected panelH = WindowHeight(#WinMain) - #UI_Margin*2 + + ; LEFT: Panel with tabs / Panneau avec onglets + PanelGadget(#GID_Panel, #UI_Margin, #UI_Margin, #UI_PanelStartW, panelH) + + ; =========================================================================== + ; TAB 1: REPO / DÉPÔT + ; =========================================================================== + AddGadgetItem(#GID_Panel, -1, T("Tab.Repo", "Dépôt")) + + ; ---- Frame: Local repository ------------------------------------------------ + FrameGadget(#GID_FrmLocal, #UI_Inset, #UI_Inset, GadgetWidth(#GID_Panel) - #UI_Inset*2, #UI_FrameHeaderH + #UI_RowH*2 + #UI_Inset*2, T("Local.FrameTitle", "Dépôt local"), #PB_Frame_Container) + + TextGadget(#GID_LblRepo, #UI_Inset, #UI_FrameHeaderH, 70, #UI_RowH, T("Local.Label.Repo","Dépôt :")) + StringGadget(#GID_EdRepo, RightOf(#GID_LblRepo) + #UI_Inset, #UI_FrameHeaderH, GadgetWidth(#GID_FrmLocal) - #UI_Inset*4 - 70 - #UI_BtnW, #UI_RowH, repoDir$) + ButtonGadget(#GID_BtnBrowseRepo, RightOf(#GID_EdRepo) + #UI_Inset, #UI_FrameHeaderH, #UI_BtnW, #UI_RowH, T("Local.Button.Browse","Parcourir…")) + + ButtonGadget(#GID_BtnInit, #UI_Inset, BottomOf(#GID_BtnBrowseRepo) + #UI_Inset, #UI_BtnW, #UI_RowH, T("Local.Button.Init","Init Dépôt")) + ButtonGadget(#GID_BtnRefresh, RightOf(#GID_BtnInit) + #UI_Inset, GadgetY(#GID_BtnInit), #UI_BtnW, #UI_RowH, T("Local.Button.Refresh","Rafraîchir")) + CloseGadgetList() + + ; ---- Frame: Remote / Branche ------------------------------------------------ + Define yRemote = BottomOf(#GID_FrmLocal) + #UI_Inset + FrameGadget(#GID_FrmRemote, #UI_Inset, yRemote, GadgetWidth(#GID_Panel) - #UI_Inset*2, #UI_FrameHeaderH + #UI_RowH*4 + #UI_Inset*4, T("Remote.FrameTitle","Distant (remote / branche)"), #PB_Frame_Container) + + TextGadget(#GID_LblRemote, #UI_Inset, #UI_FrameHeaderH, 70, #UI_RowH, T("Remote.Label.Remote","Remote :")) + StringGadget(#GID_EdRemote, RightOf(#GID_LblRemote) + #UI_Inset, #UI_FrameHeaderH, 420, #UI_RowH, "") + + TextGadget(#GID_LblBranch, RightOf(#GID_EdRemote) + #UI_Inset, #UI_FrameHeaderH, 80, #UI_RowH, T("Remote.Label.Branch","Branche :")) + ComboBoxGadget(#GID_CbBranch, RightOf(#GID_LblBranch), #UI_FrameHeaderH, 220, #UI_RowH, #PB_ComboBox_Editable) + ButtonGadget(#GID_BtnNewBranch, RightOf(#GID_CbBranch) + #UI_Inset, #UI_FrameHeaderH, 130, #UI_RowH, T("Remote.Button.NewBranch","New branch")) + + ButtonGadget(#GID_BtnClone, #UI_Inset, BottomOf(#GID_LblRemote) + #UI_Inset, #UI_BtnW, #UI_RowH, T("Remote.Button.Clone","Clone")) + ButtonGadget(#GID_BtnPull, RightOf(#GID_BtnClone) + #UI_Inset, GadgetY(#GID_BtnClone), #UI_BtnW, #UI_RowH, T("Remote.Button.Pull","Pull")) + ButtonGadget(#GID_BtnPush, RightOf(#GID_BtnPull) + #UI_Inset, GadgetY(#GID_BtnClone), #UI_BtnW, #UI_RowH, T("Remote.Button.Push","Push")) + + Define yStatus = BottomOf(#GID_BtnClone) + #UI_Inset + TextGadget(#GID_LblRemoteStatus, #UI_Inset, yStatus, 70, #UI_RowH, T("Remote.Status.Label","Status :")) + TextGadget(#GID_TxtRemoteStatus, RightOf(#GID_LblRemoteStatus), yStatus, 200, #UI_RowH, T("Remote.Status.Checking","Vérification..."), #PB_Text_Border) + + TextGadget(#GID_LblLastFetch, RightOf(#GID_TxtRemoteStatus) + 15, yStatus, 110, #UI_RowH, T("Remote.LastSync.Label","Dernière sync :")) + TextGadget(#GID_TxtLastFetch, RightOf(#GID_LblLastFetch), yStatus, 120, #UI_RowH, "-", #PB_Text_Border) + ButtonGadget(#GID_BtnVerify, RightOf(#GID_TxtLastFetch) + 10, yStatus - 2, 90, #UI_RowH, T("Remote.Button.Verify","Vérifier")) + + TextGadget(#GID_LblAction, #UI_Inset, BottomOf(#GID_LblRemoteStatus) + #UI_Inset, 60, 20, T("Remote.Action.Label","Action :")) + TextGadget(#GID_TxtAction, RightOf(#GID_LblAction), GadgetY(#GID_LblAction), 300, 20, "-", #PB_Text_Border) + CloseGadgetList() + + ; ---- Frame: Files & changes ------------------------------------------------- + Define yFiles = BottomOf(#GID_FrmRemote) + #UI_Inset + Define hFiles = panelH - yFiles - #UI_Inset + FrameGadget(#GID_FrmFiles, #UI_Inset, yFiles, GadgetWidth(#GID_Panel) - #UI_Inset*2, hFiles, T("Files.FrameTitle","Fichiers & modifications"), #PB_Frame_Container) + + ListIconGadget(#GID_ListStatus, #UI_Inset, #UI_FrameHeaderH, GadgetWidth(#GID_FrmFiles) - #UI_Inset*2, hFiles - #UI_RowH*2 - #UI_Inset*4 - #UI_FrameHeaderH, T("Files.List.Path","Path"), 300, #PB_ListIcon_CheckBoxes | #PB_ListIcon_MultiSelect | #PB_ListIcon_FullRowSelect | #PB_ListIcon_AlwaysShowSelection) + AddGadgetColumn(#GID_ListStatus, 1, T("Files.List.Status","Status"), 80) + AddGadgetColumn(#GID_ListStatus, 2, T("Files.List.Description","Description"), 300) + + Define yLocalActions = BottomOf(#GID_ListStatus) + #UI_Inset + ButtonGadget(#GID_BtnRestore, #UI_Inset + 10, yLocalActions, 110, #UI_RowH, T("LocalActions.Button.Restore","Restaurer")) + ButtonGadget(#GID_BtnRename, #UI_Inset + 130, yLocalActions, 110, #UI_RowH, T("LocalActions.Button.Rename","Renommer")) + ButtonGadget(#GID_BtnDelete, #UI_Inset + 250, yLocalActions, 110, #UI_RowH, T("LocalActions.Button.Delete","Supprimer")) + ButtonGadget(#GID_BtnIgnore, #UI_Inset + 370, yLocalActions, 110, #UI_RowH, T("LocalActions.Button.Ignore","Ignorer")) + + Define yMsg = BottomOf(#GID_BtnRestore) + #UI_Inset + TextGadget(#GID_LblMessage, #UI_Inset + 10, yMsg + 4, 80, 22, T("Commit.Label.Message","Message :")) + StringGadget(#GID_EdMessage, #UI_Inset + 95, yMsg, GadgetWidth(#GID_FrmFiles) - #UI_Inset*2 - 95 - 110, #UI_RowH, "") + ButtonGadget(#GID_BtnCommit, GadgetWidth(#GID_FrmFiles) - #UI_Inset - 100, yMsg - 2, 100, #UI_RowH, T("Commit.Button.Commit","Commit")) + CloseGadgetList() + + ; =========================================================================== + ; TAB 2: HISTORY + ; =========================================================================== + AddGadgetItem(#GID_Panel, -1, T("Tabs.History","History")) + + ListIconGadget(#GID_ListHistory, #UI_Inset, #UI_Inset, GadgetWidth(#GID_Panel) - #UI_Inset*2, panelH - #UI_Inset*2 - #UI_RowH - 10 - 150 - 10, T("History.Headers.Header","Header"), 220, #PB_ListIcon_FullRowSelect) + AddGadgetColumn(#GID_ListHistory, 1, T("History.Headers.Date","Date"), 150) + AddGadgetColumn(#GID_ListHistory, 2, T("History.Headers.Author","Auteur"), 180) + AddGadgetColumn(#GID_ListHistory, 3, T("History.Headers.Files","Fichiers"), 120) + AddGadgetColumn(#GID_ListHistory, 4, T("History.Headers.Message","Message"), (GadgetWidth(#GID_Panel) - 2*#UI_Inset) - (220 + 150 + 180 + 120) - 20) + + ButtonGadget(#GID_BtnRestoreCommit, #UI_Inset, panelH - #UI_Inset - #UI_RowH - 150 - 10, 180, #UI_RowH, T("History.Button.RestoreCommit","Restore This Commit")) + + EditorGadget(#GID_TxtCommitInfo, #UI_Inset, panelH - #UI_Inset - 150, GadgetWidth(#GID_Panel) - #UI_Inset*2, 150, #PB_Editor_ReadOnly) + + ; =========================================================================== + ; TAB 3: .gitignore + ; =========================================================================== + AddGadgetItem(#GID_Panel, -1, T("Tabs.Gitignore",".gitignore file")) + EditorGadget(#GID_TxtGitIgnore, #UI_Inset, #UI_Inset, GadgetWidth(#GID_Panel) - #UI_Inset*2, panelH - #UI_Inset*4 - #UI_RowH) + ButtonGadget(#GID_BtnSaveGitIgnore, #UI_Inset, GadgetY(#GID_TxtGitIgnore) + GadgetHeight(#GID_TxtGitIgnore) + #UI_Inset, 100, #UI_RowH, T("Gitignore.Button.SaveFile","Save File")) + + ; =========================================================================== + ; TAB 4: CONFIG + ; =========================================================================== + AddGadgetItem(#GID_Panel, -1, T("Tabs.Config","Config")) + FrameGadget(#GID_FrmConfig, #UI_Inset, #UI_Inset, GadgetWidth(#GID_Panel) - #UI_Inset*2, 170, T("Config.FrameTitle","Configuration Git"), #PB_Frame_Container) + TextGadget(#GID_LblUserName, #UI_Inset + 10, #UI_Inset + 35, 90, 22, "user.name") + StringGadget(#GID_EdUserName, #UI_Inset + 110, #UI_Inset + 33, GadgetWidth(#GID_FrmConfig) - (#UI_Inset*2) - 120, #UI_RowH, "") + TextGadget(#GID_LblUserEmail, #UI_Inset + 10, #UI_Inset + 70, 90, 22, "user.email") + StringGadget(#GID_EdUserEmail, #UI_Inset + 110, #UI_Inset + 68, GadgetWidth(#GID_FrmConfig) - (#UI_Inset*2) - 120, #UI_RowH, "") + TextGadget(#GID_LblScope, #UI_Inset + 10, #UI_Inset + 105, 90, 22, T("Config.Label.Scope","Portée")) + ComboBoxGadget(#GID_CbScope, #UI_Inset + 110, #UI_Inset + 103, 180, #UI_RowH) + AddGadgetItem(#GID_CbScope, -1, "Local") + AddGadgetItem(#GID_CbScope, -1, "System") + AddGadgetItem(#GID_CbScope, -1, "Global") + SetGadgetState(#GID_CbScope, 0) + ; GetGitIdentity() ; Uncomment if you implement it + ButtonGadget(#GID_BtnSaveCfg, GadgetWidth(#GID_FrmConfig) - #UI_Inset - 110, #UI_Inset + 100, 110, #UI_RowH, T("Config.Button.Save","Enregistrer")) + CloseGadgetList() + + ; --- End tabs --- + CloseGadgetList() + + ; =========================================================================== + ; HELP AREA (RIGHT SIDE) / ZONE AIDE (À DROITE) + ; =========================================================================== + FrameGadget(#GID_HelpFrame, #UI_Margin*2 + #UI_PanelStartW, #UI_Margin, 400, panelH, T("Help.FrameTitle","Aide"), #PB_Frame_Container) + WebViewGadget(#GID_HelpWeb, GadgetX(#GID_HelpFrame) + #UI_Inset, GadgetY(#GID_HelpFrame) + #UI_FrameHeaderH, GadgetWidth(#GID_HelpFrame) - #UI_Inset*2, GadgetHeight(#GID_HelpFrame) - #UI_FrameHeaderH - #UI_Inset) + CloseGadgetList() + + ; Initial placement / placement initial + ResizeGUI() + ; toolTip aide + ApplyToolTips() + ProcedureReturn #True + EndIf + ProcedureReturn #False + +EndProcedure + +; ----------------------------------------------------------------------------- +;-GIT FUNCTION +; ----------------------------------------------------------------------------- + +Procedure Git(param.s) + main\gitcall\exec="git" + main\gitCall\args=param + main\gitCall\workdir=GetCurrentDirectory() + ProcedureReturn RunExe(@main\gitCall) +EndProcedure + +Procedure.i GetGitVersion() + If Git("--version") = 0 And FindString(main\GitCall\output, "git version", 1) + ProcedureReturn #True + EndIf + ProcedureReturn #False +EndProcedure + +Procedure GetGitStatusPocelaine() + If Git("status --porcelain --ignored") = 0 + ProcedureReturn #True + EndIf + ProcedureReturn #False +EndProcedure + + +Procedure IsGitRepository() + If Git("status") <> 0 + ProcedureReturn #False + Else + ProcedureReturn #True + EndIf +EndProcedure + +Procedure.s GetStatusDescription(status.s) + Select status + Case " " + ProcedureReturn "Unmodified" ;Non modifié + Case "M " + ProcedureReturn "Modified in index";Modifié dans index seulement + Case " M" + ProcedureReturn "Modified in working tree";Modifié dans working tree seulement + Case "MM" + ProcedureReturn "Modified in index and working tree";Modifié dans index ET working tree + Case "A " + ProcedureReturn "Added to index";Ajouté à l'index + Case "AM" + ProcedureReturn "Added to index, modified in working tree";Ajouté à l'index, modifié dans working tree + Case " D" + ProcedureReturn "Deleted from working tree";Supprimé du working tree + Case "D " + ProcedureReturn "Deleted from index";Supprimé de l'index + Case "AD" + ProcedureReturn "Added to index, deleted from working tree";Ajouté à l'index, supprimé du working tree + Case " R" + ProcedureReturn "Renamed in working tree";Renommé dans working tree + Case "R " + ProcedureReturn "Renamed in index";Renommé dans index + Case " C" + ProcedureReturn "Copied in working tree";Copié dans working tree + Case "C " + ProcedureReturn "Copied in index";Copié dans index + Case "DD" + ProcedureReturn "Deleted by both (conflict)";Supprimé dans les deux (conflit) + Case "AU" + ProcedureReturn "Added by us (conflict)";Ajouté par eux (conflit) + Case "UD" + ProcedureReturn "Deleted by them (conflict)";Supprimé par eux (conflit) + Case "UA" + ProcedureReturn "Added by them (conflict)";Ajouté par eux (conflit) + Case "DU" + ProcedureReturn "Deleted by us (conflict)";Supprimé par nous (conflit) + Case "AA" + ProcedureReturn "Added by both (conflict)";Ajouté des deux côtés (conflit) + Case "UU" + ProcedureReturn "Modified by both (conflict)";Modifié des deux côtés (conflit) + Case "??" + ProcedureReturn "Untracked" ;Non suivi + Case "!!" + ProcedureReturn "Ignored" ;Ignoré + Default + ProcedureReturn "Unknown status ->"+status + EndSelect +EndProcedure + +; --- Helper interne : scanne un dossier et alimente la liste +Procedure _ScanFiles(path$, root$="") + If root$="":root$=path$:EndIf + Protected did.i = ExamineDirectory(#PB_Any, path$, "*") + If did = 0 : ProcedureReturn : EndIf + + While NextDirectoryEntry(did) + Protected name$ = DirectoryEntryName(did) + If name$ = "." Or name$ = ".." : Continue : EndIf + + Protected full$ = path$ + name$ + Debug full$ + If DirectoryEntryType(did) = #PB_DirectoryEntry_Directory + ; Ignorer le dépôt interne + If LCase(name$) <> ".git" + If Right(full$, 1) <> #PS$ : full$ + #PS$ : EndIf + _ScanFiles(full$, root$) + EndIf + Else + + Protected rel$ = Mid(full$, Len(root$) + 1) + rel$ = ReplaceString(rel$, "\", "/") ; chemins normalisés + + AddElement(main\Files()) + main\Files()\name = rel$ + main\Files()\status = " " ; 2 espaces = clean + main\Files()\statusDescription = "Unmodified" + EndIf + Wend + FinishDirectory(did) +EndProcedure + +Procedure readDirectory() + Protected path$ + path$ = GetCurrentDirectory() + + ; Normalise avec un séparateur de fin + If Right(path$, 1) <> #PS$ : path$ + #PS$ : EndIf + + ; On n'efface pas ici pour laisser le choix à l'appelant + _ScanFiles(path$) + + ProcedureReturn ListSize(main\Files()) + +EndProcedure + +Procedure RefreshFiles() + ClearGadgetItems(#GID_ListStatus) + ClearList(main\Files()) + readDirectory() + + Protected n.l=n-1 + ForEach main\Files() + n=n+1 + AddGadgetItem(#GID_ListStatus,n,main\Files()\name+Chr(10)+main\Files()\status+Chr(10)+GetStatusDescription(main\Files()\status)) + If Right(main\Files()\status,1)="M" + SetGadgetItemState(#GID_ListStatus,n,#PB_ListIcon_Checked) + EndIf + Next + +EndProcedure + +; ----------------------------------------------------------------------------- +;-MAIN +; ----------------------------------------------------------------------------- + +Procedure Main() + SaveLanguage() + ;-init Current Work Directory + Protected n.l,param$ + If CountProgramParameters() <> 0 + ; Parse command line arguments / Analyser les arguments de ligne de commande + For n = 0 To CountProgramParameters() - 1 + param$ = ProgramParameter(n) + Select LCase(param$) + Case "--project" : If n + 1 < CountProgramParameters() : main\GitCall\workdir = ProgramParameter(n + 1) : EndIf + EndSelect + Next n + EndIf + If main\gitCall\workdir="" + main\gitCall\workdir=GetCurrentDirectory() + EndIf + SetCurrentDirectory(main\gitCall\workdir) + + ;-detect if Git is installed + Protected osHint$,title$,msg$ + If GetGitVersion() + main\gitVersion$=SupTrim(main\gitCall\output) + + Else + CompilerSelect #PB_Compiler_OS + CompilerCase #PB_OS_Windows + osHint$ = T("git.notfound.win", + "Windows : Vérifiez que Git for Windows est installé et que 'git.exe' est dans PATH (ex. C:\Program Files\Git\bin). " + + "Dans une invite de commandes, tapez : git --version") + CompilerCase #PB_OS_Linux + osHint$ = T("git.notfound.linux", + "Linux : Installez Git via votre gestionnaire de paquets (ex. Debian/Ubuntu : sudo apt install git, Fedora : sudo dnf install git, Arch : sudo pacman -S git). " + + "Vérifiez que 'git' est accessible dans PATH (git --version).") + CompilerCase #PB_OS_MacOS + osHint$ = T("git.notfound.macos", + "macOS : Installez les Xcode Command Line Tools (xcode-select --install) ou Homebrew (brew install git). " + + "Assurez-vous que /usr/bin ou /usr/local/bin est dans PATH (git --version).") + CompilerEndSelect + title$ = T("git.notfound.title", "Git non détecté") + msg$ = T("git.notfound.body", "Git n'a pas été détecté sur ce système.") + #CRLF$ + #CRLF$ + osHint$ + #CRLF$ + #CRLF$ +T("git.notfound.exit", "L'application va se fermer.") + MessageRequester(title$, msg$, #PB_MessageRequester_Error) + End ; Quitter proprement / Exit the app + EndIf + + + If OpenGUI() + ;refresh + main\IsRepository=IsRepository() + SetWindowTitle(#WinMain,#AppTitle$+" (with "+main\gitVersion$+")") + SetGadgetText(#GID_EdRepo, GetCurrentDirectory()) + If main\IsRepository + RefreshFiles() + EndIf + ; ----------------------------------------------------------------------------- + ;-EVENT LOOP / BOUCLE D'ÉVÉNEMENTS + ; ----------------------------------------------------------------------------- + Protected.i ev, gid + + Repeat + ev = WaitWindowEvent() + Select ev + + Case #PB_Event_SizeWindow + ResizeGUI() + + Case #PB_Event_Gadget + gid = EventGadget() + Select gid + Case #GID_BtnBrowseRepo + ; FR: Sélection dossier / EN: select folder + Protected path$ = PathRequester(T("Dlg.SelectRepo","Sélectionnez le dépôt local..."), GetCurrentDirectory(),WindowID(#WinMain)) + If path$ <> "" And FileSize(path$)=-2 + main\GitCall\workdir=path$ + SetGadgetText(#GID_EdRepo, path$) + SetCurrentDirectory(path$) + main\IsRepository=IsRepository() + If main\IsRepository=#True + RefreshFiles() + EndIf + ;CreateThread(@RefreshFileList(),0) ;TODO refresh list + EndIf + + Case #GID_BtnInit + SetGadgetText(#GID_TxtAction, T("Action.InitRepo","Init dépôt demandé")) + ; TODO: call your Git init logic + + Case #GID_BtnRefresh + SetGadgetText(#GID_TxtAction, T("Action.Refresh","Rafraîchir l’état")) + ; TODO: refresh repo status + + Case #GID_BtnClone + SetGadgetText(#GID_TxtAction, T("Action.Clone","Clone demandé")) + ; TODO: git clone + + Case #GID_BtnPull + SetGadgetText(#GID_TxtAction, T("Action.Pull","Pull demandé")) + ; TODO: git pull + + Case #GID_BtnPush + SetGadgetText(#GID_TxtAction, T("Action.Push","Push demandé")) + ; TODO: git push + + Case #GID_BtnVerify + SetGadgetText(#GID_TxtRemoteStatus, T("Remote.Status.Checking","Vérification...")) + ; TODO: check remote & update last fetch + SetGadgetText(#GID_TxtLastFetch, FormatDate("%yyyy-%mm-%dd %hh:%ii:%ss", Date())) + + Case #GID_BtnRestore + ; TODO: restore + Case #GID_BtnRename + ; TODO: rename + Case #GID_BtnDelete + ; TODO: delete + Case #GID_BtnIgnore + ; TODO: ignore + + Case #GID_BtnCommit + ; TODO: commit with GetGadgetText(#GID_EdMessage) + + Case #GID_BtnSaveGitIgnore + ; TODO: save .gitignore + + Case #GID_BtnSaveCfg + ; TODO: write config + EndSelect + + EndSelect + Until ev = #PB_Event_CloseWindow + + + EndIf +EndProcedure +Main() + +; IDE Options = PureBasic 6.21 (Windows - x64) +; CursorPosition = 735 +; FirstLine = 711 +; Folding = ----- +; EnableXP +; DPIAware \ No newline at end of file