Using the Editors


Chapter 17 . Framesets and Splitters

A splitter divides a window into a number of subareas, each of which is populated separately. Each subarea is called a "pane". The movable dividing line between two panes is called a "mullion".

In Panther Windows and Motif applications, support for splitters is implemented by means of a special kind of screen called a frameset. The number and configuration of the panes into which each frameset is divided is determined in the Panther editor by placing special widgets called splitters on the frameset. Each pane is itself a Panther widget that has properties associated with it. The most important property of a pane is the name of a screen that will be rendered inside the pane at runtime. The screens associated with a frameset's panes are not displayed along with the splitters in the Panther editor. They must be created and edited separately.


Creating And Editing Framesets

In the Panther editor a new frameset is created by choosing FileNewFrameset on the editor's main menu. This will bring up a blank frameset. Such a frameset is much like an ordinary Panther screen, in that it supports a similar set of properties, is stored in libraries, and is generally worked with in much the same way. But a frameset can hold only two kinds of widgets: splitters and panes.

Figure 17-1 Book_fs from the frameset sample application

Splitter Widgets

A frameset is divided into multiple panes by placing splitter widgets on it. There are three kinds of splitter widgets – vertical splitters, horizontal splitters and two-way splitters.

Vertical splitter
Divides the frameset into two or columns, and is represented in the frameset screen in the editor by one or more vertical mullions.

Horizontal splitter
Divides the frameset into two or more rows, and will be represented in the editor by one or more horizontal mullions.

Two-way splitter
Divides the frameset into four panes, and will be represented in the editor by one vertical and one horizontal mullion.

A splitter can be configured to divide the area it was placed in into further rows or columns by changing the splitter's Number of Rows or Number of Columns property.

Placing a splitter in an empty frameset divides the whole frameset into panes. Subsequent splitter placements in that frameset will necessarily have to be placed within previously defined panes. Such splitter placements will divide the panes the splitter was placed in into smaller panes. That is to say, splitters can be nested.

Splitters are selected in the editor by clicking on one of the mullions that represents the splitter. A splitter can also be selected from the widget list.

When a splitter widget is selected in the editor, the properties window is updated to display the properties settable for splitter widgets.

The following are the properties for splitter widgets:

Figure 17-2 Properties Window for Splitter Widgets

The Rows and Columns properties determine how many panes each splitter defines. These can have integer values from 2 to 16. Changing this property will create or destroy panes associated with the splitter. Newly created panes will be added to the right or below the existing panes. Likewise, panes destroyed by changes to this property will be removed from the right hand side and from the bottom of the screen.

The height and width properties, for rows and columns respectively, will be used at runtime to render the frameset. These properties can be set in the editor by dragging one of the mullions associated with a splitter, or it can be entered in the properties window. The size properties are array-valued, with one occurrence for each row or column. Note, however, that the height of the bottommost row, and the width of the rightmost column, cannot be changed. These are computed by the sizes of the other rows or columns, and the height of the container.

Like the other size-related properties in Panther, the height and width properties for splitter rows and columns can be specified using a variety of units. The default is grid units. In many cases pixels will be the best unit to use.

The Web Options and HTML Options are relevant to web deployment of framesets. Refer to "Web Deployment of Framesets."

Pane Widgets

Unlike other widgets, pane widgets are not explicitly created by Panther users. Pane widgets are implicitly created when splitter widgets are placed on a Screen.

When a vertical or horizontal splitter is placed on an empty frameset, two panes are created. When another splitter is placed in a previously defined pane, new panes are added. Previously defined panes are not destroyed. Panes are added to the top and left, shifting existing panes to the right and down.

A pane widget is selected in the editor by clicking on a frameset screen that contains at least one splitter widget. You can also use the widget list to select a pane.

Note that if a frameset contains at least one splitter, it might not be clear how to select the frameset itself. Clicking on one of the mullions between two panes will select a splitter. Clicking on the background of the frameset will select one of the panes. The frameset will be selected when it has focus, and none of the widgets on the frameset screen is selected. A quick way to do that is to click on a pane, and then shift-click on the same pane again. The first click selects the pane, and deselects everything else. The shift-click deselects the pane, leaving the frameset itself selected. You can also select the frameset using the widget list.

Pane widgets display the following properties in the properties window when selected:

Figure 17-3 Properties Window for Pane Widgets

The Form Name property is used to designate which screen to render inside the pane at runtime. This can be the name of any Panther screen, or the name of another frameset. It is possible to nest framesets within one another.

The Web Options and HTML Options are relevant to web deployment of framesets. Refer to "Web Deployment of Framesets."

Frameset Properties

