Dialogs

How to create dialog

Working with the Dialog Editor

How to show dialog

Dialog types (modal, modeless, child)

How to get data from controls

How to initialize controls

Variable names

Using a user-defined type

Smart dialogs

Multi-page dialogs

Multiple dialogs in a macro

Unicode

Fonts and colors

Menus and accelerators (hotkeys)

Common controls, other controls

ActiveX controls

Encrypted macros

Dialogs now and before QM 2.1.9

Dialog examples and tutorials

Dialog programming info at MSDN

How to create dialog

To create new dialog, use menu File -> New -> New Dialog. It creates initial dialog definition (BEGIN DIALOG ... END DIALOG) and opens it in the Dialog Editor. You can add controls, order them, change text, etc. When you click Save, it updates the dialog definition.

 

To edit a dialog later, open the macro that contains the dialog definition and click 'Dialog Editor' button or menu item in QM window. If current macro does not contain a dialog definition, this button/menu creates new dialog.

 

You can also edit dialog definitions directly. The format is simple: The first line defines dialog window, other lines - controls. Line format: id class style exstyle x y width height text tooltip. Text and tooltip are optional. The position/size is in dialog units, not in pixels.

Working with the Dialog Editor

Add control Click or drag&drop a control from the toolbox.
Move control Drag with the mouse left button. Or use arrow keys.
Resize control or dialog Drag with the mouse right button. Or use Shift+arrow keys.
Select control Click it. A small red rectangle will appear at the top-left corner. Also you can use Tab or Shift+Tab to select next or previous control in the Z order.
Send control to the top of the Z order and tab order Shift+click.
Send control to the bottom of the Z order Ctrl+click.
Place control behind the currently selected control in the Z order Ctrl+Shift+click.
Z-order all controls Select dialog, then Ctrl+Shift+click all controls starting from the control that must be at the top of the Z order. Alternatively, close the Dialog Editor and reorder lines in the dialog definition. The order of lines matches the Z order.
Underline character in control's text Place ampersand (&) before.
Create group of option buttons Add "Option first" and then several "Option next".
Move multiple controls If the controls are in a Group control, Shift+drag it. Else create/drag a temporary Group control for it.
Move/resize with high precision. Use Alt when you drag or right-drag.
Add control similar to a control in a window Move the mouse over the control and press the hotkey defined in Dialog Editor Options.

 

In the Dialog Editor, you can set text for some controls. For other controls, text is used only as control's name, making it easier for you to recognize the control in the Dialog Editor. The first three characters also are used for variable name. Generally, controls are identified by an unique control id - numeric value that you can see in variable name. For example, if variable name is b3But, you know that control id is 3.

How to show dialog

Use function ShowDialog. The Dialog Editor creates code for it. When you click Save, it updates the code or displays in output, depending on dialog settings. The code also may contain declarations of variables for controls (read below). By default the code must be in the same function as the dialog definition, but it can be anywhere if not using a sub-function dialog procedure.

 

The minimum code to show the dialog is created by the Dialog Editor. But ShowDialog has more parameters that you can use.

 

function# [$macro] [dlgproc] [!*controls] [hwndowner] [flags] [style] [notstyle] [param] [x] [y] [$icon] [$menu] ;;flags: 1 modeless, 4 set style (default is to add), 64 raw x y.

 

macro - where to look for dialog definition. Can be:

dlgproc - address of a dialog procedure. Optional. Used with smart dialogs (read below).

controls - array of strings (address of first str variable in the array) for controls, as explained later in this topic.

hwndowner - owner window handle. If the dialog has WS_CHILD style - parent window handle.

flags:

1 Modeless dialog.
4 Replace dialog style with style. Default - add style.
64 Raw x y. Read more below.
128

QM 2.3.3. Start hidden.

  • If later need to unhide, use act (shows and activates) or hid- (does not activate).
  • It also can be used to create visible dialog without activating it. Create hidden dialog, and under case WM_INITDIALOG call hid- hDlg.
  • When this flag used, does not disable owner window (hwndowner).
  • Without this flag, modal dialogs always start visible, regardless of WS_VISIBLE style.
0x100

QM 2.4.2. Use x y specified in dialog definition. Read more below. Also you should remove DS_CENTER style.

style - combination of window styles (WS_ and/or DS_ constants). Changes style that is set in the Dialog Editor.

notstyle - combination of styles to remove from default style.

