A tiny library, OOP style, to replace the OpenFileRequester() as well as the SaveFileRequester(). It took me one day and a half to make it to work as I wanted. See the Programming Notes for more details.
EDIT 1 : A little update, it is now possible to filter more that one type of file extension. For example you want to be capable to select *.pb and *.pbi files at the same time. See the example 3rd settings for more details.
Best regards
StarBootics
Code: Select all
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Project Name : GTK FileChooser
; File Name : FileChooser - OOP.pb
; File Version : 1.0.2
; Programmation : OK
; Programmed by : StarBootics
; Date : March 19th, 2022
; Update : March 26th, 2022
; Coded for PureBasic : V6.00 Beta 5
; Platform : Linux
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Programming Notes
;
; This FileChooser can be used as a OpenFileRequester as well
; as a SaveFileRequester replacement.
;
; In Open mode, it's possible to open a single file or multiple
; files.
;
; In Save mode, the filename is being verified automatically
; to make sure that the file extension, if any, is the same as
; the selected one. If not, the file extension will be
; corrected. The "SelectMultiple" flag is ignored in Save mode.
;
; It's possible to add as much Settings as needed. Just make
; sure to call the FileChooser\UseSettings() method with the
; right settings index.
;
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
DeclareModule GtkFileChooser
#MODE_OPEN = 0
#MODE_SAVE = 1
Interface FileChooser
GetFileName.s()
AddSettings()
UseSettings(SettingID.i)
SetMode(Mode.i)
SetTitle(Title.s)
SetButtonText(ButtonID.i, Text.s)
SetPath(Path.s)
SetSelectMultiple(SelectMultiple.i)
SetDefaultPatternID(DefaultPatternID.i)
SetParent(*Parent)
AddPatterns(Label.s, Extension.s)
ClearPatterns()
SelectedPatternID.i()
SelectFileNames.i(Index.l)
NextFileNames.i()
ResetFileNames()
FileNamesSize.l()
Open.i()
Free()
EndInterface
Declare.i New()
EndDeclareModule
Module GtkFileChooser
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< GTK Methods importation <<<<<
ImportC ""
gtk_file_chooser_dialog_new(title.p-utf8, *parent.GtkWidget, action.i, b1.p-utf8, a1.i, b2.p-utf8, a2.i, null)
gtk_file_filter_set_name(*filter, label.p-utf8)
gtk_file_filter_add_pattern(*filter, pattern.p-utf8)
gtk_file_chooser_set_current_folder(*Dialog, path.p-utf8)
gtk_file_chooser_set_select_multiple(*Dialog, SelectMultiple.i)
EndImport
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Structures declaration <<<<<
Structure Pattern
*Filter
Label.s
Extension.s
EndStructure
Structure Setting
Mode.i
Title.s
ButtonText.s[2]
Path.s
SelectMultiple.i
DefaultPatternID.i
List Patterns.Pattern()
EndStructure
Structure Private_Members
VirtualTable.i
*Parent
List FileNames.s()
List Settings.Setting()
EndStructure
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The IsBetween macro <<<<<
Macro IsBetween(Value, Lower, Upper)
((Value) >= (Lower) And (Value) <= (Upper))
EndMacro
; <<<<<<<<<<<<<<<<<<<<<<
; <<<<< The getter <<<<<
Procedure.s GetFileName(*This.Private_Members)
If ListSize(*This\FileNames()) >= 1
FileName.s = *This\FileNames()
Else
FileName = ""
EndIf
ProcedureReturn FileName
EndProcedure
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The AddSettings operator <<<<<
Procedure AddSettings(*This.Private_Members)
AddElement(*This\Settings())
EndProcedure
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The UseSettings operator <<<<<
Procedure UseSettings(*This.Private_Members, SettingID.i)
If IsBetween(SettingID, 0, ListSize(*This\Settings()) - 1)
SelectElement(*This\Settings(), SettingID)
EndIf
EndProcedure
; <<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The Setters <<<<<
Procedure SetMode(*This.Private_Members, Mode.i)
*This\Settings()\Mode = Mode
EndProcedure
Procedure SetTitle(*This.Private_Members, Title.s)
*This\Settings()\Title = Title
EndProcedure
Procedure SetButtonText(*This.Private_Members, ButtonID.i, Text.s)
*This\Settings()\ButtonText[ButtonID] = Text
EndProcedure
Procedure SetPath(*This.Private_Members, Path.s)
*This\Settings()\Path = Path
EndProcedure
Procedure SetSelectMultiple(*This.Private_Members, SelectMultiple.i)
*This\Settings()\SelectMultiple = SelectMultiple
EndProcedure
Procedure SetDefaultPatternID(*This.Private_Members, DefaultPatternID.i)
*This\Settings()\DefaultPatternID = DefaultPatternID
EndProcedure
Procedure SetParent(*This.Private_Members, *Parent)
*This\Parent = *Parent
EndProcedure
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The AddPatterns operator <<<<<
Procedure AddPatterns(*This.Private_Members, Label.s, Extension.s)
AddElement(*This\Settings()\Patterns())
*This\Settings()\Patterns()\Label = Label
*This\Settings()\Patterns()\Extension = Extension
EndProcedure
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The ClearPatterns operator <<<<<
Procedure ClearPatterns(*This.Private_Members)
ClearList(*This\Settings()\Patterns())
EndProcedure
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The SelectedPatternID operator <<<<<
Procedure.i SelectedPatternID(*This.Private_Members)
ProcedureReturn ListIndex(*This\Settings()\Patterns())
EndProcedure
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The SelectFileNames operator <<<<<
Procedure.i SelectFileNames(*This.Private_Members, Index.l)
ProcedureReturn SelectElement(*This\FileNames(), Index)
EndProcedure
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The NextFileNames operator <<<<<
Procedure.i NextFileNames(*This.Private_Members)
ProcedureReturn NextElement(*This\FileNames())
EndProcedure
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The ResetFileNames operator <<<<<
Procedure ResetFileNames(*This.Private_Members)
ResetList(*This\FileNames())
EndProcedure
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The FileNamesSize operator <<<<<
Procedure.l FileNamesSize(*This.Private_Members)
ProcedureReturn ListSize(*This\FileNames())
EndProcedure
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The Open operator <<<<<
Procedure.i Open(*This.Private_Members)
Protected *Dialog, *Mem
If *This\Settings()\Mode = #MODE_OPEN
Action.i = #GTK_FILE_CHOOSER_ACTION_OPEN
Btn1.s = *This\Settings()\ButtonText[0]
Btn2.s = *This\Settings()\ButtonText[1]
Title.s = *This\Settings()\Title
ElseIf *This\Settings()\Mode = #MODE_SAVE
Action = #GTK_FILE_CHOOSER_ACTION_SAVE
Btn1 = *This\Settings()\ButtonText[0]
Btn2 = *This\Settings()\ButtonText[1]
Title = *This\Settings()\Title
EndIf
ClearList(*This\FileNames())
*Dialog = gtk_file_chooser_dialog_new(Title, *This\Parent, Action, Btn1, #GTK_RESPONSE_CANCEL, Btn2, #GTK_RESPONSE_ACCEPT, 0)
gtk_window_set_modal_(*Dialog, #True)
gtk_file_chooser_set_current_folder_(*Dialog, *This\Settings()\Path)
If *This\Settings()\Mode = #MODE_OPEN
gtk_file_chooser_set_select_multiple(*Dialog, *This\Settings()\SelectMultiple)
EndIf
ForEach *This\Settings()\Patterns()
*This\Settings()\Patterns()\Filter = gtk_file_filter_new_()
gtk_file_filter_set_name(*This\Settings()\Patterns()\Filter, *This\Settings()\Patterns()\Label)
If FindString(*This\Settings()\Patterns()\Extension, ";") = 0
gtk_file_filter_add_pattern(*This\Settings()\Patterns()\Filter, *This\Settings()\Patterns()\Extension)
Else
ExtensionMax.l = CountString(*This\Settings()\Patterns()\Extension, ";") + 1
For EntensionID = 1 To ExtensionMax
gtk_file_filter_add_pattern(*This\Settings()\Patterns()\Filter, StringField(*This\Settings()\Patterns()\Extension, EntensionID, ";"))
Next
EndIf
gtk_file_chooser_add_filter_(*Dialog, *This\Settings()\Patterns()\Filter)
Next
If IsBetween(*This\Settings()\DefaultPatternID, 0, ListSize(*This\Settings()\Patterns()) - 1)
SelectElement(*This\Settings()\Patterns(), *This\Settings()\DefaultPatternID)
gtk_file_chooser_Set_filter_(*Dialog, *This\Settings()\Patterns()\Filter)
EndIf
r1.l = gtk_dialog_run_(*Dialog)
If r1 = #GTK_RESPONSE_ACCEPT
Output.i = #True
*CurrentFilter = gtk_file_chooser_get_filter_(*Dialog)
If *CurrentFilter <> #Null
ForEach *This\Settings()\Patterns()
If *CurrentFilter = *This\Settings()\Patterns()\Filter
Break
EndIf
Next
EndIf
If *This\Settings()\Mode = #MODE_OPEN
If *This\Settings()\SelectMultiple = #False
*Mem = gtk_file_chooser_get_filename_(*Dialog)
If *Mem <> #Null
AddElement(*This\FileNames())
*This\FileNames() = PeekS(*Mem, -1, #PB_UTF8)
g_free_(*Mem)
EndIf
ElseIf *This\Settings()\SelectMultiple = #True
*FileList.GSList = gtk_file_chooser_get_filenames_(*Dialog)
*Node.GSList = *FileList
While *Node <> #Null
AddElement(*This\FileNames())
*This\FileNames() = PeekS(*Node\Data, -1, #PB_UTF8)
g_free_(*Node\Data)
*Node = *Node\Next
Wend
g_slist_free_(*FileList)
EndIf
ElseIf *This\Settings()\Mode = #MODE_SAVE
*Mem = gtk_file_chooser_get_filename_(*Dialog)
If *Mem <> #Null
; On considère que le chemin initiale est bon
GoodPath.s = PeekS(*Mem, -1, #PB_UTF8)
g_free_(*Mem)
SelectedExtension.s = StringField(*This\Settings()\Patterns()\Extension, 2, ".")
If SelectedExtension <> ""
; on découpe l'info du nom de fichier
Path.s = GetPathPart(GoodPath)
Name.s = GetFilePart(GoodPath)
CurrentExtension.s = GetExtensionPart(GoodPath)
; S'il y a des problèmes on reconstruit le chemin
If CurrentExtension = ""
GoodPath = Path + Name + "." + SelectedExtension
EndIf
If CurrentExtension <> SelectedExtension
GoodPath = Path + StringField(Name, 1, ".") + "." + SelectedExtension
EndIf
EndIf
AddElement(*This\FileNames())
*This\FileNames() = GoodPath
EndIf
EndIf
EndIf
gtk_widget_destroy_(*Dialog)
ProcedureReturn Output
EndProcedure
; <<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The Destructor <<<<<
Procedure Free(*This.Private_Members)
FreeStructure(*This)
EndProcedure
; <<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The Constructor <<<<<
Procedure.i New()
*This.Private_Members = AllocateStructure(Private_Members)
*This\VirtualTable = ?START_METHODS
ProcedureReturn *This
EndProcedure
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< The virtual table entries <<<<<
DataSection
START_METHODS:
Data.i @GetFileName()
Data.i @AddSettings()
Data.i @UseSettings()
Data.i @SetMode()
Data.i @SetTitle()
Data.i @SetButtonText()
Data.i @SetPath()
Data.i @SetSelectMultiple()
Data.i @SetDefaultPatternID()
Data.i @SetParent()
Data.i @AddPatterns()
Data.i @ClearPatterns()
Data.i @SelectedPatternID()
Data.i @SelectFileNames()
Data.i @NextFileNames()
Data.i @ResetFileNames()
Data.i @FileNamesSize()
Data.i @Open()
Data.i @Free()
END_METHODS:
EndDataSection
EndModule
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
CompilerIf #PB_Compiler_IsMainFile
gtk_init_(0, 0)
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; The FileChooser creation
FileChooser.GtkFileChooser::FileChooser = GtkFileChooser::New()
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; The FileChooser AddSettings
; (Need to be done only once)
FileChooser\AddSettings()
FileChooser\SetTitle("Test Open")
FileChooser\SetMode(GtkFileChooser::#MODE_OPEN)
FileChooser\SetButtonText(0, #GTK_STOCK_CANCEL)
FileChooser\SetButtonText(1, #GTK_STOCK_OPEN)
FileChooser\SetSelectMultiple(#True)
FileChooser\SetPath("/home/")
FileChooser\SetDefaultPatternID(1)
FileChooser\AddPatterns("PureBasic Source (*.pb)", "*.pb")
FileChooser\AddPatterns("PureBasic Include (*.pbi)", "*.pbi")
FileChooser\AddSettings()
FileChooser\SetTitle("Test Save")
FileChooser\SetMode(GtkFileChooser::#MODE_SAVE)
FileChooser\SetButtonText(0, #GTK_STOCK_CANCEL)
FileChooser\SetButtonText(1, #GTK_STOCK_SAVE)
FileChooser\SetSelectMultiple(#False)
FileChooser\SetPath("/home/")
FileChooser\SetDefaultPatternID(0)
FileChooser\AddPatterns("PureBasic Source (*.pb)", "*.pb")
FileChooser\AddPatterns("PureBasic Include (*.pbi)", "*.pbi")
FileChooser\AddSettings()
FileChooser\SetTitle("Test Open 2")
FileChooser\SetMode(GtkFileChooser::#MODE_OPEN)
FileChooser\SetButtonText(0, #GTK_STOCK_CANCEL)
FileChooser\SetButtonText(1, #GTK_STOCK_OPEN)
FileChooser\SetSelectMultiple(#True)
FileChooser\SetPath("/home/")
FileChooser\SetDefaultPatternID(0)
FileChooser\AddPatterns("PureBasic Code (*.pb, *.pbi)", "*.pb;*.pbi")
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; The FileChooser Open Multiple files test
FileChooser\UseSettings(0)
If FileChooser\Open()
FileChooser\ResetFileNames()
While FileChooser\NextFileNames()
Debug FileChooser\GetFileName()
Wend
EndIf
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; The FileChooser Save file test
FileChooser\UseSettings(1)
If FileChooser\Open()
Debug FileChooser\GetFileName()
EndIf
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; The FileChooser Open file test
FileChooser\UseSettings(0)
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; We change the "SelectMultiple" flag and the
; "DefaultPatternID" on the fly
FileChooser\SetSelectMultiple(#False)
FileChooser\SetDefaultPatternID(0)
If FileChooser\Open()
Debug FileChooser\GetFileName()
EndIf
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; The FileChooser Open file(s) 2 possible extensions test
FileChooser\UseSettings(2)
If FileChooser\Open()
FileChooser\ResetFileNames()
While FileChooser\NextFileNames()
Debug FileChooser\GetFileName()
Wend
EndIf
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; The FileChooser is no longer needed
FileChooser\Free()
CompilerEndIf
; <<<<<<<<<<<<<<<<<<<<<<<
; <<<<< END OF FILE <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<