ARWEN - Application Resource for Win32 Euphoria Nomenclature

ARWEN is a fast, compact GUI library for Windows-based applications. The control set is a modest size but the functionality is sound and ARWEN has special features not easily accessible elsewhere. This library may be of benefit to other Euphoria programmers and so I have released it into the Public Domain. If you would like to help improve ARWEN please send me an email.

Kind regards,
Mike
vulcan@win.co.nz


Disclaimer

Changes

Installation
Credits
Conventions
Event Handling
Creating Controls
Destroying Controls

Moving between Controls (using TAB)
Accelerator Keys

Windows
Buttons
MarkBoxes
Group boxes
Editable controls
Labels
Tool bars
Status bars
Scroll bars
Track bars
Progress bars
Menus
List boxes
Tab controls

HyperText control

Mouse Routines
Clipboard functions
Dib sections & Bitmaps
Icons & Cursors
Timers
Common Dialogs
Windows messages
Structures
Global Routines
To do list..
Bugs

DISCLAIMER
The software and documentation are provided "as is", without any express or implied warranty for any use whatsoever. No warranty is provided that any part of the software and/or documentation is error free. The Author will not in any event be liable for any direct, indirect, special, incidental, consequential or other imaginable damages related to any use, reproduction, modification, or distribution of the software and/or documentation. Use at own risk.


CHANGES
ver 0.94c ( 13/11/2007 )
1) Removed eu_file.e from bundle because of Orac.

ver 0.94b ( 23/10/2007 )
1) Converted the library to be compatible with Orac.

ver 0.94a ( 02/07/2007 )
1) Finally got loadDibFromClipboard() working.
2) Finally got drawDibText() to work with 8-bit DIBs - Woohoo!!
3) IDLE handler can be associated regardless if user encloses reference in braces or not.
4) Colour indexes for 8-bit DIBs are now 0-based.
5) Palettes are passed between app & lib as sequences of RGB integers, no longer as sequences of sequences.
6) Change byte-order for RGB values so now reflects memory order of BGR.
7) Various rotation & image manipulation routines have been added to the library.
8)
demo_lines.exw updated with more colour and new transformations.
9) Documentation on DibSections has been updated.

ver 0.94 ( 25/05/2007 )
1) DibSection library now accomodates 32-bit as well as 8-bit graphics - see demo_lines.exw
2) Added invalidateRect() to better manage screen updates.
3) Various other trivial changes made but I don't remember what they were.

ver 0.93c ( 29/11/2005 )
1) selectDirectory() renamed to browseForFolder() and latent memory leak obviated, thanks to Juergen Luethje.
2) Added reparentControl(), thanks to Pete Lomax.
3) Functions in misc_arwen.e corrected to use atom instead of integer for memory addresses, thanks to Pete Eberlein.

4) Calls to allocate_StringZ() now always return a 4-byte aligned address. Thanks to Pete Lomax.
5) Added getDiskFreeSpace() which retrieves info on the number of bytes available & used in from any path. NB: This function calls the newer API routine GetDiskFreeSpaceEx() so values above 2Gb are correctly returned.
6) Added isChildOf() and made getParentWindow() global.
7) Rare crashes caused by late assignment of variable MsgLoopStruct in WinMain() now fixed.
8) Corrected link to GetForegroundWindow() in dll.e - Thanks to Juergen Luethje.

ver 0.93b (07/04/2005)
1) Cosmetic change so position of TabItem labels reflects position of the TabItem frame.
2)
setTextColour() on TabItems no longer causes flicker.
3) Bug fix: TabItem controls no longer remain unpainted when calling
repaintWindow() using the main window id. Thanks to Pete Lomax for pointing this out.
3) WM_KEYDOWN & WMSYSKEYDOWN messages are now trappable by the user when the TAB key is pressed.
4) The edit fields of all Combo controls are now subclassed and Tabbing to or from the controls can occur.
5) Calls to
startIdle() & stopIdle() enhanced so they don't fail due to non-existent IDLE handler. Courtesy Pete Lomax.
6) More structure definitions and DLL links were added.

ver 0.93a (22/02/2005)
1) Bug fix where COMBO type controls were not properly selecting items. This had been an on-going problem in the past but should be fixed now.
2) Minor adjustment made to the way Tabbing onto/off a TabControl works - more intuitive.
3) Background processing no longer stalls when Scroll bars are moved (see
demo_idle.exw). Thanks to Pete Lomax.
4)
startIdle() now requires a parameter to specify a granularity in milliseconds.
5)
WinMain() and doEvents() now both call the same message loop to ensure consistent behaviour.
6) Added:
getChooseColor() to open the Color dialog box (see demo_idle.exw).
7) Added:
selectDirectory() to navigate for a folder/directory (see demo_idle.exw).

ver 0.93 20/02/2005
1) Accelerator keys are now emulated for "ALT + key" combinations. See
demo_controls.exw for an example.
2) Tabbing behaviour to/from Tab controls is slightly different as a consequence of the solution to a small anomaly.
3) Annoying bell sound made when tabbing through an EditText control has been fixed.
4)
lParam of WM_COMMAND messages now flags any accelerated events.
5) Can now
setTextColour() for TabItems.
6) Some experimental Owner-Drawn menu code has been added but it is incomplete and the default is standard menu rendering. The user may activate OD menus by setting the global integer ISMENUOWNERDRAWN to 1 prior to menu creation.

ver 0.92 5/2/2005
1) Reverted to Eu 2.4 coding style.
2) Added global function getHwnd() to get the native Windows handle of a control.
3) Added COLORDLG & TEXTMETRIC structures.
4) Fixed some bugs relating to previous version release.
5) Modified Tabbing logic so that child windows do not assert the parent window and TabItems flow more logically.
6) Bug fix: isPointIn().
7) Added: Clipboard functions for CF_TEXT (text).
8) Bug fix: Closing un-handlered child windows caused them to be destroyed instead.
9) setHyperLink() has been dropped and new openDocument() added. This makes for a more flexible system than was previously in place. However, user must manually assert the link. Please see demo_memo.exw for more details.
10) When a window is opened the focus will try to settle on the first available focussable control.
11) Added global constant ARWENVERSION, ie: {"name", major_version, minor_version}

ver 0.91 19/1/2005
1) Bug fix: createDirectory() and copyFile() routines had memory allocated with the fragile method which was in
danger of crashing the app due to unnecessary calls to free(). Now fixed thanks to Pete Lomax.
2) Bug fix (long-standing): When the background IDLE code was running and a dialog box was closed while entirely outside the app window area the idling code would stall. Now fixed thanks mainly to Pete Lomax.
3) Bug fix: insertItem() was sometimes putting list items in the wrong order when the position values were less than 3. Now fixed thanks to Pete Lomax
4) New routines added to manage background processing: startIdle() and stopIdle().
5) Bug Fix: WM_COMMAND messages trapped in SubProc() were not properly filtering COMBO type controls. Fixed thanks mainly to Pete Lomax.
6) Added: getKeyState() to check state of pressed keys.
7) Tabbing between controls is now operational.

ver 0.90a 22/11/2004
1) Scrolling bug in MultiEditText controls now fixed.
2) Suspended buggy Combo.edit control subclassing to restore basic functionality.

ver 0.90 26/07/2004
1) Fundamental change to Event system. Handlers must now be functions (except the IDLE handler).
2) varyMsgParams() is now obsolete and has been removed.
3) Documentation & Demos updated to reflect these changes.
4) Added
demo_memo.exw and demo_sysmenu.exw from Juergen Luethje.
5) getText() changed so that getText(id) will retrieve the data from Windows but getText(0-id) will merely retrieve the previous data in ARWEN's buffer. NB: this is opposite to previous implementation.

ver 0.88 08/06/2004
1) Added: moveWindow() and centerWindow() routines.
2) Fixed scroll problem with List Boxes.
3) Removed CRUSH utility, associated code and compiler documentation from library.
4) Documentation converted into Html form.
5) Various other small changes.

ver 0.86 27/05/2004
1) Added: purgeEvents() routine.
2) Greatly enhanced processing & error reporting capability in CRUSH.
3) Provided documentation about thoughts on Compiler development.

ver 0.85 16/03/2004
1) Compression utility incorporating doEvents() has been added. See crush.rtf for more details.
2) Slight enhancement for loadIcon() made. Thanks to Juergen.
3) Fixed: Vertical scrolling (via scroll bar) in MultiEditText controls now functions properly.
4) File operation routines such as createDirectory() and copyFile() have been added.

ver 0.83 04/02/2004
1) A custom HyperText control (which calls the new showExecute()) has been added. Thanks to Juergen.
2) The ProgressBar control has been added.
3) Some Mouse routines have been added including trackMouseEvent() (OS must be Win95b or higher).
4) loadIcon() & setIcon() have been added.
5) loadCursor() & getCursor() & setCursor()have been added.
6) A few more demos have been added to show the new features.

ver 0.80 16/01/2004
1) A 256-colour DIBsection library has partly been incorporated into ARWEN for high-performance graphics and 2 new demos have been added. Please consult dib256.rtf for documentation.
2) The Window class now sets the background colour to COLOR_BTNFACE instead of defaulting to the system defined one.
3) The routine destroy(integer id) has been added and the user can now destroy controls (incl Menus & MenuItems) at any time.
4) Bug fix: WM_*SCROLL messages for scroll controls (incl chevron buttons) within TabControls were not previously caught causing the Tabs to misbehave.
5) setTopIndex() has been renamed to setTopItem() to reduce confusion with setIndex().

ver 0.74 17/07/2003
1) TabControls are now Owner-drawn and definitely show disabled items as being such.
2)
a - StatusBar fields now have separate ID's.
b - The text in each Status field can also be centred or right justified - see documentation for details
c - setStatusWidths() can now refer to a single field
3) The text label of a control is now stored so that calls to getText() for controls (other than those that are editable) are now much faster. The library can still force a native text retrieval if the ID is negated, ie: text = getText( 0 - ID ).
4) Some latent bugs in the mini allocation library were fixed.
5) The order of the parameters in create() & insertMenu() has been modified & expanded to be more intuitive.
6) The linked win32 routine constant xGetObject has been renamed xGetObjectInfo. This better reflects the purpose of the routine.
7) Internal data structures have been prepared for the future implementation of ImageLists.
NB: While working on this version of ARWEN I made all Menus & MenuItems "Owner-drawn" but I then removed all that code for performance reasons.