param - some value to pass to the dialog procedure. The dialog procedure can retrieve it using DT_GetParam function.

x, y - dialog coordinates. Read more below.

icon - title bar icon.

menu - menu.

 

Actual dialog coordinates depend on x, y, hwndowner, some styles, flag 64, and _monitor. If x and y are omitted or 0, by default dialogs are shown in screen center.

 

If your dialog shows message boxes or other unowned dialogs, it should do it in the same monitor. Under case WM_INITDIALOG insert _monitor=hDlg. Without this, these unowned dialogs will be in the primary monitor regardless where your dialog is.

 

The return value depends on dialog type. Read the next chapter. Error if dialog cannot be shown, for example if a control class is not registered.

Dialog types (modal, modeless, child)

A dialog can be modal or modeless. If modal, ShowDialog returns only when the dialog is closed. If modeless, ShowDialog creates the dialog and returns immediately.

 

The Dialog Editor creates code to show modal dialog, but you can edit it to show modeless dialog (add flag 1, etc).

 

ShowDialog for a modal dialog returns 1 on OK, 0 on Cancel. When you click some other push-button in a dialog that does not have dialog function (that is, not a "smart" dialog), it closes the dialog, and ShowDialog returns button id.

 

ShowDialog for a modeless dialog returns dialog window handle. It does not wait until the dialog is closed, and does not populate dialog variables on OK. To make a modeless dialog useful, use dialog procedure (see smart dialogs). The thread must stay running and process messages. Example.

\Dialog_Editor

str controls = "3 4"
str c3Che e4
int hDlg=ShowDialog("" &sub.DlgProc &controls 0 1)
MessageLoop 2 ;;flag 2 enables keyboard navigation, which by default does not work in modeless dialogs.
out c3Che
out e4

 BEGIN DIALOG
 0 "" 0x90C80AC8 0x0 0 0 223 135 "Dialog"
 1 Button 0x54030001 0x4 120 116 48 14 "OK"
 2 Button 0x54030000 0x4 170 116 48 14 "Cancel"
 3 Button 0x54012003 0x0 8 10 48 12 "Check"
 4 Edit 0x54030080 0x200 8 24 96 14 ""
 END DIALOG
 DIALOG EDITOR: "" 0x2040104 "*" "" "" ""

#sub DlgProc
function# hDlg message wParam lParam

sel message
	case WM_INITDIALOG
	case WM_DESTROY
	PostMessage 0 2000 0 0 ;;ends MessageLoop
	case WM_COMMAND goto messages2
ret
 messages2
sel wParam
	case IDOK
	DT_GetControls hDlg ;;populated dialog variables. Optional.
	case IDCANCEL
ret 1

 

Dialogs also can be used as child windows. Add WS_CHILD style, and pass parent window handle to ShowDialog. Child dialogs are modeless.

 

QM 2.3.4. If a modeless dialog still exists when the thread ends, it receives WM_QM_ENDTHREAD message. It destroys the dialog, unless dialog procedure returns nonzero (then you must explicitly destroy the dialog before the thread completely ends).

How to get data from controls

The Dialog Editor craetes and updates ShowDialog code that may contain declarations of variables for some controls. You can use the variables to initialize the controls and get data from them. Example:

 

str controls = "3 5"
str e3Nam c5Var
if(!ShowDialog("Dialog2" 0 &controls)) ret

if(c5Var=1) ;;checked
	out e3Nam

 

Don't edit the declarations (the first two lines) manually because the variables must be in a particular order. The first str variable contains identifiers of controls. Then follow str variables for each control whose id is included in the first variable, in the same order. Variable names include control id. The third argument of ShowDialog must be address of the first variable.

 

When the user clicks OK, ShowDialog gets data from controls and populates the variables. Data format:

 

Control type Variable Notes, examples
Check, Option "1" if checked, "0" if unchecked. if(c3Exa=1) out "checked"

ComboBox,

single-selection ListBox

"index text". Here index is 0-based index of selected item, or -1 if no selection.
int iSel=val(cb3)
int i=TO_CBGetItem(cb4)
out i; out cb3
Multi-selection ListBox String consisting of '0' and '1' characters for unselected and selected items. For example, if selected are items 0 and 3 in a 4-item list box, the string will be "1001".

if(lb3[0]='1') out "selected"

