[IDE-Tool] Templates Backup

Working on new editor enhancements?
User avatar
HeX0R
Addict
Addict
Posts: 1219
Joined: Mon Sep 20, 2004 7:12 am
Location: Hell

[IDE-Tool] Templates Backup

Post by HeX0R »

I didn't touch PB for a while and was going to create a small tool today.
When opening my templates, I was quite shocked, it was simply E.M.P.T.Y.!
So ~100 templates gone, nice!
I read about that problem in the past (seems to happen rarely only) and always thought "pff.. guess it was the users fault, that will never happen to me".
Well.. here we are now, quite frustrated, I found a three year old backup at least, better than nothing.

Anyway, long story short, it was time for a quick backup IDE tool for the Templates:

Code: Select all

; [IDE Tool]
; Template Backup  V1.1
; (c) HeX0R 2025
; https://www.purebasic.fr/english/viewtopic.php?p=638730#p638730
;
; Compile, add as PB IDE Tool, give it some name, no Arguments and let it be triggered by "Editor closing"
; at first start it will ask for a backup path
; whenever Templates.prefs size has changed, it will add a backup there.
; they will be named (keep it simple):
; Templates_001.prefs
; Templates_002.prefs
; ...
; New in V1.1
; Because it happened again to me (PB deleted my Templates), I've added a restore function
; Just start it outside of PB (not as tool) and a restore GUI will come up.
; You can also remove old backups (DEL key), but this means also, you can't rely on the filename numbers any longer to find the latest version.
; That shouldn't be a big problem due to the new backup functionality.
; Files will be shown from latest to oldest, no matter the filenames

EnableExplicit

Structure Templates
	Date.q
	FileName$
EndStructure

Global NewList BkTemplate.Templates()
Global BackupPath$, PrefPath$

Runtime Procedure OnClick_CheckBox()
	Protected State
	If GetGadgetState(DialogGadget(0, "checkbox_default_path"))
		State = 1
	EndIf
	DisableGadget(DialogGadget(0, "text_template_path"), State)
	DisableGadget(DialogGadget(0, "string_template_path"), State)
	DisableGadget(DialogGadget(0, "button_browse"), State)
EndProcedure

Runtime Procedure OnChange_String()
	;maybe later
EndProcedure

Runtime Procedure OnClick_Browse()
	Protected File$
	
	File$ = PathRequester("Path to Templates.prefs", "")
	If File$
		SetGadgetText(DialogGadget(0, "string_template_path"), File$)
	EndIf
EndProcedure

