| By Hoyt Nelson | Article Rating: |
|
| February 1, 2001 12:00 AM EST | Reads: |
7,982 |
Over the years we've adopted techniques that help us develop our PowerBuilder applications faster and with fewer bugs. This article describes my favorite bag of tricks, the pop-up debug menu (see Figure 1).
The debug menu is for developers only and is unavailable when running an executable. Most of the menu items are express aids to development:
- What is this object and which PBL contains it?
- Show me the SQL that failed.
- What is the value of the key column?
- Replace those SQL question marks with the actual data values.
- Do I use the same accelerator more than once?
- Show me which DataWindow columns and labels are named improperly.
- Show the tab sequence on this DataWindow, highlighting problems.
The general philosophy is this: when we create generally applicable PowerBuilder scripts that make it easier to program, place them in the debug menu so they're readily available everywhere without any additional coding.
The debug menu is invoked by holding down a magic key (we selected {control-Q}) as the developer right clicks any window, DataWindow control, or CommandButton. The invoking code is encapsulated in a single function that's called in the base class objects' RButtonDown! event. The function knows what kind of object was clicked so it can make inapplicable menu items invisible. In part to make the debug menu universally available, I periodically pass through the application making sure all windows, DataWindow controls, and CommandButtons are descendents of the applicable base class.
This article doesn't delve deeply into how the various items are implemented; that would make an already-too-long article even longer! I'll briefly describe the first set of debug menu items, those (mostly) not specifically related to DataWindows, along with most of the technical details of the premier debug menu item, Object Name. Subsequent articles will complete the Object Name explication and present the implementation of other debug menu items.
General Purpose Menu Items
These are the items above the first separator line. They apply (with one exception) to many different types of objects, while those below the separator are DataWindow-specific.
Object Name
This is the original and still the most useful item. If you're running an unfamiliar corner of the application and see a problem - a DataWindow Object (DWO) has a misspelling - how do you find that DWO?
Years past I'd have manually searched the PBLs for a string on the DWO that seemed comparatively unique. I'd probably come up with half-a-dozen false hits before finding the DWO of interest. It can be even harder to identify a window if it doesn't contain any unusual strings. Sometimes searches for the window fail because the string you're searching for actually appears on a UserObject that's situated on the window. It's very frustrating!
Now I just pop the debug menu and select Object Name, which yields something similar to Figure 2.
Once I know the name of the DWO, the PBL it's in, the window it's on, and the window's PBL, I can go straight to the DataWindow, or to the window, and modify the DWO from there.
Clicking a button or another non-DataWindow control yields something similar to Figure 3.
It has identified the right-clicked button, the tab page containing the button, the tab that contains the tab page, and the window that contains the tab. The containing PBL is shown for each object. The ClassName shows the "runtime" name you'd use to address the object in PowerScript, as opposed to the "Library Painter name" that appears in uppercase on the left.
Note that the MessageBox's contents are placed in the clipboard each time. The goal is to preclude the programmer having to transcribe information by hand.
The code behind Object Name is discussed in the Implementing the Debug Menu section below.
DWO Name
This implementation of Object Name
is for DWOs only. Instead of popping up a MessageBox(), DWO Name displays only the DWO's name and containing PBL in the MicroHelp area:
D_PROGRAMMING in e:\p2k\pontprog.pbland just the DWO Name is placed in the clipboard.
This makes it easier to go directly to the DWO, open the select object dialog, select the PBL, and paste in the DataWindow Object name. It saves having to paste the longer message from Object Name into some script, then selecting and cutting the object name in order to paste something into the PB dialog. Another goal is to preclude the programmer from typing anything when a mere paste will do. Laziness is our watchword!
Object Name and DWO Name identify which PBL contains the DWO. For most PB objects, the containing PBL can be identified using the object's ClassDefinition.LibraryName attribute. That's not available for DWOs, however. The presentation of the "identify the DWO's PBL" technique must wait until the next article, unfortunately, because space is tight!
Context
This function is useful when you're trying to programmatically address objects that are a number of levels removed from your script. The archetypal example is a button on a tab page on a tab control on a window, as in the Object Name example above. You're on a different window so how do you address that button in PowerScript?
The Context script recurses up the GetParent() tree until it finds the encompassing parent window, collecting the ClassName of each object as it goes. The accumulated names are the "runtime" names that you'd apply in your PowerScript.
The line is in the clipboard (see Figure 4) and can be used anywhere to address the CB_SELECT button directly (assuming the window is opened "globally" so it can be addressed directly).
Switch to Testbed
This is almost incidental to the debug menu, but it's an extremely valuable technique. The idea is to have a place to develop and test your code, other than the place where you intend to ultimately apply it. The testbed is that place: it's a window in which you can quickly invoke your new code and preserve it for subsequent development and testing.
Control to Clipboard
This function does some Windows SDK calls to put the clicked control into the clipboard. This is great for writing preliminary Help documentation, for example, a button (see Figure 5)
and a DataWindow (see Figure 6).
You can do this with a utility like SnagIt (my favorite), but it's handy because it's built into the application. This is a great one to show to your testers, who might not be familiar with SnagIt or analogous tools. When they need to describe where a defect appears, it makes everyone's life simpler if they can pop the menu and pick Control to Clipboard, then paste it into their bug report.
Position/Dimensions
Perhaps you're working on screen geometry and need to know the y attribute of one control so you can set another control's y to match. Maybe you want a couple of controls to be the same width!
In that case, select the Position/Dimensions menu item (see Figure 7).
The "Maximum width" appears only when the menu item is applied to a DataWindow: it shows the sum of x plus width of the right-most visible column on the DataWindow Object, that is, the maximum width of the DataWindow display.
Why would someone care about the maximum width? I cared because of an unusual feature of my framework: if I set a maximum width attribute on a DataWindow control, the user can open a (small) DataWindow in a window that's max_width wide for easier viewing of the whole DataWindow.
Accelerators
This is the instance of Accelerators in the main body of the pop-up menu, not Menu Accelerators at the bottom. Accelerators finds the parent window, then examines the source of the window and the objects on the window to identify accelerators.
It also accumulates a list of controls (truncated in Figure 8) that could potentially have accelerators but don't. That tends to be an overwhelmingly long list on complex windows. Having accelerators everywhere is often too difficult.
The most useful part is the duplicates section, identifying ambiguous accelerators. Truth be told, this code is fallible so duplicates may be missed. However, when a duplicate is found, it's almost certainly something you should fix.
Horizontal Measurer
This is useful when you want to measure how wide something is on the screen (see Figure 9).
Position one end of the window at one side of the object of interest, then drag the vertical bar to the other edge off the object. The number of PBUs in the indicated directions is shown on the static text to the left of the vertical bar. I created this in 1992 (I've been PowerBuilding way too long) and have probably used it only about 20 times, but on those occasions it's been the tool of choice. The Help text discusses how to precisely adjust the window position in microincrements using the keyboard.
Vertical Measurer
Same as the Horizontal Measurer menu item onlyŠvertical!
Whatever's Up
The idea here is that any programmer may go into this menu item and write whatever's handy at the moment. What is the background color of this control, for example. That doesn't come up often enough to merit a dedicated menu item, but it would be quick enough to implement in Whatever's Up?
Anyone can write anything here, but we extend to each other the courtesy of commenting out the existing code instead of deleting it so it can be resurrected as needed. Something generic that's used more than a few times graduates to a new debug menu item.
Zap the App
This executes a halt close so the programmer can quickly get back to the code. It's surprisingly handy, probably the most used menu item.
If you're nested three response dialogs deep,
it's a real time and annoyance saver. Believe it or not "{control-Q} right-click, z" becomes reflexive.
Implementing the Debug Menu
Finally, some listings! I'll show how I invoke the debug menu, then briefly go into some details of the Object Name menu item, pending the next article.
f_get_parent_window(), f_key_down_debug_
menu(), f_pop_menu() and f_running _exe()
These functions are called to pop up the debug menu, like so:
if f_key_down_debug_menu() then
m_debug_popup l_popup
l_popup = create m_debug_popup
f_pop_menu( this, l_popup )
return
end if
When PB5 was introduced, attempting a create (menu) in a global function produced an instant GPF, hence the construct above. Otherwise, the create would be included in f_pop_menu().
f_key_down_debug_menu() is short:
if f_running_exe() then return falsef_running_exe() is shorter:
return keyDown( keyControl! ) and & keyDown( keyQ! )
return handle( GetApplication() ) <> 0
The f_running_exe() call keeps the debug menu from appearing in production. Otherwise, f_key_down_debug_menu() returns true if both the control key and the Q key are held down.
Using a dedicated function such as f_key_down_debug_menu() makes it easy to find those magic keystrokes. In the bad old days, I'd just call keyDown() whenever a magic key was needed, but that created problems:
- I'd forget the magic keys and have to search the code for keyDown() to remember how to invoke functionality.
- When I wanted to change a magic key, there was always a danger of changing it in some places and not others.
- When I needed to create a new magic key, it was impossible to figure out which keys were still available.
f_pop_menu() has some framework-dependent code that you'll probably have to modify for your application. Its purpose is to handle the positioning of the pop-up menu depending on the circumstances, especially whether the relevant window is the MDI frame, an MDI sheet, a response window, or a child. See Listing 1 for the details; hopefully the (excessive) comments therein will suffice. f_pop_menu() is handy because it isolates the incantation that gets the menu to pop up where you expect. That's nontrivial.
f_pop_menu() calls two more functions. f_mdi_frame() returns the MDI frame window. Store your MDI frame in a variable somewhere at start-up and return that reference with f_mdi_frame(). The other function, f_get_parent_window(), is extremely useful and it's long enough to appear as Listing 2.
Object Name's Code
See Listing 3 for the Object Name menu item's source. Due to space constraints, the listing's comments will have to do for the time being, and the following just get waved at until the next article (wave, wave):
- f_get_library_object_from_stash(): Discovers which PBL contains a DataWindow object
- f_get_parent_objects2(): Recurses up the parent tree, much like f_get_parent_window(), and information suitable for Object Name is collected en route and returned to the calling routine
- f_set_default_pbl(): The "Select <Object Type>" (PB6 and earlier) or "Open" dialog (PB7) opens with the PBL that contains the object already selected, a classic instance of overengineering in the cause of laziness
My next article will make all known! Don't miss it!
Summary
The pop-up debug menu provides a convenient way to apply functionality that makes programmers more productive and/or increases the quality of PowerBuilder applications. The best example is the Object Name menu item, which allows you to instantly identify the name and PBL location of any object in your application as you run the app.
If you're persuaded and feel the impulse to create your own debug menu, I suggest you start by creating a pop-up menu and implementing its invocation, using techniques such as f_pop_menu() and f_key_down_ debug_menu(). The next article will complete the description of Object Name's implementation and that of the kindred DWO Name. Stay tuned to PBDJ for more detailed descriptions of how the most valuable debug menu items are implemented. Before long, you'll have a powerful debug menu and wonder how you lived without it!
Published February 1, 2001 Reads 7,982
Copyright © 2001 SYS-CON Media, Inc. — All Rights Reserved.
Syndicated stories and blog feeds, all rights reserved by the author.
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!).
- Why SOA Needs Cloud Computing - Part 1
- Cloud Expo and The End of Tech Recession
- The Transition to Cloud Computing: What Does It Mean For You?
- A Rules Engine Built in PowerBuilder
- Sybase Named “Silver Sponsor” of iPhone Developer Summit
- How PowerBuilder Got Its Groove Back
- The Cloud Has Cross-Border Ambitions
- Ulitzer Named "New Media" Partner of Greatly Anticipated iStrategy Event in Berlin
- Risks and Enterprise Mobility?
- Steps for Success in Enterprise Mobility?
- Are Mobile Luddites Resisting Mobility?
- Hot Event in Santa Clara Becomes Cool with the iPhone
- The Difference Between Web Hosting and Cloud Computing
- Sybase CTO to Speak at 4th International Cloud Computing Expo
- Why SOA Needs Cloud Computing - Part 1
- Cloud Expo and The End of Tech Recession
- The Transition to Cloud Computing: What Does It Mean For You?
- Five Reasons to Choose a Private Cloud
- Seeding The Cloud: The Future of Data Management
- The Threat Behind the Firewall
- Economy Drives Adoption of Virtual Lab Technology
- Tips for Efficient PaaS Application Design
- A Rules Engine Built in PowerBuilder
- Sybase Named “Silver Sponsor” of iPhone Developer Summit
- Where Are RIA Technologies Headed in 2008?
- PowerBuilder History - How Did It Evolve?
- The Top 250 Players in the Cloud Computing Ecosystem
- Custom Common Dialogs Using SetWindowsHookEx
- DDDW Tips and Tricks
- OLE - Extending the Capabilities of PowerBuilder
- DataWindow.NET How To: Data Entry Form
- Book Excerpt: Sybase Adaptive Server Anywhere
- Sybase ASE 12.5 Performance and Tuning
- Working with SOA & Web Services in PowerBuilder
- Office 2003 Toolbar: A New Look For Your Old PowerBuilder App
- Dynamically Creating DataWindow Objects

































