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