1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
2<html>
3
4<head>
5<title>Windows Programming in Poly/ML</title>
6</head>
7
8<body>
9
10<h1 align="center">Windows Programming in Poly/ML</h1>
11
12<h2 align="center">David C.J. Matthews</h2>
13
14<h5 align="center">David C.J. Matthews 2001. Updated 2009.</h5>
15
16<p>This is a brief introduction to programming with the Windows&#153; interface in
17Poly/ML.&nbsp; It is arranged as a tutorial around the <a href="mlEdit.html">mlEdit</a>
18example, a small example text editor.&nbsp; It is not intended as a full description of
19writing programs for Windows and is no substitute for a more general guide.&nbsp; If you
20are planning to develop an application using this interface it is probably worth
21purchasing a book or CD_ROM reference for Windows such as the Microsoft Developer Network
22library.</p>
23
24<p>This introduction assumes some familiarity with programming in ML and perhaps some
25experience of other windowing systems, such as X-Windows.&nbsp; It does not assume any
26experience of programming with Windows and, for experienced Windows programmers, it will
27cover familiar ground.&nbsp; It does, though, point out some of the differences between
28the ML interface and that of other languages.&nbsp; The interface functions in ML are very
29similar to the underlying C functions.&nbsp; The main differences are in the types of the
30arguments and a few other changes necessary for ML.&nbsp;&nbsp; For more information see
31the <a href="../Winref/Reference.html">Windows Interface Reference.</a></p>
32
33<h3>Acknowledgements</h3>
34
35<p>The original version of the Windows interface was written at AHL by Panos Aguieris.&nbsp; 
36  It uses the Poly/ML <a href="../Reference/Foreign.html">Foreign structure</a> 
37  to provide nearly all the interface.&nbsp; It was extensively modified and expanded 
38  by David Matthews.</p>
39
40<h3>Index</h3>
41
42<p><a href="#Windows">1. Windows, Messages and Window Procedures</a><br>
43<a href="#Child">2. Creating the child window</a><br>
44<a href="#Parent">3. Creating the parent window</a><br>
45<a href="#Menus">4. Menus</a><br>
46<a href="#Sending">5. Sending Messages</a><br>
47<a href="#Dialogues">6. Dialogues</a><br>
48<a href="#Common">7. Common Dialogues</a><br>
49<a href="#Printing">8. Printing and Painting</a></p>
50
51<h3><a name="Windows">1. Windows, Messages and Window Procedures</a></h3>
52
53<p>When thinking about a window we tend to think of a top-level application or a document
54window.&nbsp; From the point of view of programming, though, this is just one variety.
55&nbsp; In fact each label, menu and text box is a window but because they form part of a
56bigger application they tend to be overlooked.&nbsp; These are <em>child windows</em>.
57&nbsp; Nearly every window has to deal with two main areas of concern.&nbsp; There has to
58be a way of displaying the contents of the window and there has to be a way of handling
59user input, whether by the mouse or from the keyboard.</p>
60
61<p>Most communication with windows is by sending <em>messages</em>.&nbsp; A message may be
62sent as a result of user input, such as moving the mouse or typing a character at the
63keyboard.&nbsp; It may also be sent by the application to change the appearance of the
64window or to get information from it.&nbsp; There are over two hundred different message
65types defined by Windows and it is possible to define your own for use within an
66application.&nbsp; Many of these are only relevant in special circumstances and only a few
67are used regularly.&nbsp; It's worth noting in passing that messages can be sent from one
68process to a window in another process and that <em>hidden windows</em>, windows which
69don't appear on the screen but are only ever used to receive messages, are one of the
70standard ways of communicating between processes in Windows.</p>
71
72<p>The appearance of a window is largely governed by how it responds to different kinds of
73messages.&nbsp; Every window has a <em>window procedure</em> which processes messages sent
74to the window.&nbsp; When you create a window you give the <em>class</em> of the window
75you want to create and the new window uses the window procedure for that class.&nbsp; You
76can either use a standard class, such as <a href="../Winref/Class.html#Edit">Edit</a>, or you
77can create your own class using your own window procedure using <a href="../Winref/Class.html#RegisterClassEx">RegisterClassEx</a>.&nbsp; You will nearly always
78need to create at least one class for your top-level window.&nbsp; </p>
79
80<p>To see how this works in practice let's look at an extract from the mlEdit example.
81&nbsp; </p>
82
83<pre>fun wndProc(hw: HWND, msg: Message, state as SOME{edit, fileName, ...}) =
84    case msg of
85    |   WM_CLOSE =&gt;
86        (if checkForSave(hw, edit, fileName) then DefWindowProc(hw, msg) else LRESINT 0, state)
87    .....
88    |   _ =&gt; (DefWindowProc(hw, msg), state)
89    .....
90and fun checkForSave ....</pre>
91<p>This extract from the window procedure, wndProc, processes the <a href="../Winref/Message.html#WM_CLOSE">WM_CLOSE</a> 
92  message. &nbsp; This message has no parameters and is generated by the system 
93  when the user clicks in the close box of the window (the box at the top left 
94  of the window with a cross in it). &nbsp; Every message is sent to the window 
95  procedure.&nbsp; When writing a window procedure we are often only interested 
96  in handling a few of the possibilities so Windows performs a default action 
97  if we don't want to handle the message.&nbsp; Even if we do handle it we can 
98  pass it on for default processing when we have dealt with it.&nbsp; The default 
99  action for WM_CLOSE is to destroy the window by a call to <a href="../Winref/Window.html#DestroyWindow">DestroyWindow</a>.&nbsp; 
100  In our application we want to ask the user whether to save the window if the 
101  text being edited has been modified.&nbsp; This process also gives the user 
102  the opportunity for second thoughts and they may cancel the closure.&nbsp; This 
103  is handled by our function checkForSave which returns true if it is safe to 
104  close the window. &nbsp; Window procedures in ML return a pair as their result.&nbsp; 
105  The first field of the pair is the Windows result value.&nbsp; For many messages 
106  a result of LRESINT 0 is suitable.&nbsp; In this example we return SOME(LRESINT 
107  0) if we don't want the window to be destroyed.&nbsp; </p>
108
109<p>The full type of a window procedure in ML is<br>
110  &nbsp;&nbsp;&nbsp; <tt>HWND * Message * 'a -&gt; LRESULT * 'a</tt><br>
111The third argument is the <em>state</em> of the window and an updated version of the state
112is returned as part of the result.&nbsp; This allows the window procedure to process a
113message and update the state as part of the process.&nbsp; It is an ML extension which is
114not part of the underlying message system.&nbsp; In our extract we return the previous
115state unchanged.</p>
116
117<p>The mlEdit example works by constructing a parent window using a custom class and our
118own window procedure and then making a child window within it using the system Edit class.
119&nbsp; The Edit window deals with all the keyboard input so saving us the need to write
120this ourselves.&nbsp; Our window procedure has to process messages to do with menu
121selection and other messages sent to top-level windows.&nbsp; </p>
122
123<pre>    WM_SETFOCUS _ =&gt;
124        (
125         SetFocus(SOME edit);
126         (DefWindowProc(hw, msg), state)
127        )
128    
129|    WM_SIZE{height, width, ...} =&gt;
130        (
131         MoveWindow{hWnd=edit, x=0, y=0, height=height, width=width, repaint=true};
132         (DefWindowProc(hw, msg), state)
133        )
134</pre>
135<p>The <a href="../Winref/Message.html#WM_SETFOCUS">WM_SETFOCUS</a> message is sent when the
136user clicks on a window and is intended to set the keyboard focus to that window (i.e.
137selects the window to receive keyboard input).&nbsp; The edit child window occupies the
138centre of our window so the WM_FOCUS message will be sent directly to it if the user
139clicks within that area.&nbsp; Since we are not interested in receiving characters in our
140top-level window we set the focus to the edit window if the user clicks on the border.
141&nbsp; While it isn't essential to process this message at all for the mlEdit application
142to work correctly, by doing so we improve its usability.</p>
143
144<p>The <a href="../Winref/Message.html#WM_SIZE">WM_SIZE</a> message is sent if the window is
145resized.&nbsp; Among the parameters to the window are the height and width of the <em>client
146area</em>, the area excluding the border and the menu bar.&nbsp; This is the area we want
147to use for our edit window so we use <a href="../Winref/Window.html#MoveWindow">MoveWindow</a>
148to set its size.&nbsp; Calling MoveWindow causes the edit window to receive a WM_SIZE
149message.&nbsp; The window procedure for the edit window uses this to adjust the format of
150the text within the window to suit the new size.&nbsp; We do not need to be concerned
151about how this happens in our application.&nbsp; WM_SIZE is also sent when a window is
152created.&nbsp; This is convenient and means we do not need to set the size of the edit
153window when we create it.</p>
154
155<h3><a name="Child">2. Creating the child window</a></h3>
156
157<p>So far we have not described how the edit window is created.&nbsp; We need it to be a
158child window of the top-level window and the easiest way to do that is to create it when
159the <a href="../Winref/Message.html#WM_CREATE">WM_CREATE</a> message is received.&nbsp; This
160message is sent as part of the process of creation of the top-level window.</p>
161
162<pre>fun wndProc(hw: HWND, msg: Message, NONE) =
163    (
164    case msg of
165        WM_CREATE _ =&gt; (* Create an edit window and return it as the state. *)
166        let
167            val edit =
168             CreateWindow{class = Class.Edit, name = &quot;&quot;,
169                style = Edit.Style.flags[Edit.Style.WS_CHILD, Edit.Style.WS_VISIBLE, Edit.Style.WS_VSCROLL,
170                            Edit.Style.ES_LEFT, Edit.Style.ES_MULTILINE, Edit.Style.ES_AUTOVSCROLL],
171                            x  = 0, y = 0, height = 0, width = 0, relation = ChildWindow{parent=hw, id=99},
172                            instance = Globals.ApplicationInstance(), init = ()}</pre>
173<pre>            (* We also set the font for the edit window here.  This has been omitted. *)
174         in
175            (LRESINT 0, SOME{edit=edit, devMode=NONE, devNames = NONE, fileName=&quot;&quot;})
176        end
177
178    | _ =&gt; (DefWindowProc(hw, msg), NONE)
179    )</pre>
180
181<p>This extract of the window procedure shows the creation of the edit window as part of
182processing WM_CREATE.&nbsp; We use <a href="../Winref/Window.html#CreateWindow">CreateWindow</a>
183which makes a window of a specified class and returns a handle to it.&nbsp; Once we have
184processed the message we set the state to SOME of a record containing a handle to the edit
185window.&nbsp; (Instead of doing this we could use <a href="../WinRef/Dialog.html#GetDlgItem">GetDlgItem</a>
186to find it each time we needed it, but this is easier).&nbsp; At this stage we just pass
187zeros for the size of the window since we will use the WM_SIZE message to set its size.
188&nbsp; The id value for the child window (we use 99) is irrelevant in this example since
189we never use it.&nbsp; The style does not include horizontal scrolling so the edit window
190uses word-wrapping. </p>
191
192<h3><a name="Parent">3. Creating the parent window</a></h3>
193
194<p>It's now time to see how we create the main window itself.&nbsp; Since we want to use
195our own window procedure we need to register a class even though we will only create a
196single window of this class.</p>
197
198<pre>val polyIcon = ...
199val menu = ...
200val className = &quot;mlEditWindowClass&quot;
201val app = Globals.ApplicationInstance()
202(* Register a class for the top-level window.  Use the Poly icon from the application. *)
203val myWindowClass = RegisterClassEx{style = Class.Style.flags[], wndProc = wndProc, hInstance = app,
204    hIcon = SOME polyIcon, hCursor = NONE, hbrBackGround = NONE, menuName = NONE,
205    className = className, hIconSm = NONE};
206
207
208val w = CreateWindow{class = myWindowClass, name = &quot;mlEdit&quot;, style = Window.Style.WS_OVERLAPPEDWINDOW,
209    x  = CW_USEDEFAULT, y = CW_USEDEFAULT, height = CW_USEDEFAULT, width = CW_USEDEFAULT,
210    relation = PopupWindow menu, instance = app, init = NONE};
211in
212ShowWindow(w, SW_SHOW);
213SetForegroundWindow w;
214
215RunApplication();
216UnregisterClass(className, app)
217end;</pre>
218
219<p>In the mlEdit example we use the icon from the Poly/ML application.&nbsp; It isn't
220necessary to provide an icon: Windows will provide a default one if NONE is given for
221hIcon.&nbsp; We use the WS_OVERLAPPEDWINDOW style for the window which is the standard for
222a top-level window and gives a standard border with a system menu and minimise, maximise
223and close boxes.&nbsp; CW_USEDEFAULT is used for the size and position of the window.
224&nbsp; The user can always move or resize it once it has been created.&nbsp;&nbsp; We
225create the window with the PopupWindow value and pass the menu to be used.&nbsp; Once the
226window has been created we call ShowWindow to make it visible and SetForegroundWindow to
227make it appear above other windows.</p>
228
229<p>Some messages are sent to the window procedure as a result of calling the functions
230such as CreateWindow.&nbsp; Generally, though, messages are queued and have to be
231explicitly dequeued and passed to the window procedure.&nbsp; In C this is done by a
232message loop with GetMessage and DispatchMessage.&nbsp; In ML we use the <a href="../WinRef/Message.html#RunApplication">RunApplication</a> function which deals with all
233this.&nbsp; RunApplication returns when a <a href="../WinRef/Message.html#WM_QUIT">WM_QUIT</a>
234message is received.&nbsp; To ensure that this happens we have to put this message into
235the queue.&nbsp; The easiest way to do this is to call PostQuitMessage when the window is
236about to go away.&nbsp; We have already seen how DestroyWindow is called as a result of
237processing the WM_CLOSE message.&nbsp; DestroyWindow sends <a href="../WinRef/Message.html#WM_DESTROY">WM_DESTROY</a> and <a href="../WinRef/Message.html#WM_NCDESTROY">WM_NCDESTROY</a> messages to the window procedure
238to allow it to clean up properly.&nbsp; WM_NCDESTROY will be the last message to be sent
239to this window procedure so we call PostQuitMessage while handling it.</p>
240
241<pre>|    WM_NCDESTROY =&gt;
242        (
243         PostQuitMessage 0;
244         (DefWindowProc(hw, msg), state)
245        )</pre>
246<p>We need to call <a href="../WinRef/Class.html#UnregisterClass">UnregisterClass</a> when
247RunApplication returns.&nbsp; Classes are automatically unregistered when the application
248terminates but in this context the application is the whole Poly/ML session so if we want
249to run mlEdit again within the same session we need to unregister the class. &nbsp;
250Otherwise the next time we call RegisterClassEx with the same name it will fail because
251the class is already registered.</p>
252
253<h3><a name="Menus">4. Menus</a></h3>
254
255<p>We have already seen that a menu can be set up by passing its handle as part of the
256PopupWindow value to CreateWindow.&nbsp; Let's see how the menu itself is created and how
257it is processed.</p>
258
259<p>There are a number of ways a menu can be created but perhaps the simplest is to build
260it up using calls to <a href="../WinRef/Menu.html#CreateMenu">CreateMenu</a> and <a href="../WinRef/Menu.html#AppendMenu">AppendMenu</a>.&nbsp; The structure we want for our
261menu is a bar containing File, Edit and Help menus each with several menu items.&nbsp; In
262general a menu consists of multiple items, each of which may either be a command item or
263may pull up a sub-menu.</p>
264
265<pre>val menuOpen = 1
266and menuQuit = 2
267and menuSave = 3
268...</pre>
269
270<pre>val fileMenu =
271    let
272        val fileMenu = CreateMenu();
273    in
274        AppendMenu(fileMenu, [], MenuId menuOpen, MFT_STRING &quot;&amp;Open&quot;);
275        AppendMenu(fileMenu, [], MenuId menuSave, MFT_STRING &quot;&amp;Save&quot;);
276        AppendMenu(fileMenu, [], MenuId menuSaveAs, MFT_STRING &quot;Save &amp;As...&quot;);
277        AppendMenu(fileMenu, [], MenuId 0, MFT_SEPARATOR);
278        AppendMenu(fileMenu, [], MenuId menuPageSetup, MFT_STRING &quot;Page Set&amp;up...&quot;);
279        AppendMenu(fileMenu, [], MenuId menuPrint, MFT_STRING &quot;P&amp;rint...&quot;);
280        AppendMenu(fileMenu, [], MenuId 0, MFT_SEPARATOR);
281        AppendMenu(fileMenu, [], MenuId menuQuit, MFT_STRING &quot;&amp;Quit&quot;);
282        fileMenu
283    end;</pre>
284
285<p>We create the file menu by calling CreateMenu and then AppendMenu for each item. &nbsp;
286Every item, apart from separators, has a different item ID.&nbsp; The values are arbitrary
287but we need to use different values for each because these are the values which will be
288passed to our window procedure when a particular menu item is selected. &nbsp; Separators,
289which appear as horizontal lines when the menu is pulled down, are used to improve the
290layout.&nbsp; The ampersands (&amp;s) precede the character which will be underlined in
291the menu.&nbsp; These provide keyboard shortcuts for menu items.&nbsp; Typically the first
292character is used but sometimes we have to use another character in order to give all the
293items in a menu different characters.</p>
294
295<pre>val menu = CreateMenu();
296val _ = AppendMenu(menu, [], MenuHandle fileMenu, MFT_STRING &quot;&amp;File&quot;);
297val _ = AppendMenu(menu, [], MenuHandle editMenu, MFT_STRING &quot;&amp;Edit&quot;)
298val _ = AppendMenu(menu, [], MenuHandle helpMenu, MFT_STRING &quot;&amp;Help&quot;)</pre>
299
300<p>We can create the edit and help menus in exactly the same way.&nbsp; When they have
301been created we can build the full menu.&nbsp; The argument to AppendMenu is MenuHandle
302rather than MenuId since these are sub-menus.</p>
303
304<p>As with all other input selecting a menu item causes the system to send a message to
305the window.&nbsp; It sends a <a href="../WinRef/Message.html#WM_COMMAND">WM_COMMAND</a>
306message with information about the particular menu item.&nbsp; The wId value in the
307messagecontains the identifier which was used when the menu was created.&nbsp; </p>
308
309<pre>|    WM_COMMAND{notifyCode = 0, wId, control} =&gt;
310
311        if wId = menuQuit
312        then
313        (
314        if checkForSave(hw, edit, fileName) then DestroyWindow hw else();
315        (LRESINT 0, state)
316        )
317        else ...</pre>
318
319<p>The simplest item to process is when Quit is selected.&nbsp; We process it in almost
320the same way as we process WM_CLOSE, except in this case we have to explicitly call
321DestroyWindow since the default action for this message is to do nothing.&nbsp; Note that
322since we process WM_NCDESTROY by calling PostQuitMessage the message loop in
323RunApplication will exit when the window destruction is complete.</p>
324
325<h3><a name="Sending">5. Sending Messages</a></h3>
326
327<p>So far we have seen how we process messages but not how our application can send them.
328&nbsp; An application sends messages using either <a href="../WinRef/Message.html#SendMessage">SendMessage</a> or <a href="../WinRef/Message.html#PostMessage">PostMessage</a>. &nbsp; The main difference between
329them is that SendMessage does not return until the window procedure has processed the
330message and so it is able to return a result to the caller.&nbsp; It functions essentially
331as a, possibly remote, procedure call.</p>
332
333<p>We can use SendMessage to process some of the menu items.</p>
334
335<pre>        else if wId = menuCut
336        then (SendMessage(edit, WM_CUT); (LRESINT 0, state))
337        else if wId = menuCopy
338        then (SendMessage(edit, WM_COPY); (LRESINT 0, state))
339        else if wId = menuPaste
340        then (SendMessage(edit, WM_PASTE); (LRESINT 0, state))</pre>
341
342<p>The Cut, Copy and Paste items from the edit menu are handled by sending messages to the
343edit window.&nbsp; The edit window processes these by copying data to and from the
344clipboard.&nbsp; In this example we are not interested in the result that SendMessage
345returns but we use it rather than PostMessage to ensure that the command is fully
346processed before, for example, we accept any characters as input.</p>
347
348<p>Another case where we can use SendMessage is to see whether the text has been modified
349and so whether we need to save it before quitting.</p>
350
351<pre>fun checkForSave(hw, edit, fileName) =
352    case SendMessage(edit, EM_GETMODIFY) of
353            LRESINT 0 =&gt; true (* Unmodified - continue. *)
354        |    _ =&gt; ... (* Save it. *)</pre>
355
356<p>We send the edit window an EM_GETMODIFY message.&nbsp; If the reply is LRESINT 0 (i.e.
357false) it has not been modified and we don't need to do anything.&nbsp; Otherwise we need
358to save the document, or at least ask whether we should save it.</p>
359
360<h3><a name="Dialogues">6. Dialogues</a></h3>
361
362<p>A dialogue box is a special kind of window which is used to present information to the
363user or to request information.&nbsp; They contain one or more <em>controls</em> and a
364button usually labelled OK and often a Cancel button.&nbsp; The simplest form of dialogue
365is the <em>message box</em>.&nbsp; This presents a piece of text and has one or more
366buttons. &nbsp; </p>
367
368<pre>let
369    val res =
370        MessageBox(SOME hw, &quot;Save document?&quot;, &quot;Confirm&quot;,
371                   MessageBoxStyle.MB_YESNOCANCEL)
372in
373    if res = IDYES
374    then .... (* Save document. *)  ...
375    else if res = IDNO
376    then true (* Continue anyway. *)
377    else false (* Cancel - don't exit or open. *)
378end</pre>
379
380<p>We use a message box in mlEdit to ask whether we should save the document if it has
381been modifed.&nbsp; The MB_YESNOCANCEL value for the style means that the message box will
382have three buttons: Yes, No and Cancel.&nbsp; The message box is an example of a <em>modal</em>
383dialogue.&nbsp; Modal dialogues are those which need a response before the application can
384continue.&nbsp; The application is disabled until the dialogue has been dismissed by
385clicking one of the buttons.&nbsp; In contrast a modeless dialogue acts just like another
386window and the user can interact with either the application window or the dialogue box.
387&nbsp; <a href="../WinRef/MessageBox.html#MessageBox">MessageBox</a> returns with an
388identifier which indicates the button which was pressed. &nbsp; If Yes, we need to save
389the document; if no, we don't.&nbsp; If Cancel was pressed we need to cancel the operation
390which caused this dialogue to be presented.&nbsp; For instance, if the user accidentally
391clicked on the Close box of the window and then pressed Cancel we do not pass the WM_CLOSE
392message to the default window procedure.&nbsp; It is good interface design practice to
393provide some way for the user to cancel an action.</p>
394
395<p>A more sophisticated dialogue is used when the user selects &quot;About mlEdit...&quot;
396from the Help menu.&nbsp; MessageBox is not sufficient for this dialogue since we want to
397include an icon in the dialogue box so we use one of the general dialogue functions.
398&nbsp; The layout of a dialogue can be given either in a <em>resource file</em> or as a <em>template</em>.
399&nbsp; Resource files are often a convenient way of holding dialogues as well as menus and
400other strings.&nbsp; They lend themselves particularly to producing <em>localized</em>
401versions of programs, i.e. programs with the user interaction tailored for a particular
402language and/or culture, since all the text that needs to be localized can be stored in
403the resource file.&nbsp; A resource file is either an executable file (.EXE) or a dynamic
404library (.DLL).&nbsp; To use a resource file you will need a suitable resource compiler
405which will normally form part of a development environment such as Microsoft Visual C or
406Borland C++.&nbsp; You can then load the resource file and get a handle to it using <a href="../WinRef/Resource.html#LoadLibrary">LoadLibrary</a>.&nbsp; It is possible to make use
407of resources in the Poly/ML program using the instance handle returned by <a href="../WinRef/Globals.html#ApplicationInstance">ApplicationInstance</a>.&nbsp; We actually
408do this in mlEdit to get a handle to the icon.</p>
409
410<pre>(* Borrow the Poly icon from the application program. It happens to be icon id 102. *)
411val polyIcon = Icon.LoadIcon(app, Resource.MAKEINTRESOURCE 102);</pre>
412
413<p>For this example, though, we use a template for the dialogue.</p>
414
415<pre>val pictureId = 1000 (* Could use any number here. *)
416open Static.Style
417val template =
418    {x = 0, y = 0, cx = 210, cy = 94, font = SOME (8, &quot;MS Sans Serif&quot;), menu = NONE,
419     class = NONE, title = &quot;About mlEdit&quot;, extendedStyle = 0,
420     style = flags[WS_POPUP, WS_CAPTION],
421
422     items =
423      [{x = 73, y = 62, cx = 50, cy = 14, id = 1,
424        class = DLG_BUTTON (flags[WS_CHILD, WS_VISIBLE, WS_TABSTOP]),
425        title = DLG_TITLESTRING &quot;OK&quot;, creationData = NONE, extendedStyle = 0},
426
427       {x = 7, y = 7, cx = 32, cy = 32, id = pictureId,
428        class = DLG_STATIC (flags[WS_CHILD, WS_VISIBLE, SS_ICON]),
429        title = DLG_TITLESTRING &quot;&quot;, creationData = NONE, extendedStyle = 0},
430
431       {x = 15, y = 39, cx = 180, cy = 21, id = 65535,
432        class = DLG_STATIC (flags[WS_CHILD, WS_VISIBLE, WS_GROUP]),
433        title = DLG_TITLESTRING
434               &quot;mlEdit - An exmple of Windows programming in Poly/ML\
435               \\nCopyright David C.J. Matthews 2001&quot;,
436        creationData = NONE,  extendedStyle = 0}] }</pre>
437
438<p>The dialogue contains three items: a button with the title &quot;OK&quot;, 
439  a static picture with style SS_ICON, and a piece of static text.&nbsp; Along 
440  with the layout of the dialogue we also need a dialogue procedure.&nbsp; A dialogue 
441  procedure is almost the same as a window procedure.&nbsp; It processes messages 
442  sent to the dialogue in the same way as a window procedure does.&nbsp;The only 
443  difference is that a dialogue procedure does not call DefWindowProc.</p>
444
445<pre>fun dlgProc(dial, WM_INITDIALOG _, ()) =
446    (
447        (* Send a message to the picture control to set it to this icon. *)
448        SendMessage(GetDlgItem(dial, pictureId), STM_SETICON{icon=polyIcon});
449        (LRESINT 1, ())
450    )
451
452|    dlgProc(dial, WM_COMMAND{notifyCode = 0, wId=1 (* OK button *), ...}, ()) =
453        (* When the OK button is pressed we end the dialogue. *)
454        (EndDialog(dial, 1); (LRESINT 1, ()) )
455
456|    dlgProc _ = (LRESINT 0, ())</pre>
457
458<p>We only process two messages here: WM_INITDIALOG and WM_COMMAND.&nbsp; WM_INITDIALOG is
459sent when the dialogue is created.&nbsp; We use GetDlgItem to get a handle to the static
460picture control and then send it a STM_SETICON message to set the picture.&nbsp; We only
461need to do this when the dialogue is initialised.&nbsp; The static control will take care
462of displaying the picture whenever the dialogue box is visible.&nbsp; WM_COMMAND messages
463are sent by buttons as well as by menus and in this example we process the message by
464calling EndDialog to close the dialogue box.&nbsp; The value of wId in this case is the
465identifier of the OK button.</p>
466
467<p>To construct and display the dialogue box we call DialogBoxIndirect with the template
468and the dialogue procedure.&nbsp;&nbsp; The parent of the dialogue is our main window
469(hw).&nbsp; This window will automatically be disabled until the dialogue box has been
470closed.</p>
471
472<pre>DialogBoxIndirect(app, template, hw, dlgProc, ());</pre>
473
474<h3><a name="Common">7. Common Dialogues</a></h3>
475
476<p>For many purposes a standard dialogue box can be used and Windows provides a number of
477these.&nbsp; For instance, most applications include a way for the user to open a file and
478it would be tedious to have to program this from scratch for each new application. &nbsp;
479It is far easier to use a standard dialogue.&nbsp; More to the point, by using a standard
480dialogue the interface to the user is similar to that of other applications, reducing the
481learning load.</p>
482
483<p>The mlEdit example uses a number of these.&nbsp; <a href="../WinRef/CommonDialog.html#GetOpenFileName">GetOpenFileName</a> and <a href="../WinRef/CommonDialog.html#GetSaveFileName">GetSaveFileName</a> are used to select the
484file to open and for the Save As menu item.&nbsp; <a href="../WinRef/CommonDialog.html#FindText">FindText</a> is used for the Find menu item.
485&nbsp; <a href="../WinRef/CommonDialog.html#PageSetupDlg">PageSetupDlg</a> and <a href="../WinRef/CommonDialog.html#PrintDlg">PrintDlg</a> are used for the Page Setup and
486Print items.&nbsp; They generally work in the same way, creating a modal dialogue
487requesting the information and returning when the OK or Cancel button is pressed. &nbsp;
488The ML functions take a configuration structure and return an option type.&nbsp; If the
489user cancels the operation they return NONE, if the user presses OK they return a new
490configuration structure with the requested information filled in. FindText works
491differently.&nbsp; It creates a modeless dialogue and instead sends <a href="../WinRef/Message.html#FINDMSGSTRING">FINDMSGSTRING</a> messages to the parent window.</p>
492
493<h3><a name="Printing">8. Printing and Painting</a></h3>
494
495<p>One area we have not touched on is actually how we draw to the screen.&nbsp; By using
496the Edit window to draw the text our application does not actually need to concern itself
497with exactly what happens when an area of the screen is uncovered and how characters in
498the text are converted into dots on the screen.&nbsp; This is all dealt with by the Edit
499window class.</p>
500
501<p>When it comes to printing the file these issues become apparent.&nbsp; Printing to a
502printer and drawing to the screen are handled in exactly the same way in Windows.&nbsp; In
503both cases we need to draw the document into a <em>device context</em>.&nbsp; A device
504context is an abstraction used for printers, the screen and also for <em>bitmaps</em> and <em>metafiles</em>
505in memory.&nbsp; Although there is an obvious difference in the printed page and an image
506on the screen in terms of the physical output as far as preparing the image is concerned
507they are both rectangular areas of dots (<em>pixels</em>).&nbsp; They may differ in size,
508in the range of colours possible and in the number of pixels per inch (<em>resolution</em>).
509&nbsp; Since we don't need to be concerned with drawing to the screen in this application
510we will focus on printing the file but we could easily arrange the code so that it could
511be used for both.</p>
512
513<p>We get a device context for the printer by giving the PrintDlgFlags.PD_RETURNDC option
514to the print dialogue.&nbsp; It would also be possible to get a device context using
515CreateDC.&nbsp;&nbsp; Once we have the device context we can find the size of the page.</p>
516
517<pre>val _ = SetMapMode(hdc, MM_TEXT)
518val pageWidth = GetDeviceCaps(hdc, HORZRES)
519and pageHeight = GetDeviceCaps(hdc, VERTRES)</pre>
520
521<p>We now need to find a font to use.&nbsp; We want a 10 point Courier font which we
522obtain using <a href="../WinRef/Font.html#CreateFont">CreateFont</a>.&nbsp; This is a fixed
523width font which simplifies calculating the page width.</p>
524
525<pre>val charHeight = ~10 * GetDeviceCaps(hdc, LOGPIXELSY) div 72;
526val hFont = CreateFont{height=charHeight, width=0, escapement=0, orientation=0,
527       weight=FW_DONTCARE, italic=false, underline=false, strikeOut=false,
528       charSet=ANSI_CHARSET, outputPrecision=OUT_DEFAULT_PRECIS,
529       clipPrecision=CLIP_DEFAULT_PRECIS, quality=DEFAULT_QUALITY,
530       pitch=FIXED_PITCH, family=FF_MODERN, faceName=&quot;Courier&quot;}</pre>
531
532<p>The character height calculation looks odd, giving us a negative value, but is a
533pecularity of CreateFont.&nbsp; All the other arguments are fairly obvious.&nbsp; </p>
534
535<pre>val oldFont = SelectObject(hdc, hFont);
536
537val textMetric = GetTextMetrics hdc;</pre>
538
539<p>We now select this as the font to use.&nbsp; <a href="../WinRef/DeviceContext.html#SelectObject">SelectObject</a> can be used for various
540other sorts of object, such as pens, brushes and bitmaps.&nbsp; Whenever you select in a
541particularly kind of object the previous object of that kind is returned as the result.
542&nbsp; It's generally good practice to select it back before you finish with a device
543context.&nbsp; Having selected this font we then call <a href="../WinRef/Font.html#GetTextMetrics">GetTextMetrics</a> to find the width of the font.
544&nbsp; From this and the page width we can compute the number of characters on a line.
545&nbsp;&nbsp; The height of the page divided by the height of a character gives us the
546number of characters on a page.&nbsp; We are now ready to print a page.</p>
547
548<p>We get the text from the edit window using <a href="../WinRef/Window.html#GetWindowText">GetWindowText</a>.
549&nbsp; The print dialogue gives the user the option of printing the currently selected
550area of the window rather than the whole file or a range of pages.&nbsp; To find the
551selection we send the edit window an EM_GETSEL message.</p>
552
553<p>We prepare the page by setting the colours and filling the page with white.&nbsp; These
554are likely to be the defaults anyway but there's no harm in making sure.</p>
555
556<pre>val white = RGB{red=255, blue=255, green=255}
557val black = RGB{red=0, blue=0, green = 0}
558val pageRect = {top=0, left=0, bottom=pageHeight, right=pageWidth}
559
560SetBkColor(hdc, white);
561SetTextColor(hdc, black);
562ExtTextOut(hdc, {x=0, y=0}, [ETO_OPAQUE], SOME pageRect, &quot;&quot;, []);</pre>
563
564<p><a href="../WinRef/Font.html#ExtTextOut">ExtTextOut</a> is one of the ways of drawing text
565but it is also a convenient way of filling an area with a colour.&nbsp; The ETO_OPAQUE
566option causes the rectangle to be filled with the background colour.</p>
567
568<p>Actually drawing each line is done using the <a href="../WinRef/Font.html#TabbedTextOut">TabbedTextOut</a>
569function.&nbsp; We extract a line from the document and draw it .</p>
570
571<pre>TabbedTextOut(hdc, {x=0, y= lineNo * #height textMetric},  thisLine, [], 0);</pre>
572
573<p>Each line is drawn beneath the previous one until the page is full or we have drawn all
574the text.</p>
575
576<p>All this is the same whether we are printing a file or drawing to the screen. &nbsp;
577When printing, though there are a few extra function calls we need.&nbsp; Before each page
578we call <a href="../WinRef/Printing.html#StartPage">StartPage</a> and after the page we call <a href="../WinRef/Printing.html#EndPage">EndPage</a>.&nbsp; We also need to bracket the whole
579document with calls to <a href="../WinRef/Printing.html#StartDoc">StartDoc</a> and <a href="../WinRef/Printing.html#EndDoc">EndDoc</a>.&nbsp; When the document is complete we also
580need to restore the original font, delete the Courier font we created and delete the
581device context.</p>
582
583<pre>val jobID = StartDoc(hdc, {docName=fileName, output=NONE, dType=NONE});
584...
585EndDoc hdc;
586SelectObject(hdc, oldFont);
587DeleteObject hFont;
588DeleteDC hdc;</pre>
589
590<p>We could use this code to draw to the screen if we were not using the edit 
591  window. &nbsp; In that case we would process the WM_PAINT message and bracket 
592  the calls with <a href="../WinRef/Painting.html#BeginPaint">BeginPaint</a> and 
593  <a href="../WinRef/Painting.html#EndPaint">EndPaint</a>.</p>
594
595</body>
596</html>
597