A frameset supports a set of properties much like that of an ordinary screen, and these properties are used in the same way as the corresponding properties of screens. The properties under the Web Options heading and the HTML Options and Browser Options headings are relevant to web deployment of framesets.

Many of the properties of screens are not relevant to framesets. The corresponding properties of the screens displayed in the frameset's panes would be the ones relevant at runtime.

The Wallpaper Pixmap and Color properties deserves special mention. The specified wallpaper or color will only be seen in an empty pane, that is, a pane that has no screen associated with it. A pane that has a screen associated with it will display the background color or wallpaper of that screen.

After the frameset contains splitters, you can view the frameset's properties by selecting the frameset from the Widget List or by deselecting the current pane/splitter using Ctrl+Click.


Programming With Framesets

At runtime, the frameset and the screens displayed in its panes all act as sibling windows. Focus can move from window to window within a frameset, and whatever other windows might be open, as among sibling windows in a Panther application that doesn't use framesets. The frameset itself cannot normally get focus after it has been opened.

Opening a Frameset

A frameset is opened like any other screen. When a frameset is opened, it goes through the usual set of screen entry operations (unnamed JPL, screen entry function, etc.) Note, however, that all of these operations take place before the screens designated to be opened in the frameset's panes have been opened. This means that processing associated with the frameset's entry procedures cannot reference fields on the screens that will be opened in the frameset's panes.

After the frameset's entry procedures, the screens designated to be opened in the frameset's panes will be opened. They will be opened from bottom to top, and from right to left. So the pane in the lowest and rightmost position will be populated first, and the pane in the topmost and leftmost position will be populated last. Each screen opened in a pane will itself go through the normal screen entry events. The screens are opened in the reverse or Panther's usual left-to-right, top-to-bottom order so that the screen in the upper left corner will be the last screen opened and will be the one to retain focus. In this manner none of the screens placed in the panes undergo two entry events while opening the frameset.

Once all of the frameset's panes have been populated by their designated screens, the screen in the topmost and leftmost pane will gain focus. Focus will go to the first available field in that screen. Only at this point will the keyboard stack be opened. Any logical keys ungotten by any of the screen entry procedures, including that of the frameset itself will be read by the first screen to get focus.

Cursor Movement and Window Management using Framesets

If a frameset is the only window open, cursor movement will behave much as though the screens in each of the panes were in separate sibling windows. Tabbing order in each pane will be governed by the usual rules. If you want a TAB from a field in one pane to lead to a field in another pane, you will have to write a routine that calls sm_wselect, and then perhaps sm_gofield to put focus where you want it.

When focus leaves one pane, the screen in that pane undergoes a screen exit event, with the K_HIDE flag set. Likewise when focus enters a pane, the screen in that pane undergoes a screen entry event with the K_EXPOSE flag set.

If a frameset is not the only screen open, things get more complicated. To understand how things work, it's best to think in terms of the window stack, and to remember that the frameset itself is a window on the stack, as are the screens in the frameset's panes.

When a frameset is first opened it is the active window while it opens the screens that are to populate its panes. Imagining a simple scenario of opening a frameset with three panes, the sequence of entry and exit events would be as follows:

at this point the window stack would look like:

pane1  (active)
pane2
pane3
frameset

After its initial entry the frameset itself will not, under normal circumstances, get focus, or undergo entry or exit events. Thus the frameset will tend to linger at the bottom of the window stack.

If the user clicks a button on the first pane that opens another window, a sibling to the frameset itself, the following entry and exit events would occur:

at which point the window stack would then look like this:

otherwindow  (active)
pane1
pane2
pane3
frameset

If a user now clicks on pane3 in the frameset, the following entry and exit events would occur:

and the window stack would end up looking like:

pane3  (active)
otherwindow
pane1
pane2
frameset

Other much more complicated scenarios could be envisioned. Things would get very complicated in the case where framesets are nested. In general, most people will probably not want to do anything on screen hide events (K_EXIT | K_EXPOSE) for screens that are in framesets.

Closing Framesets

A screen in Panther is typically closed in one of three ways:

A frameset is closed in the same manner.

Clicking on the 'x' control on the title bar will cause the logical key EXIT to be issued, and so the first and third of these methods will be same.

When a screen gets the logical key EXIT from the key stack, it calls sm_jclose. So really all three of these methods are fundamentally the same.

The function sm_jclose is one of those that implicitly operates on the screen that currently has focus. If the screen that currently has focus is in a frameset, then a call to sm_jclose will close the whole frameset.

Closing a screen that is in a frameset will cause the whole frameset to be closed. The screens in the frameset will all be closed, in the order in which they are found in the window stack. This list of screens includes the screens in the panes of any other framesets that are nested in the frameset being closed. After all the screens in all of the panes of the framesets have been closed, the framesets themselves will be closed. The outermost frameset will always be the last to close.

