# Property of Four Js* # (c) Copyright Four Js 1995, 2019. All Rights Reserved. # * Trademark of Four Js Development Tools Europe Ltd # in the United States and elsewhere # # Four Js and its suppliers do not warrant or guarantee that these # samples are accurate and suitable for your purposes. Their inclusion is # purely for information purposes only. #+ File open/save dialog box using MD IMPORT FGL fgldialog IMPORT os PUBLIC TYPE FILEDLG_RECORD RECORD title STRING, defaultfilename STRING, defaultpath STRING, opt_create_dirs SMALLINT, -- allows the creation of a new subdirectory --(not yet implemented) opt_delete_files SMALLINT, -- allows to delete files when running the dialog types DYNAMIC ARRAY OF RECORD -- list for the file type combobox description STRING, -- string to display suffixes STRING -- pipe separated string of all possible suffixes for one entry -- example "*.per|*.4gl" END RECORD END RECORD DEFINE _filedlg_list DYNAMIC ARRAY OF RECORD eimage STRING, -- image name entry STRING, -- Filename or Dirname esize INT, --file size emodt STRING, --modification time etype STRING -- C_DIRECTORY or "*.xxx File" END RECORD DEFINE last_opendlg_directory STRING DEFINE last_savedlg_directory STRING DEFINE m_typearr DYNAMIC ARRAY OF STRING DEFINE m_typelen INT CONSTANT C_DIRECTORY="Directory" CONSTANT C_OPEN="open" CONSTANT C_SAVE="save" #+ Opens a file dialog to open a file. #+ @returnType String #+ @return The selected file path, or NULL is canceled. #+ @param r the record describing the file dialog # FUNCTION filedlg_open(r) DEFINE r FILEDLG_RECORD DEFINE t, fn STRING IF r.defaultpath IS NULL THEN IF last_opendlg_directory IS NULL THEN LET last_opendlg_directory = "." END IF LET r.defaultpath = last_opendlg_directory END IF LET t= "Open File" IF r.title IS NOT NULL THEN LET t = r.title END IF LET fn = _filedlg_doDlg(C_OPEN,t,r.*) IF fn IS NOT NULL THEN LET last_opendlg_directory = _file_get_dirname(fn) END IF RETURN fn END FUNCTION #+ Opens a file dialog to save a file. #+ @returnType String #+ @return The selected file path, or NULL is canceled. #+ @param r The record describing the file dialog # FUNCTION filedlg_save(r) DEFINE r FILEDLG_RECORD DEFINE t, fn STRING IF r.defaultpath IS NULL THEN IF last_savedlg_directory IS NULL THEN LET last_savedlg_directory = "." END IF LET r.defaultpath = last_savedlg_directory END IF LET t = "Save File" IF r.title IS NOT NULL THEN LET t = r.title END IF LET fn = _filedlg_doDlg(C_SAVE,t,r.*) IF fn IS NOT NULL THEN LET last_savedlg_directory = _file_get_dirname(fn) END IF RETURN fn END FUNCTION ------------------- internal _filedlg_xxx functions ---------------------------- FUNCTION _filedlg_doDlg(dlgtype,title,r) DEFINE dlgtype STRING DEFINE title STRING DEFINE r FILEDLG_RECORD DEFINE currpath, path, filename, ftype, dirname, filepath STRING DEFINE delfilename, errstr STRING DEFINE doContinue, i INT DEFINE cb ui.ComboBox OPEN WINDOW _filedlg WITH FORM "filedlg" ATTRIBUTES(STYLE='dialog',TEXT=title) CALL fgl_settitle(title) LET currpath = r.defaultpath LET filename = r.defaultfilename DISPLAY BY NAME filename IF currpath="." THEN LET currpath=os.Path.pwd() END IF DISPLAY currpath TO currpath LET cb = ui.ComboBox.forName("formonly.ftype") IF cb IS NULL THEN DISPLAY "ERROR:form field \"ftype\" not found in form filedlg" EXIT PROGRAM END IF FOR i=1 TO r.types.getLength() CALL cb.addItem(r.types[i].suffixes,r.types[i].description) END FOR LET ftype=r.types[1].suffixes DIALOG ATTRIBUTE(UNBUFFERED) --use a DISPLAY ARRAY for showing the file list DISPLAY ARRAY _filedlg_list TO sr.* BEFORE DISPLAY --the following call is not effective, but should be!!! CALL DIALOG.setActionActive("del",r.opt_delete_files) BEFORE ROW IF _filedlg_list[arr_curr()].etype<>C_DIRECTORY THEN LET filename=_filedlg_list[arr_curr()].entry DISPLAY BY NAME filename END IF ON ACTION del --ask for deleting the highlighted file LET delfilename=_filedlg_list[arr_curr()].entry IF _filedlg_mbox_yn("Confirm delete",sfmt("Really delete '%1'?",delfilename),"question") THEN CALL _file_delete(os.Path.join(currpath,delfilename)) CALL _filedlg_fetch_filenames(DIALOG,currpath,ftype,NULL) END IF END DISPLAY INPUT BY NAME filename,ftype ATTRIBUTE(WITHOUT DEFAULTS) --when the type combobox changes we need to redisplay ON CHANGE ftype CALL _filedlg_fetch_filenames(DIALOG,currpath,ftype,NULL) END INPUT BEFORE DIALOG CALL _filedlg_fetch_filenames(DIALOG,currpath,ftype,filename) IF dlgtype = C_SAVE THEN NEXT FIELD filename END IF ON ACTION accept LET doContinue=FALSE --we use DIALOG.getCurrentItem() to detect where the focus currently is --it works similar like INFIELD however also for DISPLAY ARRAY IF DIALOG.getCurrentItem()="sr" THEN --we are in the display array LET filepath = os.Path.join(currpath,_filedlg_list[arr_curr()].entry) ELSE --not in display array LET filepath = os.Path.join(currpath,filename) END IF IF os.Path.exists(filepath) AND os.Path.isdirectory(filepath) THEN --switch the directory and refill the array LET currpath=_file_normalize_dir(filepath) CALL _filedlg_fetch_filenames(DIALOG,filepath,ftype,"..") DISPLAY BY NAME currpath LET filename="" LET doContinue=TRUE END IF IF NOT doContinue AND dlgtype = C_OPEN THEN IF NOT os.Path.exists(filepath) THEN LET errstr=SFMT(%"file '%1' does not exist!", os.Path.basename(filepath)) CALL _filedlg_mbox_ok("Error", errstr, "stop") ERROR errstr LET doContinue=TRUE END IF END IF IF NOT doContinue AND dlgtype = C_SAVE THEN LET dirname=_file_get_dirname(filepath) IF NOT os.Path.exists(dirname) THEN CALL _filedlg_mbox_ok("Error", SFMT(%"directory '%1' does not exist!",filepath), "stop") LET doContinue=TRUE END IF END IF IF NOT doContinue THEN EXIT DIALOG END IF ON ACTION cancel LET filepath=NULL EXIT DIALOG ON ACTION move_up --move 1 directory level up LET path = _file_get_dirname(currpath) CALL _filedlg_fetch_filenames(DIALOG,path,ftype,currpath) LET currpath=path DISPLAY BY NAME currpath END DIALOG CLOSE WINDOW _filedlg RETURN filepath END FUNCTION FUNCTION _filedlg_fetch_filenames(d,currpath,typelist,currfile) DEFINE d ui.Dialog DEFINE currpath STRING DEFINE typelist STRING DEFINE currfile STRING DEFINE i,len,found INT DEFINE st base.StringTokenizer LET st = base.StringTokenizer.create(typelist,"|") CALL m_typearr.clear() WHILE st.hasMoreTokens() LET m_typearr[m_typearr.getLength()+1]=st.nextToken() END WHILE LET m_typelen=m_typearr.getLength() CALL _filedlg_getfiles_int(currpath) LET len=_filedlg_list.getLength() --jump to the current file LET currfile=os.Path.basename(currfile) FOR i=1 TO len IF currfile=_filedlg_list[i].entry THEN LET found=1 CALL d.setCurrentRow("sr",i) EXIT FOR END IF END FOR IF NOT found THEN CALL d.setCurrentRow("sr",1) END IF END FUNCTION FUNCTION _filedlg_getfiles_int(dirpath) DEFINE dirpath STRING DEFINE dh, isdir INTEGER DEFINE fname, pname, size STRING CALL _filedlg_list.clear() LET dh = os.Path.diropen(dirpath) IF dh == 0 THEN RETURN END IF WHILE TRUE LET fname = os.Path.dirnext(dh) IF fname IS NULL THEN EXIT WHILE END IF IF fname == "." THEN CONTINUE WHILE END IF LET pname = os.Path.join(dirpath,fname) LET isdir=os.Path.isdirectory(pname) IF isdir THEN LET size = NULL ELSE LET size = os.Path.size(pname) END IF CALL _filedlg_appendEntry(isdir,fname,size,os.Path.mtime(pname)) END WHILE CALL os.Path.dirclose(dh) END FUNCTION FUNCTION _filedlg_in_typearr(type) DEFINE type STRING DEFINE i INT FOR i=1 TO m_typelen IF type=m_typearr[i] THEN RETURN TRUE END IF END FOR RETURN FALSE END FUNCTION FUNCTION _filedlg_checktypeandext(ext) DEFINE ext STRING IF _filedlg_in_typearr("*") THEN RETURN TRUE END IF IF ext IS NOT NULL THEN IF _filedlg_in_typearr("*.*") THEN RETURN TRUE END IF IF _filedlg_in_typearr("*"||ext) THEN RETURN TRUE END IF END IF RETURN FALSE END FUNCTION FUNCTION _filedlg_appendEntry(isdir,name,size,modDate) DEFINE isdir INT DEFINE name STRING DEFINE size INT DEFINE modDate STRING DEFINE type,image,ext STRING DEFINE len INT IF isdir THEN LET ext="" LET type=C_DIRECTORY LET image="folder" ELSE LET ext = _file_extension(name) LET type = SFMT(%"%1-File",ext) LET image="file" END IF IF NOT isdir AND NOT _filedlg_checktypeandext(ext) THEN RETURN END IF CALL _filedlg_list.appendElement() LET len=_filedlg_list.getLength() LET _filedlg_list[len].entry = name LET _filedlg_list[len].etype = type LET _filedlg_list[len].eimage = image LET _filedlg_list[len].esize = size LET _filedlg_list[len].emodt = modDate END FUNCTION FUNCTION _filedlg_mbox_ok(title,message,icon) DEFINE title, message, icon STRING CALL fgl_winMessage(title,message,icon) END FUNCTION FUNCTION _filedlg_mbox_yn(title,message,icon) DEFINE title, message, icon STRING DEFINE r STRING LET r = fgl_winQuestion(title,message,"yes","yes|no",icon,0) RETURN ( r == "yes" ) END FUNCTION -------------------------- _file helpers --------------------------------------- FUNCTION _file_get_dirname(filename) DEFINE filename STRING DEFINE dirname STRING LET dirname=os.Path.dirname(filename) IF dirname IS NULL THEN LET dirname="." END IF RETURN dirname END FUNCTION FUNCTION _file_extension(filename) DEFINE filename STRING DEFINE extension STRING LET extension=os.Path.extension(filename) IF extension IS NOT NULL THEN LET extension=".",extension END IF RETURN extension END FUNCTION FUNCTION _file_delete(filename) DEFINE filename STRING IF NOT os.Path.delete(filename) THEN CALL _filedlg_mbox_ok("Error",sfmt("Can't delete %1",filename),"stop") END IF END FUNCTION --normalizes a given directory name --example /home/foo/bar/../spong -> /home/foo/spong FUNCTION _file_normalize_dir(fname) DEFINE fname STRING DEFINE cmd STRING DEFINE arr DYNAMIC ARRAY OF STRING IF NOT os.Path.isdirectory(fname) THEN RETURN fname END IF IF fgl_getenv("WINDIR") IS NOT NULL THEN LET cmd="cd ",fname,"&&cd" ELSE LET cmd="cd ",fname,"&&pwd" END IF IF NOT _file_get_output(cmd,arr) THEN RETURN fname END IF RETURN arr[1] END FUNCTION --runs a program and returns an array with the output lines, --used by _file_normalize_dir FUNCTION _file_get_output(program,arr) DEFINE program,linestr STRING DEFINE arr DYNAMIC ARRAY OF STRING DEFINE mystatus,idx INTEGER DEFINE c base.Channel LET c = base.channel.create() CALL c.setDelimiter("") WHENEVER ERROR CONTINUE CALL c.openpipe(program,"r") LET mystatus=status WHENEVER ERROR STOP IF mystatus THEN DISPLAY "error in file_get_output(program,arr)" RETURN 0 END IF CALL arr.clear() WHILE (linestr:=c.readline()) IS NOT NULL LET idx=idx+1 LET arr[idx]=linestr END WHILE CALL c.close() RETURN 1 END FUNCTION