Listing 1
/* Boolean f_pop_menu( GraphicObject the_graphicobject, menu the_popup )
The way PowerBuilder places menus relative to the pointer
is not immediately obvious.f_pop_menu() simplifies the problem.
It has been designed to pop the the_pop at the current
pointer position. It works for main, response, child,
and popup windows; if you want to pop up up on a frame,
you're on your own. It handles both kinds of window
opens,especially open() and OpenSheet(). The combinations:
Open() OpenSheet()
main main
response response
child child
popup popup
have all been tested and work as expected, and they
work regardless of the frame's location. Most of
these combinations are not legal, by the way:
OpenSheet() is only
supposed to be done with main windows, and open( main )
may not make sense.
The code has a couple of "magic numbers" that you may
need to adjust for your application. The magic "205"
offset in the code accounts for the frame having a
menu with a single toolbar, when popping up a menu
in a response window. The
toolbar is assumed to NOT have text, though text will
only cause the offset to be slightly off. You will
have to muck with the number if you have no toolbar
or two toolbars. The "89" is the adjustment
for child windows, and may need similar adjustment.
The function has a special provision for menus that
inherit from m_base_popup. That
menu has at least one instance datum, igo_parent,
which is set to the_graphicobject.
f_pop_menu() _detects_ whether the_menu is a
m_base_popup descendent, so you don't have to
worry about it. If you're interested in how:
ue_is_m_base_popup_descendent is an event
inserted into m_base_popup by exporting the
source, editing and importing it, because the
Menu Painter doesn't provide a means to add
menu events. f_pop_menu() does a TriggerEvent()
to see if ue_is_m_base_popup_descendent exists
on the_menu; if so, then the menu is a m_base_popup descendent.
A not-great feature was introduced with PowerBuilder 5.0.
If you try to instantiate a menu within a global function,
PB will GPF. With PB 4.0, the create could have
taken place within f_pop_menu(), but no longer.
This means you have to create the menu before you call f_pop_menu(). See the usage.
Parameters:
the_graphicobject: Where the menu is being
popped up, e.g., a button or tab or window
the_popup: The menu, already instantiated
The_graphicobject will always be ""this," I think
. The_popup must be valid, else this function fails and returns TRUE.
Returns: TRUE on failure, FALSE on success
f_pop_menu() can fail if the arguments are invalid.
Usage:
This pop ups up the menu m_debug_popup, a m_base_popup descendent:
m_debug_popup l_popup
l_popup = create m_debug_popup
f_pop_menu( this, l_popup )
f_pop_menu() handles destroying the popup.
*/
long ll_offset
m_base_popup lm_base_popup
window lw_context, lw_frame, lw_parent
// Get the parent window
lw_parent = f_get_parent_window( the_graphicobject )
// If this is a m_base_popup descendent, then stash the
// GraphicObject so it can be referenced in the menu
if TriggerEvent( the_popup, "ue_is_m_base_popup_descendent" ) = 1 then
// Cast the_popup to a m_base_popup to make
the latter's attributes available...
lm_base_popup = the_popup
// Stash the GraphicObject in the
m_base_popup.ipo_parent attribute
m_base_popup.igo_parent = the_graphicobject
end if
// How the menu is popped is a function of the window type.
//
// The "GetActiveSheet() <> the_popup.iw_parent"
accounts for the situation in which a
// main window (which is NOT a response window)
is opened using Open() instead of
// OpenSheet(), in which case menu has to be
opened with respect to the parent
// window, not the frame. If Open() is used,
then the main is NOT the active sheet.
lw_frame = f_mdi_frame()
if not IsValid( lw_frame ) then // The MDI frame is open yet!
lw_context = f_get_parent_window( the_graphicobject )
elseif lw_frame.GetActiveSheet() <> lw_parent then
if lw_parent.WindowType = Child! then
lw_context = lw_frame
else
lw_context = f_get_parent_window( the_graphicobject )
// ll_offset is the number of PBUs between bottom
of frame title and top of sheet;
// it is suitable for an MDI frame application
with a menu and ONE toolbar.
if lw_parent.WindowType = Response! then
// For response windows, it was popping too low
ll_offset = 0
else
ll_offset = 205
end if
end if
else
if lw_parent.WindowType <> Response! then
lw_context = lw_parent
ll_offset = 100
if lw_parent.WindowType = Child!
then ll_offset = 89 // Yet another magic number
else
lw_context = lw_frame
// It IS a Response
ll_offset = -200
end if
end if
// Pop the window
// The 'item[1]' trick avoids displaying the
top-level menu
the_popup.item[1].PopMenu( lw_context.PointerX(),
lw_context.PointerY() + ll_offset )
// We're done: clean up!
destroy the_popup
return false // Success
Listing 2
/* window f_get_parent_window( GraphicObject
the_graphic_object )
This function calls GetParent() repeatedly until
the type returned is a window, to return the ultimate
window on which a userobject may appear.
Parameters:
the_graphic_object - e.g., a UserObject,
a button,... any control
You can pass in a window, in which case this
function will return that window as the
parent,...but why would you want to?
Usage:
This returns the parent of the userobject uo_test:
window lw_parent
lw_parent = f_get_parent_window( uo_test )
*/
GraphicObject the_parent
// The graphic object may be a window
if the_graphic_object.TypeOf() = Window! then return the_graphic_object
do while true // Exit on finding a window
the_parent = the_graphic_object.GetParent()
if the_parent.TypeOf() = Window! then return the_parent
the_graphic_object = the_parent // Get ready for the next loop
loop
Listing 3
ClassDefinition l_ClassDefinition
string ls_message = "m_debug_popup.m_objectname:
Could not figure out the message!", ls_library
string ls_pbl, ls_comment, ls_type
datawindow idw_parent
if IsValid( igo_parent ) then
if igo_parent.TypeOf() = DataWindow! then
// Cast it to get access to the DataObject
idw_parent = igo_parent
// Get the DataObject's PBL
if f_get_library_object_from_stash
( idw_parent.DataObject, ls_pbl, ls_comment, &
ls_type ) then ls_pbl = "<PBL Unknown>"
// Display the DataObject
ls_message = "The DataObject is "
+ upper( idw_parent.DataObject ) + " [ " + &
ls_pbl + " ]~r~non ~r~n " + &
upper( igo_parent.ClassName() )
+ "" + f_get_parent_objects( igo_parent )
// If it is NOT a DataWindow control
else
// The ClassDefintion knows the objects
REAL name, i.e. the name that
// appears in the library painter, as
opposed to the ClassName() that
// the programmer assigns in the Window
or UserObject painters. The
// real name is much more useful,
when you're wondering what's broken.
// PB6+ specific!
l_ClassDefinition = igo_parent.ClassDefinition
if l_ClassDefinition.name <> ParentWindow.ClassName()
then
ls_message = "The object is " +
l_ClassDefinition.name + " in " + &
l_ClassDefinition.LibraryName + ", on ~r~n"
ls_message += f_get_parent_objects2
( igo_parent )
// Save the first PBL encountered
if f_ns( ls_pbl ) then ls_pbl =
l_ClassDefinition.LibraryName
else
ls_message = "The window is " +
ParentWindow.ClassName() + " in " + &
l_ClassDefinition.LibraryName
// Save the first PBL encountered
if f_ns( ls_pbl ) then ls_pbl =
l_ClassDefinition.LibraryName
end if
end if
else
ls_message = "Bug! The parent graphic object is invalid!"
end if
// No typing needed!
ClipBoard( ls_message )
// Make the PBL the default, so PB opens to that PBL automatically
f_set_default_pbl( ls_pbl )
// Display it in a MessageBox(), because the framework resets
MicroHelp to "Ready" too fast
MessageBox( "It's in the clipboard for your convenience", ls_message )
Listing 4
/** Boolean f_ns( long the_string )
"ns" -> Not a String
This function returns FALSE if the_string contains at
least one nonblank character, TRUE if the_string is
NULL, empty (""), or all blanks. It's a good way to catch
functions that could return a NULL or an empty string or
all blanks, any of which will
probably screw up your code.
Parameters:
string the_string -- Typically, the string
returned by a PowerBuilder function
Returns: Boolean, TRUE if the_string is NULL, empty (""),
or all blanks, else FALSE
Note that a tab or a carriage return is NOT blank,
and f_ns( "~t" ) returns FALSE.
Usage:
if f_ns( dw_1.GetItem( 1, ls_emp_name ) ) then return
// Failed
See also: f_np(), Not Positive, similar only it returns
TRUE if the passed number is NULL or less than 1
**/
if IsNull( the_string ) then return FALSE
if len( trim( the_string ) ) = 0 then return FALSE
// At least one non-blank!
return TRUE