Welcome!

PowerBuilder Authors: Dan Joe Barry, Carmen Gonzalez, Ian Thain, Yakov Werde, Paul Slater

Related Topics: PowerBuilder

PowerBuilder: Article

Popup Debug Menu Part 2

Popup Debug Menu Part 2

In my previous article (PBDJ, Vol. 8, issue 2), I demonstrated the Object Name feature of the debug popup menu. The popup menu is a developer's tool invoked by holding down a magic key as you right-click a window or control. The initial and most useful debug menu item is Object Name, which you can use to identify any object in your PowerBuilder application as you run the application. Selecting Object Name as you right-click a DataWindow produces output like that shown in Figure 1.

Each line in the message box represents a level in the object hierarchy, with the upper item contained within the item immediately below. To the left is the name of the object as shown in the Library Painter, i.e., its development-time name. The item in brackets is the object's containing PBL. To the right is the object's ClassName(): its runtime name.

The first debug article left some subtopics unexplained. One is the technique for identifying which PBL contains a DataObject. For non-DataObjects, the ClassDefinition.LibraryName suffices, but you have to jump through some hoops to get the equivalent information for a DataObject. I postponed clarification of how f_get_parent_objects() works. I also promised to describe the f_set_default_pbl() function, which is short and simple, and makes Object Name one click easier to use. This article clarifies all of these items. By the end you'll have all you need to create your own debug menu with Object Name!

Why should you care? It is valuable to be able to click your application and immediately know the name of the DataObject and the PBL that contains it. This may seem like more trouble than it's worth. If you're having a hard time tracking down an object, this could be a big help. And remember, you can always just download the associated code from www.SYS-CON.com!

Stashing Objects
Before the ClassDefinition attribute arrived with PB6, I needed to be able to find out which PBL contained a given object, for such purposes as exporting an object's source to facilitate a search-and-replace. A series of functions provided the means, and they're still useful for DataObjects. The technique is to import all the application's objects into a DataObject, import that DataObject into a PBL, and quickly reference the object list as needed.

The following code stashes the object list. The indented functions are called by the preceding function, e.g., f_get_library_list_ as_array() is called by f_stash_library_ object_list().

f_stash_library_object_list()
f_get_utility_dw()
f_get_library_list_as_array()
f_load_dw_with_pbl_objects_by_type()
f_get_application_pbl_and_name()
f_library_import()
Listing 1 presents f_stash_library_object_ list().

1. Get a working DataWindow and assign it to the DataObject dw_pbl_directory.
The working DataWindow is one way to work around the problem discussed in my article "Writing Functions for All DataWindow-ish Types" (PBDJ, Vol. 8, issue 1). What you'd like in your script is a DataStore, but you're seemingly limited by functions that work with DataWindows. You could follow the "DataWindow-ish" prescription for adapting your functions to all DataWindow-ish types. Or, you could use a utility DW, and treat it as you would a DataStore.

f_stash_library_object_list() takes the second approach.

The utility DW is an ordinary DW that resides on some global window that is certain to be open, such as the main desktop window of your application. Make the DW invisible in its constructor and return it with a function call. My f_get_utility_dw() (see Listing 2; all listings are available at www.PowerBuilderJournal.com) provides for the missing main desktop window if we're running the testbed. Refer to the previous debug article (PBDJ, Vol. 8, issue 2) for information about the testbed.

The DataObject dw_pbl_directory has the typical columns required for importing the results of a Library Directory() call: object name, modified date, and Library Painter comment. It also has two additional columns: PBL name and object type.

2. Get the set of PBLs that comprise
the application.

The function f_get_library_list_as_array() examines the PB.INI file to identify the set of PBLs that comprise an application. See Listing 3 for all the details. You'll find f_find_pb_ini2() mentioned; replace that with your PB.INI file's location, hard-coded. f_get_library_list_as_array() also calls f_parse_item_string(), which parses a string and places its contents into an array.

3. Import all the objects in the application into the working DW.
This is accomplished by f_load_dw_with_pbl_objects_by_type() (see Listing 4). This function is artful in that it executes a separate LibraryDirectory() for each type of PB object: DataObjects, Windows, UserObjects, etc. Before it imports the resultant string into the accumulating DataWindow, it performs a substitution that embeds the PBL name and the object type into the LibraryDirectory() result string. This causes the PBL and type to be imported into the DataWindow along with the rest of the LibraryDirectory() information.

The suffix below corresponds to the letter that PowerBuilder appends to exported objects to indicate the type: "d", "w", and "u" for DataObjects, windows, and UserObjects.

