416 lines
12 KiB
Plaintext
416 lines
12 KiB
Plaintext
# 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
|