Wednesday, April 1, 2009

DataWindow Tooltips in PB 11.5

Today's sneak preview is on the DataWindow Tooltip enhancement in 11.5. This screenshot shows a tooltip on the first_name column:

This shows a tooltip with the title property populated as well as an icon property selected. It also shows the bubble format. Below you can see the list of properties that will be available for tooltips:

It is important to point out that you can place a tooltip on any control on the DataWindow. Drawing objects, buttons, nested reports, computed fields, labels, they all support the tooltip property!

Dynamically Creating DataWindow Objects

Objects can be added to your DataWindow programmatically via a Modify statement. In my opinion, the dynamic creation of objects within a DataWindow has been a highly underused feature.
Dynamically creating (or destroying) objects within a DataWindow has many advantages such as:
- Dynamically changing the content
- If a printed DataWindow varies in appearance from its visual presentation



The syntax for creating objects within DataWindows can be daunting; no wonder it's not used that often. Before I go into more detail, it's important to know how objects are contained in a DataWindow in the first place. Once you understand this, you will find that dynamically creating objects is easy.
The Naked DataWindow :


It should come as no surprise that a DataWindow is really just a collection of objects, each with its own properties. When a programmer is creating a DataWindow via the DataWindow painter, he or she is actually just using a graphical IDE to create and set the properties of the objects that make up the DataWindow. Actually, the naked DataWindow is not graphical at all but exists in text format. It's just that most programmers tend to create and edit DataWindows via the DataWindow Painter.
Prior to PowerBuilder 8, if you wanted to take a peek at what a DataWindow looked like in its text format, it would have to be exported via the library painter, then opened up within a text editor. At that point, changes could be made and the text file could be imported back into PowerBuilder. As the file was being imported, PowerBuilder would regenerate it, making sure your hack was syntactically correct.
Since PowerBuilder 8, Sybase has allowed programmers to directly modify objects via a Source Editor, effectively putting an end to the enjoyable, unsupported DataWindow source hacking days. By viewing the source code of an existing DataWindow, you'll appreciate the syntax of creating objects dynamically. A basic understanding of the DataWindow syntax can help a lot. Let's start with the DataWindow illustrated in Figure 1.



Understanding the Syntax

If this simple DataWindow is opened up in the Source Editor, you'll see all the objects that it's comprised of. The complete syntax is extremely lengthy and cannot be listed in its entirety here. At first glance, the syntax looks foreboding, but after further inspection, it becomes more familiar. If you think about it, it looks almost identical to the syntax for the Describe and Modify function. That's because it is the syntax for Describe and Modify.
The source code can be broken down into six categories:
1. Version information
2. DataWindow properties
3. Band properties
4. Source definition
5. Object definitions
6. DataWindow HTML/XML properties

Version Information

