Tutorial 3 The Dynamic Window
Tutorial 6 Fitting it all in
Tutorial 15 Capturing the Mouse
Tutorial 16 Changing the Cursor
Tutorial 19 Database Bound Controls
Tutorial 20 Packing and Docking
GWindows is incredibly powerful yet it
is simple and intuitive. The guide will get you started in no time programming
with GWindows.
Download
and install the GNAT development environment, from http://libre.adacore.com,
or
the GNAT Pro version, or the MinGW version.
Download
and install the latest "GWindows Setup DD-Mmm-YYYY.exe" installer from
http://sf.net/projects/gnavi/ .
Go to the gwindows directory and start gwindows_tutorials.gpr
with GPS ─
or "gnatmake -P gwindows_tutorials.gpr" from the command line ─
to get started.
GWindows is the Professional Open Source Ada 95 Windows RAD Framework. It
introduces for the first time to
GWindows includes extensive bindings to
the Windows GUI including support for common controls and dialogs, printing,
and owner drawn extensions to controls. In addition, GWindows adds a number of
new controls, keyboard support, multiple models of event handling, Active
X controls, support for creating
dialogs and windows from resource files, dynamic garbage collected windows,
Window docking, non-GUI bindings, database support, database bound controls,
and much more!
GWindows builds as either ANSI or
UNICODE (a first for
GWindows is designed to take advantage
of
Under the GWindows directory are the
following directories
Framework
Location
of GWindows source code[1].
Contrib
Additional
source code contributed by GWindows users
Docs
Documentation
Redist
Redistributable
binary components
Samples
Samples
of different uses of GWindows
Test
Code
used to test different areas of GWindows
Testing
Code
used to testing your software from GWindows
Tools
Tools
for programming with GWindows
Tools\gnatreg
An
application for registering directories as locations of standard GNAT
libraries.
Tools\gbmanager
An
application for creating a repository of bound ActiveX/COM/DCOM components
Tutorials
Location
of tutorial code
Base package of GWindows. gwindows.ads is a copy of either gwindows_ansi.ads or gwindows_Unicode.ads depending on the build type.
Applications at run time can
determine if they are Unicode or ANSI builds by checking the constant
Character_Mode.
GWindows allows for the
transparent use and compilation of ANSI and Unicode builds through a set of
character and string types that take the place of their standard
GCharacter, GString,
GString_Unbounded instead of
GChar_C, GString_C in place of
Interfaces.C.char and Interfaces.C.char_array
GWindows string conversion and handling subprograms. gwindows-gstrings.adb is a copy of either gwindows-gstrings_ansi.adb or gwindows-gstrings_unicode.adb depending on the build type.
Conversion routines are available to convert between, GStrings, GString_C, GString_Unbounded, Strings, Wide_Strings, BSTR and VARIANTS.
Renames Ada.Strings.Unbounded or Ada.Strings.Wide_Unbounded depending on the build type.
Message_Boxes and Input_Boxes
Subprograms needed to start Windows message loops in order for the application to start responding to events. These include the main application event loop and modal window loops and modal dialog box loops. Also contains subprograms for general application wide tasks.
Base type for all windows, dialogs and controls including general events for all GWindows objects.
Base type for windows, mdi windows, dialogs and custom controls
A GWindows window that shuts down the main application loop when the window is closed.
Types to aid in the creation of MDI based Windows applications
ActiveX Controls Type
Bindings to
Controls to make database programming easier
Buttons, Radio Buttons, Check Boxes, etc.
Combo Boxes and
Drop
Bindings to Win32 Common Controls an extend set of operating system provided controls
Controls for accepting text
Rich text controls
List box controls
Scroll bar controls (not scroll bars on windows)
Labels, Bitmap, Meta File and Icon controls
Scroll panel objects can be placed in a window to create a virtual space with in the window with automatic handling of scroll bars
Objects to control layout of controls
Panel controls
Subprograms for drawing on windows and controls
Subprograms for determining the capabilities of a device to be drawn on
Pens, Bitmaps, Icons, etc. objects for use with GWindows.Drawing
Control used to make drawing easier on windows by providing a persistent canvas
Bindings to Win32 Common Dialogs a set of dialogs boxes provided by the operating system to give applications a consistent look and feel.
Subprograms to handle window menus and popups
Subprograms for making use of different medias such as sound
Image list objects used with many common controls
Windows caret (blinking text cursor) support
Support for handling the Windows mouse cursor.
Color types, constants and subprograms
Constants such as the dialog control IDs commonly used
Subprograms to determine nature of system where application is running
Subprograms to determine keyboard state at any moment
Pre canned event handlers
Types used through out GWindows
Subprograms for handling Win32 errors
Subprograms for accessing the Win32 registry
Routines to assist in binding to Win32 methods
Internal private package for GWindows
ActiveX controls are the success story of
component based software development. There are tens of thousands of controls
written and available for free or purchase on the web and in programming catalogs,
not to mention a huge array of controls that are part of the operating system
itself. GWindows makes it possible to incorporate these controls in to
GWindows.ActiveX contains only two creation
methods and a single property. The creation methods bind to a helper dll called
gwocx1.dll that must be available on the applications path. This DLL takes
care of the nasty details of hosting the ActiveX control in a standard Windows
window, which is then embedded in to your applications window. The single
property grants access to the control s COM interface by which all
communications are done using the package GNATCOM.Dispinterface for dynamic
access to the control or through
The steps to using an ActiveX control are:
1. Create the parent window
2. Create the control using GWindows.ActiveX.Create and its CLSID or Program ID
3. Either use:
GNATCOM.Dispinterface.Query
(MyDispatch, GWindows.ActiveX.Interface (MyControl));
For dynamic control of the Control
Or
Use the Query method on the type for the Control generated by BindCOM or GBManager.
Using dynamic control for the Control either requires knowledge of the controls API at design time or it can be queried at run time using the GNATCOM.ITypeInfo interface.
The easiest way to control the Control is to create a binding for it before hand.
You can find source code for all the
tutorials in the directory GWindows\tutorials. For all directory paths, substitute
GWindows with the root director of you GWindows installation.
GWindows offers a number of Message Box
routines in the package GWindows.Message_Boxes. Message boxes give your
application the opportunity of getting the attention of the user, relating a
message to him or getting a simple response.
with GWindows.GStrings;
with GWindows.Message_Boxes;
procedure Tutorial1 is
use GWindows.Message_Boxes;
Result : Message_Box_Result;
begin
Message_Box (Title => "Tutorial1",
Text => "My first GWindows Application",
Icon => Exclamation_Icon);
Result := Message_Box (Title => "Tutorial1",
Text => "Nice GUI huh?",
Style => Yes_No_Box,
Icon => Question_Icon);
if Result = Yes then
Message_Box ("Cool....", "I like your answer");
else
Message_Box ("What....", "You have no taste");
end if;
for I in 1 .. 3 loop
Message_Box ("A Number",
GWindows.GStrings.To_GString_From_String (I'Img));
end loop;
end Tutorial1;
If you looked at the specs for
GWindows.Message_Boxes (perhaps even if you haven't) the above code should be
easy to understand. The only "twist" is the use of GWindows.GStrings.
GWindows.GStrings is the magic behind GWindows ability to implement both ANSI and UNICODE bindings to the Win32 API.
The difference between the two is that
ANSI is an 8 bit encoding of strings equivalent to the string type in
subtype GCharacter is Wide_Character;
subtype GString is Wide_String;
subtype GString_Unbounded is
Ada.Strings.Wide_Unbounded.Unbounded_Wide_String;
or sets:
subtype GCharacter is Character;
subtype GString is String;
subtype GString_Unbounded is Ada.Strings.Unbounded.Unbounded_String;
In order to make development of
software that can be compiled against either build type, GWindows provides a
package GWindows.GStrings that will convert between GStrings,
If you are going to be using only one
type of binding it may be possible to circumvent the use of GStrings, but
don't! You never know where you code may end up. Perhaps some one wants to port
your cool killer game to a Japanese version where ANSI just won't do the trick
or perhaps your neat-o server might be running on a server farm of Win 2K
machines pushing the limit and UNICODE is the way to do it.
Message Boxes may be enough for the
rare quick and dirty application, but they are hardly what makes GUI programs
more user friendly than text based applications. The following code is perhaps
the simplest full Windows application you can write in GWindows:
with GWindows.Windows.Main; use GWindows.Windows.Main;
with GWindows.Application;
procedure Tutorial2 is
pragma Linker_Options ("-mwindows");
Main_Window : Main_Window_Type;
begin
Create (Main_Window, "My First Window");
Visible (Main_Window, True);
GWindows.Application.Message_Loop;
end Tutorial2;
As you can already see GUI programming
with GWindows is not exactly brain surgery.
First we declare our Window object, in
this case a specialized type of Window, a Main_Window_Type that will
automatically shut down the application when its window is closed.
Then we begin our application by
creating the GUI object associated with our Main_Window_Type and set its
visible property to true. All top level windows are created by default as
invisible and all controls by default are created visible. As can be seen from
the specs in GWindows.Windows, it is possible to also set the location and size
of the window when creating the window (we will discuss the Is_Dynamic
parameter in the next tutorial.)
The least intuitive part of this
tutorial code is the GWindows.Application.Message_Loop line. All Windows
applications make use of message loops to dispatch serially control messages to
the GUI objects. Every task in your
GNAT can produce both console and GUI
applications for windows, the linker option, -mwindows, tells GNAT to compile a
GUI application instead of a console application. Frequently when creating
GWindows applications, I compile without the mwindows option so that I
can send output to the console while my GUI application is running. Once I know
that my application is running the way I like, I strip our the console writes
and compile with the mwindows option.
As I promised, we will talk in this
tutorial about the Is_Dynamic parameter to create. GWindows allows for the
dynamic creation of GWindows window and control types that when their GUI
elements are destroyed they are deallocated. For example:
with GWindows.Windows.Main; use GWindows.Windows.Main;
with GWindows.Windows; use GWindows.Windows;
with GWindows.GStrings;
with GWindows.Application;
procedure Tutorial3 is
pragma Linker_Options ("-mwindows");
function "+"(S : String) return GWindows.GString
renames GWindows.GStrings.To_GString_From_String;
Main_Window : Main_Window_Type;
begin
Create (Main_Window, "The Main Window");
Visible (Main_Window, True);
declare
A_Window : Window_Access;
begin
for N in 1 .. 3 loop
A_Window := new Window_Type;
Create (A_Window.all, +("Dynamic Window" & N'Img),
Width => 75,
Height => 75,
Is_Dynamic => True);
Visible (A_Window.all);
end loop;
end;
GWindows.Application.Message_Loop;
end Tutorial3;
Once any of the dynamic windows are closed (or the main window since all the the other windows will close when it closes) the memory allocated for each window by
A_Window := new Window_Type;
will be deallocated automaticly by GWindows.
Dynamic windows are not a frequent occurrence outside of MDI (Multi Document Interface) applications, but they are there when you need them.
By the way, if you are doing a lot of coding with GStrings, you may want to rename the To_GString_From_String function in to something simpler like the + operator.
So you can display windows, but how do
you find out what the user does with your window? Through events.
There are two event models used in
GWindows, inheritance and event handlers. The inheritance model is mainly used
for extending GWindows with new or custom functionality, while the event
handlers are most often used with a particular instance of a window or a
control.
There are a huge number of events that
can be made use of, for a quick look at the list of events, the Quick_Ref.txt
document in the GWindows\doc directory comes in handy. Eventually you will want
to implement handlers for these events. Events are described in the spec files
in a section that always starts with a comment that looks like this:
-------------------------------------------------------------------------
-- Window_Type - Events
-------------------------------------------------------------------------
In those sections each event is given with a short description.
To handle these events using the inheritance model, just extend the type and override the procedure.
For example:
with GWindows.Windows.Main;
package Tutorial4_Window is
type My_Window_Type is
new GWindows.Windows.Main.Main_Window_Type with private;
type My_Window_Access is access all My_Window_Type;
type Pointer_To_My_Window_Class is access all My_Window_Type'Class;
procedure On_Close (Window : in out My_Window_Type;
Can_Close : out Boolean);
private
type My_Window_Type is
new GWindows.Windows.Main.Main_Window_Type with null record;
end Tutorial4_Window;
I could have written this package with out adding all the extra access types and extending My_Window_Type in private, but it helps to be consistent with the framework when creating new type of GWindows objects.
The implementation of this package would simply be:
package body Tutorial4_Window is
--------------
-- On_Close --
--------------
procedure On_Close
(Window : in out My_Window_Type;
Can_Close : out Boolean)
is
use GWindows.Message_Boxes;
begin
Can_Close := Message_Box (Window, "Tutorial4", "Ok to close?",
Yes_No_Box, Question_Icon) = Yes;
end On_Close;
end Tutorial4_Window;
This may seem like a lot of work for implementing event handling, and it is. The pay off is when you reuse My_Window_Type for a few hundred windows. If you are in need of just creating one My_Window, then event handlers are certainly simpler to write and use.
For every event there are two additional procedures defined in a spec, handler property procedure for setting the handler and a fire procedure to execute the handler. Unless you are extending a GWindows object, you will not be using the fire procedure. The handler property procedure is declared as the name of the event followed by _Handler and takes two parameters, the object and the event handler. For example the On_Close event would be:
procedure On_Close_Handler (Window : in out Window_Type;
Handler : in Close_Event);
To implement an On_Close handler, we need to create a procedure that has the same signature as the close event, which is nothing more then an access to procedure:
type Close_Event is access
procedure (Window : in out GWindows.Base.Base_Window_Type'Class;
Can_Close : out Boolean);
The signature always has as the first parameter Window : in out GWindows.Base.Base_Window_Type'Class; and the remaining parameters if any are exactly the same as the inheritable event procedure.
So for our example we would implement the following:
declare
Main_Window : GWindows.Windows.Main.Main_Window_Type;
procedure Do_On_Close
(Window : in out GWindows.Base.Base_Window_Type'Class;
Can_Close : out Boolean)
is
use GWindows.Message_Boxes;
begin
Can_Close := Message_Box (Window, "Tutorial4", "Ok to close?",
Yes_No_Box, Question_Icon) = Yes;
end Do_On_Close;
begin
Create (Main_Window, "Event Handling Winodw");
Visible (Main_Window, True);
On_Close_Handler (Main_Window, Do_On_Close'Unrestricted_Access);
GWindows.Application.Message_Loop;
end;
You will notice that since Do_On_Close is not declared at library level, we must make use of Unrestricted_Access. You must of course make sure that Do_On_Close does not go out of scope for the length of time that it may be possible called, i.e. as along as Main_Window is around.
Here is the complete code for using both types of inheritance:
with GWindows.Windows.Main; use GWindows.Windows.Main;
with GWindows.Windows; use GWindows.Windows;
with GWindows.Base;
with GWindows.Application;
with GWindows.Message_Boxes;
with Tutorial4_Window; use Tutorial4_Window;
procedure Tutorial4 is
pragma Linker_Options ("-mwindows");
begin
declare
My_Window : Tutorial4_Window.My_Window_Type;
begin
Create (My_Window, "Event Handling Winodw");
Visible (My_Window, True);
GWindows.Application.Message_Loop;
end;
declare
Main_Window : GWindows.Windows.Main.Main_Window_Type;
procedure Do_On_Close
(Window : in out GWindows.Base.Base_Window_Type'Class;
Can_Close : out Boolean)
is
use GWindows.Message_Boxes;
begin
Can_Close := Message_Box (Window, "Tutorial4", "Ok to close?",
Yes_No_Box, Question_Icon) = Yes;
end Do_On_Close;
begin
Create (Main_Window, "Event Handling Window");
Visible (Main_Window, True);
On_Close_Handler (Main_Window, Do_On_Close'Unrestricted_Access);
GWindows.Application.Message_Loop;
end;
end Tutorial4;
Now that we understand the basics of GWindows and its event model, we can go on to make are Windows a little more usable by adding some controls. Controls in GWindows are just another type of GWindows object and follows that we have already discussed.
Here is an simple example adding controls to a window:
with GWindows.Windows.Main; use GWindows.Windows.Main;
with GWindows.Buttons; use GWindows.Buttons;
with GWindows.Edit_Boxes; use GWindows.Edit_Boxes;
with GWindows.Static_Controls; use GWindows.Static_Controls;
with GWindows.Message_Boxes;
with GWindows.Base;
with GWindows.Application;
procedure Tutorial5 is
pragma Linker_Options ("-mwindows");
Main_Window : GWindows.Windows.Main.Main_Window_Type;
User_Name : GWindows.Edit_Boxes.Edit_Box_Type;
Disp_Button : GWindows.Buttons.Button_Type;
procedure Do_Display
(Window : in out GWindows.Base.Base_Window_Type'Class)
is
begin
GWindows.Message_Boxes.Message_Box ("Controls Window", Text (User_Name));
end Do_Display;
begin
Create (Main_Window, "Controls Window", Width => 200, Height => 125);
Visible (Main_Window, True);
Keyboard_Support (Main_Window, True);
Create_Label (Main_Window, "Name :", 10, 10, 50, 25);
Create (User_Name, Main_Window, "", 70, 10, 100, 25);
Create (Disp_Button, Main_Window, "&Display", 10, 50, 75, 30);
On_Click_Handler (Disp_Button, Do_Display'Unrestricted_Access);
GWindows.Application.Message_Loop;
end Tutorial5;
As you can see adding controls is just a matter of calling the create procedure for the control, including the parent window and its location. For Static controls (like labels, icons, bitmaps, etc.) on a page, it is possible to create them with out have a variable for them. If you plan on making changes at run time or responding to events on them, you can still create them with a variable.
There is a new property that we set to true on the Main_Window, Keyboard_Support. This allows us to respond to move around the various controls using the standard Windows keyboard controls like the tab key, or to go direct to a control such as the display button using its hot key. The buttons hot key is set by using the ampersand (&) before the letter in the text to be used with the Alt-Key combination for instant access.
A more complex example of using controls can be found in form_example.adb located at GWindows\samples\simple
Using GWindows.Scroll_Panels, there is no need to worry if you are running out of space for all your controls! You can create a virtual window with in a Scroll Panel that the user can move around in using scroll bars. The following code demonstrates using Scroll_Panels as top level windows and as controls.
with GWindows.Windows.Main; use GWindows.Windows.Main;
with GWindows.Scroll_Panels; use GWindows.Scroll_Panels;
with GWindows.Buttons; use GWindows.Buttons;
with GWindows.Edit_Boxes; use GWindows.Edit_Boxes;
with GWindows.Static_Controls; use GWindows.Static_Controls;
with GWindows.Message_Boxes;
with GWindows.Events;
with GWindows.Base;
with GWindows.Application;
procedure Tutorial6 is
pragma Linker_Options ("-mwindows");
begin
-- Using a Scroll Panel as a Window
declare
Main_Window : GWindows.Scroll_Panels.Scroll_Panel_Type;
User_Name : GWindows.Edit_Boxes.Edit_Box_Type;
Disp_Button : GWindows.Buttons.Button_Type;
procedure Do_Display
(Window : in out GWindows.Base.Base_Window_Type'Class)
is
begin
GWindows.Message_Boxes.Message_Box
("Scroll Window", Text (User_Name));
end Do_Display;
begin
Create (Main_Window, "Scroll Window", Width => 200, Height => 150);
Visible (Main_Window, True);
On_Destroy_Handler (Main_Window,
GWindows.Events.Do_End_Application'Access);
-- Since Scroll_Panel_Type is not derived from Main_Window_Type
-- it will not automaticly close the application when the window
-- is destroyed. This handler will do that for us.
Panel_Size (Main_Window, 500, 500);
Keyboard_Support (Main_Window.Panel, True);
Create_Label (Main_Window.Panel, "Name :", 150, 10, 50, 25);
Create (User_Name, Main_Window.Panel, "", 230, 10, 100, 25);
Create (Disp_Button, Main_Window.Panel, "&Display", 150, 50, 75, 30);
On_Click_Handler (Disp_Button, Do_Display'Unrestricted_Access);
Focus (User_Name);
GWindows.Application.Message_Loop;
end;
-- Using a Scroll Panel as a control
declare
Main_Window : GWindows.Windows.Main.Main_Window_Type;
Scroll_Panel : GWindows.Scroll_Panels.Scroll_Panel_Type;
User_Name : GWindows.Edit_Boxes.Edit_Box_Type;
Disp_Button : GWindows.Buttons.Button_Type;
procedure Do_Display
(Window : in out GWindows.Base.Base_Window_Type'Class)
is
begin
GWindows.Message_Boxes.Message_Box
("Scroll Window", Text (User_Name));
end Do_Display;
begin
Create (Main_Window, "Scrolling Window 2", Width => 400, Height => 400);
Visible (Main_Window, True);
Create_As_Control (Scroll_Panel, Main_Window,
Top => 20,
Left => 20,
Width => 300,
Height => 300);
Panel_Size (Scroll_Panel, 500, 500);
Keyboard_Support (Scroll_Panel.Panel, True);
Create_Label (Scroll_Panel.Panel, "Name :", 150, 10, 50, 25);
Create (User_Name, Scroll_Panel.Panel, "", 230, 10, 100, 25);
Create (Disp_Button, Scroll_Panel.Panel, "&Display", 150, 50, 75, 30);
On_Click_Handler (Disp_Button, Do_Display'Unrestricted_Access);
Focus (User_Name);
GWindows.Application.Message_Loop;
end;
end Tutorial6;
Just keep in mind when adding controls that the internal panel (Scroll_Panel.Panel) needs to be the parent window and everything else is very straight forward.
As you can see, in GWindows any object derived from GWindows.Windows.Window_Type can be created as either a top level window (using Create) or as a control (Create_As_Control).
In addition to placing controls on a window, we can also draw on a window. In GWindows there are two primary ways of drawing, using a drawing panel, or directly on a window. When drawing directly on to a window, you will need to handle the On_Paint event in order to redraw any of your drawing whenever the operating system requests it. With a drawing panel, this is taken care of for you.
In this tutorial, we present use of the drawing panel, in the next, we will show how to use the On_Paint event. Following that, we will introduce more about drawing and drawing objects in GWindows
with GWindows.Windows.Main; use GWindows.Windows.Main;
with GWindows.Drawing_Panels; use GWindows.Drawing_Panels;
with GWindows.Scroll_Panels; use GWindows.Scroll_Panels;
with GWindows.Drawing;
with GWindows.Colors; use GWindows.Colors;
with GWindows.Events;
with GWindows.Base;
with GWindows.Application;
procedure Tutorial7 is
pragma Linker_Options ("-mwindows");
use GWindows.Drawing;
procedure Draw_Something
(Canvas : in out GWindows.Drawing.Canvas_Type'Class)
is
begin
for N in 1 .. 100 loop
Fill_Rectangle (Canvas,
(N * 2, N * 2, N + 20, N + 20),
COLOR_3DHILIGHT);
Fill_Rectangle (Canvas,
(N * 2 + 200, N * 2 + 200, N + 180, N + 180),
COLOR_DESKTOP);
end loop;
end Draw_Something;
begin
-- Using a Drawing Panel as a Window
declare
Main_Window : GWindows.Drawing_Panels.Drawing_Panel_Type;
Canvas : GWindows.Drawing.Memory_Canvas_Type;
begin
Create (Main_Window, "Drawing Window", Width => 200, Height => 125);
Visible (Main_Window, True);
On_Destroy_Handler (Main_Window,
GWindows.Events.Do_End_Application'Access);
-- Since Drawing_Panel_Type is not derived from Main_Window_Type
-- it will not automaticly close the application when the window
-- is destroyed. This handler will do that for us.
Auto_Resize (Main_Window, False);
Resize_Canvas (Main_Window,
GWindows.Application.Desktop_Width,
GWindows.Application.Desktop_Height);
-- By turning off auto resize and setting canvas to the size of
-- of the desktop contents will be saved no matter how we
-- resize the window
Get_Canvas (Main_Window, Canvas);
Redraw (Main_Window, False);
Draw_Something (Canvas);
GWindows.Application.Message_Loop;
end;
-- Using a Drawing Panel as a Control
declare
Main_Window : GWindows.Windows.Main.Main_Window_Type;
Draw_Control : GWindows.Drawing_Panels.Drawing_Panel_Type;
Canvas : GWindows.Drawing.Memory_Canvas_Type;
begin
Create (Main_Window, "Drawing Window", Width => 400, Height => 400);
Visible (Main_Window, True);
Create_As_Control (Draw_Control, Main_Window,
Top => 20,
Left => 20,
Width => 300,
Height => 300);
Get_Canvas (Draw_Control, Canvas);
Redraw (Draw_Control, False);
Draw_Something (Canvas);
GWindows.Application.Message_Loop;
end;
-- Using a Drawing Panel in a Scroll Panel
declare
Main_Window : GWindows.Windows.Main.Main_Window_Type;
Scroll_Panel : GWindows.Scroll_Panels.Scroll_Panel_Type;
Draw_Control : GWindows.Drawing_Panels.Drawing_Panel_Type;
Canvas : GWindows.Drawing.Memory_Canvas_Type;
begin
Create (Main_Window, "Drawing Window", Width => 400, Height => 400);
Visible (Main_Window, True);
Create_As_Control (Scroll_Panel, Main_Window,
Top => 20,
Left => 20,
Width => 300,
Height => 300);
Panel_Size (Scroll_Panel, 500, 500);
Create_As_Control (Draw_Control, Scroll_Panel.Panel,
Top => 0,
Left => 0,
Width => 0,
Height => 0);
Dock (Draw_Control, GWindows.Base.Fill);
Dock_Children (Scroll_Panel.Panel);
Get_Canvas (Draw_Control, Canvas);
Redraw (Draw_Control, False);
Draw_Something (Canvas);
GWindows.Application.Message_Loop;
end;
end Tutorial7;
As you can see after doing drawing on the internal canvas of the draw panel, we call Redraw on the panel to have the internal canvas painted on to the window. One advantage is that this provides double buffering for smooth animation and other graphic tasks.
When using the Scroll Panel version you may notice a little bit of flicker when scrolling around. This can be removed by setting an On_Erase_Background handler on Scroll_Panel.Panel that does nothing and thus prevents a draw of the background color before a redraw of the contents of the panel.
One interesting set of procedures called in this example not directly connected to drawing worth noting is:
Dock (Draw_Control, GWindows.Base.Fill);
Dock_Children (Scroll_Panel.Panel);
GWindows has a simple docking mechanism for laying out panels and controls for more examples on docking see GWindows\samples\simple\dock_test.adb
In traditional Windows painting, you would handle paint requests from the OS. This model is still available in GWindows as an alternative to Drawing Panels. Here is simple code that demonstrates how:
with GWindows.Windows.Main; use GWindows.Windows.Main;
with GWindows.Drawing;
with GWindows.Colors;
with GWindows.Types;
with GWindows.Base;
with GWindows.Application;
procedure Tutorial8 is
pragma Linker_Options ("-mwindows");
procedure Do_Paint
(Window : in out GWindows.Base.Base_Window_Type'Class;
Canvas : in out GWindows.Drawing.Canvas_Type;
Area : in GWindows.Types.Rectangle_Type)
is
use GWindows.Drawing;
use GWindows.Colors;
begin
for N in 1 .. 100 loop
Fill_Rectangle (Canvas,
(N * 2, N * 2, N + 20, N + 20),
COLOR_3DHILIGHT);
Fill_Rectangle (Canvas,
(N * 2 + 200, N * 2 + 200, N + 180, N + 180),
COLOR_DESKTOP);
end loop;
end Do_Paint;
Main_Window : GWindows.Windows.Main.Main_Window_Type;
begin
Create (Main_Window, "On_Paint Drawing Window",
Width => 200, Height => 200);
Visible (Main_Window, True);
On_Paint_Handler (Main_Window, Do_Paint'Unrestricted_Access);
GWindows.Application.Message_Loop;
end Tutorial8;
No suprises here, just a matter of setting an On_Paint handler. You could of course do the same thing using inheritance and overriding On_Pain, but we will leave that as an exercise for the reader.
The two basic concepts to be understood for drawing in GWindows is the Canvas and the Drawing Object. You draw on a Canvas with Drawing Objects, surprise!
You can request a Canvas_Type directly from a Window at any type by using the Get_Canvas method or receive a canvas as part of an On_Paint or On_Erase_Background message. The Printer_Canvas_Type is received from calls to Choose_Printer or Choose_Default_Printer from GWindows.Common_Dialogs. You request a Memory_Canvas_Type from Drawing Panels.
In all cases, the same drawing methods are used from GWindows.Drawing.
In GWindows.Drawing_Objects there are a number of Drawing_Objects that you can select in to a canvas using Select_Object. Once selected in, they will be used for drawing operations. Example so Drawing_Objects include Brushes, pens, bitmaps and icons.
Here is a simple example of using various drawing objects.
with GWindows.Drawing_Panels; use GWindows.Drawing_Panels;
with GWindows.Drawing; use GWindows.Drawing;
with GWindows.Drawing_Objects; use GWindows.Drawing_Objects;
with GWindows.Colors; use GWindows.Colors;
with GWindows.Events;
with GWindows.Base;
with GWindows.Application;
procedure Tutorial9 is
pragma Linker_Options ("-mwindows");
Main_Window : GWindows.Drawing_Panels.Drawing_Panel_Type;
Canvas : GWindows.Drawing.Memory_Canvas_Type;
Brush : GWindows.Drawing_Objects.Brush_Type;
Pen : GWindows.Drawing_Objects.Pen_Type;
begin
Create (Main_Window, "On_Paint Drawing Window",
Width => 200, Height => 200);
Visible (Main_Window, True);
On_Destroy_Handler (Main_Window,
GWindows.Events.Do_End_Application'Access);
Auto_Resize (Main_Window, False);
Resize_Canvas (Main_Window,
GWindows.Application.Desktop_Width,
GWindows.Application.Desktop_Height,
False);
Get_Canvas (Main_Window, Canvas);
Create_Pen (Pen,
Style => Solid,
Width => 3,
Color => Blue);
Select_Object (Canvas, Pen);
Create_Solid_Brush (Brush,
Color =>
Select_Object (Canvas, Brush);
Ellipse (Canvas,
10, 10,
Client_Area_Width (Main_Window) - 10,
Client_Area_Height (Main_Window) - 10);
Create_Hatch_Brush (Brush,
Style => Cross,
Color => Red);
Select_Object (Canvas, Brush);
Create_Stock_Pen (Pen, Null_Pen);
Select_Object (Canvas, Pen);
Rectangle (Canvas,
25, 25,
Client_Area_Width (Main_Window) - 25,
Client_Area_Height (Main_Window) - 25);
Redraw (Main_Window, False);
GWindows.Application.Message_Loop;
end Tutorial9;
Printing with GWindows is just an extension of drawing on any canvas. You can use one of two procedures in GWindows.Common_Dialogs to choose the printer to output to. Once you have the printers canvas, you need to start the document and give it a name for the print spooler using Start_Document. Then use Start_Page and End_Page to mark off each page.
with Interfaces.C;
with GWindows.Drawing; use GWindows.Drawing;
with GWindows.Common_Dialogs; use GWindows.Common_Dialogs;
with GWindows.Message_Boxes;
with GWindows.Windows;
procedure Tutorial10 is
Canvas : GWindows.Drawing.Printer_Canvas_Type;
Settings : GWindows.Common_Dialogs.DEVMODE;
Flags : Interfaces.C.unsigned := 0;
From_Page : Natural := 1;
To_Page : Natural := 1;
Copies : Natural := 1;
Success : Boolean;
Null_Win : GWindows.Windows.Window_Type;
begin
Choose_Printer (Null_Win, Canvas, Settings, Flags,
From_Page, To_Page, 1, 1, Copies, Success);
if Success then
Start_Document (Canvas, "Tutorial10 Document");
Start_Page (Canvas);
Put (Canvas, 100, 100, "Hello World!");
Ellipse (Canvas, 200, 200, 500, 500);
End_Page (Canvas);
End_Document (Canvas);
GWindows.Message_Boxes.Message_Box ("Tutorial 10", "Done");
else
GWindows.Message_Boxes.Message_Box ("Tutorial 10",
"Printing Canceled");
end if;
end Tutorial10;
GWindows can create menus in code or load menus from resources embedded in the application. Menus once loaded can then be placed on windows or used as context menus that pop up on a right mouse button click.
with GWindows.Windows.Main; use GWindows.Windows.Main;
with GWindows.Menus; use GWindows.Menus;
with GWindows.Application;
with GWindows.Base;
procedure Tutorial11 is
pragma Linker_Options ("-mwindows");
Main_Window : Main_Window_Type;
Main_Menu
: Menu_Type := Create_Menu;
File_Menu
: Menu_Type := Create_Popup;
Sub_Menu
: Menu_Type := Create_Popup;
ID_Exit
: constant := 100;
ID_Open
: constant := 101;
ID_Save
: constant := 102;
ID_About : constant := 103;
procedure Do_Menu_Select
(Window : in out GWindows.Base.Base_Window_Type'Class;
Item : in Integer)
is
begin
case Item is
when ID_Exit =>
GWindows.Application.End_Application;
when others =>
null;
end case;
end Do_Menu_Select;
procedure Do_Context_Menu
(Window : in out GWindows.Base.Base_Window_Type'Class;
X : in
Integer;
Y
: in Integer)
is
begin
Display_Context_Menu (Main_Window_Type (Window), File_Menu, 0, X, Y);
end Do_Context_Menu;
begin
Create (Main_Window, "My First Window");
Visible (Main_Window, True);
Append_Item (File_Menu, "&Open", ID_Open);
Append_Item (File_Menu, "&Save", ID_Save);
Append_Separator (File_Menu);
Append_Item (File_Menu, "E&xit", ID_Exit);
Append_Menu (Main_Menu, "&Files", File_Menu);
Append_Item (Sub_Menu, "A&bout", ID_About);
Append_Menu (File_Menu, "Other", Sub_Menu);
Menu (Main_Window, Main_Menu);
On_Menu_Select_Handler (Main_Window, Do_Menu_Select'Unrestricted_Access);
On_Context_Menu_Handler (Main_Window, Do_Context_Menu'Unrestricted_Access);
GWindows.Application.Message_Loop;
end Tutorial11;
You may have noticed in Tutorial 5 that the fonts used in the controls looks a little different than most Windows applications. By default Windows sets a window to use the System font. GWindows allows you to change the font used by any window. When created, controls take their font from their parent window, but this can be changed at any time.
Add a with for GWindows.Drawing_Objects to Tutorial 5 and then after the Create of the Window add:
declare
Window_Font : GWindows.Drawing_Objects.Font_Type;
begin
-- Use Standard Windows GUI font instead of system font
GWindows.Drawing_Objects.Create_Stock_Font
(Window_Font, GWindows.Drawing_Objects.Default_GUI);
Set_Font (Main_Window, Window_Font);
end;
If you wanted you could just change the font of the button by doing:
Set_Font (Disp_Button, Window_Font)
The order that windows appear on the screen
is easily controlled using the Order method. As you can also see from this
example GWindows types are like any other
with GWindows.Windows.Main; use GWindows.Windows.Main;
with GWindows.Windows; use GWindows.Windows;
with GWindows.Buttons; use GWindows.Buttons;
with GWindows.GStrings; use GWindows.GStrings;
with GWindows.Base;
with GWindows.Application;
procedure Tutorial13 is
pragma Linker_Options ("-mwindows");
subtype Window_Number_Type is Positive range 1 .. 5;
type Window_Array_Type is array (Window_Number_Type'Range) of
GWindows.Windows.Window_Type;
Main_Window : GWindows.Windows.Main.Main_Window_Type;
Windows : Window_Array_Type;
Next_Button : GWindows.Buttons.Button_Type;
Current_Window : Window_Number_Type := Window_Number_Type'Last;
procedure Do_Click
(Window : in out GWindows.Base.Base_Window_Type'Class)
is
begin
Order (Windows (Current_Window), GWindows.Base.Bottom);
if Current_Window = Window_Number_Type'Last then
Current_Window := Window_Number_Type'First;
else
Current_Window := Window_Number_Type'Succ (Current_Window);
end if;
end Do_Click;
begin
Create (Main_Window, "Z-Order Window", Width => 100, Height => 100);
Visible (Main_Window, True);
Center (Main_Window);
for N in Window_Array_Type'Range loop
Create (Windows (N), To_GString_From_String ("TEST" & N'Img),
Width => 75, Height => 75);
Visible (Windows (N));
end loop;
Create (Next_Button, Main_Window, "&Next", 10, 10,
Client_Area_Width (Main_Window) - 20,
Client_Area_Height (Main_Window) - 20);
On_Click_Handler (Next_Button, Do_Click'Unrestricted_Access);
GWindows.Application.Message_Loop;
end Tutorial13;
In GWindows it is possible to access the parent window of a child window or control and even to re-parent the window or control using the Parent property.
with GWindows.Windows.Main; use GWindows.Windows.Main;
with GWindows.Buttons; use GWindows.Buttons;
with GWindows.Base;
with GWindows.Application;
procedure Tutorial14 is
pragma Linker_Options ("-mwindows");
Window1 : Main_Window_Type;
Window2 : Main_Window_Type;
Jump_Button : Button_Type;
procedure Do_Click (Window : in out GWindows.Base.Base_Window_Type'Class) is
use GWindows.Base;
begin
if Text (Parent (Window).all) = "Window 1" then
Parent (Window, Window2);
else
Parent (Window, Window1);
end if;
end Do_Click;
begin
Create (Window1, "Window 1", Width => 100, Height => 100);
Visible (Window1, True);
Create (Window2, "Window 2",
Top => Top (Window1),
Left => Left (Window1) + 200,
Width => 100, Height => 100);
Visible (Window2, True);
Create (Jump_Button, Window1, "Jump!", 10, 10,
Client_Area_Width (Window1) - 20,
Client_Area_Height (Window1) - 20);
On_Click_Handler (Jump_Button, Do_Click'Unrestricted_Access);
GWindows.Application.Message_Loop;
end Tutorial14;
In Windows, you are generally only able to capture mouse movements with in your window, but by using the Capture and Release methods in the up and down button handlers (Window only lets you capture while the mouse button is down), you can capture the mouse movements even off your window.
with GWindows.Windows.Main; use GWindows.Windows.Main;
with GWindows.Static_Controls; use GWindows.Static_Controls;
with GWindows.GStrings; use GWindows.GStrings;
with GWindows.Base;
with GWindows.Application;
procedure Tutorial15 is
pragma Linker_Options ("-mwindows");
Main_Window : Main_Window_Type;
X_Label : Label_Type;
Y_Label : Label_Type;
procedure Do_Mouse_Move
(Window : in out GWindows.Base.Base_Window_Type'Class;
X : in
Integer;
Y
: in Integer;
Keys : in GWindows.Windows.Mouse_Key_States)
is
begin
Text (X_Label, GWindows.GStrings.Image (X));
Text (Y_Label, GWindows.GStrings.Image (Y));
end Do_Mouse_Move;
procedure Do_Left_Mouse_Button_Down
(Window : in out GWindows.Base.Base_Window_Type'Class;
X : in
Integer;
Y
: in Integer;
Keys : in GWindows.Windows.Mouse_Key_States)
is
begin
Capture_Mouse (Main_Window);
end Do_Left_Mouse_Button_Down;
procedure Do_Left_Mouse_Button_Up
(Window : in out GWindows.Base.Base_Window_Type'Class;
X : in
Integer;
Y
: in Integer;
Keys : in GWindows.Windows.Mouse_Key_States)
is
begin
GWindows.Base.Release_Mouse;
end Do_Left_Mouse_Button_Up;
begin
Create (Main_Window, "Mouse Demo Window", Width => 200, Height => 200);
Visible (Main_Window, True);
Create (X_Label, Main_Window, "0", 10, 10, 40, 25, Center);
Create (Y_Label, Main_Window, "0", 60, 10, 40, 25, Center);
On_Mouse_Move_Handler (Main_Window, Do_Mouse_Move'Unrestricted_Access);
On_Left_Mouse_Button_Down_Handler (Main_Window,
Do_Left_Mouse_Button_Down'Unrestricted_Access);
On_Left_Mouse_Button_Up_Handler (Main_Window,
Do_Left_Mouse_Button_Up'Unrestricted_Access);
GWindows.Application.Message_Loop;
end Tutorial15;
In order to change the Cursor in GWindows, you need to handle the On_Cursor_Change event and use the Set_Cursor procedure from GWindows.Cursors.
with GWindows.Windows.Main; use GWindows.Windows.Main;
with GWindows.Cursors;
with GWindows.Base;
with GWindows.Application;
procedure Tutorial16 is
pragma Linker_Options ("-mwindows");
Main_Window : Main_Window_Type;
procedure Do_Change_Cursor
(Window : in out GWindows.Base.Base_Window_Type'Class)
is
use GWindows.Cursors;
begin
Set_Cursor (Load_System_Cursor (IDC_CROSS));
end Do_Change_Cursor;
begin
Create (Main_Window, "Cursor Window", Width => 200, Height => 200);
Visible (Main_Window, True);
On_Change_Cursor_Handler (Main_Window,
Do_Change_Cursor'Unrestricted_Access);
GWindows.Application.Message_Loop;
end Tutorial16;
Creating custom dialogs in GWindows is easy. Just open the window using Show_Dialog instead of setting its visible property to true. If you use the Dialog_Button_Type, you can set a custom ID for the return value.
with GWindows.GStrings.IO; use GWindows.GStrings.IO;
with GWindows.Windows; use GWindows.Windows;
with GWindows.Buttons; use GWindows.Buttons;
with GWindows.Static_Controls; use GWindows.Static_Controls;
with GWindows.Application;
with GWindows.Constants; use GWindows.Constants;
procedure Tutorial17 is
Dialog : Window_Type;
OK_Button : Default_Button_Type;
Cancel_Button : Cancel_Button_Type;
More_Button : Dialog_Button_Type;
ID_MORE : constant := 101;
Result : Integer := ID_MORE;
begin
while Result = ID_MORE loop
Create_As_Dialog (Dialog, "My Dialog Window",
Width => 200, Height => 100);
Create_Label (Dialog, "Have you had enough yet?",
10, 10,
Client_Area_Width (Dialog) - 20,
25, Center);
Create (OK_Button, Dialog, "O&k",
10, Client_Area_Height (Dialog) - 40,
50, 25, ID => IDOK);
Create (Cancel_Button, Dialog, "&Cancel",
70, Client_Area_Height (Dialog) - 40,
50, 25, ID => IDCANCEL);
Create (More_Button, Dialog, "&More",
130, Client_Area_Height (Dialog) - 40,
50, 25, ID => ID_MORE);
Result := GWindows.Application.Show_Dialog (Dialog);
end loop;
if Result = IDOK then
Put_Line ("Have a nice day!");
else
Put_Line ("Sorry for the trouble...");
end if;
end Tutorial17;
What good would a RAD GUI tool be with out Database support?
GWindows uses
with
GNATCOM.Initialize;
with
GWindows.GStrings.IO; use GWindows.GStrings.IO;
with
GWindows.Databases; use GWindows.Databases;
procedure
Tutorial18 is
Connection : Database_Type;
Recordset
: Recordset_Type;
begin
GNATCOM.Initialize.Initialize_COM;
Open (Connection,
"Provider=Microsoft.Jet.OLEDB.4.0; " &
"Data Source=adotest.mdb");
Open (Recordset,
Connection,
"SELECT * FROM People",
Dynamic,
Optimistic);
while not EOF (Recordset) loop
for
N in 1 .. Field_Count (Recordset) loop
Put_Line
(Field_Name (Recordset, N) &
" = " & Field_Value (Recordset, N));
end loop;
if Field_Value (Recordset,
"LastName") = "TestLName" then
Put_Line ("This record is being
deleted");
Delete (Recordset);
end if;
Move_Next (Recordset);
New_Line;
end loop;
Add_New (Recordset);
Field_Value (Recordset,
"LastName", "TestLName");
Field_Value (Recordset,
"FirstName", "TestLName");
Update (Recordset);
Close (Recordset);
Close (Connection);
end
Tutorial18;
Since
GWindows goes a step further then just offering database bindings, you also have the ability to bind controls to a recordset (through GWindows.Databases.Controls.Binding_Recordset_Type) and even get a pre made database GUI control.
Here is an example of binding controls and using the Database_Control_Type:
with GNATCOM.Initialize;
with GNATCOM.IErrorInfo;
with GWindows.GStrings; use GWindows.GStrings;
with GWindows.Databases.Controls;
use GWindows.Databases.Controls; use GWindows.Databases;
with GWindows.Windows.Main; use GWindows.Windows.Main;
with GWindows.Edit_Boxes; use GWindows.Edit_Boxes;
with GWindows.Static_Controls; use GWindows.Static_Controls;
with GWindows.Message_Boxes; use GWindows.Message_Boxes;
with GWindows.Base; use GWindows.Base;
with GWindows.Application;
procedure Tutorial19 is
Main_Window : Main_Window_Type;
ID : aliased Edit_Box_Type;
LastName : aliased Edit_Box_Type;
FirstName : aliased Edit_Box_Type;
Address : aliased Edit_Box_Type;
City : aliased Edit_Box_Type;
State : aliased Edit_Box_Type;
Zip : aliased Edit_Box_Type;
Country : aliased Edit_Box_Type;
DB_Control : Database_Control_Type;
begin
GNATCOM.Initialize.Initialize_COM;
-- Setup Controls
Create (Main_Window, "Database Window", Width => 400, Height => 500);
Keyboard_Support (Main_Window);
Center (Main_Window);
Create_As_Control (DB_Control, Main_Window, "", 0, 0, 0, 30);
Dock (DB_Control, At_Top);
Create_Label (Main_Window, "ID", 10, 40, 100, 25);
Create_Label (Main_Window, "Last Name", 10, 80, 100, 25);
Create_Label (Main_Window, "First Name", 10, 120, 100, 25);
Create_Label (Main_Window, "Address", 10, 160, 100, 25);
Create_Label (Main_Window, "City", 10, 200, 100, 25);
Create_Label (Main_Window, "State", 10, 240, 100, 25);
Create_Label (Main_Window, "Zip", 10, 280, 100, 25);
Create_Label (Main_Window, "Country", 10, 320, 100, 25);
Create (ID, Main_Window, "",
120, 40, 200, 30);
Read_Only (ID);
Create (LastName, Main_Window, "",
120, 80, 200, 30);
Create (FirstName, Main_Window, "",
120, 120, 200, 30);
Create (Address, Main_Window, "",
120, 160, 200, 30);
Create (City, Main_Window, "",
120, 200, 200, 30);
Create (State, Main_Window, "",
120, 240, 200, 30);
Create (Zip, Main_Window, "",
120, 280, 200, 30);
Create (Country, Main_Window, "",
120, 320, 200, 30);
-- Setup Database and Bindings
Open (DB_Control.Database,
"Provider=Microsoft.Jet.OLEDB.4.0; " &
"Data Source=adotest.mdb");
Open (DB_Control.Recordset,
DB_Control.Database,
"SELECT * FROM People",
Dynamic,
Optimistic);
Bind_Text_Control (DB_Control.Recordset,
"ID",
ID'Unchecked_Access,
Read_Only);
Bind_Text_Control (DB_Control.Recordset,
"LastName",
LastName'Unchecked_Access);
Bind_Text_Control (DB_Control.Recordset,
"FirstName",
FirstName'Unchecked_Access);
Bind_Text_Control (DB_Control.Recordset,
"Address",
Address'Unchecked_Access);
Bind_Text_Control (DB_Control.Recordset,
"City",
City'Unchecked_Access);
Bind_Text_Control (DB_Control.Recordset,
"State",
State'Unchecked_Access);
Bind_Text_Control (DB_Control.Recordset,
"Zip",
Zip'Unchecked_Access);
Bind_Text_Control (DB_Control.Recordset,
"Country",
Country'Unchecked_Access);
-- Get Started
Fill_Bindings (DB_Control.Recordset);
Dock_Children (Main_Window);
Visible (Main_Window, True);
GWindows.Application.Message_Loop;
exception
when others =>
Message_Box ("Error",
To_GString_From_String (GNATCOM.IErrorInfo.Get_IErrorInfo),
Icon => Error_Icon);
end Tutorial19;
For an example of using Dynamic windows to automatically create a front end to a database see GWindows\samples\databases\db_view.adb
GWindows offers additional flexability beyond the standard Windows method of placing controls in a window with the use of Docking and Packing. This example demonstrates how they can be used together.
with GWindows.Windows.Main; use GWindows.Windows.Main;
with GWindows.Buttons; use GWindows.Buttons;
with GWindows.Static_Controls; use GWindows.Static_Controls;
with GWindows.Edit_Boxes; use GWindows.Edit_Boxes;
with GWindows.Packing_Boxes; use GWindows.Packing_Boxes;
with GWindows.Base;
with GWindows.Application;
procedure Tutorial20 is
Main_Window : Main_Window_Type;
Box : Packing_Box_Type;
Box2 : Packing_Box_Type;
Box3 : Packing_Box_Type;
Box4 : Packing_Box_Type;
Button1 : Button_Type;
Button2 : Button_Type;
Button3 : Button_Type;
Button4 : Button_Type;
Button5 : Button_Type;
Button6 : Button_Type;
Edit1 : Edit_Box_Type;
Edit2 : Edit_Box_Type;
Edit3 : Edit_Box_Type;
begin
Create (Main_Window, "Packing Boxes in a Window",
Width => 500, Height => 250);
Create (Box, Main_Window, 0, 0, 100, 0, Vertical);
Dock (Box, GWindows.Base.At_Left);
Fill_Width (Box);
Fill_Height (Box);
Padding (Box, 5);
Insets (Box, (10, 10, 10, 10));
Create (Box2, Main_Window, 0, 0, 0, 50, Horizontal);
Dock (Box2, GWindows.Base.At_Top);
Fill_Width (Box2);
Fill_Height (Box2);
Padding (Box2, 5);
Insets (Box2, (10, 10, 10, 10));
Create (Box3, Main_Window, 0, 0, 100, 0, Vertical);
Dock (Box3, GWindows.Base.At_Left);
Fill_Width (Box3);
Padding (Box3, 5);
Insets (Box3, (10, 15, 10, 10));
Create (Box4, Main_Window, 0, 0, 0, 0, Vertical);
Dock (Box4, GWindows.Base.Fill);
Fill_Width (Box4);
Padding (Box4, 5);
Insets (Box4, (10, 10, 10, 10));
Dock_Children (Main_Window);
Create (Button1, Box, "Button1", 0, 0, 0, 0);
Create (Button2, Box, "Button2", 0, 0, 0, 0);
Create (Button3, Box, "Button3", 0, 0, 0, 0);
Pack (Box);
Create (Button4, Box2, "Button3", 0, 0, 0, 0);
Create (Button5, Box2, "Button4", 0, 0, 0, 0);
Create (Button6, Box2, "Button5", 0, 0, 0, 0);
Pack (Box2);
Create_Label (Box3, "Field 1", 0, 0, 0, 30);
Create_Label (Box3, "Field 2", 0, 0, 0, 30);
Create_Label (Box3, "Field 3", 0, 0, 0, 30);
Pack (Box3);
Create (Edit1, Box4, "", 0, 0, 0, 30);
Create (Edit2, Box4, "", 0, 0, 0, 30);
Create (Edit3, Box4, "", 0, 0, 0, 30);
Pack (Box4);
Visible (Main_Window);
GWindows.Application.Message_Loop;
end Tutorial20;
GWindows offers some basic procedures to play wav files from files, resources or system events.
with GWindows.Multi_Media; use GWindows.Multi_Media;
procedure Tutorial21 is
begin
Play_Sound_From_Alias ("SystemStart");
Play_Sound_From_File ("hello.wav");
end Tutorial21;
The available Aliases can be found by looking at the registry at HKEY_CURRENT_USER\AppEvents\EvetLables. Any of the key names listed there can be used. The sound associated with the event in the control panel applet for sounds will then be played.
There are so many overloaded terms used in programming for ActiveX, that the best way to quickly learn how to make use of ActiveX and GWindows is just to do it.
First, lets pick an ActiveX control to bind using GBManager.
Run the program gwindows\tools\gbmanager\gbmanager.exe
From the File menu of gbmanager we can pick the location that GBManager will use for storing COM bindings by using the menu option Set Binding Repository Location. I have a directory called C:\Bindings where I place all my COM bindings. You only have to set the location once and it will be stored in the registry and GNAT will automatically look for the bindings in that location.
Look down the tree for the type library called: Microsoft Internet Controls
Expand the tree and you should see the various versions of the library that are available for binding. On my machine, it is version 1.1.
Click on the latest version and choose Bind from the file menu.
When asked for the base package name to use type: ie
After a few seconds, you will have a full binding to the explorer shell of the operating system. Using it we can embed a web/file browser in to our application.
You can now close GBManager.
If you go to the binding repository directory and open ie.ads, you can do a search in the text file for CLSID. The CLSID is a GUID (Global Unique Identifier) that uniquely names an ActiveX object. For our example, we are going to use the WebBrowser Control, CLSID_WebBrowser.
Now we are set to write our application:
with GWindows.Windows.Main; use GWindows.Windows.Main;
with GWindows.ActiveX; use GWindows.ActiveX;
with GWindows.GStrings;
with GWindows.Base;
with GWindows.Application;
with GNATCOM.Initialize;
with IE;
with IE.IWebBrowser_Interface;
procedure Tutorial22 is
Main_Window : Main_Window_Type;
IE_Control : Activex_Type;
IE_COM : IE.IWebBrowser_Interface.IWebBrowser_Type;
begin
GNATCOM.Initialize.Initialize_COM;
Create (Main_Window, "My Web Browser");
Create (IE_Control, Main_Window,
IE.CLSID_WebBrowser,
1, 1, 1, 1);
Dock (IE_Control, GWindows.Base.Fill);
Visible (Main_Window);
IE.IWebBrowser_Interface.Query (IE_COM, Interface (IE_Control));
IE.IWebBrowser_Interface.Navigate (IE_COM,
GWindows.GStrings.To_BSTR_From_GString
("http://www.adapower.com"));
GWindows.Application.Message_Loop;
end Tutorial22;
Once we create the control, we use
GWindows.ActiveX.Interface to access its internal COM interface. The COM
interface returned though is the generic IQueryInterface. Since we generated
thick bindings to the IWebBrowser interface, we use the Query procedure of the
generated IWebBrowser_Type to access the
object in a more natural
Now that we know how to create and talk to ActiveX controls it is also important to learn how to listen to them.
First we need to derive an object from the event object in our generated bindings and override any events we wish to handle . The event object for the IWebBrowser interface is located in IE.DWebBrowserEvents2_Events and is called DWebBrowserEvents2_Event.
with IE.DWebBrowserEvents2_Events;
with GNATCOM.Types;
package Tutorial23_Handle_Events is
type Handle_Event_Type is
new IE.DWebBrowserEvents2_Events.DWebBrowserEvents2_Event
with null record;
procedure DocumentComplete
(This : Handle_Event_Type;
pDisp : GNATCOM.Types.VARIANT;
URL : GNATCOM.Types.VARIANT);
-- Override from IE.DWebBrowserEvents2_Events.DWebBrowserEvents2_Event
end Tutorial23_Handle_Events;
Next implement event handlers we have overriden:
with GWindows.Message_Boxes; use GWindows.Message_Boxes;
with GWindows.GStrings;
package body Tutorial23_Handle_Events is
----------------------
-- DocumentComplete --
----------------------
procedure DocumentComplete
(This : Handle_Event_Type;
pDisp : GNATCOM.Types.VARIANT;
URL : GNATCOM.Types.VARIANT)
is
begin
Message_Box ("Tutorial23",
GWindows.GStrings.To_GString_From_VARIANT
(URL, Clear => false) &
" - Document has been loaded.");
-- Do not clear or free parameters passed in from events
end DocumentComplete;
end Tutorial23_Handle_Events;
Now it is time to connect our event object to the ActiveX control. We start out with the same basic code as the last tutorial with a few additions:
with GWindows.Windows.Main; use GWindows.Windows.Main;
with GWindows.ActiveX; use GWindows.ActiveX;
with GWindows.GStrings;
with GWindows.Base;
with GWindows.Application;
with GNATCOM.Initialize;
with GNATCOM.Create.COM_Interface;
with GNATCOM.Events;
with IE;
with IE.IWebBrowser_Interface;
with IE.DWebBrowserEvents2_Events;
with Tutorial23_Handle_Events;
procedure Tutorial23 is
Main_Window : Main_Window_Type;
IE_Control : Activex_Type;
IE_COM : IE.IWebBrowser_Interface.IWebBrowser_Type;
Handle_Event_Object : aliased Tutorial23_Handle_Events.Handle_Event_Type;
-- Create event object
Handle_Event : GNATCOM.Create.COM_Interface.Pointer_To_COM_Interface_Type :=
IE.DWebBrowserEvents2_Events.Create
(Handle_Event_Object'Unchecked_Access);
-- Create COM interface wrapper around event object
Handle_Events_Connection_Point : GNATCOM.Events.IConnectionPoint_Type;
-- Interface to connection point with in COM object
-- Used to connect and disconnect event object
-- Disconnect is performed automaticly when Handle_Events is finalized
begin
GNATCOM.Initialize.Initialize_COM;
Create (Main_Window, "My Web Browser");
Create (IE_Control, Main_Window,
IE.CLSID_WebBrowser,
1, 1, 1, 1);
Dock (IE_Control, GWindows.Base.Fill);
Visible (Main_Window);
IE.IWebBrowser_Interface.Query (IE_COM, Interface (IE_Control));
IE.DWebBrowserEvents2_Events.Set_Events
(Handle_Events_Connection_Point,
For_Object => IE_COM,
Event_Interface => Handle_Event);
IE.IWebBrowser_Interface.Navigate (IE_COM,
GWindows.GStrings.To_BSTR_From_GString
("http://www.adapower.com"));
GWindows.Application.Message_Loop;
end Tutorial23;
The additions to the code are
1. Turn our event object in to an ActiveX object (a.k.a a COM object).
First we create an aliased instance of our object, in this case Handle_Event_Object.
Then we use the Create procedure from the package we derived our event object from (IE.DWebBrowserEvents2). This new COM object we called Handle_Event.
2. Events are connected to ActiveX controls through what are called connection points. Se we need to set up an interface to it. We call this interface in our tutorial, Handle_Event_Connection_Point.
3. To actually connect everything together, we use the Set_Events procedure from the package we derived our event object from (IE.DWebBrowserEvents2).
There may be a bit of typing involved to set up the event, but it is cookie cutter code just waiting to be copied!
This example deals with the dragging of items from a control to another one, or to another window, or to another application (e.g. Windows explorer or the desktop).
[1] To relocate GWindows projects, it is sufficient to reference
or copy the entire framework directory and also the gnatcom\framework directory. Actually you can put contents of both
folders together for convenience into a dedicated subdirectory in your
projects.