SWT Step 2. Basics of GUI, part 1

Objective

Implement the basic SWT class hierarchy: from o.e.swt.widgets.Widget to o.e.swt.widgets.Shell to be able to create windows in the context of SWT. This will allow to do many other things just as they should be done. The test example should create and show two SWT windows: the top shell (filled with the default SWT background color, without any content) and the secondary shell (also empty) which is always above the top shell. Also it should handle the event queue in minimal, i.e. react to the close action, clean up correctly and exit.

Task notes

Device contexts

In fact, that there are two different layers of output device abstraction in OS/2: device contexts and presentation spaces, as opposed to Windows where the device context plays the role of both -- and handle to it is just allocated and returned by Drawable.internal_new_GC() method which is overriden for different types of devices. In OS/2 we use the presentation space handle as a return value of the internal_new_GC(). The handle to the device context is stored in the GCData.hdc field if non-null GCData object is passed to the internal_new_GC().

For windows, the device context is not really opened by the internal_new_GC(), the handle to it is just obtained via OS.GpiQueryDevice() call with the presentation space handle of the window passed as an argument. Correspondingly, it is not closed by the internal_dispose_GC().

For other devices, that require the real creation of the device context (which, together with the creation of the presentation space and associating the latter with the former, is made inside the internal_new_GC()), the GCData object passed in should not be null, because the handle of the device context will be necessary to correctly close this context by the internal_dispose_GC().

Win32 API to OS/2 PM API mappings relative to device contexts

Below is the approximate correspondence between Windows device context functions and OS/2 ones:

Windows GUI API OS/2 PM API Remarks
hDC = BeginPaint(hwnd...) hps = WinBeginPaint(hwnd, NULL...) [Cached PS] To draw during WM_PAINT message
EndPaint(hwnd...) WinEndPaint(hps)
hDC = GetDC(hwnd) or hDC = GetWindowDC(hwnd) hps = WinGetPS(hwnd), hps = WinGetScreenPS(hwndDesktop) [Cached PS] To draw during any message
ReleaseDC(hwnd, hDC) WinReleasePS(hps)
hDC = CreateDC(...) hdc = WinOpenWindowDC(hwnd) or DevOpenDC(...), hps = GpiCreatePS(hab, hdc...) [Micro/Normal PS] To dtaw on any device (usually display or printer)
DeleteDC(hDC) GpiDestroyPS(hps), DevCloseDC(hdc)
CreateCompatibleDC(hDC) hdcCompat = DevOpenDC(...hDC) DC compatible with given

Screen coordinates

The coordinate space in OS/2 is flipped horizontally when compared to its traditional orientation in the context of windows. That is, the origin of the coordinate space is located in the lower left corner but not in the upper left, as it considered to be in SWT or in Windows, in particular. This should be always kept in mind. The formula to recalculate the y coordinate of the rectangle from one system to another is very simple: y = parentHeight - (y + height), where parentHeight is the height of the parent's space, coordinates of the rectangle are relative to.