ver 0.72 02/05/2003
1) Bug Fix: demo_lists.exw failed after changes made in version 0.70 but has now been fixed along with some other latent bugs.
2) Edit controls in Combo boxes are now subclassed.
3) Created controls are now associated with a stock font referred to as DEFAULT_GUI_FONT which gives the demo programs a more consistent look.
4) Some internal identifiers were renamed.
5) setScrollInfo() & getScrollInfo() have been updated & simplified to include TrackBars. These routines have also been documented.

CHANGES - ver 0.70 30/04/2003
1) Altered (simplified really) the way that the class default style values can be modified and then restored later. NB: This was implemented for v 0.65 but not documented.
2) Documented insertMenu() and removeMenu() functions and used these in demo_menus.exw to demonstrate their use.
2a) Bug Fix: insertMenu() now correctly inserts a Menu into the top-level menu.
3) Bug Fix: Combo boxes sitting within TabControls are drawn correctly - this was discovered when Evan noted a similar problem in Win32lib.
4) Relaxed programming requirements for style flags used for creation of controls. User now can simply send either atoms or sequences of style values. ARWEN will ensure they are combined into atoms before the controls are actually created.
5) User can now trap the WM_SIZING and WM_MOVING messages to impose limits on the size a Window may grow or dimish and also enforce the position of a Wiindow. See demo_resizing.exw for an example of this.
6) Background processing will now occur when resizing or moving is happening. However, the screen is not likely to be updated during the operation since Windows will not allow a WM_PAINT message to exit the message buffer until pending messages have cleared.
7) Added provision for managing (auto-resizing & repositioning) independent scroll bars as if they were built-in scroll bars. This is so ToolBars & StatusBars can sit in their natural positions - something built-in scroll bars do not properly allow for. See demo_toolbar.exw for an example of this.
8) Renamed: setStatusBarFields() has been changed to setStatusWidths().
9) User can now specify a (mostly) borderless StatusBar - see documentation for details.

CHANGES - ver 0.65 14/04/2003
1) Support for Lists (including multiple selection lists) has been added.
2) More functions for Menus were added.
3) Modified the way that the message params interception code works so it operates at a faster rate.
4) Implemented a set of permanently allocated memory buffers to speed up memory allocations. Some common operations are now up to 5 times faster than Win32lib as a result. Not every allocation in the library uses these buffers.
5) Added support for Tab Controls.
6) Sundry bug fixes.

ver 0.50 31/03/2003
1) Some naming discrepancies and other errors in the Library and Documentation were corrected.
2) Some of the Demos have been modified slightly.
3) Support for some (Modal) Dialog Boxes has been added.
4) Corrected errors in the PAINTSTRUCT definition so WM_PAINT messages now pass the correct (client) coordinates.
5) Timers are now managed slightly differently.
6) IDLE routine does not pass any parameters now.
7) Scroll events now pass the full 32-bits position value, thanks Al.
8) WinMain() now must include parameters (just like win32lib).
9) abort_messageBox() changed to FatalErr()
10) WarnErr() added to Message Box routines.
11) Corrected error in poke2().
12) Support for menus has been added.
13) getText() will now retrieve the text in any part of a multi-field StatusBar.

ver 0.31 18/01/2003
1) Library given name: ARWEN
2) When a program is shutdown any subclassed controls are now unsubclassed in WndProc:WM_DESTROY to prevent error message boxes from appearing after program is shutdown - This formerly happened on my Win95 system when I used setText() inlined in a Handler.
3) Added varyMsgParams() to complement ARWEN's message trapping capability.
4) Enabled OnIdle capability so user can run a continuous background task.
5) Documentation updated, particularly explanations of the Windows messages.
6) Added specific Demo programs to display the library capabilities.

ORIGINAL RELEASE - ver 0.10 17/1/2003

INSTALLATION
To install ARWEN just unzip all the files into a single directory and then add the path to your autoexec.bat file. The library files are stored in a subdirectory, eg: C:\Euphoria\Include\Arwen\Bin\ so please ensure that \Bin\ is at the end of the path.

 

CREDITS
The following people have contributed ideas/code either directly or indirectly (aka plagiarised) for this Library :
Rob Craig - Euphoria language
Derek Parnell - Win32 advice + encouragement
Andrea Cini - EuWinGui comparison
David Cuny - Win32lib comparison + code
Al Getz - Scrollbar code + WinClass comparison
Jordah Ferguson - Menu ideas + other code
Bernie Ryan - Win32 comparison
Judith Evans - Text in top-level menu
Wolfgang Fritz - Text in top-level menu
Evan Marshall - Exposing Edit box anomaly in Combo controls
Elliott Sales de Andrade - Trackbar code
Euman - TrackMouseEvent code
Juergen Luethje - Original code for HyperText control, ProgressBar and many other ideas
Pete Lomax - Many bug fixes, enhancements & code examples
Kenny Hoff - Fast Bresenham line drawing routines

CONVENTIONS
All numbering is one-based to reflect Euphoria's approach with the exception of image coordinates and memory accesses. Where possible a failed function will return 0. Many routine names in ARWEN are similar to those in Win32lib because they are logical and similar to their native Win32 counterparts. This should make ARWEN easier to use if you are familiar with either the former or the latter. Windows, Controls, Menus and Menu elements are referenced using their ID (which is the library's internal representation for the item) and images (bitmaps, icons & cursors) are currently referenced using the Windows handle (hWnd) for the image, however, DIB sections have an ID which is similar in principle to control IDs in ARWEN. Please see the DIB section documentation for more details. I have tried to prevent ARWEN from getting in the way of potential flexibility and the user will therefore be required to pass certain Win32 constants as parameters in some functions. It may be advisable for a user to obtain the Win32 documentation that is referenced on the Euphoria website and keep it handy during a programming session. In the event that your program crashes because of some internal library problem then please send me the ex.err file plus any other details necessary to explain the error and I'll try to deduce what went wrong.

 

EVENT HANDLING
Perhaps the most important initial requirement for ARWEN was that the event handling system be close to maximum speed, have fast background processing capability and still be user-friendly. Win32lib is event-based where each event of a control can have it's own handler. EuWinGui uses a large, omni message loop where detected events are fed to it by the library. ARWEN has a control-based message system and is more similar to native Windows programming than these other libraries. These other libraries also use procedures to handle events but since ARWEN 0.90 handlers are functions so that modifying message parameters is intuitive & easy.

A single message-handler routine is written for each control and almost all messsages for that particular control are fed into it's handler routine, eg :

-- This is a handler routine for a Window
function handler_MAINWINDOW(integer id, integer msg, atom wParam, object lParam)
if msg = WM_PAINT then -- paint the window
elsif msg = WM_VSCROLL then -- scroll something
elsif msg = WM_HSCROLL then -- scroll something
else elsif msg = WM_TIMER then moveFish() -- Eh? Whats going on here?
end if
return 0
end function


-- This is a handler routine for a Button
function handler_BUTTON1(integer id, integer msg, atom wParam, object lParam)
if msg = WM_COMMAND then -- Button1 was clicked, do something
end if
return 0
end
function

The handler routine is associated with a control or Window by:

setHandler(MAINWINDOW, routine_id("handler_MAINWINDOW"))

Multiple controls can be associated with the same handler by listing them in a sequence as the first parameter of setHandler(), eg:

setHandler({BUTTON1, BUTTON2}, routine_id("handler_BUTTONS"))

Each handler routine is a function and must have the parameters in the following format:

function handler_CONTROL (integer id, integer msg, atom wParam, object lParam)
-- some code
return 0
end function

The default value to return from any handler function must always be 0 but certain messages can have other values and is explained further on. The routine parameters are mostly identical to the native Win32 parameters that are initially passed back to SubProc() & WndProc() by Windows. The first difference is that "atom hWnd" has been replaced with "integer id". The second difference is that lParam can sometimes be a sequence of parameters so you should probably use "object lParam". These and other differences are specific to the actual message and are shown later in this document. However, generally speaking, the interpretation of each message parameter is the same as a native Win32 message. Please note that almost all Win32 messages will pass through the user handlers. ARWEN does trap a few of them but, basically, the user has carte blanche. This may be too much of a good thing in some cases and unless you are an experienced Win32 programmer then I suggest you stick to the core Win32 messages. To be honest, I have not mapped out all the core messages myself and this document later shows the ones I have. The advantage of having unrestricted access to all messages sent to a particular control is that specialized message processing can occur.

Important Notice: Messages are sent to a control OR the control's parent (usually a Window). Notification messages are sent from a control to it's parent. The library will try to trap this type of message and redirect them to the (child) control's handler routine. Messages redirected in this way should not be (intercepted and) altered since the redirection process alters the Message format. Whoever writes the handler routines must be aware of this.

The user can intercept and alter certain messages and thus modify the default Windows processing. This is done by returning a 2-element sequence containing your values of {wParam, lParam}. This can be used to advantage when, say, implementing a password window where you do not want the actual characters shown on the screen, eg :

-- untested password capture code fragment
sequence passwordstring -- holds the password entered
passwordstring = ""
function handler_PASSWORD_EDITBOX(integer id, integer msg, atom wParam, object lParam)
if msg = WM_CHAR then -- printable key was pressed
-- add new character to the string variable
passwordstring &= wParam
-- return new character to print, in this case it's an asterisk
return {'*', lParam}
elsif msg = WM_KEYUP then
if wParam = '\13' then -- ENTER key pressed
processPassword( passwordstring )
end if
end if
return 0
end function

The return value from the handler function will be ignored if the handler is processing WM_MOUSE, WM_PAINT, WM_COMMAND or WM_*SCROLL messages. The typical messages that can be (intercepted &) modified usually relate to keystrokes, mouse and also the focus but can include things such as the TCN_SELCHANGING message trapped by the handler of a TabItem. This message notifies that the TabItem is about to close and another one will be opened. If the handler returns 1 then the tab change will be aborted. ARWEN uses this capacity to emulate an intuitive behaviour for disabled controls. Please consult the
Windows Messages section for details on all values that can be used with specific messages.

Generally speaking, the return values from handlers can be in 3 formats:

return 0 - this is the default value when you want windows to process the message
return {i} - use a 1-element sequence to tell windows to return with the enclosed value immediately
return {W, L} - intercept & modify original message for windows to process

Apart from the specific messages that ARWEN traps, the user has access to all other messages but the user is advised to refer to the API for specific implementation details. In some instances the user may be able to return an atomic value other than 0 for certain messages. These cases are discussed in other sections.

If a control has been associated with a particular handler routine and you wish to replace that routine with another then simply call setHandler() again and pass the new routine id. If you wish to disassociate the control from any handler routine then call setHandler() and pass UNDEFINED as the 2nd parameter. Multiple controls may be relinked or disassociated in a single call.