As an example, consider the following scenario. The window stack looks like this:

pane1 (active)
pane2
otherwindow
pane3.1
pane3.2
pane3.3
pane3 (nested frameset)
frameset

(This means to indicate that pane3 in the frameset is itself a frameset, containing three panes. The panes in that nested frameset are the ones designated 3.1, 3.2, and 3.3.)

If a call to sm_jclose is now issued, the window stack is reordered to place all the panes contained in the frameset (including those in the panes of the nested frameset) on the top, followed by all the framesets.

pane1 (active)
pane2
pane3.1
pane3.2
pane3.3
pane3 (nested frameset)
frameset
otherwindow

And the screens will be closed in order, from the top down. Until the outermost frameset that the screen that had been active when sm_jclose was called is closed.

When closing the screens contained in the frameset, normal screen events would occur, just as though a series of sibling windows were being closed one after another. In this case, the event sequence would be:

Which would leave the window stack as:

otherwindow (active)

The reordering of the window stack prior to closing the screens is not accompanied by screen events, except in the unusual circumstance that sm_jclose is issued when a frameset has focus. Consider a scenario where the window stack looks like:

frameset
pane1
otherwindow
pane2
pane3

And sm_jclose is called, the window stack will be reordered to place all the screens in the frameset at the top, followed by all the framesets contained in the outermost frameset. In this case, the following events would occur:

The rest of the reordering, to place the frameset after all the screens in its panes, occurs without any screen events taking place, and will leave the window stack as:

pane1 (active)
pane2
pane3
frameset
otherwindow

And the screens will close, with CLOSE and EXPOSE events occurring as described in the previous scenario.

Note that this latter scenario is very unusual. Under normal circumstances a frameset will only gain focus when it is first opened, and will not be in focus when user-written code is being executed. The only way to force a frameset to gain and retain focus is by explicitly giving it focus with a call to sm_wselect or sm_n_wselect.


Runtime Properties

Screen and Frameset Properties

Screens and framesets have a property called PR_FRAMESET that returns a handle to the frameset the screen or frameset is inside, just as the PR_GRID property returns a handle to the grid a particular field is in. If a screen or frameset is not in a frameset PR_FRAMESET returns "".

Additionally, screens and forms have a PR_SPLITTER property that returns a handle to the splitter that defines the pane in which the form is contained. If the screen is not in a frameset, this property will return "".

Screens and forms also have a PR_PANE property that returns a handle to the pane that contains the form. If the screen is not in a frameset, this property will return "".

Splitter Properties

A splitter will return PV_SPLITTER when PR_WIDGET_TYPE is queried.

The PR_SPLITTER_ROWS and PR_SPLITTER_COLS properties of splitter widgets are read-only at runtime. Since these properties are read-only, you cannot change a frameset's pane configuration on the fly. To change the number of subwindows in a frameset, you will have to nest framesets.

PR_SPLITTER_ROWS returns the number of rows in the splitter. This will be an integer from 1 to 16, but it cannot be 1 if the number of columns is 1.

PR_SPLITTER_COLS returns the number of columns in the splitter. This will be an integer from 1 to 16, but it cannot be 1 if the number of rows is 1.

The PR_SPLITTER_ROW_HEIGHT property gets or sets the height of each row. This is a multi-occurrence property, indexed from 1 to PR_SPLITTER_ROWS. The height of the bottommost row cannot be set. It is computed based on the sum of the other row heights and the size of the container.

The PR_SPLITTER_COL_WIDTH property gets or sets the width of each column. This is a multi-occurrence property, indexed from 1 to PR_SPLITTER_COLS. The width of the rightmost column cannot be set. It is computed based on the sum of the other column widths and the size of the container.

The PR_MEMBER property is an indexed property that will return handles for the panes in the splitter. The pane in the first row, first column will have the index 1. The pane in the first row, second column will have index 2. The pane in the second row, first column will have index PR_SPLITTER_COLS +1. In general the formula to determine a pane's index is:

	index = (row - 1) + PR_SPLITTER_COLS + column

PR_FRAMESET will return a handle to the frameset that the splitter is contained in.

If a splitter is contained in a pane defined by another splitter, the property PR_SPLITTER will return a handle to that other splitter and the property PR_PANE will return a handle to the pane in which the splitter is contained.

Pane Properties

The PR_PANE_FORM property of pane widgets can be changed at runtime. Changing the screen associated with a pane at runtime should close the screen currently in that pane, and then open the newly specified screen in its place. Clearing the PR_PANE_FORM property of a pane will leave the pane empty. Since issuing sm_jclose will close the whole frameset, clearing the PR_PANE_FORM property of a pane is the only way to close a single screen in a frameset.