[Note: the following information is partially outdated, see here] The package method Control.getHeight() can be used by classes within the o.e.swt.widgets package to easily obtain the height of the control itself and/or the height of its parent using parent.getHeight() call (note, that if the parent is null or when the control is a Shell instance, the Display.getHeight() must be used to obtain the parent's height). Also, the Control.getBounds(SWP) package method returns the height of the control's parent (respecting the situation when the parent is null or the control is a Shell) as the side effect of its invocation, which is used, for example by the setBounds() method. But for simple calculations the getHeight() method is more preferable since it does less calculations.

The above applies to all Win* API calls relative to sizing/positioning (such as WinQueryWindowRect(), WinSetWindowPos() and so on). Hopefully, for Gpi* calls we can setup the automatic horizontal flipping of the coordinate space to be done by changing the default view transformation matrix of the window's presentation space, so there's no need to do any coordinate conversion when using Gpi* functions (see also task notes in the SWT Step 003).

Windows

In OS/2 every window has two types of relationship: parent-child and ownership. In both SWT and Windows there is only the parentship. Although the code for OS/2 related to these things will be a bit more complicated, the situation is very simple: in most cases (window controls) the parent and the owner is be the same. For shells the parent is the Desktop and the owner is null (for top shells) or another shell (for secondary ones). Also for some other windows (such as popup menus) the Desktop willbe the parent. In SWT the term (and the field) parent references to the real parent (the window which confines a child visually) except (not embedded) shells, for which the parent is their owner (the window which receives messages from them).

In OS/2 the frame window is the composite window of the special class containing the decorative elements such as a titlebar, min/max buttons, scroll bars (which are all windows themselves) as its children and ownees, as opposed to Windows where this decoration is defined just by window style flags in a call to the window creation function. So, the work with frame windows (starting with the Decorations class in the widget hierarchy) in OS/2 is slightly different -- in particular, the Decorations.createHandle() (see below) does some additional calls to create various frame controls (decorative elements) for the frame window being created.

Dialog windows in OS/2 are just extensions of frame windows, they subclass them and do some additional work (for example, handle the focus traversal over dialog controls and maintain the result code of the dialog window). Possibly, in SWT these windows will not be used at all and their behavior will be just emulated on the basis of the ordinary Shell widget.

SWT window creation

The common point of the native window creation is the Control.createHandle() method, which is used almost by all subclasses. The logic of its work is as follows:

Step checklist

Operation Status Remarks
Partially implement the Widget class Done [dmik] Only the character translation is not implemented.
Partially Implement the GC class Delayed Almost completely commented. The major work will be done on the step 3.
Add EventTable, Listener, Event, TypedListener to compilation Done [dmik]
Implement Display.internal_new_GC() and Display.internal_dispose_GC() Done [dmik] See Task notes above.
Add o.e.swt.events.*Listener, *Event and *Adapter classes to compilation Done [dmik]
Add o.e.swt.accessibility.* (common and emulated) to compilation Done [dmik]
Add message time obtaining to Widget.sendEvent() and Widget.postEvent() Done [dmik]
Implement OS.WinSendMsg, OS.WinPostMsg(), OS.WinQueryMsgTime() Done [dmik]
Add standard WM_* constants Done [dmik] Most of them are commented and will be uncommented on demand
Add standard WC_* constants Done [dmik] Most of them are commented and will be uncommented on demand. Since they actually are integer values (integer atoms) but natives that use them require PSZ as a class name, a special static method getAtom() is added to PSZ class that returns a string representation of an atom.
Implement the WidgetTable class, OS.Win[Set|Query]Window[ULong|UShort]() and add QW[L|S]_* constants Done [dmik]
Implement the OS.WinQueryClassInfo() and add CLASSINFO class Done [dmik]
Implement OS.WinQueryWindowRect(), OS.WinMapWindowPoints(), RECTL class Done [dmik]
Implement OS.WinIsWindowEnabled(), WinIsWindowVisible(), WinQueryFocus(), WinQueryWindow(), WinShowWindow() Done [dmik]
Implement OS.WinGetPS(), WinReleasePS(), WinBeginPaint(), WinEndPaint(), WinInvalidateRect(), WinUpdateWindow() Done [dmik]
Implement the OS.WinQuerySysValue() and SV_* constants Done [dmik]
Add the static Display.height field Done [dmik] This field is used to flip the window coordinate space vertically
Add o.e.swt.internal.pm.MRESULT class Done [dmik] The reason it was added is that, as opposed to the simple int type, null can be returned by individual message handlers indicating that the message hasn't been processed and therefore should be passed to the default window procedure.
Implement the helper function OS.WinCallWindowProc() Done [dmik] To get the ability to call the window procedure given as the int value (actually the C pointer to a function)
implement the OS.WinSetWindowPos() and SWP_* constants Done [dmik]
implement the OS.WinCreateFrameControls() and FCF_*, FS_*, FF_* constants Done [dmik]
Implement the OS.WinGetScreenPC() and OS.GpiQueryDevice() Done [dmik]
Implement OS.WinQueryWindowTextLength(), WinQueryWindowText(), WinSetWindowText(), add a new constructor to the PSZ class taking an integer as the length of an empty string to create Done [dmik] Used to get/set shell titles
Implement the OS.WinFillRect() and OS.objcpy() for the RECTL structure Done [dmik] Needed to handle WM_ERASEBACKGROUND message.
Partially implement the following classes to reach the step objective: o.e.swt.widgets.Control, Scrollable, Composite, Canvas, Decorations, Shell and some others (WidgetTable etc) they depend on Done [dmik] The majority of work has been done in the Control, Decorations and Shell classes, others are almost fully commented.
Add the SWT002 testcase Done [dmik] Note: WinDestroyWindow() is not called for secondary shells explicitly. It's a planned behavior of SWT (see the description of the Widget.destroyWidget()). It is suitable for OS/2 when the window's owner is a WC_FRAME window. See remarks inside the Control.createHandle().