Edit, QM_Edit, rich edit Control text. Before QM 2.3.4: unchanged if read-only.
QM_Grid Table in CSV format. You can use ICsv interface to work with CSV.
QM_ComboBox Depends on control style and flags. Editable: control text; read-only: item index; check boxes: string of '0' and '1' characters; flag 2: hex value; flag 0x100: CSV.
Static, Button, Group, ActiveX Unchanged. The Dialog Editor does not add variables for most of these controls.
Other Control's text. Before QM 2.3.4: unchanged. The Dialog Editor adds variables only for some control types. To add for others, you can use QmSetWindowClassFlags or Dialog Editor Options.

 

To get data from controls in a dialog procedure, you can use function DT_GetControl or DT_GetControls. Also you can use window/control functions - str.getwintext, but (get check box state), CB_SelectedItem and other. Look in category control. Also you can use control messages, most of them are documented in the MSDN Library.

How to initialize controls

In the Dialog Editor you can set text for these controls: button, group, static text, read-only edit, SysLink. To initialize other controls, before showing dialog assign certain strings to the control variables.

 

Control type Variable Control contents Notes, examples
Check, Option "1" Checked. c3=1
ListBox, ComboBox List of strings. Some strings can begin with &. Each line adds item. The & selects the item.
cb3="&Zero[]One[]Two"
lb4="Zero[]&One[]&Two"
Static icon Icon file path or macro resource name. Can begin with &. Can contain exe resource id. Displays the icon. The & displays 32x32 icon instead of 16x16 icon.
si3="$my qm$\file.ico"
si4="&shell32.dll,15"
si6="resource:test.ico"
":150 file.ico"
"&:150 file.ico"
Static bitmap bmp, jpg or gif file path or macro resource name. Can contain exe resource id. QM 2.3.4: can be png. Displays the picture.
sb3="$my qm$\file.gif"
sb7="resource:test.png"
sb8=":8 $personal$\file.bmp"
Dialog itself (id=0) Any text. Displays the text in the title bar.

d0="New Title"

 

Include 0 in Dialog Editor Options -> Add variables...

Edit, QM_Edit Any text. Displays the text. e3="text"
Rich edit Any text. Can be .rtf file path or macro resource name, preceded by &. The path can contain exe resource id (QM 2.3.0). Displays the text or the file.
rea3="text"
rea4="&$personal$\rich text.rtf"
rea8="&resource:Document.rtf"
rea5="&:10 &$personal$\text.txt"
Web browser control Web page, folder or file that can be opened in web browser.

Opens the web page, folder or file. If "", opens empty page.

ax3SHD="www.quickmacros.com"
ax4SHD="$my qm$\animated.gif"
ax5SHD=""
QM_Grid Table in CSV format. Displays the table.

qmg3="a1,b1,c1[]a2,b2,c2"

 

You can use ICsv interface to work with CSV.

QM_ComboBox CSV. First row - control properties. Other rows - list items.  
qmcb3="0[]Zero[]One[]Two"
qmcb4=",,1[]Zero[]One,1[]Two"
Other Any text. Sets text of the control. The control can display it or not.

The Dialog Editor adds variables only for some control types. To add for others, you can use QmSetWindowClassFlags or Dialog Editor Options.

 

Example:

 

str controls = "3 5 6 7 4"
str c3 cb5 e6 si7 sb4
c3 = 1 ;;checkbox checked
cb5 = "Sunday[]&Monday[]Tuesday" ;;combobox with three items; Monday is selected
e6 = "Default text" ;;edit control's text is "Default text"
si7 = "&files.ico" ;;32x32 icon from files.ico file
sb4 = "c:\pictures\tree.bmp" ;;picture
if(!ShowDialog("Dialog2" 0 &controls)) ret

 

To set control data in a dialog procedure, you can use function DT_SetControl or DT_SetControls. Also you can use window/control functions - str.setwintext, but (set check box state), CB_SelectItem and other. Look in category control. Also you can use control messages, most of them are documented in the MSDN Library.

Variable names

The Dialog Editor creates names for control variables. A variable name is composed from abbreviated class name, id and max 3 characters of control's name. For example, if combo box with id 15 is named "Value", variable name is cb15Val.

 