Let's take a closer look at our Naked DataWindow:
release 9;
The first line of the syntax comprises only one statement indicating the PowerBuilder release with which this DataWindow object was constructed. This line will contain only major release numbers (you won't see 9.01). The release number is important as it tells the DataWindow Engine how to handle the rest of the syntax. Obviously, more recent DataWindow versions contain added features. If you're in PowerBuilder 8 and try to open a DataWindow that was built in PowerBuilder 9, PowerBuilder gives the error message "DataWindow Syntax has incorrect release number." On the other hand, a more recent version of PowerBuilder will happily import a DataWindow created in an earlier release. When an earlier version of a DataWindow is saved or regenerated, it's migrated to the current version.
If you're resourceful, the DataWindow may be migrated backward by changing its release number. It may take a bit of trial and error to remove any of the source code that may not be understood by previous DataWindow Engines.

DataWindow Properties
datawindow(units=0 timer_interval=0color=12632256 processing=0 HTMLDW=no print.printername=""print.documentname="" print.orientation = 0 print.margin.left =110 print.margin.right = 110 print.margin.top = 96 print.margin.bottom = 96print.paper.source = 0 print.paper.size = 0 print.canusedefaultprinter=yesprint.prompt=no print.buttons=no print.preview.buttons=no print.cliptext=noprint.overrideprintjob=no print.collate=yes hidegrayline=no )

After the PowerBuilder release number comes the DataWindow properties, such as the color and print information. Note the new DataWindow features for 9.0, such as hidegrayline, in the above source code. Another DataWindow property that's worth pointing out is the processing property. This specifies the DataWindow's Presentation Style:
0 - (Default) Form, Group, Query, Tabular, N-UP, Label
1 - Grid
2 - Label
3 - Graph
4 - Crosstab
5 - Composite
7 - RichText

The next time you want to change the style of a DataWindow, there's no need to re-create the entire DataWindow. Just change the processing property in the source code. Also at runtime, using dot notation or Describe, the "processing" attribute can be used to determine the DataWindow style. You're not allowed to change the DataWindow Presentation Style at runtime.
Band Properties
header(height=256 color="536870912" )summary(height=92 color="536870912" )footer(height=0 color="536870912" )detail(height=68 color="536870912"height.autosize=yes)

The Band Properties section consists of one statement for each band in the DataWindow. It describes the properties of each band; for example, its height, color, and any expressions it may have. Actually this section is not mandatory, as PowerBuilder will create these four bands even if you don't specify that it do so. If the band properties are not supplied, they'll be created with a height of zero. If your DataWindow contains groups, they won't be specified here. Group "bands" in PowerBuilder are specified elsewhere in the source code.
Source Definition:

Listing 1

table(column=(type=char(20) updatewhereclause=yes name=emp_lname dbname="employee.emp_lname" )column=(type=char(20) updatewhereclause=yes name=emp_fname dbname="employee.emp_fname")column=(type=decimal(3) updatewhereclause=yes name=salary dbname="employee.salary")retrieve="PBSELECT( VERSION(400) TABLE(NAME=~"employee~" )COLUMN(NAME=~"employee.emp_lname~") COLUMN(NAME=~"employee.emp_fname~") COLUMN(NAME=~"employee.salary~")WHERE( EXP1 =~"~~~"employee~~~".~~~"state~~~"~" OP =~"=~" EXP2 =~"'TX'~" ) )ORDER(NAME=~"employee.emp_lname~" ASC=yes ) ")

The source code in Listing 1 has been cosmetically aligned for readability purposes. It's divided into two sections. The first section describes the result set, specifically:

- Data types
- Update characteristics
- Database column names
- Default values

The type property defines the PowerBuilder data type for the column. This property can be changed whenever PowerBuilder fails to correctly determine the data type of a database column. This often happens when PowerBuilder is working with less common data types and time stamps.
The second portion of Listing 1 specifies the SQL source, including any PowerBuilder-defined retrieval argument. This section also describes the SQL that will generate the result set.
The SQL source is actually stored internally in a generic PowerBuilder dialect called PBSELECT. This is how the SQL gets generated when the SQL statement is "Painted". If you choose the "Convert to Syntax" option and type in the SQL statement, PowerBuilder stores the statement and standard SQL:

retrieve=" SELECT ~"employee~".~"emp_lname~",~"employee~".~"emp_fname~",~"employee~".~"salary~"FROM ~"employee~"WHERE ~"employee~".~"state~" = 'TX'ORDER BY ~"employee~".~"emp_lname~" ASC" )

Object Definitions

This section contains all the other objects in the DataWindow. It contains important information as to which band each object belongs to. Objects such as columns, text objects, computed fields, and drawing objects are found here.

Listing 2

text(band=header alignment="0" text="Emp Fname" border="0" color="33554432"x="654" y="172" height="56" width="576" html.valueishtml="0"name=emp_fname_t visible="1" font.face="Arial" font.height="-8" font.weight="700"font.family="2" font.pitch="2" font.charset="0" background.mode="1" background.color="536870912" )column(band=detail id=1 alignment="0" tabsequence=32766 border="0" color="33554432"x="73" y="4" height="60" width="576" format="[general]" html.valueishtml="0"name=emp_lname visible="1" edit.limit=20 edit.case=any edit.autoselect=yesedit.autohscroll=yes edit.imemode=0 font.face="Arial" font.height="-8" font.weight="400"font.family="2" font.pitch="2" font.charset="0" background.mode="1" background.color="536870912" )line(band=header x1="73" y1="248" x2="1559" y2="248" name=l_1 visible="1" pen.style="0"pen.width="5" pen.color="33554432" background.mode="2" background.color="1073741824" )

Listing 2 provides the object definitions for a column, text, and line object.
Notice that the code in the listing is literally a help file to see which properties belong to which objects.

DataWindow HTML/XML Properties

This final section contains all the HTML/XML properties that are associated with the DataWindow. Many of these are new to PowerBuilder 9.0.


