rexx-things/modules/windows/oodialog/controls/ToolTip/manageControlTool.rex

294 lines
13 KiB
Rexx
Raw Normal View History

2025-03-12 20:50:48 +00:00
/*----------------------------------------------------------------------------*/
/* */
/* 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.')