1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
2<html>
3
4<head>
5<title>Source for mlEdit example </title>
6</head>
7
8<body><pre>(*
9    Copyright (c) 2001-7
10        David C.J. Matthews
11
12    This library is free software; you can redistribute it and/or
13    modify it under the terms of the GNU Lesser General Public
14    License as published by the Free Software Foundation; either
15    version 2.1 of the License, or (at your option) any later version.
16    
17    This library is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20    Lesser General Public License for more details.
21    
22    You should have received a copy of the GNU Lesser General Public
23    License along with this library; if not, write to the Free Software
24    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
25*)
26(* Example text editor. *)
27fun mlEdit () =
28let
29    open Window Message Menu Edit Class Dialog CommonDialog MessageBox Caret
30    open DeviceContext Font Printing Transform Painting Color
31    open Keyboard
32
33    (* Define values to be delivered when the menu items are selected.
34       The Id is delivered as part of a WM_COMMAND message. *)
35    val menuOpen = 1
36    and menuQuit = 2
37    and menuSave = 3
38    and menuSaveAs = 4
39    and menuCut = 5
40    and menuCopy = 6
41    and menuPaste = 7
42    and menuFind = 8
43    and menuPageSetup = 9
44    and menuPrint = 10
45    and menuAbout = 11
46
47    val app = Globals.ApplicationInstance()
48
49    (* Borrow the Poly icon from the application program. It happens to
50       be icon id 102. If this doesn't work return NULL. *)
51    val polyIcon =
52        Icon.LoadIcon(app, Resource.MAKEINTRESOURCE 102) handle _ => Globals.hNull;
53
54    local
55        (* Create sub-menus. *)
56        val fileMenu =
57            let
58                val fileMenu = CreateMenu();
59            in
60                AppendMenu(fileMenu, [], MenuId menuOpen, MFT_STRING "&Open");
61                AppendMenu(fileMenu, [], MenuId menuSave, MFT_STRING "&Save");
62                AppendMenu(fileMenu, [], MenuId menuSaveAs, MFT_STRING "Save &As...");
63                AppendMenu(fileMenu, [], MenuId 0, MFT_SEPARATOR);
64                AppendMenu(fileMenu, [], MenuId menuPageSetup, MFT_STRING "Page Set&up...");
65                AppendMenu(fileMenu, [], MenuId menuPrint, MFT_STRING "P&rint...");
66                AppendMenu(fileMenu, [], MenuId 0, MFT_SEPARATOR);
67                AppendMenu(fileMenu, [], MenuId menuQuit, MFT_STRING "&Quit");
68                fileMenu
69            end;
70    
71        val editMenu =
72            let
73                val editMenu = CreateMenu();
74            in
75                AppendMenu(editMenu, [], MenuId menuCut, MFT_STRING "Cu&t");
76                AppendMenu(editMenu, [], MenuId menuCopy, MFT_STRING "&Copy");
77                AppendMenu(editMenu, [], MenuId menuPaste, MFT_STRING "&Paste");
78                AppendMenu(editMenu, [], MenuId menuFind, MFT_STRING "&Find");
79                editMenu
80            end;
81
82        val helpMenu =
83            let
84                val helpMenu = CreateMenu()
85            in
86                AppendMenu(helpMenu, [], MenuId menuAbout, MFT_STRING "&About mlEdit...");
87                helpMenu
88            end
89
90    in
91        (* Create the main menu and append the sub-menus. *)
92        val menu = CreateMenu();
93        val _ = AppendMenu(menu, [], MenuHandle fileMenu, MFT_STRING "&File");
94        val _ = AppendMenu(menu, [], MenuHandle editMenu, MFT_STRING "&Edit")
95        val _ = AppendMenu(menu, [], MenuHandle helpMenu, MFT_STRING "&Help")
96    end;
97
98    (* The "state" of the editor. *)
99    type state = {
100        edit: HWND, (* Handle to the edit window. *)
101        devMode: DEVMODE option, devNames: DEVNAMES option, (* Printer settings *)
102        fileName: string
103        }
104
105    fun wndProc(hw: HWND, msg: Message, NONE): LRESULT * state option =
106        (
107        case msg of
108            WM_CREATE _ => (* Create an edit window and return it as the state. *)
109            let
110                val edit =
111                 CreateWindow{class = Class.Edit, name = "",
112                    (* The style does not include horizontal scrolling.  That causes us to use word-wrapping. *)
113                    style = Edit.Style.flags[Edit.Style.WS_CHILD, Edit.Style.WS_VISIBLE, Edit.Style.WS_VSCROLL,
114                                    (*Edit.Style.WS_HSCROLL, *)Edit.Style.ES_LEFT, Edit.Style.ES_MULTILINE,
115                                    Edit.Style.ES_AUTOVSCROLL(*, Edit.Style.ES_AUTOHSCROLL*)],
116                    x  = 0, y = 0, height = 0, width = 0, relation = ChildWindow{parent=hw, id=99},
117                    instance = Globals.ApplicationInstance(), init = ()}
118
119                (* Create a 10 point Courier font. *)
120                val hDC = GetDC edit;
121                val height = ~10 * GetDeviceCaps(hDC, LOGPIXELSY) div 72;
122                val _ = ReleaseDC(edit, hDC);
123                val hFont = CreateFont{height=height, width=0, escapement=0, orientation=0,
124                       weight=FW_DONTCARE, italic=false, underline=false, strikeOut=false,
125                       charSet=ANSI_CHARSET, outputPrecision=OUT_DEFAULT_PRECIS,
126                       clipPrecision=CLIP_DEFAULT_PRECIS, quality=DEFAULT_QUALITY,
127                       pitch=FIXED_PITCH, family=FF_MODERN, faceName="Courier"}
128            in
129                SendMessage(edit, WM_SETFONT{font=hFont, redrawflag=false});
130                (LRESINT 0, SOME{edit=edit, devMode=NONE, devNames = NONE, fileName=""})
131            end
132
133        | _ => (DefWindowProc(hw, msg), NONE)
134        )
135
136    | wndProc(hw: HWND,
137              msg: Message,
138              state: state option as SOME{edit, devMode, devNames, fileName, ...}) =
139        case msg of
140            WM_SETFOCUS _ =>
141                (* If we get a focus request we set the focus to the edit window. *)
142                (SetFocus(SOME edit); (DefWindowProc(hw, msg), state))
143    
144        |    WM_SIZE{height, width, ...} =>
145                (* If we get a size change we set the size of the edit window. *)
146                (MoveWindow{hWnd=edit, x=0, y=0, height=height, width=width, repaint=true}; (DefWindowProc(hw, msg), state))
147    
148        |    WM_NCDESTROY =>
149                (* When the window is closed we send a QUIT message which exits from the application loop. *)
150                (PostQuitMessage 0; (DefWindowProc(hw, msg), state))
151    
152        |    WM_CLOSE =>
153                (* User has pressed the Close box.  If it's ok to close we could call
154                   DestroyWindow ourselves.  Just as an example we return NONE which
155                   passes it to the default window procedure and does it for us. *)
156                (if checkForSave(hw, edit, fileName) then DefWindowProc(hw, msg) else LRESINT 0, state)
157    
158        |    WM_COMMAND{notifyCode = 0, wId, control} =>
159                (* Menu selections arrive here. *)
160
161                if wId = menuQuit
162                then
163                    (
164                    if checkForSave(hw, edit, fileName) then DestroyWindow hw else ();
165                    (LRESINT 0, state)
166                    )
167
168                else if wId = menuOpen
169                then
170                let
171                    val on = {
172                        owner = SOME hw,
173                        template = TemplateDefault,
174                        filter =
175                            [("Text Files (*.txt)", "*.txt"),
176                             ("ML Files (*.sml)", "*.sml"),
177                             ("All Files (*.*)", "*.*")],
178                        customFilter = NONE,
179                        filterIndex = 1,
180                        file = "",
181                        maxFile = 1000,
182                        fileTitle = "",
183                        initialDir = NONE,
184                        title = NONE,
185                        flags = OpenFileFlags.flags[OpenFileFlags.OFN_HIDEREADONLY],
186                        defExt = NONE
187                    }
188                in
189                    case GetOpenFileName on of
190                        NONE => (LRESINT 0, state)
191                    |    SOME {file, ...} =>
192                        (* If it's been modified we need to ask before overwriting. *)
193                        if checkForSave(hw, edit, fileName)
194                        then
195                        (let
196                            val f = TextIO.openIn file
197                            (* Text input will convert CRNL to \n.  We need to
198                               reverse the process. *)
199                            fun nlToCrnl s =
200                                String.translate(fn #"\n" => "\r\n" | c => String.str c) s
201                        in
202                            (* Should we save any existing file? *)
203                            SetWindowText(edit, nlToCrnl(TextIO.inputAll f));
204                            TextIO.closeIn f;
205                            SendMessage(edit, EM_SETMODIFY{modified=false});
206                            (LRESINT 0, SOME{edit=edit, devMode=devMode, devNames=devNames,
207                                          fileName=file})
208                        end) handle exn =>
209                            (MessageBox(SOME hw,
210                                concat["Unable to open - ", file, "\n"(*, exnMessage exn*)],
211                                "Open failure", MessageBoxStyle.MB_OK);
212                            (LRESINT 0, state))
213                        else (LRESINT 0, state)
214                end
215
216                else if wId = menuSave andalso fileName <> ""
217                then (* Save to the original file name if there is one. *)
218                (
219                    saveDocument(hw, fileName, edit);
220                    (LRESINT 0, state)
221                )
222
223                else if wId = menuSaveAs orelse wId = menuSave (* andalso fileName = "" *)
224                then
225                (
226                    case saveAsDocument(hw, edit) of
227                        NONE => (LRESINT 0, state)
228                    |    SOME newName =>
229                            (LRESINT 0, (* Use the selected file name. *)
230                                SOME{edit=edit, devMode=devMode, devNames=devNames,
231                                     fileName=newName})
232                )
233
234                else if wId = menuFind
235                then
236                let
237                    open FindReplaceFlags
238                    (* Create a "Find" dialogue. *)
239                    val find =
240                        FindText{owner = hw, template = TemplateDefault,
241                                 flags=flags[FR_DOWN, FR_HIDEWHOLEWORD],
242                                 findWhat="", replaceWith="", bufferSize = 100}
243                in
244                    ShowWindow(find, SW_SHOW);
245                    (LRESINT 0, state)
246                end
247
248                (* Cut, Copy and Paste are all handled by the Edit window. *)
249                else if wId = menuCut
250                then (SendMessage(edit, WM_CUT); (LRESINT 0, state))
251                else if wId = menuCopy
252                then (SendMessage(edit, WM_COPY); (LRESINT 0, state))
253                else if wId = menuPaste
254                then (SendMessage(edit, WM_PASTE); (LRESINT 0, state))
255
256                else if wId = menuPageSetup
257                then
258                    (
259                    (* Put up the dialogue and change the settings if necessary. *)
260                    case PageSetupDlg {owner=SOME hw, devMode=devMode, devNames=devNames,
261                                     flags=PageSetupFlags.flags[], paperSize={x=0,y=0},
262                                     minMargin={top=0,bottom=0,left=0,right=0},
263                                     margin={top=0,bottom=0,left=0,right=0}} of
264                        NONE => (LRESINT 0, state)
265                    |    SOME {devMode, devNames, ...} =>
266                            (LRESINT 0, SOME{edit=edit, devMode=devMode, devNames=devNames,
267                                          fileName=fileName})
268                    )
269
270                else if wId = menuPrint (* "Print" menu item. *)
271                then
272                let
273                    (* Put up the dialogue box to get the settings. *)
274                    val printSettings =
275                        PrintDlg {owner=SOME hw, devMode=devMode, devNames=devNames,
276                                  context=NONE,
277                                  flags=PrintDlgFlags.flags[PrintDlgFlags.PD_RETURNDC],
278                                  fromPage=1, toPage= ~1, minPage=1, maxPage= ~1, copies=1};
279                in
280                    case printSettings of
281                        SOME {devMode, devNames, context = SOME hdc, flags, fromPage, toPage, ...} =>
282                        (let
283                            (* If the "Selection" button has been pressed we only print the
284                               selection. *)
285                            val printWhat =
286                                if PrintDlgFlags.anySet(flags, PrintDlgFlags.PD_SELECTION)
287                                then
288                                let
289                                    val from = ref 0 and to = ref 0
290                                    val _ = SendMessage(edit, EM_GETSEL{startPos = from, endPos = to})
291                                    val text = GetWindowText edit
292                                in
293                                    if !from < 0 orelse !from > size text orelse
294                                       !to < 0 orelse !from > size text
295                                    then ""
296                                    else String.substring(text, !from, !to - !from)
297                                end
298                                else (* "All" button pressed or "Pages" pressed. *)
299                                    GetWindowText edit;
300                            val textLength = size printWhat
301
302                            (* Tell the spooler to start the document. *)
303                            val jobID = StartDoc(hdc, {docName=fileName, output=NONE, dType=NONE})
304
305                            (* Find out how big a character is. From this we can work out
306                               how many characters fit on a line and how many lines on a
307                               page. Since we're using a fixed width font this is fairly
308                               easy. *)
309                            val _ = SetMapMode(hdc, MM_TEXT)
310                            val white = RGB{red=255, blue=255, green=255}
311                            val black = RGB{red=0, blue=0, green = 0}
312                            val pageWidth = GetDeviceCaps(hdc, HORZRES)
313                            and pageHeight = GetDeviceCaps(hdc, VERTRES)
314
315                            (* Create the same font as we're using on the screen. Since this is
316                               a fixed width font it makes calculating the number of characters
317                               fairly easy. *)
318                            val charHeight = ~10 * GetDeviceCaps(hdc, LOGPIXELSY) div 72;
319                            val hFont = CreateFont{height=charHeight, width=0, escapement=0, orientation=0,
320                                   weight=FW_DONTCARE, italic=false, underline=false, strikeOut=false,
321                                   charSet=ANSI_CHARSET, outputPrecision=OUT_DEFAULT_PRECIS,
322                                   clipPrecision=CLIP_DEFAULT_PRECIS, quality=DEFAULT_QUALITY,
323                                   pitch=FIXED_PITCH, family=FF_MODERN, faceName="Courier"}
324                            val oldFont = SelectObject(hdc, hFont); (* Use this font. *)
325
326                            val textMetric = GetTextMetrics hdc;
327
328                            fun printPage pno index =
329                            let
330                                (* If we are printing a range of pages we need to check whether
331                                   we are in the range. *)
332                                val printThisPage =
333                                    if PrintDlgFlags.anySet(flags, PrintDlgFlags.PD_PAGENUMS)
334                                    then pno >= fromPage andalso (pno <= toPage orelse toPage < 0)
335                                    else true
336                                val pageRect = {top=0, left=0, bottom=pageHeight, right=pageWidth}
337                                (* Calculate the number of lines and columns. *)
338                                val nLines = pageHeight div #height textMetric;
339                                val nCols = pageWidth div #maxCharWidth textMetric
340
341                                (* Output the lines to fill the page. *)
342                                fun outputLines lineNo p =
343                                    if lineNo >= nLines
344                                    then p (* Return last pointer. *)
345                                    else
346                                    let
347                                        (* Find the point to split the line.  We stop at the end of
348                                           the text, a line break, the last word break on the line
349                                           or the maximum number of characters. *)
350                                        fun findEnd lastBreak i =
351                                            if i >= textLength then (textLength, textLength)
352                                            else if i-p > nCols
353                                            then
354                                                (
355                                                case lastBreak of
356                                                    NONE => (* No breaks on the line - break just before here. *)
357                                                        (i-1, i-1)
358                                                |    SOME b => b (* Break at the last break. *)
359                                                )
360                                            else if i < textLength - 1 andalso
361                                                    String.sub(printWhat, i) = #"\r" andalso
362                                                    String.sub(printWhat, i+1) = #"\n"
363                                            then (* End of line - stop here. *)
364                                                (i, i+2)
365                                            else if Char.isSpace(String.sub(printWhat, i))
366                                            then (* Remember this. *)
367                                                findEnd (SOME(i, i+1)) (i+1)
368                                                (* Actually tabs need to be handled more carefully. *)
369                                            else findEnd lastBreak (i+1)
370
371                                        val (endLine, nextLine) = findEnd NONE p
372                                        val thisLine =
373                                            if p >= textLength
374                                            then ""
375                                            else String.substring(printWhat, p, endLine-p)
376                                    in
377                                        if printThisPage
378                                        then
379                                            (
380                                            TabbedTextOut(hdc, {x=0, y= lineNo * #height textMetric},
381                                                thisLine, [], 0);
382                                            ()
383                                            )
384                                        else ();
385                                        outputLines (lineNo+1) nextLine
386                                    end
387                                val nextPage =
388                                    if printThisPage
389                                    then
390                                        let
391                                            val _ = StartPage hdc;
392                                            (* Fill the page with white. *)
393                                            val _ = SetBkColor(hdc, white);
394                                            val _ = SetTextColor(hdc, black);
395                                            val _ = ExtTextOut(hdc, {x=0, y=0}, [ETO_OPAQUE], SOME pageRect, "", []);
396                                            (* Print the text. *)
397                                            val next = outputLines 0 index
398                                        in
399                                            EndPage hdc;
400                                            next
401                                        end
402                                    else (* Format the page but don't print it. *) outputLines 0 index
403                            in
404                                if nextPage >= size printWhat
405                                then ()
406                                else printPage (pno+1) nextPage
407                            end
408
409                            val _: unit = printPage 1 0
410                        in
411                            EndDoc hdc;
412                            (* Restore the original font and delete the new one. *)
413                            SelectObject(hdc, oldFont);
414                            DeleteObject(hFont);
415                            DeleteDC hdc; (* Now delete the device context. *)
416                            (LRESINT 0, SOME{edit=edit, devMode=devMode, devNames=devNames,
417                                          fileName=fileName})
418                        end
419                            (* If any of the functions failed simply delete the device
420                               context and return the original state. *)
421                            handle (exn as OS.SysErr _) => (
422                                print (exnName exn); AbortDoc hdc; DeleteDC hdc; (LRESINT 0, state)))
423                    |    _ => (LRESINT 0, state)
424                end
425
426                else if wId = menuAbout
427                then (aboutmlEdit hw; (LRESINT 0, state))
428
429                else (DefWindowProc(hw, msg), state)
430
431        |  FINDMSGSTRING{flags, findWhat, ...} =>
432            if FindReplaceFlags.anySet(flags, FindReplaceFlags.FR_DIALOGTERM)
433            then (* The "find" box is going away. *)
434                (
435                    SetFocus(SOME edit);
436                    (LRESINT 0, state)
437                )
438            else if FindReplaceFlags.anySet(flags, FindReplaceFlags.FR_FINDNEXT)
439            then (* The Find Next button has been pressed. *)
440            let
441                (* Get the whole of the text - not very efficient. *)
442                val text = GetWindowText edit
443                val startPos = ref 0 and endPos = ref 0
444                (* Get the starting position. *)
445                val _ = SendMessage(edit, EM_GETSEL{startPos=startPos, endPos=endPos})
446
447                val isDown = FindReplaceFlags.anySet(flags, FindReplaceFlags.FR_DOWN)
448                (* Get the starting position for the search. *)
449                val startPos = if isDown then !endPos else !startPos - 1
450
451                val findLen = size findWhat
452                (* Get the options. *)
453                local
454                    val toLower = String.map Char.toLower
455                in
456                    val doMatch: string * string -> bool =
457                        if FindReplaceFlags.anySet(flags, FindReplaceFlags.FR_MATCHCASE)
458                        then op =
459                        else fn (s1, s2) => toLower s1 = toLower s2
460                end
461
462                fun doFind p =
463                let
464                    val isMatch =
465                        p >= 0 andalso size text - p >= size findWhat andalso
466                            doMatch(String.substring(text, p, findLen), findWhat)
467                in
468                    if isMatch then p
469                    else if isDown
470                    then if p = size text then p (* Finish *) else doFind(p+1)
471                    else (* Find up *) if p = 0 then ~1 (* Finish *) else doFind(p-1)
472                end
473                val foundAt = doFind startPos
474            in
475                if foundAt >= 0 andalso foundAt + findLen < size text
476                then
477                    (
478                    SendMessage(edit, EM_SETSEL{startPos=foundAt, endPos=foundAt + findLen});
479                    SendMessage(edit, EM_SCROLLCARET);
480                    ()
481                    )
482                else MessageBeep(MessageBoxStyle.fromWord 0wxFFFFFFFF);
483                (LRESINT 0, state)
484            end
485            else (DefWindowProc(hw, msg), state)
486
487        |    _ => (DefWindowProc(hw, msg), state)
488
489    (* If this document has been modified we want to ask before quitting or
490       opening a new document. *)
491    and checkForSave(hw, edit, fileName) =
492        case SendMessage(edit, EM_GETMODIFY) of
493            LRESINT 0 => true (* Unmodified - continue. *)
494        |    _ => 
495            let
496                val res =
497                    MessageBox(SOME hw, "Save document?", "Confirm",
498                               MessageBoxStyle.MB_YESNOCANCEL)
499            in
500                if res = IDYES
501                then if fileName = ""
502                then saveAsDocument(hw, edit) <> NONE
503                else saveDocument(hw, fileName, edit)
504                else if res = IDNO
505                then true (* Continue anyway. *)
506                else false (* Cancel - don't exit or open. *)
507            end
508
509    and saveDocument(hw, fileName, edit) =
510    (* Write the document to the given file name. *)
511        let
512            (* Write the file as binary.  That way we don't need to
513               convert CRNL to NL before writing. *)
514            val f = BinIO.openOut fileName
515            val s = GetWindowText edit
516        in
517            BinIO.output(f, Byte.stringToBytes s);
518            BinIO.closeOut f;
519            (* Document is now unmodified. *)
520            SendMessage(edit, EM_SETMODIFY{modified=false});
521            true (* Succeeded. *)
522        end handle exn =>
523            (MessageBox(SOME hw,
524                concat["Unable to save to - ", fileName, "\n"(*, exnMessage exn*)],
525                "Open failure", MessageBoxStyle.MB_OK);
526             false)
527
528    and saveAsDocument(hw, edit) =
529    (* Ask for the file name before trying to save. *)
530        let
531            val on = {
532                owner = SOME hw,
533                template = TemplateDefault,
534                filter =
535                    [("Text Files (*.txt)", "*.txt"),
536                     ("ML Files (*.sml)", "*.sml"),
537                     ("All Files (*.*)", "*.*")],
538                customFilter = NONE,
539                filterIndex = 1,
540                file = "",
541                maxFile = 1000,
542                fileTitle = "",
543                initialDir = NONE,
544                title = NONE,
545                flags = OpenFileFlags.flags[],
546                defExt = NONE
547            }
548        in
549            case GetSaveFileName on of
550                NONE => NONE
551            |    SOME {file, filterIndex, fileTitle, ...} =>
552                let
553                    (* If the user typed a file name without an extension use
554                       the extension from the appropriate filter. *)
555                    val suffix =
556                        case filterIndex of
557                            1 => ".txt"
558                        |    2 => ".sml"
559                        |    _ => ""
560                    val fileName =
561                        if Char.contains fileTitle #"."
562                        then file else file ^ suffix
563                in
564                    if saveDocument(hw, fileName, edit)
565                    then SOME file (* Return the selected name. *)
566                    else NONE
567                end
568        end
569
570    and aboutmlEdit hw =
571    (* Called when the user selects "About..." from the help menu. *)
572    let
573        (* Dialogue template containing three items: an OK button, a static picture and
574           a piece of text. *)
575        val pictureId = 1000 (* Could use any number here. *)
576        open Static.Style
577        val template =
578            {x = 0, y = 0, cx = 210, cy = 94, font = SOME (8, "MS Sans Serif"), menu = NONE,
579             class = NONE,title = "About mlEdit", extendedStyle = 0,
580             style = flags[WS_POPUP, WS_CAPTION],
581             items =
582              [{x = 73, y = 62, cx = 50, cy = 14, id = 1,
583                class = DLG_BUTTON (flags[WS_CHILD, WS_VISIBLE, WS_TABSTOP]),
584                title = DLG_TITLESTRING "OK", creationData = NONE, extendedStyle = 0},
585               {x = 7, y = 7, cx = 32, cy = 32, id = pictureId,
586                class = DLG_STATIC (flags[WS_CHILD, WS_VISIBLE, SS_ICON]),
587                title = DLG_TITLESTRING "", creationData = NONE, extendedStyle = 0},
588               {x = 15, y = 39, cx = 180, cy = 21, id = 65535,
589                class = DLG_STATIC (flags[WS_CHILD, WS_VISIBLE, WS_GROUP]),
590                title =
591                    DLG_TITLESTRING
592                       "mlEdit - An example of Windows programming in Poly/ML\
593                       \\nCopyright David C.J. Matthews 2001-7",
594                creationData = NONE,  extendedStyle = 0}] }
595
596        (* Dialogue procedure. *)
597        fun dlgProc(dial, WM_INITDIALOG _, ()) =
598            (
599                (* Send a message to the picture control to set it to this icon. *)
600                SendMessage(GetDlgItem(dial, pictureId), STM_SETICON{icon=polyIcon});
601                (LRESINT 1, ())
602            )
603
604        |    dlgProc(dial, WM_COMMAND{notifyCode = 0, wId=1 (* OK button *), ...}, ()) =
605                (* When the OK button is pressed we end the dialogue. *)
606                (EndDialog(dial, 1); (LRESINT 1, ()) )
607
608        |    dlgProc _ = (LRESINT 0, ())
609
610    in
611        DialogBoxIndirect(app, template, hw, dlgProc, ());
612        ()
613    end
614
615    val className = "mlEditWindowClass"
616    (* Register a class for the top-level window.  Use the Poly icon from the application. *)
617    val myWindowClass = RegisterClassEx{style = Class.Style.flags[],
618        wndProc = wndProc, hInstance = app,
619        hIcon = SOME polyIcon, hCursor = NONE, hbrBackGround = NONE, menuName = NONE,
620        className = className, hIconSm = NONE};
621    
622    
623    val w = CreateWindow{class = myWindowClass, name = "mlEdit", style = Window.Style.WS_OVERLAPPEDWINDOW,
624        x  = CW_USEDEFAULT, y = CW_USEDEFAULT, height = CW_USEDEFAULT, width = CW_USEDEFAULT,
625        relation = PopupWindow menu,
626        instance = app, init = NONE};
627in
628    ShowWindow(w, SW_SHOW);
629    SetForegroundWindow w;
630    
631    RunApplication();
632    (* Must unregister the class before returning otherwise RegisterClass will
633       fail if we call mlEdit again. *)
634    UnregisterClass(className, app)
635end;
636         </pre>
637</body>
638</html>
639