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.