Runtime Procedure OnClick_DoRestore()
	Protected i, *D.Templates, Path$
	
	i = GetGadgetState(DialogGadget(0, "listicon_backups"))
	If i > -1
		*D = GetGadgetItemData(DialogGadget(0, "listicon_backups"), i)
		If MessageRequester("Restore Template", "Make sure that the PB IDE is closed or it will overwrite the templates when closing, again!" + #LF$ + "Do you want to proceed?", #PB_MessageRequester_Info | #PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes
			If GetGadgetState(DialogGadget(0, "checkbox_default_path"))
				Path$ = GetUserDirectory(#PB_Directory_ProgramData) + "PureBasic" + #PS$
			Else
				Path$ = GetGadgetText(DialogGadget(0, "string_template_path"))
				If Path$ And Right(Path$, 1) <> #PS$
					Path$ + #PS$
				EndIf
				If Path$
					If OpenPreferences(GetUserDirectory(#PB_Directory_ProgramData) + "hex0r" + #PS$ + "IDE_bk" + #PS$ + "bk.prefs")
						WritePreferenceString("PrefPath", Path$)
						ClosePreferences()
					EndIf
				EndIf
			EndIf
			CopyFile(*D\FileName$, Path$ + "Templates.prefs")
			DisableGadget(DialogGadget(0, "button_restore"), 1)
			SetGadgetState(DialogGadget(0, "listicon_backups"), -1)
		EndIf
	EndIf
EndProcedure

Runtime Procedure OnChange_Backups()
	Protected i
	
	i = GetGadgetState(DialogGadget(0, "listicon_backups"))
	If i > -1
		DisableGadget(DialogGadget(0, "button_restore"), 0)
	Else
		DisableGadget(DialogGadget(0, "button_restore"), 1)
	EndIf
EndProcedure

Procedure.s GetXMLString()
	Protected XML$

	XML$ + "<?xml version='1.0' encoding='UTF-16'?>"
	XML$ + ""
	XML$ + "<dialogs><!--Created by Dialog Design0R V1.87 => get it from: https://hex0rs.coderbu.de/en/sdm_downloads/dialogdesign0r/-->"
	XML$ + "  <window flags='#PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered' text='Restore Template' name='WinMain' xpos='1234' ypos='345'>"
	XML$ + "    <vbox>"
	XML$ + "      <frame text='Select Backup' name='frame_backups'>"
	XML$ + "        <listicon flags='#PB_ListIcon_GridLines | #PB_ListIcon_FullRowSelect | #PB_ListIcon_AlwaysShowSelection' height='200' width='500' text='Backup from' name='listicon_backups' onchange='OnChange_Backups()'/>"
	XML$ + "      </frame>"
	XML$ + "      <checkbox text='Use default Template path' name='checkbox_default_path' onevent='OnClick_CheckBox()'/>"
	XML$ + "      <hbox expand='item:2'>"
	XML$ + "        <text text='Path to Template (portable installations):' disabled='yes' name='text_template_path'/>"
	XML$ + "        <string disabled='yes' name='string_template_path' onchange='OnChange_String()'/>"
	XML$ + "        <button text='...' disabled='yes' name='button_browse' onevent='OnClick_Browse()'/>"
	XML$ + "      </hbox>"
	XML$ + "      <hbox align='center' expand='no'>"
	XML$ + "        <button text='Do Restore' flags='#PB_Button_Default' disabled='yes' name='button_restore' onevent='OnClick_DoRestore()'/>"
	XML$ + "      </hbox>"
	XML$ + "    </vbox>"
	XML$ + "  </window>"
	XML$ + "</dialogs><!--DDesign0R Definition: PureBasic|1|1|1|__|-|0-->"
	XML$ + ""

	ProcedureReturn XML$
EndProcedure

Procedure$ GetTemplateContent(File$)
	Protected Dir, Temp, a$, Result$
	
	If ReadFile(0, File$)
		While Eof(0) = 0
			a$ = ReadString(0)
			a$ = Trim(a$)
			If Left(a$, 10) = "Directory:"
				Dir + 1
			ElseIf Left(a$, 9) = "Template:"
				Temp + 1
			EndIf
		Wend
		CloseFile(0)
	EndIf
	Result$ = Str(Dir) + " Directories, " + Str(Temp) + " Templates"
	ProcedureReturn Result$
EndProcedure

Procedure OnDelete()
	Protected i, *D.Templates, Gadget
	
	Gadget = DialogGadget(0, "listicon_backups")
	i = GetGadgetState(Gadget)
	If i > -1
		If MessageRequester("Delete Entry", "Do you really want to delete the Template from " + GetGadgetItemText(Gadget, i, 0) + "?", #PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes
			*D = GetGadgetItemData(Gadget, i)
			DeleteFile(*D\FileName$)
			ChangeCurrentElement(BkTemplate(), *D)
			DeleteElement(BkTemplate())
			RemoveGadgetItem(Gadget, i)
			SetGadgetState(Gadget, i)
			OnChange_Backups()
		EndIf
	EndIf
EndProcedure

Procedure InitRestore()
	Protected i, Line$, LiGadget
	ParseXML(0, GetXMLString())
	CreateDialog(0)
	OpenXMLDialog(0, 0, "WinMain")
	LiGadget = DialogGadget(0, "listicon_backups")
	SetGadgetItemAttribute(LiGadget, 0, #PB_ListIcon_ColumnWidth, 140)
	AddGadgetColumn(LiGadget, 1, "Size", 80)
	AddGadgetColumn(LiGadget, 2, "Templates included", 200)
	If PrefPath$ = GetUserDirectory(#PB_Directory_ProgramData) + "PureBasic" + #PS$
		SetGadgetState(DialogGadget(0, "checkbox_default_path"), 1)
		OnClick_CheckBox()
	EndIf
	SetGadgetText(DialogGadget(0, "string_template_path"), PrefPath$)
	AddKeyboardShortcut(DialogWindow(0), #PB_Shortcut_Delete, 0)
	BindEvent(#PB_Event_Menu, @OnDelete())

	If ExamineDirectory(0, BackupPath$, "*.prefs")
		While NextDirectoryEntry(0)
			If DirectoryEntryType(0) = #PB_DirectoryEntry_File And Left(DirectoryEntryName(0), 10) = "Templates_"
				AddElement(BkTemplate())
				BkTemplate()\FileName$ = BackupPath$ + DirectoryEntryName(0)
				BkTemplate()\Date      = DirectoryEntryDate(0, #PB_Date_Modified)
			EndIf
		Wend
		FinishDirectory(0)
		SortStructuredList(BkTemplate(), #PB_Sort_Descending, OffsetOf(Templates\Date), #PB_Quad)
		ForEach BkTemplate()
			Line$ = FormatDate("%dd.%mm.%yyyy %hh:%ii:%ss", BkTemplate()\Date) + #LF$ + Str(FileSize(BkTemplate()\FileName$)) + #LF$
			Line$ + GetTemplateContent(BkTemplate()\FileName$)
			AddGadgetItem(LiGadget, -1, Line$)
			SetGadgetItemData(LiGadget, i, @BkTemplate())
			i + 1
		Next
	EndIf
EndProcedure

Procedure DoBackup(Template$)
	Protected i, Num = 1
	
	While FileSize(BackupPath$ + "Templates_" + RSet(Str(Num), 3, "0") + ".prefs") > -1
		Num + 1
	Wend
	CopyFile(Template$, BackupPath$ + "Templates_" + RSet(Str(Num), 3, "0") + ".prefs")
EndProcedure

Procedure MakeSureDirectoryExists(Dir$)
	Protected Count, i, CurrDir$
	
	If Right(Dir$, 1) <> #PS$
		Dir$ + #PS$
	EndIf
	Count = CountString(Dir$, #PS$)
	For i = 1 To Count
		CurrDir$ + StringField(Dir$, i, #PS$) + #PS$
		If FileSize(CurrDir$) <> -2
			CreateDirectory(CurrDir$)
		EndIf
	Next i
EndProcedure

Procedure main()
	Protected Path$, Template$, Own$
	Protected Template_Size
	
	Path$ = GetPathPart(GetEnvironmentVariable("PB_TOOL_Preferences"))
	Own$  = GetUserDirectory(#PB_Directory_ProgramData) + "hex0r" + #PS$ + "IDE_bk" + #PS$
	MakeSureDirectoryExists(Own$)
	OpenPreferences(Own$ + "bk.prefs")
	Template_Size = ReadPreferenceInteger("LastSize", -1)
	BackupPath$   = ReadPreferenceString("BackupPath", "")
	PrefPath$     = ReadPreferenceString("PrefPath", Path$)
	If PrefPath$ = ""
		PrefPath$ = GetUserDirectory(#PB_Directory_ProgramData) + "PureBasic" + #PS$
	EndIf
	
	ClosePreferences()
	
	If BackupPath$ = ""
		;first run
		BackupPath$ = PathRequester("Select path to store template backups to", "")
		If BackupPath$ = ""
			MessageRequester("Quit", "Can't go on without knowing the backup path, closing now!")
			ProcedureReturn
		Else
			CreatePreferences(Own$ + "bk.prefs")
			WritePreferenceString("BackupPath", BackupPath$)
			WritePreferenceString("PrefPath", PrefPath$)
			ClosePreferences()
		EndIf
	EndIf

	If Path$ = ""
		InitRestore()
		Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
		ProcedureReturn 
	EndIf
	Template$ = Path$ + "Templates.prefs"
	If FileSize(Template$) <> Template_Size
		Template_Size = FileSize(Template$)
		DoBackup(Template$)
		OpenPreferences(Own$ + "bk.prefs")
		WritePreferenceInteger("LastSize", Template_Size)
		ClosePreferences()
	EndIf
	
EndProcedure

main()
Last edited by HeX0R on Tue Oct 21, 2025 6:44 pm, edited 3 times in total.
Axolotl
Addict
Addict
Posts: 873
Joined: Wed Dec 31, 2008 3:36 pm

Re: Templates Backup Tool

Post by Axolotl »

Nice. Thanks for sharing.
To be honest, I don't really use the templates very much, but your post has reminded me that I should clean them up a bit.
Just because it worked doesn't mean it works.
PureBasic 6.04 (x86) and <latest stable version and current alpha/beta> (x64) on Windows 11 Home. Now started with Linux (VM: Ubuntu 22.04).
Quin
Addict
Addict
Posts: 1135
Joined: Thu Mar 31, 2022 7:03 pm
Location: Colorado, United States
Contact:

Re: Templates Backup Tool

Post by Quin »

I never explored the templates at all, thanks for introducing me to them! :D
Cool tool, too 8)
User avatar
le_magn
Enthusiast
Enthusiast
Posts: 289
Joined: Wed Aug 24, 2005 12:11 pm
Location: Italia

Re: Templates Backup Tool

Post by le_magn »

Thanks for this nice tool, added It to my PB...
Image
User avatar
HeX0R
Addict
Addict
Posts: 1219
Joined: Mon Sep 20, 2004 7:12 am
Location: Hell

Re: [IDE-Tool] Templates Backup

Post by HeX0R »

It happened again to me, therefore I've added a restore functionality.
Just start it outside of PB (not as a tool) to enter the restore GUI.
dcr3
Enthusiast
Enthusiast
Posts: 188
Joined: Fri Aug 04, 2017 11:03 pm

Re: [IDE-Tool] Templates Backup

Post by dcr3 »

Nice tool. Works. But restore button needs fixing. :mrgreen:
User avatar
HeX0R
Addict
Addict
Posts: 1219
Joined: Mon Sep 20, 2004 7:12 am
Location: Hell

Re: [IDE-Tool] Templates Backup

Post by HeX0R »

The button gets enabled as soon as you've selected a backup from the list, in case you meant that.
It doesn't make sense to enable a button "Restore", when there is nothing to restore.
AZJIO
Addict
Addict
Posts: 2226
Joined: Sun May 14, 2017 1:48 am

Re: [IDE-Tool] Templates Backup

Post by AZJIO »

Add a button to go to the "%APPDATE%\PureBasic" folder and simply make copies of all the files.
User avatar
HeX0R
Addict
Addict
Posts: 1219
Joined: Mon Sep 20, 2004 7:12 am
Location: Hell

Re: [IDE-Tool] Templates Backup

Post by HeX0R »

You want me to explain you the difference between a manual and an automatic backup, or what is the reason for that post?
AZJIO
Addict
Addict
Posts: 2226
Joined: Sun May 14, 2017 1:48 am

Re: [IDE-Tool] Templates Backup

Post by AZJIO »

I didn't understand how to use it. You say that you should have a backup, but where should it be and what should it be called? Theoretically, if the program restores from a backup, it should also be able to create a backup. When I started the tool, I didn't see any instructions on how to do this, except for an empty window.

You said it works on its own, so I just ran the source code with F5. Now I compiled and ran it through the launcher, but it didn't work. If I ran the compiled version without the launcher, I got the file in the list.
Last edited by AZJIO on Fri Oct 24, 2025 12:48 pm, edited 1 time in total.
User avatar
HeX0R
Addict
Addict
Posts: 1219
Joined: Mon Sep 20, 2004 7:12 am
Location: Hell

Re: [IDE-Tool] Templates Backup

Post by HeX0R »

It's in the source code

Code: Select all

; Compile, add as PB IDE Tool, give it some name, no Arguments and let it be triggered by "Editor closing"
; at first start it will ask for a backup path
; whenever Templates.prefs size has changed, it will add a backup there.
; they will be named (keep it simple):
; Templates_001.prefs
; Templates_002.prefs
Restoring had been added later, just to make it more comfortable and not have to copy files manually.
AZJIO
Addict
Addict
Posts: 2226
Joined: Sun May 14, 2017 1:48 am

Re: [IDE-Tool] Templates Backup

Post by AZJIO »

No backup path was suggested.
Can you add a "Open Backup Folder" button? I want to control the process. I can't find the path where the copies are created.

Found it, %AppData%\hex0r\IDE_bk
Why not use %AppData%\PureBasic\hex0r\IDE_bk?
User avatar
HeX0R
Addict
Addict
Posts: 1219
Joined: Mon Sep 20, 2004 7:12 am
Location: Hell

Re: [IDE-Tool] Templates Backup

Post by HeX0R »

The problem was, you didn't follow the instructions.
You didn't install it as tool, but started the restore part of the tool outside of the IDE first.
Then no backup path will be requested and the default one will be used.

In fact, the restore part is in best case never needed.
The purpose was to install it as tool, and forget about it for a long time.
Then, when you run into the same issue like me (templates got erased), only then you go the restore way.

It is no rocket science, it is an emergency tool, of course I could pimp it more, make it more fail proof, but it is fine for my needs and rescued my ass already one time.

The APPDATA path is fine, it's no official tool, therefore I'm not willing to add stuff to PureBasics very own data folder.
Post Reply