The heart of an event-driven Windows program is the Message Loop. In my library this loop resides in the WinMain() routine. Usually it would be something like this:

while c_func(xGetMessage, {msg, NULL, 0, 0}) <= 0 do
c_proc(xTranslateMessage, {msg})
c_proc(xDispatchMessage, {msg})
end while

The problem here is that the xGetMessage{} call will not return until a message (aka event) is placed in the message buffer and control is handed back to the application. This means that the program will sit idly until some event occurs. Now, some applications may need to perform certain background processing tasks but are really unable to do so using this "standard" message loop. I have modified the message loop so that while there are NO pending messages in the buffer the application can run a continuous background task. To set this up the application must include this line (as if setting up a regular handler):

setHandler ( IDLE, routine_id("MyBackgroundRoutine") )

Now, the background operation may be started or stopped using calls to
startIdle() and stopIdle().

Please note that
startIdle() must pass a timing value (in milliseconds) specifying the granularity of calls to the runIdle() manager. The particular timing value is not very important since OS latency etc will mess it up anyway. A value of, say, 50 milliseconds is probably allright.


IDLE is a global atom that ARWEN recognizes. Please note about the IDLE handler :
1) It is a procedure and NO parameters are passed to it since ARWEN does the calling not Windows.
2) The handler should spend only a short time doing any processing before returning.
3) The handler will not fire when a builtin scrollbar is being moved. See BUGS section.
4) The handler will fire when a Common Dialog box (except Message Box) is open.
5) The handler may be relinked or disassociated in exactly the same way as other control handlers can be but with the restriction of item 1).

 

CREATING CONTROLS
Controls are created in a similar way as is done in Win32lib :

constant CONTROLNAME = create(objType, label, hBitmap, pID, x, y, width, height, styleflags)
-- CONTROLNAME now has the ID of the new control

1) objType - This is an integer designating what sort of control will be created. The "Type" columns in the following table lists the ones that can be used:

CLASS TYPE TYPE TYPE TYPE
Windows Window      
Buttons Button PictureButton ToggleButton TogglePictureButton
MarkBoxes RadioButton CheckBox TriCheckBox  
Group Group      
Editable EditText MultiEditText    
Label Label      
ToolBar ToolBar ToolSep    
StatusBar StatusBar StatusField    
ScrollBar HScroll VScroll    
TrackBar HTrackBar VTrackBar    
Menus Menu MenuItem    
Lists Listbox ComboBox ComboDropDownList ComboDropDown
Tabs TabControl TabItem    
Progress ProgressBar      
HyperText HyperText      
         


2) label - This parameter holds the text string (if any) to be associated with the control/object. If no string is needed then the user may simply use any integer which will be converted an empty string at creation.

3) hBitmap - This parameter holds the handle of a bitmap that the user wishes to associate with the control/object. If no bitmap handle is to be used then please ensure that a NULL (or 0) is used.

4) pID - This is the ID of the parent control. Only a top-level Window or an un-owned Menu can have no parent, in which case this parameter would be 0.

5) x - This integer indicates the position of the control from the left boundary of it's parent. Toolbar buttons treat this parameter differently, see below for details

6) y - Position of control from the top boundary of it's parent

7) width - Gross width of the control

8) height - Gross height of the control

9) styleflags - Style flags used at the creation of the control. There are 2 kinds of style: Standard style & Extended style. If this parameter is a single atom then ARWEN will assume it holds flags for the Standard style. If this parameter is a sequence it must hold 2 elements only. The first element will be treated as the flags for the Standard style and the second element will be treated as flags for the Extended style. The sorts of flags that can be used for a particular control type vary greatly. Please consult the Win32 API for details. Each element may be composed of a number of individual flags (atoms). ARWEN will ensure the flags are all ORed together to combine into an atom, eg:

wStyle = {WS_HSCROLL, WS_VSCROLL}
wStyleEx = {WS_EX_CLIENTEDGE, WS_EX_CONTEXTHELP}

If any flags for the Extended styles are used they must not be combined with the Standard style flags but must reside in the correct atom. Please note that default Standard style flags have already been prepared for many controls. These may be seen in the classes.ew file. The flags supplied by the user at the creation of the control are combined with the existing default style flags; they do not replace them. If you wish to have a different set of default style flags for a control then you can alter the defaults by calling setClassDefaults() and supplying your own flags. Beware that this operation must occur BEFORE the control is created and that the new default style flags will be used during the creation of proceeding controls of the same type. This function returns the previous style flag values. The default styles for a control class may be restored by calling resetClassDefaults(),eg:

MAINWINDOW = create(Window, "Main Win", 0, 0, 0, 0, 800, 600, 0) -- Normal window is created
oldstyles = setClassDefaults( Window, wStyle, wStyleEx ) -- flag values as above
-- Each Window created will have Scroll bars & Context help
AWINDOW1 = (Window, "A Win1", 0, MAINWINDOW, 10,10, 400, 300, 0)
AWINDOW2 = ...
AWINDOW3 = ...
-- The original style flags for the Window class will now be restored
void = resetClassDefaults( Window )
AWINDOW4 = create(Window, "A Win4", 0, MAINWINDOW, 10,10, 400, 300, 0)
-- Normal window is created

DESTROYING CONTROLS
When an application in ARWEN is closed the library will destroy all the controls that were created and (hopefully) release all allocated memory. However, there may be occasions when the user would like to destroy a control while the application is still running. An example of this could be a set of text labels along the axes of a graph. If the boundaries of the graph are altered so that the number of labels is different then the labels should really all be destroyed and then recreated. To destroy a control or Menu or MenuItem simply use:

procedure destroy(integer id)

If the control is a Menu then ARWEN will destroy all attached MenuItems but will only unattach (sub) Menus before destroying the Menu itself. Please note that after ARWEN destroys the control via the API it simply marks the id slot as vacant and will recycle it at the next available opportunity. Therefore, beware of how the id of the original control is accessed in other parts of the program since it's type could be altered which would cause much confusion. Perhaps a good idea is to limit recreated controls to static types. Another problem with destroying & recreating (non-static) controls is where you must have a handler associated with them. This means that you must invoke setHandler() after recreating the control and since handler routines are generally at the end of the source code there are opportunities for frustration due to Euphoria's declare-before-use philosophy. The solution is declare an integer variable at the top of the program that will eventually be assigned the routine id of the handler and use that integer variable in setHandler() anywhere you like in the program.

MOVING BETWEEN CONTROLS
There are times when the user may wish or need to move the focus from control to control without using the mouse. ARWEN now supports sophisticated tabbing between child controls in a Window. Generally, to advance the focus this way simply press the TAB key. However, certain controls require an extra key pressed since they have other uses for the TAB key. Also, another key is required to move the focus in reverse. This table shows the appropriate combinations:

  MultiEditText & TabItem All other Controls
Move Forward CTRL + TAB TAB
Move Backwards CTRL + SHIFT + TAB TAB + SHIFT

For MultiEditText controls it was necessary to add the CTRL keypress because the default behaviour for the lone TAB key is already taken for inserting a Tab character into the text field. And a TabItem control will repeatedly cycle through all the child controls on it's client area if only the TAB key is pressed. To jump to an adjacent control the extra CTRL or SHIFT key is needed.


ACCELERATOR KEYS

ARWEN emulates support for Accelerator keys. These are user-defined key combinations that are generally used to bypass menu navigations and jump directly to a desired operation. Although there is support for Accelerator keys inside the API I was never able to get it working. Instead, I had to write my own system for managing this capability. A downside is that the key combinations are limited to "ALT + key" pairings (ie, CTRL & SHIFT are ignored). The first way to register an Accelerator key is indirectly when creating the control by inserting the Ampersand character immediately before the character you nominate as the accelerator, ie:

id = create(Button, "&Open",..) -- ALT + "O"

Now, whenever the owner window is open and has the focus (ie, either on itself or any child controls belonging to it), pressing this combination will cause a WM_COMMAND message to be sent to the child control's handler. In effect, the behaviour is mimicking a keypress or mouse click on that control. The caption on the control will indicate the accelerator key with an underline. Because of the process & various limitations the key nomination is NOT case sensitive.

Only one key may be nominated this way. However, you may directly specify any number of accelerators for any control by invoking:

void = setAccelerator(integer id, object caption)

ID naturally refers to the control and although it can be of any type it is best to use "clickable" controls to avoid confusion.
caption can be (i) a virtual key code integer or (ii) a length=1 sequence containing the ASCII code or (iii) a length>1 sequence containing the control' caption in the same format as used in the create() example above.

If this call succeeds the result will be True otherwise False. A failed call could happen where 2 controls, both having the same window owner, attempt to nominate the same accelerator key.

Nominating keys normally used by Window's own accelerated functions is highly unwise because you might lose keyboard control for some important functions.

In the control handler that receives the WM_COMMAND message as the result of an Accelerator the
lParam will be 1 otherwise it will be 0. If the handler returns a 0 (default) then the focus will be placed onto the control associated with the Accelerator. If that control is not a focussable kind then the next available, focussable control will be sought instead. This behaviour could have esoteric consequences if the nominated control is, say, a menu because the next available control is unlikely to be related and hence moving the focus to that control doesn't serve a useful purpose. Any return value other than 0 will prevent the focus moving from it's original position. In most cases the user will not want the focus to move, therefore the usual return value will be, say, 1.

If the nominated control is disabled the Accelerator will not fire.

Menus & Accelerators
It seems that because Windows' manages the Menu loop itself, it will exhibit the actions of Access keys for Menus which is easily confused with Accelerator keys. Since I cannot alter the behaviour of Menus in this regard I thought it'd be better to only allow Accelerator keys for Menus via setAccelerator(). Therefore, when the user creates an accelerated control the key underscore & linking will be done automatically. However, when the control is a menu only the underscore will automatically show and Windows will use the nominated key as an Access key (to quickly jump along menu navigations). Linking a menu to an Accelerator must be done explicitly, using a different key combination, eg:

id = create(MenuItem, "&File (ALT+ F1)",..) -- caption is: File (ALT+ F1)

setAccelerator(id, VK_F1) -- link to accelerator key


SPECIFIC CONTROL INFORMATION

WINDOWS
These are created in the usual way. Child windows are created when pID refers to an already existing window (NB: This is not the same as specifying WS_CHILD in the style). All the standard & extended class styles are available to the user but Common Dialog windows will have Windows-defined parameters. By specifying WS_HSCROLL and/or WS_VSCROLL in the style parameter the window will be created with builtin scroll bar/s. In this situation the window's handler routine will then be sent any scroll messages relating to those scroll bars. ARWEN has provision for managing an independent set of scroll bars to replace the poorly performing built-in scroll bars.

