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