If you change variable names (change variable's text in the Dialog Editor), need to update variable names in the macro. The Dialog Editor can do it automatically on Save, depending on dialog settings.

Using a user-defined type

Instead of multiple str variables for controls, you can use a user-defined type. You can enter type name in Dialog Editor Options. Example:

 

type TYPE_A ~controls ~c3Che ~e4 ~cb5
TYPE_A d.controls="3 4 5"

d.cb5="Sunday[]&Monday[]Tuesday"

if(!ShowDialog("Dialog4" 0 +&d)) ret

TO_CBGetItem d.cb5
out d.cb5

 

Examples of getting control values in a dialog procedure:

 

 modal dialog (don't use this for modeless dialogs)
TYPE_A& d=+DT_GetControls(hDlg)
out d.e4

 modal or modeless dialog
TYPE_A d.controls="3 4 5"
DT_GetControls(hDlg &d)
out d.e4

 

Example of using the registry to save dialog data:

 

type DLGVAR ~controls ~e3 ~c4Che
DLGVAR d.controls="3 4"

if(rget(_s "DLGVAR" "\test")) _s.setstruct(d)

if(!ShowDialog("Dialog33" 0 &d)) ret

_s.getstruct(d); rset(_s "DLGVAR" "\test")

Smart dialogs

When you use a simple dialog, you can only initialize controls before calling ShowDialog, and get data from them when ShowDialog returns. While the dialog is open, the macro waits until it is closed. That is enough in many cases. But sometimes you need to execute some code while dialog is open. For example, show an Open File dialog when the user clicks a button. Then you need a special function - dialog procedure.

 

Dialog procedure

A dialog procedure is a user-defined function (can be sub-function) that begins with function# hDlg message wParam lParam. Its address is passed to ShowDialog as the second argument. To create a dialog with a dialog procedure, use menu File -> New -> New Dialog. Select 'New smart dialog'.

 

Example:

\Dialog_Editor

 This code defines and shows dialog.
 Everything is like with simple dialogs, but we use the second ShowDialog argument - address of dialog procedure.

str dd=
 BEGIN DIALOG
 0 "" 0x90C80AC8 0x0 0 0 224 136 "Dialog"
 3 Button 0x54032000 0x0 8 8 48 14 "Test"
 4 ComboBox 0x54230243 0x0 8 28 96 213 ""
 1 Button 0x54030001 0x4 116 116 48 14 "OK"
 2 Button 0x54030000 0x4 168 116 48 14 "Cancel"
 END DIALOG
 DIALOG EDITOR: "" 0x2040108 "*" "" "" ""

str controls = "4"
str cb4
cb4="&A[]B[]C"
if(!ShowDialog(dd &sub.DlgProc &controls)) ret


#sub DlgProc ;;this is the dialog procedure
function# hDlg message wParam lParam

sel message
	case WM_INITDIALOG
	out "dialog created"
	
	case WM_DESTROY
	out "closing"
	
	case WM_COMMAND goto messages2
ret
 messages2
sel wParam
	case IDOK
	out "button OK clicked"
	
	case 3
	out "button Test clicked"
	
	case CBN_SELENDOK<<16|4
	_i=CB_SelectedItem(lParam)
	out F"ComboBox item {_i} selected"
ret 1

 

The dialog procedure is called by the system whenever the dialog receives a message (e.g., when the user clicks a button or selects a combobox item).

 

The first sel is for dialog-related messages. The second sel is for control-related messages (WM_COMMAND). When a message arrives, is executed code that follows the case statement for that message.

 

To add case statements for various messages, you can use the Events dialog in the Dialog Editor. Then add your code in or below the case line.

 

Some often used messages.

WM_INITDIALOG - the dialog has been created. Here you can add code that initializes some controls, sets timers etc.

 

WM_DESTROY - the dialog will be destroyed. It is already hidden, but controls still exist. If the dialog procedure has allocated data that must be deleted when the dialog is closed, do it here.

 

WM_CREATE - although Windows does not send this message to dialogs, QM sends it before initializing controls. WM_INITDIALOG is sent after initializing controls. This message is rarely used.

 

WM_COMMAND - an input event in a standard control. wParam contains control id (in low-order word) and notification code (in high-order word). lParam is control handle. Several often used notification codes:

 

BN_CLICKED - button is clicked. There are two special control ids:

    IDOK (1) - OK button. Return 1 (default) to close the dialog, or 0 to not close.

    IDCANCEL (2) - Cancel button. Return 1 (default) to close the dialog, or 0 to not close.

 

CBN_SELENDOK - selected combo box item. To determine which item is selected, use CB_SelectedItem.

 

LBN_SELCHANGE - selected list box item. To determine which item is selected, use LB_SelectedItem.

 

The case statements that handle WM_COMMAND notification codes may include notification code and control id (e.g. case CBN_SELENDOK<<16|3). Since BN_CLICKED is 0, it can be omitted (case 3).

 

Menus also send WM_COMMAND with notification code 0 (same as BN_CLICK). Accelerators (in QM only) and toolbar buttons too. The low-order word is the menu item id (or accelerator id, or toolbar button id). It means that you can handle menu item clicks, accelerators and toolbar button clicks in the same way as button clicks. It also means that menu item ids should not be the same as button ids, unless you need that they would behave identically.

 

 

Parameters and the return value of dialog procedure

 

Parameters:

hDlg - dialog window handle. In dialog procedure always use hDlg, not win("Dialog name"). Example: int hwndControl=id(3 hDlg).

message - an integer value of the message, for example WM_INITDIALOG.

wParam, lParam - two integer values that depend on message.

 

The dialog procedure can return:

 

How to get/set control values in dialog procedure

The control variables are used before and after ShowDialog, but not in the dialog procedure.

 

To get/set control values in dialog procedure, you can use functions from the control category. For example, str.getwintext/setwintext to get/set Edit control text; but to click button, check/uncheck or get checked; CB_SelectedItem to get combo box selected item index. Also you can send control messages with SendMessage. Control messages are documented in the MSDN Library.

 

Alternatively use function DT_GetControl to get control data in the format described in the How to get data from controls chapter. You can use function DT_GetControls to get data from multiple controls. Use function DT_SetControl or DT_SetControls to set control data.

 

More info

Usually don't need to add code to close dialog. It is automatically closed on OK and Cancel, unless you return 0 on IDOK/IDCANCEL. To close explicitly from the dialog procedure, use clo hDlg or DT_Cancel (same as pressing Cancel) or DT_Ok (same as pressing OK). Don't use EndDialog and DestroyWindow. To close a dialog from outside if you don't have its handle, use clo like when you close any other window.

 

There are more functions that can be used in dialog procedures. Their names begin with DT_. They are in category _other.__in_dlgproc. For example, _other.__in_dlgproc.DT_SetAutoSizeControls.

 

Examples

Dialog examples and tutorials

 

Also you can look for examples in "\System\Tools" folder. Code toolbar dialogs are implemented there. They use several private functions, usually their names begin with TO_. Don't use the private functions in your macros, because they may be changed or deleted in the future, but you can make their copies. You can use public functions from folders "\System\Functions", "\System\Dialogs\Dialog Functions" and "\System\Dialogs\Dialog Control Classes". Also you can use private functions that don't show warning "private System function X in user code...".

Multi-page dialogs

QM dialogs don't have support for real property pages (child dialogs), but you can easily show and hide groups (pages) of related controls so that only one page would be visible at a time. In the Dialog Editor, use buttons < and > to switch pages and create new pages. Controls with id 1 to 999 are always visible (common). Controls with id greater or equal 1000 belong to pages that can be easily shown and hidden. Each page can have maximum 100 controls. For example, controls in page 0 have id 1000 to 1099, controls in page 1 have id 1100 to 1199, and so on.

 

To switch pages at run time, in the dialog procedure call function DT_Page.

 

function hDlg index [~map]

 

hDlg - hDlg.

index - 0-based page index. Use -1 to hide all pages.

map - can be used to map index to different page.

Multiple dialogs in a macro

A macro can contain more than 1 dialog. Use sub-functions for it.

 

 \Dialog_Editor

str dd=
 BEGIN DIALOG
 ...
if(!ShowDialog(dd &sub.DlgProc 0)) ret
 ...


#sub DlgProc
function# hDlg message wParam lParam
 ...


#sub Dialog2
function# [hwndOwner]

str dd=
 BEGIN DIALOG
 ...
if(!ShowDialog(dd &sub.DlgProc2 0 hwndOwner)) ret
 ...


#sub DlgProc2
function# hDlg message wParam lParam
 ...

 

In this example there are 2 dialogs. One in the parent function and one in sub-function Dialog2. When opening the Dialog Editor, you can choose which dialog to edit. If a sub-function or parent function contains multiple dialogs, the Dialog Editor uses the first dialog in it (the first dialog definition, ShowDialog statement, variable declarations, and the first found dialog procedure below it).

Unicode

QM 2.3.0 and later versions support Unicode. To enable it for whole program, check the checkbox in Options. To enable it for a dialog, select "Unicode" or "As QM" in dialog editor Options. By default, dialogs work in ANSI mode, like in previous QM versions.

 

If QM is running in Unicode mode, most controls work well with any text regardless of dialog Unicode mode. To use Unicode text with rich edit controls, either set Unicode mode for the dialog, or use control of class RichEdit20W or RichEdit50W instead of RichEdit20A.

 

When Unicode is enabled for a dialog, its dialog procedure receives Unicode versions of some messages, particularly those that can pass or query text. Then the text is in Unicode UTF-16 format. QM string functions don't understand UTF-16. You can use str.ansi to convert the text to UTF-8 or ANSI. The _unicode variable tells you in which mode QM is running.

 

For example, in a dialog procedure of a Unicode dialog, lParam of WM_SETTEXT is UTF-16 text. If the dialog is ANSI, it is ANSI text. Common controls (tree view, list view, etc) have their own Unicode mode, which is enabled in Unicode dialogs, and disabled in ANSI dialogs. In Unicode mode they send different notification messages (codes used with WM_NOTIFY message). For example, in Unicode mode a tree view control sends TVN_SELCHANGEDW instead of TVN_SELCHANGED. Control Unicode mode can be turned on/off using CCM_SETUNICODEFORMAT message, documented in MSDN library.

Fonts and colors

QM 2.2.1. Use class __Font. Example.

Function Dialog_font_sample

\Dialog_Editor
function# hDlg message wParam lParam
if(hDlg) goto messages

str controls = "6 3"
str lb6 e3
e3="Text"
lb6="item1[]item2"
if(!ShowDialog("Dialog_font_sample" &Dialog_font_sample &controls)) ret

 BEGIN DIALOG
 0 "" 0x10C80A44 0x100 0 0 173 99 "Dialog Fonts"
 4 Static 0x54000000 0x4 10 22 48 14 "Text"
 6 ListBox 0x54230101 0x200 104 20 60 34 ""
 3 Edit 0x54030080 0x204 10 38 82 16 ""
 1 Button 0x54030001 0x4 4 82 48 14 "OK"
 2 Button 0x54030000 0x4 56 82 48 14 "Cancel"
 5 Button 0x54020007 0x4 4 6 166 54 "Text"
 END DIALOG
 DIALOG EDITOR: "" 0x2020103 "*" "" ""

ret
 messages
sel message
	case WM_INITDIALOG
	__Font-- f
	f.Create("Courier New" 14 1)
	f.SetDialogFont(hDlg "3-5")
	f.SetDialogFontColor(hDlg 0xff0000 "3 4")
	
	__Font-- f2
	f2.Create("Comic Sans MS" 7 2)
	f2.SetDialogFont(hDlg "2")
	
	__Font-- f3
	f.SetDialogFontColor(hDlg 0x008000 "6")
	
	 note: __Font variables must not be local because they must exist while the dialog is open.
	 note: in a dialog, use different __Font variables for different fonts.

	case WM_DESTROY
	case WM_COMMAND goto messages2
ret
 messages2
sel wParam
	case IDOK
	case IDCANCEL
ret 1

 

Menus and accelerators (hotkeys)

ShowDialog has an optional menu parameter. It can be the name of a macro (or item of other type) that contains menu definition. It also can be menu definition itself.

 

To create menu definitions, use the Menu Editor. You can find it in code toolbar -> More tools.

 

Example menu definition:

 

 BEGIN MENU
 >&File
 	&Open : 101 0 0 Co
 	-
 	>&Recent
 		Empty : 102 0 3
 		<
 	<
 >&Edit
 	Cu&t : 103 0 0 Cx
 	-
 	Select &All : 104 0 0 Ca
 	<
 &Help : 105 0
 END MENU

 

Menu definition format

You can see that it is similar to QM menus. A single line (except <), creates a menu item. Items that open popup menus or submenus begin with >. They usually don't have an id and accelerator. The end of a popup menu or submenu is marked by < line. Hyphen is used for separators. A line can optionally begin with any number of spaces and tabs.

 

Syntax used for menu items:

 

Label : id [type] [state] [accelerator]

 

label - menu item text. Use ampersand to underline characters. Can be used escape sequences (not in Menu Editor).

id - menu item id. Should be 0 to 65535 (0xFFFF). When you click the menu item, the dialog procedure receives WM_COMMAND message that can be handled in the same way as it would be a button with the id. Use bigger values (e.g. above 10000) to avoid conflicts with buttons.

type - numeric combination of menu item types. Normally it is omitted or 0.

state - numeric combination of menu item states. Useful states: 3 disabled, 8 checked.

accelerator - a hotkey in QM format (same as with key). The hotkey string is appended to the menu item label automatically, unless label contains a tab character.

 

id, type and state must be single numbers, without operators and named constants. More info about menu item types and states can be found in MSDN Library (search for MENUITEMINFO structure).

 

Other info

In exe also can be used menus from resources. The menu argument of ShowDialog must be semicolon followed by resource id, e.g. ":15". If the resource contains accelerator table with the same id, the table is used for accelerators, although accelerator text is not added (must be included in label text, after tab).

 

To change or remove menu at run time, call DT_SetMenu in dialog procedure. To create or load menu, use DT_CreateMenu.

 

Menu bars and accelerators can be used with modal and modeless dialogs, but not with child dialogs.

 

QM 2.4.2. Menu definitions also can be used to show popup menus. Use ShowMenu or MenuPopup.Create.

 

QM 2.4.2. You can set menu icons with DT_SetMenuIcons.

Common controls, other controls

The Dialog Editor and dialog functions support controls of the following classes: Button (push button, checkbox, group), Edit, RichEdit20A, RichEdit20W, Static (static text, bitmap, icon, line), ComboBox, LixtBox and QM_Grid. Working with controls of these classes is easy. You can initialize them before you call ShowDialog, and get data (text, checked, etc) from them when dialog is closed.

 

Dialogs can contain controls of other classes too. For example, TreeView32, SysTabControl32. However, only programmers can use them. The dialog must be a "smart" dialog, otherwise the controls will be useless. To interact with these controls, are used various messages (with SendMessage). To notify the dialog about various events, these controls usually send WM_NOTIFY message, not WM_COMMAND. Messages, styles and other information can be found in MSDN library. If you don't know the value of a constant, click it, press F1, and search for definition on the Internet.

 

Several samples can be found in the forum. Search for the class name.

ActiveX controls

Controls that you usually use in dialogs are Windows controls. Also you can use ActiveX controls. ActiveX controls are COM objects that provide graphical user interface. They are used differently than Windows controls. To work with Windows controls, are used messages. To work with ActiveX controls, are used functions that they provide.

 

To insert an ActiveX control, click "ActiveX controls..." in the Dialog Editor. It opens the COM Libraries dialog that displays available (registered) ActiveX controls. Select the control you need and click Add Control To Dialog.

 

ActiveX controls are defined in type libraries, as COM classes. To use an ActiveX control in a dialog, at first declare the type library where the control class is defined. To insert type library declarations, use the COM Libraries dialog. When adding a control to a dialog, QM prompts to insert type library declaration if it still does not exist in current macro. Several type libraries, including SHDocVw (web browser control) are already declared.

 

You can read more about type libraries and ActiveX controls in the Using COM Components topic.

 

When you have an ActiveX control created, you usually need some way to communicate with it. For this purpose, declare a variable of that class and call _getcontrol. Pass handle of ActiveX control's host window (which itself is a Windows control of "ActiveX" class). Then you can call functions with the variable to manipulate the control. To receive events, use _setevents. The variable must be local (not global or thread). Call _getcontrol every time before using it.

 

Example:

 

...
sel message
	case WM_INITDIALOG ;;dialog created
	SHDocVw.WebBrowser b
	b._getcontrol(id(3 hDlg))
	b._setevents("sub.b")
	b.Navigate("http://www.google.com")
...
	case 4 ;;Back button
	b._getcontrol(id(3 hDlg))
	b.GoBack
...

 

There is a sample dialog with web browser control in the forum.

 

You cannot use str.setwintext with ActiveX controls, except web browser control. If the text is URL or path of a html, image or other file, web browser control opens that web page or file. Does not wait until finished loading. Sets silent mode to avoid script error messages. If the text is "", loads "about:blank" or clears the control. To load HTML, use function HtmlToWebBrowserControl.

 

ActiveX controls can be used in other windows (non-dialog) too. To create an ActiveX control, use CreateControl or CreateWindowEx. Class name and text must be like in dialog definitions: class "ActiveX", text like "Typelib.Class {GUID}".

 

QM supports run-time licensing of ActiveX controls. It allows you to add purchased ActiveX controls to macros and exes and distribute these macros and exes. When you insert an ActiveX control in the Dialog Editor using the COM Libraries dialog, QM automatically attaches encrypted run-time license key, if the control can generate it. With attached run-time license key, the control can be used on any computer. If you distribute macro (not exe), you should encrypt the macro that contains the dialog definition to prevent others to reuse the run-time license key in their macros. See Encrypted Macros. Even if the macro is not encrypted, the key can only be used in Quick Macros, because only QM can decrypt the key. Also, others will not be able to create exes with the key. Note that some controls may use different ways to verify license. For example, you may have to call certain function and pass the license key.

 

Some ActiveX controls don't work well in QM. This is because they are designed for full-featured containers, such as Visual Basic forms, or only for certain programs. At design time, you can only move and resize ActiveX controls (or control placeholders). Properties can be changed only at run time.

 

Many other COM components are not controls, and must be created with _create or other functions instead. Such components usually are in dll files. Controls usually are in ocx or dll files.

 

QM 2.3.4. It is possible to use some ActiveX controls even if the COM component is not registered on that computer. In dialog definition add full path of the COM component (dll or ocx) to the ActiveX text string, like "Typelib.Class {CLSID} ''dll:$qm$\file.ocx''". If the file not found, tries to create as registered. In exe, if path begins with "$qm$", searches for the dll in exe folder; if not found there, searches in QM folder if QM is installed on that computer.

 

QM 2.3.5. To create COM components without registration, you can use manifest and __ComActivator class.

 

See also: Using COM components.

Encrypted macros

If you encrypt macro that contains dialog definition, the dialog will not work unless the dialog definition is in a variable. Examples:

 

This macro will not work encrypted.

 BEGIN DIALOG
 0 "" 0x90C80AC8 0x0 0 0 224 136 "Dialog"
 1 Button 0x54030001 0x4 116 116 48 14 "OK"
 2 Button 0x54030000 0x4 168 116 48 14 "Cancel"
 END DIALOG
 DIALOG EDITOR: "" 0 "*" "" "" ""

if(!ShowDialog("This macro" 0 0)) ret

 

This macro will work always.

str dd=
 BEGIN DIALOG
 0 "" 0x90C80AC8 0x0 0 0 224 136 "Dialog"
 1 Button 0x54030001 0x4 116 116 48 14 "OK"
 2 Button 0x54030000 0x4 168 116 48 14 "Cancel"
 END DIALOG
 DIALOG EDITOR: "" 0 "*" "" "" ""

if(!ShowDialog(dd 0 0)) ret

 

The same is with menu definitions. Use a variable if want to encrypt the dialog.

Dialogs now and before QM 2.1.9

In QM versions < 2.1.9, the dialog procedure must call DT_Init on WM_INITDIALOG, DT_DeleteData on WM_DESTROY, DT_Ok on OK, and DT_Cancel on Cancel. Starting from QM 2.1.9, these functions are not necessary. Now you have to use them only if the dialog will be used by someone that has an older QM version (this does not apply to exe-macros). Also, to avoid any problems, you should leave these functions in dialogs where they already exist.

 

A note about IDOK and IDCANCEL. Before QM 2.1.9, DT_Ok and DT_Cancel functions were used to close the dialog. If they were not called, the dialog was not closed. Now dialogs are closed automatically. To prevent this, return 0 on IDOK or IDCANCEL. For backward compatibility, a dialog also is not closed automatically if it calls DT_Init on WM_INITDIALOG (it is the way QM recognizes old dialogs).

Dialog examples and tutorials

Search for ShowDialog or dialog in the forum.

 

Several topics to get started:

 

Tips how to copy/paste QM code in the forum, and how to use dialog examples.

 

A tutorial for a simplest dialog with checkboxes.

 

Running and ending macros from a dialog (without closing the dialog).

 

Executing code from a dialog with a combo box (without closing the dialog). An example with a list box.

 

Collection of dialogs and other code

Dialog programming info at MSDN

Dialog box programming info at MSDN

 

Dialog programming in QM is similar to dialog programming in C++ using Windows API, as documented in MSDN. In QM it is simplified. When you know how dialogs work at Windows API level (dialog procedure, messages, notification messages, etc), you can also understand how it works in QM.