Merge branch 'ical'
[alpine.git] / pico / osdep / msdlg.c
blobbb64d252d2e26aa181fcb67197b00aa8fe928ae5
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: msdlg.c 1025 2008-04-08 22:59:38Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-2017 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*---------------------------------------------------------------------------
21 * Module: msdlg.c
23 * Thomas Unger
24 * Networks and Distributed Computing
25 * Computing and Communications
26 * University of Washington
27 * Administration Builiding, AG-44
28 * Seattle, Washington, 98195, USA
29 * Internet: tunger@cac.washington.edu
32 * Pine and Pico are registered trademarks of the University of Washington.
33 * No commercial use of these trademarks may be made without prior written
34 * permission of the University of Washington.
36 * Pine, Pico, and Pilot software and its included text are Copyright
37 * 1989-1998 by the University of Washington.
39 * The full text of our legal notices is contained in the file called
40 * CPYRIGHT, included with this distribution.
42 *--------------------------------------------------------------------------*/
44 #define WIN31
45 #define STRICT
47 #include <system.h>
48 #include <general.h>
50 #include <stdarg.h>
51 #include <ddeml.h>
53 #include "mswin.h"
54 #include "msmenu.h"
55 #include "resource.h"
57 #include "../estruct.h"
58 #include "../mode.h"
59 #include "../pico.h"
60 #include "../keydefs.h"
61 #include "../edef.h"
62 #include "../efunc.h"
63 #include "../utf8stub.h"
64 #include "../../pith/charconv/filesys.h"
67 extern HWND ghTTYWnd;
68 extern HINSTANCE ghInstance;
69 extern BOOL gfUseDialogs;
70 extern FILE *mswin_debugfile;
71 extern int mswin_debug;
72 extern TCHAR gszAppName[45];
76 #define BTN_FIRSTID 200
77 #define BSPACE 4
78 #define BWIDTH_MIN 120
81 typedef struct {
82 LPTSTR prompt; /* Prompt. */
83 LPTSTR string; /* Resulting string. */
84 int strlen; /* Length of buffer. */
85 int append; /* Append to existing string. */
86 int passwd; /* Passwd, don't echo (1 use asterisk, 10 space). */
87 unsigned flags; /* other flags. */
88 int dflt;
89 int cancel;
90 int button_count; /* Number of additional buttons. */
91 MDlgButton *button_list; /* List of other buttons. */
92 char **helptext; /* Help text. */
93 char helpkey;
94 int result;
95 } OEInfo;
98 static OEInfo gOEInfo;
99 static BOOL gDoHelpFirst;
100 static WNDPROC gpOldEditProc; /* Old Edit window proc. */
101 static WNDPROC gpEditProc;
102 static WNDPROC gpOldBtnProc; /* Old Edit window proc. */
103 static WNDPROC gpBtnProc;
109 * Forward function declaratins.
111 LONG FAR PASCAL __export EditProc(HWND hBtn, UINT msg, WPARAM wParam,
112 LPARAM lParam);
113 LONG FAR PASCAL __export ButtonProc(HWND hBtn, UINT msg, WPARAM wParam,
114 LPARAM lParam);
115 BOOL CALLBACK __export mswin_dialog_proc (HWND hDlg, UINT uMsg,
116 WPARAM wParam, LPARAM lParam);
117 BOOL CALLBACK __export mswin_select_proc (HWND hDlg, UINT uMsg,
118 WPARAM wParam, LPARAM lParam);
119 LONG FAR PASCAL __export KBCProc (HWND hWnd, UINT uMsg, WPARAM wParam,
120 LPARAM lParam);
123 static void BuildButtonList (HWND, MDlgButton *, int, MDlgButton *);
124 static BOOL ProcessChar (HWND, WPARAM, OEInfo *, long *);
125 static void GetBtnPos (HWND, HWND, RECT *);
127 int mswin_yesno_lptstr (LPTSTR);
131 mswin_usedialog (void)
133 return (gfUseDialogs);
138 /*----------------------------------------------------------------------
141 Generic text entry. Prompt user for a string.
144 Args:
145 prompt -- The string to prompt with
146 string -- the buffer result is returned in, and original string (if
147 any) is passed in.
148 field_len -- Maximum length of string to accept
149 append_current -- flag indicating string should not be truncated before
150 accepting input
151 passwd -- a pass word is being fetch. Don't echo on screen
152 button_list -- pointer to array of mDlgButton's. input chars matching
153 those in list return value from list. Nearly identical
154 to Pine's ESCKEY structure.
155 help -- Arrary of strings for help text in bottom screen lines
156 flags -- flags
158 Result: editing input string
159 returns -1 unexpected errors
160 returns 0 typed CR or pressed "OK"
161 returns 1 typed ESC or pressed "Cancel"
162 returns 3 typed ^G or PF1 (help)
163 returns 4 typed ^L for a screen redraw
164 or one of the other return values defined in button_list.
166 WARNING: Care is required with regard to the escape_list processing.
167 The passed array is terminated with an entry that has ch = -1.
168 Function key labels and key strokes need to be setup externally!
169 Traditionally, a return value of 2 is used for ^T escapes.
172 Note About Help: We don't get the help text on the first call. If we
173 want help text we have to return 3. We'll get called again
174 with help text. To make it look good, we want to display
175 this the second time we're called. But we don't want to
176 display the help text every time we're called with help
177 text. To know the difference we set gDoHelpFirst when
178 exiting to request help text. If this is set then we
179 display help. If not, then no help.
182 ----------------------------------------------------------------------*/
186 * xxx implement flags.
187 * xxx display.d uses flags QFFILE, QDEFLT, and QBOBUF
191 mswin_dialog(UCS *prompt, UCS *buf, int nbuf, int append_current,
192 int passwd, MDlgButton *button_list, char **help, unsigned flags)
194 DLGPROC dlgprc;
195 int c;
196 LPTSTR promptlpt, buflpt, b;
197 UCS *ucs;
199 mswin_flush();
201 promptlpt = ucs4_to_lptstr(prompt);
202 buflpt = (LPTSTR) fs_get(nbuf * sizeof(TCHAR));
203 b = ucs4_to_lptstr(buf);
204 if(b){
205 int i;
207 for(i = 0; i < nbuf && b[i]; i++)
208 buflpt[i] = b[i];
210 if(i < nbuf)
211 buflpt[i] = 0;
213 buflpt[nbuf-1] = 0;
214 fs_give((void **) &b);
217 gOEInfo.prompt = promptlpt;
218 gOEInfo.string = buflpt;
219 gOEInfo.strlen = nbuf;
220 gOEInfo.append = append_current;
221 gOEInfo.passwd = passwd;
222 gOEInfo.helptext = help;
223 gOEInfo.helpkey = 0x07; /* ^G */
224 gOEInfo.flags = flags;
225 gOEInfo.button_list = button_list;
226 gOEInfo.result = 0;
228 for (c = 0; button_list && button_list[c].ch != -1; ++c)
231 gOEInfo.button_count = c;
233 gpEditProc = (WNDPROC) MakeProcInstance((FARPROC) EditProc, ghInstance);
234 gpBtnProc = (WNDPROC) MakeProcInstance((FARPROC) ButtonProc, ghInstance);
235 dlgprc = (DLGPROC) MakeProcInstance((FARPROC) mswin_dialog_proc, ghInstance);
237 DialogBox(ghInstance, MAKEINTRESOURCE (IDD_OPTIONALYENTER), ghTTYWnd, dlgprc);
239 FreeProcInstance((FARPROC) dlgprc);
240 FreeProcInstance((FARPROC) gpBtnProc);
241 FreeProcInstance((FARPROC) gpEditProc);
243 if(promptlpt)
244 fs_give((void **) &promptlpt);
246 ucs = lptstr_to_ucs4(buflpt);
247 if(ucs){
248 int i;
250 for(i = 0; i < nbuf && ucs[i]; i++)
251 buf[i] = ucs[i];
253 if(i < nbuf)
254 buf[i] = '\0';
256 buf[nbuf-1] = '\0';
257 fs_give((void **) &ucs);
260 return(gOEInfo.result);
267 * Dialog procedure for the generic text entry dialog box.
269 BOOL CALLBACK __export
270 mswin_dialog_proc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
272 BOOL ret = FALSE;
273 int i;
274 int biCount;
275 HWND hEdit;
276 MDlgButton built_in[3];
277 long l;
280 switch (uMsg) {
281 case WM_INITDIALOG:
283 * Set the prompt text.
285 SetDlgItemText(hDlg, IDC_PROMPT, gOEInfo.prompt);
288 * Set the initial edit text and configure the edit control
290 if (!gOEInfo.append)
291 *gOEInfo.string = '\0';
293 SetDlgItemText(hDlg, IDC_RESPONCE, gOEInfo.string);
295 SendDlgItemMessage (hDlg, IDC_RESPONCE, EM_SETPASSWORDCHAR,
296 (gOEInfo.passwd == 1) ? '*' : gOEInfo.passwd ? ' ' : 0, 0L);
297 SendDlgItemMessage (hDlg, IDC_RESPONCE, EM_LIMITTEXT,
298 gOEInfo.strlen - 1, 0L);
299 l = (long) lstrlen(gOEInfo.string);
300 SendDlgItemMessage(hDlg, IDC_RESPONCE, EM_SETSEL, 0, l);
301 hEdit = GetDlgItem (hDlg, IDC_RESPONCE);
302 SetFocus (hEdit);
303 gpOldEditProc = (WNDPROC) GetWindowLong (hEdit, GWL_WNDPROC);
304 SetWindowLong (hEdit, GWL_WNDPROC, (DWORD)(FARPROC)gpEditProc);
308 * Subclass the standard buttons and build buttons for each item
309 * in the button_list.
311 gpOldBtnProc = (WNDPROC) GetWindowLong (GetDlgItem (hDlg, IDOK), GWL_WNDPROC);
312 SetWindowLong (GetDlgItem (hDlg, IDOK), GWL_WNDPROC,
313 (DWORD)(FARPROC)gpBtnProc);
314 SetWindowLong (GetDlgItem (hDlg, IDCANCEL), GWL_WNDPROC,
315 (DWORD)(FARPROC)gpBtnProc);
317 memset (&built_in, 0, sizeof (built_in));
318 built_in[0].id = IDOK;
319 built_in[1].id = IDCANCEL;
320 if (1) {
321 built_in[2].id = IDC_GETHELP;
322 biCount = 3;
324 else {
325 DestroyWindow (GetDlgItem (hDlg, IDC_GETHELP));
326 biCount = 2;
328 BuildButtonList (hDlg, built_in, biCount, gOEInfo.button_list);
331 if (gOEInfo.helptext && gDoHelpFirst) {
332 mswin_showhelpmsg ((WINHAND)hDlg, gOEInfo.helptext);
333 gDoHelpFirst = FALSE;
335 return (0);
338 case WM_ACTIVATE:
340 * Keep focus on the edit item so key strokes are always
341 * received.
343 SetFocus (GetDlgItem (hDlg, IDC_RESPONCE));
344 break;
347 case WM_COMMAND:
348 switch (wParam) {
349 case IDOK:
351 * Normal exit. Accept the new text.
353 GetDlgItemText(hDlg, IDC_RESPONCE, gOEInfo.string, gOEInfo.strlen);
355 gOEInfo.result = 0;
356 EndDialog (hDlg, gOEInfo.result);
357 ret = TRUE;
358 break;
360 case IDCANCEL:
362 * Cancel operation. Don't retreive new text.
364 gOEInfo.result = 1;
365 EndDialog (hDlg, gOEInfo.result);
366 ret = TRUE;
367 break;
369 case IDC_GETHELP:
371 * Get help. If we have help text display it. If not,
372 * return value 3, which tells the caller to provide us
373 * with help text.
375 if (gOEInfo.helptext != NULL) {
376 mswin_showhelpmsg ((WINHAND)hDlg, gOEInfo.helptext);
378 else {
379 GetDlgItemText(hDlg, IDC_RESPONCE, gOEInfo.string, gOEInfo.strlen);
380 gOEInfo.result = 3;
381 gDoHelpFirst = TRUE;
382 EndDialog (hDlg, gOEInfo.result);
384 ret = TRUE;
385 break;
387 default:
389 * Search button list for button with this ID. If found
390 * return it's result code. Retreive text.
392 if ( wParam >= BTN_FIRSTID &&
393 wParam < BTN_FIRSTID + (WPARAM) gOEInfo.button_count) {
394 GetDlgItemText(hDlg, IDC_RESPONCE, gOEInfo.string, gOEInfo.strlen);
395 i = wParam - BTN_FIRSTID;
396 gOEInfo.result = gOEInfo.button_list[i].rval;
397 EndDialog (hDlg, gOEInfo.result);
398 ret = TRUE;
403 return (ret) ;
410 * Subclassed window procedure for Edit control on generic text
411 * entry dialog box.
413 LONG FAR PASCAL __export
414 EditProc (HWND hEdit, UINT msg, WPARAM wParam, LPARAM lParam)
416 HWND hDlg;
417 LONG ret;
419 hDlg = GetParent (hEdit);
421 if (msg == WM_GETDLGCODE) {
423 * Tell windows that we want to receive ALL keystrokes.
425 ret = CallWindowProc (gpOldEditProc, hEdit, msg, wParam, lParam);
426 ret |= DLGC_WANTALLKEYS;
427 return (ret);
431 if (msg == WM_CHAR) {
433 * See if the character typed is a shortcut for any of the
434 * buttons.
436 if (ProcessChar (hDlg, wParam, &gOEInfo, &ret))
437 return (ret);
439 /* No... Fall through for deault processing. */
442 return (CallWindowProc (gpOldEditProc, hEdit, msg, wParam, lParam));
449 * Subclass button windows.
451 * These buttons will automatically return the input focus to
452 * a control with id IDC_RESPONCE.
454 LONG FAR PASCAL __export
455 ButtonProc (HWND hBtn, UINT uMsg, WPARAM wParam, LPARAM lParam)
457 HWND hDlg;
458 LONG ret;
461 if (uMsg == WM_LBUTTONUP || uMsg == WM_MBUTTONUP || uMsg == WM_RBUTTONUP) {
463 * On mouse button up restore input focus to IDC_RESPONCE, which
464 * processes keyboard input.
466 ret = CallWindowProc (gpOldBtnProc, hBtn, uMsg, wParam, lParam);
467 hDlg = GetParent (hBtn);
468 SetFocus (GetDlgItem (hDlg, IDC_RESPONCE));
469 return (ret);
472 return (CallWindowProc (gpOldBtnProc, hBtn, uMsg, wParam, lParam));
478 mswin_yesno(UCS *prompt_ucs4)
480 int ret;
481 LPTSTR prompt_lptstr;
483 prompt_lptstr = ucs4_to_lptstr(prompt_ucs4);
484 ret = mswin_yesno_lptstr(prompt_lptstr);
485 fs_give((void **) &prompt_lptstr);
487 return(ret);
492 mswin_yesno_utf8(char *prompt_utf8)
494 int ret;
495 LPTSTR prompt_lptstr;
497 prompt_lptstr = utf8_to_lptstr(prompt_utf8);
498 ret = mswin_yesno_lptstr(prompt_lptstr);
499 fs_give((void **) &prompt_lptstr);
501 return(ret);
505 * Ask a yes/no question with the MessageBox procedure.
507 * Returns:
508 * 0 - Cancel operation.
509 * 1 - "Yes" was selected.
510 * 2 - "No" was selected.
513 mswin_yesno_lptstr(LPTSTR prompt)
515 int ret;
517 mswin_flush ();
519 mswin_killsplash();
521 ret = MessageBox (ghTTYWnd, prompt, gszAppName,
522 MB_APPLMODAL | MB_ICONQUESTION | MB_YESNOCANCEL);
524 switch (ret) {
525 case IDYES: ret = 1; break;
526 case IDNO: ret = 2; break;
527 default:
528 case IDCANCEL: ret = 0; break;
530 return (ret);
534 /*----------------------------------------------------------------------
537 Generic selection routine. Display a prompt and a set of buttons for
538 each possible answer.
542 Args:
543 prompt -- The string to prompt with.
544 button_list -- pointer to array of mDlgButton's. input chars
545 matching those in list return value from
546 mlist. Nearly identical to Pine's ESCKEY
547 mstructure.
548 dflt -- Value returned when "Enter" is pressed.
549 on_ctrl_C -- Value returned to cancel dialog (ESC).
550 help -- Arrary of strings for help text in bottom screen
551 lines
552 flags -- flags
554 Result:
555 dflt -- Default option selected.
556 on_ctrl_C -- Calcel operation.
557 or one of the other return values defined in button_list.
560 Note: To prcess keyboard in put we use a custom dialog control
561 which is invisible but always has the input focus.
563 ----------------------------------------------------------------------*/
566 mswin_select(char *utf8prompt, MDlgButton *button_list, int dflt, int on_ctrl_C,
567 char **help, unsigned flags)
569 WNDCLASS wndclass;
570 DLGPROC dlgprc;
571 WNDPROC kbcProc;
572 int c;
573 LPTSTR promptlpt;
575 mswin_flush ();
577 promptlpt = utf8_to_lptstr(utf8prompt);
580 * Setup dialog config structure.
582 gOEInfo.prompt = promptlpt;
583 gOEInfo.string = NULL;
584 gOEInfo.strlen = 0;
585 gOEInfo.append = 0;
586 gOEInfo.passwd = 0;
587 gOEInfo.helptext = help;
588 gOEInfo.helpkey = 'g';
589 gOEInfo.dflt = dflt;
590 gOEInfo.cancel = on_ctrl_C;
591 gOEInfo.flags = flags;
592 gOEInfo.button_list = button_list;
593 gOEInfo.result = 0;
596 * Count the buttons.
598 for(c = 0; button_list && button_list[c].ch != -1; ++c)
601 gOEInfo.button_count = c;
604 * Register the class for the user control which will receive keyboard
605 * input events.
607 kbcProc = (WNDPROC) MakeProcInstance((FARPROC) KBCProc, ghInstance);
608 wndclass.style = 0;
609 wndclass.lpfnWndProc = kbcProc;
610 wndclass.cbClsExtra = 0;
611 wndclass.cbWndExtra = 0;
612 wndclass.hInstance = ghInstance;
613 wndclass.hIcon = NULL;
614 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
615 wndclass.hbrBackground = 0;
616 wndclass.lpszMenuName = NULL;
617 wndclass.lpszClassName = TEXT("KeyboardCapture");
619 if(RegisterClass (&wndclass) == 0){
621 * There is no good return value to indicate an error.
622 * Cancel the operation.
624 return(on_ctrl_C);
628 * Make other procedure instances.
630 dlgprc = (DLGPROC) MakeProcInstance((FARPROC) mswin_select_proc, ghInstance);
631 gpBtnProc = (WNDPROC) MakeProcInstance((FARPROC) ButtonProc, ghInstance);
634 * Bring up the dialog box.
636 DialogBox(ghInstance, MAKEINTRESOURCE(IDD_SELECT), ghTTYWnd, dlgprc);
639 * Free the proc instances and window class.
641 FreeProcInstance((FARPROC) dlgprc);
642 FreeProcInstance((FARPROC) gpBtnProc);
643 UnregisterClass (TEXT("KeyboardCapture"), ghInstance);
644 FreeProcInstance((FARPROC) kbcProc);
646 if(promptlpt)
647 fs_give((void **) &promptlpt);
649 return(gOEInfo.result);
656 * Dialog procedure for the button selection dialog box.
658 BOOL CALLBACK __export
659 mswin_select_proc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
661 BOOL ret = FALSE;
662 int i;
663 int biCount;
664 MDlgButton built_in[3];
666 if (mswin_debug >= 1)
667 fprintf (mswin_debugfile, "Select: uMsg x%x (%d), wParam x%x, lParam x%lx\n",
668 uMsg, uMsg, wParam, lParam);
671 switch (uMsg) {
672 case WM_INITDIALOG:
674 * Show the prompt.
676 SetDlgItemText(hDlg, IDC_PROMPT, gOEInfo.prompt);
679 * Set focus to the invisible custom control
681 SetFocus (GetDlgItem (hDlg, IDC_RESPONCE));
684 * Subclass the standard buttons and build buttons for each item
685 * in the button_list.
687 gpOldBtnProc = (WNDPROC) GetWindowLong (GetDlgItem (hDlg, IDCANCEL), GWL_WNDPROC);
688 SetWindowLong (GetDlgItem (hDlg, IDC_GETHELP), GWL_WNDPROC,
689 (DWORD)(FARPROC)gpBtnProc);
690 SetWindowLong (GetDlgItem (hDlg, IDCANCEL), GWL_WNDPROC,
691 (DWORD)(FARPROC)gpBtnProc);
693 memset (&built_in, 0, sizeof (built_in));
694 built_in[0].id = IDCANCEL;
695 if (gOEInfo.helptext != NULL) {
696 built_in[1].id = IDC_GETHELP;
697 biCount = 2;
699 else {
700 DestroyWindow (GetDlgItem (hDlg, IDC_GETHELP));
701 gOEInfo.helpkey = 0;
702 biCount = 1;
704 BuildButtonList (hDlg, built_in, biCount, gOEInfo.button_list);
705 gDoHelpFirst = FALSE;
706 return (0);
709 case WM_ACTIVATE:
711 * Keep focus on the custom control item so key strokes are always
712 * received.
714 SetFocus (GetDlgItem (hDlg, IDC_RESPONCE));
715 break;
718 case WM_COMMAND:
719 switch (wParam) {
720 case IDOK:
721 gOEInfo.result = gOEInfo.dflt;
722 EndDialog (hDlg, gOEInfo.result);
723 ret = TRUE;
724 break;
726 case IDCANCEL:
727 gOEInfo.result = gOEInfo.cancel;
728 EndDialog (hDlg, gOEInfo.result);
729 ret = TRUE;
730 break;
732 case IDC_GETHELP:
734 * Get help. If we have help text display it. If not,
735 * return value 3, which tells the caller to provide us
736 * with help text.
738 if (gOEInfo.helptext != NULL) {
739 mswin_showhelpmsg ((WINHAND) hDlg, gOEInfo.helptext);
741 ret = TRUE;
742 break;
744 default:
745 if ( wParam >= BTN_FIRSTID &&
746 wParam < BTN_FIRSTID + (WPARAM) gOEInfo.button_count) {
747 i = wParam - BTN_FIRSTID;
748 gOEInfo.result = gOEInfo.button_list[i].rval;
749 EndDialog (hDlg, gOEInfo.result);
750 ret = TRUE;
755 return (ret) ;
761 * Window procedure for the hidden custom control.
763 LONG FAR PASCAL __export
764 KBCProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
766 LONG ret;
769 if (uMsg == WM_GETDLGCODE) {
771 * Tell windows that we want all the character messages.
773 ret = DefWindowProc (hWnd, uMsg, wParam, lParam);
774 ret |= DLGC_WANTALLKEYS;
775 return (ret);
779 if (uMsg == WM_CHAR) {
781 * See if the character typed is a shortcut for any of the
782 * buttons.
784 if (ProcessChar (GetParent (hWnd), wParam, &gOEInfo, &ret))
785 return (ret);
787 /* Fall through for default processing. */
790 return (DefWindowProc (hWnd, uMsg, wParam, lParam));
799 * Check if character activates any button. If it does, send WM_COMMAND
800 * to target window.
802 * Args:
803 * hWnd - Window to send message to.
804 * wParam - character typed.
805 * oeinfo - dialog config structure. Contains button list.
806 * ret - value to return to windows.
808 * Returns:
809 * TRUE - Found a match. Exit from Window Procedure returning
810 * *ret to windows.
812 * FALSE - No match. Continue with default processing of character
813 * message.
816 static BOOL
817 ProcessChar (HWND hWnd, WPARAM wParam, OEInfo *oeinfo, long *ret)
819 int i;
820 int id;
823 *ret = 0;
824 if (wParam == 0x0d) {
826 * "Enter" is same as clicking on OK button.
828 PostMessage (hWnd, WM_COMMAND, IDOK,
829 MAKELONG (GetDlgItem (hWnd, IDOK), 1));
830 return (TRUE);
832 if (wParam == 0x1b || wParam == 0x03) {
834 * Esc is same as clicking on Cancel button.
836 PostMessage (hWnd, WM_COMMAND, IDCANCEL,
837 MAKELONG (GetDlgItem (hWnd, IDCANCEL), 1));
838 return (TRUE);
842 * Any of the custom buttons respond to this key?
844 for (i = 0; i < oeinfo->button_count; ++i) {
845 if (wParam == oeinfo->button_list[i].ch) {
846 id = oeinfo->button_list[i].id;
847 PostMessage (hWnd, WM_COMMAND, id,
848 MAKELONG (GetDlgItem (hWnd, id), 1));
849 return (TRUE);
854 * Lastly, is it the help key?
856 if (oeinfo->helpkey != 0 && wParam == oeinfo->helpkey) {
857 id = IDC_GETHELP;
858 PostMessage (hWnd, WM_COMMAND, id,
859 MAKELONG (GetDlgItem (hWnd, id), 1));
860 return (TRUE);
865 * Nothing we understand.
867 return (FALSE);
875 * Get a button position in the parent's coordinate space.
877 static void
878 GetBtnPos (HWND hPrnt, HWND hWnd, RECT *r)
880 GetWindowRect (hWnd, r);
881 ScreenToClient (hPrnt, (POINT *) r);
882 ScreenToClient (hPrnt, (POINT *) &r->right);
889 * Add buttons to the dialog box.
891 static void
892 BuildButtonList(HWND hDlg, MDlgButton *built_in, int biCount, MDlgButton *button_list)
894 RECT rDlg, rb1, rb2, r;
895 HDC hdc;
896 MDlgButton *pB; /* pointer to button struct*/
897 HWND hBtn, hBtn1, hBtn2; /* handle to buttons */
898 int btnCount; /* extra button count */
899 int rows, cols; /* button rows and columns */
900 int bpos; /* button position */
901 int i;
902 int maxstrIdx, maxstrLen; /* button w/ longest caption*/
903 DWORD textExtent; /* width of button caption */
904 int margine; /* left and right margines */
905 int btop, bleft; /* button position */
906 int bwidth, bheight, w; /* button size */
907 int bMinWidth; /* minimum buttonwidth */
908 int bvertSpace, bhorzSpace; /* button spacing */
909 int newWHeight, delta; /* window resizing */
910 TCHAR caption[128];
911 size_t ncaption;
912 HFONT btnFont;
913 SIZE size;
916 * Are there any buttons to add?
918 if(button_list == NULL)
919 return;
921 maxstrIdx = 0;
922 for(btnCount = 0; button_list[btnCount].ch != -1; ++btnCount){
923 if(lstrlen(button_list[btnCount].label) >
924 lstrlen(button_list[maxstrIdx].label))
925 maxstrIdx = btnCount;
928 if(btnCount == 0)
929 return;
933 * Get the size of the dialog box and the positions of the
934 * first and, if there is one, the second button. Calculate or
935 * default button offsets and positions.
937 GetClientRect(hDlg, &rDlg);
938 hBtn1 = GetDlgItem(hDlg, built_in[0].id);
939 GetBtnPos(hDlg, hBtn1, &rb1);
940 margine = rb1.left; /* left and right margine */
941 bheight = rb1.bottom - rb1.top; /* button width */
942 bwidth = rb1.right - rb1.left; /* button height. */
943 if(biCount > 1){
944 hBtn2 = GetDlgItem(hDlg, built_in[1].id);
945 GetBtnPos(hDlg, hBtn2, &rb2);
946 bvertSpace = rb2.top - rb1.top; /* vertical spacing */
948 else{
949 bvertSpace = bheight + BSPACE; /* vertical spacing */
954 * Get the button font.
956 btnFont = (HFONT) SendMessage (hBtn1, WM_GETFONT, 0, 0);
959 * Get the screen extent of the longest button label. min width
960 * is the extent, plus the average width of 5 extra characters for
961 * key stroke, plus margine.
963 hdc = GetDC (hBtn1);
964 ncaption = sizeof(caption)/sizeof(TCHAR);
965 _sntprintf(caption, ncaption, TEXT("%s '%s'"), button_list[maxstrIdx].label,
966 button_list[maxstrIdx].name);
968 maxstrLen = lstrlen(caption);
969 GetTextExtentPoint32(hdc, caption, maxstrLen, &size);
971 textExtent = size.cx;
973 ReleaseDC(hBtn1, hdc);
974 bMinWidth = LOWORD (textExtent) + (2 * (LOWORD(textExtent) / maxstrLen));
975 if(bwidth < bMinWidth)
976 bwidth = bMinWidth;
979 * Calculate button positions. If the buttons are going to extend
980 * past the right edge of the dialog box, shrink their width. But
981 * if they get narrower than the min width calculated above then
982 * increase the number of rows.
984 rows = 1;
985 do {
986 ++rows; /* More rows. */
987 w = bwidth; /* Original button width. */
988 cols = 1 + ((biCount + btnCount) - 1) / rows; /* Calc num cols. */
990 /* Need to srink button width? */
991 if (2 * margine + bwidth * cols + BSPACE * (cols - 1) > rDlg.right) {
992 w = ((rDlg.right - (2 * margine)) - (BSPACE * (cols-1))) / cols;
994 /* Did buttons get too narrow? */
995 } while (w < bMinWidth && cols > 1);
997 bwidth = w;
998 bhorzSpace = bwidth + BSPACE; /* horizontal spacing */
1002 * Resize window.
1004 newWHeight = rb1.top + (rows * bvertSpace);
1005 delta = newWHeight - rDlg.bottom;
1006 if (delta != 0) {
1007 GetWindowRect (hDlg, &r);
1008 MoveWindow (hDlg, r.left, r.top, r.right - r.left,
1009 (r.bottom - r.top) + delta, FALSE);
1015 * Create new buttons.
1017 for(bpos = 0; bpos < biCount + btnCount; ++bpos) {
1019 * Calculate position for this button.
1021 btop = rb1.top + (bpos % rows) * bvertSpace;
1022 bleft = rb1.left + (bpos / rows) * bhorzSpace;
1024 if(bpos < biCount){
1026 * Resize existing buttons.
1028 MoveWindow(GetDlgItem(hDlg, built_in[bpos].id),
1029 bleft, btop, bwidth, bheight, FALSE);
1031 else{
1032 i = bpos - biCount;
1033 pB = &button_list[i];
1035 _sntprintf(caption, ncaption, TEXT("%s '%s'"), pB->label, pB->name);
1036 hBtn = CreateWindow(TEXT("BUTTON"), caption,
1037 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
1038 bleft, btop, bwidth, bheight,
1039 hDlg, NULL, ghInstance, NULL);
1041 pB->id = BTN_FIRSTID + i;
1042 SetWindowLong(hBtn, GWL_ID, pB->id);
1043 SendMessage(hBtn, WM_SETFONT, (WPARAM)btnFont, MAKELPARAM (0, 0));
1045 /* Subclass button. */
1046 SetWindowLong(hBtn, GWL_WNDPROC, (DWORD)(FARPROC)gpBtnProc);
1047 EnableWindow(hBtn, TRUE);