/*----------------------------------------------------------------------------*/
/*                                                                            */
/* Copyright (c) 2012-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.               */
/*                                                                            */
/*----------------------------------------------------------------------------*/

/**
 * ToolTip control example.
 *
 * This example demonstrates several advanced techniques in using ToolTips.
 *
 * Typically a single tool is created for a dialog control.  This works fine
 * for creating tools for, say, buttons.  Even if you have 20 buttons, it is
 * pretty reasonable to create 20 tools in a ToolTip.
 *
 * But, what if you want to display a tip for each item in a tree-view?  What if
 * the tree-view has hundreds, or even thousands, of items.  Creating several
 * thousand tools is no longer reasonable.  One technique is to create one tool
 * and dynamically make it appear to be multiple tools.
 *
 * That technique is demonstrated in this example.  We use a tree-view control
 * to show the technique.  The basic principle is to track where the mouse is,
 * and dynamically update the tool each time it moves from one item to another.
 *
 * Normally you would program this by monitoring the mouse moves and updating
 * the tool when the mouse moves require it.  To do this, you can not use the
 * SUBCLASS flag for the tool, because you need to dynamically update tool
 * _before_ the tool sees the mouse move.  You would monitor the mouse moves,
 * decide whether to update or not the tool, and then forward every mouse move
 * on to the tool.  For the puposes of discussion, we will refer to the
 * forwarding of the mouse moves as 'relaying' the moves.  And, actually for the
 * ToolTip to work correctly it needs to see all the mouse related messages.
 * So, the relaying is really relaying all mouse messages, not just the mouse
 * move message.
 *
 * This presents a problem in ooDialog because mouse messages are sent to the
 * window under the mouse, in this case the tree-view.  In addition, if we set
 * up a tool for the tree-view, but can not use the SUBCLASS flags, the ToolTip
 * event notifications are sent to the tree-view, not to our dialog.
 *
 * In this case, we need a way to monitor the mouse messages sent to the
 * tree-view and a way to intercept the ToolTip event notifications that would
 * be sent to the tree-view.  For ToolTips, ooDialog provides a method, the
 * manageAtypicalTool() method, that provides this functionality.
 *
 * This method monitors all mouse messages and relays the messages to the tool.
 * Before it relays the mouse messages to the took, it will invoke a method in
 * the Rexx dialog for the relay event.  In addition, it intercepts all the
 * ToolTip event notifications sent to the dialog control and invokes a method
 * in the Rexx dialog for each event.
 *
 * Of course, the application may not need to have an event handler for all the
 * events, or even for the relay event.  So, manageAtypicalTool() allows the
 * programmer to just connect the events it needs.
 *
 * Although this example just focuses on a specific use of manageAtypicalTool(),
 * the method can be used in any situation where custom ToolTips are needed for
 * dialog controls.  The same principles that necessitate its use here, will be
 * found in many situations.
 *
 * Note that in this specific case, there is an easier way to produce the same
 * results as this example does.  The customPositionToolTip.rex example shows
 * how to use the easier method.  However, this example is very useful in
 * showing how the manageAtypicalToo() works.  The principles here can be used
 * in other situations.
 */

  sd = locate()
  .application~setDefaults('O', sd'manageControlTool.h', .false)

  dlg = .TreeViewDialog~new(sd'manageControlTool.rc', IDD_TREEVIEW)
  dlg~execute("SHOWTOP", IDI_DLG_OOREXX)


::requires "ooDialog.cls"

::class 'TreeViewDialog' subclass RcDialog

::method initDialog
  expose currentItem currentLocation currentText

  -- These variables are used to help track which tool needs to be shown.
  currentItem = 0
  currentText = ''
  currentLocation = 'NOWHERE'

  tv = self~newTreeView(IDC_TREE)

  -- Set up our ToolTip
  tt = self~createToolTip(IDC_TT)

  rect = tv~clientRect

  -- We need to specify very precisely the attributes of the tool we are going
  -- to add.  We can not do that using the addTool() or addToolRect()
  -- convenience methods.  So we need a ToolInfo object and to use the
  -- addToolEx() method.

  toolInfo = .ToolInfo~new(tv, 10, '', 'TRANSPARENT', rect)
  tt~addToolEx(toolInfo)

  tt~setMaxTipWidth(250)

  -- Set up our tree-view and populate it.
  tv~setItemHeight(20)
  self~populateTree(tv)

  -- Now start the monitoring process.
  tt~manageAtypicalTool(tv, .array~of('RELAY', 'NEEDTEXT', 'SHOW'))


/** onRelay()
 *
 * This is the event handler for the mouse message relaying event.  It is
 * invoked by our monitoring process for every mouse message.  The method is
 * invoked _before_ the monitor relays the mouse message to the ToolTip.
 *
 * This method is crucial to how this example works.  For every mouse message,
 * we calculate if the mouse is still over the label of the same item it was
 * over previously.  If it is not, we hide the ToolTip window by invoking the
 * pop().
 *
 * After we return form this method, the mouse message is forwarded on to the
 * ToolTip to let it do its ToolTip thing.  When the ToolTip decides the mouse
 * has hovered long enough in one spot, it displays its window.  Since we have
 * set the current text to the string for the tree-view item when the mouse
 * first moved over the item, the correct "tool" text is displayed.
 *
 * The text to display is actually passed on to the ToolTip from our onNeedTex()
 * method.
 */
