/*----------------------------------------------------------------------------*/
/*                                                                            */
/* 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)

