Questions
|
Why does FOX look so much like Windows?
|
FOX looks much like Windows in part because of historical reasons, and
in part because it is intentional. Having FOX look similar to Windows
means the users of a FOX application will be able to bring to bear their
prior experience on Windows, and therefore they will be able to be productive
much quicker.
But let there be no mistake about it:- for software developers, FOX
looks very differently internally.
Has FOX been ported to the Apple Macintosh?
|
Basically, if you have installed Apple's own X Server, FOX can be compiled and
run on OS-X. Only a few environment variables need to be modified (the usual bunch,
see INSTALL for more detailed information).
A native port running on top of OS-X w/o the need of an X Server may be a project
that will be undertaken at some time in the future; however, please be aware that
since the loop and feel is completely determined by FOX's own implementation, a
natively ported FOX application will look identical to the one using the X Server.
Which Systems are supported by FOX?
|
There are two main ports of FOX:- UNIX/X11 and MS-Windows. Specifically,
FOX is known to work on the following systems:
- Linux.
Any version of Linux should work, on any hardware. FOX is being
developed on LINUX.
- FreeBSD.
FreeBSD is reported to work.
- OpenBSD.
packages are also available.
- SGI IRIX.
SGI systems with IRIX 5.3 and up. It can be compiled with GCC or
SGI's MIPS Pro C++ compiler.
- SunOS/Solaris.
Either Solaris or SunOS. Compiled with GCC or Sun Workshop C++.
- HP-UX.
Tested with GCC under HP-UX 9.x and 10.x.
- IBM AIX.
Tested with GCC under AIX 4.2.
- DEC Alpha/COMPAQ Tru64.
Tested with GCC under Digital Unix 4.0.
- Other UNIX Systems.
If you have a reasonable C++ compiler, UNIX, X11, chances are very good you can get
FOX to work.
- Microsoft Windows 2000, Windows NT 4.0, Windows 95,
Windows 98.
Compile either with Visual C++ 6.0,
Borland C++ Builder,
CYGWIN32, MINGWIN32,
and IBM VisualAge C++ for Windows.
Older versions of Visual C++ will work, but you must create your own projects
in that case.
These are the systems we know about. Since FOX uses GNU autoconfigure, chances
are good that most UNIX/X11 combinations can be made to work.
If you're running FOX under a system not mentioned above, please drop
me a line so we can add it to the list.
Is FOX `64-bit clean'?
|
With the arrival of 64-bit PC's, this is a pertinent question. The answer is
yes. FOX is currently being compiled on several 64-bit architectures, such
as HP/Compaq/DEC Alpha, HP-PA, SUN UltraSparc, and SGI MIPS R10000. More recently,
porting to Intel Itanium and AMD Opteron has been performed. We have reports from
people who have compiled on Windows-XP 64.
While coding of FOX itself has been prepared for the coming 64-bit era, the same may
not be true for your own application code. A few guidelines:
- Most systems (except Microsoft) follow the ILP32 vs LP64 model,
i.e. in 32-bit mode integers, longs, and pointers are 32-bits, and in 64-bit mode longs and
pointers become 64-bits while integers stay 32-bit. Microsoft seems to continue with
long equal to 32-bit even for 64-bit CPU's.
If you use the FOX types like FXlong and FXint the sizes are going to be typedef-ed
properly according to the LP64 model.
- On some 64-bit compilers, a double-cast may be needed for casting pointers to an
int via long. For this purpose, FOX defines a signed and unsigned integer
type whose size is big enough to fit a pointer.
These types are called FXival and FXuval. Note that FXival and FXuval
are defined to be the size of a pointer, and so these are not fixed-size types to be
used for streaming!
For portable code you will need to cast void* to FXint by way of FXival or FXuval, as
in:
intval=(FXint)(FXival)ptr;
Conversely, its a good idea to cast from FXint to void* by way of FXival or FXuval
to get predictable zero-extended or sign-extended results:
ptr=(void*)(FXuval)intval;
- You should use the FXint, FXlong, etc. types when serializing data using FXStream.
The types FXint, FXlong, etc. are defined to be a certain size and are the same on
all architectures; otherwise binary data will not be compatible between platforms.
Of course all the usual caveats apply, like using sizeof() instead of hard-coded
assumptions about sizes; the best practice of developing portable code is to develop it
on a number different platforms from the very beginning.
How do I write portable code?
|
FOX is a platform-independent toolkit (currently, it supports many architectures and
compilers), but a few caveats still apply if your own code is going to have
to be ported.
A few guidelines which I found useful in my own development practices can make
the process very painless:
- Don't include any platform-dependent libraries (e.g. no "Windows.h"
or "X11.h". Just your regular stdio, stdlib and so on (those are
C/C++-language standard libraries, and pretty much the same on most
machines except for some minor nitty gritty stuff.
- Of course, you will use FOX, and while your own code isn't platform
dependent, FOX itself is. So you *will* have to link your application
with various platform-dependent libraries that FOX itself uses.
- You can safely assume sizes of FXchar, FXshort, FXint, FXlong, FXfloat
and FXdouble to be the same on all platforms.
However, int, long, etc. are NOT guaranteed to be
a certain size. This is important when saving loading binary data
files (e.g. using FOX's FXStream class).
Also, there is byte-order. This is also platform-dependent. FXStream
allows for byte swapping during I/O.
The symbol FOX_BIGENDIAN is set 1 on big-endian machines (e.g. SGI,
PPC) and 0 for little-endian machines (Intel, Alpha).
- To be 64-bit future-proof, you *must* use double-cast to- and from
pointers. see above. Just to be sure.
- Try avoid very sophisticated C++ features (this means most of
STL). Code that does a lot of STL doesn't port one iota's worth,
at best it compiles with thousands of warnings [some people are
going to flame me for this, but its true!].
- Use FOX macros like FXMALLOC(), FXFREE() and so on. They are
implemented to behave EXACTLY the same on all systems (even though
underlying malloc() and realloc() library functions don't always
treat the corner cases properly.
- Use FXTRACE(()) for debugging; use FXASSERT for asserts. Use fxwarning
fxerror() and fxmessage() for messages to the console (on Windows, you
don't have a console, and printf() will crash!!). These functions "do
the right thing" on the various platforms.
- Don't try to do your own filename manipulation routines. Use the functions
in the FXFile namespace. These functions properly handle drive letters,
Windows Uniform Naming Convention, and so on. They also know about
forward and backward slashes and such stuff. Use the PATHSEP, ISPATHSEP
etc. macros to check for path separation characters.
- Do not assume characters are signed or unsigned; FXuchar is always
unsigned, however, FXchar may be either.
- When opening a binary file, don't forget fopen("name","rb") instead of
fopen("name","r"). On UNIX there is no difference, but on Windows, forgetting
the "b" will cause "\r\n" to be translated to "\n", corrupting the data stream.
Why do I get an `illegal icon specified' message when I change some Widget's icon?
|
Basically, an Icon [Widget, Image, Font, etc] comprises a part which lives in your
program [the client-side], and a part which lives in the X-Server or GDI subsystem
[the server-side].
The C++ constructor of an object only builds the part in the client side. The server side part of the
Icon [Widget, Image, etc] is realized when you call create() on it.
For convenience, all reachable resources may be created with a single call to FXApp::create().
The call to FXApp::create() traverses the entire Widget tree and creates all Windows, Icons,
and other resources that are needed to realize that Widget tree on the screen.
The reasons for all this are:
- Since all FOX classes may be subclassed, it cannot be
assumed that after an object's ctor has run that the object has been fully constructed;
after all, you may have subclassed it.
- It will also be important later on when the GUI itself will be subject to serialization.
As we can not serialize server-resident resources, we need a two-step process of construction
[via deserialization] and creation [from a single call to FXApp::create()].
Because of this, when you construct an FXIcon later on, then you need to call FXIcon::create()
manually, as the icon was not a part of the Widget tree at the time the Widget tree was first realized.
Compiling FOX as a DLL under VC++ gives me a unresolved external symbol?
|
If you build a project under VC++ to compile a FOX application, do not forget to specify
-DFOXDLL on the compiler command line. Without it, you will get an error like:
error LNK2001: unresolved external symbol "public: static struct FXMetaClass const FXApp::metaClass"
(?metaClass@FXApp@@2UFXMetaClass@@B). Or words to that effect.
Of course, you can also build FOX as a static library, in which case there is no problem.
When do I call flush(), forceRefresh(), refresh() and update() after a change?
|
Under normal circumstances, the display on the screen is kept up-to-date automatically.
However, FOX uses a lazy screen refreshing technique. The lazy technique allows
your callback routine to make lots of changes in the GUI, then update the screen in one
fell swoop. This obviates the need that some other toolkits have for freeze/thaw
API's.
There are several aspects to this: repainting the screen, i.e. processing expose or repaint events,
performing layout computations, and performing GUI updating.
- Repaint Events are piled up until there are no further events to be processed.
In fact, repaint events are not simply saved but are conglomerated into larger and larger
rectangles, so as to minimize the number of times that repainting is actually done (it actually
also minimizes the area which is repainted).
- Layout Reconciliation is also delayed. No layout is performed until returning
to the event loop, in fact. Like repainting, layout is delayed until no more events are
available.
- GUI Updating is also delayed until there are no more events in the queue.
The order in which these are performed are repaints, layout, and gui-updates.
Contrary to intuition, delaying the expensive operations such as repainting instead of doing them
right away is actually faster.
Sometimes, you want to force the display to refresh without returning from a callback; this
can be effected with the following routines:
- FXApp::flush() will flush all drawing commands to the display.
In other words, it will cause all drawing commands to be executed eventually.
Note that flush() is moderately expensive as it causes synchronization between the display
and the program.
- FXApp::forceRefresh() will perform a layout reconciliation, followed by a GUI update.
Since this may lead to more expose events, you typically follow a call to forceRefresh() with
a call to flush(). Note that forceRefresh() is a very expensive operation.
- FXApp::refresh() is similar to forceRefresh() in that it causes a layout and a GUI update to
be performed. The difference is that refresh() will not actually perform the operation but
simply cause the system to perform layout/gui-update later, after returning to the event loop.
The call to refresh() is very cheap.
- FXWindow::update() marks the window, or part of the window as dirty. This causes the window
to receive an expose event at some point in the future.
The rectangles marked with update() as dirty may be conglomerated into a few big rectangles.
No repainting is actually performed until returning to the event loop or calling flush().
Since no drawing is performed, a call to update() is fairly cheap.
- FXWindow::repaint() performs the opposite of update(). It paints the window, or part of
the window by issuing SEL_PAINT messages if necessary.
It works by first pulls all outstanding expose events from the X server, compositing
the dirty rectangles. Then, it processes all dirty rectangles pertaining to the given
window; if a rectangle is passed, it only processes the dirty rectangles overlapping with the
given rectangle.
Note that repaint() should NOT be called while performing a painting operation as it would
lead to multiple locks of the device context FXDCWindow.
- FXApp::repaint(), does the same as FXWindow::repaint(), except FXApp::repaint()
causes ALL windows to process their backlogged SEL_PAINT messages.
- FXWindow::recalc() marks the widget, and all of its parents, as dirty, i.e. in need of
layout. Under normal circumstances, layout is only called when the size of a widget has changed;
however, there are often other reasons why layout is needed. In such cases, calling recalc()
will ultimately cause a layout to happen also. The entire chain from the widget to its top
most parent must be marked this way, otherwise (as there may have been no size changes), the
layout process will stop. A few widgets will overload recalc() to cause additional layout
computations to happen (e.g. computing content size inside a scrolled view of a list).
How does layout work, exactly?
|
Automatic layout is a very usuful feature in FOX. It allows automatic placement of
widgets in the desired arrangement without explicitly placing each widget in terms
of position and size. Thus, changes in widget's contents, font, and language binding
can be accomodated with ease.
But automatic placement takes a bit of getting used to, especially for Windows developers
who are not accustomed to the concept (many UNIX GUI systems such as Xt and Motif have
similar layout facilities so UNIX programmers tend to be more familiar with the idea).
Composite widgets may contain one or more child widgets. These child widgets could be
simple controls, like Buttons, but also other Composite widgets. Thus, layout is inherently
a recursive process.
Layout of a widget tree is determined by the following:
- The arrangement pattern. For example, a Matrix layout manager arranges the children
in rows and columns; a HorizontalFrame arranges its children side by side, and a Packer
arranges them against the sides of the interior.
- The packing flags. A layout manager has certain flags which apply to the layout
of all of its children. For example the flag PACK_UNIFORM_WIDTH causes each child to
be made the same width.
- The layout hints. Each child has certain layout flags which affect the way the
layout manager places that child; an example is the LAYOUT_FIX_WIDTH flag which tells
the layout manager of that child that the child wants to keep its initially assigned
width instead of its minimum (default) width.
Not all layout hints are observed by the layout manager; certain arrangements interpret
certain hints, and ignore others. For instance the Switcher layout manager places
all children on top of each other, and makes them all the same size.
- Other information, such as the interior padding between the edges of the layout
manager and its children, spacing between children, and other information like
the number of rows and columns in a Matrix layout manager, or which child is on top
in a Switcher layout manager, and so on.
Layout is a recursive process, proceeding from the top down. A layout recalculation
is needed under the following circumstances:
- The widget's size has changed in response to position() or resize().
- The widget has been marked as dirty by means of recalc().
- In a few cases, layout() is called directly.
Because layout involves a lot of calculations, its quite expensive; we therefore try
to perform it as infrequently as possible, and to stop the layout recursion as soon as we
can.
The former is implemented by performing the layout only when there's nothing better to
do, during idle time when there are no events demanding our immediate attention.
The latter is done by stopping the recursion when we hit a widget that already has the right
size and is not marked as dirty.
This makes layout pretty fast (close to real-time when interactively resizing windows).
Only a handful of API's are responsible for the whole layout process:
- getDefaultWidth() and getDefaultHeight(). These API's measure the minimum size of the
widget. For simple controls such as Buttons the implementation consists of simply adding
up the size of the icon, caption, and surrounding borders.
For layout managers however, it is more complex:- the size of the layout manager depends
on the arrangement, and the size of the children, and needs to take into account the
packing flags and layout hints as well.
- getWidthForHeight() and getHeightForWidth(). These measure the width of a widget
in terms of its height, and the height of a widget given its width, respectively.
In order for getWidthForHeight() to work, the height has to be known in advance.
Because of these restrictions, these functions are basically only called from top level
windows, as top level windows are given a size directly by the user and therefore the
required information is known.
- position(). This API physcally moves a child's to its new location, and if
the child's size was changed or the child was marked as dirty, recursively invokes
layout() on the child.
- layout(). This is the workhorse of the layout system. The typical implementation in
a layout manager loops over all the children, applying each child's layout hints and
default size as computed by getDefaultWidth() and getDefaultHeight(), and then placing
each child accordingly by calling position(). Note that by calling position(), the
child's layout() may in turn be called!
When I construct a window at the beginning it works but when I construct it later it doesn't
|
About numbering Message ID's.
|
When deriving classes from FOX Widgets such as FXDialogBox, make sure you're messages
are numbered so as to not conflict with those of the base classes.
The most simple way is to continue numbering from where the base class left of; I
suggest the following C++ trick:
class MyDialog : public FXDialogBox {
...
enum{
ID_CLICKED_YES=FXDialogBox::ID_LAST,
ID_CLICKED_NO,
ID_CLICKED_OK,
ID_CLICKED_CANCEL,
ID_CLICKED_QUIT,
ID_CLICKED_SAVE,
ID_LAST
};
...
};
As you see, the implementor of the base class can insert additional message ID's but the
numbering is automatically kept straight by the compiler. Also, if you're own class
is being derived from then this derived class can start counting from MyDialog::ID_LAST
and not worry about any messages being inserted into MyDialog.
Why Support GIF!
|
The compression technique in GIF was patented by UNISYS. In 2003, this patent expires
and using GIFs no longer presents a problem; until that time FOX does not support
compressed output, only decompression.
From what I've read,
LZW decompression is not subject to the patent [hence ability gzip support for the
old ``compressed'' files].
I feel that there is therefore no need to remove the FOX support for GIF icons/images, and
therefore any existing investment should you have in large icon collections would be
protected.
Should you still harbor any qualms about using GIF's in your project, you could of
course always use BMP icons. However, GIF icons appear to be typically about half
the size of BMP icons.
Note that the LZW patent will expire soon, and so compressed GIF support will therefore
be reinstated.
Nevertheless, software patents are very bad for software developers, particularly since
so many completely trivial ideas seem to slip through the patent system.
Failing to delete menu panes in owner's destructor.
|
When you create a MenuPane, the pointer passed in to MenuPane is the owner
of that MenuPane, which is typically a MainWindow or DialogBox. This causes the
MenuPane to stay on top of the owner window.
When adding a MenuCommand in a MenuPane, the MenuCommand will obtain the owner
of the MenuPane and install its accelerator in the owner's accelerator table.
This is necessary, as the accelerator is to be effective without the MenuPane
being having been popped up.
When the MenuCommand is destroyed, it tries to remove this accelerator again
by obtaining the MenuPane's owner and removing the accelerator from the owner's
accelerator table. Should it be the case that the owner of the MenuPane had
already been deleted, an access to a non-existing memory location (segmentation
fault) will likely occur.
Thus, it is necessary for the owner of a MenuPane to be still in existence when
the MenuPane itself is destroyed. So when the owner of a MenuPane is destroyed, it
must make sure the MenuPane is destroyed before its destructor completes:- otherwise,
the MenuPane would refer to a non-existent owner.
As a general rule, shared resources such as Icons, Fonts, Cursors, Bitmaps, and
also MenuPanes, must be explicitly deleted by the widgets that owns them.
How does FXStream serialize an FXObject?
|
When you serialize a pointer to an object, like for example:
// Declarations
FXStream stream;
FXDocument *document;
// Serialize
stream.open(FXStreamSave);
stream << document;
stream.close();
// Deserialize
stream.open(FXStreamLoad);
stream >> document;
stream.close();
What really happens when you serialize a pointer to an object is the following:
- stream checks an internal hash table to see if document has been serialized before.
- if it has been serialized before we just save a reference number into the stream,
and we're done.
- if document was not encountered before, stream saves the classname.
- then, the document->save(stream) is called to save the object's contents [member data]
into the stream.
When you deserialize an object is:
- stream loads an item from the stream.
- if the item represents a reference number, then we must have loaded the document
previously; using an internal lookup table, stream maps reference number to the
memory address where document was loaded, and returns this address.
- if the item is a classname, then stream calls FXMetaClass::getMetaClassFromName(classname)
to obtain the metaclass, and calls metaclass->makeInstance() to properly manufacture an object.
- then, stream calls document->load(stream) to pull in the object's contents.
Sometimes, a special container object is referred by other objects, but should not itself be serialized.
In this case, you may want to use the constructor:
FXStream stream(container);
instead. This will add the pointer container to the internal table of stream, so
that any subsequent encounter of the same pointer value will generate a reference number only.
Why did FOX choose the message-map based callback paradigm
|
There are several different mechanisms to connect Widgets, the sources of events, and
their targets, or the application code that you write.
I have evaluated several different callback mechanisms, each have their different
strengths and weaknesses.
- Function pointer [Motif, Xt].
- Very dissatisfying for C++ programs, as it is not object-oriented.
- Extra callback parameter [call_data] is not type safe.
- This is mostly interesting for procedural programming styles, e.g. using C.
- Hard to serialize (or save to file) the connectivity.
- Message handling with fixed routing [Microsoft Foundation Classes, Borland C++ Builder].
- Can bind source and target together at run time.
- Need fixed message routing scheme, since there is no specific target.
- Need to keep the messages globally unique (because of the fixed message routing scheme).
- Message-delegation [forwarding of messages to another party] is easy.
- GUI widgets can not receive messages, only sent them (because of a fixed message routine scheme).
- May be not type-safe.
- Easy to serialize the connectivity.
- Signal/Slot [Qt (Preprocessor), C++ templates (Gtk--, I believe)].
- A special preprocessor does not seem very elegant to me [Qt].
- A template-based signal/slot system is elegant from a theoretical point of view.
- Templates are instantiated at compile time. This means it's going to be difficult to
hook up objects which are only known at run time [like e.g. loading a custom control from a DLL].
- To connect a message source and a target, you actually construct a
connector object that is parameterized by the target; you can not
do this unless you know the type of the target [This is because
pointer-to-member-of-class types can only be downcast!].
- Hard to serialize, because a pointer-to-member-of-class contains a function-pointer
when the member is not a virtual.
- Message handling with specific target [FOX, NeXTSTEP (using Objective-C of course)].
- You can connect at run-time, because connectivity does not involve compile-time
code generation like with a signal-slot mechanism.
Run-time connectivity is needed because you may load objects dynamically from a DLL,
from deserialization, or you may want to write an GUI-Builder which interactively
connects things up.
- There is no need for a globally unique list of message ID's. The message is addressed
to a specific target, and the same message-ID can be reused by another target for another
purpose.
- Widgets may receive messages as well as send them.
This is very important for component-based programming paradigms.
It is also important to note that this allows for ``glue-less'' programming; for
example, a message ID_HIDE to any Control will hide the Control.
- No special preprocessor is needed. The compiler automatically keeps the message-ID's
straight [see above].
- It is not type safe, in the sense that if you do need to interpret
the void*, you would need to cast. Note however that in the vast majority
of cases, the void* argument is not used; when it is used, the use is pretty
consistent:- what do you think the void* refers to when the message-id is
ID_SETINTVALUE?
- In the case of NeXTSTEP, the argument signature was a part of the message, which
means that for an object to implement the message, the signature had to match up; this
was type-safe while at the same time very flexible.
Alas, FOX was written in C++, not Objective-C.
- You connect things simply by passing the target-pointer in the constructor
when you create a widget.
- FOX supports message delegation very easily.
- You can actually turn FOX into a type of template-based signal/slot
system of you make FXObject-derived connectors!
As you see, FOX's message handling system may not be type safe, but it is
very compact, allows for run-time connectivity, is serializable,
and favors component-oriented development.
Were FOX written in Objective-C, one could achieve the goal of type-safety as well; C++
clearly limits our choices.
Why does a AUTOGRAY disable, but not enable the Button?
|
AUTOGRAY and AUTOHIDE are very useful features when messages are being delegated
around, like for example in an Multiple Document Interface [MDI] application.
In an MDI application, the target which actually ends up handling the message may be different
from one moment to the next.
When no target handles the message (e.g. when all FXMDIChild windows have been deleted), an
FXButton which is set to BUTTON_AUTOGRAY will be
disabled automatically. When there is a target, this target should enable or disable the button
as appropriate. The FXButton does not automatically enable itself when there is
a target that handles the message, as it is not necessarily the case that the button should be
enabled when it does.
I get compiler errors in Visual C++ when inclusing FXArray or FXElement
|
FOX uses the placement version of the C++ new operator. Declarations for this
operator may be made available to your program by:
#include < new >
just before including FXArray and FXElement.
FXArray and FXElement are not automatically included into fx.h because these files rely
on a proper template implementation of your compiler; also, FOX widgets do not need these headers.
My Motif application complains with messages about failing to allocate colors
|
This typically happens on PseudoColor systems, i.e. systems which use a colormap or color palette.
Even many high end SGI systems run the Motif GUI in PseudoColor mode [these systems support multiple
hardware colormaps].
FOX normally allocates about half of the available colormap [125 colors, to be exact]. Under normal
circumstances, this leaves plenty of colors for other applications.
However, sometimes of course it can happen that other X11 applications require more colors than are
available.
Fortunately, FOX can be told to use a different number of colors. There are several ways to do
this:
On some machines, you may be able to switch the video hardware into a higher color resolution,
and if this is possible, it may be by far the best solution.
File fxpngio.cpp does not compile on IRIX 6.5
|
FOX uses GNU autoconfigure to determine the whereabouts of
various files. It so happens that IRIX 6.5 ships with an older release of the PNG library.
You can do two things:
- Re-run configure as configure --disable-png to disable PNG image file support.
FOX will be fully functional, except that PNG image file support is stubbed out.
- Obtain the PNG library libpng version 1.05 or better, and install this on your machine.
You can find PNG on: http://www.libpng.org/pub/png/.
If you choose the latter, you will of course have to make sure the configure script is
able to locate the new library; how this is done depends on where it is installed.
Developing FOX Applications under Windows.
|
Developing FOX applications under Windows warrants a lot of extra information. You can
find this here.
Why are there various flavors of running an event loop?
|
FOX applications are event driven applications. All FOX applications therefore spend almost all
their time in an event loop, waiting for events [such as keyboard and mouse events] from a user.
Depending on the situation, there are several types of event loops possible:
- FXApp::run(). This is the main event loop and it is entered when you
start your program, and does not exit until you call FXApp::exit(), or the
application object receives the ID_QUIT message.
Typically, after returning from FXApp::run() your program will exit.
- FXApp::runModalFor(window). You enter this event loop to run a modal dialog.
A modal dialog is a dialog which will block any interaction with any other window of the
program except for the indicated dialog window, until the modal event loop is exited.
FXApp::runModalFor() is a recursive invocation of the event loop, and it will not return until
FXApp::stopModal(window,code) is called. The return code is passed along and will be
returned by FXApp::runModalFor().
- FXApp::runModalWhileShown(window). This routine is a variant of FXApp::runModalFor()
which returns as soon as the window is no longer visible, or until terminated by FXApp::stopModal().
- FXApp::runUntil(condition). This routine enters a recursive event loop, passing all
events normally. The event loop returns as soon as the variable condition is set no a
non-zero value.
- FXApp::runWhileEvents(). This routine enters a recursive event loop, but returns
as soon as no current events are outstanding in the event queue. This can be used to catch up
with the event stream during processing of some long computation, and then resume the computation
as soon as all events have been processed.
- FXApp::runOneEvent(). This function processes one single event and then returns.
- FXApp::peekEvent(). This tests if any events are present on the event queue, and
returns immediately with TRUE if there are, or FALSE otherwise.
Recursive invocations of the event loop are very useful, because they allow you to temporarily
resume processing of events without returning from your message handler.
The runModalFor() is especially useful if your message handler needs to display a temporary
dialog box, acquire some information from a user, and then continue processing the user
input all without returning to the main event loop.
Why do I need to declare a default contructor in my classes?
|
The FXObject-derived classes need to have the FXDECLARE() macro in the header (.h) file and the
FXIMPLEMENT() macro in the implementation (.cpp) file. The FXDECLARE macro declares
a static const member variable called metaClass, which is a table describing this class. It provides some
form of runtime type information. It also declares a virtual function getMetaClass() which can be used to
obtain a pointer to an objects metaclass variable; this way, one can interrogate the type of
an object.
In addition, it declares a static member function called
manufacture() which will construct an object of this class using the default constructor.
Finally, it declares two convenience functions for serialization of pointers to objects of this class.
The FXIMPLEMENT macro is used to define and fill-in the table declared using FXDECLARE. It defines
the static member function manufacture(), the virtual member function getMetaClass(),
and fills in the static member variable metaClass. If the object handles messages, it also
fills in a pointer to the message table.
A default constructor needs to be defined in your class because the manufacture() function needs
to use the default contructor to create a properly initialized object of this type.
This is needed by the deserialization system so that it can allocate and initialize an object prior
to loading values for the persistent member variables.
Which FOX objects do I need to delete to avoid memory leaks?
|
Most FOX Widgets are automatically deleted by their parent Widget. However there are some
resources which are sharable, and these resources must be deleted explicitly by the
program in order to avoid memory leaks or other problems.
Cursors, Fonts, Images, Icons, Bitmaps, and Visuals are sharable resources which must be
cleaned up explicitly.
Because several Widgets may refer to the same icon or font, these resources are not
automatically deleted by the Widget as they may be used in another Widget.
A number of resources, such as the default cursors, the default font, and the default
visual, are automatically created by the Application object, and the Application object
also assumes responsibility to destroy these when the Application object itself is
being destroyed.
Menu panes usually refer to the Widget that owns them, and because dangling references
to a deleted owner object are not allowed, the owner Widget must make sure the Menu
panes are deleted when the owner Widget itself is. Failing to do this will leave the
Menu pane in existence while their owner is already deleted, and this will cause
problems.
Ordinary Widgets, like Buttons, Sliders, and so on, are automatically deleted by
their parent Widget; therefore it is not necessary for your application to keep
track of them explicitly
What's the difference between detach and delete?
|
Many FOX objects, like widgets and icons and fonts and so on, have resources which
are resident in the X-Server (or GDI in the case of Windows). The existence of
these X-Server resident resources is manipulated through the member functions
create() and destroy().
When a program's GUI is realized, all the X-Server resident resources are automatically
created by recursively working through the entire widget tree and calling create()
for every reachable object.
When a widget is deleted, the X-Server resident resources are released by recursing
through the widget tree and calling destroy().
However, destroy() is only called for those objects which not shared; shared
resources like icons and fonts are not destroyed because they may still be referenced from other places.
On UNIX systems, it is possible to fork() a process, which creates a child
process which has initially all the same data as the parent. This includes the
handles to the X-Server resident resources. Of course, these resources
really belong to the parent process, and should not be references by the child
process after the fork.
To clean up, a child process forked off from a parent process needs to call detach().
The call to detach() will recursively work down the widget tree and detach all reachable objects
(widgets as well as sharable resources like icons and fonts) from their X-Server resident representations.
After having been detached, the objects can then be destroyed without generating a
call to destroy() along the way, so the child will not accidentally release
any resources which the parent process still needs.
Can I use multiple threads in my FOX application?
|
FOX assumes one single thread to be responsible for the User Interface related tasks.
This is because certain FOX resources are not thread-safe; also, because on MS-Windows
message queues from a window are tied to the thread that created that window, it is
very important for portability reasons that it is always the same thread
performing the User Interface tasks.
You can however use any number of threads in your application, as long as they
are worker threads, i.e. they do not perform User Interface functions.
Synchronization between the User Interface thread and the worker threads can be performed
using a synchronization object, a pipe (UNIX/LINUX) or an event object (MS-Windows).
The synchronization object is passed to FXApp::addInput() so that the User Interface thread
is awakened when the worker thread turns the synchronization object into a signalled
state.
In the more recent FOX versions (FOX 1.7 and newer), a facility has been implemented
called FXMessageChannel to make this easier. Using FXMessageChannel, a worker thread
can send an asynchronous message to the GUI thread, causing it to wake up and dispatch
to a handler in order to process a message on the worker thread's behalf. (Older
versions of FOX used FXGUISignal, which is a more limited form of this facility).
Can I cross compile FOX on Linux for Windows?
|
FROM: Markus Fleck
DATE: 04/10/2001 09:42:55
SUBJECT: [Foxgui-users]Convenient cross-compiling for Win32 on Linux
Hi!
Below are some experiences that I thought I'd share; they're mostly of
interest if you're a UNIX developer at heart and are forced to create Win32
versions of your applications as well (using FOX for the GUI part, of
course :-).
I'm currently using FOX for cross-platform development (Linux and Win32),
and have been using Cygwin under Windows for some time to create binaries
for Win32. Unfortunately, Cygwin under Windows is quite slow, and tends to
crash rather often (at least for me.)
Recently, I came across a patched GCC cross-compiler for Win32:
http://www.libsdl.org/Xmingw32/
A Linux binary build of the cross-compiler can be downloaded from that site,
or you can use the script at
http://www.libsdl.org/extras/win32/cross/build-cross.sh
to automatically download, configure and build the cross-compiler from
sources for your platform.
The cross-compiler works like a charm; I am now able to create Win32 .EXE
binaries in a fraction of the time that Cygwin used to require running under
native Windows.
Using the cross-configure.sh/cross-make.sh scripts as a starting point, even
"configure" and "make" can be run on Linux, even though you're generating
target code for the Win32 platform.
I have also started to make use of "Wine" (the Linux/FreeBSD-based execution
environment for Win32 EXE/DLL code) instead of booting into Windows for the
purpose of testing my application; I had to uncomment (or "#if 0") the call
to "TrackMouseEvent" in FOX's src/FXApp.cpp, though, because apparently Wine
doesn't implement that function and aborts when it encounters a call to it.
I also had to disable (or actually, comment out using "dnl") the invocation
of autoconf's "AC_C_BIGENDIAN" macro in configure.in (and invoke "autoconf"
to rebuild the "configure" script); it appears that "AC_C_BIGENDIAN" doesn't
(yet) accept a default endianness value to use when cross-compiling, so that
effectively the "AC_C_BIGENDIAN" test cannot be used when cross-compiling
(yet). So in order to better support cross-compiling, configure.in should
probably test for "ac_cv_prog_cc_cross=yes" and/or shortcut the endianness
test if Win32 is the target platform.
In a nutshell, I can only recommend using the GCC cross-compiler to build
Win32 executables; it's much faster than Cygwin and much more convenient if
you prefer to do development on a UNIX-type system. If you're using Linux
or FreeBSD, "Wine" can give you some additional convenience when it comes
to testing you application.
So cross compiling is not only possible, but it works very well and may be faster
than working natively.
One note on the AC_C_BIGENDIAN thing:- you can simply pass -DFOX_BIGENDIAN=0 on
the compiler command line to override it.
Can I have other colors besides black and white for my cursor?
|
As of FOX 1.1.45, yes! Under X11, you will need the Xcursor extension, which means
you must have a recent X server such as XFree 4.3 or later. On Windows, it should
be OK in all cases.
Why is the SEL_DELETED message sent before the actual deletion?
|
SEL_DELETED is sent so as to afford the target of the message a chance to save the text that
existed prior to the removal; this is useful for the purpose of maintaining undo list.
Since the SEL_DELETED message contains the information about the to-be-deleted stuff, its a small
matter to determine what the state of the widget will be after the deletion, should it be necessary.
How do I perform double buffered drawing?
|
You can perform double buffered drawing by using an FXImage. First, create an off-screen
FXImage, which will be the target of the drawing operations. Then, set the FXDCWindow
to draw on the FXImage. Finally, whenever the widget needs to be repainted, BLIT the
contents, or part of the contents of the FXImage to the Widget.
In code:
picture=new FXBMPImage(getApp(),NULL,IMAGE_SHMI|IMAGE_SHMP,500,500);
...
picture->create();
...
FXDCWindow dc(picture);
dc.setBackground(FXRGB(255,255,255));
dc.drawRectangle(0,0,500,500); // Erase it!
...
... drawing commands ...
...
And in onPaint:
FXDCWindow dc(this,(FXEvent*)ptr);
...
dc.drawImage(picture,0,0);
...
Some details are omitted here; most likely, the drawing of the FXImage and the repainting
are not both done in onPaint(); because the FXImage is off-screen, its not "clobbered" by
other programs so there's no need to redraw it unless the widget changed size or if the
content data was changed.
How do I make sure drawing is flicker-free?
|
The following tricks have been proven to be very helpful in reducing visual flicker while
redrawing widgets:
- Limit drawing to the dirty area. Use FXDCWindow dc(this,event), because that will clip all
drawing commands to the event's dirty rectangle.
Drawing takes more time that almost anything else; so it is worth a lot of work to limit the
amount of drawing. I refer to FXTable to give you an idea. Drawing the table seems simple enough,
but initially it was very slow. I've since then expended a lot of code to determine which cells of
the table were clobbered, and for each cell set clipping rectangles so as to not erase the grid lines.
Now the table is virtually flicker free, and draws a whole lot faster.
- Try to avoid erasing the background of the entire widget. Instead, paint
background around the content area, then paint the content over.
This is done in most FOX widgets, particularly FXTable.
- If you have to erase the background, try and erase it little bits at a time.
For example, in the FXList widget, instead of erasing the entire widget and
then painting the icons over it, I erase and paint over each item individually.
While is is actually a bit slower, it is visually much more acceptable because
at no time is the entire widget completely erased.
- Flush the command buffer prior to the erase, and after the last drawing command
which paints over the erased background. Drawing commands are buffered, and the buffer
is flushed when it is full. However, you don't want to have the situation where the
last command sent is the erase, as in that case the window will stay white for the
entire duration while the command buffer is filled up with commands to paint over
the background. You want the erase command and the paint command to be in the same batch, if
possible, so that they get executed by the X server right after each other.
- Perform double-buffered drawing, using an FXImage as noted above.
Why does the border style not follow what I specify?
|
Under Windows, you should essentially get what was specified. Some decorations under
Windows are tied, for example it is not possible get DECOR_MINIMIZE and DECOR_MAXIMIZE
without also getting the System Menu. Also, the Close button is tied to the Menu.
Under X11, the decorations are mere Hints to the Window Manager. Different
Window Managers may or may not observe the hints properly. As the Window Manager
is a program written by other people, I have little influence over their correctness
in observing the decoration hints.
However, I have had fairly good luck with Motif, KDE 2.2, and other Window Managers
like WindowMaker, Sawmill, BlackBox, and Enlightenment.
The Free Desktop Project is trying to standardize
various aspects of client and Window Manager interaction, and FOX will try to take advantage of
this where it makes sense.
What's the deal with default buttons?
|
Certain buttons in a dialog box may be designated as default buttons. If
the focus moves onto a default button, a RETURN key will be dispatched to this button.
The way to make a button be a default button is to pass BUTTON_DEFAULT.
One single button in the dialog box may be designated as the initial default
button. The initial default button is the one that will become the default button
whenever the focus moves to a widget which can not itself be the default widget and
which does not handle the RETURN key.
Passing BUTTON_INITIAL will make it the initial default. BUTTON_DEFAULT
means that if the focus lands on this button, it will become the one to
respond to RETURN key.
To programmatically set BUTTON_DEFAULT, use setButtonStyle(BUTTON_DEFAULT).
Calling setDefault(TRUE) will MAKE the button the default; this is different
from ALLOWING it to be the default, which is what BUTTON_DEFAULT does.
Recommendations:
Shouldn't fx.h include fxkeys.h?
|
The header file fxkeys.h is rarely used, basically only by programs
which catch keyboard messages directly. The vast number of programs will use
the widgets and rely on the regular callbacks from these widgets.
To make programs compile faster, the file fxkeys.h, which contains a
very large number of definitions, is therefore not normally included.
I have this great idea, but you will need to rewrite all of the FOX library and applications.
|
How do I monitor activity on sockets?
|
When you are running a GUI, you can monitor sockets in two ways:- first, you can
of course dedicate a worker thread to the socket activity, and leave GUI work to
the GUI thread.
Another approach is to use the addInput() facility of FXApp. The addInput()
call registers a callback message, and target object, to be invoked when the event
on the file descriptor occurs. For example, adding:
app->addInput(fd,INPUT_READ|INPUT_WRITE,myobject,MyClass::ID_FD);
Will send a message ID_FD of type SEL_IO_READ when new data is available on fd to
be read, and a message ID_FD of type SEL_IO_WRITE when the buffer is ready to
accept more data to be written. In either case the void* ptr refers to the
file descriptor fd, permitting you to use the same handler for multiple file
descriptors.
On MS-Windows, instead of a POSIX file descriptor, you must use a handle.
Thus, under MS-Windows, the addInput() API can be used to wait on a great variety of
kernel objects, ranging from event objects (which is what you need to use for sockets),
to process and thread handles, and so on. Please consult the MSDN documentation on
Winsock2 for details.
What do the different version numbers for FOX mean?
|
Starting with the FOX 1.0.0 release, FOX library releases will have a version number of the
form major.minor.patch. When the minor version number is odd,
this indicates that it's a development version of FOX; when it's even,
this indicates a stable release version.
The intent is for even-numbered releases to keep the header files untouched, to
guarantee DLL or shared library compatibility. If bugs are fixed, the patch number will
be increased.
Thus, applications should be able to link to shared library versions of FOX with
confidence.
For odd-numbered releases, everything may be changed from one patch level to the next,
so one would have to recompile applications (that's the nature of it being a development
version!).
Why didn't you fix the problem of the last pixel of a line not being drawn on MS-Windows?
|
I have received many such suggestions. Here's why the suggestions received so
far do not work:
The moral of the story is that we're basically better off NOT trying to fix this problem, but simply
organize code so as to (a) minimize the reliance on line drawing, and use rectangle fills instead,
and (b) whenever we do need to draw lines, try to make sure this effect is hidden, and finally (c) if
all else fails, there is #ifdef style conditional compilation.
As it happens, I have been able to make do with (a) and (b) in almost all cases, and had to resort to (c)
only once or twice.
Why doesn't FOX use ASSERTs to check API arguments?
|
The philosophy of error checking is like this:
- As a library, FOX is installed and used by many programs, and so under normal circumstances it is
compiled with optimization flags, and so assertions and such are normally removed.
- Application developers may compile their own code with debugging enabled, but usually link against a
pre-installed copy of the library.
- Thus, errors in arguments and such need to be checked by means other than assertions.
- If you're working on additional FOX widgets, or perhaps suspect a bug in the FOX library, then
the library can be compiled for debug mode. In this case, we're interested to uncover bugs and
inconsistencies in the library itself, so assertions and tracing and so on must be turned on.
- The assertions in the library itself should however NEVER be triggered, even if the application
program is ill-behaved or the library is used in the wrong way:- because the assertions are there
to verify the correctness of the library itself, and the standpoint is that other checks should
catch bad parameters to API's and other abuses.
All this checking introduces some overhead, of course. But lets not forget that its a GUI
library, and so the speed of many operations is not so critical, because 99% of the time
the application is simply waiting for the user to do something; also, the dominant amount
of CPU is actually spent on drawing, which is not slowed down at all.
The payoff of all this checking is that various kinds of programming problems are (hopefully)
discovered sooner.
Why can I not paste clipboard data out of a deleted window?
|
Deferring the generation of the clipboard data until a request is received from
another application has a number of advantages:
- A cut or copy operation can be nearly instantaneous,
even for very large data, as no actual data are being transferred.
- It is more open-ended in terms of clipboard data types, since not
all data formats need to be generated up front.
- It allows for some optimizations; for example, if the requestor
and the source are in fact the same application, no inter-process
communication needs to be performed at all; data can be exchanged
directly in memory.
The alternative would be to generate all clipboard-types up front; imagine what
this would mean if we had 10 image formats and you clipped an image
to the clipboard:- you'd have to place the same image on the clipboard in all
10 image formats just on the off-chance that one of them may be requested!
The source of the clipboard data is the widget from which the data was clipped
to the clipboard; that widget is the one which owns the clipboard,
and that widget is the one that will be asked to generate a specific representation
of the data for a specific clipboard type when it is asked by another application.
Only that particular widget has the methods and knowledge for generating the
requested representation of the clipped data.
While this allows for a lot of flexibility in the clipboard data types, it
does have a downside:- when the owner of the clipboard is deleted, so does
the clipped data.
What is valgrind?
|
Valgrind is a tool for detecting various
memory problems in your code, such as:
- Use of uninitialised memory;
- Reading/writing memory after it has been freed;
- Reading/writing off the end of malloc'd blocks;
- Reading/writing inappropriate areas on the stack;
- Memory leaks -- where pointers to malloc'd blocks are lost forever;
- Passing of uninitialised and/or unaddressible memory to system calls;
- Mismatched use of malloc/new/new [] vs free/delete/delete [];
- Some misuses of the POSIX pthreads API.
Valgrind works on Linux/x86 only (on Windows, consider tools such as
Purify or BoundsChecked;
there may be other tools).
Valgrind translates x86 instructions into instrumented code, basically
inserting various checks on memory references.
Because its still not completely aware of all sorts of x86 instruction set
extensions like 3DNow and SSE, you should probably compile your code in "vanilla"
pentium mode.
If your code links against OpenGL libraries from NVidia
then you can disable the OpenGL library's use of 3DNow and SSE by setting:
export __GL_FORCE_GENERIC_CPU=1
on recent releases of the OpenGL library.
If you care about software quality, you owe it to yourself to try valgrind out as
it can catch a great many bugs; some memory bugs are like "time-bombs" and may
linger in the code for a long time; valgrind can ferret them out.
When is a member function virtual or not?
|
There are often questions about why certain member functions of FOX classes
are not virtual. The point below attempt to explain the reasoning behind
the decisions.
- Functions which need to be virtual are. Examples of these are
functions like getDefaultWidth() and layout().
- Certain functions which may be overloaded in derived
classes are; for instance expandTree() is virtual because it allows
a derived class to know when a subtree is unfolded.
- Message handlers are never virtual. You can just add the same
message handler in the derived class's message map, which is
faster anyway.
- Functions which should not be overloaded (e.g because it could
break the abstraction, like for example getNext() and getPrev()),
are never virtual either.
- Other than the above 4 rules, non-virtual may have been chosen
without good reason. On such cases, I'm open to suggestions.
Obviously the full scope of widget subclassing is not really
known until people try; because of rule (4) chosing non-virtual
is a better way to enforce the integrity of the abstraction.
Why does a toplevel window have different decorations than I specified?
|
The toplevel windows (anything derived from FXTopWindow) can have various decoration hints,
such as DECOR_TITLE, DECOR_BORDER, and so on. FXTopWindow passes these hints to the
underlying system. On X11, a separate program called a Window Manager is responsible
for interpreting these hints and applying the appropriate borders, title bar, close
buttons, minimize and maximize buttons and so on. On Microsoft Windows, this is done
by the GDI layer. Either way, the decorations specified from within the FOX
program are just hints:- the interpretation of these hints depends on the
underlying system, and is therefore out of our jurisdiction.
There are many Window Managers under X11, such as KDE, WindowMaker, FVWM,
Motif Window Manager (MWM), and so on. The ICCCM document details a few common
conventions that Window Managers are supposed to adhere to; unfortunately, decoration
styles are not part of this document. Because of its historic popularity, many
Window Managers have opted to follow the Motif Window Manager hints.
There is also some effort under way to define some more modern standards, the
Free Desktop Organization.
FOX adheres to many of the Free Desktop's standards, insofar as it does not
conflict with Motif Window Manager standards (since FOX needs to work reliably
under Motif for some time to come).
Under MS-Windows, the part that is responsible for the title and border drawing
is implemented in the Windows DLL's that FOX needs to link to. The non-client
drawing is handled by these libraries, ensuring that FOX's toplevel windows
look and behave according to the particular version of Windows your FOX program
runs on.
Since the behaviour of the decoration hints depends on the underlying system,
FOX programmers must perform some testing under various systems to ensure that
the particular combinations of hints they have chosen work as expected; the
FOX library itself uses very conservative settings which are known to work
properly on most Window Managers.
Why do none of the FXScrollArea derived widgets have a frame?
|
The short answer is that FXScrollArea is derived from FXComposite, and FXComposite
does not support a frame.
The longer and more accurate answer is a bit more complicated. The FXScrollArea
widget, and its derivatives, present a small view of a larger content.
The content may be a drawing, but sometimes also other widgets are involved, for example
in the case of FXIconList.
It is necessary to clip all drawing to the visible viewport of the FXScrollArea.
This is made possible by making sure the FXScrollArea's scroll bars and scroll corners are
always the topmost three children, and positioned so as to leave a simple rectangular
viewport area in which the content is shown.
If FXScrollArea would draw borders or padding around itself, this would necessarily
not be covered by the scroll bars and scroll corner; however that would present a
problem as any content or sub-windows of the content would be drawn on top of
the FXScrollArea's borders.
Thus, FXScrollArea does not support borders. If you need a sunken border around a
scrolling widget, simply create a FXVerticalFrame with no padding as the immediate
parent.
What is the Legal Status of FOX since your departure from CFD Research?
|
What is all this stuff with FXSelector and so on?
|
When a target receives a message from a widget, it may want to know several things:
- From which widget did the target receive the message? This is
determined by FXObject *sender argument of the message handler.
- What happened? The answer to this question is in the type-part of the
FXSelector sel argument of the message handler. You can obtain the
message type by means of the macro: FXSELTYPE(sel).
- The identity of the widget from which the message was received. This is answered
by the id-part of the FXSelector sel argument of the message handler.
You can obtain the message id by means of the macro: FXSELID(sel).
- Any other pertinent data. This data depends on the type of widget, the message
type which was received, and is found in the void* ptr argument of
the message handler. You must typically cast this to the appropriate type.
Sometimes, a handler may have to send messages back to the sender, for example, in
response to a SEL_COMMAND message a message handler may want to obtain the value
of the sending widget by sending it a message of type SEL_COMMAND, and message id
ID_GETINTVALUE. To build a message out of the type and id parts, you can use the
macro: FXSEL(type,id).
For historical reasons, the data type used for the message-id, message-type, as well
as the combined selector is FXSelector.
How do I add an icon for Windows Explorer?
|
You need to create a file, call it "myapp.rc", and then put into it:
0 ICON DISCARDABLE "myapp16.ico"
1 ICON DISCARDABLE "myapp32.ico"
Where, obviously, myapp16.ico and myapp32.ico are the icons you want to
be associated with your application.
You will also need to convince your resource compiler to compile that,
of course.
The TextField stops updating while the cursor is in it?
|
While a Control is being manipulated by the user, the GUI update (the process by which
the widget updates itself from the application state) is turned off.
For most simple controls like Sliders this is done only during the time the mouse has
grabbed the slider head.
However, for TextFields the updating is turned off while the TextField is being edited.
There is no easy way to detect when the user is "done" with the TextField; but it is
clear that the TextField can be updated again when:
- You've moved the focus to another Control, or
- You've hit RETURN in the Text Field to accept the typed input.
Building and using DLL's with FOX under Windows.
|
When you compile something, the header files can be parsed two ways on
Windows. When compiling the FOX library itself as a DLL, the FXAPI macro
should be set to FXEXPORT, which itself is set to __declspec(dllexport),
(or possibly as something else depending on the compiler and linkage
ideosyncracies). When you're compiling your own code which uses
the FOX DLL, then FXAPI is defined as FXIMPORT which is then typically set to
__declspec(dllimport).
There are two layers of macros so for your own DLL and EXE building you
won't have to remember what to do for export or import, you can use
the FXEXPORT and FXIMPORT macros. Now, you can NOT use FXAPI. When
you build your own library, that library is a importer of the FOX
API but an exporter of its own API!
For example, in FXChart new symbol FXCHARTAPI is defined so that when
it is compiled it could be import symbols from FOX yet at the same time export
its own symbols.
So in a nutshell:
- Compile FOX with -DFOXDLL and -DFOXDLL_EXPORTS.
- Compile programs which use FOX with -DFOXDLL and nothing else.
- Compile your own DLL's which use FOX with -DFOXDLL and
-DYOURDLL_EXPORTS [see FXChart for example]. Your own
FOX-based DLL's export ONLY their own symbols but import
the core library's symbols.
Creating an MDI application.
|
When you build an MDI application, messages should be routed via the
FXMDIClient to an FXMDIChild, and from there, to either FXMDIChild's
content window and the FXMDIChild's target. The FXMDIChild's target
is typically a document object of some kind.
So, GUI controls basically have FXMDIClient as their target. The
FXMDIClient is a kind of delegator, in the sense that when it does not
understand a message, it forwards it to its currently active FXMDIChild;
if there is no such child, then control is returned to the calling
widget with the "unhandled" message return code.
The FXMDIChild similarly does a delegation. It tries the content
window first, then its target "document" object.
Since we do not know which FXMDIChild is going to be active, it
is important to ensure that all message ID's are unique if you
have different types of FXMDIChild widgets.
For example, an application may have a 3D viewer in one FXMDIChild
and a text view in another FXMDIChild. We don't want messages
intended for a 3D viewer to be connected to a wrong handler
when the text view FXMDIChild is active.
So, I recommend a common base class for your various FXMDIChild
subclasses, and to define all message ID's in there. Then
various subclasses of this base class map whatever ID's they
need to specific handlers.
If you also have a Document class as target of the FXMDIChild
windows, then number the messages the Document deals with starting
from the ID_LAST of the FXMDIChild base class widget in your
application. See below:
// MDI Child base class
class MyMDIChildBase : public FXMDIChild {
...
enum {
ID_FIRST=3000, // High number away from any ID's defined in widgets
...
messages defined for all subclasses of MyMDIChildBase
...
ID_LAST
};
...
};
// Custom document class
class MyDocument : public FXObjecvt {
...
enum {
ID_FIRST=MyMDIChildBase::ID_LAST,
...
ID_LAST
};
...
};
This is convenient. For example, a "Cut Selection" message could have
radically different implementations in the various subclasses, yet be
invoked from the same single menu.
When a particular FXMDIChild is active, some message ID's will be
mapped to a handler, and some will not be.
FOX can take advantage of this fact by allowing you to specify the
AUTOGRAY or AUTOHIDE flags on certain controls. For example, if
you were to specify BUTTON_AUTOGRAY on a toolbar button, the button
is automatically grayed out when no corresponding handler is found
in the currently active FXMDIChild, or when there is no open
document at all.
BUTTON_AUTOHIDE works similarly, except that in this case the button
will be hidden instead of simply grayed out.
When you use BUTTON_AUTOGRAY, it is of course going to be necessary to
implement the SEL_UPDATE handler as well as the usual SEL_COMMAND
handler, so that you can enable the button. You can enable the
button by means of a message, so that your handler won't have to
know what kind of widget the sender was.
Example:
// Undo last command
long MyDocument::onCmdUndo(FXObject*,FXSelector,void*){
/// perform the command for undo
return 1;
}
// Sensitize undo widget; the ID_ENABLE message
// enables the sender [presumably a widget of some sort]
// We don't need to know what kind of widget the sender is,
// so we can actually use this handler for sensitizing toolbar
// buttons as well as the corresponding pulldown menu commands.
long MyDocument::onUpdUndo(FXObject* sender,FXSelector,void*){
sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_ENABLE),NULL);
return 1;
}
// Example: pulldown menu command
new FXMenuCommand(editmenu,"&Undo",undoicon,clientarea,MyDocument::ID_UNDO,MENU_AUTOGRAY);
// Example: toolbar command
new FXButton(maintoolbar,"\tUndo",undoicon,clientarea,MyDocument::ID_UNDO,BUTTON_AUTOGRAY|BUTTON_TOOLBAR|FRAME_RAISED);
Of course there may be a few cases where you may need to perform a wholesale
update of the user interface when the user switches FXMDIChild windows or
documents. Some major widgets with lots of content should probably not
be updated using the SEL_UPDATE mechanism.
There are two ways; first is the SEL_CHANGED from FXMDIClient; a better
way, and the recommended one, is to catch the SEL_SELECTED and
SEL_DESELECTED messages from FXMDIChild:
// Switched to new active document
long MyDocument::onChildActivate(FXObject*,FXSelector,void* ptr){
if(!ptr || ((FXMDIChild*)ptr)->getTarget()!=this) activateDocument();
return 1;
}
// Switched from old active documents
long MyDocument::onChildDeactivate(FXObject*,FXSelector,void* ptr){
if(!ptr || ((FXMDIChild*)ptr)->getTarget()!=this) deactivateDocument();
return 1;
}
The void* in these messages reflects the OLD FXMDIChild window that
was active before the switch. The code above takes care of the
case where there are multiple FXMDIChild windows which may have
a common document, and we would of course only want to update the
GUI controls when we switch documents, not when we switch between
FXMDIChild windows of the same document.
The implementation of activateDocument() will be responsible for setting
up the GUI controls with data pertaining to the document. Likewise,
deactivateDocument() should tear down the user interface and leave
widgets in their pristine state [as if there were no document].
MS-Windows GDI handle limits.
|
MS-Windows has finite limits on GDI handles; the maximum number of HWND handles
in the entire system is determined by 16-bit handle values. There is also a maximum
number of bitmap handles.
These limits manifest themselves when calls to create() are failing by throwing
an exception.
Windows NT, 2K, and XP probably have higher limits than Windows 95, Windows 98,
and Windows ME.
It is therefore important to make sure that resources are deleted as soon as
they're no longer needed. Since its pretty fast to create resources, its best
to delay creation of windows and icons until they're actually needed.
In particular, dialogs should "own" their icons, and delete them in the dialog's
destructor. Try to construct dialogs only when they're actually brought on-screen,
and try to delete them when the interaction with them is done.
Note that un-created() icons and images do not use any handles in the system, so
refrain from calling create() on icons or images if all you want to do is load
and manipulate the image's pixel data. You only need to call create() when images
are to be drawn into or used as sources for drawing (like e.g. drawing onto
widgets).
How to I make a back-buffered widget?
|
There is a certain boiler-plate logic underlying a back buffered widget which is described
below.
- Note that there are two types of drawing: program-initiated, and paint-message
initiated. In a paint-message initiated onPaint(), you simply do:
FXDCWindow dc(this,(FXEvent*)ptr);
dc.drawImage(backbufferimage,0,0);
and you are done; the backbufferimage will never get clobbered so there's no
need to regenerate it each time you paint. Passing the FXEvent to FXDCWindow
constructor sets the clip rectangle to the dirty-area, and this means that
in most cases, you'd only paint the smallest sliver of the backbufferimage.
In other words, this is pretty FAST and totally independent of the complexity
of the drawing in the image!
- When your widget changes size, layout() is called. You will need to overload
layout() and resize the image. The layout() function is also called when there is reason
to believe stuff is "dirty". The layout() function you implement needs to treat two cases:
- If it changed size, resize the image. Code will be more or less like:
if(backbufferimage->getWidth()!=width || backbufferimage->getHeight()!=height){
backbufferimage->resize(width,height);
}
The test is obviously needed because resizing an image is slow and painful: we
really don't want to do it unless its necessary!
- If the image changed size, or if it was dirty, redraw the image in the layout()
function. This goes something like this:
FXDCWindow dc(backbufferimage);
dc.drawLine(..); ... // or whatever drawing commands you need!
At the end of layout() you set the dirty flag back to false:
flags&=~FLAG_DIRTY;
- Whenever you programmatically change the contents to be displayed in the backbufferimage,
call recalc(). This sets the FLAG_DIRTY and thus instigates a layout() at some
point in the future.
Note it doesn't redraw immediately; it just sets a flag to do it later, when we return
to the event loop. Thus, you can change many things about the widget, and yet incur the
cost of redrawing only once!
- Some examples of this in FOX widget collection: FXGradientBar, FXColorWheel,
FXChart, and a few others. In the case of FXColorWheel, the backbufferimage
is actually constructed pixel by pixel, and the whole off-screen image regenerated
by a call to backbufferimage->render().
It should be clear that the logic above takes great pains to avoid the hard work
[redrawing the off-screen image] as much as possible. 99% of the time, we can
redraw some tiny sliver from the backbufferimage using a fast BLIT operation.
Then for the remaining 1% of the time, we'd regenerate the contents only.
Only once in a while we have to both resize and regenerate the contents; but at
least, we've put this work off till idle time!
Because at idle time, the only thing we'd be doing otherwise would be to wait for
events anyway!
Advanced drag and drop topics.
|
Occasionally, there is a need to perform user-interaction when a drop target
receives an SEL_DND_DROP message. Doing this can be a bit tricky, especially
when dragging and dropping within the same application.
- When the widget is the drag source, it calls beginDrag(), handleDrag()
and endDrag() in succession.
- When you do a drag and drop, two programs are normally involved [although
drag and drop also works within the same program]. When the drag source [sender]
performs a drop using endDrag(), a drop-message is sent to the drop-target [receiver].
After sending this drop-message, the sender goes into a little event loop, waiting
for confirmation that the drop-target has obtained the data, and coordinating with
the receiver to transfer the drop-data.
- After the data transfer has completed, the receiver sends a finish-message to
the sender to let the sender know it has obtained the data; this message is normally
send automatically upon returning from the drop-message handler.
When the sender receives this message, it can then wrap up the drag operation, and
return from the endDrag() routine.
- As the sender is waiting for the finish-message in its event loop, it keeps timers
to ward against receivers crashing or just being broken in some way. After all, we
don't want a bad behaviour in another program to cause a problem in the sender itself.
We assume that if a timeout has occurred, the data transfer has been unsuccessful
and thus the sender should not delete any data in that case.
- Now, in most simple drag and drop cases, the operations performed by the receiver
after obtaining the data from a drop are pretty quick, and no special handling is needed.
However, when a receiver needs to do a lot of work upon receiving a drop-message,
the receiver can explicitly send a finish-message to the sender [dropFinished()] after
obtaining the data. We thus release the sender from its event loop quickly, before the
receiver would normally return from the drop-message handler. Having thus released the
sender, the receiver can then proceed to work on the data at its leisure.
- However, a problem still exists when sender and receiver are one and the same.
Because in that case, control cannot return from endDrag() because the drop-message
is still being processed. To avoid these problems, we need to ensure that control
returns to the main event loop before the drop-data is processed.
- The solution to this problem is to set a chore to process the actual data,
and return from the SEL_DND_DROP handler immediately. This way, if sender and receiver
happen to be the same, we are assured that the chore handler is executed after
returning to the top-most event loop.
- Now, the sender can return from the endDrag() function, and normal event
processing resumes. The first thing that will happen after this is the chore which
we've set in the drop-message handler. Since we're no longer in endDrag()'s modal loop we
can invoke anything we want in the chore handler, which includes popping dialogs
and invoking popup menus!
FOX Exception handling.
|
Some notes about the FOX exception handling philosophy.
FOX Widgets like buttons, sliders, text fields, and so on, rely on a particular, well-defined
sequence of events to maintain internal consistent state. During the processing of events,
widgets may issue callbacks to message handlers in the application program to inform of certain
events.
A problem exists, however, if the message handler code does not normally return to its caller
but throws an exception. In this case, exception processing prevents the normal sequence
of from taking place, leaving a widget in a potentially inconsistent state.
We want to ensure that when exceptions are thrown, the widget can recover. To this end we
notice that BEFORE the message handler was invoked, things were still OK. Therefore, the
point of message handler invocation represents a safe roll-back point.
To ensure that a widget can be brought back into a known state, message handlers are invoked
through the tryHandle() function. Any exceptions that are raised while executing
the handler can be caught in tryHandle().
However, not all exceptions should be treated this gently; after all, in correct programs,
only resource-exceptions are to be expected, whereas other types of exceptions may be
raised only in the presence of non-recoverable errors or incorrectly functioning software.
These kinds of exceptions should be passed on, to allow developers to debug their software.
In FOX, FXResourceException is the type of exception that is expected to be recoverable.
It includes typical scenarios such as running out of disk space, RAM memory, or window-handles.
It is important to distingish these errors from programming errors:- perfectly correct programs
can, after all, run into resource limits on the user's machine, which has only finite amounts
of memory, disk space, and other resource. Conversely, programming errors such as indexing
lists out of range, and so on, should not occur in well-written software.
Consider the following code-fragment:
// About box
long TextWindow::onCmdAbout(FXObject*,FXSelector,void*){
FXDialogBox about(this,tr("About Adie"),DECOR_TITLE|DECOR_BORDER,0,0,0,0, 0,0,0,0, 0,0);
FXGIFIcon picture(getApp(),adie_gif);
///// build a gui //////
about.execute(PLACEMENT_OWNER);
return 1;
}
In this code, FOX API's are invoked to create a dialog box for an application. This
typically entails generating windows, icons, cursors, and font resource in the system. If we
fail to allocate any of these resources [for example, the number of window handles is
exceeded], then the responsible FOX API throws a FXResourceException.
Ssince FXDialogBox and FXGIFIcon are stack-allocated, they will be nicely cleaned up, and the
whole stack is unrolled until the point inside tryHandle() where a FXResourceException is
caught and discarded. Now, the user can fix the situation [say, close a few big applications]
and try again.
Note that other exceptions than FXResourceExceptions are not caught in tryHandle().
Thus, to recap:
- FXResourceException's are thrown by FOX, and caught in FOX, but may unroll through the
application-programmer's own message handler-code. You can use them in your own code, but
keep in mind that they're intented for recoverable types of errors, and not for detecting
programming errors.
- You can throw other kinds of exceptions [exceptions not related to FXException], but
FOX will not catch them. However, since these will unroll through tryHandle() and the widget's
event processing code, there will be a possibility that the widget will remain in an
inconsistent state. Its therefore probably better to avoid raising these types of exceptions
unless they're caught prior to returning from the message handler.
- Writing your message-handlers exception-safe allows your program to fully recover from
resource errors, should they occur.
Copyright © 1997-2022 Jeroen van der Zijp
|