BUTTONS
Buttons can operate either as a standard push button or as a toggle button where the button will appear pressed down after one click but is restored after a second click. Buttons can have text OR pictures on their faces. Combinations of the 2 are possible with FlatToolbars but ARWEN doesn't support this yet, sorry. It is still possible to have 'text' on the face of a picture button but only if the bitmap image on the face of that button has been drawn with text in it. When a button is created the 'face' parameter (see above) will contain either a text string or a bitmap handle depending on which type is being created. This parameter can be set to NULL if desired. Buttons or ToggleButtons can have their title text altered with: setText(BUTTON, "sometext") PictureButton & TogglePictureButton can have their face image altered with:

void = sendMessage(id, BM_SETIMAGE, IMAGE_BITMAP, hBitmap)
-- hbitmap is the hWnd of a bitmap

When a button is clicked a WM_COMMAND message is (eventually) sent to the handler. It is best not to try to use any other message to detect mouse clicks for Buttons. An application can determine a button's state by sending it a BM_GETCHECK or BM_GETSTATE message; the application can set a button's state by sending it a BM_SETCHECK or BM_SETSTATE message.

MARKBOXES
Markboxes (my name for this class) are controls with a field that leaves a mark when it is clicked by the mouse. You can associate text with a markbox either at it's creation or at any other time using setText(). A CheckBox can have 2 states (on & off) which toggle with each mouse click. A TriCheckBox has 3 states (on, off & grayed). A RadioButton has 2 states (on & off) but it's behaviour is influenced by any other sibling RadioButtons. As a RadioButton is clicked all the other ones will be cleared. this is the default behaviour from Windows. An application can determine a button's state by sending it a BM_GETCHECK or BM_GETSTATE message; the application can set a button's state by sending it a BM_SETCHECK or BM_SETSTATE message.

GROUP
A Group is a static, transparent container for controls with a common purpose. I believe RadioButtons in a Group will exhibit mutually exclusive behaviour. The title of a Group can be altered using setText(). A Group does not respond to events.

EDITABLE
This class includes EditText (a single editable field) or MultiEditText (a mulit-line edit field - basically a mini editor). Be very careful if you trap any events destined for these controls as the resulting behaviour is likely to be highly esoteric. Please examine the control styles for this class in the Win32 API documentation for behavioural possibilities. Perversely, an EditText is always left-justified and can never be right or center justified even though the style constants ES_RIGHT & ES_CENTER exist and apparently work for MultiEditText controls. Text can be read/written using getText()& setText().

It is possible to change the background colour of these controls by including this code in the handler:

atom hBrush
hBrush = c_func(xCreateSolidBrush, {Parchment})
procedure handler_EDIT(integer id, integer msg, atom wParam, object lParam)
if msg = WM_CTLCOLOREDIT then
void = c_func(xSetBkColor, {wParam, Parchment})
return hBrush
elsif msg = WM_DESTROY then
void = deleteObject(hBrush)
end if
end procedure

LABEL
A Label is a static control that displays a single line of text. The creation style will determine the orientation of the text. getText() & setText()apply.

TOOLBAR
A Toolbar is a container for other controls, usually buttons. A ToolBar resides at the top of the window. Any controls in it are sent messages directly so you can view them like any other control of that type. When a control is created for a Toolbar the 'x' parameter becomes a displacement from the previous control added. A small separation can be made between clumps of controls by 'creating' a ToolSep, eg:

void = create(ToolSep, 0, 0, TOOLBARMAIN, 0, 0, 0, 0, 0)

In this case all the parameters are ignored (except the id of the parent). Another way of making a separation is to have any positive value other than 0 in the 'x' parameter field when creating a normal toolbar button. There is a border around any Buttons in the ToolBar that has a minimum width. I think this may be something that Windows enforces.

STATUSBAR
A StatusBar is the client-wide field placed right at the bottom of a window (but inside the borders). This field is used to display (usually) text messages about the "status" of the application, hence the name. When a StatusBar is created it has one field but additional fields can be added by creating StatusField controls as children of the StatusBar:

STATUSBAR = create(StatusBar, "SB_1", 0, WINDOW, 0, 0, width, 0, flags)
STATUSFIELD1 = create(StatusField, "SF_1", 0, STATUSBAR, 0, 0, width, 0, flags)
STATUSFIELD2 = create(StatusField, "SF_2", 0, STATUSBAR, 0, 0, width, 0, flags)

The width parameter signifies how wide the field will be (in pixels) but if it is 0 then the field will stretch across to the right border. All other dimensions are ignored. The text in the StatusBar/Field can be set at creation time or accessed by using getText()& setText() in the normal manner. By default, text is left-aligned within the specified part of a status window. You can embed tab characters "\t" in the text to center or right-align it. Text to the right of a single tab character is centered, and text to the right of a double tab character "\t\t" is right-aligned. The widths of each field can be adjusted with:

setStatusWidths( integer id, object fieldwidths )

If fieldwidths is an integer then only that particular field will be re-lengthed but if fieldwidths is a sequence of integers then id must be the StatusBar parent which will be divided into up to 255 items where each integer relates to the desired width of each field. Please take care that the length of the sequence is the same as the total number of fields in the StatusBar (including the parent). If there is a discrepancy then ARWEN will complain and refuse to cooperate. If any item's length is 0 then it's corresponding field will stretch to the edge of the screen (excluding any Size Grip). ARWEN will automatically resposition the status bar when the parent window is resized. An interesting twist for multiple-field StatusBars is that the user can put it into "simple" mode by sending the SB_SIMPLE message, ie:

void = sendMessage(id, SB_SIMPLE, 1, 0)
-- StatusBar will switch to a single field mode

In this state one field is shown as if it was the only one. In fact, it is an additional field not normally seen and any info in previously set fields is retained. The switch can be made back by sending this message:

void = sendMessage(id, SB_SIMPLE, 0, 0)
-- StatusBar will switch back to multiple field mode

SCROLLBAR
A Scrollbar can be created on its own OR as a builtin element of a window. The latter way occurs when WS_HSCROLL and/or WS_VSCROLL are specified in the style parameter flags at the window creation. In this situation scroll events will be sent to the window owner's handler. When scroll bars are made this way it has the advantage that they automatically resize as the window is resized. The disadvantage is that they are not quite so flexible as scroll bars created on their own are. To differentiate between scroll bar controls created on their own or as part of a window the user must pass a flag into getScrollInfo() or setScrollInfo() that indicates what type is being referred to.

procedure setScrollInfo(object id, sequence settings, integer fRedraw)

id - refers to the id of the ScrollBar or TrackBar or ProgressBar - for builtin scroll bars it's a sequence {Window, flag} where Window is the id of the owner window and flag is SB_HORZ or SB_VERT to specify correct bar.

settings - A list containing {Minimum, Maximum, PageSize, Position}, it can also have the additional item of TrackPos but you may never need it. - Scrollbars must have the 4 main items but for TrackBars only need the first 2 items are mandatory and the 5th possible item is never used. - For ProgressBars the interpretation of this parameter is {Minimum, Maximum, StepSize, Position} where StepSize refers to the amount that the pointer will advance in the next iteration.

fRedraw - Flag indicating whether the control needs to be redrawn after the settings have been updated. True for yes, False for no. - Has no effect for ProgressBars.

function getScrollInfo(object id)

id has the same meaning as in setScrollInfo(). The function will return a sequence of {Minimum, Maximum, PageSize, Position} for TrackBars but will return {Minimum, Maximum, PageSize, Position, TrackPos} for ScrollBars. Currently this function doesn't provide for Progressbars. If an error occurs then it will return {0,0,0,0,0}.

function getPos(object id)

Will return the current position of the ScrollBar, TrackBar or ProgressBar.

procedure setPos(object id, atom pos)

Will set the current position of the ScrollBar, TrackBar or ProgressBar. Builtin scroll bars will cause WM_HSCROLL or WM_VSCROLL messages to be sent to the handler routine of the owner window. The messages will pass the full 32-bit position of the scroll control.

ARWEN has been updated to separately manage independent scroll bars which are similar to those that can be created as attributes of a Window. Since builtin scroll bars are possible attributes of windows the user might inadvertently create them by invoking setScrollInfo() and passing the id of the Window. Now that you know about this anomaly you'll probably never make that mistake anyway. The reason ARWEN now manages independent scroll bars in this way is to overcome the limitations that the built-in scroll bars have. Independent scroll bars reside along the borders of the window and are automatically resized with the window. To create these scroll bars use the flags SBS_BOTTOMALIGN or SBS_RIGHTALIGN for Horizontal or Vertical scroll bars respectively,ie:

constant HHH = create(HScroll, "", 0, DEMO, 0,0,0,0, SBS_BOTTOMALIGN)
constant VVV = create(VScroll, "", 0, DEMO, 0,0,0,0, SBS_RIGHTALIGN)

The scroll bars will be created using the default width for scroll bars. You may change this by specifying the nWidth or nHeight fields for Vertical or Horizontal scroll bars respectively. All other dimensions are ignored when these special flags are used. The benefits of these managed scroll bars is that they are always perfectly positioned, ie, the vertical scroll bar is always beneath the ToolBar and above the StatusBar (if any) and the Horizontal scroll bar always sits above the StatusBar (if any). Access to these independent scroll bars is the same as to any other independent scroll bar because they are actual controls in their own right, not attributes of a window. You may still use the builtin scroll bars if you wish. As far as I can tell there is one advantage the built-in scroll bars have and that is where the pair of them exist (& are visible) then a size-box is automatically shown. I do not make any provision for emulating this behaviour. However, you may specify the SBARS_SIZEGRIP flag when creating a StatusBar which will then have a sizebox. Applications usually have a StatusBar but if your's didn't you could use the built-in scroll bars to ensure the use of the size-box. One drawback that the managed scroll bars have is that they are placed in the Client area and therefore may suffer interference from other controls such as Buttons, CheckBoxes etc.. I have not yet found an easy way to stop this but the user can limit the size of a window by trapping the WM_SIZING message which might help. Also, scroll bars seem likely only where the Client area is entirely user-drawn so this issue may not really be a problem at all.

TRACKBAR
A TrackBar is similar to a scroll bar control. These controls will generate WM_HSCROLL or WM_VSCROLL messages like scroll bars do. You can use getPos()& setPos() to get the position of a track bar and getScrollInfo() or setScrollInfo() to access other settings as explained above. Other actions, such as setting the tick marks along a TrackBar, are possible by sending certain messages. See the Win32 API documentation for details. An important difference between TrackBars and ScrollBars is that because TrackBars have no "thumb" the pointer position can reach to MAXRANGE but a Scrollbar position can only reach to MAXRANGE - PAGESIZE + 1.