htmltable(border="1" )htmlgen(clientevents="1" clientvalidation="1" clientcomputedfields="1"clientformatting="0" clientscriptable="0"generatejavascript="1" encodeselflinkargs="1" netscapelayers="0" )export.xml(headgroups="1" includewhitespace="0"metadatatype=0 savemetadata=0 )import.xml()export.pdf(method=0 distill.custompostscript="0" xslfop.print="0" )

Creating Dynamic Objects

Getting the Syntax : Now that you've seen a naked DataWindow, it should be easy to create and destroy DataWindow objects. Why? Because you've already seen the syntax. For example, let's say that when you print a DataWindow, you want to add a computed field containing the page number. To do this, create the computed file on the DataWindow, then open it up in the source editor. The source code for our new computed field looks like

compute(band=header alignment="1" expression="'Page ' + page() + ' of ' + pageCount()"border="0"color="33554432" x="1157" y="24" height="88" width="466" format="[general]"html.valueishtml="0" name=page_1 visible="1" font.face="Arial" font.height="-10"font.weight="400" font.family="2" font.pitch="2" font.charset="0" background.mode="2" background.color="1073741824" )

The DataWindow did us a favor and built the syntax that we're now going to use to build this object dynamically. At this point, copy and paste the source code to a safe place, then delete the object in the DataWindow painter.
Adding the CodeA logical place to put the code is the DataWindow PrintStart Event. In PrintStart we can place the code to create our computed column. When the DataWindow is finished printing, we can destroy the object in PrintEnd.

Listing 4:

dw_1.Modify("create compute(band=header alignment=~"1~" expression=~"'Page ' + page() + ' of '+ pageCount()~"border=~"0~" color=~"33554432~" x=~"1157~" y=~"24~" height=~"88~"width=~"466~" format=~"[general]~" html.valueishtml=~"0~" name=page_1 visible=~"1~"font.face=~"Arial~" font.height=~"-10~" font.weight=~"400~" font.family=~"2~" font.pitch=~"2~"font.charset=~"0~" background.mode=~"2~" background.color=~"1073741824~" )")

Listing 5:

dw_1.Modify("destroy compute(band=header alignment=~"1~" expression=~"'Page ' + page() + ' of' + pageCount()~"border=~"0~" color=~"33554432~" x=~"1157~" y=~"24~" height=~"88~"width=~"466~" format=~"[general]~" html.valueishtml=~"0~" name=page_1 visible=~"1~"font.face=~"Arial~" font.height=~"-10~" font.weight=~"400~" font.family=~"2~" font.pitch=~"2~"font.charset=~"0~" background.mode=~"2~" background.color=~"1073741824~" )")

Listing 4 provides code for the PrintStart Event, and Listing 5 provides code for the PrintEnd Event.
By using the create function within Modify, when printed the DataWindow will contain a computed column containing the page number. The destroy function cleans it up. As you can see, what looked like a very cumbersome create syntax basically becomes a copy-and-paste job. The secret is to create the object in the DataWindow painter, copy the source code it generated, delete the object on the DataWindow, and paste the code into a Modify statement in the event of your choice.
Summary :

Creating a dynamic DataWindow looks like a daunting task. But as you can see, if you know what a naked DataWindow looks like, the job becomes much easier.

PBBrowse 2.14 for PowerBuilder Developer

PowerBuilder provides an object browser - the utility accessed via the "Cubes with a pair of eyeglasses" icon on the PowerBar menu. If you haven't tried the PB browser, you should. Among other things, it eliminates the problem of identifying which PBL contains the object of interest. Click a tab to select an object type and you're immediately presented with a list of the current application's objects of that type. Right-click an object, select Edit from the popup menu and PB opens the object in the relevant painter. It's a fast way to navigate your PB application. The PB browser has other essential functionalities: for example, the OLE tab presents the list of OLE objects known to your Windows OS. Check it out.
PBBrowse 2.14 is the latest version of Ken Howe's alternative to the native PB browser. Ken is the proprietor of PBDR.COM, the PowerBuilder Developer's Resource. I've long been a fan of Ken's PBDelta differencing tool, so I came to PBBrowse optimistically. It took a little while for me to understand the value of PBBrowse, since it seemed redundant with the native browser that works so well. With time, however, I've come to appreciate that PBBrowse is a superb way to do what its name implies - browse a PB application. It's not really redundant - it's in addition to.
Features :

Figure 1 shows the main PBBrowse toolbar. Taking the icons from left to right provides a good overview of the utility's main features

Browse List of Applications :

