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 = "&lt;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