Visual Programming Interface
(c) Copyright 1986-1997
Prolog Development Center A/S
H.J. Holst Vej 3A-5A, Copenhagen
DK - 2605 Broendby
Denmark
Copyright
The documentation for this software is copyrighted, and all rights are reserved. It may not be reproduced, transmitted, stored in a retrieval system, or translated, either by electronic, mechanical or any other means, without the prior written consent of Prolog Development Center A/S.
The software products described in these manuals are also copyrighted, and are licensed to the End User only for use in accordance with the End User License Agreement, which is printed on the diskette packaging. The prospective user should read this agreement carefully prior to use of the software.
Visual Prolog is a registered trademark of Prolog Development Center A/S.
Portions of the Software and Documentation under license from:
Other brand and product names are trademarks or registered trademarks of their respective holders.
Table of Contents
Introduction. The Visual Programming Interface
Event-driven Applications *
About this Book
*Invoking the VPI
*Windows
*Window Types
*Window Styles
*The Client Rectangle
*The Screen Window
*The Task Window
*Top-Level Windows
*Child Windows
*Controls
*Dialogs
*Event-Handlers
*Event Handler Return Values
*Creating Windows
*Creating a Regular Window
*Creating a Dialog
*Creating a Control
*Creating Windows Dynamically
*Destroying Windows
*Retrieving the Client Rectangle
*The Clipping Rectangle
*Retrieving the Outer Rectangle
*Calculating Client Rectangle from Outer Rectangle
*Moving and Re-sizing Windows
*Changing the State for a Window
*Disabling and Enabling Windows
*Hiding and Showing Windows
*Minimizing, Maximizing and Restoring Windows
*Changing the Icon Associated with a Window
*Changing the Window Text or Window Title
*Changing the Event-handler
*Connecting Data to a Window
*Finding the Task Window
*Finding the Parent Window
*Finding the Active Window
*Setting the Focus
*Finding the Window which has the Focus
*Window Ordering
*Window Updating
*Invalidate Window
*Validate Part of the Window
*Check whether Part of the Window Needs Updating
*Force Updating of the Window
*Events
*Window Creation
*Window Being Destroyed
*User Requests to Close Window
*GUI is Closing Down
*Menu Becomes Active
*A Menu Selection has been Made
*Mouse Click
*Mouse Double Click
*Mouse Moved in Window
*Mouse Button Released
*Keyboard Input
*A Key has been Pressed Down
*A Key has been Released
*Horizontal Scroll Event
*Vertical Scroll Event
*Focus is being Gained
*Focus is being Lost
*Background Erasing
*Window Location has Changed
*Window has Changed Size
*Window has Changed State
*Native GUI Events
*Owner Drawing
*Return Owner Draw Size
*Updating the Window
*Timer Interval has Expired
*A Programmed Event has Occurred
*Dynamic Data Exchange
*Control Notification Events
*The CONTROL_INFO Domain
*Application Termination Notification Events
*Application has Activated
*Application has been Deactivated
*Controls
*Manipulating Controls
*Style Flags
*Events from Controls
*Creating Controls
*The Various Controls
*Group Box Controls
*Icon Controls
*Static Text Controls
*Edit Controls
*Push Buttons Controls
*Check Box Controls
*Radio Button Controls
*List Box Controls
*List Box Predicates
*List Buttons
*List Edit Boxes
*Notification Events from List Edit Boxes
*Scroll Bars
*Notification Events from Scroll Bars
*Scroll Bar Referencing
*Scroll Bar Range
*Scroll Bar Positioning
*Scroll Bar Proportioning
*Owner Drawing for Controls
*Custom Controls
*Dialogs
*Common Dialogs
*Get User Response to a Yes/No etc. Question
*User Selects a Filename(s) to Opening/Saving a File(s)
*User Input of a String
*User Selects from a List of Strings
*User Selection and Set up of a Printer
*User Selection of a Color
*User Selection of a Font
*Show an Error
*Show a Note
*User Defined Dialogs
*Control Identifiers
*Creating a Dialog
*Initializing a Dialog
*Drawing Operations
*Drawing Tools
*Pens
*Background Modes
*Brushes
*Fonts
*Drawing Modes
*Drawing Predicates
*Drawing a Pixel
*Filling an Area
*Drawing Icons
*Drawing Open Figures
*Drawing Filled Figures
*Drawing Text
*Measuring Text
*Coloring Text
*Font Handling
*Font Creation
*Font Selection
*Obtaining Font Information
*Color Handling
*Coordinate Systems
*Dialog Base Units
*Mapping, Scaling, Panning and Zooming
*Mapping Examples
*Printing
*Start a Print Job
*End a Print Job
*Set a Page Orientation
*Start a New Page
*End a Printed Page
*Abort a Print Job
*Retrieve Printing Settings
*Cursors
*Cursor Predicates
*Examples
*Mouse Handling
*Capturing the Mouse
*Mouse Events
*Rectangles
*Intersection of Two Rectangles
*Inflate the Size of a Rectangle
*Determine Whether the Area of a Rectangle is Zero
*Move a Rectangle
*Determine Whether a Point is Inside a Rectangle
*Create the Union of Two Rectangles
*Event Handling
*Process Pending Events
*Sending and Posting Events
*Resources
*Resource Naming
*Resource Linking
*Bitmaps, Icons and Cursors
*Dialog Resources
*Menu Resources
*String Resources
*Menus
*MENU Domains
*Specifying Actions for Menu Items
*Loading a Menu from the Resource File
*Changing the Text for a Menu Item
*Check Marks
*Enabling/Disabling Menu Items
*Forcing a Menu Update
*Handling Sub-Menus under MS Windows
*Pop-Up Menus
*Timers
*Starting a Timer
*Stopping a Timer
*Pictures
*Converting Pictures to Binary Data
*Creating a Picture from Drawing Operations
*Destroying a Picture
*Drawing a Picture
*No Stretch
*Stretching
*The Raster Operations
*Get a Picture from the Contents of a Window
*Load a Picture from a File
*Load a Picture from the Resource Segment
*Returning Size Information from a Picture
*Saving a Picture to a File
*Rotating a Picture
*The Clipboard
*Getting Data from the Clipboard
*Putting Data on the Clipboard
*Testing for Data on the Clipboard
*Carets
*VPI Attribute Flags
*Window Classes
*Controls Subclassing
*Implementing On-line Help
*Dynamic Data Exchange (DDE)
*The Client Role
*The Server Role
*Client and Server Interaction
*Service/Topic/Items
*Initializing and Closing Down the DDE
*Simple DDE Client Operations
*Connecting to a Server Application
*Executing a Command on the Server Application
*Requesting Data from a Server Application
*Sending Data to a Server Application
*More Advanced Client Operations
*Transactions
*Transferring Data between the Server and a Client
*Restarting a Conversation that has been Terminated
*Multiple Connections
*Wild Connections
*The DDE Server Operations
*Initializing a Server Application
*Accepting a Connection from a Client
*Accepting a Command from a Client
*Accepting Data from a Client
*Responding to a Data Request
*Advisory Loops
*Accepting a Wildconnection
*Temporarily Queuing DDE Events
*Critical Errors
*Table of DDE Predicates/DDE Events
*NETDDE
*Metafile Handling
*Load a Metafile from a File
*Play a Metafile in a Window
*Destroy Metafile
*Metafile Recording
*Error Handling
*Argument Validation
*Installing a New Error Handler
*Native API Functions
*Calling Native GUI Functions
*Creating a Window outside the VPI
*Native Events for a VPI Window
*Retrieving the Native Window Handle
*Retrieving the Native Device Context
*Running External Applications
*Chapter 2 VPI Packages
Dialog Package *
Creating and Initializing a New Dialog
*The Event-handler for a New Dialog
*Getting the Return Values from a Dialog
*Dialog Domains
*Change Enable and/or Show States for Controls
*Dialog Enable Domains
*Fetch Values of Individual Controls from a Values List.
*Set/Fetch Values of Individual Controls in a Dialog.
*Retrieving Values
*Setting New Values
*Editor API
*Edit Window Creation
*Retrieving the Current Text from an Editor Window
*Adding New Text
*Editing Functions
*Set/Get Current Selection
*Return Position Information
*Moving the Caret
*Undo/Redo
*Clipboard Handling
*Case Reversing
*Activating Editor Dialogs
*Hypertext Links
*Suspend/Resume Editor Updating
*Caret Showing/Hiding
*Hide/Show Status Line
*Color Associations
*Editing Options
*Message Win Package
*Toolbar Package
*Creation of a Toolbar
*Resizing of a Toolbar
*Removing a Toolbar
*Changing the Values in a Toolbar
*Creating Radio Buttons in a Toolbar
*Tree Package
*The Domains in the Tree Tool
*The Predicates in the Tree Tool
*Creating a Window with a Tree
*Creating a Tree in an Already Open Window (Subclass)
*Return a List of All Nodes
*Return the Current Selected Node
*Return the Parent Node for the Current Selected
*Return the Node Type for the Current Selected
*Return Settings from the Tree Window
*Return Rectangle for the Tree Window
*Return Current Search Value - or Fail
*Move Selection to the First Node Matching the Selector
*Sets New Direction for the Tree
*Sets new Font for the Tree
*Set a New Tree in the Window
*Explode/Implode Subtree from the Selected Node
*Select First Node Using the Selector
*Select next node using the chosen Selector
*Make Selected Node Visible
*Move Selection from Child Node to the Parent Node
*Print the Tree
*Ownerdrawing Package
*Index
*
The Visual Programming Interface
The Visual Prolog (VP) product consists of several elements. It has an interactive Visual Development Environment (VDE) which includes text and various graphical editors, code generating tools (Experts), build control logic, and extensions to Prolog in the form of a Visual Programming Interface (VPI). It also includes a Prolog compiler, various include files and libraries, a linker, and various example and help files.
Visual Prolog is designed to make it easy, convenient and fast to develop, test, and modify applications written in PDC Prolog. It supports many platforms, including MS-DOS, PharLap-Extended DOS, MS Windows 3.1, MS Windows 3.11, MS Windows 95, Windows NT, as well as 16 bit and 32 bit OS/2 target platforms using processors from the 80x86 base family. Functionally identical VDEs will ultimately be available for all versions of Windows, OS/2, as well as certain other GUI platforms.
In the Application Expert, for User Interface Strategy you should select the Visual Programming Interface (VPI). This provides access to a set of Code Experts, resource editors, and additional VPI-specific predicates, definitions, and libraries. The resource editors are used to define, layout, and edit windows, dialogs, menus, tool bars, help bars, string tables, icons, cursors, bitmaps, and run time on-line help. From these, Code Experts generate the necessary Prolog source code framework to present these components in some presentation space (i.e. in a window). This framework is ready to be compiled, linked, and run.
At the programmer’s request, the Code Experts can bring up any part of its generated framework in an editor window for inspection and filling out with application logic to transform the framework into a useful application. This completion process in an editor window is greatly assisted by a variety of commonly needed functions being available at a click of the mouse right button from a multilevel pop up menu of editing, selecting, searching, replacing, and inserting functions. Much code can be accurately generated by use of the insert item of this menu which permits selection of colors, VPI and standard Prolog predicate templates, user-defined predicates for dialogs and windows, domain names, resource identifiers, VPI constants, quoted full path and file names as obtained by directory browsing, file names, key words, and much more.
The Visual Programming Interface (VPI) is a high-level Application Programming Interface (API) and is designed to make it easy for Prolog applications to provide sophisticated user interfaces utilizing the graphical capabilities of today's operating systems and display hardware. The Resources and Tools needed by such applications - Windows, Menus, Dialogs, Controls, Pens, Brushes, Cursors, Carets, Pictures, etc. all appear as simple Prolog structures.
The VPI makes it possible to create portable source code that can be cross-compiled to run in 16 bit mode under MS-Windows, 32 bit mode under Windows NT or Windows95, under the OS/2 presentation manager, and eventually under other platforms as well.
The VPI is an abstraction of the supported Graphical User Interface (GUI) platform APIs so the functions, flags and events are handled independently of the platform. However, not all facilities of every GUI appear in the VPI, nor is every facility in the VPI fully portable; rather, the VPI tries to support all the features found on each platform in a uniform way.
The domains, constants and predicates in the VPI do not change with target platform. To assist developers not interested in portability, but who need maximum flexibility on one platform, the VPI also contains a number of predicates that are not portable, but are very relevant on a given platform. Just as when using other general purpose programming languages, it is possible to write a portable VPI application, but it is also possible to write one that is not portable.
In most DOS text mode applications the sequence of operations is determined by the programmer at implementation time. There is very little interaction with other applications and no interference from the operating system. Most things are completely controlled by the programmer. A DOS application communicates directly with its user and is often based on a static design philosophy.
Modern GUI systems are much more dynamic in their nature. The operating system doesn’t sit back and wait any more. Rather than only providing a layer of services, it is actively involved in the execution sequencing of all applications. An application talks to its user indirectly via the operating system. When the user does something (presses a key, moves the mouse, clicks on an icon etc.) an event is generated by the hardware interface, the operating system translates this event into a standardized message format and sends the event notification message to its correct owner, usually the window that is active (said to 'have the focus').
These events can arrive at the application asynchronously, and so the application must be written most carefully in order to maintain the integrity of all its data and processes no matter what the sequence of events. GUI-based applications must be reactive to the world around them and cannot mimic the proactivity and imperial behavior of DOS applications. The applications behind inactive windows remain active and running. They may write or draw into their inactive windows at any time and this will be quite visible unless the window is covered or overlapped by another. An application may at any time suddenly activate one of its own windows to request input. The previously active window becomes inactive, but its application continues running.
In the middle of entering a string into an edit box, the user may receive an urgent telephone call. The user may respond by selecting your Task window’s system button or file menu and requesting the shut down of the application. The operating system will inform the application of the event by sending a message to the Task window's event-handler, and the programmer should have arranged to do in an orderly fashion any necessary house-keeping, then terminate the application by destroying the Task window. In many multi-tasking systems the operating system can suspend your application at any time. The application will never know if the user requested a shutdown or if, for example, the operating system has detected a coming power failure. It just receives a message and acts accordingly. This flexibility is not confined to the operating system and to your application’s user. If the application wants to close some window it can send that window an e_CloseRequest, just like the one it receives when your user selects 'Close' from that Task window menu.
Of course you can reduce the quantity of possible events and messages by disabling menu items and push buttons that are not relevant to the task at hand. The message handling part of GUI applications can be very complex, and this is the price for the great flexibility these systems give you and your users. In a C application the result is often an enormous and unwieldy case statement. Prolog and the VPI make life a lot easier for you than if you interface the application to the native GUI directly. The Code Expert and the Browser make it easy to find and maintain the code that handles a specific event by responding to a specific message and you rarely have to deal with the intricate details of the underlying messaging logic.
Before reading this book, you should have a knowledge about the Prolog language. You can get this knowledge by reading the Language Tutorial manual, and working your way through the examples in that book.
Also, before starting on this book, you should study the Guided Tour in Visual Prolog in the getting started manual. The Visual Development Environment of Visual Prolog is tightly coupled to the VPI layer, and can give much assistance in using this. The high productivity of Visual Prolog comes from a proper knowledge about using the Layout Editors and Code Experts in conjunction with the VPI and the Prolog language. The Guided Tour gives you an overview of coupling these things together.
This book gives a detailed introduction to the VPI layer. It explains the concept of Windows, the various attributes and functions calls. After this, it describes the Event handling, and working with controls and dialogs. After these basic things are introduces the various groups of Windowing operations are introduces one at a time. The book ends by describing some tool packages, that are implemented on top of the basic VPI layer.
This manual are part of the on-line help of Visual Prolog, but the on-line help does also contain one entry for each VPI predicate, giving the full description about each predicate.
The functionality of the VPI is illustrated by various examples in the subdirectory VPI\EXAMPLES.
For full and accurate declarations of all the VPI layer components, the reader is referred to the content of the three files VPI.CON, VPI.DOM and VPI.PRE in the VPI\INCLUDE directory.
Execution of a VPI application starts with the usual Prolog GOAL. The basic function of the GOAL is to invoke the predicate vpi_Init, which will start the VPI windowing system, and create one main window which we call the Task window. The only code which should precede the call to vpi_Init is that involved with setting the parameters of vpi_Init. These can all be set by the predicate vpi_SetAttrVal.
The Application Expert will create the GOAL section for you, with all applicable attributes and flags set according to the choices you made in the Application Expert dialog. Any other application initialization operations should be done in response to the e_Create event for the Task window. All termination should be done in response to the e_Destroy event.
GOAL
....
vpi_Init(WSFlags, EventHandler, Menu, AppName, TaskWinTitle).
Notice that vpi_Init will return only after the user closes the created Task window.
WSFlags is a list of the window style flags for the Task window. These determine whether it should start minimized, maximized, etc. Menu is from the MENU domain
(res_menu (resource_id) or no_menu). EventHandler is the predicate from the EHANDLER domain, that will receive events for the Task window. AppName is used to identify the application. Finally, TaskWinTitle will become the title of the Task window. These flags are described in more detail later under Creating Windows.
A window is a rectangular presentation space on a display. A window is used by the computer application to present output in and receive input from the user. The entire screen space is treated as a special window called "the Screen window". Every window shares the screen with other windows, including those from other applications and sometimes from the operating system itself, and these windows may overlap.
No matter how many applications are concurrently running, only one window at a time is active in the sense that it can receive input from the user. An active window is displayed and is in full color, so that, using a desktop metaphor, it appears to be "on top". The user can use the mouse, the keyboard, or other input devices to communicate with this window - and thereby with the underlying application that owns it. Windows are the only immediate data turn-around device that an application has to interact with its current user. Therefore, one of the first functions of every application is to create and show a window. This first-created window for each application is called its Task window, and while it need not necessarily be shown, it does represent the application as a whole, as a receptor of messages.
All of the VPI run-time window creation predicates create a memory structure, display the window according to this control structure, and return a window handle. A window handle is a unique identifying number that the VPI assigns when a given window is created. Not only visible windows have handles, but also non-displayed or not-yet-displayed windows used for drawing or printing. Nearly all the other VPI predicates require a window handle as input in order to indicate which window to access. Loaded and ready-for-use resources of all kinds are also issued handles by which they are accessed. The actual value of any handle is irrelevant to the application and should be ignored.
Every window or dialog in an application is represented in the application not only by a memory structure, but also by an event-handling predicate, to which the VPI sends messages when user or system events occur. These messages can arrive at any time and need not be synchronized with the application. The application can also send event messages to any of its event-handlers. In the VPI, names in the event message domain always have an 'e_' prefix.
Windows are classified by type. Window types must belong to the WINTYPE domain, and have 'w_' (for windows), 'wc_' (for controls), or 'wd_' (for dialogs) prefix. The window type can be obtained from the window handle by a call to the win_GetType predicate. The possible window types are listed in the table below.
Table 1.1: Window Types and Classes
Identifier |
Meaning |
w_TopLevel |
Normal document window |
w_Child |
Child window |
w_Task |
Task window |
w_Screen |
The display Screen Itself |
wd_Modal |
Modal dialog |
wd_ModeLess |
Modeless dialog |
wc_PushButton |
Push button control |
wc_RadioButton |
Radio button control |
wc_CheckBox |
Check box control |
wc_HScroll |
Horizontal scroll bar control |
wc_VScroll |
Vertical scroll bar control |
wc_Edit |
Edit control |
wc_Text |
Static text control |
wc_LBox |
List box control |
wc_LBoxButton |
Button with drop-down list |
wc_LBoxEdit |
Edit field with drop-down list |
wc_GroupBox |
Group box |
wc_Icon |
Icon control |
wc_Custom |
Custom control |
Internal window used during printing |
|
w_Picture |
Internal window used during picture creation |
w_Metafile |
Internal window used during metafile creation |
When you create a window or a dialog there are various flags to determine how the window should look, what kind of border it should have, whether it should have scroll bars, whether it should be possible to maximize it, etc. You use the VDE's Window and Dialog editor to do this. All flags have a 'wsf_' prefix. After you create a window, style flags for the window may be obtained by a call to win_GetState. Some may be changed by a call to win_SetState. A table of all the possible values follows:
Style Flag |
Meaning that ... |
wsf_Border |
Window gets thin border. |
wsf_SizeBorder |
Window gets double border. Used to re-size the window. |
wsf_DlgBorder |
Window gets border like a dialog. Window can not be re-sized. |
wsf_Close |
Window gets System Menu. Generally used to close Window. |
wsf_Maximize |
Window gets maximize button. Do not specify this flag if the window should not be re-sized. |
wsf_Minimize |
Window gets minimize button. |
wsf_HScroll |
Window gets horizontal scroll bar. |
wsf_VScroll |
Window gets vertical scroll bar. |
wsf_TitleBar |
Window gets title bar. |
wsf_Visible |
Window is initially visible. This is the default. |
wsf_Invisible |
Window should be created invisible |
wsf_Enabled |
Window should be created initially enabled. This is default. |
wsf_Disabled |
Window should be created initially disabled |
wsf_Maximized |
Window should be created initially maximized |
wsf_Minimized |
Window should be created initially minimized |
wsf_Restored |
This flag is used to restore a window from the minimized or maximized state. |
wsf_ClipChildren |
Drawing over any Child windows should be clipped. This flag must be given when creating the parent window. |
wsf_ClipSiblings |
Clipping should be done when Child windows overlap. This flag can be used only for Child windows. |
wsf_NoClipSiblings |
Deactivates the wsf_ClipSiblings flag. |
wsf_Topmost |
Window will be on top of all other non-TopMost windows. |
wsf_Transparent |
Window should be transparent, and not damage any windows underneath it. |
The portion of any window inside any title, menu, and scroll bars is termed the Client Rectangle. The Client Rectangle is the working area of a window - it is available for controls, child windows, coloring, and the drawing of text and graphics.
Rectangles are always specified as RCT structures: rct(Left, Top, Right, Bottom).
Note that the VPI uses Right, Bottom NOT Width, Height.
For most windows coordinates are expressed in pixels. This applies when working in windows on the screen with editors, menus, trees, toolbars, etc.
But when working with dialogs and controls, sizes should be dependent on the font size and type used of the system font. When the system font changes, the size of text controls will change and the whole dialog will be scaled accordingly. Therefore, the size of dialogs is measured in Dialog Base Units, whose metrics depends upon the system font.
The Screen window is an abstraction that represents the entire screen. The Screen window is always the parent of the Task window. Top-Level windows may also have the Screen window as their parent, which allows moving them outside of the Task window.
The Task window is an abstraction, which represents the application itself. On Windows and OS/2 it can be used much like a normal window, but on the Macintosh and under Motif this may not be so - it may not display on some platforms. The Task window is the main window for an application and is created with the w_Task window type by a call to the VPI predicate vpi_Init. This is generated by the Code Expert as the final call in the application goal.
Application initialization and termination are always associated with the Task window. Initialization should be programmed in response to the e_Create Task window event, which is triggered automatically by a call to the vpi_Init predicate. The clean-up on termination should be programmed in response to the e_Destroy event. Application termination results from the VPI predicate win_Destroy are applied to the Task window. One way this might occur would be in response to the user selecting the File|Exit menu item. This will typically trigger an event resulting in a call to the win_Destroy predicate.
The Task window also receives some special events that are not sent to other windows. An e_EndSession may be received from the windowing system to indicate that the windowing system is being shut down, and the various Dynamic Data Exchange (DDE) events used to communicate with other applications are also sent to the Task window's event-handler.
Under Windows' Multiple Document Interface (MDI) mode, the Task window is the container for all Top-Level windows created by the application and for their minimized icons, and it is the Task window that carries the menu of any active MDI window, otherwise it carries its own. When running in MDI mode, it is not possible to draw in the Task window.
In the VPI, a Top-Level window is a window that has either the Screen window or the Task window as the parent window. Top-level windows are created with the w_TopLevel window type. These windows will often contain a menu, and they can usually be minimized and maximized. They may also be used as container windows, and may carry Child windows or controls.
In MDI mode under Windows, the menus of Top-Level windows will be displayed in the Task window. However, in SDI (single document interface) mode, any Top-Level window can nominate the Screen window as its parent window. This allows each Top-Level window in the application to contain its own menu.
Child windows are always restricted to stay inside their parent window. Child windows are created by requesting the w_Child window type. When a parent window is destroyed, minimized, disabled, or hidden, the operation is automatically performed for all of the Child windows and controls before being done for the parent.
The term Controls applies to smaller windows with a specialized appearance and fixed functionality supplied by the operating system. They are used in fixed arrangements inside other windows, mainly as components of dialog boxes, but they can also be used in other windows. Controls provide the user with the means to type text, choose options, or invoke commands. Commonly available named types of controls are supported by the VPI and these names commence with a 'wc_' prefix. Their events cause notification messages to be sent to their parent window event-handler, which would generally be the dialog event-handler.
A Dialog box is a special kind of window, which will be referred to throughout this user guide simply as a "dialog". They are restricted in functionality compared to regular windows. Dialogs are normally filled with controls, which can interact with the user by displaying output, accepting input, offering selection from a number of options, or allowing editing. The operating system underlying the VPI provides the ability to navigate between controls by using the Tab and Shift+Tab keys to shift the point of user interaction from one control forwards and backwards to another.
Each window and dialog has an associated predicate, which handles all events for the window. It should be declared as belonging to the EHANDLER domain and takes two input arguments: a handle to the window and the event. The predicate has a LONG return final parameter, whose value is only used in a few cases. A sample event-handler might look like:
PREDICATES
mywin_event_handler : EHANDLER
CLAUSES
mywin_event_handler(Win, e_Create(CreationData), 0):-
The event-handler predicate for each window should be declared and created by the Dialog and Window Expert. You should generate event code only for the events that need to be handled explicitly by your application. If the event-handler predicate for that event fails, the VPI will perform default processing appropriate to the event. If the predicate succeeds, the VPI will perform no extra processing. As an example, the e_CloseRequest event is sent when the user double clicks in the close box for a window. If there is no clause to catch an e_CloseRequest event the default handling will be to close the window. If there is a clause to catch an e_CloseRequest event, the programmer can decide to leave the window open by having the predicate succeed, or close it by either failing or by doing a win_Destroy.
If no event-handler clause for an event exists or the event-handler clause for that event fails the VPI will perform default processing appropriate to the event. If the event-handler clause succeeds no additional or default handling is performed.
If the return parameter of an event-handler predicate does not carry any special meaning it should be set to zero. Generally, this is the case for most events. There are a some cases where it has special meaning. These include: e_Native events to give a return value to the underlying GUI; to give a return value in response to an e_User event arising from a call to win_SendEvent; to return a size in response to e_OwnerMeasureItem; or to return data to another application in a DDE conversation.
In Visual Prolog applications, the creation a window is normally done in the code generated by the Dialog and Window Expert. The Code Expert will here use all the settings specified in the Attribute dialogs and the window layout to create the windows.
Creation a windows, a dialog or a control is performed by one of the VPI predicates starting with win_Create. They all return a handle to the newly created window. Note, that if the window or dialog created was modal, the window will already have been closed when we return, so the returned window handle will be meaningless in those cases.
When a window is created, the user must specify the desired client rectangle for the window. The coordinates are Top, Left, Right, and Bottom relative to the client rectangle of the parent window.
None of the VPI window and dialog creation predicates will return before the e_Create clause or its default handler completes. The code which calls win_Create can use this mechanism to indirectly pass data to the event-handler code by using the data directly as the Creation Data, if it is numeric and fits within a LONG. If not, more complex data can be passed by using cast to store a pointer to the data, and passing the pointer as the LONG value.
If there is no e_Create clause, or if it fails, the default e_Create handler will be called and will succeed - the window will always be created and displayed. If the initialization fails, ordinarily your program should supply a second e_Create clause which catches this situation and take appropriate measures. The Code Expert cannot generate duplicate event clauses for you - they must be cut and pasted manually. Remember to remove the code-expert-generated early cut from all e_Create clauses except the last.
Note: If a window is being destroyed during the e_Create event by calling the predicate win_Destroy, the predicate win_Create will generate the error 6306.
WINDOW = win_Create(WINTYPE,
Rct, Title, Menu, ParentWin, WSFlags, EventHandler, CreationData)
win_Create
is used to create either Top-Level or Child windows. The parameter WINTYPE specifies which kind of window it should be. The Rct parameter specifies the size of the new client window, and Title will be displayed in the title bar of the window (if the window is specified to have a title bar). For Top-Level windows the menu parameter can be used to specify which menu a window should have. ParentWin specifies the window handle of the parent window, which for Top-Level windows must be either the Screen window or the Task window, and for Child windows, it must be either a Top-Level window or another Child window. WSFlags is a list of flag values that specifies how the window should look and behave. EventHandler specifies the predicate which should be called when an event arrives for the window. CreationData is a LONG value that will be passed to the event-handler predicate as part of the e_Create event.WINDOW = win_CreateResDialog(
ParentWin, WINTYPE, ResId, Eventhandler, CreationData)
There are two kinds of dialogs: modeless and modal dialogs. A modal dialog must be satisfied before the user can activate any other window, menu or control in the application. On the other hand, any number of modeless dialogs can concurrently be on the screen of which any one can be made active by the user clicking the mouse in it so that it will accept input.
This predicate will create either a modal or a modeless dialog as described in the WINTYPE parameter.
WINDOW = win_CreateControl(
WINTYPE, Rct, Title, ParentWin, WinStyleFlags, CtrlId)
This predicate is used to add a new control to either a window or a dialog. (On some platforms it is not possible to add controls to a dialog after creation). WINTYPE is used to specify which kind of control to create. CtlFlags is a list of parameters to specify the behavior of the control. CtrlId is an integer constant used to identify the control within the window or dialog.
It is possible to specify either a window or dialog with included controls in a single Prolog structure from the WINDEF_LIST domain. This may indicate that all the information is to be obtained from the resource segment, or may specifically include all the controls and font information. Additionally, you can specify all the coordinates in dialog base units, to make the dialogs independent of the font.
GLOBAL DOMAINS
WINDEF_LIST = WINDEF*
WINDEF =
resdlg(RESID, WINTYPE);
dlg(WDEF, WSFLAGS);
dlg_font(WDEF, STRING FontName, Integer FontSize, WSFLAGS);
topwin(WDEF, MENU, WSFLAGS);
childwin(WDEF, CTLID, WSFLAGS);
icon(WDEF, CTLID, RESID, WSFLAGS);
ctl(WDEF, CTLID, WSFLAGS);
customctl(WDEF, CLASSNAME, CTLID, WSFLAGS)
WDEF = wdef(WINTYPE, RCT, STRING, UNIT_TYPE)
UNIT_TYPE = INTEGER
UNIT_TYPE CONSTANTS
u_Pixels = 0 % scaling in pixels
u_DlgPlatform = 1 % scaling in Platform Native Dialog Base Units
u_DlgBase = 2 % scaling in Windows Based Dialog Base Units
Windows and dialogs may be built dynamically during the application from a list of component controls, rather than using pre-built resources from the application's resource segment. The first element in the list must specify the window, and the rest of the elements in the list must specify either Child windows or controls. In every case the handle to the created window or dialog is returned.
To build and create a window:
WINDOW = win_CreateDyn(WINDEF_LIST, WINDOW Parent, EHANDLER,
LONG CreationData)
To build and create a dialog:
WINDOW = win_CreateDynDialog(WINDOW Parent, WINDEF_LIST,EHANDLER,
LONG CreationData)
To create a number of controls in a created window or dialog which may or may not yet be displayed:
WINDOW = win_CreateDynControl(WINDEF_LIST, WINDOW Parent)
This predicate returns the handle to the first of the created controls.
A window, a dialog or a control can be destroyed by a call to win_Destroy. When this call has been made for a particular window, the window's event-handler predicate will receive an e_Destroy event. This should be used to retract any facts, close files etc. associated with the window.
When a window is destroyed, all of its Child windows are automatically destroyed. All Child windows will receive an e_Destroy event before the parent window receives this event.
Example code for window destruction:
% The OK button will close the window
mywin_event_handler(Win, e_Control(idc_ok, _, CtlWin, CtlInfo), 0):- !,
win_Destroy(Win).
% Do all clean-up in the e_Destroy event
mywin_event_handler(Win, e_Destroy, 0):- !,
retractall(mywin_data(Win, _)).
Retrieving the Client Rectangle
The win_GetClientRect function retrieves the coordinates of the window's Client Area. win_GetClientRect fills a RCT structure with the coordinates of the upper-left and lower-right corners of the client area, but the coordinates are relative to the Client Area itself (Client Coordinates). This means that the coordinates of a client area's upper-left corner are always (0, 0), and the coordinates of the lower-right corner are the width and height of the client area.
ClientRCT = win_GetClientRect(Window)
By default, window output is clipped to the client area, but it is possible to set the clipping region explicitly to any rectangle with a call to win_SetClip:
my_event_handler(Win, e_Update(_Rct), 0):- !,
win_SetClip(Win, rct(60, 60, 100, 100)),
draw_Text(Win, 70, 70, "Hello, World").
All future output to the given window will then be clipped to the specified rectangle.
To stop clipping, just set clipping to a rectangle at least as large as the client rectangle.
The current clipping rectangle for any window may be obtained:
ClipRct = win_GetClip(WINDOW)
In both cases, the rectangle coordinates are relative to the client rectangle of the window.
Retrieving the Outer Rectangle
An application can retrieve the coordinates of a window's outer rectangle by:
OuterRect = win_GetOuterRect(Window)
The RCT structure is filled with the coordinates of the window's upper-left and lower-right corners. For dialogs and for the Task window the coordinates returned are relative to the screen, and for all other windows relative to the parent window.
Calculating Client Rectangle from Outer Rectangle
If a window must be placed on the basis of its outer rectangle (for example while moving or re-sizing operations), the predicate rect_GetClient can be used to convert the desired outer rectangle to the resulting client rectangle. The styles for the window must be given as input:
ClientRCT = rect_GetClient(WSFlags, BOOLEAN HasMenu, OuterRct)
In this case the ClientRCT RCT structure returns the window's client area coordinates relative to the parent window coordinates. (For dialogs and Task windows the returned RCT structure is relative to the screen coordinates.)
After a window is created, an application can change the window's size and position by calling win_Move. The following example shows how to move a window 10 pixels to the right and down on a mouse click:
win_event_handler(Win, e_MouseUp(PNT, ShiftCtlAlt, Button), 0):- !,
OuterRCT = win_GetOuterRect(Win),
WsFlags = win_GetState(Win),
ClientRCT = rect_GetClient(WsFlags, const_HasMenu, OuterRCT),
NewClient = rect_Offset(ClientRCT , 10, 10),
win_Move(Win, NewClient ).
Note that you cannot simply use rect_Offset on the rectangle returned by win_GetClientRect, because that rectangle is relative to itself, i.e., always has an origin of (0,0). See also Coordinate System section (page
*).Changing the State for a Window
When a window is created, it is given a number of style flags. These flags indicate the initial state settings for a window. They determine whether the window is visible, disabled or minimized. A table of possible window styles can be found on page
*.It is possible to change the state of a window by a call to win_SetState, and to receive the actual state for a window with a call to win_GetState.
win_SetState(WINDOW,WSFLAGS WinStateList)
WSFLAGS = win_GetState(WINDOW)
Disabling and Enabling Windows
A disabled window receives no keyboard or mouse input, but it can receive messages from other windows, from other applications, and from Windows. An application typically disables a window to prevent the user from using the window. For example, an application may disable the OK push button in a dialog box to prevent the user from choosing it until certain required data in other controls is present. An application can enable a disabled window at any time. A window is usually enabled when created, but the wsf_Disabled style can be specified in order to create a disabled window.
When a Child window is disabled, Windows passes the child's mouse input messages to the parent window as if there was no Child window covering the given rectangle.
To enable and disable windows use the calls:
win_SetState(WINDOW,[wsf_Disabled])
win_SetState(WINDOW,[wsf_Enabled])
A window or control can be made totally invisible. In this case the user will se no effect of the window. However the program can still use the window as if it where a regular window. In is very common to let dialogs contain invisible controls, that become visible when some button or action is performed.
To hide and make visible windows use the calls:
win_SetState(WINDOW,[wsf_Invisible])
win_SetState(WINDOW,[wsf_Visible])
Minimizing, Maximizing and Restoring Windows
A window can be minimized, maximized or restored to its previous size from one of these states. This can either be done by the user if the window is created with the proper style flags, or it can be done by calling the win_SetState predicate.
To minimize, maximize or restore windows use the calls:
win_SetState(WINDOW,[wsf_Minimized])
win_SetState(WINDOW,[wsf_Maximized])
win_SetState(WINDOW,[wsf_Restored])
Changing the Icon Associated with a Window
To associate with a window the icon that is drawn in the window's titlebar and when the window is minimized, use the predicate:
Win_SetIcon(Window, IconResourceIdentifier)
Changing the Window Text or Window Title
This predicate is used to change the text shown by a static text control and the text contained in an edit control:
win_SetText(Window, Title)
For windows which have a title bar, it is also the predicate used to change the text of the window's title (such windows do not have content text in the above sense).
The VPI allows you to dynamically change the event-handler predicate for a window. This is done using the predicate:
win_SetHandler(Win, other_event_handler)
All events for the specified window will subsequently be directed to the other_event_handler predicate. This feature is used as an example in the Tree tool when a tree should be put into an already created window.
Data can be directly connected to a window. This feature is not used frequently, but it allows you to retrieve and change a LONG value relating to an internal window control structure. This LONG value can at any time be retrieved and changed with a call to the two predicates:
Data = win_GetData(WINDOW),
win_SetData(WINDOW, NewData)
Every running program has the task window. The handle of the task window can be obtained by a call to vpi_GetTaskWin:
TaskWinHandle = vpi_GetTaskWin()
Every window except the Screen window has a parent. The handle of the parent window of any given window can be obtained by a call to win_GetParent:
ParentWin = win_GetParent(ChildWin)
The active (foreground) window is either a Top-Level window or a task window that either has the focus itself or contains a control or Child window that has the focus. The handle to the active window can be returned with a call to:
Win = win_GetActiveWindow()
Only one control or window at a time can have the focus. Having the focus means that it will receive input from the keyboard. Changing the focus can either be done by the user by clicking on another control, or by the application with a call to:
win_SetFocus(NewWindow)
Finding the Window which has the Focus
Similarly, the reverse is a call to:
Window = win_GetFocus()
This is used to find out which control or window currently has the focus.
Sometimes it is necessary to bring a window to the front of the screen without changing the focus. This can be done with a call:
win_BringToTop(Window)
The application must always be ready to re-draw the given rectangle of the window in response to an e_Update(Rct) event. If the window has been re-sized, restored, partly or fully invalidated or an overlapping window has been fully or partly removed, an e_Update(Rct) event will occur. For simplicity, the application can just re-draw the entire window and the windowing system will automatically clip the drawing to just the area where the updating is needed.
Since the application must be able to restore the window contents on an e_Update event, it is very important to arrange the application so that all window drawing is done here. This centralizes all window output in one place in the code, instead of having different portions produced at different events.
The following predicates are used to control when a window needs to be updated. The predicates can be used to optimize the drawing, so as little as possible will be redrawn.
win_Invalidate(Window)
win_Invalidate
is the standard way for you to initiate total re-drawing of one of your windows because it immediately causes an e_Update event to be sent to that window's event-handler.win_Invalidate(Window, Rct)
This predicate can be used to specify that only a part of the window needs to be updated.
win_ValidateRect(Window, Rct)
This is used to tell the windowing system that no updating is to be done in the specified part of the window.
Check whether Part of the Window Needs Updating
win_NeedsUpdate(Window, Rct)
The win_NeedsUpdate can be used in certain situations to speed up window updating or printing. Before drawing an area that requires time-consuming calculations, database look up or complex drawing, this predicate can be used to test whether the indicated region really needs to be re-drawn. While the e_Update term will indicate one rectangle (corresponding, say, to the area just uncovered by another window), the updating code may need to segment its drawing by regions defined by its underlying data structures. By using win_NeedsUpdate it can ask about specific sub-rectangles more convenient to its purposes.
win_Update(Window)
The win_Update predicate causes any update events that might be pending in the event queue to occur and ensures that the window is completely drawn. For example, this might be needed before scrolling a part of the window.
Events occurring in the VPI system result in the construction of a data structure from the EVENT domain according to the type of event. These event structures are sent to the event-handler for the window owning or originating the event and will appear as the second argument to the event-handler. Descriptions of the event domain structures and the recommended action by the event-handler for each of them follow. Where no clause exists for a particular event domain alternative, default handling is performed - the default handling is also shown.
To study the events sent in various situations, you should run the VPI example in the Events subdirectory.
e_Create(LONG CreationData)
This event is the first one that any window or dialog receives. It occurs just after the window or dialog has been internally created, but before it has been displayed. Generally, this is the right place to do initialization (such as database assertions, enabling, disabling, or graying controls, and initializing control values). You will note that the Code Experts in the VPI place their code for creating controls, Child windows, and tool and help bars here so that everything is created before the window becomes visible. The predicate that creates the window will not return until the e_Create event clause has completed.
It is frequently necessary to pass instance-specific information from the creation predicate through to the created instance of the window. A good example would be a new or modified title if the same dialog is being used from more than one place in an application. The only place this can be done is in the event-handler's e_Create event, since the window and its window handle do not exist until that time and, since the dialog is not yet visible, changes can be made without being visible on the display.
This can be done by giving a value in the LONG CreationData parameter of the creation predicate; this value is then passed into the e_Create event. The code would look something like:
dlg_criterion_Create(TaskWindow, MenuItem, Caption):-
CreationVal = criterion_context(TaskWindow, MenuItem, Caption),
LongVal = cast(LONG, CreationVal),
win_CreateResDialog(WINTYPE, resource_id, event_handler, LongVal).
event_handler(Win, e_Create(LongVal), 0):-
CreationVal = cast(CRITERION_CONTEXT, LongVal ),
CreationVal = criterion_context(TaskW, MenuItem, Caption)),
...
Note that the e_Create event for the Task window is used as the place to do initialization code for the application as a whole.
e_Destroy()
This event is the place to do the final retraction of all instance data that is specific to the window. It is the last event your window will process. Your code has access to all controls and Child windows (and their contents) even though they are not visible on the screen.
A window will always receive an e_Destroy event, independently of whether the window was destroyed by a call to win_Destroy or whether its parent window was destroyed.
e_CloseRequest()
This event tells you that the user has double-clicked the system menu button for the window or selected Close from its system menu. If you do not catch this event, the default handling is to destroy the window, without performing any application wind-up operations. If the event-handler predicate succeeds for this event, no automatic closing of the window is done. You might as an example catch this event to perform application wind up and/or to query the user and ask for confirmation before calling win_Destroy ().
e_EndSession(AbortPossible):
When this event is received, the GUI environment is about to shut down. It can be invoked either by the user or in some automatic way as in a power failure where the operating system has some time to terminate because of an UPS system. This message originates with the underlying operating system and is sent to all running applications.
The event is sent to the Task window event handler. In response to it the clause catching e_EndSession event in the event handler predicate in your application should fail, if it approves the close request, or succeed if it disapproves.
The usual action taken for an e_EndSession event is to save any modified data.
The return parameter, in the below arbitrarily set to b_True, is meaningless.
my_window_event_handler(Win, e_EndSession(b_True), b_True):- !,
% If this code is executed and succeeds the system does not close.
% This predicate must fail if it is OK to close the system
e_InitMenu()
The e_InitMenu event is sent when a menu is about to become active. It occurs when the user clicks an item on the menu bar or presses a menu key. This allows the application to modify the drop-down menu before it is displayed.
A Menu Selection has been Made
e_Menu(MENU_TAG, INTEGER ShiftCtrlAlt)
When the user makes a menu selection, an e_Menu event is sent to the application. The parameter MENU_TAG is the resource identifier of the constant that identifies the selected menu entry. The ShiftControlAlt parameter contains the state of the Shift and Controls keys that may have been pressed during activation of the menu entry.
task_win_event_handler(Win, e_Menu(id_file_exit, ShiftCtrlAlt), 0):- !,
win_Destroy(Win).
e_Menu
events are only sent to windows with enabled menus. If the window is created without a menu, it will not receive e_Menu events. On some platforms, there are various restrictions concerning menus for Dialogs or Child windows.The tool bar push buttons for a window will also send e_Menu events to the window's event-handler.
Often windows need to pass unrecognized events to the Task window for default processing of things like File Handling, Help etc. The code for this is automatically inserted by the Code Expert for default handling of e_Menu events:
my_event_handler(W, e_Menu(ID, ShiftCtrlAlt), 0):- !,
PW = win_GetParent(W),
win_SendEvent(PW, e_Menu(ID, ShiftCtrlAlt)).
e_MouseDown(PNT, ShiftCtrlAlt, Button)
This event is generated when a mouse button has been clicked in the client area of the window.
The Button parameter indicates which button has been pressed. The following constants are defined in VPI.CON: mouse_button_left, mouse_button_right, mouse_button_middle.
Note: VPI refers to ‘left button’ and ‘right button’ for simplicity. For users operating the mouse with their left hand, the buttons are reversed. This is handled by the operating system, so developers can safely assume that the ‘left button’ is the primary mouse button, while the ‘right button’ is the secondary button.
The ShiftControlAlt parameter contains the status of the Shift, Control and Alt keys. The following constants are defined in VPI.CON: c_Nothing, c_Shift, c_Control, c_Alt, c_ShiftCtl, c_ShiftAlt, c_CtlAlt, c_ShiftCtlAlt.
The PNT structure contains the position in the client window where the mouse click occurred.
Normally you do not invoke actions on an e_MouseDown event, instead this event is used to prepare an operation that should react to or track e_MouseMove events until finally the e_MouseUp event occurs. To make sure there will be a e_MouseUp message, you need to Capture the mouse within the window, otherwise, the mouse could be moved outside of the window before it is released. In that case no e_MouseUp events would be sent to the original window's event-handler.
When the mouse is clicked on buttons, menus, borders etc. other control events are generated which are not Mouse events.
e_MouseDbl(PNT, ShiftControlAlt, Button)
This event is generated when a mouse button is double clicked in the client area of the window. The MouseDbl event does not preclude the constituent e_MouseDown and the e_MouseUp messages also being sent.
e_MouseMove(PNT, ShiftCtrlAlt, ButtonsList)
This event is generated whenever the mouse is moved inside a window, and PNT represents the new location. A stream of these events will be generated while the mouse moves.
MouseMove events can be used for various purposes. For example, it can be used to handle dragging such as when the VP text editor drags a block of text. Yet another frequent usage is to make a rubber band-like line for marking an area - this is done in the VP dialog editor.
e_MouseUp(PNT, ShiftCtrlAlt, Button)
This event is generated anytime a mouse button is released in the client area of the window.
Note: A MouseUp event is not always preceded by a MouseDown event. Your application must be able to ignore such situations.
e_Char(INTEGER Char, INTEGER ShiftCtrlAlt)
When a key is pressed on the keyboard, an e_Char event will be sent to the window, which currently has the focus.
There are basically two different key types: the data characters that occur in text, and the control keys like Enter, Del, the function and the cursor keys. The data keys are represented by their value, while the control keys are indicated by virtual key codes defined in VPI.CON.
e_KeyDown(INTEGER Char, INTEGER ShiftCtrlAlt)
This event is sent as soon as a key is pressed.
e_KeyUp(INTEGER Char, INTEGER ShiftCtrlAlt)
This event is sent when a key is released.
e_KeyDown and e_KeyUp events usually occur in pairs, but if the user holds down a key long enough to start the keyboard's automatic repeat feature, the system generates a number of e_Char events in a row. It then generates a single e_KeyUp event when the user releases the key. So the following sequence of events is generated:
e_KeyDown, e_Char, e_Char, …, e_Char, e_KeyUp
e_HScroll(SCROLLCODE, INTEGER Pos)
This event is sent when the user activates the horizontal scroll bar of a window. The Pos parameter indicates the setting for the scroll bar.
e_VScroll(SCROLLCODE, INTEGER Pos)
This event is sent when the user activates the vertical scroll bar of a window. The Pos parameter indicates the setting for the scroll bar.
e_GetFocus
This event occurs before focus is given to the window so the user has not been able to press any buttons or to use any other controls that your window can have.
The
e_Control(_,_,_,getfocus) event comes to a dialog or a window if its edit or listedit control gains focus..e_LoseFocus
This event is generated when the user selects another window or application that will cause the current window to lose focus. When the event arrives the window still has the focus, and keeps it until your code has been executed.
The
e_Control(_,_,_,losefocus) event comes to a dialog or a window if its edit or listedit control loses focus.:e_EraseBackground()
The e_EraseBackground is sent when a window needs to be updated. Normally the windowing system fills a window prior to updating with the default background color. If the event-handler predicate succeeds for this event, no background painting is done.
If you want a transparent window, or you are sure that you draw the background anyway as part of processing the e_Update event, you can catch this event (make it succeed) to make updating faster and free of flicker.
% Do no automatic background filling
my_window_event_handler(Win, e_EraseBackground(), 0):- !.
e_Move(TopCornerX, TopCornerY)
This event tells you that the position of your window has changed. The top left hand corner of your window's client rectangle has been moved to position TopCornerX, TopCornerY (in screen coordinates).
An e_Move event can be forced by using the win_Move predicate.
e_Size(NewWidth, NewHeight)
This event tells you that the window has changed size. If you have saved the old size in a database (maybe you did this in the e_Create event) you can determine whether the window has been made larger or smaller than before. In some cases, the window will not need to be updated if it has been made smaller.
The usual action taken in response to an e_Size event is to recalculate the size of drawings, Child windows or controls and adjust or move their size and positions. As an example, if some text is to remain always centered in a window, the entire window needs to be updated, the technique for this is to invalidate the window by a call to win_Invalidate, where the code for the update event would always calculate a new center and redraw the window.
Note that these events can either be triggered by the user re-sizing or moving the window or by a call to win_Move. They are also sent when a window is minimized or maximized.
e_State(StateInfo)
The e_State event is sent immediately after the state of a task window, top-level window, child window or custom control has changed. The StateInfo notifies about the following circumstances:
StateInfo |
Description |
font(FONT) |
win_SetFont is called |
text(STRING) |
win_SetText is called |
disabled() |
window is disabled by win_SetState |
enabled() |
window is enabled by win_SetState |
invisible() |
window is set invisible by win_SetState |
visible() |
window is set visible by win_SetState |
e_Native(Message, wParam, lParam)
The e_Native event provides the possibility of receiving all native GUI events that are sent to a window. To enable such e_Native messages, you must call :
win_EnableHook(WINDOW, BOOLEAN OnOff)
with a b_True parameter.
If the e_Native event-handler succeeds, no default VPI processing is done for the event. If it fails, the event will if needed be converted to the usual VPI event.
A LONG value can be returned to the windowing system.
Owner Drawing
e_OwnerDraw(CtlType, CtlId, ItemID, Action, State, Window, RCT RectItem,
LONG ItemData)
This event is sent to a window which has controls with the wsf_OwnerDraw or wsf_OwnerDrawVariable style flag. When this event is received, it is up to the application to draw the shape of a control.
For further information see the section about Owner Drawing (Page
*), and the OWN_DRAW example in the VPI\TOOLEXAM directory.e_OwnerMeasureItem(CtlType, CtlId, ItemId, Data)
This event is sent to a window which has created some controls with the OwnerDraw style flag. The event is used to let the application specify the size of a Menu Item, a List Item in a list box etc. The size must be returned as the return value of the event-handler predicate.
e_Update(RCT)
When your window receives this event you know that the system wants you to redraw the window. The rectangle RCT specifies the coordinates for the part of your window that needs to be updated. Often it is easier to redraw the whole window than to work out what to draw in the part that has been invalidated. Either way, the windowing system sets up a clipping region so that whatever you draw, only the invalid area is actually updated.
my_window_event_handler(Win, e_Update(ClipRct), 0):- !,
% Get Client Rectangle
ClientRCT = win_GetClientRect(Win),
ClientRCT = rct(Wl, Wt, Wr, Wb),
:
% Your Update code goes here
:
The e_Update event can be triggered by using the win_Invalidate predicate.
e_Timer(LONG TimerId)
Following the issue of:
TimerId = timer_Set(Window, Interval)
e_Timer
events will be sent to the event-handler for Window every Interval milliseconds until timer_Kill(TimerId) is issued. The TimerId can be used to distinguish between multiple timers.For further information about timers see the Timer section (page
*) and the RUNTIMER example in the VPI\EXAMPLES directory.A Programmed Event has Occurred
e_User(LONG Id, LONG Ptr)
The e_User events are a way to make application-specific extensions to the event domain.
e_DDE(DDE_CONV Conv, DDE_EVENT Event)
When doing Dynamic Data Exchange (DDE) under MS Windows, the Task window event-handler automatically receives all the e_DDE events. The first parameter is a handle to the particular conversation going on, the second is a substructure which further specifies the event.
For further information about the DDE handling see the section about DDE (page
*) and the DDE example in the VPI\EXAMPLES directory.e_Control(CtlId, CtlType, CtlWin, CONTROL_INFO)
Controls send events to their parent window using e_Control events. These events are normally notification events from the control. Notification events include those for getting and losing focus (e.g. using the Tab key to move out of an edit control), clicking on a button, changing values or a scroll bar etc.
CtlId is the integer constant assigned during resource editing that uniquely identifies the control in the window or dialog.
CtlType is the WINTYPE of the control and will be a 'wc_' prefixed constant.
CrlWin is the window handle of the control.
CONTROL_INFO is a sub-message that explains the activation cause in detail. The domain alternatives are described below.
scroll(ScrlEvent, Pos)
Where Pos is the new position of the scroll bar thumb tracker box on the scroll bar (after the user has moved it).
ScrlEvent identifies the type of scroll bar event:
Sc_None |
nowhere (event should be ignored) |
Sc_LineUp |
one line up |
Sc_LineDown |
one line down |
Sc_PageUp |
previous page |
Sc_PageDown |
next page |
sc_Top |
to top |
sc_Bottom |
to bottom |
sc_Thumb |
thumb re-positioning |
sc_ThumbTrack |
dynamic thumb tracking |
getfocus()
losefocus()
Control Content has been Modified.
modified()
This occurs once for every keystroke or mouse operation which results in a change to the value displayed in the control.
selchanged()
This event is sent each time the selection is changed in list boxes, list buttons and list edit controls.
dropdown()
dropdown will be sent for list edits and list buttons when the user drops down the list. The event can be used to temporarily disable heavy operations on selchanged events.
closeup()
dropdown will be sent for list edits and list buttons when the user closes the dropdown list.
Control is activated by mouse or keyboard
activated()
Application Termination Notification Events
e_EndApplication(ApplicationId)
VPI sends this event to the event-handler of the ReceiveMsgWin window immediately after termination of an application (with the application identifier ApplicationId) that was run by a call:
ApplicationId
= vpi_CreateProcess(ReceiveMsgWin, Command, ..., ...)Here the Command specifies the program to be loaded.
e_Activate()
VPI sends e_Activate event to the task window event-handler immediately after the application has activated (has become foreground).
Application has been Deactivated
e_Deactivate()
VPI sends e_Deactivate event to the task window event-handler immediately after the application has been deactivated.
Controls are specialized Child windows. They are mainly used inside dialog boxes to perform input/output, but they can also be created inside a regular window. It is not possible to draw in controls (unless ownerdrawing is specified), and it is not possible to supply an event-handler for a control. Their events cause notification messages to be sent to their parent window event-handler. The predefined control classes are listed below:
Table 1.3: Predefined Control Classes
Control type: |
Window class: |
Push button |
wc_PushButton |
Radio Button |
wc_RadioButton |
Check box |
wc_CheckBox |
Hscroll |
wc_Hscroll |
Vscroll |
wc_Vscroll |
Edit |
wc_Edit |
Text |
wc_Text |
Lbox |
wc_Lbox |
LBoxButton |
wc_LboxButton |
LBoxEdit |
wc_LboxEdit |
Group Box |
wc_GroupBox |
Icon |
wc_Icon |
Because controls are windows, an application can manipulate them by using the window-management functions. The window handle for a control can always be retrieved from the parent window and the resource identifier for the control:
CtrlHndl = win_GetCtlHandle(ParentWin, CtrlId)
The following table shows a number of predicates that are often used for the controls.
Table 1.4: Manipulating Controls
Operation |
Predicate |
Page |
Creation |
win_CreateControl win_CreateDynControl |
* * |
Destruction |
win_Destroy |
* |
Size/Location |
win_GetOuterRect win_Move win_GetClientRect |
* * * |
Title/Label/Text |
win_GetText win_SetText |
* * |
Show, Hide, Enable, Disable |
win_GetState win_SetState |
* * |
Focus |
win_GetFocus win_SetFocus |
* * |
Window hierarchy |
win_GetParent |
* |
Window type |
win_GetType |
* |
When creating a control, you can specify various style flags for the control. Some style flags are portable, while others exist only on specific platforms. However, it is possible on all platforms to specify any flag - inappropriate flags will simply be ignored. In this way it is possible to create both portable applications, which at the same time take advantage of all possibilities on each platform. The meaning of each flag when present in the list of flags is covered in the table below.
Table 1.5: General Control Style Flags
Flag Identifier |
Meaning |
wsf_Disabled |
The control will be initially disabled |
wsf_Checked |
Start the check box in the checked state |
wsf_Default |
Flag for default push button in a dialog |
wsf_Invisible |
Create an Invisible control |
wsf_Group |
Flag to start a new group of controls |
wsf_TabStop |
Allow tabbing to the control |
wsf_ReadOnly |
A read-only edit control |
wsf_MultiLine |
Allow multiple lines in an edit control |
wsf_MultiSelect |
Allow multiple selections in a list box |
wsf_MultiColumn |
Specify multiple columns in a list box |
wsf_AlignLeft |
Left alignment of text |
wsf_AlignCenter |
Centered alignment of text |
wsf_AlignRight |
Right alignment of text |
wsf_LeftText |
Specifies that the check box or radio button should have the text on the left side. |
wsf_OwnerDraw |
Specifies ownerdrawing for a control, in this case the user must do all the drawing of the control |
wsf_VScroll |
Specifies that a multi-line edit control or a list box will get a vertical scroll bar when it needs one |
wsf_HScroll |
Specifies that a multi-line edit control or a list box will have a horizontal scroll bar when it needs one |
wsf_NoBorder |
Specifies that there should not be a border around the control |
wsf_Simple |
Designates a simple rectangle and displays a single line of text left-aligned in the rectangle. The line of text cannot be shortened or altered in any way |
wsf_NoWrap |
Designates a simple rectangle and displays the given text left-aligned in the rectangle. Tabs are expanded but words are not wrapped. Text that extends past the end of a line is clipped |
wsf_NoPrefix |
Prevents interpretation of any ' &' characters in the control's text as accelerator prefix characters (which are displayed with the '&' removed and the next character in the string underlined). This static control style may be included with any of the defined static controls. This is most often used when filenames or other strings that may contain an & need to be displayed in a static control in a dialog box |
wsf_UpperCase |
Converts all characters to upper case as they are typed into the edit control |
wsf_LowerCase |
Converts all characters to lower case as they are typed into the edit control |
wsf_Password |
Displays all characters as an asterisk (*) as they are typed into the edit control |
wsf_AutoVScroll |
Automatically scrolls text up when the user presses Enter on the last line |
wsf_AutoHScroll |
Automatically scrolls text to the right by 10 characters when the user types a character at the end of the line. When the user presses the Enter key, the control scrolls all text back to position zero |
wsf_NoHideSel |
Negates the default behavior for an edit control. The default behavior is to hide the selection when the control loses the input focus and invert the selection when the control receives the input focus |
wsf_OEMConvert |
Converts text entered in the edit control from the Windows character set to the OEM character set and then back to the Windows set. This ensures proper character conversion when the application calls the AnsiToOem function to convert a Windows string in the edit control to OEM characters. This style is most useful for edit controls that contain filenames |
wsf_WantReturn |
Specifies that a carriage return will be inserted as data when the user presses the Enter key while entering text into a multi-line edit control in a dialog box. If this style is not specified, pressing the Enter key has the same effect as pressing the dialog box's Default push button. This style has no effect on a single-line edit control |
wsf_3State |
Creates a button that is the same as a check box, except that the box can be grayed (dimmed) as well as checked. The grayed state is used to show that the state of the check box is not determined |
wsf_Auto |
Automatic radio button groups or automatic check box |
wsf_Auto3State |
Automatic 3-State check box |
wsf_NoNotify |
Do not notify the parent window with an input message whenever the user clicks or double-clicks in a list box |
wsf_Sort |
Sorts strings in the list box alphabetically |
wsf_NoReDraw |
Specifies that the list box’s appearance is not updated when changes are made. This style can be changed at any time by using lbox_Suspend and lbox_ReSume |
wsf_OwnerDraw- Variable |
Specifies that the owner of the list box is responsible for drawing its contents and that the items in the list box are variable in height. The owner window receives a e_OwnerMeasureItem message for each item in the list box when the list box is created and a e_OwnerDrawItem message whenever the visual aspect of the list box changes |
wsf_HasStrings |
Specifies that a list box contains items consisting of strings. By default, all list boxes except ownerdrawn list boxes have this style. An application can create an ownerdrawn list box either with or without this style |
wsf_UseTabStops |
Allows a list box to recognize and expand tab characters when drawing its strings. The default tab positions are 32 dialog box units |
wsf_NoIntegralHeight |
Specifies that the size of the list box is exactly the size specified by the application when it created the list box. Normally, Windows re-sizes a list box so that the list box does not display partial items |
wsf_WantKeyboardInput |
Specifies that the parent window of the list box receives WM_VKEYTOITEM or WM_CHARTOITEM messages whenever the user presses a key and the list box has the input focus. This allows an application to perform special processing on the keyboard input. If a list box has the LBS_HASSTRINGS style, the list box can receive WM_VKEYTOITEM messages but not WM_CHARTOITEM messages. If a list box does not have the LBS_HASSTRINGS style, the list box can receive WM_CHARTOITEM messages but not WM_VKEYTOITEM messages |
wsf_ExtendedSel |
Allows multiple items to be selected by using the Shift key and the mouse or special key combinations |
wsf_DisableNoScroll |
Shows a disabled vertical scroll bar for the list box when the box does not contain enough items to scroll. If this style is not specified, the scroll bar is hidden when the list box does not contain enough items |
wsf_BlackRect |
Specifies a wc_Text filled with the color used to draw window frames. This color is black in the default Windows color scheme |
wsf_GrayRect |
Specifies a wc_Text filled with the color used to fill the screen background. This color is gray in the default Windows color scheme |
wsf_WhiteRect |
Specifies a wc_Text filled with the color used to fill window backgrounds. This color is white in the default Windows color scheme |
wsf_BlackFrame |
Specifies a wc_Text with a frame drawn in the same color as window frames. This color is black in the default Windows color scheme |
wsf_GrayFrame |
Specifies a wc_Text with a frame drawn with the same color as the screen background (desktop). This color is gray in the default Windows color scheme |
wsf_WhiteFrame |
Specifies a box with a frame drawn in the same color as window backgrounds. This color is white in the default Windows color scheme |
A table showing which 'wsf_' flags are meaningful for each class of control under Windows and which are meaningful under OS/2 Presentation Manager follows:
Table 1.6: Windows and OS/2 PM Control Flags
Control |
Possible Flags Under Windows |
Possible Flags under PM |
Push button |
wsf_Disabled, wsf_Default, wsf_Invisible, wsf_Group, wsf_TabStop, wsf_OwnerDraw |
wsf_Disabled, wsf_Default, wsf_Invisible, wsf_Group, wsf_TabStop, wsf_NoBorder |
Check box |
wsf_Disabled, wsf_Invisible, wsf_Group, wsf_TabStop, wsf_OwnerDraw, wsf_LeftText, wsf_Auto, wsf_3State, wsf_Auto3State |
wsf_Disabled, wsf_Invisible, wsf_Group, wsf_TabStop, wsf_Auto, wsf_3State |
Radio button |
wsf_Disabled, wsf_Invisible, wsf_Group, wsf_TabStop, wsf_LeftText, wsf_OwnerDraw, wsf_Auto |
wsf_Disabled, wsf_Invisible, wsf_Group, wsf_TabStop, wsf_Auto |
Static text |
wsf_Disabled, wsf_Invisible, wsf_AlignLeft, wsf_AlignCenter, wsf_AlignRight, wsf_BlackRect, wsf_GrayRect, wsf_WhiteRect, wsf_BlackFrame, wsf_GrayFrame, wsf_WhiteFrame, wsf_Simple, wsf_LeftNoWordWrap, wsf_NoPrefix |
wsf_Disabled, wsf_Invisible, wsf_AlignLeft, wsf_AlignRight, wsf_AlignCenter, wsf_BlackRect, wsf_GrayRect, wsf_WhiteRect, wsf_BlackFrame, wsf_GrayFrame, wsf_WhiteFrame, wsf_LeftNoWordWrap |
Group box |
wsf_Disabled, wsf_Invisible, |
wsf_Disabled, wsf_Invisible |
Edit control |
wsf_Disabled, wsf_Invisible, wsf_Group, wsf_TabStop, wsf_AlignLeft, wsf_AlignCenter, wsf_AlignRight, wsf_MultiLine, wsf_AutoVScroll, wsf_AutoHScroll, wsf_VScroll, wsf_HScroll, wsf_Password, wsf_NoHideSel, wsf_OEMConvert, wsf_WantReturn, wsf_NoBorder, wsf_UpperCase, wsf_LowerCase, wsf_ReadOnly |
wsf_Disabled, wsf_Invisible, wsf_Group, wsf_TabStop, wsf_AlignLeft, wsf_AlignCenter, wsf_AlignRight, wsf_MultiLine, wsf_AutoVScroll, wsf_AutoHScroll, wsf_NoBorder(for multiline), |
Scroll bar |
wsf_Disabled, wsf_Invisible, wsf_Group, wsf_TabStop |
wsf_Disabled, wsf_Invisible, wsf_Group, wsf_TabStop |
List box |
wsf_Disabled, wsf_Invisible, wsf_Group, wsf_TabStop, wsf_MultiSelect, wsf_ExtendedSel, wsf_OwnerDraw, wsf_OwnerDrawVariable, wsf_HasStrings, wsf_NoBorder, wsf_Sort, wsf_NoNotify, wsf_MultiColumn, wsf_HScroll, wsf_VScroll, wsf_NoReDraw, wsf_UseTabStops, wsf_WantKeyboardInput, wsf_DisableNoScroll, wsf_NoIntegralHeight |
wsf_Disabled, wsf_Invisible, wsf_Group, wsf_TabStop, wsf_MultiSelect, wsf_ExtendedSel, wsf_OwnerDraw, wsf_HScroll |
List edit |
wsf_Disabled, wsf_Invisible, wsf_Group, wsf_TabStop, wsf_OwnerDraw, wsf_OwnerDrawVariable, wsf_HasStrings, wsf_Sort, wsf_VScroll, wsf_NoIntegralHeight, wsf_AutoHScroll, wsf_DisableNoScroll, wsf_OEMConvert |
wsf_Disabled, wsf_Invisible, wsf_Group, wsf_TabStop, wsf_HScroll |
List button |
wsf_Disabled, wsf_Invisible, wsf_Group, wsf_TabStop, wsf_OwnerDraw, wsf_OwnerDrawVariable, wsf_HasStrings, wsf_Sort, wsf_VScroll, wsf_NoIntegralHeight, wsf_AutoHScroll, wsf_DisableNoScroll |
wsf_Disabled, wsf_Invisible, wsf_Group, wsf_TabStop, wsf_HScroll |
Icon |
||
Custom control |
wsf_Disabled, wsf_Invisible, wsf_Group, wsf_TabStop, wsf_Topmost, wsf_Transparent, wsf_HScroll, wsf_Vscroll |
wsf_Disabled, wsf_Invisible, wsf_Group, wsf_TabStop |
Even though controls are windows like any others, control event-handlers are not ordinarily part of an application, since standard controls are usually implemented by the host windowing system itself, or in extensions to it in the case of custom controls. When a control is created, the control must be the Child of another window. This relationship is important because a control sends messages, called notification messages, to its parent window when events such as input from the user occur in the control. The application uses these notification messages to invoke the appropriate processing. The notification messages appear as parent window e_Control events which include the Control's resource identifier, the type of the control, the Window handle of the control and provision for extra information describing the event:
e_Control(INTEGER Id, WINTYPE, Window Ctrl, CONTROL_INFO);
The extra information for the control event belongs to the following domain:
CONTROL_INFO =
activated(); % Click on a button etc.
scroll(SCROLLCODE, INTEGER); % Kind of scroll + new position
getfocus(); % Control will get focus
losefocus(); % Control will lose focus
modified(); % Content has been modified
dropdown(); % Listbutton drop down of list
closeup(); % Listbutton closes up of list
selchanged; % Selection changed in listbox
Note: e_Control events are not sent for scroll bars which are created according to the window style flags along the edges of a window. For these, special e_VScroll and e_HScroll messages are sent.
Creation of controls can either be automatic as part of creating a dialog from a description defined in the resource segment of an application or they may be created dynamically at run time. Dialogs in the resource files are laid out by the dialog editor in the VDE, and they are then created at run time by a call to win_CreateResDialog . Dialogs not laid out in resource files can be created dynamically at run time by a call to win_CreateDynDialog, but all parameters must then be specified at creation time.
To create a new control in a window, the predicate win_CreateControl can be used. To create a number of controls in a window from a structure definition (a WINDEF_LIST) win_CreateDynControl can be used. It is possible to add extra controls to a regular window or a dialog. One way to work around this is to create an invisible control, which can be made visible later.
The possibilities for creating a control can be seen by studying the parameters to the win_CreateControl predicates:
WINTYPE, % The type of the control
RCT, % Size and position
STRING Title, % Title to be displayed
WINDOW Parent, % Parent window for control
WSFLAGS, % Style flags for the control
INTEGER CtrlId) % A value supplied to the e_Create event
CtrlWin = win_CreateDynControl(
WINDEF_LIST, % Structure definition
WINDOW Parent) % A value supplied to the e_Create event
Group Box Controls
A group box is a rectangle in a dialog - it can appear as depressions on the dialog surface. The left-aligned title replaces the top line from the left for the length necessary. A group box is used to indicate visually that the set of controls within it has some common meaning as a group. A group box has no functional effect - it does not actually invoke group behavior in the controls it surrounds (this is done using the wsf_Group style for the controls in question), rather it is just like a static text control, it cannot be selected and no events are sent. There is no parent-child relationship to the controls inside a group box.
Icon Controls
An icon control allows you to display in a window or dialog an icon defined in the resources. They are always small bitmaps that are mapped to the pixels of the display. The apparent size relative to their parent window may therefore vary with screen resolution. They behave like a static text control in that no events are sent.
When an icon from the resource segment must be created dynamically, call win_CreateDynControl with the ICON domain alternative:
icon(WDEF, CTLID, RESID, WSFLAGS);
Text controls contain small pieces of text, generally used as labels for other controls, which can be placed at specific locations in the window or dialog. A static text control does not generate any events. It is possible to change the text while the application is running by a call to win_SetText and to hide and show the control by win_SetState.
Edit controls are used to allow the user to input text. These are often just a single line which will contain a name or a number etc., but can also be much larger and support multiple lines of data.
To set and get the content of an edit control the following predicates must be used:
Where WINDOW refers to the window handle for the control (which can be obtained by a call to:
CtrlHndl = win_GetCtlHandle(ParentWin, CtrlId)
Edit controls send three notification events:
ehandler(Win, e_Control(CtlID, CtrlType, CtrlWin, getfocus), 0):-
ehandler(Win, e_Control(CtlID, CtrlType, CtrlWin, losefocus), 0):-
ehandler(Win, e_Control(CtlID, CtrlType, CtrlWin, modified), 0):-
The modified notification is issued synchronously with every change made by the user, and will therefore be issued many times if the text in the edit control is being edited or if new text is being entered.
Push Buttons Controls
Push buttons are used to let the user invoke actions and are very simple to use. They can be either the usual OK, Close, or Cancel buttons to close the window or dialog, or other buttons that spawn a new window or dialog, start printing etc.
Push buttons are controls that appear to recede on mouse down and return on mouse up. The mouse up generates an e_Control event with the resource identifier of the push button which is sent to the event-handler of the parent window. If the mouse is dragged outside the push button before being released, the push button will return but no event will be generated.
For keyboard operation of push buttons, use the Tab or Shift+Tab keys to navigate until the desired push button is highlighted, then press the space bar.
If the resource identifier of the control is
idc_hello, the following clause could be used to create a little pop-up note:ehandler(Win, e_Control(idc_hello, _, _, _), 0):- !,
dlg_Note("Hello There!").
A push button can be assigned to be the default push button for a dialog. This means that if Enter is pressed anywhere in the dialog (except within an edit control with wsf_WantReturn set), the dialog will behave as if the Default push button had been pressed.
A Default push button is initially drawn with a heavier surrounding rectangle, indicating that it is the active control. A default push button is created by using the style flag wsf_Default, this is normally done by checking the corresponding option for a Default push button in the dialog editor in the VDE.
Notification Events for Push Buttons
Push buttons generate only one type of event:
ehandler(Win, e_Control(CtlId, CtlType, CtlWin, activated), 0):-
Tabbing to or away from a push button will not generate getfocus or losefocus e_Control events.
Check Box Controls
Check boxes are used to indicate a Boolean state. As an example, the check boxes in the VP compiler options dialog provide various settings indicating whether stack checking needs to be done, line number information should be generated etc. Unlike radio buttons, each check box is normally independent of other check boxes.
When the user clicks in a check box, an e_Control event is sent to the event-handler predicate for the window. If you are not using the Dialog Package, then you need to write code for your application to change the state of the check box. This can be accomplished with a call to the predicate:
win_Check(WINDOW, INTEGER OnOff).
A clause to handle the reversal of the check mark could look like the following:
mywin_event_handler(Win,e_Control(idc_check, CtrlType,CtrlWin,_),0):- !,
:
% Check that it is valid to change the state
:
State = win_IsChecked(CtrlWin),
toggle(State, NewState),
win_Check(CtrlWin, NewState).
When using the Dialog Package, you do not need to worry about changing the state of check boxes. In this case the initial setting must be supplied when the dialog is created and the final resulting value is returned when the dialog is terminated.
Note: Under MS Windows it is also possible to have a check box that automatically reverses its state by setting the flag wsf_Auto.
Check Provide Notification of Only One Type of Event:
ehandler(Win, e_Control(CtlId, CtlType, CtlWin, activated), 0):-
There are no focus notifications sent when tabbing to or away from a check box.
Radio Button Controls
Radio buttons are similar to check boxes with the difference that only one of the buttons in a group of radio buttons can be checked.
On the MS Windows platform, it is possible to give the wsf_Auto flag for radio buttons, which means that when the user clicks on a new radio button in a group, Windows itself will turn off the previously checked button in the group.
However for portability reasons, there is a predicate:
win_CheckRadioButton(WINDOW, WINLIST RadioWinList)
which can be used to check a new button, and will automatically turn off the previous selection in the group specified by the list of radio buttons. When using the Dialog Package, this predicate is used to manage the radio button checking automatically.
Notification Events from Radio Buttons
Radio buttons generate only one event:
ehandler(Win, e_Control(CtlId, CtlType, CtlWin, activated), 0):-
There are no focus notifications sent when the user uses the TAB key to move the focus to or away from a check box.
List Box Controls
A list box is a control that contains a list of items from which the user can choose. If the list box contains more elements than can be displayed simultaneously in the window, and the wsf_VScroll flag is not turned off, the window will automatically provide a vertical scroll bar. The user can scroll up and down in the list to change a current selection or double-click on a given list box element.
There are various specialized list boxes like list buttons and list edits, and by setting appropriate flags you can allow multiple selections in a list box or multiple columns (multiple columns are not supported on all platforms).
List boxes will normally contain strings, but by using the ownerdraw style, you can create list boxes that can display any kind of data.
The VPI has a number of predicates which operate on list boxes. These all have the prefix "lbox_". Each of these predicates requires the handle to the list box, which can be obtained with a call to win_GetCtlHandle using the handle to the parent window and the list box resource identifier. When specifying an index in a list box, 0 indicates the first, 1 the second etc. The index value -1 has a special meaning - it is used to add an element at the end.
The VPI\EXAMPLES\LISTBOX example shows how to use these list box predicates:
lbox_Add(WINDOW, INTEGER Index, STRING Str)
This predicate will add a new string to the list box and display it if it lies within the list box window. It will be inserted so the new string will be positioned at Index after it is inserted. If Index is -1 the string will be added to the end of the list and displayed if still in the list box window. No scrolling will occur and the currently selected string remains selected.
lbox_Add(WINDOW, INTEGER Index, SLIST StringList)
This version of lbox_Add adds all the strings in the list StringList list at the given position in the existing list. Using the Index -1 will add the list of strings to the end of the list box.
lbox_Add(WINDOW, STRING Str)
This version must be used when the sort attribute is set for a listbox. For a listbox with no sorting the items will be added to the end of the listbox.
lbox_Add(WINDOW, SLIST StringList)
This version must be used to add a list of strings to a listbox with the sort flag set. If sort is not set, the strings will be added to the end of the listbox.
Calling this predicate will clear the list box and remove all entries.
lbox_Delete(WINDOW, INTEGER Index)
This predicate can be used to delete a given element in the list. 0 is the first, 1 is the second etc. A -1 value will be ignored.
Returns all the strings from a list box as a list of strings.
INTEGER = lbox_CountAll(WINDOW)
Returns the number of strings in the list box.
lbox_GetItem(WINDOW, INTEGER Index)
This predicate can be used to retrieve a given element in the list. 0 is the first, 1 is the second etc. A -1 value will be ignored.
lbox_GetSel(WINDOW, SLIST, ILIST)
Returns a list of the selected strings and a list of their indices, both lists in the same order. This predicate is intended for multiple selection list boxes, although it will return single item lists for regular list boxes with something selected. If there are no selections, the predicate will return two empty lists.
INTEGER = lbox_GetSelIndex(WINDOW)
Return the index of the current selection. The predicate will fail if there is no current selection.
lbox_IsSel(WINDOW, INTEGER Index)
This predicate will succeed or fail depending on whether or not the given element in a list box is currently selected.
lbox_SetSel(WINDOW, INTEGER Index, BOOLEAN Select)
This predicate is used to select or de-select an element in a list box. For a multiple selection list box, this predicate will turn on or off the selection of this one element without affecting other selections. For single selection list boxes, setting the selection to a given element will automatically turn off any previous selection.
lbox_Suspend(WINDOW)
lbox_Resume(WINDOW)
These two predicates can be used to inhibit screen updating temporarily and then restore it, as might be required to keep a list box from 'flashing' during updating activity. As an example, if filling a list box from an external data-source, involving a series of fetch-data - lbox_add operations, it will be more visually pleasing to enclose the operation with lbox_suspend, lbox_resume.
lbox_SetColumnWidth(WINDOW, INTEGER width)
This predicate can be used to set the width of the columns in multi-column list boxes.
The preceding dialog was created by selecting the multi-column style for the list box, and adding the following code for the initialization:
dlg_choose_your_favorite_language_eh(_Win, e_Create(_),0):-!,
LB = win_getCtlHandle(_Win, idc_choose_your_favorite_language),
lbox_setColumnWidth(LB, 56),
!.
lbox_SetTabStops(WINDOW, ILIST TabList)
For a list box defined with the flag wsf_UseTabStops, you can use this predicate to set tab stops to define "visual columns". The textual information presented on each line of the list box here is considered one element. Note that this is not to be confused with the multi-column list boxes. If the list box strings have embedded tab characters, the characters following each tab will be aligned with the corresponding position specified in the tab position list. The tab positions are given in Dialog Base Units.
This technique is required if the dialog font is proportional and you need to align columns of information as in the following display:
Note that each line remains a single selected entity, as the tab stop affects only the line's display characteristics.
As an example of calling the predicate, the above tab settings were initialized as:
dlg_select_employee_eh(_Win, e_Create(_),0):-!,
List = win_getCtlHandle(_Win, idc_select_employee_1), lbox_setTabstops(List, [36,128,238]),
!.
The actual tab stop values were derived by subtracting the dialog units value of the list control's left-most coordinate from the leftmost coordinate of each of the text controls forming the headers.
Notification Events from List Boxes
% when the user makes a selection
ehandler(Win, e_Control(CtrlID, CtrlType, CtrlWin, selchanged), 0):-
% when the user double clicks in a list box
ehandler(Win, e_Control(CtrlID, CtrlType, CtrlWin, activated), 0):-
% when a list box is about to gain focus
ehandler(Win, e_Control(CtrlID, CtrlType, CtrlWin, getfocus), 0):-
% when a list box is about to lose focus
ehandler(Win, e_Control(CtrlID, CtrlType, CtrlWin, losefocus), 0):-
lbox_SetTopIndex(WINDOW Win, INTEGER Index)
Scrolls the list box so that either the Index item appears at the top of the list box or the maximum scroll range has been reached. The Index is the zero-based index of the item in the list box.
List buttons are a combination of a push button and a list box. In the initial state the list button occupies a single line, but there is a little push button on the right which when pressed shows the contents of the list in a pop-up window below or, failing that, above the control, with a vertical scroll bar if necessary. A selection can be done from the list of alternatives. Pressing the button again will retract the list from the display, leaving the current selection in the remaining single line. Both the single line and the list are read-only and cannot be edited by the user.
The styles, events and operations are the same as for list boxes, with the following exceptions:
When creating a list button, the rectangle given will be the size of the control including the drop down list.
A list button can not have the multi-selection style flag. List buttons also do not support tab stops or multiple columns, so the tab stop and multi-column flags, as well as the lbox_SetTabStops and lbox_setColumnWidth predicates are not applicable.
Notification Events from List Buttons
% when the user changes a selection
ehandler(Win, e_Control(CtrlID, CtrlType, CtrlWin, selchanged, 0):-
% when the List-button drops down
ehandler(Win, e_Control(CtrlID, CtrlType, CtrlWin, dropdown), 0):-
% when the List-button closes up
ehandler(Win, e_Control(CtrlID, CtrlType, CtrlWin, closeup), 0):-
% when a List-button is about to gain focus (only under Windows)
ehandler(Win, e_Control(CtrlID, CtrlType, CtrlWin, getfocus), 0):-
% when a List-button is about to lose focus (only under Windows)
ehandler(Win, e_Control(CtrlID, CtrlType, CtrlWin, losefocus), 0):-
A List Edit is a combination of an edit control and a list box. They behave primarily like edit controls, but the user can pop up a list of predefined strings and if one is selected , it is placed in the edit control for further editing. With the list showing, editing the single line can be used to edit that selection. With the list showing, clearing the single line and entering a new value can be used to place a new value in the list in addition to the old one. The drop down list can not be edited by the user (although you could add this feature yourself).
Notification Events from List Edit Boxes
% when changing the selection:
ehandler(Win, e_Control(CtrlID, CtrlType, CtrlWin, selchanged, 0):-
% when the List-button drops down
ehandler(Win, e_Control(CtrlID, CtrlType, CtrlWin, dropdown), 0):-
% when the List-button closes up
ehandler(Win, e_Control(CtrlID, CtrlType, CtrlWin, closeup), 0):-
% when a List-button is about to gain focus
ehandler(Win, e_Control(CtlID, CtrlType, CtrlWin, getfocus), 0):-
% when a List-button is about to lose focus
ehandler(Win, e_Control(CtlID, CtrlType, CtrlWin, losefocus), 0):-
% when a List-button is modified
ehandler(Win, e_Control(CtlID, CtrlType, CtrlWin, modified), 0):-
With the window types wc_HScroll and wc_VScroll, it is possible to create horizontal and vertical scroll bars at any location in a window. They behave like the scroll bars belonging to the window decorations, and can be used in a very similar way.
Scroll bars can also be specified as part of edit controls and of list box controls. In these cases the scroll processing is done by the control and no notification is provided to the parent dialog.
Notification Events from Scroll Bars
Scroll bar controls send e_Control events, while the scroll bars belonging to the window decorations send e_HScroll and e_VScroll events.
The event-handler clauses for these three types of scroll bars are:
ehandler(Win,e_Control(ResId,CtrlType,CtrlWin,scroll(ScrCode, Pos)),0):-
ehandler(Win,e_VScroll(ScrCode,Pos),0):-
% vertical scroll bar activated
ehandler(Win,e_HScroll(ScrCode,Pos),0):-
% horizontal scroll bar activated
ScrCode belongs to the domain SCROLLCODE which tells what the user has done with the scroll bar (line up, page down, end etc.). Values are listed in the table below.
A single click on the scroll bar above or below the scroll thumb will generate a sc_PageUp or sc_PageDown event, respectively. It is entirely up to the programmer to interpret what these events should mean in the context of the application and make any resulting changes to the window.
The Pos field is important when the user drags the scroll bar thumb. This value indicates the thumb position relative to the range of the scroll bar. While the user is dragging the thumb, the application will receive sc_ThumbTrack events, and when the user releases the thumb sc_Thumb event will be sent.
Table 1.7: Scroll Bar Codes
Code |
Meaning |
sc_LineUp |
One line up |
sc_LineDown |
One line down |
sc_PageUp |
Previous page |
sc_PageDown |
Next page |
sc_Top |
To top |
sc_Bottom |
To bottom |
sc_Thumb |
Thumb re-positioning |
sc_ThumbTrack |
Dynamic thumb tracking |
The same set of predicates is used to manipulate the scroll bars for the window decorations and the scroll bars which are controls. Since there is no separate window handle for the decoration scroll bars, the handle to the window itself is used to reference both of these, and a special parameter SCROLL_TYPE is used to tell which scroll bar is referred to:
GLOBAL DOMAINS
SCROLL_TYPE = INTEGER
CONSTANTS
sb_Horz = 0 % Horizontal window-scroll bar
sb_Vert = 1 % Vertical window-scroll bar
sb_Ctl = 2 % Scroll bar controls in dialog or window
When working with the actual position of the scroll bar thumb, the range of the scroll bar must first be set. The scroll bar range can be set by a call to the predicate:
win_SetScrollRange(WIN, SCROLL_TYPE, INTEGER Min, INTEGER Max)
and the range can be retrieved later by a call to:
win_GetScrollRange(WIN, SCROLL_TYPE, INTEGER Min, INTEGER Max)
If you do not explicitly set a range, the default scroll range for the standard window decoration scroll bars is 0 to 100 (there is no default range for scroll bars used as controls). On the 16-bit Windows platform, the difference between Min and Max must be no larger than 32,767.
The actual position of the scroll bar thumb must be moderated by the application - it is not changed automatically by the user's mouse manipulations. The position must be inside the set range for the scroll bar. An application sets the position using:
win_SetScrollPos(WINDOW, SCROLL_TYPE, INTEGER Pos)
The current position may be read using:
INTEGER = win_GetScrollPos(WINDOW, SCROLL_TYPE)
On some platforms it is possible to set the size of the thumb relative to the size of the scroll bar. This provides a visual representation of how much of the data can be seen in the window compared to the full data. The proportion is a value between zero and the length of the interval for the scroll bar (Max - Min)
win_SetScrollProportion(WINDOW, SCROLL_TYPE, UNSIGNED Proportion)
The current proportion may similarly be read using
UNSIGNED = win_GetScrollProportion(WINDOW, SCROLL_TYPE)
By specifying the attribute wsf_OwnerDraw or wsf_OwnerDrawVariable, it is possible to create a control which otherwise behaves as the selected control normally does, being responsible for the actual drawing of the control. Specifying this attribute means that the control's parent window event-handler (for dialog controls, the dialog event-handler) must be able to handle the following two events:
e_OwnerMeasureItem(CtlType, CtlId, ItemId, LONG data);
e_OwnerDraw(UNSIGNED CtlType, UNSIGNED CtlId,
UNSIGNED ItemID, OD_ITEMACTION ItemAction,
OD_ITEMSTATE ItemState, WINDOW Win,
RCT RectItem, LONG ItemData);
The first event is used for list boxes to measure the size of an item, the event-handler must return a value which is the size of the control. and the second is the signal for the application to draw the content of the control.
Please study the VPI\TOOLEXAM\OWN_DRAW example for the details.
Besides the portable built-in controls on most platforms, it is also possible to create and use non-portable custom controls.
Custom controls are normally distributed as Dynamic Link Libraries (.DLL), in this case you must have a .LIB file for the custom control, link it into the .EXE file, - and know the function to call to initialize the .DLL.
If you are creating a custom-control (Child) window programmatically, you would ordinarily use win_CreateDynControl in the following manner:
CustomCtl = win_CreateDynControl(
[customctl(wdef(wc_Custom, RCT, Title, u_Pixels),
ControlClass, 101, FLAGS)], Win, 0),
The ControlClass is a string which identifies the custom window class for the control.
When you want to put a custom control in a dialog from the VDE dialog editor, you need to select Custom from the Controls menu. In the properties of the control you need to type in the desired class name.
For custom controls, the Flags list must be formatted according to a special convention to allow simultaneous specification of native (custom-control specific) attribute values, which are the actual 32-bit values OR-ed together, and the VPI style values, which are multi-platform inclusive, and re-mapped by the VPI code to the underlying platform 32-bit values. By convention the Flags style list values are processed as VPI style constants until a 0 list element is encountered, following which all subsequent style values are directly OR-ed together. As an example, the previous FLAGS variable, when using one of the more popular grid controls in a situation where it is to start invisible, might be specified as:
FLAGS=[ wsf_Tabstop, wsf_Invisible, % vpi flags
0, % switch to non-mapped flags
HGRID_ColumnSelect, % these are defined by header files
HGRID_AllowInsert ], % supplied with controls
It is also possible to create dynamic dialogs using custom controls, as in the following call which uses some of Borland's popular control extensions:
WLIST = [dlg_font(wdef(wd_Modal, rct(10, 10, 210, 210), "Dialog", u_Pixels), "Helv", 8, [wsf_Close, wsf_TitleBar, wsf_Minimize, wsf_Maximize]),
customctl(wdef(wc_Custom, rct(5, 5, 65, 30), "OK", u_Pixels), "BorBtn", 1, [wsf_Default, wsf_TabStop]),
customctl(wdef(wc_Custom, rct(5, 35, 65, 50), "Check box", u_Pixels), "BorCheck", 102, [wsf_TabStop]),
customctl(wdef(wc_Custom, rct(5, 55, 65, 70), "Radio", u_Pixels), "BorRadio", 103, [wsf_TabStop]),
customctl(wdef(wc_Custom, rct(5, 32, 65, 34), "", u_Pixels), "BorShade", 104, [wsf_TabStop]),
ctl(wdef(wc_PushButton, rct(5, 80, 65, 95), "NotCustom", u_Pixels), 105, [wsf_TabStop]),
customctl(wdef(wc_Custom, rct(-4, -4, 204, 204), "", u_Pixels), "BorShade", 110, [wsf_TabStop])],
win_CreateDynDialog(WLIST, dialog_event_handler, b_False).
Note that custom controls do not fully participate in the VPI framework. Thus, if they send non-standard messages to the dialog event-handler, these events will arrive as e_Native events which must be decoded according to control-specific constants or other information supplied by the vendor for use with these products.
In general, you must be sure that the custom control classes are properly initialized before attempting to use them in a dialog. The event-handlers specific to these controls are normally implemented in .DLL files. It is often necessary to call one or more initialization functions in the .DLL to initialize the control classes. Some DLLs accomplish this in a hidden way that depends on linking the DLL's import library in a particular order. If you are using a third party control library and you encounter an error when attempting to use your dialog, you should review the control's documentation to be sure that you are properly calling any documented initialization functions first, and as a last resort review your Build scripts to ensure that the DLL's import library (usually a file with the same name and a .LIB extension) is present, where it may need to precede the other libraries.
VBX controls are a more complicated extension of the same idea, where the control classes not only need to be initialized, but the host environment (in this case your Visual Prolog program) must pretend to be the Visual Basic Runtime environment, from the standpoint of properly following the message protocols expected by the VBX controls. Therefore the use of VBX controls requires that you link with an emulation library (such libraries ship with Visual C, Borland C, or can be purchased separately from third party vendors). Note that apart from the special initialization requirements, the VPI programming is the same for these and for the simpler custom controls.
*.
Dialog boxes are a special kind of windows. Dialogs are normally filled with controls which can interact with the user by displaying output or accepting input.
The VPI provides a number of "common dialogs", or convenient pre-defined dialog boxes and handlers with a simple function-call interface for commonly occurring situations. This group of VPI predicates all start with the prefix 'dlg_', and can be easily inserted into the source code using the right-button mouse menu.
Where no suitable common dialog exists, a custom dialog may be designed with the VDE Dialog editor. This dialog may be stored is the resource segment (.RES file) or be created dynamically by calling some predicates that builds up the dialog either in a single call from a WINDEF_LIST, or control by calling the predicate win_CreateControl. Whichever method is used, the VDE Dialog Expert can generate the code framework.
Setting, retrieving or changing the contents of the controls in a dialog can be done on a per-control basis by using VPI predicates, and can also be done on a per-dialog basis by using the VPI Dialog Package. The Dialog Package is described in chapter 2 of this book..
One feature that is supported in dialogs but not in windows is the ability to navigate between controls by using the Tab and Shift+Tab keys.
There are two kinds of dialogs: modeless and modal dialogs. A modal dialog require that certain actions be taken before the user can activate any other window, menu or control in the application. One way to remember this is to think of a modal dialog as putting the whole application into a particular mode - one in which the dialog must finish before anything else in the application can receive input. In contrast, any number of modeless dialogs can concurrently be on the screen of which any one can be made active by the user clicking the mouse in it so that it will accept input
On the MS Windows platform, dialogs will always appear and remain in front of their parent window and do not respect its boundary.
Common dialog boxes make it easier and faster to develop applications. A common dialog box is a dialog box an application can display by calling a single predicate rather than by supplying a dialog box event-handler and a resource description containing a dialog box layout. The calls to the common dialogs can be pasted into the application being edited by using the Edit|Insert|Predicate call|Window, Dialog or Toolbar facility.
Get User Response to a Yes/No etc. Question
Integer = dlg_Ask(PromptString, ButtonTitleList)
Integer = dlg_Ask(TitleString, PromptString, ButtonTitleList)
Integer = dlg_Ask(ParentWin, TitleString, PromptString, ButtonTitleList)
The predicate dlg_Ask puts up a little dialog, which has up to three buttons. The titles for the buttons are given in a list. The buttons will normally be something like Yes, No and Cancel. The possible responses correspond to the order in the ButtonTitleList and indicate which button was pressed:
resp_default = 0
resp_2 = 1
resp_3 = 2
As example the following call:
_ANSWER = dlg_Ask("MyTitle", "MyPrompt",
["MyButton1", "MyButton2", "MyButton3"]),
Will result in the following dialog:
User Selects a Filename(s) to Opening/Saving a File(s)
STRING /*FirstFileName=*/ dlg_GetFileName (
STRING InitMask, SLIST Types, Title, ILIST Flags, StartPath,
SLIST SelectedFileNames)
The dlg_GetFileName is used to set up a dialog where the user can select a filename(s) for the file to open/save. The InitMask is the default file mask. The Types is a list of alternative file masks that the user can choose to select instead. The Title specifies the dialog title. The Flags is a list of flags specifying the dialog properties. The StartPath specifies a start directory for searching files. If StartPath = "", the current directory is used. The FirstFileName returns the fully qualified filename of the first selected file.
The dlg_GetFileName allows the user to select a list of filenames when the dlgfn_MultiSel flag is specified in the Flags list. The SelectedFileNames is the output parameter containing the list of all selected filenames. If the flag dlgfn_Save is specified, the "Save As" style dialog box is displayed. If the selected file already exists, then the "Save As" style dialog box generates a message box in that the user must confirm whether to overwrite the file. If the dlgfn_Save flag is not specified, the "Open File" style dialog box is displayed (default).
As an example, consider this call:
Flags = [dlgfn_MultiSel],
FirstFileName = dlg_GetFileName("*.pro",
["Prolog Files (*.pro)","*.pro",
"Prolog Headers (*.pre,)","*.pre",
"All Files (*.*)", "*.*", ],"MyTitle",
Flags,"",
OutFileNames),
It will result in the following dialog:
String = dlg_GetStr(Title, MessageString, InitString)
String = dlg_GetStr(ParentWin, Title, MessageString, InitString)
The dlg_GetStr is used to let the user input a string. The following call:
_NewSTRING = dlg_GetStr("MyTitle","MyMessage","InitStr"),
This will result in the following dialog:
User Selects from a List of Strings
BOOLEAN = dlg_ListSelect(Title, StringList, PreSelectionIndexInteger,
SelectedString, IndexInteger)
BOOLEAN = dlg_ListSelect(ParentWin, Title, StringList,
PreSelectionIndexInteger, SelectedString, IndexInteger)
BOOLEAN = dlg_ListSelect(ParentWin, Title, StringList,
PreSelectionIndexInteger, SelectedString, IndexInteger, BOOLEAN Sort)
The dlg_ListSelect is used to let the user choose an element from a list of values. As an example, see the following call:
SLIST=["First string", "Second String", "Third string"],
PreSel= 1,
dlg_ListSelect("MyTitle",SLIST, PreSel, _StrSel, _Index),
This call will result in the following dialog:
User Selection and Set up of a Printer
dlg_PrintSetup(NoOfCopiesInteger, FirstPageInteger, LastPageInteger)
The dlg_PrintSetup is used to let the user choose which printer to use for the printing. The call will return how many copies the user wants, and the desired page range.
dlg_PrintSetup(_NoOfCopies, _FirstPage, _LastPage),
This call will result in the following dialog:
COLOR = dlg_ChooseColor(OldColor)
COLOR = dlg_ChooseColor(ParentWin, OldColor)
The dlg_ChooseColor are used to let the user select a color.
InitColor = color_Red,
_NewCOLOR = dlg_ChooseColor(InitCOLOR),
This will result in the following dialog:
The dlg_ChooseColor/2 is used to specify the window ParentWin that will receive the focus, after the dialog will be closed.
NewFontBinary = dlg_ChooseFont(OldFontBinary)
NewFontBinary = dlg_ChooseFont(
FONT OldFont,
STRING FontName,
INTEGER FontSize)
The dlg_ChooseFont is used to let the user select a font.
OldFONT = font_Create(ff_fixed,[],9),
_NewFONT = dlg_ChooseFont(OldFONT),
This will result in the following dialog:
dlg_Error(MessageString)
The dlg_Error should be called when the program needs to display an error message. As example the following call:
dlg_Error("My Error Message"),
Will result in the following dialog:
dlg_Note(MessageString)
dlg_Note(TitleString, MessageString)
dlg_Note
is handy for popping up a small note box to display a short message. As example the following call:dlg_Note("MyTitle", "Hello There"),
Will result in the following dialog:
Each control in a dialog must have an integer identifier value, that is unique within the dialog. This identifier is called the control identifier (CtrlId). The integer corresponding to the control identifier is used in notification events back to the parent window. It control identifier indicates which control issued the event.
When a new control is placed in a dialog with the VDE Dialog and Window editor, it receives a named Prolog constant identifier. At compilation time, the VDE generates a <project>.CON file which assigns integer values to all the control resource identifiers according to certain rules. This file is included in all modules that utilize the VPI. At project build time, the VDE also generates the <project>.RES file in which all controls are identified only by these integers.
Special Control Resource identifiers
The following resource identifiers should be used for all OK and Cancel buttons. The constants are predefined in the VPI.CON file.
idc_ok = 1
idc_cancel = 2
idc_help = 3
When using the Dialog Package it is important that a dialog have these three push buttons with the proper identifiers, because the Dialog Package assumes that these push buttons exist, and uses these to terminate the dialog.
If a dialog has been created with Visual Prolog Dialog Editor and is placed in the resources segment, it is possible to create a dialog with the call:
WINDOW = win_CreateResDialog(
ParentWin, WINTYPE, ResId, EventHandler, CreationData)
ParentWin
should be the window to gain focus after the dialog is terminated. WINTYPE should be either wd_Modal or wd_ModeLess depending on whether a modal or modeless dialog should be created. ResId is the integer, that identifies the dialog in the .RES file, EventHandler is the Prolog callback predicate which is used to handle the events from the dialog, CreationData is a long value which is passed to the e_Create event.Alternatively, is it possible to create a dialog from a Prolog list structure by a call to:
WINDOW = win_CreateDynDialog(ParentWin, WINDEF_LIST, EventHandler,
CreationData)
Another way to create a dialog is to first make a call to win_Create to create the dialog window itself, and then create the controls one by one with calls to win_CreateControl.
As soon as the dialog is created, there will be sent an e_Create event to the event-handler for the dialog. In the clause catching the e_Create event, all controls in the dialog should be initialized. For each control type there are different predicates that are used to set the values of the control.
Table 1.8: Manipulation Predicates for Controls
Control type: |
Manipulation predicates: |
Check boxes |
win_Check |
Scroll bars |
win_SetScrollPos |
Edit controls |
win_SetText |
Static text |
win_SetText |
List boxes |
All the lbox_ predicates |
Note, that all the manipulation predicates work on the window handles for the controls, not on their control identifiers.
There are a number of VPI predicates that draw text, pixels and various geometric shapes. These predicates are all prefixed by 'draw_'. Drawing operations in a window utilize the current drawing attributes for that window, which are referred to as drawing tools. The drawing tools are: Pen, Brush, Drawmode, Font, Foreground Color, Background Color and Background Filling Mode.
The example in VPI\EXAMPLES\DRAWING shows various combinations of drawing primitives and drawing tool options, and illustrates the corresponding results.
It is possible to retrieve the settings of all the current drawing tools for a window into a single variable and then use this variable to restore them at a later point, by using the two predicates win_GetDrawTools/1 and win_SetDrawTools/2. Here is the code sequence:
SaveTools = win_GetDrawTools( Win ),
% Change the drawing settings and draw with different settings
win_SetDrawTools(Win, SaveTools ), % restore the pre-existing ones
The sub-components of the drawing tools object are defined in the DRAWTOOLS domain:
GLOBAL DOMAINS
DRAWTOOLS = draw_tools(
PEN,
BRUSH,
DRAWMODE,
FONT,
COLOR Foreground,
COLOR Background,
BK_MODE)
Normally you would use one or more of the tool-specific predicates to change only the tools that need to be changed.
Predicates for the individual tools are described below.
Lines and the periphery of figures are drawn using a PEN. The way a line will look, i.e. its width, style and color, can be changed by setting the pen attributes before the drawing is done.
The domain for pens is:
GLOBAL DOMAINS
PENWIDTH = INTEGER
PEN = pen(PENWIDTH, PENSTYLE, COLOR)
Table 1.9: Pen Styles
Style |
Meaning |
ps_Hollow |
Selects an invisible pen. |
ps_Solid |
Solid pen. |
ps_Dash |
Dashed pen. |
ps_Dot |
Dotted pen. |
ps_DashDot |
Alternating dashes and dots. |
ps_DashDotDot |
Alternating dashes and double dots. |
The win_GetPen predicate returns the current Pen. This is often used to temporarily change the pen and set it back to the old value after drawing.
Pen = win_GetPen(Win)
Setting the Pen to be Current for a Window
The win_SetPen/2 predicate changes the pen used for drawing in a given window. The following code sets a red pen with the solid style and a width of two pixels.
PenWidth = 2,
Pen = pen(PenWidth , ps_Solid, color_Red),
win_SetPen(Win, Pen),
When drawing text, the text is drawn with the current foreground color, and if the BK_MODE is set to bk_Opaque, the background will first be filled with the current background color. By using the value bk_Transparent, the text will be drawn directly on whatever is underneath it.
GLOBAL DOMAINS
BK_MODE = INTEGER
CONSTANTS
bk_Transparent = 1
bk_Opaque = 2
To change the current background mode use the following predicate:
win_SetBackMode(Win, BK_MODE)
Figures are filled using a Brush. The way a brush paints is determined by the brush attributes, which consist of a color and a fill pattern style.
GLOBAL DOMAINS
BRUSH = brush(PATSTYLE, COLOR)
A table of fill pattern styles will be found below. A table of the basic 16 colors is on page
*.Table 1.10: Brush Fill Pattern Styles
Style Identifier |
Meaning |
pat_Hollow |
Hollow brush |
pat_Solid |
A solid brush with specified color |
pat_Horz |
Horizontal hatch |
pat_Vert |
Vertical hatch |
pat_Fdiag |
45-degree downward hatch (left to right) |
pat_Bdiag |
45-degree upward hatch (left to right) |
pat_Cross |
Horizontal and vertical cross-hatch |
pat_DiagCross |
45-degree cross-hatch |
Getting and Setting the Current Brush
The win_GetBrush predicate returns the current Brush
CurrentBrush = win_GetBrush(Win)
The win_SetBrush/2 predicate changes the current brush for a window.
win_SetBrush(Win, NewBrush)
It is possible to change the current font by a call to:
win_SetFont(Win, FontBinary)
You can retrieve the current font by a call to:
FontBinary = win_GetFont(Win)
Please refer to the section about font handling (page
*) for an explanation on how to create or select alternate fonts.The drawing mode specifies how pixels that are drawn on the screen combine with those already there. The drawing mode options are listed in the table below.
The normal drawing mode is dm_CopyPen which just sets the pixels to the new values. dm_XorPen or dm_Not can be used to draw rubber bands, where the 'stretching' effect is created by drawing a rectangle, and erasing it by drawing a second time (See VPI\EXAMPLES\MAPPING for an example of such code).
Getting and Setting the Current Drawing Mode
The win_GetDrawMode/1 predicate obtains information about the current drawing mode:
DrawMode = win_GetDrawMode(Win)
The win_SetDrawMode/2 predicate sets a new drawing mode:
win_SetDrawMode(Win, dm_Copy)
Table 1.11: Available Drawing Modes
Mode Identifier |
Meaning |
dm_Black |
Black. |
dm_NotMergePen |
Not(Pen | Screen Pixel) |
dm_MaskNotPen |
(Not(Screen Pixel)) & Pen) |
dm_NotCopyPen |
Not(Pen) |
dm_MaskPenNot |
(Not(Pen)) & Screen Pixel |
dm_Not |
Not(Screen) |
dm_XorPen |
Pen Xor Screen Pixel |
dm_NotMaskPen |
Not(Pen & Screen Pixel) |
dm_MaskPen |
Pen & Screen Pixel |
dm_NotXorPen |
Not(Pen Xor Screen Pixel) |
dm_Nop |
Screen not changed |
dm_MergeNotPen |
(Not(Screen Pixel)) | Pen) |
dm_CopyPen |
Pen |
dm_MergePenNot |
(Not(Pen)) | Screen Pixel) |
dm_MergePen |
Pen | Screen Pixel |
dm_White |
White. |
The following summarizes the available drawing operations. For displaying bitmaps or graphics, please refer to the Pictures section (page
*).The draw_Pixel/3 predicate sets a pixel to a given color.
draw_Pixel(WINDOW Window, PNT Position, COLOR Color)
The reverse operation is performed by:
Color = Win_GetPixel(Win , pnt(X, Y))
This returns the color of the pixel at logical coordinate (X, Y) in window Win.
draw_FloodFill(Win, PNT, StopColor)
changes the pixel at coordinate (X, Y) to the color specified by the last win_SetBrush/2 operation for the window. The filling operation is then recursively applied to all neighbor pixels until the color STOP_COLOR is reached. If not reached, the drawing window will be completely filled.
draw_Icon(Win, X, Y, resource_identifier)
This predicate retrieves an icon with the given resource identifier from the resource segment of the .EXE file of the active application and draws it. The icon's top left corner is placed at logical coordinate (X, Y).
draw_Line(Win, PNT, PNT)
draw_Arc(Win, Rct, INTEGER StartX, INTEGER StartY, INTEGER StopX, INTEGER StopY)
draw_Polyline(Win, PntList)
These predicates draw figures using with the Pen without filling by the Brush.
draw_Ellipse(Win, Rct)
draw_Pie(Win, Rct, INTEGER StartX, INTEGER StartY, INTEGER StopX, INTEGER StopY)
draw_Polygon(Win, PNTLIST)
draw_Rect(Win, Rct)
draw_RoundRect(Win, Rct, INTEGER EllipseWidth, INTEGER EllipseHeight)
The shape around the figures is drawn by the current Pen. These figures are then automatically filled by the current Brush.
There are many options when drawing text:
draw_Text(Win, X, Y, String)
draw_Text(Win, X, Y, String, DrawLength)
draw_Text
draws the first DrawLength characters from String in the window Win, where X and Y are the logical coordinates for the top left corner of the first letter in the string. You can omit DrawLength or set it to -1 to draw the entire string.draw_TextInRect(Win, Rct, String, DrawLength, ILIST DrawFlags)
The draw_TextInRect function writes the first DrawLength characters from String within a clipping rectangle, using the currently selected font. The dtext_Flag possibilities are shown in the table below:
Table 1.12: DrawText Flags
Flag Identifier |
Meaning |
dtext_center |
Centers the text horizontally in the rectangle |
dtext_left |
Aligns the text to the left of the rectangle. |
dtext_right |
Aligns the text to the right of the rectangle. |
dtext_vcenter |
Centers the text vertically. Only works in combinations with dtext_singleline. |
dtext_bottom |
Specifies that the text is aligned to the bottom of the rectangle. Only works with dtext_singleline. |
dtext_top |
Aligns the text to the top of the rectangle. |
dtext_singleline |
Specifies a single line. Line feeds '\n' are ignored |
dtext_noclip |
Draws the text without clipping to the rectangle. |
dtext_wordbreak |
Draws the text with word wrapping |
dtext_expandtabs |
Expands tab characters. |
dtext_noprefix |
Turns off processing of the & prefix characters. |
dtext_externalleading |
Includes the font external leading in line height. |
It is often important to know beforehand what the presentation size of a text string will be. The win_GetTextExtent/5 predicate returns the height and width (in logical units) of the rectangle that would contain the text were it to be drawn in the given window. The third parameter gives the number of characters to include in the measurement, and setting the value to -1 includes the entire string.
Text = "This is a test",
win_GetTextExtent(Win, Text, -1, Width, Height),
There are two predicates to change the foreground and background color for the drawing text:
win_SetBackColor(Window, Color)
win_SetForeColor(Window, Color)
A table of identifiers for the basic 16 colors is be found on page
*. There are no predicates for directly retrieving the current foreground and background colors, however they may be obtained from the draw_tools structure returned by a call to win_GetDrawTools for that window.At draw_Text time, if the current background drawing mode is bk_Opaque for the draw_Text window the rectangle is filled with the color from the last issued win_SetBackColor/2 predicate for that window, then the text is drawn. If the background drawing mode for the draw_Text window is bk_Transparent, the text is drawn on the existing background.
% Draw Text in White on Yellow background
win_SetForeColor(Win, color_White),
win_SetBackColor(Win, color_Yellow),
win_SetBackMode(Win, bk_Opaque),
Text = "This is some text"
X = 20, Y = 20,
draw_Text(Win, X, Y, Text),
Each window has a font, which is used when drawing text. This font can be changed by calling the win_SetFont predicate.
A font can either be selected by the user in a call to the common font dialog - or it can be created based on font family, font style and font size by a call to font_Create.
A font is a VPI binary domain. This means that it can be stored in a file or external database to be used at a later time. It is not possible to inspect the settings directly but the predicate font_GetAttrs can retrieve some information.
Font = font_Create (FontFamily, StyleFlags, Size)
This returns a font structure. For the font family and for the style flags there are the following possibilities:
Table 1.13: Font Families
ff_System |
ff_Fixed |
ff_Times |
ff_Helvetica |
Table 1.14: Font Style Flags
fs_Bold |
fs_Italic |
fs_UnderLine |
The StyleFlags parameter in font creation is a list so that more than one can be specified. An empty list will create a normal font.
To set a new font in a window use the win_SetFont/2 predicate:
win_SetFont(Win, Font)
To change the attributes of a font use the predicate:
font_SetAttrs(Font, FontStyle, FontSize)
Size information for the current font can be obtained by using the win_GetFontMetrics/4 predicate.
win_GetFontMetrics(Win, Leading, Ascent, Descent)
Win
is the handle to the window, Leading is the amount of extra leading (space) that the application adds between rows. Ascent is the space between the base line and the top of the character cell. Descent is the space between the bottom of the character cell and the base line.Style information for the current font is obtained by using the font_GetAttrs/3 predicate:
FontName = font_GetAttrs(Font, StyleFlags, FontSize),
Here, the returned StyleFlags is a list of styles, returned FontSize is an integer, and the returned FontName is the font name.
It is possible to retrieve the current font in a window by a call to:
Font = win_GetFont(Win)
Note that the FONT produced by Windows is the best approximation that the GDI can make to the requested characteristics. In some cases it might be of an unexpected size. This size however should not normally exceed the size requested, but that can happen too.
The VPI uses an RGB color model, which allows you to work with 16 million colors. You compose your desired color from values specifying how much Red (0 to 255) , how much Green (0 to 255) and how much Blue (0 to 255) you want in your color.
As opposed to coloring by painting, which is termed subtractive because each added color blocks the color below, coloring by the use of light is termed additive because the color of each pixel on a display is specified by adding the intensity values for each of the primary colors: red, green, and blue. For example, if the minimum intensity on a given display was specified as 0 and maximum intensity was specified as 255, a white pixel would be identified by the RGB triplet (255, 255, 255), a black pixel would be identified by the RGB triplet (0, 0, 0), and a cyan pixel would be identified by the RGB triplet (0, 255, 255).
Depending on your video adapter and printer you might actually only be able to display a smaller set of the possible colors. A monochrome printer will produce output in black and white and the video card might produce only 16 colors. However, the underlying native GUI software provides advanced color mappings and color compositions, so if you try to draw 2000 rectangles each with a different color, the underlying GUI can often arrange the colors and pixels in these rectangles to approximate the 2000 different colors. On monochrome devices shades of gray produced by printing of pixel patterns are used to represent colors.
Palette technology is also used to provide additional flexibility in available colors. Color palettes are used by devices that are capable of generating many colors but that can only display or draw a subset of these at any given time. A palette is an array of the colors used in a picture and permits each member color to be specified. When the screen card supports, for example, 256 colors simultaneously, it means, that it can display 256 colors correctly, the palette can specify which 256 colors can be displayed. The windowing system has one global system palette, and each window can have a local palette. The windowing system will try to display the colors in the window that has the focus as correctly as possible. This makes it possible, for example, to set 200 of the 256 possible colors of a particular video card to various shades of light blue, with the other 56 colors being available for other windows on the screen.
Fortunately, neither the programmer nor the user normally have to worry about color handling. The user knows that the better hardware bought, the better the screen looks. Unless displaying pictures or something else with many colors, the programmer can just choose any color for his GUI elements. However because there are the above mentioned limitations on the total number of colors that can be simultaneously displayed on the screen, and also to provide better portability to different machines, it is a good idea where possible to limit the usage of colors to the constant values defined in VPI.CON:
Table 1.15: Identifiers for 16 Standard Colors
Color: |
Value (B-G-R): |
color_Red |
0x0000FF |
color_Green |
0x00FF00 |
color_Blue |
0xFF0000 |
color_Cyan |
0xFFFF00 |
color_Magenta |
0xFF00FF |
color_Yellow |
0x00FFFF |
color_Black |
0x000000 |
color_DkGray |
0x404040 |
color_Gray |
0x808080 |
color_LtGray |
0xC0C0C0 |
color_White |
0xFFFFFF |
If you want to make your applications look good on monochrome equipment, you should use the color selections Black, DkGray, Gray, LtGray and White. There is an attribute attr_have_color, that can be used in vpi_GetAttrVal to test if the display hardware supports color. Otherwise make sure that the color contrast is large enough that two adjacent but different colors are not mapped to the same gray color.
The predicate:
COLOR = vpi_ComposeRGB(Red, Green, Blue),
can be used to create a color from the basic red, green and blue primary colors. The luminosity of each primary color is specified as an integer but only the low order 8 bits are significant, giving a range 0 to 255.
The common dialog:
COLOR = dlg_ChooseColor(COLOR OldColor)
can be used to let the user choose any of the possible colors.
Color handling for the various drawing predicates is explained for each predicate and illustrated in the VPI\EXAMPLES\DRAWING example project.
The VPI\EXAMPLES\COLOR example project shows how to draw a number of rectangles with different colors. It shows how to compose and decompose colors, and illustrates that the color value retrieved in win_GetPixel, may not be the same as the color value specified during drawing depending upon the number of available colors on the video adapter card.
When choosing colors for window backgrounds, the application should use the default settings selected in the GUI environment. For this purpose, there are some attributes that can be used to retrieve these color values, as example attr_color_menu return the color for the menus, and attr_color_window will return the color which should be used for drawing the window background.
In the default logical coordinate system used in the VPI, the X axis goes from the left to the right, and the Y axis from the top to the bottom. By default, the logical units will be screen picture elements (pixels), so that (0,0) would represent the top left corner pixel of a presentation space and lie just immediately inside the top left corner of the bounding rectangle. Since these coordinates are zero-based, any attempt to address the pixel located at coordinates (Width, Height) will be clipped, since it would lie outside the presentation space and in fact on the bounding rectangle itself. It is also possible to work with other logical units and coordinate mapping systems - see the section on Mapping, Scaling, Panning and Zooming (page
*).When creating, moving or re-sizing a child window, the coordinates are relative to the parent window (Parent Coordinates). In manipulations with task windows and dialogs the coordinates are always relative to the screen (Screen Coordinates).
When drawing or doing any similar operations in a window, the coordinates are relative to the window's client rectangle (Client Coordinates).
Sometimes, however, it is necessary to map coordinates in one window to those of another window, for this purpose there is the predicate win_MapPoints that can convert coordinates which are relative to one window into coordinates relative to another window:
NewPntList = win_MapPoints(FromWin, ToWin, OldPntLists).
For example, if ToWin is the parent window of FromWin window then win_MapPoints recalculates client coordinates of points to parent window coordinates, if ToWin is the Screen window then win_MapPoints converts client coordinates to screen coordinates (and vice versa).
When working with rectangles the values are always rct(Left, Top, Right, Bottom).
Important Note: The VPI does NOT specify rectangles as (X, Y, Width, Height).
Normally coordinates are expressed in pixels, which is the right choice when working with editors, menus, trees, toolbars etc. in windows on the screen. Screen resolution is usually misstated as numbers of pixels in the X and Y directions (e.g. 640 x 480 or 1024 x 768) whereas more properly it should be a measure of pixels per centimeter or inch. The "dot pitch" is the center to center distance of adjacent physical pixel areas, which of course is the inverse of the resolution. Higher resolution screens have greater numbers of pixels, enabling more information to be presented at the same time on the screen. Twice the resolution would permit presentation of 4 times as much information without loss of detail. However, each item displayed becomes a quarter the size, necessitating much closer viewing or a display with four times the area (twice the diagonal measurement of the active area).
However, dialog and control sizes normally are multiples of the font chosen for the dialog, which, by default, is the system font which might be changed by the user. That means that their coordinates are scaled in the Dialog Base Units. When the system font changes, the size of text controls will change and the whole dialog will be scaled accordingly.
VPI supports two kinds of Dialog Base Units: Windows Based and Platform Native.
There is a predicate:
vpi_GetBaseUnits(INTEGER Width, INTEGER Height)
which returns the pixel sizes of the horizontal (width) and vertical (height) platform native Dialog Base Units. They are calculated as the average width/4 and height/8 of the characters for the system font. These values should be used for positioning and layout of windows and dialogs that depend on the system font.
win_CreateDyn, win_CreateDynControl and win_CreateDynDialog support the possibility to use both kinds of Dialog Base Units to calculate the sizes and positions of windows, dialogs an controls. The kind of units must be specified in the object description (WINDEF domain) by the u_DlgBase, u_DlgPlatform or u_Pixels flag in the UType subcomponent of the WDEF sub-structure of the WINDEF domain.
Using of Platform Native Dialog Base Units in these predicates in case of multi-platform projects has the following imperfection. When dialog layouts are designed under one platform but the target application should be used under both Windows and OS/2 platforms then the width of dialogs and controls under Windows will be about 30% wider then under OS/2.
To avoid this problem the Windows Based Dialog Base Units can be used, in this case, width of dialogs and controls under both Windows and OS/2 platforms will be the same. However, in this case the width of the text strings under Windows will be about 30% wider then under OS/2. Therefore, in this case if you design layouts under OS/2, you must check that text strings satisfy to the controls' width under Windows.
The following convention to convert coordinates scaled in pixel to coordinates scaled in Platform Native Dialog Base Units can be used:
NatDBU_X = (Xpixel * 4) div NatDlgBaseWidth
NatDBU_Y = (Ypixel * 8) div NatDlgBaseHeight
The relations between Windows Based and Platform Native Dialog Base Units are the following:
WinDlgBaseWidth = 4/3*NatDlgBaseWidth
WinDlgBaseHeight = NatDlgBaseHeight
WinDlgBaseWidth = 4/3*NatDlgBaseWidth
WinDlgBaseHeight = NatDlgBaseHeight
Mapping, Scaling, Panning and Zooming
These words all cover the same basic operation - mapping. The mapping facility allows the application to change logical coordinate and scaling systems which are converted and scaled by the underlying GUI system to the actual physical window, printer or Fax coordinates, thus making the application device independent.
Once a mapping mode has been set up for a window, the application must give and will receive logical coordinates instead of the physical coordinates of the actual device. For example if the logical X coordinate ranges from 10000 to 20000 and the logical Y coordinate ranges from 10000 to -10000, then mapping can be set up to map these ranges into the actual physical pixel window coordinates, for example with physical X ranging from 0 to 349 and physical Y from 0 to 249.
win_SetMapMode (Window, MapMode)
A call to the predicate win_SetMapMode will change a window's mapping mode. The possible mapping modes are listed in the table below.
The default setting is always mm_Text, so that the coordinates correspond to the actual pixels on the screen or printer. The mm_Text is device dependent mode while the other modes are device independent. The Metric, English and Twips mapping modes allow the application to work with actual physical sizes, which is especially useful for printing. The mode mm_Arbitrary allows any range of logical coordinates to be mapped to the actual range of coordinates in a window.
Table 1.16: Mapping Modes
Mode |
Mapping |
X+Direction |
Y+Direction |
mm_Text |
Logical = Physical (default). |
Right |
Down |
mm_Arbitrary |
Set by win_SetMapScale() |
||
mm_HiMetric |
Logical = 0.01 millimeter. |
Right |
Up |
mm_LoMetric |
Logical = 0.1 millimeter. |
Right |
Up |
mm_HiEnglish |
Logical = 0.001 inch. |
Right |
Up |
mm_LoEnglish |
Logical = 0.01 inch. |
Right |
Up |
mm_Twips |
Logical = 1/20 of a point. (72 points = 1 inch) |
Right |
Up |
win_SetMapScale(Win, PNT DevOrg, PNT DevExt, PNT LogOrg, PNT LogExt)
win_GetMapScale(Win, PNT DevOrg, PNT DevExt, PNT LogOrg, PNT LogExt)
The predicate win_SetMapScale can be used to set up any scaling in logical coordinates.
win_SetMapScale(
Win, % target window
PNT DevOrg, % physical origin
PNT DevExt, % physical range extent
PNT LogOrg, % desired logical origin
PNT LogExt) % desired logical extent
When the mapping mode is mm_Arbitrary, the following coordinate transformations will be done for both the X and the Y coordinate:
DevCoord = DevOrg + (LogCoord - LogOrg) * DevExt / LogExt
where LogCoord is the coordinate in logical units and DevCoord is the resulting in coordinate physical units.
By changing only the DevOrg argument, it is possible implement panning (moving the origin of the coordinate system). By changing the DevExt and LogExt it is possible to scale and zoom a drawing.
Using the same scaling on each axis gives Isotropic scaling, and using different scaling gives Anisotropic scaling.
The following predicate can be used to retrieve the current mapping scale of that window:
win_GetMapScale(Win,
PNT DevOrg,
PNT DevExt,
PNT LogOrg,
PNT LogExt)
LogPntList = win_DPtoLP(WIN, PNTLIST DevicePoints)
DevPntList = win_LPtoDP(WIN, PNTLIST LogPoints)
This two predicates can be used to convert device coordinates (screen pixels, printer or fax dots) to logical coordinates and back. This is useful because the values in the Mouse and Update events etc. are always given in pixel coordinates.
Unfortunately, on 16-bit Windows the scale for logical coordinates is limited to -32000 < X < +32000 because the coordinates are handled as 16-bit integers.
In the VPI\EXAMPLES\MAPPING directory there is an example showing how to draw a map of Denmark which supports zooming in and out on this map. There is a fact database of polygons representing the coastline in a coordinate system from -10000 to +10000. Initially this coordinate system is mapped to the actual window device coordinates. When zooming is done, the act of the user dragging a rectangle sets up a mapping for zooming the selected area to fill the window.
When creating print routines for a window on the screen, there is often the problem that the window might consist of several Child windows which need to be printed separately by different routines. One easy solution is to use mapping to properly position each of the Child windows on the page. The mapping can also scale everything back up again to natural size because of the higher resolution of the printer. See the example VPI\EXAMPLES\PRINT for how to use mapping to scale faxed or printed output.
Printing is very similar to drawing in a window. During printing a special Printer Window handle is used in all the drawing functions. Printing is handled one page at a time and drawing may occur at any place on the page in any sequence. There is a standard VPI dialog dlg_PrintSetup (calling the Print dialog box) which enables operator selection of the range of pages to print (but that selection is not made available to the application), the printer resolution, number of copies and whether they should be collated, selection of printer to use (but not to change the selection of default printer), the orientation of printing on the page (portrait or landscape), paper size and source bin, dithering choice, print intensity slider and whether to print TrueType fonts as graphics. All these will then apply for the whole print job.
For MS Windows, the default printer may only be changed by native Windows applications or through the Windows Control Panel, but this will not change the printer in any VPI applications that are already running using the default printer. VP printing may be performed to a fax driver.
Today's windowing systems do an impressive job of letting applications be device independent. The windowing system knows everything about handling the specifics of individual printers, how to set bold or italic typefaces, how to do page shifts etc. For the application there is generally no programming difference between drawing on the screen, a laser printer, a plotter or a daisy-wheel or typewriter-style printer, or a facsimile device. The main difference between the screen and the printer is usually that there is much higher resolution on the printer, and that the printer can only print in black and white. If nothing special is done to scale up the print output, it may appear very small on the printer.
If an application is to print WYSIWYG output, it should scale the printing based on the printer and display resolutions. This information is returned by calling vpi_GetAttrVal (see page
*). Arguments provide the ability to obtain the number of Dots Per Inch on the screen and on the printer. Scaling by these values makes the drawing on paper appear similar to the drawings on the screen. The following framework can be used to scale printing done in pixel coordinates:VResPrinter = vpi_GetAttrVal(attr_printer_vres),
HResPrinter = vpi_GetAttrVal(attr_printer_hres),
VResScreen = vpi_GetAttrVal(attr_screen_vres),
HResScreen = vpi_GetAttrVal(attr_screen_hres),
PRINTWIN = print_StartJob("Test printing. . . "),
win_SetMapMode(PRINTWIN, mm_Arbitrary),
win_SetMapScale(PRINTWIN,
pnt(0, 0), pnt(VResPrinter, HResPrinter),
pnt(0, 0), pnt(VResScreen, HResScreen)),
print_StartPage(PRINTWIN),
:
draw_xxx(PRINTWIN), <------------ The actual drawing of a page
:
print_EndPage(PRINTWIN),
print_EndJob(PRINTWIN).
See VPI\EXAMPLES\PRINT for how to handle the difference between drawing in a window and to the printer, and VPI\EXAMPLES\PRINTTXT for an example of how to print text files.
The VPI contains six predicates to support printing:
WINDOW = print_StartJob(Title)
To start a new print use print_StartJob. This will return a Window handle that can be used as the target object of drawing routines and the other printing predicates.
print_EndJob(PrintWin)
When all printing has been completed a call to print_EndJob must be done.
print_SetOrientation(IsOrientationLandscape)
Before printing a page the current page orientation can be changed (under MS Windows underlying platforms) by a call to print_SetOrientation. The default orientation is Portrait. Specify IsOrientationLandscape = b_true to set Landscape orientation.
print_StartPage(WINDOW PrintWin)
When printing, the application must print one page at a time. Before starting to print a page the print_StartPage predicate must be called. For MS Windows, if this is called and the printer supports banding, the system will automatically perform the banding.
print_EndPage(WINDOW PrintWin)
After all output to the page has been completed the predicate print_EndPage is called.
print_AbortJob(PrintWin)
If the application for some reason wants to stop the printing, the predicate print_AbortJob can be called.
While printing a file the dialog "File printing" is displayed. The user can interrupt printing by pressing the "Cancel" button. This dialog contains two strings with the default texts: "Page" and "Cancel". These strings have the fixed resource identifiers: vpi_strcon_prn_page and vpi_strcon_prn_cancel respectively. Therefore the programmer can easily edit these strings using the Resource Strings Editor
Calling print_GetConfig predicate:
PrintConfig = print_GetConfig()
it is possible to retrieve information about some of the user's selections done in the Print dialog box. The returned printer configuration record PrintConfig contains information that the dlg_printSetup predicate uses to initialize the Print dialog box and PrintConfig is intended only for further use in a call:
print_SetConfig(PrintConfig)
to restore printing settings. You can save PrintConfig to a file and read it back, but remember that the contents of the printer configuration record depend on the underlying windows platform and do not platform compatible.
The Cursor is the pointing shape for the Mouse or track ball device. Each window on the screen has its own cursor, which can be changed by the application. The cursors that can be set to a window are either predefined, - or they can be specified in the resource file.
There are 11 predefined cursor types available, These predefined constants belong to the CURSOR domain and are defined in the table below.
Additional cursors can be defined in the resource file.
If you need user defined cursors they must be given a resource
id number (constant) of 12 or more. The VDE supplies a constant value automatically when you register a cursor by using the Cursor and New buttons of the project (.VPR) window. The Cursors in the project can either be created by the VP image editor, or any external tool that can store the cursor data in a separate .CUR file.Table 1.17: Cursor Types
Cursor Identifier |
Description of Cursor |
Cursor_Arrow |
Standard arrow cursor. |
Cursor_Cross |
Crosshair cursor. |
Cursor_Ibeam |
Text I-beam cursor. |
cursor_Icon |
Empty icon. |
cursor_Size |
A square with a smaller square inside its lower right hand corner. |
cursor_SizeNESW |
Double-pointed cursor pointing northeast/southwest. |
cursor_SizeNS |
Double-pointed cursor pointing north/south. |
cursor_SizeNWSE |
Double-pointed cursor pointing northwest/southeast. |
cursor_SizeWE |
Double-pointed cursor pointing east/west |
cursor_Uparrow |
Vertical arrow cursor |
cursor_Wait |
Hourglass cursor |
cursor_User |
First resource ID to use for your own designs |
The VPI has four predicates for handling cursors, they are described in the VPI help file.
cursor_Set(Window, CursorID)
This immediately replaces the cursor of the window referred to by Window with the one defined by CursorID
cursor_Set(Window, CursorID, ChangeNow)
On the contrary to the previous, the cursor_Set(_, _, b_false) changes cursor only after a mouse movement in the specified window or when the mouse comes into the specified Window. On the other hand, the cursor_Set(_, _, b_true) works exactly like cursor_Set/2.
cursor_Get(Window)
This retrieves the current cursor. This is useful if you want to restore the previous cursor after replacing it with the Hourglass cursor.
cursor_Hide()
This makes the cursor disappear until the cursor_Set predicate is called or the mouse is moved.
cursor_SetWait()
This sets the current cursor to Hourglass. Notice that any mouse movement restores the default cursor.
CursorPosition = cursor_GetPos(Window)
This retrieves the current cursor (mouse) position related to the given window Window.
See the example program in the VPI\EXAMPLES\CURSOR directory.
There are several different kinds of mouse devices. If a mouse device has only one button (as on a Macintosh) it normally acts as a left mouse button. Some PC mice have three buttons (left, center and right). One button mice also appear in the form of digitizers and touch sensitive screens. Most mouse devices have two buttons, and a center button is simulated when both buttons are pressed down simultaneously.
When a user clicks (or double-clicks) in a window, that window will usually gain the focus. When a window (or control) has the focus it will receive mouse events which occur within its border.
With the mouse capture set to a window, all mouse input is directed to that window, regardless of whether or not the cursor is over that window. Only one window at a time can capture the mouse. This can be used as in the VP text editor to automatically scroll the text when the user wants to drag a piece of text to another location.
win_CaptureMouse(Window)
This predicate redirects all mouse events to the window given by Window. All e_MouseMove events are still sent to the Window while capturing.
win_ReleaseMouse()
Releases the mouse after a call to win_CaptureMouse.
The VPI translates the native mouse events into the following four operating system independent messages.
e_MouseDown(PNT, Shift_Ctl_Alt_STATUS, Button)
e_MouseUp(PNT, Shift_Ctl_Alt_STATUS, Button)
e_MouseDbl(PNT, Shift_Ctl_Alt_STATUS, Button)
e_MouseMove(PNT, Shift_Ctl_Alt_STATUS, ButtonsList)
Each message has three parameters called PNT, Shift_Ctl_Alt_STATUS, and Button or ButtonsList
PNT is a structure of the form
pnt(x, y) where x and y are integers that give the coordinates of the mouse, relative to the window's top left hand corner (0, 0)Shift_Ctl_Alt_STATUS shows the status of the Shift, Control and Alt keys
.Button
and ButtonsList are an integer(s) that tells which button(s) has been pressed. The following constants are defined in VPI.CON:mouse_button_left
Left mouse button pressed / releasedmouse_button_right Right mouse button pressed / released
mouse_button_middle Center button pressed / released
Rectangles are used in many places in the VPI. Position and size of a window is specified by a rectangle, the windowing system updates the windows in rectangles, it is very common to draw rectangles etc. Because rectangles are used so often, there are some special predicates that implement some common rectangle handling functions:
Intersection of Two Rectangles
Rct = rect_Intersect(Rct1, Rct2)
The IntersectRect function calculates the intersection of two rectangles and places the coordinates of the intersection rectangle into the returned rectangle. If the rectangles do not intersect, an empty rectangle rct(0, 0, 0, 0) is returned.
Inflate the Size of a Rectangle
Rct = rect_Inflate(Rct, Integer Dh, Integer Dv)
The rect_Inflate function increases or decreases the width and height of a rectangle. The rect_Inflate function adds Dh div 2 units to the left and right ends of the rectangle and adds Dv div 2 units to the top and bottom. The Dh and Dv parameters are signed values; positive values increase the width and height, and negative values decrease them.
Determine Whether the Area of a Rectangle is Zero
rect_IsEmpty(Rct)
This predicate test whether a rectangle is empty, which means that the area of the rectangle is zero. The predicate fails if the area of the rectangle is not zero.
RCT = rect_Offset(Rct, Integer Dh, Integer Dv)
The rect_Offset function moves the given rectangle by the specified offsets. Dh and Dv are signed values so the rectangle can be moved in any direction.
Determine Whether a Point is Inside a Rectangle
rect_PntInside(Rct, PNT)
The rect_PntInside predicate determines whether the specified point lies within a given rectangle. A point is within a rectangle if it lies on the left or top side or is within all four sides. A point on the right or bottom side is considered outside the rectangle. The predicate will fail if the point is not inside the rectangle.
Create the Union of Two Rectangles
RCT = rect_Union(RCT Rct1, RCT Rct2)
The rect_Union function creates the union of two rectangles. The union is the smallest rectangle that contains both rectangles.
vpi_ProcessEvents(SendEventsToThisAppBoolean)
This predicate will process the events that are currently in the event queue before it returns. It yields control to other applications before processing of every event from the application's event queue. This is useful in a number of situations.
MS Windows 3.x does not have preemptive scheduling. This means that, when an event-handler predicate is called, no other applications or windows will receive any events before the predicate returns. A similar problem exists in the presentation manager in OS/2 3.x because PM itself runs in a single thread so no other applications will receive events before there is a return from the previous event (timer events are an exception).
This means that it is not desirable to have very long uninterrupted operations like compilation, database report generation, drawing of large pictures, heavy calculations etc. without some mechanism for keeping events flowing to other event-handlers. This is why you must call vpi_ProcessEvents regularly inside such long operations.
As an example the Prolog compiler in the VDE regularly calls vpi_ProcessEvents to support compiling in the background, and so it is possible to keep working in the environment and be running other applications while the compiler is running.
The Boolean parameter SendEventsToThisAppBoolean specifies whether vpi_ProcessEvents should cause a processing of events from the application's event queue (b_True) - or it only yields control to other applications in the Windowing environment (b_False). In the second case, it causes processing of only e_Timer and e_Update events are currently in the application's event queue; all other events, except for e_Timer and e_Update, will be just deleted from the application's event queue.
The vpi_ProcessEvents/0 version of the predicate works exactly like vpi_ProcessEvents(b_True).
vpi_ProcessEvents(SendEventsToThisAppBoolean, WaitNewEventsBoolean)
The distinction of the vpi_ProcessEvents/2 from the vpi_ProcessEvents/1 has place only when the WaitNewEvents = b_true. In this case the vpi_ProcessEvents/2 suspends the application if there is no events in the application's events queue and waits until a new event will come, then it causes a processing of this event.
The vpi_ProcessEvents/2 is usually used in application's loops in which the application waits for events from other applications.
Note, you should make sure that you can terminate a long process, if the user uses File|Exit to terminate the application. Instead of calling vpi_ProcessEvents you should call a predicate, that checks a flag to see if it should terminate before calling vpi_ProcessEvents.
While events normally originate in the windowing system, your application code can send an event to one of its own event-handlers using the predicate win_SendEvent, the effect being to call the event-handler for that window. Of course it is possible to call the event-handler directly if it is accessible at that point, but in many situations only the window handle is known so it is better practice to use a call based on the window handle. The LONG which is returned by win_SendEvent is the return value (the final argument) of the event-handler clause for that event.
LONG = win_SendEvent(Win, EVENT)
One use of this would be to relay menu events to the parent window by the call:
win_SendEvent(ParentWin, e_Menu(ID, CtrlAltShift)),
The call to win_SendEvent will not return until the event-handler target clause completes. If the target clause fails, win_SendEvent will return zero.
Sometimes it is desirable to put an event into the event queue, so it will be processed after the current event and any other pending events have terminated. win_PostEvent will do this:
win_PostEvent(WINDOW, EVENT)
This predicate returns immediately after placing the event in the queue.
The term Resources applies to many things in the computing world. In GUI system environments the term resources refers to all the windows, dialogs, pictures, string tables, icons, cursors, device contexts, pens, brushes, memory allocations, etc. for all active applications.
When we refer to resources in Visual Prolog, we normally mean the descriptions of dialogs, menus, tool and help bars, cursors, bitmaps, icons, and string tables which are created by the various resource editors in the VDE. The layout and the attributes of these resources are stored in the project database (<project>.VPR ), and when you request a build, the VDE will generate an industry-standard binary resource file (<project>.RES) and a file of Prolog-format constant definitions for inclusion in source modules. The VP code-generators for windows (as opposed to dialogs), and for toolbars and help bars generate Prolog code directly in the application source since such things were not anticipated by the standard .RC or .RES file formats.
Alternatively, or in addition, you may request under Options|Project|Code Generator the generation of <project>.RC and .H files , which provide industry-standard text descriptions of these same resources. These .RC and .H files may be converted into a .RES file by any of the commercially available tools (an RC compiler or any of the resource preparation tools).
When a resource is created by one of the VDE resource editors, it needs to be symbolically named. The VDE supplies a possible symbol based on a set of rules, and the user can accept, change, or totally replace it. These symbols, which will later become Prolog constants, will be used by the application to refer to resource items, and at compile time each resolves to a unique integer value.
During project building, both a .RES file, destined for the linker, and a .CON file, destined for inclusion in those project source modules concerned with handling resources, are generated (but there are other options as well). The .CON file is the record of the VDE's assignment of a unique integer value to each of the resource item symbols. These numeric values, and not the symbolic names written in the application, become at run time the actual identification numbers of the resources to the VPI predicates and to the underlying GUI.
The following things go into the resource file: Dialogs, Menus, Cursors, Bitmaps, Icons and Strings. They are all used in different ways.
The .RES file is linked into the executable file (<project>.EXE), in which it forms the resource segment(s). When using OPTLINK or some of the newer Borland or Microsoft linkers, this operation can be performed by the linker, otherwise this operation can be done by using the resource compiler to bind the resource segment to the .EXE file after the normal linking. During execution, VPI predicates retrieve dialogs, menus, icons, bitmaps, and string table strings from the resource segment of the .EXE.
These can be created either in the VP image editor, or using any third-party tools specifically designed to create, capture or modify graphic images. Unlike dialogs, menus and strings, the definitions of icons, bitmaps and cursors will exist in separate files before they are transferred to the .RES file.
Dialogs are normally created in the Dialog Editor in the VDE. It removes all the tedious work of positioning and sizing the controls in a dialog. When a definition exists in the resource file, a dialog can be created directly by a call to win_CreateResDialog - or the definition can be retrieved into a VPI structure by a call to the predicate:
The following predicate call can be used to check if a dialog definition exists in the resource file:
Result = vpi_CheckExistDialog(ResID)
Menus are normally designed in the VP menu editor. When windows are created by a call to win_Create, the menu for a window is given as a parameter, and by giving the alternative:
res_menu(ResId),
the window will automatically retrieve the menu from the resource data associated with ResId.
Strings are put into string table resources for several reasons. Having them in resources makes it easy to make changes like translating to another language without having to modify the source code. The application can retrieve the string from a resource identifier by a call:
Str = vpi_GetResStr(ResID)
The following predicate call can be used to check if a string with the specified resource identifier exists in the resource file:
Result = vpi_CheckExistString(ResID)
The easiest way to give your users a way to select the next action is to organize the possible actions as menu items. The Menu Editor in the VDE makes this task very easy. For each menu item a resource identifier is given to identify the menu entry to the application. Your application can be made more readable by choosing the name of the related action for each resource identifier.
Menus are initially connected to windows during the creation of the window. The menu parameter in the win_Create call can specify that the menu is located in the resource file, or is to be built dynamically from a Prolog structure.
When using MDI mode, all menus will appear in the Task window. The menu will automatically change as the active window changes.
For every window to carry its own menu, the MDI flag must not be set in the Application Expert, and the windows must be created with the Screen window as the parent window.
The VPI contains the facility to have owner draw menus. This will not be portable to all platforms. Note also that the menu editor in the VDE only supports menus with all items being text.
MENU = res_menu(RESID); dyn_menu(MENU_ITEM_LIST); no_menu
MENU_ITEM_LIST = MENU_ITEM*
MENU_ITEM =
txt(MENU_TAG, % Integer value for this entry
STRING, % The menu text
INTEGER Mnemonic, % Letter for accelerator
BOOLEAN Enabled,
BOOLEAN Checked,
INTEGER ItemState, % Constants: mis_None, mis_Checked, mis_Help.
% Last puts the 'Window' item just before this
% item. Usually it is used to put the 'Help'
% item to the far right
MENU_ITEM_LIST);
ownerdraw(
MENU_TAG,
LONG Val, % 32-bit val passed in e_drawowner
BOOLEAN Enabled,
BOOLEAN Checked,
MENU_ITEM_LIST);
separator; % Draws a horizontal line in the menu
menu_break % Column break in menu
MENU_TAG = unsigned
The ItemState identifies the state of a menu item:
mis_None |
specifies the ordinary state |
mis_Checked |
sets the check mark to the menu item |
mis_Help |
specifies that this menu item must be positioned immediately after the 'Window' menu item; usually, it is used to put the 'Help' item to the far right (only under MDI mode). |
Use the following sum:
ItemState = mis_Checked + mis_Help,
to specify that the menu item should be positioned immediately after the 'Window' item and it should have the check mark.
Specifying Actions for Menu Items
To specify an action for a menu item you should use the Code Expert to generate a clause for that menu event in the appropriate window's event-handler. Any code placed in the event-handler clause will be invoked when the menu item is chosen (while the menu item is grayed (disabled) your code can never be called - unless there is a tool bar push button which is not disabled and has the same resource identifier).
The following code reacts to the event that is generated when the File|Open item is selected in the Task window menu:
task_win_event_handler(Win, e_Menu(id_file_open, ShiftCtlAlt), 0):-
dlg_note("Menu", "You chose Open"), !.
Loading a Menu from the Resource File
The menu_GetRes predicate is used to retrieve a menu from a resource file. The menu is loaded into a VPI data structure from the MENU domain.
NewMenu = menu_GetRes(irm_my_menu),
menu_Set(Win, NewMenu),
Changing the Text for a Menu Item
The text of an individual menu item can be changed by using the menu_SetText/3 predicate. The following line inserted into the Task window create event (e_Create) will change the standard File Open text to something different:
menu_SetText(Win, id_file_open, "Open DataBase")
Menu items can have an associated check mark, which indicates whether some facility is presently available or not. The initial state is given in the definition of the menu, and it is possible to change this state for a menu item in a window by call to the menu_Check/3 predicate:
menu_Check(Win, menu_item_resource_identifier, BOOLEAN)
By default new menu items are enabled in the menu editor (except for the ones automatically generated by the Application Expert without code), but they can be disabled (grayed) or enabled at design by double-clicking the item name (while in the menu editor) or changing the state of the disabled box or at run time by using the menu_Enable predicate with the last parameter set to b_False.
% Disable the File|New menu item
menu_Enable(Window, id_file_new, b_False),
% Enable the File|New menu item
menu_Enable(Window, id_file_new, b_True).
If you dynamically change the text of menu items in events other than e_Create, the effect of your change may not be visible to the user on all platforms. The menu_Update/1 predicate invalidates the screen area used by the menu, causing it to be re-drawn.
menu_SetText(Win, id_file_open, "Open DB"),
% Force Screen Menu Update
menu_Update(Win)
Handling Sub-Menus under MS Windows
Under MS-Windows, sub-menus can not be referred to by a resource identifier. If the menu entry for a sub-menu, needs to enabled, disabled, checked or have the text changed. it is necessary to use a special versions of the menu_Enable, menu_Check and menu_SetText predicates, which specify the text of the menu entry instead of the constant.
menu_Enable(Win, String, BOOLEAN)
menu_Check(Win, String, BOOLEAN)
menu_SetText(Win, String, NewString)
The menu_PopUp/4 predicate is used to create a pop-up menu and would usually be called in response to a mouse click. The following clause:
my_event_handler(Win, e_MouseDbl(PNT, _, _), 0):- !,
menu_PopUp(Win, res_menu(idr_my_pop_up), PNT, align_Left).
will pop-up the menu whose resource identifier is idr_my_popup at position PNT in window Win when the user double clicks inside it.
The last parameter, which can be either align_Left, align_Right or align_Center specifies how the menu will pop-up relative to the specified PNT.
When the user clicks in a pop-up menu for a given window, the VPI sends the usual e_Menu event to that window's event-handler.
A timer is an internal routine that repeatedly every time a given interval has elapsed sends an e_Timer event to a window event-handler associated with the timer. Because the accuracy of a timer depends on the system clock rate and how often the application retrieves messages from the message queue, the time-out value is only approximate.
A new timer starts timing as soon as it is created. An application can destroy a timer by using the timer_Kill function. To use system resources efficiently, applications should destroy timers that are no longer necessary. Each timer has a unique timer identifier which is returned when the timer is created.
A new timer is created with a call to:
TimerId = timer_Set(Window, UNSIGNED Interval)
The interval is specified in milliseconds. The returned timer identifier can be used to stop the timer.
A timer is destroyed by a call to:
timer_Kill(LONG TimerIdentifier)
Where the TimerIdentifier is the value returned by timer_Set.
The VPI contains a group of predicates to handle pictures. Pictures are stored as Windows .BMP files and are not limited to 64 Kb in size. Operations on pictures are done using a handle to the picture, which means once it is finished with, this handle has to be explicitly destroyed to release its allocated resources.
There are four ways to obtain a picture:
Converting Pictures to Binary Data
For storing pictures in databases, sending them over a network etc. it is handy to convert a picture to a standard Prolog binary term. There are two predicates that can do this conversion:
Binary = pict_ToBin(PICTURE)
Picture = pict_FromBin(BINARY)
Note, that on 16-bit platforms, there is a 64 Kb - limit on the size of Binary structures, calling pict_ToBin on a picture larger than this will result in a run time error.
Creating a Picture from Drawing Operations
It is also possible to create a picture in memory by collecting a number of drawing operations. This requires three steps:
PictWIn = pict_Open(Win, Rct)
:
% drawing operations into the PictWin
:
Picture = pict_Close(PictWin)
When a picture is no longer needed, the allocated memory should be released by a call to pict_Destroy:
pict_Destroy(Picture)
There are two ways to draw a picture, with or without stretching.
A full picture can be drawn at a given location in a window. The top left corner of where the picture is to be drawn relative to the window's client area must be specified in PNT.
pict_Draw(Win, Picture, PNT, ROP)
In the stretching version of pict_Draw, the picture will be zoomed to fill the chosen area (DestRct). It is also possible to specify that only part of the picture should be drawn (PictRct), with PictRct referring to the unstretched picture:
pict_Draw(Win, Picture, DestRct, PictRct, ROP)
The ROP argument in pict_Draw specifies an advanced feature indicating how to combine:
The combinations are done bit-wise by logical operations. This feature allows you to make a bitmap which may be partly transparent, to only update parts of the window, to undraw what was previously drawn, etc.
Table 1.18: Raster Operations
Operation |
Result |
rop_Blackness |
Black |
rop_Whiteness |
White |
rop_DstInvert |
Not Dst |
rop_MergeCopy |
Pat And Src |
rop_MergePaint |
(Not Src) Or Dst |
rop_NotSrcCopy |
Not Src |
rop_NotSrcErase |
Not(Src Or Dst) |
rop_PatCopy |
Pat |
rop_PatInvert |
Dst Xor Pat |
rop_PatPaint |
(Not Src) Or Pat Or Dst |
rop_SrcAnd |
Src And Dst |
rop_SrcCopy |
Src |
rop_SrcErase |
(Not Dst) And Src |
rop_SrcInvert |
Src Xor Dst |
rop_SrcPaint |
Src Or Dst |
Get a Picture from the Contents of a Window
It is possible to retrieve the contents of all or part of the client rectangle of a window by a call to:
Picture = pict_GetFromWin(Window, Rct)
where Rct specifies the rectangle within the Window to be captured.
Pictures are normally stored with the .BMP (bitmap) extension. A picture can be loaded from a file that stores such a windows bitmap and may be larger than 64 Kb.
Picture = pict_Load(FileName)
Load a Picture from the Resource Segment
It is possible to load a bitmap from the resource segment of the application .EXE-file directly into a picture by a call to:
Picture = pict_GetFromRes(resource_identifier)
Returning Size Information from a Picture
The pict_GetSize predicate can be used before drawing to return Height and Width of a picture - or it can be used to retrieve the size of a picture before converting it to a binary or storing it in a file:
pict_GetSize(Picture, INTEGER Width, INTEGER Height, LONG Size)
It is possible to save the contents of a picture in a file by a call to:
pict_Save(Picture, FileName)
It is possible to rotate a picture by a call to:
NewPicture=pict_Rotate(Picture, Angle)
The clipboard is the common place where running applications can exchange data through copy and paste operations. Remember that the clipboard is user-controlled; the application should not place anything on the clipboard or clear it without the user's knowledge.
The clipboard data can be in a number of different formats. The VPI supports three portable formats, which it handles in a way compatible with other applications. The three open formats are:
You can also put proprietary data formats on the clipboard which can be exchanged between Prolog applications. These proprietary data are put on the clipboard as a binary combined with a name for the format. Since arbitrary Prolog terms can be converted to binaries, any domain can in principle be stored on the clipboard (subject to the platform size limits of binary data).
There are three things you can do with the clipboard:
Note: Take a look at the example in the VPI\EXAMPLES\CLIPBRD directory, and see how the VPI formats are compatible with your other programs.
Getting Data from the Clipboard
To retrieve the data from the clipboard, the following predicates can be used. If the selected type of data is not present, the call will fail.
Binary = cb_GetBin(STRING)
Picture = cb_GetPicture()
MetaFileHandle = cb_GetMetafile()
StringLength = cb_GetSize()
Note
: the string returned from cb_GetString will return a string according to the Windows conventions about a carriage return–linefeed (CR-LF) combination. The predicate str_DOSStr can be used to convert these CR-LF combinations to a regular Prolog string with just \n characters.The following three predicates are used to put something on the clipboard:
cb_PutString(String)
cb_PutPicture(Picture)
cb_PutMetafile(MetaFileHandle)
cb_PutBin(FormatName, Binary)
Any previous clipboard content will be lost.
Testing for Data on the Clipboard
There is a predicate to test for each type of data on the clipboard. To test if a binary is available, a name for the format must be supplied.
cb_StringAvailable()
cb_PictureAvailable()
cb_MetafileAvailable()
cb_BinAvailable(FormatName)
Applications often want to enable or disable the Paste menu item in the Edit menu depending upon whether or not there is something suitable on the clipboard. There is a special event, e_InitMenu, that is sent when the menu is activated. This means, that it is only necessary to change the menu, when this event is sent.
ehandler(Win, e_InitMenu(), 0):-
cb_StringAvailable(),
menu_Enable(Win, menu_edit_paste, b_True), !.
ehandler(Win, e_InitMenu(), 0):-
menu_Enable(Win, menu_edit_paste, b_False).
A caret marks the text insertion point of an edit control or a window. It must not be confused with the cursor, which marks the current screen position of the mouse. Often an application will place the caret by checking for text in the area of a mouse click. When the mouse is moved by the user the caret stays put. The caret is often referred to as the text insertion point. Each window has its own caret with its own position.
There are three caret handling predicates:
caret_Set(Window, X, Y)
Moves the caret of Window to the coordinate given by X and Y (relative to the top left corner of the window).
caret_Set(Window, X, Y, Width, Height)
Shows the carret in the specified Window (if it was hidden), sets caret dimensions equal to Width and Height, and moves it to the coordinate given by X and Y.
caret_Size(Window, Width, Height)
Defines the caret to be used in the window identified by Window, the Width and Height are given in Logical units.
caret_Off(Window)
Makes the caret of Window invisible.
The attribute flag identifiers name a number of flag values that can be set or read from the VPI. Some attributes are global for the VPI, others are local to a particular window. A list of attribute identifiers and their descriptions will be found in the following table. While all may be retrieved, the values of those marked 'read only' may not be set from the VPI. Of those which can be set, some must be set prior to calling vpi_Init, and setting them at any other time will have no effect.
Table 1.19: Attribute Identifiers
Attribute Name |
Description |
Environment: |
|
attr_have_color |
Read-only flag that returns the Boolean value that determines whether the screen can show colors |
attr_have_mouse |
Read-only flag that returns the Boolean value which determines whether there is a mouse connected to the computer |
attr_num_timers |
Read-only flag that returns the maximal number of timers |
attr_CheckPortability |
In case this flag is set to b_false (0), the VPI-does not check whether a predicate is supported under the chosen platform. (The correspondent error 6008). Default is b_true (1). |
Object Size Attributes: |
|
attr_ctl_button_height |
Read-only flag that returns the minimal height of push button controls |
attr_ctl_checkbox_height |
Read-only flag that returns the minimal height of check box controls |
attr_ctl_edit_text_height |
Read-only flag that returns the minimal height of edit controls |
attr_ctl_horz_sbar_height |
Read-only flag that returns the height of arrow bitmaps on horizontal scroll bars |
attr_ctl_vert_sbar_width |
Read-only flag that returns the width of arrow bitmaps on vertical scroll bars |
attr_ctl_radiobutton_height |
Read-only flag that returns the minimal height of radio button controls |
attr_ctl_static_text_height |
Read-only flag that returns the minimal height of static text controls |
attr_icon_width |
Read-only flag that returns the width of icons |
attr_icon_height |
Read-only flag that returns the height of icons |
Predefined Windows: |
|
attr_screen_window |
Read-only flag that returns the VPI handle of the Screen window |
attr_task_window |
Read-only flag that returns the VPI handle of the Task window |
System Metric Attributes: |
|
attr_screen_height |
Read-only flag that returns the height of the display in raster lines |
attr_screen_width |
Read-only flag that returns the width of the display in pixels |
attr_screen_hres |
Read-only flag that returns the number of pixels per logical inch along the display width |
attr_screen_vres |
Read-only flag that returns the number of pixels per logical inch along the display height |
attr_printer_height |
Read-only flag that returns the height of the being printed page in raster lines |
attr_printer_width |
Read-only flag that returns the width of the being printed page in pixels |
attr_printer_hres |
Read-only flag that returns the number of pixels of the being printed page per logical inch along the page width |
attr_printer_vres |
Read-only flag that returns the number of pixels of the being printed page per logical inch along the page height |
Window Metric Attributes: |
|
attr_docframe_width |
Read-only flag that returns the width of the window frame that can be sized |
attr_docframe_height |
Read-only flag that returns the height of the window frame that can be sized |
attr_frame_width |
Read-only flag that returns the width of the window frame that can not be sized |
attr_frame_height |
Read-only flag that returns the height of the window frame that can not be sized |
attr_dblframe_width |
Read-only flag that returns the frame width when window has the wsf_DlgBorder style |
attr_dblframe_height |
Read-only flag that returns the frame height when a window has the wsf_DlgBorder style |
attr_menu_height |
Read-only flag that returns the height of single line menu bars. This is the menu height minus the height of the window frame that can not be sized |
attr_title_height |
Read-only flag that returns the height of a window title. This is the title height plus the height of the window frame that can not be sized |
|
|
System-defined Window Colors: |
|
attr_color_activeborder |
Read-only flag that returns an active window border color |
attr_color_activecaption |
Read-only flag that returns an active window title color |
attr_color_appworkspace |
Read-only flag that returns a background color of multiple document interface (MDI) applications |
attr_color_desktop |
Read-only flag that returns a desktop color |
attr_color_btnface |
Read-only flag that returns a face shading color on push buttons |
attr_color_btnshadow |
Read-only flag that returns an edge shading color on push buttons |
attr_color_btntext |
Read-only flag that returns a text color on push buttons |
attr_color_captiontext |
Read-only flag that returns the text color in title bars, size buttons, scroll-bar arrow buttons |
attr_color_graytext |
Read-only flag that returns a grayed (dimmed) text color. This color is zero if the current display driver does not support a solid gray color |
attr_color_highlight |
Read-only flag that returns the background color of the selected item in a control |
attr_color_highlighttext |
Read-only flag that returns a text color of the selected item in a control |
attr_color_inactiveborder |
Read-only flag that returns a color of an inactive window border |
attr_color_inactivecaption |
Read-only flag that returns the title color of inactive windows |
attr_color_inactivecaptiontext |
Read-only flag that returns the title text color of inactive windows |
attr_color_menu |
Read-only flag that returns the background color of menus |
attr_color_menutext |
Read-only flag that returns the text color in menus |
attr_color_scrollbar |
Read-only flag that returns the gray area color of scroll-bars |
attr_color_windowtext |
Read-only flag that returns the system text color in the client area of windows |
attr_color_window |
Read-only flag that returns the system background color in the client area of windows |
attr_color_windowframe |
Read-only flag that returns the frame color of windows |
attr_color_windowstatictext |
Read-only flag that returns the text color in static text controls |
attr_color_tooltiptext |
Read-only flag that returns the text color in tooltips under Windows 95. Under other platforms it returns 0 |
attr_color_tooltipback |
Read-only flag that returns the background color of tooltips under Windows 95. Under other platforms it returns 0 |
WindowAttributes: |
|
attr_native_window |
Read-only flag that returns the native handle of a window |
attr_win_instance |
Read-only flag that returns the instance handle of the application |
attr_win_mdi |
Under Windows attr_win_mdi is used to set the MDI mode. Under OS/2 MDI mode is not supported. Under OS/2 attr_win_mdi can be used to specify that menus must have Window menu item displaying a list of opened windows. Notice that in this case Cascade, Tile, and Arrange Icons functions of the (of the standard MDI mode Window item) are not implemented. |
attr_win_mdi_client_hwnd |
Read-only flag. Under Windows it returns the native handle of the MDI task window. Under OS/2 it always returns 0. |
attr_win_tbar |
Flag that is used to set the height of the tool bar area on the top of a window |
attr_win_lbar |
Flag that is used to set/get the width of the tool bar area on the left side of a window |
attr_win_rbar |
Flag that is used to set/get the width of the tool bar area on the right side of a window |
attr_win_sbar |
Flag that is used to set the height of the status line area in the bottom of a window |
attr_win_3dcontrols |
Flag that is used to turn on/off support for 3d-controls. Note: this flag must be set prior to calling vpi_Init. |
attr_suppress_update_check |
Flag that is used to suppress the modified() notification for edit controls. |
attr_erase_background |
If this flag is set to b_False, there will be no automatic erasing of the background for windows. |
The current value of any of these attributes may be retrieved at any time by:
LONG = vpi_GetAttrVal(LONG Attribute)
or for attributes connected to the windows
LONG = win_GetAttrVal(WINDOW, LONG Attribute)
Since the value is returned as a long, it is sometimes necessary to use the CAST operation to turn the value into the proper domain. E.g. to retrieve the TaskWindow handle, the following clause can be used:
TaskWin = cast(WINDOW, vpi_GetAttrVal(attr_task_window)),
The attributes, which are not read-only can be changed by a call to:
vpi_SetAttrVal(LONG Attr, LONG Value)
There are currently no attributes specific to a given window that can be changed.
Creating window classes is supported in the VPI. A window class can be used to implement a custom control which can be put into a dialog.
Creating a class can be done by a call to:
class_Create(STRING ClassName, EHANDLER)
And the class can be removed by a call to:
class_Destroy(STRING ClassName)
Make sure that a class is created before using a dialog with custom controls. When a class is created, there is a normal Prolog event-handler predicate associated with the class. When a dialog is created, the window for the custom control is automatically created, and the event-handler predicate will receive an e_Create event for the class.
When class_Create is called in a dynamic-link library (DLL), it creates a global window class. The operating system loads the specified DLLs in the context of the newly started process before calling the Goal in that process, a DLL registers global window classes during its initialization procedure.
As an example, the following piece of code can be used to implement a control that displays a picture in a dialog:
bmpctrl_register
bmpctrl_SetBMPFile(WINDOW, String File, BOOLEAN Stretch) - (i,i,i)
DOMAINS
CTRLBMP = file(STRING FileName); resid(RESID)
DATABASE - bmpctrl
ctrl_bmp(WINDOW, CTRLBMP, BOOLEAN Stretch)
PREDICATES
win_bmpctrl_eh : EHANDLER
CLAUSES
win_bmpctrl_eh(_Win, e_Update(_UpdateRct),0):-
ctrl_bmp(_Win, file(File),b_True),!,
Picture = pict_Load(File),
pict_GetSize(Picture, X, Y, _Size),
WinRCT = win_GetClientRect(_Win),
pict_Draw(_Win, Picture, WinRCT, rct(0,0,X,Y),rop_SrcCopy),
pict_Destroy(Picture),
!.
CLAUSES
bmpctrl_register :-
class_Create("BMPCTRL", win_bmpctrl_eh).
CLAUSES
bmpctrl_SetBMPFile(Win, File, Stretch) :-
retractall(ctrl_bmp(Win,_,_)),
assert(ctrl_bmp(Win, file(File),Stretch)).
Note also, that the predefined tools like the Tree tool, and the editor can also be put into a custom control. For this purpose there are some predicates: edit_CreateSubClass and tree_CreateSubClass that must be called in the e_Create event for the subclass. Please study the VPI\EXAMPLES\CLASS for an example on how to create tree and editor subclasses.
If you create any tools, that can be put into a custom control, you should use the same mechanism as is used in the Tree tool. The trick is that the Tree tool changes the event-handler for the window, remembers the event-handler for the subclass, and passes the events back to the subclass:
CLAUSES
tree_CreateSubClass(Win,.................):-
OldHandler = win_GetHandler(Win),
assert(tree_class_callback(Win, OldHandler)),
win_SetHandler(Win,tree_class_handler).
tree_ehandler(Win, e_Create(....)).
tree_class_handler(_Win, Event, Ret):-
tree_class_callback(_Win, USER_CALLBACK),
Ret = USER_CALLBACK(Win, Event),!,
tree_after_handling(Win, Event).
tree_class_handler(_Win, Event, Ret):-
Ret = tree_ehandler(Win, Event).
tree_after_handling(Win, e_Destroy()):-
tree_ehandler(Win, e_Destroy()).
tree_after_handling(Win, e_Size()):-
tree_ehandler(Win, e_Size()).
tree_after_handling(_,_).
Normally, all controls from the same control class share the same window event-handler. A subclass is a control or set of controls that belong to the same predefined control class (standard types of controls), and whose events are intercepted and processed by another window event-handler before being passed to the default event-handler of the predefined control class.
To create a subclass, call the win_SetSubClassHandler predicate to associate the new subclass event-handler with a particular control. For this control VPI first will pass each event to the specified subclass event-handler and then, if an event is not caught by the subclass event-handler or the event-handler fails for the event, will pass the event to the default event-handler of the primary predefined control class. In VPI it is possible to create subclass only to the predefined control classes (not to custom controls and not to ordinary windows or dialogs)
The VPI supports the following predicates to activate the on-line help. See the Guided Tour example for how to get support from the VDE to do on-line help.
vpi_ShowHelp(STRING FileName)
vpi_ShowHelpContext(STRING FileName, LONG ContextId)
vpi_ShowHelpKeyWord(STRING FileName, STRING KeyWord)
vpi_HelpClose()
Dynamic Data Exchange (DDE) is used to communicate with other applications. The communication can either be a single data transfer or it can establish a communication link between two tasks. For example, it is possible to send a command to a word processor for it to load a text file, to read the contents of a spreadsheet cell, or communicate with a database server.
The DDE support provided in Visual Prolog is based on the DDEML (Dynamic data Exchange Management Library) library on the MS-Windows platform. It is currently not portable to the other platforms.
DDE communication supported by Visual Prolog is fully compatible with other MS Windows applications. While DDE communication can be done between two Prolog applications, you can also use it to communicate with applications like MS Excel, MS Word, the Program Manager etc. There is a further advantage when using Windows NT, Windows 95. or Windows 3.11 (Windows for work groups). The data exchange can run over a network through NETDDE to implement sophisticated network-based client server computing.
Usually an application is either a client or a server. Some applications (e.g. MS Word) can take both client and server roles.
Communication is always initiated and controlled by the client.
A client program displays the information obtained from a server in an application dependent way. To obtain information it contacts a server and sends commands or makes a request for data.
The client can set up an advisory loop, where the server can send data asynchronously to the client.
A client can request data from multiple servers simultaneously.
Clients often need to send messages to other clients, this is usually done by using a (common) server as a message router.
A server program responds to requests from clients by obtaining information and formatting this information to make it usable by the client.
A server will register the service name(s) that it supports.
A server can have many clients simultaneously.
The DDE client initiates the communication by establishing a conversation with a DDE server, which responds by sending a conversation handle to the client. Both parties can terminate the conversation at any time.
All DDE messages are sent as e_DDE events that the application receives in its Task window event-handler.
For example, when a client application attempts to establish a conversation with a server application, the client calls the dde_Connect function. This will result in the arrival of a e_DDE(connect) event in the server's Task window event-handler. The server can allow the connection by returning b_True, or it can deny the connection by returning b_False from the event-handler.
When a client application begins a DDE conversation, it must specify two things:
A Service name, which usually is the name of the application to talk to: Microsoft Access, Microsoft Excel, Microsoft Word for Windows, Program Manager, etc.
A Topic name, which is the subject of the conversation. Many programs like Word for Windows and Excel uses the name of the file as the topic "REPORT.DOC" or "BUDGET.XLS". A special topic that many applications supports is the "system" topic. The System topic can usually be used to query which other topics are available.
Once the conversation is established, it is not possible to change the Service and the Topic. But the conversation might refer to several different pieces of information by referring to different items. In Excel a Cell is the Item (Such as R1C2), in Word a Bookmark can be the Item, in Access, a record in the database is the Item.
Initializing and Closing Down the DDE
An application must initialize the DDE before use. This is best done in the e_Create event of the applications Task window:
task_win_eh(_Win, e_Create(_),0):-
dde_Init([ddef_clientonly]),
....
The dde_Init predicate takes a list of flags. Clients should always just specify ddef_clientonly. For servers, there are many other possibilities.
When the application terminates it must call the dde_End predicate to close down the DDE system:
task_win_eh(_Win, e_Destroy, 0):-
dde_End().
Most applications will just use simple DDE clients to communicate with other standard applications like the Program Manager, Word for Windows and Excel, so most people can just read the following information about the DDE Client operations, which is quite simple to use. If you need to implement a full client/server architecture you need to read the whole DDE chapter.
Connecting to a Server Application
After initializing its DDE the client can attempt to connect to a server by using the dde_Connect predicate:
DDE_CONV dde_Connect(STRING Service, STRING Topic)
It takes a Service and a Topic name, and if the connection is successful, it returns a conversation handle, if it is not successful it exits with a runtime error.
The communication channel can be closed down with a call to dde_DisConnect:
dde_DisConnect(DDE_CONV Conv)
A little example on how to connect to Word for Windows and disconnect again are:
task_win_eh(_Win, e_Menu(id_Connect_Word,_),0):-!,
trap(Conv = dde_Connect("WinWord","System"),_,
write("WinWord not running\n")),
%.... Perform some DDE operations
dde_DisConnect(Conv).
Executing a Command on the Server Application
Some servers may allow clients to issue execute command strings. The dde_Execute predicate is provided for this purpose.
DDE_TRANSID dde_Execute(DDE_CONV Conv, STRING Command, LONG Timeout)
The timeout value, and the transaction identifier will be explained later.
As example the following clause is used to connect to Word for Windows and send it a command to load the file DDETEST.DOC from the current directory of the application:
task_win_eh(_Win, e_Menu(id_word_open_ddetest, _ShiftCtlAlt),0):-!,
trap(Conv = dde_Connect("WinWord", "System"),_,
write("WinWord not running\n")),
filenamepath(FullName, "", "ddetest.doc"),
format(Command, "[FileOpen .Name=\"%\"]", FullName),
write("Command = ", Command), nl,
dde_Execute(Conv, Command, 0),
dde_DisConnect(Conv),
The available commands and their syntax vary from application to application.
Requesting Data from a Server Application
The predicate dde_request can be used to retrieve data from the server:
DDE_TRANSID dde_Request(DDE_CONV Conv, STRING Item,
LONG TimeOut, DDE_FORMAT, DDE_DATA Val)
Basically it takes a handle to an open conversation and the name of an Item, and returns the requested data. The predicate also takes a timeout value, that indicates how long to wait before timing out, and there is the possibility to specify what kind of data to request, for most DDE operations this should just be set to the value ddecf_text which gives a regular text string. The purpose of the returned transaction identifier will be explained under the more advances transaction handling.
The following clause will both establish a connection with Excel and retrieve the value a Row1, Column1:
task_win_eh(_Win, e_Menu(id_excel_peek, _ShiftCtlAlt),0):-!,
trap(Conv = dde_Connect("Excel","[ddetest.xls]Sheet1"),_,
write("ddetest.xls not loaded\n")),
dde_Request(Conv, "R1C1", 1000, ddecf_Text, Val),
write("Got: ", Val), nl,
dde_DisConnect(Conv).
Sending Data to a Server Application
The predicate dde_Poke is used to send data to a another application:
DDE_TRANSID dde_Poke(DDE_CONV Conv, STRING Item,
DDE_DATA Val, DDE_FORMAT, LONG TimeOut)
As example, the following clause will open a DDE connection and put a value in a spreadsheet cell:
task_win_eh(_Win, e_Menu(id_excel_poke, _ShiftCtlAlt),0):- !,
trap(Conv = dde_Connect("Excel","[ddetest.xls]Sheet1"),_,
write("ddetest.xls not loaded\n")),
dde_Poke(Conv, "R1C1", str(Val),ddecf_text, 0),
dde_DisConnect(Conv),
More Advanced Client Operations
When the goal is to establish a true client/server architecture things become a little bit more complicated.
The previous example on requesting data from the server by dde_Request was a synchronous request; the client application was waiting until the data arrived from the server. It is often not desirable to wait for operations to complete, the system must be fully responsive, and can not accept to block the whole application until the server responds.
If blocking is not accepted, asynchronous operation must be used instead. In this case a timeout value of zero is used and the dde_Request predicate will return immediately after the call with a transaction identifier instead of data. The data will then arrive sometime later as an event identified by the returned transaction identifier. The disadvantage of using asynchronous programming is that it is a bit more complicated.
When the data arrives to the client, it will arrive as a e_DDE event to the Task window event-handler:
task_win_eh(_Win, e_DDE(_Conv,
trans_complete(_Topic, _Item, _TRANSID, _DDE_DATA)),0):-!,
retract(trid(_Conv, _TRANSID)),!,
DDE_DATA = str(Data),
write("Received from a server (asynchronous): ",Data),nl.
Abandoning an Asynchronous Data Request
A Client can call the dde_AbandonTrans() predicate if it no longer needs the data resulting from a asynchronous request.
dde_AbandonTrans(DDE_CONV Conv, DDE_TRANSID Id)
This call should only be issued by a client. After issue any arriving asynchronous data (for the conversation that issued the dde_AbandonTrans()) will be ignored.
Transferring Data between the Server and a Client
The data, that are sent between the server and the client belong to the DDE_DATA domain. The DDE_DATA domain is defined as follows:
DDE_DATA = str(STRING); bin(BINARY); pict(PICTURE); nodata
The form nodata is returned in all the situations when a timeout value of zero is requested, in this case, the real data arrives later in the trans_complete event.
Since the program in the other end might not be a Visual Prolog application, the DDE supports all formats sent under DDE. This is done by giving the requested format in the dde_Request call. The possible formats under Windows are:
ddecf_Text, ddecf_Bitmap, ddecf_Metafilepict, ddecf_Sylk, ddecf_DIF, ddecf_Tiff, ddecf_OEMText, ddecf_Dib, ddecf_Palette, ddecf_PenData, ddecf_Riff, ddecf_Wave
When using ddecf_text a str(STRING) is returned, and when using ddecf_Bitmap a pict(PICTURE) is returned, for all other formats bin(BINARY) is returned.
Restarting a Conversation that has been Terminated
The dde_ReConnect() predicate can be used to attempt to reestablish a conversation with a service that has terminated a conversation with the client.
dde_ReConnect(DDE_CONV Conv)
When the conversation is reestablished, the DDEML attempts to reestablish any preexisting advise loops.
A client can obtain connections to all servers that support a given service and topic by using the dde_ConnectMult(). This predicate returns a list of conversation handles.
DDE_CONVLIST dde_ConnectMult(Service, Topic, DDE_CONVMULT)
The argument DDE_CONVMULT returns a special handle, that must be used to close down the connection after use.
dde_DisConnectMult(DDE_CONVMULT ConvMult)
The predicate dde_Connect has a special feature to connect to all topics for a given service. In this case, the topic parameter should be an empty string.
_Dde_Conv = dde_Connect("Myserv", ""),
Initializing a Server Application
When calling dde_Init in a server application, there are additional relevant flags. These all serve to skip unnecessary events in order to optimize the application’s performance.
Table 1.20: Initializing a Server Application
Constant |
Meaning |
ddef_fail_selfconnections |
Prevents an application from connecting to itself |
ddef_fail_connections |
Prevents the application from receiving any connects |
ddef_fail_advises |
Prevents the application from receiving any advisory loop start and stop commands |
ddef_fail_executes |
Prevents the application from receiving any execute commands |
ddef_fail_pokes |
Prevents the application from receiving any poke commands. |
ddef_fail_requests |
Prevents the application from receiving any request commands |
ddef_fail_allsvrxactions |
Prevents the application from receiving any server events. (Combine all the ddef_fail* events) |
ddef_skip_connect_confirms |
Prevents the application from receiving any connect_confirmation events |
ddef_skip_disconnects |
Prevents the application from receiving any disconnect events |
ddef_skip_allnotifications |
Prevents the application from receiving any notification events (Combine all the ddef_skip flags) |
In addition the Server application must register each service that it supports by calling the dde_RegisterService predicate:
dde_RegisterService(STRING Service, BOOLEAN Register)
As an example, look at:
task_win_eh(_Win, e_Create(_),0):-!,
dde_Init([ddef_fail_pokes]),
dde_RegisterService("Myserv", b_true).
The service must be unregistered at the termination of the application:
task_win_eh(_Win, e_Destroy,0):-!,
dde_RegisterService("Myserv", b_false),
dde_End().
Accepting a Connection from a Client
When a client requests a connection, the sever receives a connect message in its DDE event-handler. The server accepts the connection by returning a non-zero value in the last parameter, a value of zero (or b_False) rejects the request. The first parameter of the DDE submessage (usually) contains a unique conversation identifier (handle) that both parties must use when conducting the conversation. However, in the servers connect event this variable (_Conv) will not contain a valid conversation handle and must not be used.
task_win_eh(_Win, e_DDE(_Conv, connect("service", "topic")), b_True):-!,
write("connect() message is received and accepted"),nl,
As noted above the conversation handle that a server receives in a DDE connect submessage must not be used in conversations. The "real" handle arrives after the connection is established. A connect_confirm event is generated by the system. This event can be used to save the conversation handle in a database:
task_win_eh(_Win, e_DDE(_Conv, connect_confirm("service", "topic")),0):-
assert(valid_dde_handle(_Win, _Conv, "service", "topic")),
write("connect_confirm() message is received"),nl,
When the conversation is finally terminated by the client, the server will receive a disconnect event:
task_win_eh(_Win, e_DDE(_Conv, disconnect()),0):-!,
retract(conv(_Conv)),
Accepting a Command from a Client
When a client sends a command string by calling dde_Execute, the server will receive an execute() message:
task_win_eh(_Win, e_DDE(_Conv, execute(_Topic, _Command)), Ret):-!,
The server must take appropriate actions for the command, and return of the following values:
Table 1.21: Constants for Accepting a Command from a Client
Constant |
Meaning |
dderes_fnotprocessed |
This value should be returned if the command could not be processed |
dderes_fbusy |
This value should be returned if the server is too busy to handle the command |
dderes_fack |
This value should be returned if the command are properly executed |
When the client sends data to the server by calling the dde_Poke predicate, the server will in the other end receive a poke message:
task_win_eh(_Win, e_DDE(_Conv,
poke("Mytopic", "Myitem", _DDE_DATA)),dderes_fack):-!,
_DDE_DATA = str(S),
write("Received from a client: ",S),nl,
The return value must be either dderes_fack, dderes_fbusy or dderes_fnotprocessed.
When a client issues a dde_Request command, the server will receive a request event, and must return data in response to this event. This gives a little problem in Prolog because the return value is a long value, and the DDE_DATA is allocated on the Prolog Global Stack. As a work-around for this, a special predicate has to be used to prepare the return data:
LONG dde_MakeDDEReturnData(DDE_DATA Val)
A typical clause to return data to the client will then be:
task_win_eh(_Win, e_DDE(_Conv, request("Mytopic", "Myitem")),DATA):-!,
DATA = dde_MakeDDEReturnData(str("Hello from a Server")),
A client application can use the DDE to establish one or more permanent links to items in a server application. When such a link is established, the server sends periodic updates about the linked item to the client (typically, whenever the value of the item associated with the server application changes). This establishes an advise loop between the two applications that remains in place until the client ends it. The advisory loop is started when the client calls the dde_StartAdv predicate:
DDE_TRANSID dde_StartAdv(DDE_CONV Conv, STRING Item, DDE_FORMAT, DDE_ADVFLAGS, LONG TimeOut)
The DDE_ADVFLAGS is a list that can contain the following two values:
ddeadv_NoData |
Instructs the server to notify the client of any data changes without actually sending the data. This flag gives the client the option of ignoring the notification or requesting the changed data from the server. When the flag is specified the connection is usually referred to as a Warm link instead of a Hot link when the data are actually sent. |
ddeadv_AckReq |
Instructs the server to wait until the client acknowledges that it received the previous data item before sending the next data item. This flag prevents a fast server from sending data faster than the client can process it. |
The advise loop can be stopped by the client calling the dde_StopAdv predicate:
DDE_TRANSID dde_StopAdv(DDE_CONV Conv, STRING Item,
LONG TimeOut)
After the client has successfully called the dde_StartAdv predicate, the server will receive an advstart event. In response to this event, the server must set up some mechanism so it can inform the client when the requested item has changed.
In the following example clause, a timer is set up, so each time this triggers a new value is sent back to the client.
task_win_eh(_Win, e_DDE(_Conv, advstart("MYtopic", "Myitem")),1):-!,
clear_timers,
TIMERID = timer_Set(_Win, 2000),
assert(tim(TIMERID)),
write("Start advise loop"),
Similar, the server will receive an advstop event when the advisory loop is terminated by the client:
task_win_eh(_Win, e_DDE(_Conv, advstop("MYtopic", "Myitem")),1):-!,
clear_timers,
When the server wants to send data for the advise loop back to the client, it is done indirectly by calling the dde_PostAdvise predicate.
dde_PostAdvise(STRING Topic, STRING Item)
As example from the previous example where a timer was set up:
task_win_eh(_Win, e_Timer(_TimerId),0):-!,
dde_PostAdvise("MYtopic", "Myitem").
The result of calling the dde_PostAdvise predicate will be that the server receives a advreq event, and the data must be returned to the client in response to this event:
task_win_eh(_Win, e_DDE(_Conv, advreq("MYtopic", "Myitem")),DATA):-!,
time(HH, MM, SS, _CC),
format(Msg, "Time is: (%02:%02:%02)",HH, MM, SS),
MyData = str(Msg),
DATA = dde_MakeDDEReturnData(MyData).
On the client side, the client receives the data in an advdata event:
task_win_eh(_WIN, e_DDE(_Conv, advdata(_Topic, _Item, DATA)),0):-!,
DATA = str(Str),
write("Received: ",Str),nl,
DDE clients can make a wildconnection, which means, that it will connect to all topics for a given service. In response to this, the server must return a structure with a combination of Service/Topic pairs.
task_win_eh(_, e_DDE(_Conv, wildconnect("Myserv",_Topic)),ServiceInfo):-
ServiceInfo = dde_MakeDDEReturnServiceInfo(
[service_info("Myserv", "Mytopic"),service_info("Myserv", "System")]).
After the server has returned the list of Service/Topic pairs, it will receive a connect_confirm for each pair.
Temporarily Queuing DDE Events
The dde_EnableEvents predicate can be used to queue a conversation event temporarily.
dde_EnableEvents(DDE_CONV Conv, BOOLEAN EnableDisable)
Giving a value of b_False will queue up all DDE events, giving a value of b_True will enable the DDE events again. Normal transmission of events should be enabled as soon as possible.
An error() event is issued to a client or to a server if a critical error occurs during the DDE conversations.
Table of DDE Predicates/DDE Events
The following table lists the available predicates and (where applicable) the events received by the other party:
Table 1.22: DDE Predicates/DDE Events
DDE PREDICATES |
DDE EVENTS (Received by the other part) |
Client: |
|
dde_AbandonTrans |
|
dde_Connect |
connect(STRING Service, STRING Topic) |
connect_confirm(STRING Service, STRING Topic) |
|
dde_ConnectMult |
wildconnect(STRING Service, STRING Topic) |
dde_DisConnectMult |
|
dde_EnableEvents |
|
dde_Execute |
execute(STRING Topic, STRING Command) |
dde_Poke |
poke(STRING Topic, STRING Item, DDE_DATA Data) |
dde_ReConnect |
|
dde_Request |
request(STRING Topic, STRING Item) |
dde_StartAdv |
advstart(STRING Topic, STRING Item) |
dde_StopAdv |
advstop(STRING Topic, STRING Item) |
Server: |
|
dde_MakeDDEReturnData |
|
dde_MakeDDEReturnServiceInfo |
|
dde_RegisterService |
|
dde_PostAdvise |
advreq(STRING Topic, STRING Item) |
advdata(STRING Topic, STRING Item, DDE_DATA Data) |
|
trans_complete(STRING Topic, STRING Item, DDE_TRANSID Id, DDE_DATA Data) |
|
Client/Server: |
|
dde_Init |
|
dde_DisConnect |
disconnect(); |
dde_End |
|
error() |
Many of the predicates used generate blocking calls. They can however be used to obtain data in a non blocking manner, namely the advisory loop (warm and hot links).
Microsoft's NETDDE.DLL extends DDE capability across to applications running on a Microsoft LAN.
To use NETDDE the server program must be registered as being sharable. The client program uses a special form of DDE connect that includes the network name of the machine running the server application.
The format for the NETDDE version of the dde_Connect command is:
DDE_CONV = dde_Connect("\\\\server_machines_network_name\\NDDE$", "dummy_topic")
The topic that is accessed will be the one defined in the DDESHARE registration.
The Microsoft Windows for Workgroups Software Development Kit (SDK) contains a program called DDESHARE.EXE, which can be used to easily set up DDE shares.
A server registered via DDE share can be automatically started; if it is not running when a client requests a connection.
To run NETDDE programs in Windows95, you must add a shortcut for NETDDE.EXE to the Startup group. To do so, use the following four steps:
As an alternative to storing pictures in bitmaps, MS-Windows can store pictures in metafiles. A metafile is a recording of the actual drawing operations necessary to create a picture. In the file there are structures that contain enough information for each drawing operation. The picture can be created at a later time by playing these drawing operations in a window.
An advantage with metafiles is that they are completely device independent. When a bitmap is scaled to a printer, each dot needs to be scaled. But for a metafile, the drawings are done in the new device context. A disadvantage is that metafiles are not portable across platforms. Depending on the actual pictures, there might also be substantial speed difference between drawing them as metafiles or as bitmaps.
A metafile must be loaded into memory before it can be drawn in a window, and the predicate mf_Load does this. Remember to call mf_Destroy after you are done with the metafile to release the associated memory.
METAFILE mf_Load(STRING FileName)
METAFILE mf_Load(STRING FileName, RCT RctMf, UNSIGNED Resolution)
When a metafile is loaded into memory, it can be played in a window or to a printer by a call to mf_Play/3 or mf_Play/2 predicate.
Notice that a metafile keeps the picture frame coordinates in the metafile header. The mf_Play/3 predicate uses this picture frame to map a picture onto the rectangle pointed to by the Rect parameter:
mf_Play(WINDOW Win, METAFILE Mf, RCT Rect)
In opposite, mf_Play/2 simply plays a metafile in the rectangle with the picture frame coordinates.
mf_Play(WINDOW Win, METAFILE Mf)
Therefore, to play a metafile in the desired position by mf_Play/2, the programmer should use an appropriate scaling system correlated with the picture frame coordinates. Calling the win_SetMapMode and the win_SetMapScale, before playing the metafile can set up a scaling system.
When your application is done with using a metafile, the memory space must be released with a call to:
mf_Destroy(METAFILE Mf)
It is possible to record a number of drawing operations in a metafile. The steps involved are:
WINDOW mf_Open()
mf_Open
returns a window handle, which must be used for all drawing operations to the metafileMETAFILE = mf_Close(MetafileWindow)
mf_Save(Metafile, Rct, FileName)
The Rct value is used to indicate the desired size of the drawing in the metafile, so the metafile can be scaled during a later replay.
The VPI provides a safety-net for VPI applications. The event-handling mechanism catches failing event-handlers, exits and run-time errors in the event-handler predicates (or predicates called by them). Therefore, the programmer does not need to create his own event-wrapping predicates as in the earlier Windows version of Prolog.
In order to make debugging of malfunctioning applications faster, the VPI performs a number of validations on the arguments to VPI predicates. If there are any problems, a run-time error is issued, which reports the source position of the bad call if that information is available. This argument validation takes time, so for well-tested applications it is possible to disable this validation by:
vpi_ArgValidation(BOOLEAN On/Off)
Installing a New Error Handler
When an error occurs - and the error is not trapped - the VPI catches the exit in the event-handler and does default error handling with a call to the common dialog dlg_Error.
It is possible to install a user-defined error handling predicate which might, for example, log the error in a file and request that the user send the log file back to the developer.
The error handler predicate must be declared as belonging to the domain ERRHANDLER., and takes two arguments; the window handle and the error number.
A typical error handler in an application might be:
PREDICATES
my_err_handler : ERRHANDLER
CLAUSES
my_err_handler(Win, Err):-
lasterror(ErrorNo, FileName, IncludeFileName, Position),
format(S,
"ErrorNo=%, FileName=<%>\nIncludeFileName=<%>, Position=%",
ErrorNo, FileName, IncludeFileName, Position),
file_str("error.log", S),
dlg_Note(
"Internal error\nPlease send the file ERROR.LOG to <developer>").
The new error handler can be installed with the call:
vpi_SetErrorHandler(my_err_handler),
Note that in order to get accurate error positions from the lasterror predicate, the application must be compiled with Options|Project|Compiler|Errorlevel set to medium or maximum. The resulting larger application will run a little slower, so in well-tested applications it might be an acceptable trade off to report only the error number so that it can be compiled without diagnostic information being placed in the executable file.
The VPI handles the most common features found in the native windowing systems. However, for special applications, it might be necessary to go underneath the VPI layer. The VPI layer is essentially a number of functions which make calls to the underlying native API - it is still possible to call all the underlying API functions directly.
If you need to take advantage of special capabilities of the native operating system you can call the functions of the underlying API (Microsoft Windows, PM etc.) directly. This will make your application non-portable, so it is a good idea to isolate such calls in a module that contains your operating system-dependent code. If you later decide to port the application to another operating system you will only need to rewrite the operating system dependent parts. You can also make use of the automatically defined constants (ws_win, ws_pm etc.) inside ifdef ... enddef constructs to conditionally enable sections of source code based on the windowing system target selected.
In the VPI the native API functions can be declared as global predicates. The language calling convention to the underlying API are different from platform to platform.
Win16 |
PASCAL |
Win32 |
STDCALL |
PM |
SYSCALL |
For the 16_bit MS-Windows platform, the WINBIND sub-directory contains declarations for most of the native calls. By including WINDOWS.CON, WINDOWS.DOM and WINDOWS.PRE from the WINBIND\INCLUDE directory you can easily call the 16-bit MS-Windows API directly.
Creating a Window outside the VPI
It is possible to create a window by making calls directly to the underlying API, and install event-handler predicates that operate directly on native events.
Native Events for a VPI Window
For a window created with the VPI, the VPI layer only sends relatively few high-level events to the windows event-handler predicate. However, it is possible to receive all events the underlying GUI sends to the window by calling the predicate:
win_EnableHook(Win, BOOLEAN OnOff),
When this is called with the b_True parameter for a window the window will start to receive e_Native events on the form:
my_event_handler(Win, e_Native(Message, WParam, LParam), 0):-
Where Message in the underlying event identification, WParam is the value (a WORD for 16-bits platforms, a LONG for 32-bits platforms) and LParam is a LONG value. The example in VPI\EXAMPLES\HOOK is an illustration of how this works.
Retrieving the Native Window Handle
A VPI Window argument does actually just pass around the native window handle. This can just be typecast to the correct domain for the native window handle. However, a cleaner and safer method may be instead to use:
NativeWin = win_GetAttrVal(Win, attr_native_window),
Retrieving the Native Device Context
It is possible to retrieve the native Device Context (DC) associated with a window by making the call:
DC = win_GetNativeGraphicContext(Window,ReleaseFlag)-(i,o)
The predicate returns a DC, which can be used for drawing operations directly to the native windows API. The flag returns a flag that must be passed back to the call for releasing a device context again:
win_ReleaseNativeGraphicContext(Window,DC,ReleaseFlag)
From your VPI programs you can execute any external program with the call:
ApplicationId = vpi_CreateProcess(ReceiveMsgWin, Program, Param, TaskWinInitFlag)
Here Program is a string specifying the path and filename of the application to be run. Param is a string specifying the command line for the application. TaskWinInitFlag can be one of the following flags: wsf_Invisible, wsf_Minimized, wsf_Restored, wsf_Maximized. When TaskWinInitFlag = wsf_Invisible the loaded application starts in the background mode, otherwise in the foreground mode. The predicate returns the application identifier ApplicationId of the loaded application (when ReceiveMsgWin <> NullWin). This application identifier will be returned in the e_EndApplication(ApplicationId) event that will be sent to the specified ReceiveMsgWin to notify about the loaded application termination.
It is possible to terminate previously loaded external application with the application identifier ApplicationId by the call to vpi_CloseProcess:
vpi_CloseProcess(ApplicationId)
Notice that as a module loaded by the vpi_CreateProcess can be not only an application of the underlying OS, so the call of vpi_CloseProcess does not guarantee the specified application termination, for example, if it is an MS-DOS application loaded from a Windows program. Therefore the programmer must wait for e_EndApplication(ApplicationId) event to ensure that the application ApplicationId has been terminated.
If you specify ReceiveMsgWin = NullWin, where NullWin is a special window handle that should be defined in your program by an expression like the following:
NullWin = cast(WINDOW, 0)
then:
Notice that the application identifier is not adequate to the application instance handle that can be received as a value of attr_win_instance by a call to vpi_GetAttrVal.
You should know, that under some platforms it is only possible to have a few simultaneous DC open at the same time. Under Windows 3.1 this number is only 4. Visual Prolog uses an advanced mechanism for cashing open DC’s, this is the reason for the ReleaseFlag.
Visual Prolog includes a dialog package which makes it easier to handle dialogs. Using the dialog package is optional both on a project basis and on an individual dialog basis - you can create dialogs in your applications entirely without it. If you do wish to make use of it in a project, check the box Project | Application Expert | Options | VPI Options | Dialog Package. This causes the proper files to be included in the project.
The Dialog Package is written entirely in Prolog source and is defined in the files DIALOG.PRO, DIALOG.PRE and DIALOG.DOM. All the global predicates in the Dialog Package are prefixed by 'dialog_'.
To support the dialog package a special tool called the Dialog Package Expert can be used. To activate the Dialog Package Expert for a particular dialog, the Dialog and Window Expert must first be invoked with that dialog, and then the Code Style listbox selection set to dialog_Create(...). The Dialog Package Expert can then be invoked by pressing the Dialog Pack push button.
The benefits of using the dialog package are:
Creating and Initializing a New Dialog
When using the Dialog and Window Expert, it will for a dialog generate the usual dlg_<name of dialog>_create predicate, which can create the dialog with the specified layout:
dlg_mymodal_Create(Parent):-
CHECKBOX = b_false,
AGE = void,
%MARK mymodal, new variables
dialog_CreateModal(Parent,dlg_mymodal_ResID,
[
%BEGIN mymodal, ControlList, 20:45:09-5.9.1995, Code automatically updated!
df(idc_checkbox,checkbox(CHECKBOX),nopr),
df(idc_age,editint(AGE,[mandatory,range(5,99)]),
str_prompt("Wrong !"))
%END mymodal, ControlList
],
dlg_mymodal_eh,0,VALLIST,ANSWER),
ANSWER = idc_ok,
dlg_mymodal_update(VALLIST),!.
The predicate for creating the dialog will use two different calls from the dialog package depending on whether a modal or a modeless dialog is to be created:
WINDOW dialog_CreateModal(WINDOW ParentWindow,RESID ResID, STRING Title,
DIALOG_FIELD_LIST InitialFields,EHANDLER, LONG CreationData,
DIALOG_VAL_LIST OutputValues,INTEGER ReturnButton)
WINDOW dialog_CreateModeless(WINDOW ParentWindow,RESID ResID,
STRING Title,DIALOG_FIELD_LIST InitialFields,
EHANDLER,LONG CreationData)
The difference is that the call to dialog_CreateModal will not return before the dialog is terminated. In this case the return values will be delivered in the two variables OutputValues and ReturnButton.
When calling dialog_CreateModeless, the predicate will return as soon as the dialog is created, while the dialog stays on the screen like a normal window. Collecting any return values from a modeless dialog is normally done when the OK button is pressed (or any other button which is used to terminate the dialog).
The argument DIALOG_FIELD_LIST InitialFields contains a description of the controls to the dialog package. It is a list of functors, each with the form:
df(ControlId, Properties, ErrorHandling)
Where ControlId is the integer constant that identifies the control within the dialog and ErrorHandling is the action to take if any of the validation actions fail.
The argument DIALOG_VAL_LIST OutputValues for dialog_CreateModal returns in a list the resulting values for the controls when a dialog is terminated. Each element in the list has the form:
dv(DIALOG_CONTROL_ID,DIALOG_ITEM_VAL)
The DIALOG_ITEM_VAL is different for each control type. A predicate is generated to access these resulting values. This is described below.
Note how the Dialog Package Expert insert new variables. At the mark "
%MARK mymodal, new variables", new variable bindings will be inserted when you specify new in the Dialog Package Expert. These variable bindings will not be changed/deleted when you make any changes in the Dialog Editor or in the Dialog Package Expert - it is up to you to modify, maintain and delete these.The Event-handler for a New Dialog
For many dialogs you need not worry about the Event Handler when you use the dialog package. Default handling for the controls are done by the dialog package, and in many cases this suffices.
However, the Dialog and Window Expert will generate the usual Event Handler callback predicate, where it is possible to react to pushbuttons, changed selection in a listbox etc. The Dialog and Window Expert can be used to add and edit clauses for the dialog events.
If you are inserting any event handling clauses, you should remember the common rule: If the predicate fails, the dialog package will do the default handling for the event. If the predicate succeeds for an event, no further processing will be done by the dialog package for this event.
Getting the Return Values from a Dialog
The Dialog and Window Expert will generate a predicate for the dialog. The predicate will called dlg_<dlgname>_update, and it can be used for access to the control values when the dialog is terminated. When the dialog is terminated the dialog package will collect all the values in a list. To retrieve the actual values, a set of dialog_VLGet* predicates can be used. (In this case VL means Value List).
dlg_mymodal_update(_VALLIST):-!,
%BEGIN mymodal, Update controls, 20:45:09-5.9.1995, Code automatically updated!
_CHECKBOX = dialog_VLGetCheck(idc_checkbox,_VALLIST),
_AGE = dialog_VLGetint(idc_age,_VALLIST),
%END mymodal, Update controls
!.
Inside the block of code that is automatically updated, the variables specified in the Dialog Package Expert will be bound to the values of the controls.
It is fairly easy to change the dlg_<dlgname>_update predicate so that it returns the values needed in the actual application. But remember, that only the code inside the blocks marked with '
Code automatically updated!', will be changed by the Code Experts; thus you should modify the rest of the generated default code.You must take a look at the file DIALOG.DOM in the VPI\INCLUDE directory to see the actual defined domains. One thing you should be aware of is that the Dialog Package has a feature to allow working with void values (no value) for the numeric domains. In effect, for the numeric domains, the Dialog Package does not use the values directly, but surrounds them by a functor so it is possible to see if the value is void or not.
DIALOG_INT = void; i(INTEGER)
DIALOG_REAL = void; r(REAL)
DIALOG_LONG = void; l(LONG)
Change Enable and/or Show States for Controls
With the predicate dialog_SetState it is possible to enable/disable or hide/show a whole list of controls in one call. This feature is powerful and should be used often.
dialog_SetState(WINDOW, DIALOG_ENABLE_LIST)
DIALOG_ENABLE_LIST = DIALOG_ENABLE*
DIALOG_ENABLE =
enable(DIALOG_CONTROL_ID, BOOLEAN);
show(DIALOG_CONTROL_ID, BOOLEAN)
Fetch Values of Individual Controls from a Values List.
There are a number of predicates which can be used to pick out the values from the Value List returned when a dialog is terminated. Each predicate takes the control identifier and the value list as input parameters, and returns the corresponding value:
BOOLEAN dialog_VLGetCheck(DIALOG_CONTROL_ID,DIALOG_VAL_LIST)
DIALOG_CONTROL_ID dialog_VLGetRadiobutton(
DIALOG_CONTROL_ID,DIALOG_VAL_LIST)
STRING dialog_VLGetStr(DIALOG_CONTROL_ID,DIALOG_VAL_LIST)
DIALOG_INT dialog_VLGetInt(DIALOG_CONTROL_ID,DIALOG_VAL_LIST)
DIALOG_LONG dialog_VLGetLong(DIALOG_CONTROL_ID,DIALOG_VAL_LIST)
DIALOG_REAL dialog_VLGetReal(DIALOG_CONTROL_ID,DIALOG_VAL_LIST)
STRING dialog_VLGetListEdit(DIALOG_CONTROL_ID,DIALOG_VAL_LIST)
INTEGER dialog_VLGetScrollBar(DIALOG_CONTROL_ID,DIALOG_VAL_LIST)
dialog_VLGetListBox(DIALOG_CONTROL_ID,DIALOG_VAL_LIST,SLIST,ILIST)
dialog_VLGetListButton(DIALOG_CONTROL_ID,DIALOG_VAL_LIST,STRING,INTEGER)
dialog_VLGetCustom(DIALOG_CONTROL_ID,DIALOG_VAL_LIST,STRING,LONG)
STRING dialog_VLGetDataField(DIALOG_CONTROL_ID,DIALOG_VAL_LIST)
Set/Fetch Values of Individual Controls in a Dialog.
Some dialogs provide a number of actions while the dialog is open. These may be triggered by the activation of a push button at which time other control values may change, or they can be triggered by the selection in a listbox, upon which some other values will change. To make this easy, a range of predicates that take the dialog handle and the control identifier as parameters can be used:
BOOLEAN dialog_GetCheck(WINDOW,DIALOG_CONTROL_ID)
DIALOG_CONTROL_ID dialog_GetRadiobutton(
WINDOW,DIALOG_CONTROL_ID FirstFromList)
STRING dialog_GetStr(WINDOW,DIALOG_CONTROL_ID)
DIALOG_INT dialog_GetInt(WINDOW,DIALOG_CONTROL_ID)
DIALOG_LONG dialog_GetLong(WINDOW,DIALOG_CONTROL_ID)
DIALOG_REAL dialog_GetReal(WINDOW,DIALOG_CONTROL_ID)
STRING dialog_GetListEdit(WINDOW,DIALOG_CONTROL_ID)
INTEGER dialog_GetScrollBar(WINDOW,DIALOG_CONTROL_ID)
dialog_GetListBox(WINDOW,DIALOG_CONTROL_ID,SLIST,ILIST)
dialog_GetListButton(WINDOW,DIALOG_CONTROL_ID,STRING,INTEGER)
dialog_GetCustom(WINDOW,DIALOG_CONTROL_ID,STRING,LONG)
STRING dialog_GetDataField(WINDOW,DIALOG_CONTROL_ID)
dialog_SetCheck(WINDOW,DIALOG_CONTROL_ID,BOOLEAN)
dialog_SetRadiobutton(WINDOW,DIALOG_CONTROL_ID FirstFromList,
DIALOG_CONTROL_ID Pushed)
dialog_SetStr(WINDOW,DIALOG_CONTROL_ID,STRING)
dialog_SetInt(WINDOW,DIALOG_CONTROL_ID,DIALOG_INT)
dialog_SetLong(WINDOW,DIALOG_CONTROL_ID,DIALOG_LONG)
dialog_SetReal(WINDOW,DIALOG_CONTROL_ID,DIALOG_REAL)
dialog_SetListEdit(WINDOW,DIALOG_CONTROL_ID,STRING)
dialog_SetScrollBar(WINDOW,DIALOG_CONTROL_ID,INTEGER)
dialog_SetListBox(WINDOW,DIALOG_CONTROL_ID,ILIST)
dialog_SetListButton(WINDOW,DIALOG_CONTROL_ID,INTEGER)
dialog_SetCustom(WINDOW,DIALOG_CONTROL_ID,STRING,LONG)
dialog_SetDataField(WINDOW,DIALOG_CONTROL_ID,STRING)
dialog_CheckEditProperties(WINDOW,DIALOG_CONTROL_ID)
dialog_CheckEditProperties(WINDOW)
dialog_SetControlTitle(WINDOW,DIALOG_CONTROL_ID,STRING Title)
It is possible to use the Visual Prolog editor in the application you create. The file EDIT.PRE in the VPI\INCLUDE directory contains declarations of the predicates that are be used to operate on editor windows. The example in VPI\EXAMPLES\EDITOR shows how to handle normal file editors, which supports loading, saving, cut and paste, etc.
There are basically three predicates to create an editor window. These three predicates determine factors like whether the editor should be a regular editor, whether it should support word wrapping, - or support hypertext fields. The predicates will create a new window.
WINDOW edit_Create(WINTYPE wType, RCT rct, STRING FileName,
MENU menu, WINDOW ParentWin, WSFLAGS WinFlags,
FONT font, BOOLEAN ReadOnly, BOOLEAN Indent,
STRING InputStr, ULONG InitPos,EHANDLER handler)
WINDOW edit_CreateWrap(WINTYPE wType, RCT rct, STRING FileName,
MENU menu, WINDOW ParentWin, WSFLAGS WinFlags,
FONT font, BOOLEAN ReadOnly, BOOLEAN Indent,
BOOLEAN Wrap, STRING InputStr, ULONG InitPos,
EHANDLER handler)
WINDOW edit_CreateHyper( WINTYPE wType, RCT rct, STRING FileName,
MENU menu, WINDOW ParentWin, WSFLAGS WinFlags,
FONT font, BOOLEAN ReadOnly, BOOLEAN Indent,
BOOLEAN Wrap, STRING InputStr, ULONG InitPos,
EHANDLER )
Also, for handling an editor in a window that is already created, the following predicate can be used:
edit_CreateSubClass(WINDOW Win, STRING FileName,
FONT font, BOOLEAN ReadOnly, BOOLEAN Indent,
BOOLEAN Wrap, STRING InputStr, ULONG InitPos,
EHANDLER handler)
One typical use of edit_CreateSubClass is when creating custom controls that are based on the editor.
Retrieving the Current Text from an Editor Window
The following two predicates allows retrieving the whole text from an editor window, or just a part of the text in a editor window.
STRING edit_GetText(WINDOW)
STRING edit_GetText(WINDOW,UNSIGNED StartPos,UNSIGNED EndPos)
There are three predicates to add new text into an editor window. The text can either be pasted at the current position, at a given position, or at the end by the following three predicates:
edit_PasteStr(WINDOW Win,STRING NewStr)
BOOLEAN edit_PasteStr( WINDOW, ULONG Pos, STRING Str)
edit_AppendStr(WINDOW, STRING Str)
The group of editing predicates are used to either simulate that a given key has been pressed, or to delete a piece of text relative to the current position.
edit_Char( WINDOW, CHAR )
edit_Enter( WINDOW )
edit_Del( WINDOW )
edit_Backspace( WINDOW )
edit_DelLeftWord( WINDOW )
edit_DelRightWord( WINDOW )
edit_DelToStartOfLine( WINDOW )
edit_DelToEndOfLine( WINDOW )
This group of predicates can be used to set or retrieve the current selection in the editor window:
BOOLEAN edit_SetSelection(WINDOW Win,ULONG Pos1,ULONG Pos2)
edit_GetSelection(WINDOW Win,ULONG Pos1, ULONG Pos2)
edit_SelectCurLine(WINDOW)
BOOLEAN edit_SelectWord(WINDOW Win)
The following predicates can be used to report the current position of the editor caret in either an offset or as Line + Column information.
ULONG edit_GetPos(WINDOW Win)
edit_GetPos(WINDOW,INTEGER Line,INTEGER Column)
INTEGER edit_GetNumberOfLines( WINDOW )
This group of predicates are used to move the current position in the editor.
edit_Left( WINDOW )
edit_Right( WINDOW )
edit_Up( WINDOW )
edit_Down( WINDOW )
edit_PgUp( WINDOW )
edit_PgDn( WINDOW )
edit_WordLeft( WINDOW )
edit_WordRight( WINDOW )
edit_LineHome( WINDOW )
edit_LineEnd( WINDOW )
edit_FileHome( WINDOW )
edit_FileEnd( WINDOW )
BOOLEAN edit_GotoPos(WINDOW Win, ULONG Pos)
edit_GotoLine(WINDOW,INTEGER Line)
The Application can use the Undo and Redo facilities by calling the two predicates:
edit_Undo(WINDOW)
edit_Redo(WINDOW)
The following predicate can return two flags which indicate, whether it is actually possible to perform an undo / redo operation at the current time:
edit_PossibleUnDoReDo(WINDOW,BOOLEAN Undo,BOOLEAN Redo)
To handle the Clipboard operations, the following predicates can be called to perform the operations:
edit_Cut(WINDOW)
edit_Copy(WINDOW)
edit_Paste(WINDOW)
edit_Delete(WINDOW)
The editor has a facility to change the casing of the current selection. The following predicates can be used:
edit_ReverseCase(WINDOW)
edit_UpperCase(WINDOW)
edit_LowerCase(WINDOW)
The editor has a number of internal dialogs. These can be invoked by the following calls:
edit_OptionsDlg()
edit_SearchDlg(WINDOW)
edit_SearchAgain(WINDOW) % Position function
edit_ReplaceDlg(WINDOW)
edit_GotoLineDlg(WINDOW)
edit_GotoPosDlg(WINDOW)
edit_SetFontDlg(WINDOW)
If you are working with the hypertext editor, the two following predicates can be used to create and delete hypertext links.
edit_CreateHyperField(WINDOW)
edit_DeleteHyperField(WINDOW)
To understand the hypertext editor, you should take a look at the VPI\EXAMPLES\HYPER. You should also know that a hypertext link in the editor is inserted as the following text sequence:
<hyper_begin><Highlighted text><hyper_middle><Hidden text><hyper_end>
Where hyper_begin, hyper_middle and hyper_end are special character values defined in EDIT.DOM.
So if you want to insert a new field you should use some code like:
format(HyperField,"%c%c%c%c%c", hyper_begin,HighLight,yper_middle,Hidden,hyper_end),
edit_PasteStr(Win,HyperField),
Suspend/Resume Editor Updating
If you need to do many operations on the editor, you can turn updating off while doing these operations. This is done with the following predicates:
edit_Suspend(WINDOW)
edit_Resume(WINDOW)
Caret Showing/Hiding
With the following predicate, it is possible to hide or show the caret in the editor:
edit_CaretShow(WINDOW,BOOLEAN DoShow)
With the following predicate, it is possible to hide the top status line in the editor:
edit_StatusLineShow(WINDOW,BOOLEAN Hide/Show)
The token coloring in the editor can be set with the following predicates:
edit_SetColoring(WINDOW,INTEGER TypeOfColoring)
edit_SetAssociations(EDIT_ASSLIST Associations)
EDIT_ASSLIST = edit_GetAssociations()
Editing Options
The various settings for the editor can be set with the following predicates:
INTEGER edit_GetTabSize()
edit_SetTabSize( WINDOW, INTEGER TabSize )
BOOLEAN edit_GetIndent()
edit_SetIndent( WINDOW, BOOLEAN On/Off )
BOOLEAN edit_GetInsert()
edit_SetInsert( WINDOW, BOOLEAN On/Off )
% Return / Set all options
edit_GetOptions(ILIST ReturnedOptionList)
edit_SetOptions(ILIST OptionList)
The message window package provides a tool for handling a dialog of short messages. Furthermore, by defining clauses for the IO_WrStrCon predicates defined in IODECEL.PRE in the ROOT\INCLUDE directory, the message window can capture the output from the write and writef predicates.
When enabling the "message window" option in the Application Expert, the header files for the message window will automatically be included in an application, and the message window will be created.
The Message Window Package is defined in the files MESSAGE.PRO and MESSAGE.PRE in the VPI\INCLUDE directory.
The Message Window Package defines the following predicates:
msg_create(INTEGER NoOfLines) |
Create a message window with a given number of lines stored. |
msg_close() |
Close the message window. |
msg_SendMsg(STRING) |
Append a new string. |
msg_SetLines(INTEGER NoofLines) |
Change the Max number of lines. |
msg_Clear() |
Clean the window. |
msg_Resize(WINDOW Parent) |
Parent window has been resized. |
msg_SetFocus() |
Set the focus to the message window. |
Toolbar Package
Visual Prolog provides an easy to use toolbar package defined in the files TOOLBAR.DOM and TOOLBAR.PRE.
A toolbar is always associated with a window irrespectively of whether it has the style Left, Right, Top, Bottom, Inside or movable.
The layout of a toolbar will normally be handled in the Toolbar editor, and generating code to create the toolbar should be handled using in the Toolbar Expert.
When the toolbar is used to provide toolbar buttons for menu entries, you need not provide extra coding. This is because toolbar buttons send an e_Menu type event to the parent window.
The creation of a toolbar is done by the predicate:
WINDOW toolbar_create(TOOLBAR_STYLE,COLOR,WINDOW Parent,TOOLBAR_LIST)
Where TOOLBAR_LIST is a list of toolbar control descriptions for the toolbar.
The size of the toolbar is adjusted when it is created. It is also important to remember, that toolbars must be resized if the parent window is resized. For the e_Size event, The Dialog and Window Expert will insert a call to:
toolbar_resize(WINDOW ParentWin)
If a toolbar should be removed, the following predicate can be called:
toolbar_remove(WINDOW ToolbarWin)
Changing the Values in a Toolbar
For check boxes, list buttons and text fields, the following two predicates can be used to get and set the values of the controls:
toolbar_SetValue(WINDOW TbWin,MENU_TAG,TOOLBAR_VALUE)
toolbar_GetValue(WINDOW TbWin,MENU_TAG,TOOLBAR_VALUE)
Where MENU_TAG identifies the control, and TOOLBAR_VALUE belongs to the following domain:
TOOLBAR_VALUE =
ctrl_value(BOOLEAN,BOOLEAN); % (Enabled,Released)
text_value(STRING); % (Text)
lbut_value(BOOLEAN,INTEGER); % (Enabled,PositionInList)
none
Creating Radio Buttons in a Toolbar
In the same way as it is possible to have a group of radio button controls in a dialog, it is possible to specify for a toolbar that a list of check boxes belongs to a group, and only one at a time should be pressed. This facility can not be defined in the Toolbar editor, but must be specified with a call to toolbar_GroupControls:
toolbar_GroupControls(WINDOW TbWin,INTEGER GroupNo,TOOLBAR_MENU_TAGS)
The tree package is a tool to make it easy to display and work with tree diagrams. The tree can be shown in the four different directions.
The example VPI\EXAMPLES\TREE illustrates how to use the tree tool.
To create a tree, you need to build a structure from the TREE domain. To highlight a path in the tree, you can supply a path from the TREE_PATH domain.
CONSTANTS
tree_DirRight = 0
tree_DirLeft = 1
tree_DirDown = 2
tree_DirUp = 3
GLOBAL DOMAINS
TREE_PATH = LONGLIST*
LONGLIST = LONG*
TREE_NODE_MARK = marked ; unmarked
TREE_SELECTOR = string
TREE_DIRECTION = INTEGER
TREE_ARROW_TYPE = INTEGER
TREE_ARROW_PEN = tree_arrowPen ( TREE_ARROW_TYPE, PEN )
TREE_ARROW_PEN_LIST= TREE_ARROW_PEN*
TREE_WIN_INFO =
tree_WinInfo ( FONT, TREE_DIRECTION, TREE_ARROW_PEN_LIST );
tree_WinInfoAdv ( FONT, TREE_DIRECTION, TREE_ARROW_PEN_LIST, SLIST, PNT )
TREE = tree ( TREE_SELECTOR, TREE_NODE_MARK, TREELIST, TREE_ARROW_TYPE )
TREELIST = TREE*
The Predicates in the Tree Tool
WINDOW tree_Create (
WINTYPE WinType,RCT,STRING Title,MENU Menu,WINDOW Parent,
WSFLAGS Flags,EHANDLER Eventhandler,LONG CreationData,
TREE Tree,TREE_WIN_INFO TreeWindowInfo,TREE_PATH Path )
Creating a Tree in an Already Open Window (Subclass)
tree_CreateSubclass( WINDOW, TREE, TREE_WIN_INFO, TREE_PATH )
SLIST tree_GetAllNodes( WINDOW )
Return the Current Selected Node
TREE_SELECTOR tree_GetSelectedNode( WINDOW )
Return the Parent Node for the Current Selected
TREE_SELECTOR tree_GetParentNode( WINDOW )
Return the Node Type for the Current Selected
TREE_ARROW_TYPE tree_GetNodeArrowType( WINDOW )
Return Settings from the Tree Window
TREE_WIN_INFO tree_GetWinInfo( WINDOW )
Return Rectangle for the Tree Window
RCT tree_GetWinRect( WINDOW, WINDOW OutWindow )
Return Current Search Value - or Fail
TREE_SELECTOR tree_GetCurrentSearch ( WINDOW )
Move Selection to the First Node Matching the Selector
tree_SetSelectedNode( WINDOW, TREE_SELECTOR Selector )
Sets New Direction for the Tree
tree_SetDirection( WINDOW, INTEGER )
tree_SetFont( WINDOW, FONT )
tree_Update( WINDOW, TREE, TREE_PATH )
Explode/Implode Subtree from the Selected Node
tree_OpenCloseNode( WINDOW )
Select First Node Using the Selector
tree_SearchFirstNode( WINDOW, TREE_SELECTOR )
Select next node using the chosen Selector
tree_SearchNodeAgain( WINDOW )
tree_MoveToSelectedNode( WINDOW )
Move Selection from Child Node to the Parent Node
tree_MoveToParentNode( WINDOW )
tree_Print( WINDOW Win, STRING Title )
If you are using ownerdrawing, you should take a look at the following files in the VPI\INCLUDE directory: OWNDRAW.DOM, OWNDRAW.PRE and OWNDRAW.PRO, these files can make ownerdrawing easier.