ls_directory = f_substr( ls_directory,
"~n", "~t" + the_pbls[ j ] + "~t" + &
ls_suffix + "~r~n" )
At this point I have a working DW with a PBL populated with the usual Library- Directory() information (object name, modification date, and Library Painter comment), along with each object's containing PBL and a single-letter indicator of the object's type.

Next, I need to stash the object list, which means that I need to import the object list into the application, retaining the data I just imported!

4. Get the syntax of the now-populated list of application objects.
These are available as DataWindow attributes you can describe as follows:

ls_dwo_syntax = &
ldw_work.describe( "DataWindow.Syntax" )
+ &
ldw_work.describe(
"DataWindow.Syntax.Data" )
The "DataWindow.Syntax.Data" is the part that returns the syntax for the object names and related elements that I imported into the DataObject in the step above "Import all the objects in the application into the working DW."

5. Get the name of the application object and the name of its PBL.
See Listing 5. The function f_get_application_pbl_and_name() looks up the application object name and the PBL in the PB.INI file. Again, you'll have to hard-code the location of your PB.INI file.

6. Import the syntax.
The application object name is used to define the name of the DataObject to be imported. This allows a given PBL to contain multiple stash DataObjects: one for each application object in that PBL.

ls_dwo_name = "dw_" + ls_app_name +
"_objects"
if f_library_import( ls_app_pbl,
ls_dwo_name, &
ls_dwo_syntax, ls_comment ) then exit
Listing 6 shows how f_library_import() works. This function applies a very superstitious, possibly unviable, theory of mine; opening an invisible window before the LibraryImport() makes the import more reliable. Hey, it doesn't hurt! Just create a window and make it invisible.

So what have we accomplished? We now have, as a permanent DataWindow Object in the application, something named dw__objects which has the usual LibraryDirectory() information, plus the PBL and type, for every object in the application. If you opened the DataObject in the DataWindow Painter, and pulled down Rows -> Data, you would see something similar to Figure 2.

This is how Object Name gets the PBL that contains the DataObject. The PBL name and the object's type are out of sight, off to the right.

Using the Stashed List of Objects
The function hierarchy for getting an object out of the stash is simpler.

f_get_library_object_from_stash()
f_get_utility_dw()
I have a favorite function, which returns the utility DataWindow used by f_get_library_object_from_stash() to look up an object using the Find() command. See Listing 7. This function executes quickly.

f_get_parent_objects2()
Ignore the "2" - it merely indicates that this is a variant of the original. The source is shown in Listing 8. This function loops up the control hierarchy until it reaches the containing window. Because the debug menu is always invoked from a visual object, you can depend on eventually arriving at the parent window. En route, f_get_parent_objects2() builds the string that describes the object hierarchy, including the ClassDefinition.name attribute that is the development-time name of the object.

f_set_default_pbl()
This simple function makes Object Name a little easier to use. If you look in your PB.INI file (pre-PB8, I am informed), you'll see something like the following:

[Application]
DefLib=e: \p2k\ pontfunc.pbl
AppLib=e: \p2k\ pontappl.pbl
AppName=pont
The function f_set_default_pbl() sets the DefLib entry. This is where PB stashes the information that the last PBL you operated on was pontfunc.pbl. If you open a PBL in the Library Painter, for example, DefLib is set to the name of that PBL. The next time you open the Select <Object Type> dialog, as in the Select DataWindow dialog in Figure 3, the PBL identified by the DefLib will be pre-selected.

The DefLib was set to "e:\p2k\pontfunc.pbl" when the Figure 3 dialog was opened.

The function f_set_default_pbl() simply sets that attribute (see Listing 9). As a result, when you use Object Name to identify the name and containing PBL of an object in your application, the DefLib attribute is set so your next Select <Object Type> operation has the containing PBL pre-selected for you. It saves you from navigating the list of PBLs at the bottom. (Is that cool? Or am I just off-the-chart lazy?!)

Summary
This article presents a somewhat round- about method for identifying the PBL that contains a DataObject and the code for retrieving that information at runtime. The immediate application is in the Object Name script shown in Part 1 of this series (PBDJ, Vol. 8, issue 2), where the PBL containing the DataObject of interest is identified. I also addressed the promised subtopics that didn't fit into the previous articles, including the functions for recursion up the tree of containing objects and for setting the default library.

You have all you need to create your own Object Name debug menu item. Or, just visit www.SYS-CON.com to download the source! Check it out!

More Stories By Hoyt Nelson

Hoyt Nelson is an independent contractor, a 10-year PowerBuilder veteran, and father to the amazing Nelson, Kent, Emma, and Molly (Hi, kids!).

Comments (0)

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.