Files
PBIDE-GitTool/PBIDE-GitTool.pb
2025-08-15 07:09:05 +02:00

972 lines
33 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

; PBIDE-GitTool.pb — Outil externe Git pour lIDE PureBasic
; - 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 dinstallation 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
#EnableDebug = #True
; ====== Séparateur de chemin (portable) ======
CompilerIf #PB_Compiler_OS = #PB_OS_Windows
#PathSep$ = "\"
; Chemin Git forcé pour Windows :
#GitExe$ = "C:\Program Files\Git\cmd\git.exe"
CompilerElse
#PathSep$ = "/"
; Sur macOS/Linux on garde 'git' dans le PATH :
#GitExe$ = "git"
CompilerEndIf
; ====== 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 dinstallation
#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
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 ======
Declare.s TrimNewlines(text$)
Declare.i RunGit(*call.GitCall)
Declare.s DetectRepoRoot(startDir$)
Declare.i LoadPrefs(prefsPath$, remote$, branch$)
Declare.i SavePrefs(prefsPath$, remote$, branch$)
Declare.i EnsureGitAvailable()
Declare.i OpenGUI(initialDir$, prefsPath$)
Declare.s DirFromArgOrFallback()
Declare.i DoInitRepo(repoDir$)
Declare.i DoStatus(repoDir$, out$)
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$)
Protected s$ = text$
While Right(s$, 1) = #LF$ Or Right(s$, 1) = #CR$
s$ = Left(s$, Len(s$)-1)
Wend
ProcedureReturn s$
EndProcedure
; Exécute git avec capture stdout/stderr (structure passée par pointeur)
Procedure.i RunGit(*call.GitCall)
Protected prg.i, line$, out$, err$
If #EnableDebug
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\exitcode = -1
ProcedureReturn *call\exitcode
EndIf
While ProgramRunning(prg)
While AvailableProgramOutput(prg)
line$ = ReadProgramString(prg)
If line$ <> "" : out$ + line$ + #LF$ : EndIf
Wend
line$ = ReadProgramError(prg) ; renvoie "" si rien à lire (non bloquant)
If line$ <> "" : err$ + line$ + #LF$ : EndIf
Delay(5)
Wend
While AvailableProgramOutput(prg)
line$ = ReadProgramString(prg)
If line$ <> "" : out$ + line$ + #LF$ : EndIf
Wend
Repeat
line$ = ReadProgramError(prg)
If line$ = "" : Break : EndIf
err$ + line$ + #LF$
ForEver
*call\output = out$
*call\errors = err$
*call\exitcode = ProgramExitCode(prg)
CloseProgram(prg)
If #EnableDebug
Debug "[RunGit] code=" + Str(*call\exitcode)
If *call\output <> "" : Debug *call\output : EndIf
If *call\errors <> "" : Debug "[stderr] " + *call\errors : EndIf
EndIf
ProcedureReturn *call\exitcode
EndProcedure
Procedure.s DetectRepoRoot(startDir$)
Protected gc.GitCall
gc\args = "rev-parse --show-toplevel"
gc\workdir = startDir$
If RunGit(@gc) = 0
ProcedureReturn TrimNewlines(gc\output)
EndIf
ProcedureReturn startDir$
EndProcedure
Procedure.i LoadPrefs(prefsPath$, remote$, branch$)
If OpenPreferences(prefsPath$)
PreferenceGroup("git")
remote$ = ReadPreferenceString("remote", "origin")
branch$ = ReadPreferenceString("branch", "main")
ClosePreferences()
If #EnableDebug
Debug "[LoadPrefs] " + prefsPath$ + " remote=" + remote$ + " branch=" + branch$
EndIf
ProcedureReturn 1
EndIf
remote$ = "origin"
branch$ = "main"
ProcedureReturn 0
EndProcedure
Procedure.i SavePrefs(prefsPath$, remote$, branch$)
If CreatePreferences(prefsPath$)
PreferenceGroup("git")
WritePreferenceString("remote", remote$)
WritePreferenceString("branch", branch$)
ClosePreferences()
If #EnableDebug
Debug "[SavePrefs] " + prefsPath$ + " remote=" + remote$ + " branch=" + branch$
EndIf
ProcedureReturn 1
EndIf
ProcedureReturn 0
EndProcedure
Procedure.i EnsureGitAvailable()
Protected gc.GitCall
gc\args = "--version"
If RunGit(@gc) = 0 And FindString(LCase(gc\output), "git version", 1)
ProcedureReturn 1
EndIf
MessageRequester("PBIDE-GitTool", "Git n'est pas détecté à lemplacement prévu : " + #GitExe$, #PB_MessageRequester_Warning)
ProcedureReturn 0
EndProcedure
; Détermine un répertoire pertinent (project → env → --repo → exe)
Procedure.s DirFromArgOrFallback()
Protected n.i = CountProgramParameters()
Protected gotNextProject.i = 0
Protected gotNextRepo.i = 0
Protected dir$ = "", i.i, p$
For i = 0 To n - 1
p$ = ProgramParameter(i)
If gotNextProject
dir$ = p$
gotNextProject = 0
ElseIf gotNextRepo
dir$ = p$
gotNextRepo = 0
ElseIf LCase(p$) = "--project"
gotNextProject = 1
ElseIf LCase(p$) = "--repo"
gotNextRepo = 1
ElseIf Left(p$, 1) <> "-"
If dir$ = "" : dir$ = p$ : EndIf
EndIf
Next
If dir$ <> ""
If #EnableDebug : Debug "[Dir] from args: " + dir$ : EndIf
ProcedureReturn dir$
EndIf
Protected projFile$ = GetEnvironmentVariable("PB_TOOL_Project")
If projFile$ <> ""
Protected projDir$ = GetPathPart(projFile$)
If projDir$ <> ""
If #EnableDebug : Debug "[Dir] from PB_TOOL_Project: " + projDir$ : EndIf
ProcedureReturn projDir$
EndIf
EndIf
dir$ = GetPathPart(ProgramFilename())
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\workdir = repoDir$
If RunGit(@gc) = 0
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)
ProcedureReturn 0
EndProcedure
Procedure.i DoStatus(repoDir$, out$)
Protected gc.GitCall
gc\args = "status --porcelain"
gc\workdir = repoDir$
If RunGit(@gc) <> 0
out$ = ""
MessageRequester("Git status", "Échec: " + #LF$ + TrimNewlines(gc\errors), #PB_MessageRequester_Error)
ProcedureReturn 0
EndIf
out$ = gc\output
ProcedureReturn 1
EndProcedure
Procedure.i DoCommit(repoDir$, message$, doPush.i, remote$, branch$)
Protected gc.GitCall, code.i
gc\workdir = repoDir$
gc\args = "add -A"
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
Procedure.i DoPush(repoDir$, remote$, branch$)
Protected gc.GitCall
gc\args = "push " + remote$ + " " + branch$
gc\workdir = repoDir$
If RunGit(@gc) = 0
MessageRequester("Git push", "OK:" + #LF$ + TrimNewlines(gc\output), #PB_MessageRequester_Info)
ProcedureReturn 1
EndIf
MessageRequester("Git push", "Échec: " + #LF$ + TrimNewlines(gc\errors), #PB_MessageRequester_Error)
ProcedureReturn 0
EndProcedure
Procedure.i DoPull(repoDir$, remote$, branch$)
Protected gc.GitCall
gc\args = "pull " + remote$ + " " + branch$
gc\workdir = repoDir$
If RunGit(@gc) = 0
MessageRequester("Git pull", "OK:" + #LF$ + TrimNewlines(gc\output), #PB_MessageRequester_Info)
ProcedureReturn 1
EndIf
MessageRequester("Git pull", "Échec: " + #LF$ + TrimNewlines(gc\errors), #PB_MessageRequester_Error)
ProcedureReturn 0
EndProcedure
Procedure.i ListBranches(repoDir$, List branchesList.s())
ClearList(branchesList())
Protected gc.GitCall
gc\args = "branch --list --format='%(refname:short)'"
gc\workdir = repoDir$
If RunGit(@gc) <> 0
AddElement(branchesList()) : branchesList() = "main"
AddElement(branchesList()) : branchesList() = "master"
ProcedureReturn 0
EndIf
Protected out$ = ReplaceString(gc\output, "'", "")
Protected i.i, n.i = CountString(out$, #LF$) + 1
For i = 1 To n
Protected b$ = Trim(StringField(out$, i, #LF$))
If b$ <> ""
AddElement(branchesList())
branchesList() = b$
EndIf
Next i
ProcedureReturn 1
EndProcedure
; ====== 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 <branche> (ou git checkout).")
ButtonGadget(#GAdvRestore, 190, 60, 170, 30, "Restaurer depuis la branche")
GadgetToolTip(#GAdvRestore, "git restore --source <branche> -- . (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
#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
Procedure.i OpenGUI(initialDir$, prefsPath$)
Protected repoDir$ = DetectRepoRoot(initialDir$)
Protected remote$ = "", branch$ = ""
LoadPrefs(prefsPath$, remote$, branch$)
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, 540, 24, repoDir$)
ButtonGadget(#GButtonBrowse, 630, 10, 100, 24, "Parcourir…")
; Liste avec cases à cocher
ListIconGadget(#GListStatus, 10, 46, 720, 280, "Statut", 100, #PB_ListIcon_CheckBoxes | #PB_ListIcon_FullRowSelect)
AddGadgetColumn(#GListStatus, 1, "Fichier", 600)
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, 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, 440, 120, 30, "Add + Commit")
ButtonGadget(#GPush, 140, 440, 120, 30, "Push")
ButtonGadget(#GPull, 270, 440, 120, 30, "Pull")
; 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, "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(#GComboBranch, "Branche cible (ex: main).")
GadgetToolTip(#GSavePrefs, "Sauver remote/branche comme valeurs par défaut.")
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).")
; Branches et status
NewList branchItems.s()
ListBranches(repoDir$, branchItems())
ClearGadgetItems(#GComboBranch)
ForEach branchItems() : AddGadgetItem(#GComboBranch, -1, branchItems()) : Next
If branch$ <> "" : SetGadgetText(#GComboBranch, branch$) : 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$ <> ""
repoDir$ = newDir$
SetGadgetText(#GStringRepo, repoDir$)
ClearList(branchItems())
ListBranches(repoDir$, branchItems())
ClearGadgetItems(#GComboBranch)
ForEach branchItems() : AddGadgetItem(#GComboBranch, -1, branchItems()) : Next
ClearList(rows())
LoadStatusRows(repoDir$, rows())
FillStatusList(rows())
EndIf
Case #GRefresh
repoDir$ = GetGadgetText(#GStringRepo)
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)
branch$ = GetGadgetText(#GComboBranch)
If SavePrefs(prefsPath$, remote$, branch$)
MessageRequester("Préférences", "Valeurs par défaut enregistrées.", #PB_MessageRequester_Info)
Else
MessageRequester("Préférences", "Échec d'enregistrement.", #PB_MessageRequester_Error)
EndIf
Case #GCommit
repoDir$ = GetGadgetText(#GStringRepo)
remote$ = GetGadgetText(#GStringRemote)
branch$ = GetGadgetText(#GComboBranch)
Protected msg$ = GetGadgetText(#GStringMsg)
If Trim(msg$) = ""
MessageRequester("Commit", "Merci de saisir un message.", #PB_MessageRequester_Warning)
Else
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
repoDir$ = GetGadgetText(#GStringRepo)
remote$ = GetGadgetText(#GStringRemote)
branch$ = GetGadgetText(#GComboBranch)
DoPush(repoDir$, remote$, branch$)
Case #GPull
repoDir$ = GetGadgetText(#GStringRepo)
remote$ = GetGadgetText(#GStringRemote)
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
; ====== Installation IDE ======
; Fenêtre assistant
#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
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 doutils 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 limport 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 dinstallation 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,
"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 lexécutable de lIDE PureBasic.")
GadgetToolTip(#GStringTools, "Fichier doutils externes à importer (/A).")
GadgetToolTip(#GButtonTools, "Parcourir le fichier 'git_tools.prefs'.")
GadgetToolTip(#GStringTheme, "Archive ZIP du thème dicônes pour la Toolbar.")
GadgetToolTip(#GButtonTheme, "Parcourir le fichier 'PBGitTheme.zip'.")
GadgetToolTip(#GInstallGo, "Lancer linstallation (copie du thème + import des outils).")
GadgetToolTip(#GInstallCancel, "Fermer lassistant sans rien modifier.")
Repeat
Protected ev.i = WaitWindowEvent()
If ev = #PB_Event_Gadget
Select EventGadget()
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
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 lassistant dinstallation
If EnsureGitAvailable() = 0
; Averti, mais continue.
EndIf
; Préférences locales de loutil
Define baseDoc$ = GetUserDirectory(#PB_Directory_Documents)
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"
; Paramètres CLI
Define argCount.i = CountProgramParameters()
Define useGui.i = 1
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 linstallation IDE
If argCount = 0
If MessageRequester("PBIDE-GitTool", "Aucun paramètre détecté." + #LF$ + "Souhaitez-vous installer lintégration IDE (outils + thème) maintenant ?", #PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes
OpenInstallWizard()
End
EndIf
EndIf
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
Case "--commit" : wantCommit = 1 : useGui = 0 : If w + 1 < argCount : msgArg$ = ProgramParameter(w + 1) : EndIf
Case "--push" : wantPush = 1 : useGui = 0
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
EndSelect
Next w
If useGui
Define dir$ = repoArg$
If dir$ = "" : dir$ = DirFromArgOrFallback() : EndIf
OpenGUI(dir$, prefsPath$)
Else
; 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$, okstat.i = DoStatus(baseDir$, st$)
If okstat : PrintN(st$) : EndIf
ElseIf wantInit
DoInitRepo(baseDir$)
ElseIf wantCommit
If msgArg$ = "" : msgArg$ = "update" : EndIf
DoCommit(baseDir$, msgArg$, wantPush, remote$, branch$)
ElseIf wantPush
DoPush(baseDir$, remote$, branch$)
ElseIf wantPull
DoPull(baseDir$, remote$, branch$)
EndIf
EndIf
; IDE Options = PureBasic 6.21 (Windows - x64)
; CursorPosition = 632
; FirstLine = 628
; Folding = -----
; EnableXP
; DPIAware
; Executable = ..\PBIDE-GitTool.exe
; CompileSourceDirectory