664 lines
26 KiB
Rexx
664 lines
26 KiB
Rexx
|
/*----------------------------------------------------------------------------*/
|
|||
|
/* */
|
|||
|
/* 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. */
|
|||
|
/* */
|
|||
|
/*----------------------------------------------------------------------------*/
|
|||
|
|
|||
|
/**
|
|||
|
* This example shows how to add tool tips to a combo box.
|
|||
|
*
|
|||
|
* When the items in a combo box are longer than the width of the combo box,
|
|||
|
* they can be difficult for the user to read. Tool tips are a good solution
|
|||
|
* to this, but combo boxes do not have built in support for tool tips like
|
|||
|
* list-views or tree-views.
|
|||
|
*
|
|||
|
* Adding the support to a combo box is doable, but it takes a little work.
|
|||
|
* This example shows how it is done. The example also shows how to custom
|
|||
|
* position the tool tips. To see the difference in where the tool tip
|
|||
|
* positions the tips by default and where this program positions them, the
|
|||
|
* dialog has 2 radio buttons that allow switching between the default
|
|||
|
* positioning and the custom positioning.
|
|||
|
*
|
|||
|
* One approach to adding tool tips to a combo box would be to just have the
|
|||
|
* tool tips display for the items in the drop down list. This example goes
|
|||
|
* beyond that to also show tool tips for the selection field and the drop down
|
|||
|
* button.
|
|||
|
*
|
|||
|
* When the drop down is closed, adding tool tips to the selection field and
|
|||
|
* the button is relatively easy. But, when the drop down is open, it is hard
|
|||
|
* because the list box has the mouse captured. This prevents tool tips from
|
|||
|
* activating when the mouse goes over the selection field or the button. What
|
|||
|
* we do here is create tracking tool tips for the selection field and the
|
|||
|
* button to use when the drop down is open. Tracking tool tips need to be
|
|||
|
* shown and hidden manually, which is what is done in this program.
|
|||
|
*/
|
|||
|
|
|||
|
sd = locate()
|
|||
|
.application~setDefaults('O', sd'comboBoxToolTip.h', .false)
|
|||
|
|
|||
|
dlg = .CitiesOfTheWorldDlg~new(sd'comboBoxToolTip.rc', IDD_MAJOR_CITIES)
|
|||
|
dlg~execute("SHOWTOP", IDI_DLG_OOREXX)
|
|||
|
|
|||
|
return 0
|
|||
|
|
|||
|
::requires "ooDialog.cls"
|
|||
|
|
|||
|
::class 'CitiesOfTheWorldDlg' subclass RcDialog
|
|||
|
|
|||
|
/** init()
|
|||
|
*
|
|||
|
* Normally for RcDialog dialogs there is no need for an init() method. Here,
|
|||
|
* we use the init() method to set up our state varialbes and to connect a
|
|||
|
* combo box event.
|
|||
|
*
|
|||
|
* There is no real need to do it here, it could just as well been done in the
|
|||
|
* initDialog() method.
|
|||
|
*/
|
|||
|
::method init
|
|||
|
expose currentItem currentText currentOnClient ttEditActive ttButtonActive doCustomPositioning
|
|||
|
|
|||
|
forward class (super) continue
|
|||
|
|
|||
|
-- These variables are used to help track which tool needs to be shown.
|
|||
|
currentItem = 0
|
|||
|
currentText = ''
|
|||
|
currentOnClient = .false
|
|||
|
ttEditActive = .false
|
|||
|
ttButtonActive = .false
|
|||
|
|
|||
|
doCustomPositioning = .true
|
|||
|
|
|||
|
self~connectComboBoxEvent(IDC_COMBOBOX, 'CLOSEUP', onCloseUP)
|
|||
|
|
|||
|
self~connectButtonEvent(IDC_RB_DEFAULT, 'CLICKED', doDefault)
|
|||
|
self~connectButtonEvent(IDC_RB_CUSTOM, 'CLICKED', doCustom)
|
|||
|
|
|||
|
|
|||
|
/** initDialog()
|
|||
|
*
|
|||
|
* This is a busy initDialog(). We need to set up and configure the combo box
|
|||
|
* and create and configure the tool tips.
|
|||
|
*/
|
|||
|
::method initDialog
|
|||
|
expose comboBox cbEdit cbListBox lbRect editRect buttonRect ttEditTrack ttButtonTrack itemHeight
|
|||
|
|
|||
|
comboBox = self~newComboBox(IDC_COMBOBOX)
|
|||
|
|
|||
|
-- The tool tips are slightly higher than the default height of the combo box
|
|||
|
-- items. So, when we position the tool tip over an item, it does not quite
|
|||
|
-- fit. Making the item height slightly bigger makes things look a lot
|
|||
|
-- better.
|
|||
|
--
|
|||
|
-- If the item height is to be changed, be sure to do it before the items
|
|||
|
-- are inserted. Otherwise when we get the client rect for the list box, its
|
|||
|
-- vertical height will be incorrect.
|
|||
|
itemHeight = comboBox~getItemHeight
|
|||
|
itemHeight += 2
|
|||
|
comboBox~setItemHeight(itemHeight)
|
|||
|
|
|||
|
entries = self~getComboItems
|
|||
|
do e over entries
|
|||
|
comboBox~add(e)
|
|||
|
end
|
|||
|
|
|||
|
comboBox~setCue("Select a city")
|
|||
|
comboBox~setMinVisible(10)
|
|||
|
|
|||
|
d = comboBox~getComboBoxInfo
|
|||
|
cbEdit = d~textObj
|
|||
|
cbListBox = d~listBoxObj
|
|||
|
|
|||
|
self~calcRects(d)
|
|||
|
|
|||
|
self~newRadioButton(IDC_RB_CUSTOM)~check
|
|||
|
|
|||
|
-- Create a tracking tool tip for the selection field of the combo box. For a
|
|||
|
-- drop-down list combo box this is actually the combo box itself. We still
|
|||
|
-- use cbEdit, even though we know cbEdit and comboBox are the same.
|
|||
|
--
|
|||
|
-- 1.) This tool tip is used for the selection field when the dropdown is open
|
|||
|
ttEditTrack = self~createToolTip(IDC_TT_EDIT_TRACK)
|
|||
|
|
|||
|
toolInfo = .ToolInfo~new(self, cbEdit, '', 'ABSOLUTE TRACK IDISHWND', .Rect~new)
|
|||
|
ttEditTrack~addToolEx(toolInfo)
|
|||
|
|
|||
|
self~connectToolTipEvent(IDC_TT_EDIT_TRACK, 'NEEDTEXT', onEditTrackNeedText)
|
|||
|
self~connectToolTipEvent(IDC_TT_EDIT_TRACK, 'SHOW', onEditTrackShow)
|
|||
|
|
|||
|
-- Create a tracking tool tip for the button portion of the combo box.
|
|||
|
--
|
|||
|
-- 2.) This tool tip is used for the button when the dropdown is open.
|
|||
|
ttButtonTrack = self~createToolTip(IDC_TT_BUTTON_TRACK)
|
|||
|
|
|||
|
text = 'Press the button to close the drop down'
|
|||
|
toolInfo = .ToolInfo~new(self, comboBox, text, 'ABSOLUTE TRACK IDISHWND', .Rect~new)
|
|||
|
ttButtonTrack~addToolEx(toolInfo)
|
|||
|
|
|||
|
self~connectToolTipEvent(IDC_TT_BUTTON_TRACK, 'SHOW', onButtonTrackShow)
|
|||
|
|
|||
|
-- Create a tool tip for the combo box itself.
|
|||
|
--
|
|||
|
-- 3.) This tool tip is used for the selection and button components when the
|
|||
|
-- dropdown is closed.
|
|||
|
ttComboBox = self~createToolTip(IDC_TT_CB_MAIN)
|
|||
|
|
|||
|
toolInfo = .ToolInfo~new(comboBox, IDC_TI_EDITRECT, '', 'TRANSPARENT', editRect, IDC_TI_EDITRECT)
|
|||
|
ret = ttComboBox~addToolEx(toolInfo)
|
|||
|
|
|||
|
text = 'Press the button to open the drop down'
|
|||
|
toolInfo = .ToolInfo~new(comboBox, IDC_TI_BUTTONRECT, text, 'TRANSPARENT', buttonRect, IDC_TI_BUTTONRECT)
|
|||
|
ttComboBox~addToolEx(toolInfo)
|
|||
|
|
|||
|
ttComboBox~manageAtypicalTool(comboBox, .array~of('NEEDTEXT', 'SHOW'), .array~of('ONCBNEEDTEXT', 'ONCBSHOW'))
|
|||
|
|
|||
|
-- Create tool tip for the items of the combo box.
|
|||
|
--
|
|||
|
-- 4.) This tool tip is for the individual items when the dropdown is open.
|
|||
|
ttList = self~createToolTip(IDC_TT_CB_LISTBOX)
|
|||
|
|
|||
|
toolInfo = .ToolInfo~new(cbListBox, IDC_TI_LISTBOX, '', 'TRANSPARENT', lbRect)
|
|||
|
ttList~addToolEx(toolInfo)
|
|||
|
|
|||
|
-- Now start the monitoring process for the list box tool.
|
|||
|
ttList~manageAtypicalTool(cbListBox, .array~of('RELAY', 'NEEDTEXT', 'SHOW'))
|
|||
|
|
|||
|
|
|||
|
/** onRelay()
|
|||
|
*
|
|||
|
* The onRelay() event handler is invoked by the monitor that the
|
|||
|
* manageAtypicalTool() method sets up. It is invoked for every mouse message
|
|||
|
* that is sent to the list box control.
|
|||
|
*
|
|||
|
* The monitor first informs us of the mouse message. This gives us a chance to
|
|||
|
* examine the message *before* the tool tip sees it.
|
|||
|
*
|
|||
|
* This event handler here is the key to how this whole program works.
|
|||
|
*
|
|||
|
* All the rest of the code in the program is simply busy work, keeping track of
|
|||
|
* what state we are in. In this method we determine where the mouse is and
|
|||
|
* then determine how we have to configure all the tool tips we are using.
|
|||
|
*
|
|||
|
* We need to know, or to take into account, some facts about the combo box. 1
|
|||
|
* fact is that when the list box is dropped down, it captures the mouse. All
|
|||
|
* mouse messages are then sent to the list box until it releases the capture
|
|||
|
* when it closes up. This means that the pos argument can be anywhere on the
|
|||
|
* screen.
|
|||
|
*
|
|||
|
* Fact number 2 is that the combo box will always tell us the index of the item
|
|||
|
* that is *closest* to the mouse position. So, even if the mouse is on the
|
|||
|
* other side of the screen, the combo box will still return a valid item index.
|
|||
|
* It will be what ever item is closest to that mouse position, even if it is
|
|||
|
* 500 pixels away.
|
|||
|
*
|
|||
|
* We need to track what item the mouse is over, and whether the mouse is over
|
|||
|
* the client area of the combo box. Every time the location changes from over
|
|||
|
* the clien to not over the client, and every time the item the mouse is over
|
|||
|
* changes, the tool tips need to be updated.
|
|||
|
*
|
|||
|
* To handle the selection field and the button, we treat them a pseudo list box
|
|||
|
* item. When the mouse is over either of them, it will not be over the list
|
|||
|
* box client area. We *pretend* that it is by changing the value in the
|
|||
|
* directory object returned by the hit test info method. We do a similar thing
|
|||
|
* for the item index. We *pretend* that the selection field has an item index
|
|||
|
* of 0 and that the button has an item index of -1.
|
|||
|
*
|
|||
|
* If we are, newly, over the selection field or button, we need to manually
|
|||
|
* activate the tracking tool tip and send it the mouse position. In screen
|
|||
|
* coordinates, not client coordinates. Other code in this program is
|
|||
|
* responsible for hiding the tracking tool tips at the proper time.
|
|||
|
*
|
|||
|
* The monitor will pass all mouse messages on to the tool tip after we return
|
|||
|
* from this method. This allows the tool tip to 'do its thing.'
|
|||
|
*/
|
|||
|
::method onRelay unguarded
|
|||
|
expose currentItem currentText currentOnClient editOffsetRect buttonOffsetRect -
|
|||
|
ttEditTrack ttEditActive cbEdit ttButtonTrack ttButtonActive comboBox
|
|||
|
use arg toolTip, pos, mouseMsg, listBox
|
|||
|
|
|||
|
-- Determine which, if any, item the mouse is over.
|
|||
|
d = .directory~new
|
|||
|
ret = listBox~hitTestInfo(pos, d)
|
|||
|
|
|||
|
-- Treat the edit and button rects as though they are in the client area of
|
|||
|
-- the list box and assign them a, fake, item index.
|
|||
|
if pos~inRect(editOffsetRect) then do
|
|||
|
d~inClientArea = .true
|
|||
|
d~itemIndex = 0
|
|||
|
end
|
|||
|
else if pos~inRect(buttonOffsetRect) then do
|
|||
|
d~inClientArea = .true
|
|||
|
d~itemIndex = -1
|
|||
|
end
|
|||
|
|
|||
|
-- Save the old item and location and sent the current item and location to
|
|||
|
-- where the mouse is at this moment.
|
|||
|
oldItem = currentItem
|
|||
|
oldOnClient = currentOnClient
|
|||
|
currentItem = d~itemIndex
|
|||
|
currentOnClient = d~inClientArea
|
|||
|
|
|||
|
-- 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
|
|||
|
-- on the client area, set the current text to the empty string.
|
|||
|
if oldItem \== currentItem | oldOnClient \== currentOnClient then do
|
|||
|
if currentOnClient then do
|
|||
|
if d~itemIndex == -1 then do
|
|||
|
p = pos~copy
|
|||
|
listBox~client2Screen(p)
|
|||
|
ttButtonTrack~trackActivate(self, comboBox, .true)
|
|||
|
ttButtonActive = .true
|
|||
|
turnTTEditActiveOff = .true
|
|||
|
turnTTButtonActiveOff = .false
|
|||
|
ttEditTrack~trackPosition(p)
|
|||
|
end
|
|||
|
else if d~itemIndex == 0 then do
|
|||
|
p = pos~copy
|
|||
|
listBox~client2Screen(p)
|
|||
|
ttEditTrack~trackActivate(self, cbEdit, .true)
|
|||
|
ttEditActive = .true
|
|||
|
turnTTEditActiveOff = .false
|
|||
|
turnTTButtonActiveOff = .true
|
|||
|
ttEditTrack~trackPosition(p)
|
|||
|
end
|
|||
|
else do
|
|||
|
turnTTButtonActiveOff = .true
|
|||
|
turnTTEditActiveOff = .true
|
|||
|
currentText = listBox~getText(d~itemIndex)
|
|||
|
end
|
|||
|
end
|
|||
|
else do
|
|||
|
turnTTButtonActiveOff = .true
|
|||
|
turnTTEditActiveOff = .true
|
|||
|
currentText = ''
|
|||
|
end
|
|||
|
|
|||
|
-- Each time we have a change, we close the current tool tip. The tool
|
|||
|
-- tips are now configured for the *new* item the mouse is over. Which
|
|||
|
-- may be not over an item at all, of course. When we return from this
|
|||
|
-- handler, the mouse message will be relayed on to the tool tip, and it
|
|||
|
-- will do its tool tip thing.
|
|||
|
toolTip~pop
|
|||
|
|
|||
|
if turnTTEditActiveOff, ttEditActive then do
|
|||
|
ttEditTrack~trackActivate(self, cbEdit, .false)
|
|||
|
ttEditActive = .false
|
|||
|
end
|
|||
|
|
|||
|
if turnTTButtonActiveOff, ttButtonActive then do
|
|||
|
ttButtonTrack~trackActivate(self, comboBox, .false)
|
|||
|
ttButtonActive = .false
|
|||
|
end
|
|||
|
end
|
|||
|
|
|||
|
return 0
|
|||
|
|
|||
|
|
|||
|
/** onNeedText()
|
|||
|
*
|
|||
|
* The event handler for the NEEDTEXT event. This method is invoked by the
|
|||
|
* monitor for the list box tool when that tool is requesting the text it should
|
|||
|
* display.
|
|||
|
*
|
|||
|
* We set the text to the text of the current item the mouse is over.
|
|||
|
*/
|
|||
|
::method onNeedText unguarded
|
|||
|
expose currentItem currentText
|
|||
|
use arg toolTip, listBox, info
|
|||
|
|
|||
|
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()
|
|||
|
*
|
|||
|
* The event handler for the SHOW event. This method is invoked by the monitor
|
|||
|
* for the list box tool when that tool is notifiying the list box that it is
|
|||
|
* about to show the tip. This gives us the chance to do a custom positioning
|
|||
|
* of the tip.
|
|||
|
*
|
|||
|
* If we are not doing custom positioning we just return false. False tells the
|
|||
|
* tool tip that it should position the tip itself.
|
|||
|
*
|
|||
|
* If we are doing the positioning, we set the postion we want and return true.
|
|||
|
* True tells the tool tip that we already positioned the tip and it should not
|
|||
|
* change its postion.
|
|||
|
*
|
|||
|
* To get the position we determine which item the mouse is over, in list box
|
|||
|
* client coordinates and convert the client coordinates to screen coordinates.
|
|||
|
* The position we want is the top left corner of the list box item. But, the
|
|||
|
* tool tip tips are slightly taller than a list box item, so we shift the
|
|||
|
* position up 3 pixels, which gives us a position that does not obscure either
|
|||
|
* the item above or the item below.
|
|||
|
*/
|
|||
|
::method onShow unguarded
|
|||
|
expose currentItem comboBox lbRect itemHeight doCustomPositioning
|
|||
|
use arg toolTip, listBox, toolID
|
|||
|
|
|||
|
if \ doCustomPositioning then return .false
|
|||
|
|
|||
|
if currentItem > 0 then do
|
|||
|
topIndex = comboBox~getFirstVisible
|
|||
|
r = lbRect~copy
|
|||
|
r~top = (currentItem - topIndex) * itemHeight
|
|||
|
r~top -= 3
|
|||
|
listBox~client2screen(r)
|
|||
|
|
|||
|
toolTip~setWindowPos(0, r~left, r~top, 0, 0, "NOACTIVATE NOSIZE")
|
|||
|
return .true
|
|||
|
end
|
|||
|
|
|||
|
return .false
|
|||
|
|
|||
|
|
|||
|
/** onCBNeetText()
|
|||
|
*
|
|||
|
* The event handler for the NEEDTEXT event. This method is invoked when the
|
|||
|
* combo box tool tip is requesting the text for the tip.
|
|||
|
*
|
|||
|
* The combo box tool tip is a normal tool tip, there is no manageAtypicalTool
|
|||
|
* monitor for this tool tip. Recall, that we set this tool tip up with 2
|
|||
|
* tools. One for the rectangle of the selection field and one for the
|
|||
|
* rectangle of the button. We use the tool ID to detemine which tool we are
|
|||
|
* invoked for. If it is the selection field, we return the selected item text.
|
|||
|
* If it is the button, we return the button tip.
|
|||
|
*
|
|||
|
* We can only be invoked when the drop down is closed.
|
|||
|
*/
|
|||
|
::method onCBNeedText unguarded
|
|||
|
use arg toolTip, comboBox, info
|
|||
|
|
|||
|
if info~toolID == .constDir[IDC_TI_EDITRECT] then do
|
|||
|
info~text = comboBox~selected
|
|||
|
end
|
|||
|
else if info~toolID == .constDir[IDC_TI_BUTTONRECT] then do
|
|||
|
info~text = 'Press the button to open the drop down'
|
|||
|
end
|
|||
|
else do
|
|||
|
-- Can not happen
|
|||
|
info~text = ''
|
|||
|
return .true
|
|||
|
end
|
|||
|
|
|||
|
return .false
|
|||
|
|
|||
|
|
|||
|
/** onCBShow()
|
|||
|
*
|
|||
|
* The event handler for the SHOW event. This method is invoked when the
|
|||
|
* combo box tool tip is about to show the tip. This gives us a chance to do
|
|||
|
* custom positiong. If we are not doing custom positioning, we simple return
|
|||
|
* false, telling the tool tip to position the tip itself.
|
|||
|
*
|
|||
|
* The combo box tool tip is a normal tool tip, there is no manageAtypicalTool
|
|||
|
* monitor for this tool tip. Recall, that we set this tool tip up with 2
|
|||
|
* tools. One for the rectangle of the selection field and one for the
|
|||
|
* rectangle of the button. We use the tool ID to detemine which tool we are
|
|||
|
* invoked for. If it is the selection field, we use the postion of the
|
|||
|
* selection field rectangle, convert it to screen coordinates and position the
|
|||
|
* tip at the top left corner.
|
|||
|
*
|
|||
|
* If it is the button, we do the same thing, but position the tip immediately
|
|||
|
* to right of the button.
|
|||
|
*
|
|||
|
* We can only be invoked when the drop down is closed.
|
|||
|
*/
|
|||
|
::method onCBShow unguarded
|
|||
|
expose editRect buttonRect doCustomPositioning
|
|||
|
use arg toolTip, comboBox, toolID
|
|||
|
|
|||
|
if \ doCustomPositioning then return .false
|
|||
|
|
|||
|
if toolID == .constDir[IDC_TI_EDITRECT] then do
|
|||
|
r = editRect~copy
|
|||
|
|
|||
|
comboBox~client2screen(r)
|
|||
|
toolTip~setWindowPos(0, r~left, r~top, 0, 0, "NOACTIVATE NOSIZE NOZORDER")
|
|||
|
return .true
|
|||
|
end
|
|||
|
else if toolID == .constDir[IDC_TI_BUTTONRECT] then do
|
|||
|
r = buttonRect~copy
|
|||
|
|
|||
|
comboBox~client2screen(r)
|
|||
|
toolTip~setWindowPos(0, r~right, r~top, 0, 0, "NOACTIVATE NOSIZE NOZORDER")
|
|||
|
return .true
|
|||
|
end
|
|||
|
|
|||
|
return .false
|
|||
|
|
|||
|
|
|||
|
/** onEditTrackNeedText()
|
|||
|
*
|
|||
|
* This is the event handler for the NEEDTEXT event. It is invoked when the
|
|||
|
* edit tracking tool tip needs the text to display for the selection field.
|
|||
|
*
|
|||
|
* We simply send it the text of the currently selected item.
|
|||
|
*/
|
|||
|
::method onEditTrackNeedText unguarded
|
|||
|
use arg comboBox, toolTip, info
|
|||
|
|
|||
|
info~text = comboBox~selected
|
|||
|
return .false
|
|||
|
|
|||
|
|
|||
|
/** onEditTrackShow
|
|||
|
*
|
|||
|
* This is the event handler for the SHOW event. It is invoked when the edit
|
|||
|
* tracking tool tip is about to show the tip. It gives us the chance to do
|
|||
|
* custom positioning of the tip. If we are not doing custom positioning of the
|
|||
|
* tip we simply return false to tell the tool tip to position the tip itself.
|
|||
|
*
|
|||
|
* If we are positioing the tip, we place it directly over the selection field.
|
|||
|
*/
|
|||
|
::method onEditTrackShow unguarded
|
|||
|
expose editRect doCustomPositioning
|
|||
|
use arg comboBox, toolTip
|
|||
|
|
|||
|
if \ doCustomPositioning then return .false
|
|||
|
|
|||
|
r = editRect~copy
|
|||
|
|
|||
|
comboBox~client2screen(r)
|
|||
|
toolTip~setWindowPos(0, r~left, r~top, 0, 0, "NOACTIVATE NOSIZE NOZORDER")
|
|||
|
return .true
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/** onButtonTrackShow
|
|||
|
*
|
|||
|
* This is the event handler for the SHOW event. It is invoked when the button
|
|||
|
* tracking tool tip is about to show the tip. It gives us the chance to do
|
|||
|
* custom positioning of the tip. If we are not doing custom positioning of the
|
|||
|
* tip we simply return false to tell the tool tip to position the tip itself.
|
|||
|
*
|
|||
|
* If we are positioing the tip, we place it directly to the right of the
|
|||
|
* button.
|
|||
|
*/
|
|||
|
::method onButtonTrackShow unguarded
|
|||
|
expose buttonRect doCustomPositioning
|
|||
|
use arg comboBox, toolTip
|
|||
|
|
|||
|
if \ doCustomPositioning then return .false
|
|||
|
|
|||
|
r = buttonRect~copy
|
|||
|
|
|||
|
comboBox~client2screen(r)
|
|||
|
toolTip~setWindowPos(0, r~right, r~top, 0, 0, "NOACTIVATE NOSIZE NOZORDER")
|
|||
|
return .true
|
|||
|
|
|||
|
|
|||
|
/** onCloseUp()
|
|||
|
*
|
|||
|
* Event handler for the CLOSEUP event. This method is invoked when the drop
|
|||
|
* down list is closed.
|
|||
|
*
|
|||
|
* We have 2 tracking tool tips in this program. They must be manually shown
|
|||
|
* and hidden. If one or the other is visble and the drop down is closed, it
|
|||
|
* will remain on the screen. So, here we hide the tool tip if it is currently
|
|||
|
* visible.
|
|||
|
*/
|
|||
|
::method onCloseup unguarded
|
|||
|
expose ttEditTrack ttEditActive cbEdit ttButtonTrack ttButtonActive comboBox
|
|||
|
|
|||
|
if ttEditActive then do
|
|||
|
ttEditTrack~trackActivate(self, cbEdit, .false)
|
|||
|
ttEditActive = .false
|
|||
|
end
|
|||
|
|
|||
|
if ttButtonActive then do
|
|||
|
ttButtonTrack~trackActivate(self, comboBox, .false)
|
|||
|
ttButtonActive = .false
|
|||
|
end
|
|||
|
|
|||
|
return 0
|
|||
|
|
|||
|
|
|||
|
/** doDefault()
|
|||
|
*
|
|||
|
* The event handler for the 'Tool Tip positions the tool tips' radio button
|
|||
|
* click event. We just need to flip our flag.
|
|||
|
*/
|
|||
|
::method doDefault unguarded
|
|||
|
expose doCustomPositioning
|
|||
|
doCustomPositioning = .false
|
|||
|
return 0
|
|||
|
|
|||
|
|
|||
|
/** doCustom()
|
|||
|
*
|
|||
|
* The event handler for the 'Application positions the tool tips' radio button
|
|||
|
* click event. We just need to flip our flag.
|
|||
|
*/
|
|||
|
::method doCustom unguarded
|
|||
|
expose doCustomPositioning
|
|||
|
doCustomPositioning = .true
|
|||
|
return 0
|
|||
|
|
|||
|
|
|||
|
/** getComboItems()
|
|||
|
*
|
|||
|
* Simple convenience method, returns an array of the items we will add to the
|
|||
|
* combo box.
|
|||
|
*/
|
|||
|
::method getComboItems private
|
|||
|
|
|||
|
entries = .array~of("New York, United States of America, North America", -
|
|||
|
"Johannesburg, South Africa, Africa", -
|
|||
|
"Cape Town, South Africa, Africa", -
|
|||
|
"Saint Petersburg, Russia, Europe", -
|
|||
|
"Mexico City, Mexico, North America", -
|
|||
|
"Los Angles, United States of America, North America", -
|
|||
|
"Casablanca, Morocco, Africa", -
|
|||
|
"Pyongyang, North Korea, Asia", -
|
|||
|
"New Taipei, Republic of China (Taiwan), Asia", -
|
|||
|
"Addis Ababa, Ethiopia, Africa", -
|
|||
|
"Moscow, Russia, Europe", -
|
|||
|
"Yokohama, Japan, Asia", -
|
|||
|
"Chongqing, China, Asia", -
|
|||
|
"Berlin, Germany, Europe", -
|
|||
|
"Rio de Janeiro, Brazil, South America", -
|
|||
|
"Guangzhou, China, Asia", -
|
|||
|
"S<EFBFBD>o Paulo, Brazil, South America", -
|
|||
|
"Karachi, Pakistan, Asia", -
|
|||
|
"Istanbul, Turkey, Europe / Asia", -
|
|||
|
)
|
|||
|
|
|||
|
return entries
|
|||
|
|
|||
|
|
|||
|
/** calcRects()
|
|||
|
*
|
|||
|
* To properly set the tool info for our tool tips, and to properly position
|
|||
|
* the tool tips when we do custom positioning, we need to calculate and save
|
|||
|
* a number of rectangles.
|
|||
|
*
|
|||
|
* We start off with the client rectangles of the combo box, the drop down list
|
|||
|
* box, the selection field, and the button. The client rects within the combo
|
|||
|
* box do not include the borders of the controls. We want to adjust those
|
|||
|
* rects so that they do include the borders.
|
|||
|
*
|
|||
|
* The other problem is that when the list box is dropped down, the list box
|
|||
|
* has the mouse captured, so all mouse messages go to the list box and all
|
|||
|
* mouse positions are relative to the client area of the list box. So, we
|
|||
|
* need to calculate a rectangle for the selection field and the button that is
|
|||
|
* in client coordinates of the list box.
|
|||
|
*
|
|||
|
* The 'd' argument passed in here is the .Directory object returned from
|
|||
|
* getComboBoxInfo(). This directory object contains both the selection field
|
|||
|
* client rect and the button client rect.
|
|||
|
*/
|
|||
|
::method calcRects private
|
|||
|
expose comboBox cbEdit cbListBox lbRect editRect buttonRect editOffsetRect buttonOffsetRect
|
|||
|
use arg d
|
|||
|
|
|||
|
-- The button rect, edit rect, and lb rect do not include borders. We
|
|||
|
-- basically want to adjust them to the combo box width and height.
|
|||
|
cbRect = comboBox~clientRect
|
|||
|
|
|||
|
lbRect = cbListBox~clientRect
|
|||
|
editRect = d~textRect
|
|||
|
buttonRect = d~buttonRect
|
|||
|
|
|||
|
lbRect~left = cbRect~left
|
|||
|
lbRect~right = cbRect~right
|
|||
|
|
|||
|
-- The top and bottom coordinates of both the text and button rects have to
|
|||
|
-- be in relationship to the client rect of the list box. I.e. the
|
|||
|
-- bottoms are at the 0 coordinate of the list box client area and the tops
|
|||
|
-- are at 0 - the height of the combo box.
|
|||
|
height = cbRect~bottom - cbRect~top
|
|||
|
|
|||
|
editRect~left = cbRect~left
|
|||
|
editRect~top = cbRect~top
|
|||
|
editRect~right = buttonRect~left
|
|||
|
editRect~bottom = cbRect~bottom
|
|||
|
|
|||
|
-- Button rect left stays the same.
|
|||
|
buttonRect~top = cbRect~top
|
|||
|
buttonRect~right = cbRect~right
|
|||
|
buttonRect~bottom = cbRect~bottom
|
|||
|
|
|||
|
editOffsetRect = editRect~copy
|
|||
|
editOffsetRect~top -= height
|
|||
|
editOffSetRect~bottom -= height
|
|||
|
|
|||
|
buttonOffsetRect = buttonRect~copy
|
|||
|
buttonOffsetRect~top -= height
|
|||
|
buttonOffsetRect~bottom -= height
|
|||
|
|
|||
|
|
|||
|
|