The PR_PANE_ROW property gets the row number of the pane.

The PR_PANE_COL property gets the column number of the pane.

The PR_PANE_INDEX property gets the index of this pane. The indices are the same as those used by the PR_MEMBER property of framesets. The JPL code

index = mypane->pane_index

is equivalent to:

index = (mypane->PR_PANE_ROW - 1) + \
(@widget(mypane->PR_SPLITTER)->PR_SPLITTER_COLS + \
mypane->PR_PANE_COL

The PR_MEMBER property of a pane will return an object id for the pane's contents. This will either be the id of a form (a screen or another frameset), or a splitter, or nothing.

The PR_FRAMESET property returns an object id for the frameset in which the pane is contained.

The PR_SPLITTER property returns an object id for the splitter that contains the pane.


Web Deployment of Framesets

If a frameset is brought up in a jserver, HTML to represent the frameset will be generated and sent to the browser.

The properties in the Web Properties sections of the Properties window control various options relevant to HTML generation of framesets.

Frameset Web Properties

Framesets support the NOFRAMES Markup property. The text entered here will be retured to the browser in the event that the frameset is accessed by a browser that does not support frames. The value of this property is indexed (like the JavaScript and VBScript properties). If no value is provided for the NOFRAMES Markup property, the following text will be returned to browsers that do not support frames:

	This Page Requires Frames.

The NOFRAMES Markup property is accessible at runtime, its C constant name is PR_FRAME_NOFRAMES and its JPL mnemonic is frame_noframes.

Splitter Web Properties

Splitter Widgets display the following properties under the Web Options heading in the Properties window:

		No Border
Spacing
Row 1 Height
Row 2 Height

Col 1 Width
Col 2 Width

The No Border property defaults to No. Hence by defaults borders will be shown. If No Border is set to Yes the HTML output is FRAMEBORDER="0". The No Border property is referred to at runtime as PR_FRAME_NOBORDER in C and as frame_noborder in JPL.

Spacing is the width of the mullions between panes. Setting Spacing = 0 has the same effect as setting No Border = Yes. The Spacing property is referred to at runtime as PR_FRAME_SPACING in C and as frame_spacing in JPL.

The height and width properties in the Web Options section can be used to override the similar properties in the Geometry section. The latter are used for GUI display, and are used for HTML generation as well, unless height and width properties are explicitly specified in the Web Options section.

There will be one height shown for each row, and one width for each column. If there is only one row, then no heights will be shown, and if there is only one column, then no widths will be shown.

The Web heights and widths can be either given as percentages or in pixels. They also accept the special value *. Omitted entries default to *, which tells the browser to choose whatever size it wants. Typically it will divide the available space equally among the regions specified with stars.

For example:

		Row 1 Height = 25%
Row 2 Height =
Row 3 Height = 25%
Row 4 Height = *

will be output as ROWS="25%,*,25%,*". The browser will allocate half the available space to the two specified rows, and divide the remaining space equally among the rows specified with stars. In this case, that would result in four rows of equal size.

Heights and widths should either be given in percentages or in pixels. Do not mix units.

Pane Web Propeerties

Like splitters, panes each have the No Border property. As noted above, it defaults to No. If set to Yes the 3-D border around the pane may be removed (or it may not be, this is browser-dependent behavior). Setting No Border to Yes for some panes and No for other panes can yield strange and unsightly results. It is recommended that you use the splitter-level No Border property if possible.

The Scrolling property supports three values: Auto, No and Yes. It defaults to Auto. If set to Auto, scroll bars will appear only when needed. If set to No, scroll bars will never appear and if set to Yes scroll bars will always appear. The Scrolling property is referred to at runtime as PR_FRAME_SCROLLING in C and as frame_scrolling in JPL.

The Margin Height and Margin Width properties control the space between the splitter lines and the HTML inside the pane. It defaults to 13 or 14 pixels. However, if you set one and not the other, the unspecified one will default to 0. These are referred to at runtime as PR_FRAME_MARGIN_HEIGHT and PR_FRAME_MARGIN_WIDTH in C and as frame_margin_height and frame_margin_width in JPL.


The Frameset Sample Application

In the directory $SMBASE\samples\frameset there are three files: a .lib file, a database file and a README file. These constitute the sample application provided to illustrate the use of framesets. This application uses ODBC to access its database. To run the frameset sample application you must have the ODBC database driver installed.

This application also provides examples of code to manipulate a few common ActiveX controls: the TreeView control, the ListView control and the ImageList control. The code for all the application's functionality is in the screens that are found in the library Biblio.lib. The JPL that manipulates the ActiveX controls has been commented and written in such a way that it can easily be re-used.

See the $SMBASE\samples\frameset\README file for more information, regarding installing and running the frameset sample.