The principle PBBrowse idiom is, "Present the user with a list of objects in the application, then let the user easily explore the code therein." After a little setup in which you tell the utility where your PB.INI file lives, you get the list of applications in your PB.INI file by clicking this icon. Double-click an application, PBBrowse processes for 5-10 seconds, then presents you with all the objects in the application. That is, it (apparently) uses the information in the PB.INI file to identify the PBLs associated with the application you selected, and generates the list of objects that appears in those PBLs. The objects are sorted by name within the object type: for example the applications are first, followed by DataWindow objects, then global functions. The object order and the icons used are just like the PB Library Painter, so you're immediately oriented.

Double-click any object and the browser window opens (see Figure 2). A nicety: PBBrowse doesn't present a tab if there are no corresponding items. For example, the Local External tab appears only if the object contains external function declarations. Each of the tabs with the split window works as you'd guess: click the item on the top and it's displayed below. This is a significant win in my opinion - it's much easier to navigate among the various scripts of a window (for example) than to manipulate PB's "Declare -->> Window functions" dialog or open the Script Painter for a given control and navigate the dropdown of events. Since I've gotten this far I was thinking, "This is clearly the easiest way to browse an object when looking at it for the first time, trying to understand the lay of the land." The PB browser offers nothing similar: it dumps you into the relevant Painter and you have to navigate the object the (relatively difficult) PB way. That's the primary function of PBBrowse: to get to an object quickly and browse its scripts and attributes. Return to the main toolbar to understand its other features.

Browse Current Application:

This is an alternative to selecting an application from the entire set of applications in the PB.INI file. PBBrowse will use the PB.INI file to identify the current application and immediately load the objects in the corresponding PBLs into the application list.

Browse a Single Library:

This is yet another alternative: instead of populating the application list with all the PBLs in an application, you can just look at a single PBL object.

Find Objects :

This opens a versatile search-for-strings utility, a much more powerful alternative to the Library Painter's search facility.

Among other things, you can:

- Search all or just one of the PB.INI's applications

- Search all or just one of an application's PBLs

- Search all or one specific object type (e.g., global functions)

- Search for one or multiple strings doing an AND or OR search as desired

- Specify case sensitivity

- Specify the string-to-search-for using regular expressions (metacharacters, match())

- Specify date ranges



The output of the find operation is a list of objects where the strings were found with each "hit" line displayed with the object (see Figure 3). You can print it. As with the Library Painter search, you can double-click a "hit" to open the corresponding object. I don't know of any other way to accomplish this advanced search functionality. Imagine trying to do it with the Library Painter: you'd typically have to do one PBL at a time, you could search only for a single string, and you'd have none of the other options of the PBBrowse find. I've put this to work to identify the changes I've made since the last time I consolidated my work into the team's shared PBLs. I searched for "Hoyt 3/10/2000" and "Hoyt 3/11/2000" (with an OR) and it identified each of the objects so marked.

Browse Enumerated Types:

This is redundant with the PB browser. It lists all of the enumerated types and when you click one, it lists the corresponding values. You can select a value (e.g., cascaded) and enter control-c (or click an icon) to copy it into the clipboard, in order to paste it into your application. The exclamation point isn't copied into the clipboard so you'll have to add that yourself.

Preferences:

PBBrowse has a really nice preferences dialog. It lets you tell (once) where your PB.INI file resides. There's control over what maximizes when. You get to pick between a faster straight text presentation of scripts or a nicely color-coded RTF version that's only slightly slower. There's even a checkbox that turns on a prompt to warn you, when you start a find operation on "All applications," that it's going to take a long time. The preferences dialog reflects the fact that Ken actually uses this tool and has built in options to make it friendly.

Produce Statistics for Highlighted Application:

This icon appears only when you're looking at the application list. Select an application, click this icon, go to lunch (it takes only about five minutes, actually) and you'll get something similar to Figure 4.


These statistics are also displayed as bar charts. Run this when your boss asks you how long it will take to familiarize yourself with the code. "Hmmm, at five lines per minute..." The other main toolbar items are mostly variants on what you've seen already. A couple object-browser-specific items bear mentioning, however.

PowerScript in PB Dev Env:

This is very slick. Select some text in an object browser script, click this icon and PBBrowse will paste the text into whichever PB window is uppermost, at that window's current insertion point. If you're the kind of developer who cuts and pastes a lot, this is a big win. Use the find function to find the right script, double-click the find result to open the object, select the text and then click the icon.

Paste Function/Event Call in PB Dev Env:

