rexx-things/modules/windows/oodialog/examples/openSaveFileDemo.rex
2025-03-12 20:50:48 +00:00

539 lines
22 KiB
Rexx
Executable File

/*----------------------------------------------------------------------------*/
/* */
/* Copyright (c) 2013-2014 Rexx Language Association. All rights reserved. */
/* */
/* This program and the accompanying materials are made available under */
/* the terms of the Common Public License v1.0 which accompanies this */
/* distribution. A copy is also available at the following address: */
/* https://www.oorexx.org/license.html */
/* */
/* Redistribution and use in source and binary forms, with or */
/* without modification, are permitted provided that the following */
/* conditions are met: */
/* */
/* Redistributions of source code must retain the above copyright */
/* notice, this list of conditions and the following disclaimer. */
/* Redistributions in binary form must reproduce the above copyright */
/* notice, this list of conditions and the following disclaimer in */
/* the documentation and/or other materials provided with the distribution. */
/* */
/* Neither the name of Rexx Language Association nor the names */
/* of its contributors may be used to endorse or promote products */
/* derived from this software without specific prior written permission. */
/* */
/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */
/* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */
/* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS */
/* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT */
/* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */
/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED */
/* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, */
/* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY */
/* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING */
/* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */
/* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
/* */
/*----------------------------------------------------------------------------*/
/** openSaveFileDemo.rex
*
* This application shows several examples of using the Common Item Dialog.
*
* The Common Item Dialog (formerly known as the Common File Dialog) is used in
* two variations: the Open dialog and the Save dialog. These two dialogs share
* most of their functionality, but each have some unique methods.
*
* The common item dialog implementation found in Windows Vista and later
* provides several advantages over the implementation provided in earlier
* versions of Windows.
*
* ooDialog provides complete support for all features of the Common Item
* Dialog.
*
* This example will only run on Vista or later.
*
* Both the .OpenFileDialog and the .SaveFileDialog are subclasses of the
* .CommonItemDialog class. The Rexx programmer can not instantiate a
* .CommonItemDialog object. Instead the programmer picks the type of file
* dialog he wants, a save file or an open file dialog and instantitates that
* class. Almost all of the methods for these classes come from the
* CommonItemDialog class and are documented in the reference manual under the
* CommonItemDialog section.
*
* Some of the features of the Common Item Dialog are a little advanced. How-
* ever, the basic every day usage is simple. This example puts up an opening
* dialog that allows the user to choose from a number of different usage
* patterns and then shows the Common Item Dialog picked.
*
* One of the features of the Common Item Dialog is that it allows its state to
* be saved on a per instance basis in addition to the per process basis.
* This is done by generating a GUID and assigning it to the dialog before it
* is configured. Then, for each dialog with the same GUID, the operating
* system saves its state separately.
*
* To make use of this feature, the programmer would generate a single GUID and
* then assign the same GUID each time the file dialog was shown in the
* application.
*
* This example makes use of that feature by assigning a different GUID for
* each of the XX dialogs that can be displayed from the opening dialog.
*
* This simple program can be used to generate a GUID. Run the program and
* then copy and paste the output on the command line in to your program.
*
* - - - - - - - - - Cut begin - - - - - - - - - - - - - - - - - - - - - - - -
* /* genGUID.rex */
*
* guid = .DlgUtil~getGUID
* say guid
* return 0
*
* ::requires 'ooDialog.cls'
* - - - - - - - - - Cut end - - - - - - - - - - - - - - - - - - - - - - - - -
*
*/
if \ .application~requiredOS('Vista', 'openSaveFileDemo.rex') then return 99
srcDir = locate()
-- Set up the symbolic IDs and then put up our example dialog.
.application~setDefaults('O', srcDir'resources\osfDialogs.h', .false)
dlg = .CommonSaveDialog~new(srcDir'resources\osfDialogs.rc', IDD_SIMPLE_OSF_DIALOGS)
dlg~execute("SHOWTOP", IDI_DLG_OOREXX)
return 0
::requires "ooDialog.cls"
::class 'CommonSaveDialog' subclass RcDialog
-- Do not copy these GUIDs into your own code. Always generate your own GUID.
::constant GUID_OPEN '021b0a3d-cb16-4def-ab42-7fc259fd84d9'
::constant GUID_SAVE 'beff4495-fa60-468b-90c3-1dcb6fde0e78'
::constant GUID_OPEN_MULTI 'b6c25d80-198e-4baa-aad8-1249e4f15b3f'
::constant GUID_OPEN_CUSTOM '745c45cb-3464-46fc-96cf-8628ad4bf63b'
::constant GUID_SAVE_DEFAULT 'fe2c5305-d7f9-43cd-9d7b-9c3d4d1b79be'
::constant GUID_OPEN_FOLDER 'ce5ad183-b42f-4f37-a5d6-78d1cffea2e6'
/** initDialog()
*
* Simple standard init dialog method. We save references to our commonly used
* controls, and connect the button click event for the push button.
*
* We also use this method to find the installed directory of ooRexx.
*/
::method initDialog
expose rbOpen rbSave rbOpenMulti rbOpenCustom rbSaveDefault rbOpenFolder edit rexx_home
rbOpen = self~newRadioButton(IDC_RB_OPEN)~~check
rbSave = self~newRadioButton(IDC_RB_SAVE)
rbOpenMulti = self~newRadioButton(IDC_RB_OPEN_MULTI)
rbOpenCustom = self~newRadioButton(IDC_RB_OPEN_CUSTOM)
rbSaveDefault = self~newRadioButton(IDC_RB_SAVE_DEFAULT)
rbOpenFolder = self~newRadioButton(IDC_RB_OPEN_FOLDER)
edit = self~newEdit(IDC_EDIT_OSF)
self~connectButtonEvent(IDC_PB_SHOW_OSF, 'CLICKED', onShowOSFDialog)
rexx_home = value('REXX_HOME', , 'ENVIRONMENT')
if rexx_home == '' then rexx_home = 'C:\Program Files'
self~addToolTips
/** onShowOSFDialog()
*
* When the user clicks on the 'Show' push button, we put up the example Common
* Item Dialog specified by the user through the radio buttons.
*
* Each Common Item Dialog is displayed using a separate, private, method. To
* look at the example code for some type of dialog, for instance the simple
* open file dialog, examine the associated method.
*/
::method onShowOSFDialog unguarded
expose rbOpen rbSave rbOpenMulti rbOpenCustom rbOpenFolder rbSaveDefault
select
when rbOpen~checked then return self~openExample
when rbSave~checked then return self~saveExample
when rbOpenMulti~checked then return self~openMultifilesExample
when rbOpenCustom~checked then return self~openCustomExample
when rbSaveDefault~checked then return self~saveDefaultExample
when rbOpenFolder~checked then return self~openFolderExample
otherwise return 0 -- Can not really happen
end
-- End select
/** openExample()
*
* Displays the most simple Open File Dialog example. We just show the dialog
* and get the result. We do not use any customization or advanced features,
* other than slightly changing the options.
*/
::method openExample unguarded private
-- Setting the client GUID has the operating system preserve the state for
-- this specific open file dialog.
ofd = .OpenFileDialog~new
ret = ofd~setClientGuid(self~GUID_OPEN)
-- The default flags for the Open File Dialog contain the file must exsit
-- flag. This prevents the user from typing in their own file name; the
-- user can only open an exsiting file.
--
-- For certain applications, this behaviour may be desired. But for this
-- example, we want the user to be able to type in any file name, so we
-- remove that flag. We get the exsiting flags and set the flags to the
-- existing flags minus the FILEMUSTEXIST keyword.
ofd~options = ofd~options~delWord(ofd~options~wordPos('FILEMUSTEXIST'), 1)
-- Very simple, we are all set, show the dialog and get the user's response:
ret = ofd~show(self)
if ret == ofd~canceled then text = 'The user canceled the open'
else text = 'Open file:' ofd~getResult
-- The proper use of both the .SaveFileDialog and the .OpenFileDialog is to
-- instantiate the object, configure it, show it, and then release it. The
-- release() method is essential to ensuring the COM resources are properly
-- cleaned up.
--
-- If release() is not called, the ooDialog framework will *attempt* to do
-- the clean up in an uninit() method. However, 1.) there is *no* guarentee
-- that the interpreter will invoke the uninit() method. 2.) There is *no*
-- guarentee the uninit() will be run on this thread. The COM resources can
-- *not* be cleaned up if uninit() is run on another thread than this one.
--
-- The only way to guarentee that the COM resources are cleaned up properly
-- is for the programmer to invoke the release() method.
ofd~release
-- Have the edit box display the result.
self~showResult(text)
return 0
/** saveExample()
*
* Displays the most simple Save File Dialog example. We just show the dialog
* and get the result. We do not use any customization or advanced features.
*/
::method saveExample unguarded private
-- Setting the client GUID has the operating system preserve the state for
-- this specific save file dialog.
sfd = .SaveFileDialog~new
ret = sfd~setClientGuid(self~GUID_SAVE)
-- We are all set, show the dialog and get the user's response:
ret = sfd~show(self)
if ret == sfd~canceled then text = 'The user canceled the save'
else text = 'Save to file:' sfd~getResult
-- The proper use of both the .SaveFileDialog and the .OpenFileDialog is to
-- instantiate the object, configure it, show it, and then release it. The
-- release() method is essential to ensuring the COM resources are properly
-- cleaned up.
--
-- If release() is not called, the ooDialog framework will *attempt* to do
-- the clean up in an uninit() method. However, 1.) there is *no* guarentee
-- that the interpreter will invoke the uninit() method. 2.) There is *no*
-- guarentee the uninit() will be run on this thread. The COM resources can
-- *not* be cleaned up if uninit() is run on another thread than this one.
--
-- The only way to guarentee that the COM resources are cleaned up properly
-- is for the programmer to invoke the release() method.
sfd~release
-- Have the edit box display the result.
return self~showResult(text)
return 0
/** openMultiFilesExample()
*
* Displays a basic open file dialog that allows the user to pick multiple file
* names at one time.
*/
::method openMultiFilesExample unguarded private
expose rexx_home
ofd = .OpenFileDialog~new
ret = ofd~setClientGuid(self~GUID_OPEN_MULTI)
-- To allow the user to select multiple files, we need to add this keywod
-- to the options:
ofd~options = ofd~options 'ALLOWMULTISELECT'
-- Show the dialog and get the response
ret = ofd~show(self)
-- If msg ends up not being .nil, we show a message box ...
msg = .nil
if ret == ofd~canceled then do
text = 'The user canceled the open operation.'
end
else do
files = ofd~getResults
if files~items == 1 then do
text = 'Open file:' files[1]
end
else do
text = 'Open files (file names only):'
msg = 'Open files (complete file names as returned):' || .endOfLine~copies(2)
do f over files
text ||= ' ' || fileSpec('N', f)
msg ||= f || .endOfLine
end
end
end
ofd~release
-- Have the edit box display the result.
self~showResult(text)
if msg \== .nil then do
title = 'Complete Open File Names'
j = MessageDialog(msg, self~hwnd, title, 'OK', 'INFORMATION')
end
return 0
/** openCustomExample()
*
* Displays an Open File Dialog that has some easy to use customizations.
*/
::method openCustomExample unguarded private
ofd = .OpenFileDialog~new
ret = ofd~setClientGuid(self~GUID_OPEN_CUSTOM)
ofd~options = ofd~options~delWord(ofd~options~wordPos('FILEMUSTEXIST'), 1)
-- With the Common Item Dialog you can easily change the title for the
-- dialog, the label for the edit box, and the label for the ok button:
ofd~setTitle("The ooRexx Project's Open File Dialog")
ofd~setFileNameLabel('Type in, or select an existing, file to open here:')
ofd~setOkButtonLabel('Finished')
-- There are also a large number of options that can be set or changed.
-- Here, we show just a few that effect the appearance of the dialog:
opts = 'FORCEPREVIEWPANEON HIDEMRUPLACES HIDEPINNEDPLACES'
ofd~options = ofd~options opts
-- Okay, we are all set, show the dialog and get the user's response:
ret = ofd~show(self)
if ret == ofd~canceled then text = 'The user canceled the open'
else text = 'Open file:' ofd~getResult
ofd~release
-- Have the edit box display the result.
return self~showResult(text)
/** saveDefaultExample()
*
* Displays a save file dialog that uses file type filters and sets the initial
* file name and directory location.
*/
::method saveDefaultExample unguarded private
expose rexx_home
sfd = .SaveFileDialog~new
ret = sfd~setClientGuid(self~GUID_SAVE_DEFAULT)
-- Add a file types filter. To do this you use an array containing the
-- filter values. Each single filter consists of 2 parts, the description
-- and the extension, so the array must contain an even number of items.
filter = .array~of('Rexx Program File', '*.rex', 'Rexx Class File', '*.cls', 'Text File', '*.txt')
sfd~setFileTypes(filter)
-- Set the initial filter selected. This is done by using the index for
-- the filter in the array. Each filter consists of a pair, so in the above
-- we have 3 filters. The second filter is the 'Rexx Class File' filter
sfd~setFileTypeIndex(2)
-- Set the default extension. Note that you do not include the period. The
-- default extension governs whether or not an extension is added if the
-- user leaves off the extension. If you do not set a deault extension, no
-- extension is added when the user does not type an extension, even if a
-- file type filter is in effect. When a default extension is set, if the
-- user picks a new filter, the extension is updated automatically by the
-- operating system.
sfd~setDefaultExtension('cls')
-- Set the initial save as file name. You must provide a complete path
-- name or an error is generated. Likewise the file must exist. The
-- complete path name also serves to specifiy the directory that the save
-- file dialog is initially opened in.
ret = sfd~setSaveAsItem(rexx_home'\ooDialog.cls')
-- The default options for the Save File Dialog contain the OVERWRITEPROMPT
-- keyword. This option causes the operating system to prompt the user if
-- they select a save file name that is the name of an existing file. If
-- that behaviour is not desired it can be changed by removing the keyword
-- as in this code. Uncomment to see the effect:
/* sfd~options = sfd~options~delWord(sfd~options~wordPos('OVERWRITEPROMPT'), 1) */
-- Show the dialog and get the response
ret = sfd~show(self)
if ret == sfd~canceled then text = 'The user canceled the save operation.'
else text = 'Save file:' sfd~getResult
sfd~release
-- Have the edit box display the result.
return self~showResult(text)
/** openFolderExample()
*
* Displays a basic open file dialog. The main point here is to demonstrate
* how to control which directory is initially selected when the dialog opens.
*/
::method openFolderExample unguarded private
expose rexx_home
ofd = .OpenFileDialog~new
ret = ofd~setClientGuid(self~GUID_OPEN_FOLDER)
-- Normally the operating system will pick the initial directory that is
-- selected when the open file dialog is first shown. Usually this is the
-- last directory the user had selected. The setFolder() method over-rides
-- this behaviour and forces the dialog to open with the specified directory
-- selected.
ofd~setFolder(rexx_home)
-- In this example we do not change the default options. One of the
-- defaults is that the file must exist. This is useful in the case where
-- we want to ensure the user picks an exsisting file. We do not need to
-- check for file existence. The operating system forces the user to pick
-- an exsiting file or cancel.
-- Show the dialog and get the response
ret = ofd~show(self)
if ret == ofd~canceled then text = 'The user canceled the open operation.'
else text = 'Open file:' ofd~getResult
ofd~release
-- Have the edit box display the result.
return self~showResult(text)
/** showResult()
*
* A convenience method that updates the edit box with the results for each
* open / save file dialog.
*/
::method showResult unguarded private
expose edit
use strict arg text
edit~setText(text)
return 0
/** addToolTips()
*
* A convenience method to add a tool tip for each radio button. The tool tips
* let us shwo some text explaining the tip of Open / Save File dialog that will
* be displayed when the specific radio button is selected.
*
* Notes: A tool tip will display the text on multiple lines, and will break
* the text at embedded new line characters. But - it will only do this
* if setMaxTipWidth() is invoked. However, if the width is set to less
* than the width up to the new line, the tool tip will break the text
* at a word break. So, what we do is set the max width to very large,
* the width of an entire text, and then the tool tip breaks the text at
* every new line.
*
* The text is pretty long, so to ensure the user has enough time to
* read it all, we set the delay before the tool tip disappears to twice
* the default delay time.
*/
::method addToolTips private
expose rbOpen rbSave rbOpenMulti rbOpenCustom rbSaveDefault rbOpenFolder edit
text = "Select this Radio Button to show " || .endOfLine || -
"a simple Open File Dialog that" || .endOfLine || -
"allows the user to type in a" || .endOfLine || -
"file name."
s = self~getTextSizePX(text)
ttOpen = self~createToolTip('IDC_TT_OPEN')
ttOpen~addTool(rbOpen, text)
ttOpen~setMaxTipWidth(s~width)
delayTime = ttOpen~getDelayTime('AUTOPOP') * 2
ttOpen~setDelayTime('AUTOPOP', delayTime)
text = "Select this Radio Button to show " || .endOfLine || -
"a simple Save File Dialog that" || .endOfLine || -
"allows the user to type in a" || .endOfLine || -
"file name."
ttSave = self~createToolTip('IDC_TT_SAVE')
ttSave~addTool(rbSave, text)
ttSave~setMaxTipWidth(s~width)
ttSave~setDelayTime('AUTOPOP', delayTime)
text = "Select this Radio Button to show" || .endOfLine || -
"an Open File Dialog that allows" || .endOfLine || -
"the user to select multiple files" || .endOfLine || -
"at one time."
ttMulti = self~createToolTip('IDC_TT_OPEN_MULTI')
ttMulti~addTool(rbOpenMulti, text)
ttMulti~setMaxTipWidth(s~width)
ttMulti~setDelayTime('AUTOPOP', delayTime)
text = "Select this Radio Button to show" || .endOfLine || -
"an Open File Dialog that has some" || .endOfLine || -
"simple customizations of the apparence" || .endOfLine || -
"of the dialog."
ttCustom = self~createToolTip('IDC_TT_OPEN_CUSTOM')
ttCustom~addTool(rbOpenCustom, text)
ttCustom~setMaxTipWidth(s~width)
ttCustom~setDelayTime('AUTOPOP', delayTime)
text = "Select this Radio Button to show" || .endOfLine || -
"a Save File Dialog that sets a" || .endOfLine || -
"default save as file name and a" || .endOfLine || -
"file type filter."
ttDefault = self~createToolTip('IDC_TT_SAVE_DEFAULT')
ttDefault~addTool(rbSaveDefault, text)
ttDefault~setMaxTipWidth(s~width)
ttDefault~setDelayTime('AUTOPOP', delayTime)
text = "Select this Radio Button to show" || .endOfLine || -
"an Open File Dialog that sets" || .endOfLine || -
"the initial direcotry that the" || .endOfLine || -
"dialog opens in."
ttFolder = self~createToolTip('IDC_TT_OPEN_FOLDER')
ttFolder~addTool(rbOpenFolder, text)
ttFolder~setMaxTipWidth(s~width)
ttFolder~setDelayTime('AUTOPOP', delayTime)