PROGRESSBAR
This is similar to a TrackBar or ScrollBar but does not generate WM_HSCROLL or WM_VSCROLL messages. You can use getPos()& setPos() to access the position of the leading edge of a ProgressBar and setScrollInfo() to access other settings as explained above. ProgressBars are, by default, horizontal but using PBS_VERTICAL in the style parameter on creation will produce a vertical bar. ProgressBars also have a default output of discrete graphic blocks but this can be changed by including PBS_SMOOTH in the style parameter on creation. To advance the leading edge by another iteration you can call makeProgress(). Please note that when using this routine once the leading edge reaches the end then the very next call will reset the edge back to the beginning where it will start progressing again. I think there is some potential for incorrect behaviour here. Please note that all the parameter values must be in the range 0 to 65535.

MENUS
Menus are ubiquitous in Windows and are a convenient way of grouping related options in a tidy format particularly if there is a large number of them. Menus are usually accessed from the MenuBar which is located at the top of a window however they can be made to "popup" anywhere on the screen. A Menu is really a container for one or more Menu elements. Menu elements can be either MenuItems or other Menus (aka submenus). When a Menuitem is selected by the user then a WM_COMMAND is sent to the Menuitem's handler, if any. Submenus do not generate this message. A menu is created using:

MENU_FILE = create(Menu, text, hBitmap, parent, 0, 0, 0, 0, flags)

parent is the id of a Window or another Menu or it can be NULL. If the parent is a Window then that window must not be a child window. If the parent is another menu then the new menu will appear as a submenu. If the the parent field is NULL then the menu can only be opened with trackPopupMenu().Alternatively, this non-owned menu can be inserted into another Menu - see below for more details.

text is the character string that will appear in the menu.

flags can be either a single atom or sequence containing menu-specific flags.

A MenuItem is created with:

MENUITEM_OPEN = create(MenuItem, text, hBitmap, parent, 0, 0, 0, 0, flags)