This is similar. If you're in a function or event script, clicking this icon will paste the prototype into the topmost PB window.

The result looks like:
boolean = f_dw_scroll_to_row_column(datawindow the_dw, long the_row, string the_column)

You know what the arguments are and what the function returns. Can it get any easier?

Help for Highlighted Command:

If you double-click a PB function name in the object browser and then click this icon, PBBrowse will look up the highlighted function in PB help. For those of us who haven't yet memorized every PB factoid, this is another assist to quickly understanding extant code.

Browse for Highlighted Object Name:

This is kindred except it works on objects and functions created by the user.
If I highlight f_dw_scroll_to_row_column() in a script and click this icon, PBBrowse will work for a few seconds and then open that function in a new object browser. Wonder what a function does? Double-click it and click the icon. PBBrowse searches based on the object name, so it's not going to succeed with object functions (it apparently scans the object list for the name; searching each object for its functions would take until next Tuesday).
If you instantiate "g_powersmith" as "g", it's not going to find "g" either. Fortunately, I use tons of global functions so this is great for my code.

Browse Ancestor Object : If you're in something that has an ancestor, clicking this icon will open that ancestor in another object browser window. If w_whatever inherits from w_base, for example, then you can open w_base from w_whatever by clicking the icon. That's a good roundup of the main features of PBBrowse.

More Features

The Hierarchical Object View :

The object list can be presented, as shown above, in a DataWindow with one line per object. That's the listview, and it's the faster route. The slower but more interesting alternative is the hierarchical view. As you'd guess, clicking the plus sign expands the treeview to show you all of the function objects. This view lets you ask questions such as, "What windows descend from w_base?" You can do the same thing with the PB browser, by right-clicking the object and selecting "Show Hierarchy" from the popup menu. The PBBrowse presentation is very nice, nonetheless.

PBBrTray System Tray Utility:

We've seen that PBBrowse integrates with PowerBuilder through its ability to paste code and function/event prototypes into the topmost PB window. The PBBrTray.exe utility integrates in the other direction. When running PBBrTray appears as the PBBrowse icon in the system tray, in the lower right-hand corner of your Windows desktop. If you right-click the icon, then a menu appears showing the objects currently open in PB. Selecting one of the objects from the list at the top of the menu will open that object in PBBrowse. How nice, I'm confused by this thing I'm looking at in PB so I'll right-click this icon and be able to view it in PBBrowse a few moments later. Interestingly, global functions aren't added to the menu, presumably because if you're looking at a global function, PBBrowse doesn't have anything else to tell you about it. Clever. Object History :

As you open objects to browse them, PBBrowse adds them to an Object History window that's always on top. Double-clicking on any object will reopen that object in a browser window. Again, Ken uses this utility and he's made it friendly.

Cons :

Naturally there are some cons. I found PBBrowse's Help less than illuminating. It has no images or browse sequence. I finally resorted to getting all the Help text by sequentially selecting each topic from the Help dialog's index tab. (This article will get you most of the way.)
The product isn't entirely robust: I can crash it with certain objects, and the object parser had trouble finding certain function scripts in my NVO where the bad() function is overloaded more than twenty times. Ninety-nine percent of the time, however, it works as advertised. To my mind, a major drawback to PBBrowse is the inability to modify code. There you are: you've fired up PBBrowse and found what you're looking for, and now you want to fix it. PBBrowse is read-only, however, so you have to renavigate to the same place in PowerBuilder and do the fix the usual way. That makes sense, of course: it's a browser, not an IDE. Still, I wish Ken could surmount the storied hassle of ORCA and make PBBrowse a read-write tool.

Summary :

I like PBBrowse. It has typical PBDR.COM polish, niceties like draggable separator bars, that great preferences window and features that are genuinely useful for the typical developer. The two-way integration is terrific. As a peripatetic contract programmer, routinely arriving at a new client site and being presented with a pile of unfamiliar code, PBBrowse is a no-brainer for me. I'll use it whenever I make yet another foray into unfamiliar territory. It'll also be key when I'm trying to do a search more sophisticated than, "Is this string in this PBL?" I think it'll take a while before I fully appreciate the power of the regular expressions offered by PBBrowse's find facility. It's great to simply have another way to look at the application. I'm in a window function (in one-function-at-a-time PB6) and can't remember the name of another window function. I right-click the icon in the tray, select the window from the popup menu, and I'm looking at the window in the object browser, able to find the function in question with only a few clicks.

Custom Search