diff --git a/PBIDE-GitTool.pb b/PBIDE-GitTool.pb index 255ea12..f5cbdca 100644 --- a/PBIDE-GitTool.pb +++ b/PBIDE-GitTool.pb @@ -1,31 +1,90 @@ ; PBIDE-GitTool.pb — Outil externe Git pour l’IDE PureBasic -; Gère init, status, add+commit, push/pull avec paramètres simples -; PureBasic 6.x, multiplateforme -; ------------------------------------------------------------ +; - Init, Status, Add+Commit (sélectif), Push/Pull +; - UI : liste avec cases à cocher + actions Inclure/Exclure/Tout +; - Fenêtre "Avancé…" : switch/restore de branche +; - Assistant d’installation IDE si lancé sans paramètre +; Contraintes respectées : pas d'underscore dans nos identifiants, +; Protected uniquement dans les procédures, pas de IIf, déclarations = implémentations, +; comparaisons uniquement dans If/While/Until/For, etc. EnableExplicit - -; ====== Debug global ====== #EnableDebug = #True ; ====== Séparateur de chemin (portable) ====== CompilerIf #PB_Compiler_OS = #PB_OS_Windows #PathSep$ = "\" - ; Chemin Git forcé pour Windows (proposé) : + ; Chemin Git forcé pour Windows : #GitExe$ = "C:\Program Files\Git\cmd\git.exe" CompilerElse #PathSep$ = "/" - ; Sur macOS/Linux on garde 'git' dans le PATH + ; Sur macOS/Linux on garde 'git' dans le PATH : #GitExe$ = "git" CompilerEndIf -; ====== Structure d'appel Git ====== +; ====== IDs UI (placer ce bloc AVANT toutes les procédures) ====== + +; Fenêtre principale +#GWindow = 1 +#GLabelRepo = 10 +#GStringRepo = 11 +#GButtonBrowse = 12 +#GListStatus = 13 +#GRefresh = 14 +#GLabelMsg = 15 +#GStringMsg = 16 +#GCheckPush = 17 +#GLabelRemote = 18 +#GStringRemote = 19 +#GLabelBranch = 20 +#GComboBranch = 21 +#GSavePrefs = 22 +#GInit = 23 +#GCommit = 24 +#GPull = 25 +#GPush = 26 +#GInclude = 27 +#GExclude = 28 +#GIncludeAll = 29 +#GExcludeAll = 30 +#GAdvanced = 31 + +; Fenêtre avancée (branches) +#WAdv = 200 +#GAdvLabel = 210 +#GAdvCombo = 211 +#GAdvSwitch = 212 +#GAdvRestore = 213 +#GAdvClose = 214 + +; Assistant d’installation +#WInstall = 100 +#GLabelIde = 110 +#GStringIde = 111 +#GButtonIde = 112 +#GLabelTools = 113 +#GStringTools = 114 +#GButtonTools = 115 +#GLabelTheme = 116 +#GStringTheme = 117 +#GButtonTheme = 118 +#GInstallGo = 119 +#GInstallCancel = 120 +#GInstallNote = 121 + + +; ====== Structures ====== Structure GitCall - args.s ; ex: "status --porcelain" - workdir.s ; répertoire de travail - output.s ; stdout complet - errors.s ; stderr complet - exitcode.i ; code retour + args.s + workdir.s + output.s + errors.s + exitcode.i +EndStructure + +Structure FileRow + stat.s ; 2 lettres porcelain (" M", "A ", "??", etc.) + file.s ; chemin relatif + include.i ; 1=coché (inclus), 0=exclu EndStructure ; ====== Déclarations ====== @@ -43,12 +102,19 @@ Declare.i DoCommit(repoDir$, message$, doPush.i, remote$, branch$) Declare.i DoPush(repoDir$, remote$, branch$) Declare.i DoPull(repoDir$, remote$, branch$) Declare.i ListBranches(repoDir$, List branchesList.s()) +Declare.i LoadStatusRows(repoDir$, List rows.FileRow()) +Declare.i FillStatusList(List rows.FileRow()) +Declare.i ToggleIncludeAt(index.i, List rows.FileRow()) +Declare.i CollectIncludedFiles(List rows.FileRow(), List files.s()) +Declare.i DoCommitSelected(repoDir$, message$, doPush.i, remote$, branch$, List files.s()) +Declare.i OpenAdvancedWindow(repoDir$) +Declare.i SwitchToBranch(repoDir$, branch$) +Declare.i RestoreFromBranch(repoDir$, branch$) Declare.i InstallPBGitInIDE(ideExe$, toolsPrefs$, themeZip$) Declare.i OpenInstallWizard() ; ====== Utils ====== Procedure.s TrimNewlines(text$) - ; Supprime \r et \n en fin de chaîne Protected s$ = text$ While Right(s$, 1) = #LF$ Or Right(s$, 1) = #CR$ s$ = Left(s$, Len(s$)-1) @@ -56,20 +122,17 @@ Procedure.s TrimNewlines(text$) ProcedureReturn s$ EndProcedure -; Exécute git avec une structure d'appel passée par pointeur -; Remplit *call\output, *call\errors, *call\exitcode +; Exécute git avec capture stdout/stderr (structure passée par pointeur) Procedure.i RunGit(*call.GitCall) - Protected prg.i, line$ - Protected out$, err$ - + Protected prg.i, line$, out$, err$ If #EnableDebug - Debug "[RunGit] exe=" + #GitExe$ + " args=" + *call\args + " wd=" + *call\workdir + Debug "[RunGit] " + #GitExe$ + " " + *call\args + " (wd=" + *call\workdir + ")" EndIf prg = RunProgram(#GitExe$, *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 '" + #GitExe$ + "'." + *call\output = "" + *call\errors = "Impossible de lancer '" + #GitExe$ + "'." *call\exitcode = -1 ProcedureReturn *call\exitcode EndIf @@ -77,34 +140,20 @@ Procedure.i RunGit(*call.GitCall) While ProgramRunning(prg) While AvailableProgramOutput(prg) line$ = ReadProgramString(prg) - If line$ <> "" - out$ + line$ + #LF$ - EndIf + If line$ <> "" : out$ + line$ + #LF$ : EndIf Wend - - ; ReadProgramError() renvoie "" s'il n'y a rien (non bloquant) - line$ = ReadProgramError(prg) - If line$ <> "" - err$ + line$ + #LF$ - EndIf - + line$ = ReadProgramError(prg) ; renvoie "" si rien à lire (non bloquant) + If line$ <> "" : err$ + line$ + #LF$ : EndIf Delay(5) Wend - ; Vidange finale stdout While AvailableProgramOutput(prg) line$ = ReadProgramString(prg) - If line$ <> "" - out$ + line$ + #LF$ - EndIf + If line$ <> "" : out$ + line$ + #LF$ : EndIf Wend - - ; Vidange finale stderr (jusqu'à chaîne vide) Repeat line$ = ReadProgramError(prg) - If line$ = "" - Break - EndIf + If line$ = "" : Break : EndIf err$ + line$ + #LF$ ForEver @@ -123,9 +172,8 @@ Procedure.i RunGit(*call.GitCall) EndProcedure Procedure.s DetectRepoRoot(startDir$) - ; Renvoie le toplevel git si présent, sinon startDir$ Protected gc.GitCall - gc\args = "rev-parse --show-toplevel" + gc\args = "rev-parse --show-toplevel" gc\workdir = startDir$ If RunGit(@gc) = 0 ProcedureReturn TrimNewlines(gc\output) @@ -169,20 +217,16 @@ Procedure.i EnsureGitAvailable() If RunGit(@gc) = 0 And FindString(LCase(gc\output), "git version", 1) ProcedureReturn 1 EndIf - MessageRequester("PBIDE-GitTool", "Git n'est pas détecté à l'emplacement prévu : " + #GitExe$, #PB_MessageRequester_Warning) + MessageRequester("PBIDE-GitTool", "Git n'est pas détecté à l’emplacement prévu : " + #GitExe$, #PB_MessageRequester_Warning) ProcedureReturn 0 EndProcedure +; Détermine un répertoire pertinent (project → env → --repo → exe) Procedure.s DirFromArgOrFallback() - ; 1) --project "" si fourni (via %PROJECT) - ; 2) PB_TOOL_Project -> GetPathPart() - ; 3) --repo "" ou 1er argument libre - ; 4) dossier de l'exécutable Protected n.i = CountProgramParameters() Protected gotNextProject.i = 0 Protected gotNextRepo.i = 0 - Protected dir$ = "" - Protected i.i, p$ + Protected dir$ = "", i.i, p$ For i = 0 To n - 1 p$ = ProgramParameter(i) @@ -202,7 +246,7 @@ Procedure.s DirFromArgOrFallback() Next If dir$ <> "" - If #EnableDebug : Debug "[DirFromArgOrFallback] from args: " + dir$ : EndIf + If #EnableDebug : Debug "[Dir] from args: " + dir$ : EndIf ProcedureReturn dir$ EndIf @@ -210,23 +254,23 @@ Procedure.s DirFromArgOrFallback() If projFile$ <> "" Protected projDir$ = GetPathPart(projFile$) If projDir$ <> "" - If #EnableDebug : Debug "[DirFromArgOrFallback] from PB_TOOL_Project: " + projDir$ : EndIf + If #EnableDebug : Debug "[Dir] from PB_TOOL_Project: " + projDir$ : EndIf ProcedureReturn projDir$ EndIf EndIf dir$ = GetPathPart(ProgramFilename()) - If #EnableDebug : Debug "[DirFromArgOrFallback] fallback exe dir: " + dir$ : EndIf + If #EnableDebug : Debug "[Dir] fallback exe dir: " + dir$ : EndIf ProcedureReturn dir$ EndProcedure ; ====== Opérations Git de base ====== Procedure.i DoInitRepo(repoDir$) Protected gc.GitCall - gc\args = "init" + gc\args = "init" gc\workdir = repoDir$ If RunGit(@gc) = 0 - MessageRequester("Git init", "Répertoire initialisé comme dépôt Git." + #LF$ + TrimNewlines(gc\output), #PB_MessageRequester_Info) + MessageRequester("Git init", "Répertoire initialisé." + #LF$ + TrimNewlines(gc\output), #PB_MessageRequester_Info) ProcedureReturn 1 EndIf MessageRequester("Git init", "Échec: " + #LF$ + TrimNewlines(gc\errors), #PB_MessageRequester_Error) @@ -235,7 +279,7 @@ EndProcedure Procedure.i DoStatus(repoDir$, out$) Protected gc.GitCall - gc\args = "status --porcelain" + gc\args = "status --porcelain" gc\workdir = repoDir$ If RunGit(@gc) <> 0 out$ = "" @@ -247,23 +291,21 @@ Procedure.i DoStatus(repoDir$, out$) EndProcedure Procedure.i DoCommit(repoDir$, message$, doPush.i, remote$, branch$) - Protected gc.GitCall - - ; add -A - gc\args = "add -A" + Protected gc.GitCall, code.i gc\workdir = repoDir$ - If RunGit(@gc) <> 0 + + gc\args = "add -A" + code = RunGit(@gc) + If code <> 0 MessageRequester("Git add", "Échec: " + #LF$ + TrimNewlines(gc\errors), #PB_MessageRequester_Error) ProcedureReturn 0 EndIf - ; commit gc\args = "commit -m " + Chr(34) + message$ + Chr(34) - If RunGit(@gc) <> 0 + code = RunGit(@gc) + If code <> 0 MessageRequester("Git commit", "Échec ou rien à valider: " + #LF$ + TrimNewlines(gc\errors) + #LF$ + TrimNewlines(gc\output), #PB_MessageRequester_Warning) - If doPush = 0 - ProcedureReturn 0 - EndIf + If doPush = 0 : ProcedureReturn 0 : EndIf Else MessageRequester("Git commit", "OK:" + #LF$ + TrimNewlines(gc\output), #PB_MessageRequester_Info) EndIf @@ -271,13 +313,12 @@ Procedure.i DoCommit(repoDir$, message$, doPush.i, remote$, branch$) If doPush ProcedureReturn DoPush(repoDir$, remote$, branch$) EndIf - ProcedureReturn 1 EndProcedure Procedure.i DoPush(repoDir$, remote$, branch$) Protected gc.GitCall - gc\args = "push " + remote$ + " " + branch$ + gc\args = "push " + remote$ + " " + branch$ gc\workdir = repoDir$ If RunGit(@gc) = 0 MessageRequester("Git push", "OK:" + #LF$ + TrimNewlines(gc\output), #PB_MessageRequester_Info) @@ -289,7 +330,7 @@ EndProcedure Procedure.i DoPull(repoDir$, remote$, branch$) Protected gc.GitCall - gc\args = "pull " + remote$ + " " + branch$ + gc\args = "pull " + remote$ + " " + branch$ gc\workdir = repoDir$ If RunGit(@gc) = 0 MessageRequester("Git pull", "OK:" + #LF$ + TrimNewlines(gc\output), #PB_MessageRequester_Info) @@ -300,11 +341,9 @@ Procedure.i DoPull(repoDir$, remote$, branch$) EndProcedure Procedure.i ListBranches(repoDir$, List branchesList.s()) - ; Remplit la liste 'branchesList()' avec les noms de branches ClearList(branchesList()) - Protected gc.GitCall - gc\args = "branch --list --format='%(refname:short)'" + gc\args = "branch --list --format='%(refname:short)'" gc\workdir = repoDir$ If RunGit(@gc) <> 0 @@ -322,12 +361,198 @@ Procedure.i ListBranches(repoDir$, List branchesList.s()) branchesList() = b$ EndIf Next i - ProcedureReturn 1 EndProcedure -; ====== Interface simple ====== -; Gadgets IDs (sans underscores) +; ====== Status → lignes et gestion des coches ====== +Procedure.i LoadStatusRows(repoDir$, List rows.FileRow()) + Protected gc.GitCall, text$, line$, n.i, i.i + gc\args = "status --porcelain" + gc\workdir = repoDir$ + If RunGit(@gc) <> 0 + MessageRequester("Git status", "Échec: " + #LF$ + TrimNewlines(gc\errors), #PB_MessageRequester_Error) + ProcedureReturn 0 + EndIf + + text$ = gc\output + n = CountString(text$, #LF$) + 1 + For i = 1 To n + line$ = Trim(StringField(text$, i, #LF$)) + If line$ <> "" + AddElement(rows()) + rows()\stat = Left(line$, 2) + rows()\file = Trim(Mid(line$, 4)) + rows()\include = 0 + EndIf + Next i + ProcedureReturn ListSize(rows()) +EndProcedure + +Procedure.i FillStatusList(List rows.FileRow()) + ClearGadgetItems(#GListStatus) + Protected idx.i = 0 + ForEach rows() + AddGadgetItem(#GListStatus, -1, rows()\stat + #LF$ + rows()\file) + If rows()\include + SetGadgetItemState(#GListStatus, idx, #PB_ListIcon_Checked) + Else + SetGadgetItemState(#GListStatus, idx, 0) + EndIf + idx + 1 + Next + ProcedureReturn CountGadgetItems(#GListStatus) +EndProcedure + +Procedure.i ToggleIncludeAt(index.i, List rows.FileRow()) + If index < 0 : ProcedureReturn 0 : EndIf + Protected c.i = CountGadgetItems(#GListStatus) + If index >= c : ProcedureReturn 0 : EndIf + + Protected state.i = GetGadgetItemState(#GListStatus, index) + Protected want.i + If state & #PB_ListIcon_Checked : want = 1 : Else : want = 0 : EndIf + + Protected j.i = 0 + ForEach rows() + If j = index + rows()\include = want + Break + EndIf + j + 1 + Next + ProcedureReturn 1 +EndProcedure + +Procedure.i CollectIncludedFiles(List rows.FileRow(), List files.s()) + ClearList(files()) + ForEach rows() + If rows()\include + AddElement(files()) + files() = rows()\file + EndIf + Next + ProcedureReturn ListSize(files()) +EndProcedure + +; Commit sélectif si des fichiers sont cochés +Procedure.i DoCommitSelected(repoDir$, message$, doPush.i, remote$, branch$, List files.s()) + If ListSize(files()) = 0 + ProcedureReturn DoCommit(repoDir$, message$, doPush, remote$, branch$) + EndIf + + Protected gc.GitCall, code.i, argsAdd$, q$ + gc\workdir = repoDir$ + q$ = Chr(34) + + argsAdd$ = "add" + ForEach files() + argsAdd$ + " " + q$ + files() + q$ + Next + gc\args = argsAdd$ + code = RunGit(@gc) + If code <> 0 + MessageRequester("Git add", "Échec: " + #LF$ + TrimNewlines(gc\errors), #PB_MessageRequester_Error) + ProcedureReturn 0 + EndIf + + gc\args = "commit -m " + Chr(34) + message$ + Chr(34) + code = RunGit(@gc) + If code <> 0 + MessageRequester("Git commit", "Échec ou rien à valider: " + #LF$ + TrimNewlines(gc\errors) + #LF$ + TrimNewlines(gc\output), #PB_MessageRequester_Warning) + If doPush = 0 : ProcedureReturn 0 : EndIf + Else + MessageRequester("Git commit", "OK:" + #LF$ + TrimNewlines(gc\output), #PB_MessageRequester_Info) + EndIf + + If doPush + ProcedureReturn DoPush(repoDir$, remote$, branch$) + EndIf + ProcedureReturn 1 +EndProcedure + +; ====== Fenêtre Avancé… (branches) ====== +#WAdv = 200 +#GAdvLabel = 210 +#GAdvCombo = 211 +#GAdvSwitch = 212 +#GAdvRestore = 213 +#GAdvClose = 214 + +Procedure.i OpenAdvancedWindow(repoDir$) + Protected b$ + NewList blist.s() + ListBranches(repoDir$, blist()) + + If OpenWindow(#WAdv, 0, 0, 440, 160, "Actions avancées — Branches", #PB_Window_SystemMenu | #PB_Window_ScreenCentered) + TextGadget(#GAdvLabel, 10, 14, 140, 24, "Branche :") + ComboBoxGadget(#GAdvCombo, 160, 12, 270, 24) + ForEach blist() : AddGadgetItem(#GAdvCombo, -1, blist()) : Next + GadgetToolTip(#GAdvCombo, "Sélectionnez la branche cible.") + + ButtonGadget(#GAdvSwitch, 10, 60, 170, 30, "Basculer sur la branche") + GadgetToolTip(#GAdvSwitch, "git switch (ou git checkout).") + + ButtonGadget(#GAdvRestore, 190, 60, 170, 30, "Restaurer depuis la branche") + GadgetToolTip(#GAdvRestore, "git restore --source -- . (remplace le contenu de travail).") + + ButtonGadget(#GAdvClose, 370, 60, 60, 30, "Fermer") + + Repeat + Protected ev.i = WaitWindowEvent() + If ev = #PB_Event_Gadget + Select EventGadget() + Case #GAdvSwitch + b$ = GetGadgetText(#GAdvCombo) + If b$ <> "" + If SwitchToBranch(repoDir$, b$) + MessageRequester("Branche", "Basculé sur '" + b$ + "'.", #PB_MessageRequester_Info) + EndIf + EndIf + + Case #GAdvRestore + b$ = GetGadgetText(#GAdvCombo) + If b$ <> "" + If MessageRequester("Restauration", "Cette action va restaurer le contenu depuis '" + b$ + "'." + #LF$ + "Continuer ?", #PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes + If RestoreFromBranch(repoDir$, b$) + MessageRequester("Restauration", "Contenu restauré depuis '" + b$ + "'.", #PB_MessageRequester_Info) + EndIf + EndIf + EndIf + + Case #GAdvClose + CloseWindow(#WAdv) + ProcedureReturn 1 + EndSelect + EndIf + Until ev = #PB_Event_CloseWindow + + ProcedureReturn 1 + EndIf + ProcedureReturn 0 +EndProcedure + +Procedure.i SwitchToBranch(repoDir$, branch$) + Protected gc.GitCall + gc\workdir = repoDir$ + gc\args = "switch " + Chr(34) + branch$ + Chr(34) + If RunGit(@gc) = 0 : ProcedureReturn 1 : EndIf + gc\args = "checkout " + Chr(34) + branch$ + Chr(34) ; fallback versions anciennes + If RunGit(@gc) = 0 : ProcedureReturn 1 : EndIf + MessageRequester("Branche", "Échec: " + #LF$ + TrimNewlines(gc\errors), #PB_MessageRequester_Error) + ProcedureReturn 0 +EndProcedure + +Procedure.i RestoreFromBranch(repoDir$, branch$) + Protected gc.GitCall + gc\workdir = repoDir$ + gc\args = "restore --source " + Chr(34) + branch$ + Chr(34) + " -- ." + If RunGit(@gc) = 0 : ProcedureReturn 1 : EndIf + MessageRequester("Restauration", "Échec: " + #LF$ + TrimNewlines(gc\errors), #PB_MessageRequester_Error) + ProcedureReturn 0 +EndProcedure + +; ====== UI principale ====== +; Gadgets IDs (inclut les NOUVEAUX boutons) #GWindow = 1 #GLabelRepo = 10 #GStringRepo = 11 @@ -346,81 +571,87 @@ EndProcedure #GCommit = 24 #GPull = 25 #GPush = 26 +#GInclude = 27 +#GExclude = 28 +#GIncludeAll = 29 +#GExcludeAll = 30 +#GAdvanced = 31 Procedure.i OpenGUI(initialDir$, prefsPath$) Protected repoDir$ = DetectRepoRoot(initialDir$) Protected remote$ = "", branch$ = "" LoadPrefs(prefsPath$, remote$, branch$) - If OpenWindow(#GWindow, 0, 0, 680, 520, "PBIDE-GitTool — Git simplifié pour PureBasic", #PB_Window_SystemMenu | #PB_Window_ScreenCentered) + If OpenWindow(#GWindow, 0, 0, 740, 560, "PBIDE-GitTool — Git simplifié pour PureBasic", #PB_Window_SystemMenu | #PB_Window_ScreenCentered) TextGadget(#GLabelRepo, 10, 12, 60, 22, "Dépôt :") - StringGadget(#GStringRepo, 80, 10, 480, 24, repoDir$) - ButtonGadget(#GButtonBrowse, 570, 10, 100, 24, "Parcourir…") + StringGadget(#GStringRepo, 80, 10, 540, 24, repoDir$) + ButtonGadget(#GButtonBrowse, 630, 10, 100, 24, "Parcourir…") - ListViewGadget(#GListStatus, 10, 46, 660, 250) - ButtonGadget(#GRefresh, 10, 302, 90, 26, "Rafraîchir") - ButtonGadget(#GInit, 110, 302, 90, 26, "Init repo") + ; Liste avec cases à cocher + ListIconGadget(#GListStatus, 10, 46, 720, 280, "Statut", 100, #PB_ListIcon_CheckBoxes | #PB_ListIcon_FullRowSelect) + AddGadgetColumn(#GListStatus, 1, "Fichier", 600) - TextGadget(#GLabelMsg, 10, 342, 100, 22, "Message :") - StringGadget(#GStringMsg, 110, 340, 560, 24, "") - CheckBoxGadget(#GCheckPush, 10, 372, 120, 22, "Pousser après") + ButtonGadget(#GRefresh, 10, 332, 90, 26, "Rafraîchir") + ButtonGadget(#GInit, 110, 332, 90, 26, "Init repo") + ButtonGadget(#GInclude, 210, 332, 90, 26, "Inclure") + ButtonGadget(#GExclude, 310, 332, 90, 26, "Exclure") + ButtonGadget(#GIncludeAll, 410, 332, 110, 26, "Tout inclure") + ButtonGadget(#GExcludeAll, 530, 332, 110, 26, "Tout exclure") + ButtonGadget(#GAdvanced, 650, 332, 80, 26, "Avancé…") + + TextGadget(#GLabelMsg, 10, 372, 100, 22, "Message :") + StringGadget(#GStringMsg, 110, 370, 620, 24, "") + CheckBoxGadget(#GCheckPush, 10, 402, 120, 22, "Pousser après") SetGadgetState(#GCheckPush, #True) - TextGadget(#GLabelRemote, 150, 372, 60, 22, "Remote :") - StringGadget(#GStringRemote, 210, 370, 120, 24, remote$) - TextGadget(#GLabelBranch, 340, 372, 60, 22, "Branche :") - ComboBoxGadget(#GComboBranch, 400, 370, 150, 24) - ButtonGadget(#GSavePrefs, 560, 370, 110, 24, "Sauver défauts") + TextGadget(#GLabelRemote, 150, 402, 60, 22, "Remote :") + StringGadget(#GStringRemote, 210, 400, 120, 24, remote$) + TextGadget(#GLabelBranch, 340, 402, 60, 22, "Branche :") + ComboBoxGadget(#GComboBranch, 400, 400, 170, 24) + ButtonGadget(#GSavePrefs, 580, 400, 150, 24, "Sauver défauts") - ButtonGadget(#GCommit, 10, 410, 120, 30, "Add + Commit") - ButtonGadget(#GPush, 140, 410, 120, 30, "Push") - ButtonGadget(#GPull, 270, 410, 120, 30, "Pull") + ButtonGadget(#GCommit, 10, 440, 120, 30, "Add + Commit") + ButtonGadget(#GPush, 140, 440, 120, 30, "Push") + ButtonGadget(#GPull, 270, 440, 120, 30, "Pull") - ; --------- Infobulles (tooltips) --------- + ; Infobulles GadgetToolTip(#GStringRepo, "Dossier du dépôt Git. Utilisez 'Parcourir…' pour le sélectionner.") GadgetToolTip(#GButtonBrowse, "Choisir le répertoire du dépôt.") - GadgetToolTip(#GListStatus, "État des fichiers (git status --porcelain).") - GadgetToolTip(#GRefresh, "Rafraîchir l’état du dépôt.") + GadgetToolTip(#GListStatus, "Cochez pour inclure un fichier dans le prochain commit. Colonnes : Statut / Fichier.") + GadgetToolTip(#GRefresh, "Rafraîchir l’état du dépôt (git status --porcelain).") GadgetToolTip(#GInit, "Initialiser un dépôt Git ici (git init).") + GadgetToolTip(#GInclude, "Inclure l’élément sélectionné.") + GadgetToolTip(#GExclude, "Exclure l’élément sélectionné.") + GadgetToolTip(#GIncludeAll, "Inclure tous les éléments de la liste.") + GadgetToolTip(#GExcludeAll, "Exclure tous les éléments de la liste.") + GadgetToolTip(#GAdvanced, "Ouvrir les actions avancées (branches, restauration…).") GadgetToolTip(#GStringMsg, "Message de commit.") GadgetToolTip(#GCheckPush, "Activer pour pousser après le commit (git push).") GadgetToolTip(#GStringRemote, "Nom du remote (ex: origin).") - GadgetToolTip(#GLabelRemote, "Nom du remote par défaut.") GadgetToolTip(#GComboBranch, "Branche cible (ex: main).") GadgetToolTip(#GSavePrefs, "Sauver remote/branche comme valeurs par défaut.") - GadgetToolTip(#GCommit, "git add -A puis git commit -m "+Chr(34)+"message"+Chr(34)+" (et push si coché).") + GadgetToolTip(#GCommit, ~"git add (fichiers cochés ou -A si aucun coché) puis git commit -m \"message\" (et push si coché).") GadgetToolTip(#GPush, "Pousser la branche (git push).") GadgetToolTip(#GPull, "Récupérer les changements distants (git pull).") - ; Remplir branches via la LISTE + ; Branches et status NewList branchItems.s() ListBranches(repoDir$, branchItems()) ClearGadgetItems(#GComboBranch) - ForEach branchItems() - AddGadgetItem(#GComboBranch, -1, branchItems()) - Next - If branch$ <> "" - SetGadgetText(#GComboBranch, branch$) - EndIf + ForEach branchItems() : AddGadgetItem(#GComboBranch, -1, branchItems()) : Next + If branch$ <> "" : SetGadgetText(#GComboBranch, branch$) : EndIf - ; Status initial - Protected stat$, line$, n.i, i.i - If DoStatus(repoDir$, stat$) - ClearGadgetItems(#GListStatus) - n = CountString(stat$, #LF$) + 1 - For i = 1 To n - line$ = StringField(stat$, i, #LF$) - If Trim(line$) <> "" - AddGadgetItem(#GListStatus, -1, line$) - EndIf - Next i - EndIf + NewList rows.FileRow() + LoadStatusRows(repoDir$, rows()) + FillStatusList(rows()) + ; Boucle Repeat Protected ev.i = WaitWindowEvent() Select ev Case #PB_Event_Gadget Select EventGadget() + Case #GButtonBrowse Protected newDir$ = PathRequester("Choisir le répertoire du dépôt", repoDir$) If newDir$ <> "" @@ -429,27 +660,55 @@ Procedure.i OpenGUI(initialDir$, prefsPath$) ClearList(branchItems()) ListBranches(repoDir$, branchItems()) ClearGadgetItems(#GComboBranch) - ForEach branchItems() - AddGadgetItem(#GComboBranch, -1, branchItems()) - Next + ForEach branchItems() : AddGadgetItem(#GComboBranch, -1, branchItems()) : Next + ClearList(rows()) + LoadStatusRows(repoDir$, rows()) + FillStatusList(rows()) EndIf Case #GRefresh repoDir$ = GetGadgetText(#GStringRepo) - If DoStatus(repoDir$, stat$) - ClearGadgetItems(#GListStatus) - n = CountString(stat$, #LF$) + 1 - For i = 1 To n - line$ = StringField(stat$, i, #LF$) - If Trim(line$) <> "" - AddGadgetItem(#GListStatus, -1, line$) - EndIf - Next i - EndIf + ClearList(rows()) + LoadStatusRows(repoDir$, rows()) + FillStatusList(rows()) Case #GInit repoDir$ = GetGadgetText(#GStringRepo) DoInitRepo(repoDir$) + ClearList(rows()) + LoadStatusRows(repoDir$, rows()) + FillStatusList(rows()) + + Case #GInclude + Protected idx.i = GetGadgetState(#GListStatus) + If idx >= 0 + SetGadgetItemState(#GListStatus, idx, #PB_ListIcon_Checked) + ToggleIncludeAt(idx, rows()) + EndIf + + Case #GExclude + idx = GetGadgetState(#GListStatus) + If idx >= 0 + SetGadgetItemState(#GListStatus, idx, 0) + ToggleIncludeAt(idx, rows()) + EndIf + + Case #GIncludeAll + Protected c.i = CountGadgetItems(#GListStatus) + For idx = 0 To c - 1 : SetGadgetItemState(#GListStatus, idx, #PB_ListIcon_Checked) : Next + ForEach rows() : rows()\include = 1 : Next + + Case #GExcludeAll + c = CountGadgetItems(#GListStatus) + For idx = 0 To c - 1 : SetGadgetItemState(#GListStatus, idx, 0) : Next + ForEach rows() : rows()\include = 0 : Next + + Case #GAdvanced + repoDir$ = GetGadgetText(#GStringRepo) + OpenAdvancedWindow(repoDir$) + ClearList(rows()) + LoadStatusRows(repoDir$, rows()) + FillStatusList(rows()) Case #GSavePrefs remote$ = GetGadgetText(#GStringRemote) @@ -468,7 +727,16 @@ Procedure.i OpenGUI(initialDir$, prefsPath$) If Trim(msg$) = "" MessageRequester("Commit", "Merci de saisir un message.", #PB_MessageRequester_Warning) Else - DoCommit(repoDir$, msg$, GetGadgetState(#GCheckPush), remote$, branch$) + NewList files.s() + CollectIncludedFiles(rows(), files()) + If ListSize(files()) > 0 + DoCommitSelected(repoDir$, msg$, GetGadgetState(#GCheckPush), remote$, branch$, files()) + Else + DoCommit(repoDir$, msg$, GetGadgetState(#GCheckPush), remote$, branch$) + EndIf + ClearList(rows()) + LoadStatusRows(repoDir$, rows()) + FillStatusList(rows()) EndIf Case #GPush @@ -483,78 +751,21 @@ Procedure.i OpenGUI(initialDir$, prefsPath$) branch$ = GetGadgetText(#GComboBranch) DoPull(repoDir$, remote$, branch$) + Case #GListStatus + idx = GetGadgetState(#GListStatus) + If idx >= 0 : ToggleIncludeAt(idx, rows()) : EndIf + EndSelect EndSelect - Until ev = #PB_Event_CloseWindow ProcedureReturn 1 EndIf - ProcedureReturn 0 EndProcedure -; ------------------------------------------------------------------ -; Installe l’intégration dans l’IDE PureBasic : -; - copie le thème d’icônes (zip) dans \Themes\ -; - lance l’IDE avec /A "" pour importer les outils -; Retourne 1 si OK, 0 sinon. -; ------------------------------------------------------------------ -Procedure.i InstallPBGitInIDE(ideExe$, toolsPrefs$, themeZip$) - Protected ok.i = 1 - Protected ideHome$, themesDir$, destZip$, args$, prg.i, copyOk.i - - If FileSize(ideExe$) <= 0 Or FileSize(toolsPrefs$) <= 0 Or FileSize(themeZip$) <= 0 - MessageRequester("Installation", "Chemins invalides. Merci de sélectionner PureBasic.exe, le fichier d’outils et le thème.", #PB_MessageRequester_Error) - ProcedureReturn 0 - EndIf - - ideHome$ = GetPathPart(ideExe$) - themesDir$ = ideHome$ + "Themes" + #PathSep$ - destZip$ = themesDir$ + GetFilePart(themeZip$) - - ; Créer le dossier Themes si besoin - If FileSize(themesDir$) <> -2 - If CreateDirectory(themesDir$) = 0 - MessageRequester("Installation", "Impossible de créer le dossier Themes : " + themesDir$, #PB_MessageRequester_Error) - ProcedureReturn 0 - EndIf - EndIf - - ; Copier le thème - copyOk = CopyFile(themeZip$, destZip$) - If copyOk = 0 - MessageRequester("Installation", "Échec de copie du thème vers : " + destZip$, #PB_MessageRequester_Error) - ok = 0 - EndIf - - ; Importer les outils via /A "" - If ok - args$ = "/A " + Chr(34) + toolsPrefs$ + Chr(34) - prg = RunProgram(ideExe$, args$, "", #PB_Program_Hide | #PB_Program_Wait) - If prg = 0 - MessageRequester("Installation", "Échec de l’import des outils (/A).", #PB_MessageRequester_Error) - ok = 0 - EndIf - EndIf - - If ok - MessageRequester("Installation", "Intégration importée !" + #LF$ + - "- Thème copié dans: " + destZip$ + #LF$ + - "- Outils importés via /A", #PB_MessageRequester_Info) - EndIf - - ProcedureReturn ok -EndProcedure - -; ------------------------------------------------------------------ -; Assistant d’installation graphique quand PBIDE-GitTool est lancé sans paramètre. -; - Permet de choisir PureBasic.exe, git_tools.prefs et PBGitTheme.zip -; - Appelle InstallPBGitInIDE() -; Retourne 1 si installation OK, 0 sinon. -; ------------------------------------------------------------------ - -; Gadgets de la fenêtre d'installation (évite toute collision avec l’UI principale) +; ====== Installation IDE ====== +; Fenêtre assistant #WInstall = 100 #GLabelIde = 110 #GStringIde = 111 @@ -569,33 +780,74 @@ EndProcedure #GInstallCancel = 120 #GInstallNote = 121 +Procedure.i InstallPBGitInIDE(ideExe$, toolsPrefs$, themeZip$) + Protected ok.i = 1, ideHome$, themesDir$, destZip$, args$, prg.i, copyok.i + + If FileSize(ideExe$) <= 0 Or FileSize(toolsPrefs$) <= 0 Or FileSize(themeZip$) <= 0 + MessageRequester("Installation", "Chemins invalides. Sélectionnez PureBasic.exe, le fichier d’outils et le thème.", #PB_MessageRequester_Error) + ProcedureReturn 0 + EndIf + + ideHome$ = GetPathPart(ideExe$) + themesDir$ = ideHome$ + "Themes" + #PathSep$ + destZip$ = themesDir$ + GetFilePart(themeZip$) + + If FileSize(themesDir$) <> -2 + If CreateDirectory(themesDir$) = 0 + MessageRequester("Installation", "Impossible de créer le dossier Themes : " + themesDir$, #PB_MessageRequester_Error) + ProcedureReturn 0 + EndIf + EndIf + + copyok = CopyFile(themeZip$, destZip$) + If copyok = 0 + MessageRequester("Installation", "Échec de copie du thème vers : " + destZip$, #PB_MessageRequester_Error) + ok = 0 + EndIf + + If ok + args$ = "/A " + Chr(34) + toolsPrefs$ + Chr(34) + prg = RunProgram(ideExe$, args$, "", #PB_Program_Hide | #PB_Program_Wait) + If prg = 0 + MessageRequester("Installation", "Échec de l’import des outils (/A).", #PB_MessageRequester_Error) + ok = 0 + EndIf + EndIf + + If ok + MessageRequester("Installation", "Intégration importée !" + #LF$ + + "- Thème copié : " + destZip$ + #LF$ + + "- Outils importés via /A", #PB_MessageRequester_Info) + EndIf + ProcedureReturn ok +EndProcedure + Procedure.i OpenInstallWizard() Protected exeDefault$ = "" Protected dirExe$ = GetPathPart(ProgramFilename()) Protected prefsDefault$ = dirExe$ + "git_tools.prefs" Protected themeDefault$ = dirExe$ + "PBGitTheme.zip" - + If OpenWindow(#WInstall, 0, 0, 680, 240, "PBIDE-GitTool — Assistant d’installation IDE", #PB_Window_SystemMenu | #PB_Window_ScreenCentered) TextGadget(#GLabelIde, 10, 16, 140, 22, "PureBasic.exe :") StringGadget(#GStringIde,160, 14, 420, 24, exeDefault$) ButtonGadget(#GButtonIde,590, 14, 80, 24, "Parcourir…") - + TextGadget(#GLabelTools, 10, 56, 140, 22, "Fichier outils :") StringGadget(#GStringTools,160, 54, 420, 24, prefsDefault$) ButtonGadget(#GButtonTools,590, 54, 80, 24, "Parcourir…") - + TextGadget(#GLabelTheme, 10, 96, 140, 22, "Thème icônes :") StringGadget(#GStringTheme,160, 94, 420, 24, themeDefault$) ButtonGadget(#GButtonTheme,590, 94, 80, 24, "Parcourir…") - + ButtonGadget(#GInstallGo, 380, 150, 140, 30, "Installer") ButtonGadget(#GInstallCancel, 530, 150, 140, 30, "Annuler") - - TextGadget(#GInstallNote, 10, 190, 660, 40, - "Conseil : placez 'git_tools.prefs' et 'PBGitTheme.zip' à côté de PBIDE-GitTool.exe pour auto-renseignement." + #LF$ + - "Après import, choisissez le thème dans Préférences → General → Themes, puis ajoutez des boutons 'Run tool' dans la Toolbar.") - - ; Infobulles de l’assistant + + TextGadget(#GInstallNote, 10, 190, 660, 40, + "Astuce : placez 'git_tools.prefs' et 'PBGitTheme.zip' à côté de PBIDE-GitTool.exe pour auto-renseignement." + #LF$ + + "Ensuite choisissez le thème (Préférences → Themes) et ajoutez les boutons 'Run tool' dans la Toolbar.") + GadgetToolTip(#GStringIde, "Chemin de PureBasic.exe (IDE).") GadgetToolTip(#GButtonIde, "Parcourir pour sélectionner l’exécutable de l’IDE PureBasic.") GadgetToolTip(#GStringTools, "Fichier d’outils externes à importer (/A).") @@ -604,7 +856,7 @@ Procedure.i OpenInstallWizard() GadgetToolTip(#GButtonTheme, "Parcourir le fichier 'PBGitTheme.zip'.") GadgetToolTip(#GInstallGo, "Lancer l’installation (copie du thème + import des outils).") GadgetToolTip(#GInstallCancel, "Fermer l’assistant sans rien modifier.") - + Repeat Protected ev.i = WaitWindowEvent() If ev = #PB_Event_Gadget @@ -612,46 +864,40 @@ Procedure.i OpenInstallWizard() Case #GButtonIde Protected pickExe$ = OpenFileRequester("Choisir PureBasic.exe", GetPathPart(GetGadgetText(#GStringIde)), "Exécutable|*.exe;*|Tous|*.*", 0) If pickExe$ <> "" : SetGadgetText(#GStringIde, pickExe$) : EndIf - + Case #GButtonTools Protected pickPrefs$ = OpenFileRequester("Choisir le fichier d'outils", GetGadgetText(#GStringTools), "Prefs|*.prefs;*.txt|Tous|*.*", 0) If pickPrefs$ <> "" : SetGadgetText(#GStringTools, pickPrefs$) : EndIf - + Case #GButtonTheme Protected pickZip$ = OpenFileRequester("Choisir le thème (zip)", GetGadgetText(#GStringTheme), "Zip|*.zip|Tous|*.*", 0) If pickZip$ <> "" : SetGadgetText(#GStringTheme, pickZip$) : EndIf - + Case #GInstallGo Protected ok.i = InstallPBGitInIDE(GetGadgetText(#GStringIde), GetGadgetText(#GStringTools), GetGadgetText(#GStringTheme)) - If ok - CloseWindow(#WInstall) - ProcedureReturn 1 - EndIf - + If ok : CloseWindow(#WInstall) : ProcedureReturn 1 : EndIf + Case #GInstallCancel CloseWindow(#WInstall) ProcedureReturn 0 EndSelect EndIf Until ev = #PB_Event_CloseWindow - + ProcedureReturn 0 EndIf - ProcedureReturn 0 EndProcedure ; ====== Entrée ====== +; On ne coupe pas si Git manque, pour permettre l’assistant d’installation If EnsureGitAvailable() = 0 - ; On n’arrête pas forcément l’outil si Git manque, pour l’assistant d’installation - ; Mais on avertit : Git sera requis pour les opérations. + ; Averti, mais continue. EndIf -; Emplacement prefs locale de l’outil +; Préférences locales de l’outil Define baseDoc$ = GetUserDirectory(#PB_Directory_Documents) -If Right(baseDoc$, 1) <> #PathSep$ - baseDoc$ + #PathSep$ -EndIf +If Right(baseDoc$, 1) <> #PathSep$ : baseDoc$ + #PathSep$ : EndIf Define prefsDir$ = baseDoc$ + "PBIDE-GitTool" + #PathSep$ If FileSize(prefsDir$) <> -2 : CreateDirectory(prefsDir$) : EndIf Define prefsPath$ = prefsDir$ + "settings.prefs" @@ -663,7 +909,7 @@ Define repoArg$ = "", msgArg$ = "", remoteArg$ = "", branchArg$ = "" Define wantStatus.i = 0, wantInit.i = 0, wantCommit.i = 0, wantPush.i = 0, wantPull.i = 0 Define w.i -; --- Si aucun paramètre : proposer l’installation IDE --- +; Si aucun paramètre : proposer l’installation IDE If argCount = 0 If MessageRequester("PBIDE-GitTool", "Aucun paramètre détecté." + #LF$ + "Souhaitez-vous installer l’intégration IDE (outils + thème) maintenant ?", #PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes OpenInstallWizard() @@ -671,10 +917,10 @@ If argCount = 0 EndIf EndIf -; Parsing arguments For w = 0 To argCount - 1 Define a$ = ProgramParameter(w) Select LCase(a$) + Case "--project" : If w + 1 < argCount : repoArg$ = ProgramParameter(w + 1) : EndIf Case "--repo" : If w + 1 < argCount : repoArg$ = ProgramParameter(w + 1) : EndIf Case "--status" : wantStatus = 1 : useGui = 0 Case "--init" : wantInit = 1 : useGui = 0 @@ -683,7 +929,6 @@ For w = 0 To argCount - 1 Case "--pull" : wantPull = 1 : useGui = 0 Case "--remote" : If w + 1 < argCount : remoteArg$ = ProgramParameter(w + 1) : EndIf Case "--branch" : If w + 1 < argCount : branchArg$ = ProgramParameter(w + 1) : EndIf - Case "--project" : If w + 1 < argCount : repoArg$ = ProgramParameter(w + 1) : EndIf EndSelect Next w @@ -692,19 +937,19 @@ If useGui If dir$ = "" : dir$ = DirFromArgOrFallback() : EndIf OpenGUI(dir$, prefsPath$) Else - ; Exécution non interactive + ; Mode non interactif Define remote$ = "", branch$ = "" LoadPrefs(prefsPath$, remote$, branch$) If remoteArg$ <> "" : remote$ = remoteArg$ : EndIf If branchArg$ <> "" : branch$ = branchArg$ : EndIf - + Define baseDir$ = repoArg$ If baseDir$ = "" : baseDir$ = DirFromArgOrFallback() : EndIf baseDir$ = DetectRepoRoot(baseDir$) - + If wantStatus - Define st$, ok.i = DoStatus(baseDir$, st$) - If ok : PrintN(st$) : EndIf + Define st$, okstat.i = DoStatus(baseDir$, st$) + If okstat : PrintN(st$) : EndIf ElseIf wantInit DoInitRepo(baseDir$) ElseIf wantCommit @@ -718,9 +963,9 @@ Else EndIf ; IDE Options = PureBasic 6.21 (Windows - x64) -; CursorPosition = 390 -; FirstLine = 386 -; Folding = --- +; CursorPosition = 632 +; FirstLine = 628 +; Folding = ----- ; EnableXP ; DPIAware ; Executable = ..\PBIDE-GitTool.exe diff --git a/PureBasic_Compilation0.exe b/PureBasic_Compilation0.exe index 9978629..4e6842c 100644 Binary files a/PureBasic_Compilation0.exe and b/PureBasic_Compilation0.exe differ