In this situation the parent field must specify a previously created menu. A MenuItem cannot be placed into the top-level menu (a window's menubar). A horizontal separator line can be appended onto a menu by specifying:

MENUITEM_SEP = create(MenuItem, "-", 0, parent, 0, 0, 0, 0, flags)

NB: The Separator really is a MenuItem in its own right but it does not respond to events. When creating menu elements it should be noted that some types of flags may be ignored. Please consult the source code to determine which ones apply.

When a menu is displayed the entries are displayed in a single vertical column. It is possible to show additional vertical columns by specifying MF_MENUBARBREAK or MF_MENUBREAK in the flags field when creating a new menu element. The new Menu element will appear in the menu at the top of a new column. The only difference between these last two flags is that MF_MENUBARBREAK will place a vertical line between the columns. Please note that these flags are really an attribute of a Menu or MenuItem unlike the Separator line which is a form of MenuItem. This means that these flags can be included in the creation of a Separator line but there may be no point to doing so since the result will look a bit strange in the menu, ie: A new vertical column will be formed with the Separator line right at the top of it.

Any Menu or MenuItem can be enabled or disabled using setEnable(). Text labels can be accessed for any Menu or MenuItem using getText()& setText(). Any Menu (except top-level menus) or MenuItem can be "checked" using setCheck(). If a Window is sized so that the the main Menu does not fit on a single line then it will "wrap" the excess items onto an additional line. A Menu element may be inserted into a Menu using :

function insertMenu(object item, integer pID, integer pos, atom flags)

item - either the id of an unowned Menu OR a text string for a new MenuItem
pID - id of the destination Menu
pos - position that the item is to be inserted at. If pos is 0 then the item will be put at the end of the Menu
flags - additional flags you may wish to specify. These are the same as are used when a menu element is created

If the function succeeds it will return the id of the Menu inserted or the id of the new MenuItem (created &) inserted. Otherwise it returns 0. Please note that ARWEN will not allow MenuItems to be inserted into a top-level menu. According to Petzold "Top-level items without popups can be too easily chosen by mistake." so I am enforcing this apparent limitation. From a programming perspective a MenuItem in a top-level menu is actually a Menu without an associated popup menu and it possibly could be quite awkward to maintain code for this though I don't know this from experience. Any Menu element may be removed from a Menu using:

function removeMenu(integer id)

This routine will remove the menu from it's parent menu but it can be inserted later or used as an unowned popup Menu. The functions returns True on success or False on failure. Any Menu or MenuItem can be destroyed with:

function destroy(integer id)

The function returns False on failure or non-zero for success (actually the number of items successfully destroyed). These 2 last functions could be used to maintain a 'Recent files' list inside a Menu.

To trap messages (WM_COMMAND of course) from inserted MenuItems you can do so by setting up a handler for the parent Menu OR set up a handler for the Menu element when the insertion is made. I think the latter way is the most secure thing to do. Any menu (dropdown, submenu or unowned) can be accessed with the trackPopupMenu() function which will, well, popup a menu where you have clicked on the application window, eg:

void = trackPopupMenu( id, window, flags )

id is the id of the menu window is the id of the window that you wish to associate the menu with
flags are any combination of Button and Mode, ie: Button - TPM_LEFTBUTTON or TPM_RIGHTBUTTON Mode - TPM_LEFTALIGN, TPM_CENTERALIGN or TPM_RIGHTALIGN (If you put 0 in the flags field then you will get the standard behaviour. I may drop these flags in the future, we'll see.) To use trackPopupMenu() I trap WM_RBUTTONDOWN message in the Window handler routine and then make the call as above. You could just as easily trap the WM_LBUTTONDOWN message, in fact any message you like such as,say, keystrokes. Apparently the proper way is to trap the WM_CONTEXTMENU message in the owner window handler. I use the current mouse coordinates to dictate where the menu will popup on the screen because that is the expected behaviour (and is natural since menus are accessed with the mouse etc..) but does anyone out there want more flexibility so that they can force the popup to appear anywhere on the screen (not just the window y' know) ? When the call is made to trackPopupMenu() then it doesn't return until the popup has closed. In one way it is like the Dialog windows.

At times the user may wish to treat a common group of MenuItems in a similar way to the behaviour of RadioButtons where one item can be marked to the exclusion of all other items in the group. You can use this procedure for MenuItems:

checkMenuRadioItem(integer id, integer first, integer last)

This routine will set a radiobutton mark next to the entry id and remove any marks against all other members of the range (inclusive) as specified from first to last. Please note that all items should belong to the same menu. The call may fail or behave corruptly if they are not - not that I have actually tested this behaviour... At this point in time Accelerator keys for Menus are not yet supported.

LIST CONTROLS
This general class of control comprises 2 basic groups - ListBox & ComboBox. Now, a ListBox is just a list of text strings as you would expect. A ComboBox comprises a ListBox AND an EditText control combination, which explains the name. Programmatically, the difference between each type of list is this: (i) A Listbox can select multiple items if created with the appropriate flag/s. (ii) A ComboBox's edit field allows access to it via getText()& setText(). The Combox group includes 2 additional kinds - ComboDropDownList & ComboDropDown. A ComboDropDownList behaves exactly list a ListBox except that it has the DropDown feature but no edit box. A ComboDropDown is a ComboBox where the list part drops down. Due to the shortsightedness of the powers that be, a list can only contain 32767 items. When an event occurs in a list it sends a notification message to the dialog box procedure of the owner window in the form of a WM_COMMAND message. At the creation of a list control the style flags can be used to modify the behaviour/appearance etc.. Please consult the API documentation for more details. The control types ComboBox & ComboDropDown have edit controls embedded within them. These edit controls are subclassed and almost all messages are sent via the Combo's handler.

Where a control has a fixed list, the list items may be selected by using the usual navigation keys such as the arrow keys. Where a control has a dropdown list (ComboDropDown or ComboDropDownList) the list may be opened or closed by pressing F4 or clicking the down facing chevron.

The following routines are used with Lists & Combos (NB: some of these routines are heavily optimized; all of them are fast) :

function insertItem(integer id, sequence text, integer pos)

The new item will be inserted into the list at index pos. If pos is 0 then the item will be appended to the list unless the list has sort flags in which case the item will be inserted in it's sort order. If text contains multiple text strings then they will all be inserted into the list starting from the point specified; sorted lists will put each item in sort order. The function will return the index position of the last item inserted except for sorted lists where 1 will be returned. If an error then 0 will be returned.

procedure deleteItem(integer id, object pos)

The list item will be deleted. If pos is 0 then the entire list will be erased. If pos is a sequence then it must be a 2 element sequence which specifies the lower & upper boundaries (inclusive) of a range of elements that will be deleted from the list.

function getItem(integer id, object pos)

The list item's text will be retrieved. If the function fails then an empty string will be returned. If pos is a sequence then it must be a 2 element sequence which specifies the lower & upper boundaries (inclusive) of a range of elements that will be retrieved from the list. If pos = 0 then this function will attempt to retrieve the currently selected item but will fail (return "") if multiple items were selected.

procedure setItem(integer id, sequence text, integer pos)

The text of the item at index pos will be replaced by the new string. A side-effect of the API calls within this routine is that the list will be scrolled, if needed, so that the item at the index position is visible in the display window.

function getCount(integer id)

Returns the number of items in the list.

function findItem(integer id, sequence text, integer pos)

An attempt is made to find the index of the list item where the text matches the items prefix (ie, the list item's text can be longer). The search will start from pos. If the search reaches the end of the list it will loop back to the start and continue until it meets pos again. If pos is 0 then the search will commence from the start of the list. function getIndex(integer id) Retrieves the list index currently selected. If the list is capable of multiple selections and more than one item is selected then a sequence containing the selected items is returned.

procedure setIndex(integer id, object selection)

Causes the list index to point to item selection. If selection is 0 then the list will be entirely de-selected. If the list is a ListBox AND it was created with the LBS_MULTIPLESEL flag then: (i) selection can be a sequence of indexes that will be simultaneously selected. All previously selected items will first be de-selected. (ii) If selection is -1 the whole list will be selected.

procedure setTopItem(integer id, integer pos)

The list will be scrolled so that the position referenced appears at the top of the display window. This does NOT mean that that the topmost item displayed is now the current selection - you must use setIndex() for that.

TAB CONTROLS
A TabControl can have a number of TabItems which each exclusively open up their own set of controls in the display area of the TabControl. TabItems are created as children of the TabControl. The text of TabItems is set at its' creation but can be accessed anytime using getText()& setText(). TabControls can be created with a number of different options. Please see the API documentation for more information. One option that I thought was quite useful is TCS_RIGHTJUSTIFY where the TabItems are spaced so that they cover the entire width of the TabControl. The problem with this specification is that you also have to have TCS_MULTILINE as well. I have found that unless the quantity of TabItems is enough to exceed one full row then the desirable right-justification doesn't manifest itself. Controls of any sort may be placed on a TabItem by creating them with the TabItem as the parent. ARWEN has to reparent this to be the TabControl since the TabControl is the only real control and TabItems are really attributes of it; not real controls in their own right. The x,y coordinates of any controls created in this way are relative to the TabControl's display area - as you can imagine. Please beware that the coordinates of controls on a TabItem are set at it's creation and if more than one row of tabItems is created then there may be undesirable clipping of the controls occuring. The solution is to decide to have a single row of tabs OR ensure that the uppermost coordinate of the controls will allow it to be unaffected should the number of rows of tabs increase.

When a TabItem is clicked then a WM_NOTIFY message is sent to the outgoing and incoming TabItems. The notification code will be in the wParam member and is TCN_SELCHANGING for the outgoing TabItem and TCN_SELCHANGE for the incoming TabItem. If the handler traps TCN_SELCHANGING it can return 1 to prevent the tab selection from changing. This behaviour may be useful where, say, an edit field has not been completed by the user and must be corrected before continuing. The id of the currently selected TabItem can be obtained by calling getIndex() and passing the id of the TabControl.

A TabItem may be opened/activated/selected by invoking setFocus(). I had imagined that I should be using a complementary pair such as getIndex/setIndex OR getFocus/setFocus but only the titles are intuitive not the expected behaviour. Would someone like to offer any suggestions on this?

The TabControl owner of the TabItems can be hidden or shown using setVisible(). If a TabControl is created but no TabItems are put in it then it will not show on the screen. However, do not use setVisible() to modify the appearance state of a TabItem as the program will crash. Also, I have not made any allowance for inserting a TabControl within a TabItem, although this seems an unlikely possibility anyway. TabItems can now be disabled (using setVisible()) even though the API does not properly allow for it. ARWEN emulates disabled behaviour for TabItems by trapping the TCN_SELCHANGING msg and then actually drawing the disabled text. To be able to draw text on a TabItem means that the TabControl must be owner-drawn and so I have decided to permanently set the TCS_OWNERDRAWFIXED flag at the creation of the Tab Control..

HYPERTEXT
This is a customised flat button that has an underlined text field and the cursor changes to the Url hand image when passed over the control. This style is similar to many web-linked documents and the user may wish to emulate this effect by calling openDocument() inside the control handler.

function openDocument(sequence target)

target is the document target and when this function is invoked it will action the link using whatever executable program is currently associated with the document. The document target could be any sort of file and it will be executed by the application that Windows associates with the document type. A user could open a bitmap file in MS Paint; a txt file in NotePad; a Web page in the default browser; even open a blank email, put some text in it ready to send to a recipient.

MOUSE ROUTINES

procedure trackMouseEvent(integer id, integer flags, integer hovertime)

Will send WM_MOUSEHOVER and WM_MOUSELEAVE messages to the window. Could be used for a custom drawn button. Window OS must be Win95b or greater. For more information please see the Win32 documentation. id - the control id flags - Any combination of the following: TME_CANCEL The caller wants to cancel a prior tracking request. Must include the type of tracking to be cancelled, eg: TME_CANCEL + TME_HOVER. TME_HOVER The caller wants to receive a WM_MOUSEHOVER message. If the caller requests hover tracking while hover tracking is already active, the hover timer will be reset.This flag is ignored if the mouse pointer is not over the specified window or area. TME_LEAVE The caller wants a WM_MOUSELEAVE message. TME_QUERY The function fills in a TRACKMOUSEEVENT structure instead of treating it as a tracking request. hovertime - Time in milliseconds for mouse to 'hover' over control before WM_MOUSEHOVER message is sent. If HOVER_DEFAULT is used here then Windows will wait for the default hover time before issuing the WM_MOUSEHOVER message.

function findMouse()
Retrieves the id of the control that has captured the mouse.

procedure captureMouse(integer id)
Captures the mouse for the specified control so that even if the mouse leaves the client area of the control it will still be sent mouse messages.

procedure releaseMouse()
Returns control of the mouse to Windows so that all mouse messages are processed normally.

CLIPBOARD FUNCTIONS
ARWEN supports copying & retrieving data to/from Windows' clipboard.

function getClipboardText()
Returns a sequence containing the text if successful. If a failure occurs then will return empty sequence. The user can also use getText(CLIPBOARD) to perform the same function.

procedure setClipboardText(sequence text)
Will copy the text data into the clipboard. The user can also call setText(CLIPBOARD, text) to perfrom the same action.

procedure copyDibToClipboard(integer id)
Will copy a DibSection into the Windows clipboard.

procedure loadDibFromClipboard(integer id)
Will create a DibSection from the image in the Windows clipboard. id refers to the window id of the "owner" of the clipboard - simply pass the id of you main window in the app. The return value is either the id of a new DIB section or NULL if an error occurs.

DIB SECTIONS & BITMAPS
In Windows, there are 2 basic kinds of bitmaps - DDB & DIB - the former are Device Dependent Bitmaps which are a legacy from the early days of the OS. The latter are Device Independent Bitmaps. The basic difference is that the former are stored in a formatto match the display adapter settings and so have a problem porting the colour system whereas the latter do not and therefore are the preferred choice for graphics. However, the stored format of DDBs means that they can have better performance than DIBs. Windows is forced to accomodate both for the sake of backwards compatibility. ARWEN uses a DIB section library. DIB sections are DIBs stored in memory that the user can directly write to. The library has a basic set of routines for manipulating DIBs including pixel and line drawing routines. Please see DibSection.rtf and the source code for details.

DIB's can be created from scratch or can be loaded from a bitmap on disk or pasted from the clipboard. They can also be saved to disk as bitmaps or copied to the clipboard. The library supports only 8-bit or 32-bit DIBs. Whenever a bitmap of a different colour depth is encountered, the library will convert it into one of the supported formats. 8-bit DIBs have an associated 256 entry palette which is easy & fast to manipulate to provide effects such as fading, contrast & brightness adjustment etc.. These DIBs also have better blitting performance than 32-bit DIBs, however, those have unlimited access to all possible colours so they are useful for high-resolution graphics but they do require about 4 times as much memory and are slower to manipulate. An important advantage of DIB sections is that the user is not forced to use the cumbersome Windows GDI system - those fast action games you may have seen for Windows probably use DirectX or OpenGL but NOT the normal GDI functions. Since the DIB pixels are directly accesible we can use routines like mem_set() to do things like clear the whole image or draw horizontal lines at near maximum speeds. The library also has a fast line-drawing routine that takes advantage of having direct access to the DIB. Drawing text on DIBs is also possible but the font used is a default (stock) one as Font creation in Arwen has not been added yet. Lastly, all graphics routines in the library are written entirely in Euphoria. This means that direct operations, such as line drawing, will benefit greatly from translation & compilation as there is no overhead for calling a DLL routine to perform the operation. Such applications can be highly graphic performant.

ICON & CURSOR (mouse pointer) ROUTINES

global function loadCursor(object fName)
Load a single cursor. fName can be a fully qualified path or simply a filename (where it will be searched for it in the application directory). If fName is integer then will load stock cursor resource.

global function getCursor()
Get the handle of the current cursor associated with the mouse pointer.

global function setCursor(atom hCur)
Associates a new cursor with the mouse pointer.

global function loadIcon(object icon)
Get the handle of an icon : from an ico file, the icon bound to the executable OR a standard icon. icon can be a fully qualified path or simply a filename (where it will be searched for it in the application directory). If icon is NULL then it will load icon "exw" associated with the executable program. If icon is one of the following integers then will load the associated stock icon:
IDI_APPLICATION -- icon signifying plain window
IDI_HAND -- red circle with stocky x inside - fatal error
IDI_QUESTION -- Speech bubble with ? inside
IDI_EXCLAMATION -- Yellow triangle with ! inside
IDI_ASTERISK -- Speech bubble with i inside
IDI_WINLOGO -- windows logo

global procedure setIcon(integer id, atom hIcon)
Associate the icon to the window. The icon handle must first be obtained using loadIcon().


TIMERS
A Timer can be used to generate a periodic event. You could use this to implement a background save capability or to issue time-out warnings or maybe just to build your own custom digital clock. To create a timer use this code:

constant TIMER = createTimer() -- TIMER is now the ID of the new timer

To start a timer operating use:

startTimer( TimerID, OwnerID, n )
-- TimerID is the ID of the timer
-- OwnerID is the ID of the window that WM_TIMER messages will be sent to
-- 'n' is the number of milliseconds between each timer event.

To stop a timer use:

stopTimer(TimerID)

Later on you can restart the timer by invoking startTimer() again and you can use a different timing value or destination window if you wish. The timing events are sent to the associated window's handler as a WM_TIMER message.
Please note that if any WM_TIMER messages are still in the message queue at the time that stopTimer() is invoked then those queued messages will still be sent to the handler. Because of program latency timing messages will never be accurate. Also, the user will not be able to time very fine events because of the Windows-pegged event resolution. Additionally, Windows only releases WM_TIMER messages when the message queue buffer is otherwise empty so a very "busy" window may miss some timer events when a fresh timer rollover occurs but the previous timer event is still in the message queue. I don't know how many timers are allowed to exist at any one time but I think the fewer there are the better.

COMMON DIALOG WINDOWS
Common Dialog windows are predefined resources that can provide certain specialized functions in a simple to use way. Basically they are windows with controls in them and almost all the event processing is handled by Windows - from a programmers perspective this is an attractive attribute indeed! Examples of Common Dialogs are the "Open" and "Save as" windows that appear when a user wishes to graphically select a file name from disk to open or save a file. The Message Box is a special example of a Dialog window.

In ARWEN Common Dialog windows are Modal and never Modeless. An explanation of these terms is in order: A Modal window is one where the message loop handling of the owner application is suspended while the Modal window is in operation. A call is made to open the Modal window and doesn't return until the user closes the Modal window. An example of this is the Message Box window. The whole operation can be invoked in a single line which makes Modal Dialog windows very convenient. Although the normal message loop is interrupted with a Modal Dialog box ARWEN still enables the IDLE handler routine to keep operating. A Modeless window is a window that doesn't cause any interruption in the flow of the application message loop. The main window in an application is Modeless and any child windows would normally be too. ARWEN does not have an associated Resource Compiler that would enable it to fabricate Modeless Common Dialog windows and therefore major customisations to Dialog windows are not possible. However, minor customisations are possible for certain features of some Common Dialog windows when they are initialized. ARWEN allows access to these - see the specific calling routines for more details. Please note that MessageBox is a special dialog box not normally considered as part of the Common Dialog box group but I am showing it here since the implementation is essentially identical. Only one Common Dialog box can be open for operation at any one time with the exception of the Message Box dialog.

global function messageBox(sequence title, sequence text, object style)

This dialog box was copied from the Euphoria distribution files. Basically, all I have done is alter the parameters order so that the title of the window is first and then the actual text of the window is placed second. The customary sequence of parameters was defined long ago by the Win32 API but it didn't seem logical to me so I changed it. Do you think I should change it back? I also tidied up the constants and added WarnErr(sequence text) and FatalErr(sequence text) routines.

title
This is the text string that will be displayed in the title bar of the window. It could be something like Error or Warning etc..

text
This is the message text that will be displayed in the window itself. You must insert '\n' characters where you want the line breaks to occur. Windows will automatically resize the dialog box to encompass the message.

style
Can be any combination of the following predefined constants:
MB_APPLMODAL User must respond before doing something else
MB_SYSTEMMODAL All applications suspended until user responds
MB_TASKMODAL Similar to MB_APPLMODAL
MB_TOPMOST
MB_DEFAULT_DESKTOP_ONLY
MB_DEFBUTTON1 First button is default button
MB_DEFBUTTON2 Second button is default button
MB_DEFBUTTON3 Third button is default button
MB_DEFBUTTON4 Fourth button is default button
MB_OK Message box contains one push button: OK
MB_OKCANCEL Message box contains OK and Cancel
MB_ABORTRETRYIGNORE Abort, Retry, Ignore
MB_YESNOCANCEL Message box contains Yes, No, and Cancel
MB_YESNO Message box contains Yes and No
MB_RETRYCANCEL Message box contains Retry and Cancel
MB_USERICON -- What's this for?
MB_ICONASTERISK
MB_ICONERROR
MB_ICONEXCLAMATION Exclamation-point appears in the box
MB_ICONHAND Same as MB_ICONERROR
MB_ICONINFORMATION Lowercase letter i in a circle appears
MB_ICONQUESTION A question-mark icon appears
MB_ICONSTOP Same as MB_ICONERROR
MB_ICONWARNING Same as MB_ICONEXCLAMATION
MB_RIGHT Windows 95: The text is right-justified
MB_RTLREADING Windows 95: For Hebrew and Arabic systems
MB_SERVICE_NOTIFICATION Windows NT: The caller is a service
MB_SETFOREGROUND Message box becomes the foreground window
MB_HELP Windows 95: Help button generates help event

One of the following values will be returned when the MessageBox is closed:
IDERROR -- FAILURE (constant doesn't exist)
IDOK -- OK button was selected.
IDCANCEL -- Cancel button was selected.
IDABORT -- Abort button was selected.
IDRETRY -- Retry button was selected.
IDIGNORE -- Ignore button was selected.
IDYES -- Yes button was selected.
IDNO -- No button was selected.

global functions:
getOpenFileName(integer id, sequence fName, sequence filters, object flags)
getSaveFileName(integer id, sequence fName, sequence filters, object flags)

These Dialogs allows a user to navigate available disk drives to select a file to either 'open' or to 'save as'. Multiple files can be selected for getOpenFileName() by including the OFN_ALLOWMULTISELECT flag. This flag is ignored for getSaveFileName(). If the routine fails it will return NULL otherwise it returns the strings of the selection. If one file is selected the return value will be { "path\name" }, eg: { "c:\Euphoria\bin\ex.exe" }. If multiple files are selected then the return value will be { "path\", "nameFile1.ext", "namefile2.ext", .. }

id - ID of the owner window. You can have NULL in this field but the IDLE handler will not fire while the Dialog box is open.
fName - default name of the file to appear in the edit field of the Dialog.
filters - a sequence of paired strings in this form: {displayStr1, patternStr1, displayStr2, patternStr2...} eg, { "Text Files", "*.TXT", ... } To specify multiple filter patterns for a single display string, use a semicolon to separate the patterns, eg: "*.TXT;*.DOC;*.BAK" This field can contain a blank sequence if no filter patterns are required.
flags - any OR'ed combination of the following. Please note that other flags exist but are not supported by ARWEN. For a full description of each flag please refer to the Win32 API documentation.
OFN_ALLOWMULTISELECT -- User can select more than one file but, for some reason, old-style 'Open' dialogs have short directory names.
OFN_CREATEPROMPT
OFN_FILEMUSTEXIST
OFN_HIDEREADONLY
OFN_NOCHANGEDIR
OFN_NONETWORKBUTTON
OFN_NOVALIDATE
OFN_OVERWRITEPROMPT
OFN_PATHMUSTEXIST
OFN_READONLY
OFN_SHAREAWARE
OFN_SHOWHELP
If you need to separate the path, filename or file extension from the return results of these Dialog boxes then you can use extractPathAndName() and extractNameAndExtn().

global function browseForFolder(sequence message)

message refers to any text information that you wish to display when the window opens. If this parameter is an empty string then no text message will be shown except what is in the title bar which always is "Browse for Folder" (or language equivalent).

This dialog box is a modal window used to visually select a directory (aka folder). If the function is successfull it will return the fully qualified path to the folder otherwise it will return an empty string.



global function getChooseColor(integer id)

id is the window owner. If a non-Window control is specified the function will abort with a warning, otherwise a modal dialog-box is opened. If id is NULL then the dialog box will be modeless - just like another window. Be very careful if you want to try this last approach.

The Color dialog box enables the user to choose a predefined colour or specify their own. The function will return an integer specifying either a colour in RGB format or -1. Please note that the Red component occupies the lower 8 bits; the green component the next 8 bits and the Blue component the upper 8 bits.

In theory, the user could edit up to 16 custom colours that they might like to provide but if anyone wants this provision to be enabled then please contact me to discuss it.

 

global function choosefont() -- NOT YET IMPLEMENTED

global function pageSetup() -- NOT YET IMPLEMENTED

global function printDlg() -- NOT YET IMPLEMENTED

global function findText() -- NOT YET IMPLEMENTED, not sure if I want to anyway

global function replaceText() -- NOT YET IMPLEMENTED, not sure if I want to anyway

WINDOWS MESSAGES
The interpretation of the parameters, sent along with the message to the control handler, varies. Please see the Win32 API doc's for details. I have copied the comments directly from the API documentation and have edited them as necessary to account for any alterations to the native format.

WM_CHAR
The WM_CHAR message is posted to the window with the keyboard focus when a WM_KEYDOWN message is translated by the TranslateMessage() function.

wParam - the character code
lParam - the key data - see API documentation for details

WM_KEYDOWN (& WM_KEYUP) messages in connection with KEYBOARD_DELAYS are used to generate WM_CHAR messages. This and the other key messages can be intercepted as shown in one of the demo programs. If you need to get the ASCII value of a key then trap this message.

WM_KEYDOWN
The WM_KEYDOWN message is posted to the window with the keyboard focus when a nonsystem key is pressed. A nonsystem key is a key that is pressed when the ALT key is not pressed. This message and WM_KEYUP are often used complementarily and it may be that trap one of these messages you can ignore the other. I think that if you need to get the ASCII value of a key then trap WM_CHAR otherwise use WM_KEYDOWN or WM_KEYUP. Please note that the values of the key constants used for these messages are defined in the constants.ew file.

wParam - the virtual-key code
lParam - the key data - see API documentation for details

WM_KEYUP
The WM_KEYUP message is posted to the window with the keyboard focus when a nonsystem key is released. A nonsystem key is a key that is pressed when the ALT key is not pressed, or a keyboard key that is pressed when a window has the keyboard focus.

wParam - the virtual-key code
lParam - the key data - see API documentation for details

MOUSE MESSAGES
WM_LBUTTONDBLCLK Left mouse button has been doubleclicked
WM_LBUTTONDOWN Left mouse button has been pressed down
WM_LBUTTONUP Left mouse button has been released
WM_MBUTTONDBLCLK Middle mouse button blah, blah, blah
WM_MBUTTONDOWN
WM_MBUTTONUP
WM_RBUTTONDBLCLK Right mouse button blah, blah, blah
WM_RBUTTONDOWN
WM_RBUTTONUP

wParam - key flags which can be combinations of any of the following:
MK_CONTROL Set if the CTRL key is down.
MK_LBUTTON Set if the left mouse button is down.
MK_MBUTTON Set if the middle mouse button is down.
MK_RBUTTON Set if the right mouse button is down.
MK_SHIFT Set if the SHIFT key is down.

lParam is a sequence of the X & Y coordinates of the mouse point relative to the Client area of a window or control. Note: These values could be negative if the mouse is above or left of the window boundary. Double Clicking a mouse will actually generate 4 messages: BUTTONDOWN, BUTTONUP, BUTTONDBLCLK, and BUTTONUP. Mouse messages occur when the user has moved the mouse or pressed (or unpressed) a mouse button. These messages are usually sent to the controls that the mouse is directly over so you may be surprised that you can trap the movements of the mouse over, say, Push Buttons for example. The return value is ignored.

WM_COMMAND
This message is sent to the parent to indicate some action taken by the child, eg a button is clicked or menuitem is selected or an Accelerator key was fired. Use WM_COMMAND instead of WM_LBUTTONUP to detect click events for buttons. Although the message is sent to the parent, ARWEN will redirect it to the child's handler routine.

id - ID of the control that was clicked, Menu that was selected etc..
wParam - Notification code if id refers to a control (not a menu)
lParam - Acceleration flags, 1 for yes, 0 for no.

The notification code from the initial WndProc call is usually in the Hi-Word of wParam but ARWEN converts it prior to invoking the handler routine so the user can directly test for specific codes. The return value is ignored except when for accelerator generated WM_COMMANDs. In this case returning any value other than 0 will prevent the focus from being placed on the control associated with the Accelerator. NB: If that particular control is not a focussable one then the next available focus position will be sought.

WM_CLOSE
Sent to the Primary Window. Application is closing,

wParam - NULL
lParam - NULL

The window is about to close because the user has clicked the x button in the corner of the window (or something). You should run any clean up code at this point. If the return value is 1 then the close action will be aborted. Please do not return anything other than an integer from the handler because ARWEN will crash if a sequence is returned.

WM_HSCROLL
A horizontal scroll event has been triggered.

wParam - Position of Scroll box or Track Bar pointer
lParam - NULL

If this message was triggered by a Scroll bar builtin into a Window then the message will be sent to that Window's handler otherwise it is sent to the control's handler. TrackBars will also cause Scroll events to happen. The return value is ignored.

WM_VSCROLL
A vertical scroll event has been triggered.

wParam - Position of Scroll box or Track Bar pointer
lParam - NULL

Comments identical to WM_HSCROLL above.

WM_SIZE
Window size has changed.

wParam - Size flag which is one of the following:
SIZE_MAXHIDE Message is sent to all pop-up windows when some other window is maximized.
SIZE_MAXIMIZED Window has been maximized.
SIZE_MAXSHOW Message is sent to all pop-up windows when some other window has been restored to its former size.
SIZE_MINIMIZED Window has been minimized.
SIZE_RESTORED Window has been resized, but neither the SIZE_MINIMIZED nor SIZE_MAXIMIZED value applies.

lParam - A sequence containing the Top_Left and Bottom_Right coordinates of the Client area, ie: {left, top, right, bottom}. Please note that these coordinates may not always start at (0,0) since Windows doesn't exclude items such as ToolBars from what it thinks the Client area is.

This message is sent after the window has changed. It is recommended that WM_SIZE messages only be handled for actual windows. If Toolbars, StatusBars or managed Scroll bars exist and are visible then ARWEN will automatically resize & reposition these. The return value is ignored.

WM_SIZING
Window is being resized by the user dragging a border (or size_box).

wParam - Resizing flag which indicates which border element the mouse is dragging.
WMSZ_BOTTOM Bottom edge
WMSZ_BOTTOMLEFT Bottom-left corner
WMSZ_BOTTOMRIGHT Bottom-right corner
WMSZ_LEFT Left edge
WMSZ_RIGHT Right edge
WMSZ_TOP Top edge
WMSZ_TOPLEFT Top-left corner
WMSZ_TOPRIGHT Top-right corner

lParam - A sequence that contains the screen coordinates of the drag rectangle.

The WM_SIZING message is sent to a window that the user is currently resizing. By processing this message, an application can monitor the size and position of the drag rectangle and, if needed, change its size or position. This could be useful if the user wishes to impose a minimum or maximum size of the Window dimensions. Another possibility is that the position of the window could be changed so the operation would, in effect, be moving the window as the border is being dragged. Please note that the user can still maximize or minimize a window. If the user returns a sequence then ARWEN will cause the new position and/or dimensions contained therein to be reflected in the Window. If no action is to be taken then the user should return 0.

WM_MOVING
Window is being moved by the user dragging the title bar. Please refer to the WM_SIZING message for specific details since both messages are identical in behaviour & form.

WM_TIMER
A timer event has occurred.

wParam - NULL
lParam - NULL

The return value is ignored.

WM_PAINT
Windows wants to repaint some part of the window.

wParam - hDC of the window to repaint
lParam - A sequence containing the top-left & bottom-right coords of the bounding rectangle.

User is responsible for repainting the rectangle to maintain any persistent graphics. It is recommended that WM_PAINT events only be handled for actual windows. The return value is ignored.

WM_NOTIFY
Parent window is informed that an event has occurred in a child control or that the control requires some kind of information.

wParam - Notification code -- See API for details
lParam - Pointer to an NMHDR structure

This message is currently only trapped for TabControls where one of the TabItems is clicked. If the notification code is TCN_SELCHANGING then returning 1 from ther handler will have the effect of aborting the tab change otherwise the routine will have no effect.

WM_CTLCOLOREDIT
Windows wants to paint the background colour on an edit control and the user has an opportunity to change it from the default colors.

wParam - handle of display context
lParam - handle of static control.

If an application processes this message, it must return the handle of a brush. Windows uses the brush to paint the background of the edit control. A return value of 0 ignored and the default colours are used. Please note that only integers/atoms should be returned from this message.

STRUCTURES
Many Win32 functions require the use of structures and I have developed my own system for creating & accessing structures. It may not be pretty OR safe but it is fast. A necessary part of using structures is allocating & releasing small blocks of memory. This is a (relatively) time consuming exercise. On my P150 I get 14.3 seconds using allocate() & free() for 1,000,000 small blocks of memory ~ 70k/s. However, where possible I am directly reusing the structure memory for an associated function within a management routine. This can significantly boost the performance of the routine since the overhead of extra allocate() & free() calls is avoided. I have now started using a permanently allocated RAM scratchpad for some functions which (i) could be called a lot and (ii) I would not expect to be interrupted by another call which would use (therefore corrupt) the same memory. In some instances I am getting 1.26 seconds ~ 790k/s. In the case of allocating & pokeing null terminated text strings the result is 3.42 seconds ~ 290k/s and is 4 times faster than allocate_string() & free() but, of course, without any safety barrier (other than the size of the buffer) to prevent corruption of the referenced memory.

Although only a small number of structures (approx. 25) have been defined in structures.ew I think the library is still quite capable for many purposes. I will add more structure definitions as time goes on and the need for them becomes apparent.

Structure definitions can ONLY be added to this file since the functions to create them are local to that include file. Additional structures can be defined in 2 easy steps: --
step 1) define the name of the structure as a constant. I always use the Win32 API names.eg:

RECT = new_struct()

step 2) define the individual elements of the structure as constants. Note the Structure name is prefixed to the member names. A bit verbose but safe.

constant
RECT_left = struc(C_LONG),
RECT_top = struc(C_LONG),
RECT_right = struc(C_LONG),
RECT_bottom = struc(C_LONG)

The parameter in the struc() function can be ANY predefined structure name or the usual structure constants as defined in dll.e Now, the structure name (RECT) has become an index to my structure list but the elements are actually displacements relative to the first element. ONLY these displacements can be used later on when accessing the structure.

To ascertain the size of the structure in bytes simply call sizeof(STRUCTNAME), eg:

lpRect = allocate( sizeof( RECT ) )

This size value is needed when allocating memory to poke a structure into memory or alternatively when reserving space for a Win32 API function to use. Later structures may now be defined incorporating the earlier ones, eg:

-- Define PAINTSTRUCT Structure
PAINTSTRUCT = new_struct(),
PAINTSTRUCT_hdc = struc(C_LONG),
PAINTSTRUCT_fErase = struc(C_SHORT),
PAINTSTRUCT_rcPaint = struc(RECT), -- ***** note the use of RECT here!!
PAINTSTRUCT_fRestore = struc(C_SHORT),
PAINTSTRUCT_rgbReserved = struc(anySize(32)),

Please ensure that the minor structures are completed BEFORE incorporating them into a larger structure. You will note a function called anySize() which can be used to reserve an arbitrary number of bytes in the structure; 32 in the example above. Access to structures is simply a matter of peeking or pokeing the desired structure member, eg:

ps = allocate( sizeof(PAINTSTRUCT) )
-- do some code
rFlag = peek4u(ps + PAINTSTRUCT_fRestore)
-- rFlag now has the value of the Restore flag

PLEASE NOTE that although the size in bytes of each structure member seems to be recorded in the structure list, in reality no error checking is done on the size of the peeked or poked value so the user is cautioned to be very careful when accessing the structure. Another trap is the fact that where minor structures are incorporated in other structures then the user has to allow for accessing the deeper structure, ie if the user wanted to access the RECT_top member of the RECT structure embedded in PAINTSTRUCT structure then they would have to do so explicitly, eg:

top = peek4u(ps + PAINTSTRUCT_rcPaint + RECT_top )

Care must be made when accessing the, by now, somewhat rare WORD (16-bits) fields since Euphoria has no native access to this type. But really, how difficult would it be to add your own peek2() functions. If you use my editor then it is a cinch to allow syntax coluring to make it quite easy to spot typing errors for those new routines that you might make. [Update - I have now added poke2(), peek2s() & peek2u() to structures.ew] There is a distinct lack of error checking in the structures section because I am chiefly concerned (some might say obsessed) with performance. By using verbose member designations I have had little problem accessing the correct elements.

TOOLBAR RESOURCE
I have developed a custom way of creating tool bar icons (really bitmaps) from a large bitmap. Currently the images have to be all the same size but if anyone would care to recommend anything different then I am open to change it.

GLOBAL ROUTINES
Er, I'll document these at a later stage...maybe never..

GLOBAL ATOMS
Several groups of global atoms are visible to the application program:
1) All the Win32 constants that have been defined in the include files
2) Constant names of all the linked Win32 functions - perhaps you may wish to directly call a Win32 function, who knows?
3)
void - Strictly speaking void is not an atom; it is declared as a global object. Many library routines will return some value but most of these can be ignored. void is a convenient variable to assign the return value. It will save having to create yet another scratch variable. Just be careful that you are not tempted to read the value assigned to void since it is being constantly overwritten.
4) UNDEFINED - This a flag used to represent an undefined state for some of the vasriables used in the library. You'd use it to dis-associate controls from their handlers.
5) IDLE - Used to enable a routine to run continuously (looping).
6) True & False - Boolean values as expected. Please note that Win32 functions do not always represent failure as 0 or success as 1, however, many functions in ARWEN do return boolean values to indicate the outcome. Please consult the documentation for specific details.
7) CLIPBOARD - Used to access the clipboard text functions when invoking getText() or setText().