::method onRelay unguarded
    expose currentItem currentText currentLocation
    use arg toolTip, pos, mouseMsg, treeView

    -- Determine which, if any, item the mouse is over.
    d = treeView~hitTestInfo(pos)

    -- Save the old item and location and sent the current item and location to
    -- where the mouse is at this moment.
    oldItem = currentItem
    oldLocation = currentLocation
    currentItem = d~hItem
    currentLocation = d~location

    -- If mouse is over a new item or location, hide the ToolTip.  Set our
    -- current text to the text for the item we are now over.  If we are not
    -- over the label of an item, set the current text to the empty string.
    if oldItem \== currentItem | oldLocation \== currentLocation then do
        if currentItem <> 0, currentLocation~wordPos('ONLABEL') <> 0 then do
            currentText = treeView~getItemData(currentItem)
        end
        else do
            currentText = ''
        end
        toolTip~pop
    end

    return 0


/** onNeedText()
 *
 * This is the event handler for the ToolTip's NEEDTEXT event notification.
 *
 * The notification was actually sent to the tree-view, but, our monitor process
 * has intercepted the notification and sent it to us instead of the tree-view.
 *
 * The real work was already done in the onRelay() method.  All we need to do is
 * set the TEXT index in the info .Directory object to what the onRelay() method
 * set the current text to.
 */
::method onNeedText unguarded
    expose currentItem currentText
    use arg toolTip, treeView, info, userData, flags

    info~text = currentText

    -- Return false so that the ToolTip does not store the text.  We want it to
    -- always ask us for the text because the mouse could be over any item.
    return .false



/** onShow()
 *
 * This is our event handler for the ToolTip's SHOW event notification.
 *
 * The notification was actually sent to the tree-view, but our monitor process
 * has intercepted the notificatio and sent it to us instead of the tree-view.
 *
 * Normally the ToolTip places its window below the tree-view item.  This often
 * over-laps and obscures the other items.  What we want is to place the ToolTip
 * window to the right and centered with the item.  This way nothing is
 * obscured.
 *
 * The custom positioning of the ToolTip window is straight-forward.  We get the
 * bounding rectangle of the tree-view item's label, convert the point to client
 * coordinates, and then reposition the ToolTip window to where we want it.
 *
 * Note that we must return .true then so that the ToolTip is informed not to
 * position the window in its default position.
 */
::method onShow unguarded
    expose currentItem
    use arg toolTip, treeView

    r = .Rect~new
    if treeView~getItemRect(currentItem, r) then do
        treeView~client2screen(r)

        toolTip~setWindowPos(0, r~right + 5, r~top, 0, 0, "NOACTIVATE NOSIZE NOZORDER")
        return .true
    end

    return .false


/** populateTree()
 *
 * This is a private method used to populate the tree-view control with our
 * items.
 *
 * Note that each item has an item data object assigned to it.  In this case the
 * object is a string.  The string is the text to display for the tool tip for
 * that item.
 */
::method populateTree private
    use strict arg tv

    hItem = tv~add("Peter", , ,"BOLD EXPANDED", , 'Data unique to Peter.')

    hItem = tv~add(,"Mike", , ,"EXPANDED", , 'Mike talks a lot.')

    data = 'George was famous once.'.endOfLine'He is unknown to my'.endOfLine'niece.'
    hItem = tv~add(, ,"George", , , , , data)

    hItem = tv~add(, ,"Monique", , , , , 'Monique has never been to France.')

    hItem = tv~add(, , ,"John", , , , , 'John owns 4 dogs.')

    hItem = tv~add(, ,"Chris", , , , , 'Chris will never be president.')

    hItem = tv~add( , "Maud", , , , , 'Maud likes to bake.')

    hItem = tv~add( , "Ringo", , , , , 'Ringo has a LOT of money.')

    hItem = tv~add("Paul", , ,"BOLD EXPANDED", , "Two roads lead to Paul's house.")

    hItem = tv~add( , "Dave", , , , , 'No one lives with Dave.')

    hItem = tv~add( , "Sam", , , , , 'Sam loves pizza.')

    hItem = tv~add( , "Jeff", , , , , 'Jeff rides his bike to work.')

    hItem = tv~add("Mary", , ,"BOLD EXPANDED", , 'Mary owes $49.50 to her dentist.')

    hItem = tv~add( , "Helen", , , , , 'A closer look at Helen is warranted.')

    hItem = tv~add( , "Michelle", , , , , 'The Toyota Camray belongs to Michelle.')

    hItem = tv~add( , "Diana", , , , , 'Go go Chargers.')