THINGS THAT STILL NEED TO BE DONE..
Flat tool bar support
finish Menu support (bitmaps..other?)
emulate modality *fully*
icons/bitmap support
setting fonts
some graphic primitives
improve documentation
improve intuitiveness for activating a TabItem and retrieving the open tab
must invalidate the rect that covers the main window when another window is closed. Could use MapWindowPoints() then RedrawWindow()
tidy up DC arrangement - does it need it?
purge the memory allocation code (in arwen.ew) so that buffer overflows can be prevented
finish adding image lists - make separate include file make icon/bitmap references as indexes rather than handles in control creation but bitmaps can be handles otherwise. Ensure the imagelists are destroyed as appropriate Perhaps have some default icons in the base imagelists....
destroy() should properly deal with all parent controls and their children - in particular Windows & toolbars

BUGS
1) Execution of IDLE code stalls when the Message Box dialog is opened. I really should write my own custom Message window. Other Common Dialog windows are not affected though execution of the IDLE handler is interrupted a little bit when a Dialog window is opened.

2) If a handler routine for a Status Bar invokes setText() to draw some text on itself and the call is "inlined" (ie, not part of a message trap) then a fatal error occurs. This doesn't happen with other controls I tested. I think the reason is that when setText() is invoked it sends a message to the Status bar handler which means the handler indirectly calls itself in this situation.