Add support for tab-completion when selecting by rule
[alpine.git] / pico / osdep / msdlg.c
blob8aab4f7a63161c4a455f8b22dd4881e07be4cd87
1 /*
2 * ========================================================================
3 * Copyright 2006-2008 University of Washington
4 * Copyright 2013-2022 Eduardo Chappa
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 /*---------------------------------------------------------------------------
17 * Module: msdlg.c
19 * Thomas Unger
20 * Networks and Distributed Computing
21 * Computing and Communications
22 * University of Washington
23 * Administration Builiding, AG-44
24 * Seattle, Washington, 98195, USA
25 * Internet: tunger@cac.washington.edu
28 * Pine and Pico are registered trademarks of the University of Washington.
29 * No commercial use of these trademarks may be made without prior written
30 * permission of the University of Washington.
32 * Pine, Pico, and Pilot software and its included text are Copyright
33 * 1989-1998 by the University of Washington.
35 * The full text of our legal notices is contained in the file called
36 * CPYRIGHT, included with this distribution.
38 *--------------------------------------------------------------------------*/
40 #define WIN31
41 #define STRICT
43 #include <system.h>
44 #include <general.h>
46 #include <stdarg.h>
47 #include <ddeml.h>
49 #include "mswin.h"
50 #include "msmenu.h"
51 #include "resource.h"
53 #include "../estruct.h"
54 #include "../mode.h"
55 #include "../pico.h"
56 #include "../keydefs.h"
57 #include "../edef.h"
58 #include "../efunc.h"
59 #include "../utf8stub.h"
60 #include "../../pith/charconv/filesys.h"
63 extern HWND ghTTYWnd;
64 extern HINSTANCE ghInstance;
65 extern BOOL gfUseDialogs;
66 extern FILE *mswin_debugfile;
67 extern int mswin_debug;
68 extern TCHAR gszAppName[45];
72 #define BTN_FIRSTID 200
73 #define BSPACE 4
74 #define BWIDTH_MIN 120
77 typedef struct {
78 LPTSTR prompt; /* Prompt. */
79 LPTSTR string; /* Resulting string. */
80 int strlen; /* Length of buffer. */
81 int append; /* Append to existing string. */
82 int passwd; /* Passwd, don't echo (1 use asterisk, 10 space). */
83 unsigned flags; /* other flags. */
84 int dflt;
85 int cancel;
86 int button_count; /* Number of additional buttons. */
87 MDlgButton *button_list; /* List of other buttons. */
88 char **helptext; /* Help text. */
89 char helpkey;
90 int result;
91 } OEInfo;
94 static OEInfo gOEInfo;
95 static BOOL gDoHelpFirst;
96 static WNDPROC gpOldEditProc; /* Old Edit window proc. */
97 static WNDPROC gpEditProc;
98 static WNDPROC gpOldBtnProc; /* Old Edit window proc. */
99 static WNDPROC gpBtnProc;
105 * Forward function declaratins.
107 LONG FAR PASCAL __export EditProc(HWND hBtn, UINT msg, WPARAM wParam,
108 LPARAM lParam);
109 LONG FAR PASCAL __export ButtonProc(HWND hBtn, UINT msg, WPARAM wParam,
110 LPARAM lParam);
111 BOOL CALLBACK __export mswin_dialog_proc (HWND hDlg, UINT uMsg,
112 WPARAM wParam, LPARAM lParam);
113 BOOL CALLBACK __export mswin_select_proc (HWND hDlg, UINT uMsg,
114 WPARAM wParam, LPARAM lParam);
115 LONG FAR PASCAL __export KBCProc (HWND hWnd, UINT uMsg, WPARAM wParam,
116 LPARAM lParam);
119 static void BuildButtonList (HWND, MDlgButton *, int, MDlgButton *);
120 static BOOL ProcessChar (HWND, WPARAM, OEInfo *, long *);
121 static void GetBtnPos (HWND, HWND, RECT *);
123 int mswin_yesno_lptstr (LPTSTR);
127 mswin_usedialog (void)
129 return (gfUseDialogs);
134 /*----------------------------------------------------------------------
137 Generic text entry. Prompt user for a string.
140 Args:
141 prompt -- The string to prompt with
142 string -- the buffer result is returned in, and original string (if
143 any) is passed in.
144 field_len -- Maximum length of string to accept
145 append_current -- flag indicating string should not be truncated before
146 accepting input
147 passwd -- a pass word is being fetch. Don't echo on screen
148 button_list -- pointer to array of mDlgButton's. input chars matching
149 those in list return value from list. Nearly identical
150 to Pine's ESCKEY structure.
151 help -- Array of strings for help text in bottom screen lines
152 flags -- flags
154 Result: editing input string
155 returns -1 unexpected errors
156 returns 0 typed CR or pressed "OK"
157 returns 1 typed ESC or pressed "Cancel"
158 returns 3 typed ^G or PF1 (help)
159 returns 4 typed ^L for a screen redraw
160 or one of the other return values defined in button_list.
162 WARNING: Care is required with regard to the escape_list processing.
163 The passed array is terminated with an entry that has ch = -1.
164 Function key labels and key strokes need to be setup externally!
165 Traditionally, a return value of 2 is used for ^T escapes.
168 Note About Help: We don't get the help text on the first call. If we
169 want help text we have to return 3. We'll get called again
170 with help text. To make it look good, we want to display
171 this the second time we're called. But we don't want to
172 display the help text every time we're called with help
173 text. To know the difference we set gDoHelpFirst when
174 exiting to request help text. If this is set then we
175 display help. If not, then no help.
178 ----------------------------------------------------------------------*/
182 * xxx implement flags.
183 * xxx display.d uses flags QFFILE, QDEFLT, and QBOBUF
187 mswin_dialog(UCS *prompt, UCS *buf, int nbuf, int append_current,
188 int passwd, MDlgButton *button_list, char **help, unsigned flags)
190 DLGPROC dlgprc;
191 int c;
192 LPTSTR promptlpt, buflpt, b;
193 UCS *ucs;
195 mswin_flush();
197 promptlpt = ucs4_to_lptstr(prompt);
198 buflpt = (LPTSTR) fs_get(nbuf * sizeof(TCHAR));
199 b = ucs4_to_lptstr(buf);
200 if(b){
201 int i;
203 for(i = 0; i < nbuf && b[i]; i++)
204 buflpt[i] = b[i];
206 if(i < nbuf)
207 buflpt[i] = 0;
209 buflpt[nbuf-1] = 0;
210 fs_give((void **) &b);
213 gOEInfo.prompt = promptlpt;
214 gOEInfo.string = buflpt;
215 gOEInfo.strlen = nbuf;
216 gOEInfo.append = append_current;
217 gOEInfo.passwd = passwd;
218 gOEInfo.helptext = help;
219 gOEInfo.helpkey = 0x07; /* ^G */
220 gOEInfo.flags = flags;
221 gOEInfo.button_list = button_list;
222 gOEInfo.result = 0;
224 for (c = 0; button_list && button_list[c].ch != -1; ++c)
227 gOEInfo.button_count = c;
229 gpEditProc = (WNDPROC) MakeProcInstance((FARPROC) EditProc, ghInstance);
230 gpBtnProc = (WNDPROC) MakeProcInstance((FARPROC) ButtonProc, ghInstance);
231 dlgprc = (DLGPROC) MakeProcInstance((FARPROC) mswin_dialog_proc, ghInstance);
233 DialogBox(ghInstance, MAKEINTRESOURCE (IDD_OPTIONALYENTER), ghTTYWnd, dlgprc);
235 FreeProcInstance((FARPROC) dlgprc);
236 FreeProcInstance((FARPROC) gpBtnProc);
237 FreeProcInstance((FARPROC) gpEditProc);
239 if(promptlpt)
240 fs_give((void **) &promptlpt);
242 ucs = lptstr_to_ucs4(buflpt);
243 if(ucs){
244 int i;
246 for(i = 0; i < nbuf && ucs[i]; i++)
247 buf[i] = ucs[i];
249 if(i < nbuf)
250 buf[i] = '\0';
252 buf[nbuf-1] = '\0';
253 fs_give((void **) &ucs);
256 return(gOEInfo.result);
263 * Dialog procedure for the generic text entry dialog box.
265 BOOL CALLBACK __export
266 mswin_dialog_proc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
268 BOOL ret = FALSE;
269 int i;
270 int biCount;
271 HWND hEdit;
272 MDlgButton built_in[3];
273 long l;
276 switch (uMsg) {
277 case WM_INITDIALOG:
279 * Set the prompt text.
281 SetDlgItemText(hDlg, IDC_PROMPT, gOEInfo.prompt);
284 * Set the initial edit text and configure the edit control
286 if (!gOEInfo.append)
287 *gOEInfo.string = '\0';
289 SetDlgItemText(hDlg, IDC_RESPONCE, gOEInfo.string);
291 SendDlgItemMessage (hDlg, IDC_RESPONCE, EM_SETPASSWORDCHAR,
292 (gOEInfo.passwd == 1) ? '*' : gOEInfo.passwd ? ' ' : 0, 0L);
293 SendDlgItemMessage (hDlg, IDC_RESPONCE, EM_LIMITTEXT,
294 gOEInfo.strlen - 1, 0L);
295 l = (long) lstrlen(gOEInfo.string);
296 SendDlgItemMessage(hDlg, IDC_RESPONCE, EM_SETSEL, 0, l);
297 hEdit = GetDlgItem (hDlg, IDC_RESPONCE);
298 SetFocus (hEdit);
299 gpOldEditProc = (WNDPROC) GetWindowLong (hEdit, GWL_WNDPROC);
300 SetWindowLong (hEdit, GWL_WNDPROC, (DWORD)(FARPROC)gpEditProc);
304 * Subclass the standard buttons and build buttons for each item
305 * in the button_list.
307 gpOldBtnProc = (WNDPROC) GetWindowLong (GetDlgItem (hDlg, IDOK), GWL_WNDPROC);
308 SetWindowLong (GetDlgItem (hDlg, IDOK), GWL_WNDPROC,
309 (DWORD)(FARPROC)gpBtnProc);
310 SetWindowLong (GetDlgItem (hDlg, IDCANCEL), GWL_WNDPROC,
311 (DWORD)(FARPROC)gpBtnProc);
313 memset (&built_in, 0, sizeof (built_in));
314 built_in[0].id = IDOK;
315 built_in[1].id = IDCANCEL;
316 if (1) {
317 built_in[2].id = IDC_GETHELP;
318 biCount = 3;
320 else {
321 DestroyWindow (GetDlgItem (hDlg, IDC_GETHELP));
322 biCount = 2;
324 BuildButtonList (hDlg, built_in, biCount, gOEInfo.button_list);
327 if (gOEInfo.helptext && gDoHelpFirst) {
328 mswin_showhelpmsg ((WINHAND)hDlg, gOEInfo.helptext);
329 gDoHelpFirst = FALSE;
331 return (0);
334 case WM_ACTIVATE:
336 * Keep focus on the edit item so key strokes are always
337 * received.
339 SetFocus (GetDlgItem (hDlg, IDC_RESPONCE));
340 break;
343 case WM_COMMAND:
344 switch (wParam) {
345 case IDOK:
347 * Normal exit. Accept the new text.
349 GetDlgItemText(hDlg, IDC_RESPONCE, gOEInfo.string, gOEInfo.strlen);
351 gOEInfo.result = 0;
352 EndDialog (hDlg, gOEInfo.result);
353 ret = TRUE;
354 break;
356 case IDCANCEL:
358 * Cancel operation. Don't retrieve new text.
360 gOEInfo.result = 1;
361 EndDialog (hDlg, gOEInfo.result);
362 ret = TRUE;
363 break;
365 case IDC_GETHELP:
367 * Get help. If we have help text display it. If not,
368 * return value 3, which tells the caller to provide us
369 * with help text.
371 if (gOEInfo.helptext != NULL) {
372 mswin_showhelpmsg ((WINHAND)hDlg, gOEInfo.helptext);
374 else {
375 GetDlgItemText(hDlg, IDC_RESPONCE, gOEInfo.string, gOEInfo.strlen);
376 gOEInfo.result = 3;
377 gDoHelpFirst = TRUE;
378 EndDialog (hDlg, gOEInfo.result);
380 ret = TRUE;
381 break;
383 default:
385 * Search button list for button with this ID. If found
386 * return it's result code. Retrieve text.
388 if ( wParam >= BTN_FIRSTID &&
389 wParam < BTN_FIRSTID + (WPARAM) gOEInfo.button_count) {
390 GetDlgItemText(hDlg, IDC_RESPONCE, gOEInfo.string, gOEInfo.strlen);
391 i = wParam - BTN_FIRSTID;
392 gOEInfo.result = gOEInfo.button_list[i].rval;
393 EndDialog (hDlg, gOEInfo.result);
394 ret = TRUE;
399 return (ret) ;
406 * Subclassed window procedure for Edit control on generic text
407 * entry dialog box.
409 LONG FAR PASCAL __export
410 EditProc (HWND hEdit, UINT msg, WPARAM wParam, LPARAM lParam)
412 HWND hDlg;
413 LONG ret;
415 hDlg = GetParent (hEdit);
417 if (msg == WM_GETDLGCODE) {
419 * Tell windows that we want to receive ALL keystrokes.
421 ret = CallWindowProc (gpOldEditProc, hEdit, msg, wParam, lParam);
422 ret |= DLGC_WANTALLKEYS;
423 return (ret);
427 if (msg == WM_CHAR) {
429 * See if the character typed is a shortcut for any of the
430 * buttons.
432 if (ProcessChar (hDlg, wParam, &gOEInfo, &ret))
433 return (ret);
435 /* No... Fall through for default processing. */
438 return (CallWindowProc (gpOldEditProc, hEdit, msg, wParam, lParam));
445 * Subclass button windows.
447 * These buttons will automatically return the input focus to
448 * a control with id IDC_RESPONCE.
450 LONG FAR PASCAL __export
451 ButtonProc (HWND hBtn, UINT uMsg, WPARAM wParam, LPARAM lParam)
453 HWND hDlg;
454 LONG ret;
457 if (uMsg == WM_LBUTTONUP || uMsg == WM_MBUTTONUP || uMsg == WM_RBUTTONUP) {
459 * On mouse button up restore input focus to IDC_RESPONCE, which
460 * processes keyboard input.
462 ret = CallWindowProc (gpOldBtnProc, hBtn, uMsg, wParam, lParam);
463 hDlg = GetParent (hBtn);
464 SetFocus (GetDlgItem (hDlg, IDC_RESPONCE));
465 return (ret);
468 return (CallWindowProc (gpOldBtnProc, hBtn, uMsg, wParam, lParam));
474 mswin_yesno(UCS *prompt_ucs4)
476 int ret;
477 LPTSTR prompt_lptstr;
479 prompt_lptstr = ucs4_to_lptstr(prompt_ucs4);
480 ret = mswin_yesno_lptstr(prompt_lptstr);
481 fs_give((void **) &prompt_lptstr);
483 return(ret);
488 mswin_yesno_utf8(char *prompt_utf8)
490 int ret;
491 LPTSTR prompt_lptstr;
493 prompt_lptstr = utf8_to_lptstr(prompt_utf8);
494 ret = mswin_yesno_lptstr(prompt_lptstr);
495 fs_give((void **) &prompt_lptstr);
497 return(ret);
501 * Ask a yes/no question with the MessageBox procedure.
503 * Returns:
504 * 0 - Cancel operation.
505 * 1 - "Yes" was selected.
506 * 2 - "No" was selected.
509 mswin_yesno_lptstr(LPTSTR prompt)
511 int ret;
513 mswin_flush ();
515 mswin_killsplash();
517 ret = MessageBox (ghTTYWnd, prompt, gszAppName,
518 MB_APPLMODAL | MB_ICONQUESTION | MB_YESNOCANCEL);
520 switch (ret) {
521 case IDYES: ret = 1; break;
522 case IDNO: ret = 2; break;
523 default:
524 case IDCANCEL: ret = 0; break;
526 return (ret);
530 /*----------------------------------------------------------------------
533 Generic selection routine. Display a prompt and a set of buttons for
534 each possible answer.
538 Args:
539 prompt -- The string to prompt with.
540 button_list -- pointer to array of mDlgButton's. input chars
541 matching those in list return value from
542 mlist. Nearly identical to Pine's ESCKEY
543 mstructure.
544 dflt -- Value returned when "Enter" is pressed.
545 on_ctrl_C -- Value returned to cancel dialog (ESC).
546 help -- Array of strings for help text in bottom screen
547 lines
548 flags -- flags
550 Result:
551 dflt -- Default option selected.
552 on_ctrl_C -- Calcel operation.
553 or one of the other return values defined in button_list.
556 Note: To process keyboard input we use a custom dialog control
557 which is invisible but always has the input focus.
559 ----------------------------------------------------------------------*/
562 mswin_select(char *utf8prompt, MDlgButton *button_list, int dflt, int on_ctrl_C,
563 char **help, unsigned flags)
565 WNDCLASS wndclass;
566 DLGPROC dlgprc;
567 WNDPROC kbcProc;
568 int c;
569 LPTSTR promptlpt;
571 mswin_flush ();
573 promptlpt = utf8_to_lptstr(utf8prompt);
576 * Setup dialog config structure.
578 gOEInfo.prompt = promptlpt;
579 gOEInfo.string = NULL;
580 gOEInfo.strlen = 0;
581 gOEInfo.append = 0;
582 gOEInfo.passwd = 0;
583 gOEInfo.helptext = help;
584 gOEInfo.helpkey = 'g';
585 gOEInfo.dflt = dflt;
586 gOEInfo.cancel = on_ctrl_C;
587 gOEInfo.flags = flags;
588 gOEInfo.button_list = button_list;
589 gOEInfo.result = 0;
592 * Count the buttons.
594 for(c = 0; button_list && button_list[c].ch != -1; ++c)
597 gOEInfo.button_count = c;
600 * Register the class for the user control which will receive keyboard
601 * input events.
603 kbcProc = (WNDPROC) MakeProcInstance((FARPROC) KBCProc, ghInstance);
604 wndclass.style = 0;
605 wndclass.lpfnWndProc = kbcProc;
606 wndclass.cbClsExtra = 0;
607 wndclass.cbWndExtra = 0;
608 wndclass.hInstance = ghInstance;
609 wndclass.hIcon = NULL;
610 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
611 wndclass.hbrBackground = 0;
612 wndclass.lpszMenuName = NULL;
613 wndclass.lpszClassName = TEXT("KeyboardCapture");
615 if(RegisterClass (&wndclass) == 0){
617 * There is no good return value to indicate an error.
618 * Cancel the operation.
620 return(on_ctrl_C);
624 * Make other procedure instances.
626 dlgprc = (DLGPROC) MakeProcInstance((FARPROC) mswin_select_proc, ghInstance);
627 gpBtnProc = (WNDPROC) MakeProcInstance((FARPROC) ButtonProc, ghInstance);
630 * Bring up the dialog box.
632 DialogBox(ghInstance, MAKEINTRESOURCE(IDD_SELECT), ghTTYWnd, dlgprc);
635 * Free the proc instances and window class.
637 FreeProcInstance((FARPROC) dlgprc);
638 FreeProcInstance((FARPROC) gpBtnProc);
639 UnregisterClass (TEXT("KeyboardCapture"), ghInstance);
640 FreeProcInstance((FARPROC) kbcProc);
642 if(promptlpt)
643 fs_give((void **) &promptlpt);
645 return(gOEInfo.result);
652 * Dialog procedure for the button selection dialog box.
654 BOOL CALLBACK __export
655 mswin_select_proc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
657 BOOL ret = FALSE;
658 int i;
659 int biCount;
660 MDlgButton built_in[3];
662 if (mswin_debug >= 1)
663 fprintf (mswin_debugfile, "Select: uMsg x%x (%d), wParam x%x, lParam x%lx\n",
664 uMsg, uMsg, wParam, lParam);
667 switch (uMsg) {
668 case WM_INITDIALOG:
670 * Show the prompt.
672 SetDlgItemText(hDlg, IDC_PROMPT, gOEInfo.prompt);
675 * Set focus to the invisible custom control
677 SetFocus (GetDlgItem (hDlg, IDC_RESPONCE));
680 * Subclass the standard buttons and build buttons for each item
681 * in the button_list.
683 gpOldBtnProc = (WNDPROC) GetWindowLong (GetDlgItem (hDlg, IDCANCEL), GWL_WNDPROC);
684 SetWindowLong (GetDlgItem (hDlg, IDC_GETHELP), GWL_WNDPROC,
685 (DWORD)(FARPROC)gpBtnProc);
686 SetWindowLong (GetDlgItem (hDlg, IDCANCEL), GWL_WNDPROC,
687 (DWORD)(FARPROC)gpBtnProc);
689 memset (&built_in, 0, sizeof (built_in));
690 built_in[0].id = IDCANCEL;
691 if (gOEInfo.helptext != NULL) {
692 built_in[1].id = IDC_GETHELP;
693 biCount = 2;
695 else {
696 DestroyWindow (GetDlgItem (hDlg, IDC_GETHELP));
697 gOEInfo.helpkey = 0;
698 biCount = 1;
700 BuildButtonList (hDlg, built_in, biCount, gOEInfo.button_list);
701 gDoHelpFirst = FALSE;
702 return (0);
705 case WM_ACTIVATE:
707 * Keep focus on the custom control item so key strokes are always
708 * received.
710 SetFocus (GetDlgItem (hDlg, IDC_RESPONCE));
711 break;
714 case WM_COMMAND:
715 switch (wParam) {
716 case IDOK:
717 gOEInfo.result = gOEInfo.dflt;
718 EndDialog (hDlg, gOEInfo.result);
719 ret = TRUE;
720 break;
722 case IDCANCEL:
723 gOEInfo.result = gOEInfo.cancel;
724 EndDialog (hDlg, gOEInfo.result);
725 ret = TRUE;
726 break;
728 case IDC_GETHELP:
730 * Get help. If we have help text display it. If not,
731 * return value 3, which tells the caller to provide us
732 * with help text.
734 if (gOEInfo.helptext != NULL) {
735 mswin_showhelpmsg ((WINHAND) hDlg, gOEInfo.helptext);
737 ret = TRUE;
738 break;
740 default:
741 if ( wParam >= BTN_FIRSTID &&
742 wParam < BTN_FIRSTID + (WPARAM) gOEInfo.button_count) {
743 i = wParam - BTN_FIRSTID;
744 gOEInfo.result = gOEInfo.button_list[i].rval;
745 EndDialog (hDlg, gOEInfo.result);
746 ret = TRUE;
751 return (ret) ;
757 * Window procedure for the hidden custom control.
759 LONG FAR PASCAL __export
760 KBCProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
762 LONG ret;
765 if (uMsg == WM_GETDLGCODE) {
767 * Tell windows that we want all the character messages.
769 ret = DefWindowProc (hWnd, uMsg, wParam, lParam);
770 ret |= DLGC_WANTALLKEYS;
771 return (ret);
775 if (uMsg == WM_CHAR) {
777 * See if the character typed is a shortcut for any of the
778 * buttons.
780 if (ProcessChar (GetParent (hWnd), wParam, &gOEInfo, &ret))
781 return (ret);
783 /* Fall through for default processing. */
786 return (DefWindowProc (hWnd, uMsg, wParam, lParam));
795 * Check if character activates any button. If it does, send WM_COMMAND
796 * to target window.
798 * Args:
799 * hWnd - Window to send message to.
800 * wParam - character typed.
801 * oeinfo - dialog config structure. Contains button list.
802 * ret - value to return to windows.
804 * Returns:
805 * TRUE - Found a match. Exit from Window Procedure returning
806 * *ret to windows.
808 * FALSE - No match. Continue with default processing of character
809 * message.
812 static BOOL
813 ProcessChar (HWND hWnd, WPARAM wParam, OEInfo *oeinfo, long *ret)
815 int i;
816 int id;
819 *ret = 0;
820 if (wParam == 0x0d) {
822 * "Enter" is same as clicking on OK button.
824 PostMessage (hWnd, WM_COMMAND, IDOK,
825 MAKELONG (GetDlgItem (hWnd, IDOK), 1));
826 return (TRUE);
828 if (wParam == 0x1b || wParam == 0x03) {
830 * Esc is same as clicking on Cancel button.
832 PostMessage (hWnd, WM_COMMAND, IDCANCEL,
833 MAKELONG (GetDlgItem (hWnd, IDCANCEL), 1));
834 return (TRUE);
838 * Any of the custom buttons respond to this key?
840 for (i = 0; i < oeinfo->button_count; ++i) {
841 if (wParam == oeinfo->button_list[i].ch) {
842 id = oeinfo->button_list[i].id;
843 PostMessage (hWnd, WM_COMMAND, id,
844 MAKELONG (GetDlgItem (hWnd, id), 1));
845 return (TRUE);
850 * Lastly, is it the help key?
852 if (oeinfo->helpkey != 0 && wParam == oeinfo->helpkey) {
853 id = IDC_GETHELP;
854 PostMessage (hWnd, WM_COMMAND, id,
855 MAKELONG (GetDlgItem (hWnd, id), 1));
856 return (TRUE);
861 * Nothing we understand.
863 return (FALSE);
871 * Get a button position in the parent's coordinate space.
873 static void
874 GetBtnPos (HWND hPrnt, HWND hWnd, RECT *r)
876 GetWindowRect (hWnd, r);
877 ScreenToClient (hPrnt, (POINT *) r);
878 ScreenToClient (hPrnt, (POINT *) &r->right);
885 * Add buttons to the dialog box.
887 static void
888 BuildButtonList(HWND hDlg, MDlgButton *built_in, int biCount, MDlgButton *button_list)
890 RECT rDlg, rb1, rb2, r;
891 HDC hdc;
892 MDlgButton *pB; /* pointer to button struct*/
893 HWND hBtn, hBtn1, hBtn2; /* handle to buttons */
894 int btnCount; /* extra button count */
895 int rows, cols; /* button rows and columns */
896 int bpos; /* button position */
897 int i;
898 int maxstrIdx, maxstrLen; /* button w/ longest caption*/
899 DWORD textExtent; /* width of button caption */
900 int margine; /* left and right margines */
901 int btop, bleft; /* button position */
902 int bwidth, bheight, w; /* button size */
903 int bMinWidth; /* minimum buttonwidth */
904 int bvertSpace, bhorzSpace; /* button spacing */
905 int newWHeight, delta; /* window resizing */
906 TCHAR caption[128];
907 size_t ncaption;
908 HFONT btnFont;
909 SIZE size;
912 * Are there any buttons to add?
914 if(button_list == NULL)
915 return;
917 maxstrIdx = 0;
918 for(btnCount = 0; button_list[btnCount].ch != -1; ++btnCount){
919 if(lstrlen(button_list[btnCount].label) >
920 lstrlen(button_list[maxstrIdx].label))
921 maxstrIdx = btnCount;
924 if(btnCount == 0)
925 return;
929 * Get the size of the dialog box and the positions of the
930 * first and, if there is one, the second button. Calculate or
931 * default button offsets and positions.
933 GetClientRect(hDlg, &rDlg);
934 hBtn1 = GetDlgItem(hDlg, built_in[0].id);
935 GetBtnPos(hDlg, hBtn1, &rb1);
936 margine = rb1.left; /* left and right margine */
937 bheight = rb1.bottom - rb1.top; /* button width */
938 bwidth = rb1.right - rb1.left; /* button height. */
939 if(biCount > 1){
940 hBtn2 = GetDlgItem(hDlg, built_in[1].id);
941 GetBtnPos(hDlg, hBtn2, &rb2);
942 bvertSpace = rb2.top - rb1.top; /* vertical spacing */
944 else{
945 bvertSpace = bheight + BSPACE; /* vertical spacing */
950 * Get the button font.
952 btnFont = (HFONT) SendMessage (hBtn1, WM_GETFONT, 0, 0);
955 * Get the screen extent of the longest button label. min width
956 * is the extent, plus the average width of 5 extra characters for
957 * key stroke, plus margine.
959 hdc = GetDC (hBtn1);
960 ncaption = sizeof(caption)/sizeof(TCHAR);
961 _sntprintf(caption, ncaption, TEXT("%s '%s'"), button_list[maxstrIdx].label,
962 button_list[maxstrIdx].name);
964 maxstrLen = lstrlen(caption);
965 GetTextExtentPoint32(hdc, caption, maxstrLen, &size);
967 textExtent = size.cx;
969 ReleaseDC(hBtn1, hdc);
970 bMinWidth = LOWORD (textExtent) + (2 * (LOWORD(textExtent) / maxstrLen));
971 if(bwidth < bMinWidth)
972 bwidth = bMinWidth;
975 * Calculate button positions. If the buttons are going to extend
976 * past the right edge of the dialog box, shrink their width. But
977 * if they get narrower than the min width calculated above then
978 * increase the number of rows.
980 rows = 1;
981 do {
982 ++rows; /* More rows. */
983 w = bwidth; /* Original button width. */
984 cols = 1 + ((biCount + btnCount) - 1) / rows; /* Calc num cols. */
986 /* Need to shrink button width? */
987 if (2 * margine + bwidth * cols + BSPACE * (cols - 1) > rDlg.right) {
988 w = ((rDlg.right - (2 * margine)) - (BSPACE * (cols-1))) / cols;
990 /* Did buttons get too narrow? */
991 } while (w < bMinWidth && cols > 1);
993 bwidth = w;
994 bhorzSpace = bwidth + BSPACE; /* horizontal spacing */
998 * Resize window.
1000 newWHeight = rb1.top + (rows * bvertSpace);
1001 delta = newWHeight - rDlg.bottom;
1002 if (delta != 0) {
1003 GetWindowRect (hDlg, &r);
1004 MoveWindow (hDlg, r.left, r.top, r.right - r.left,
1005 (r.bottom - r.top) + delta, FALSE);
1011 * Create new buttons.
1013 for(bpos = 0; bpos < biCount + btnCount; ++bpos) {
1015 * Calculate position for this button.
1017 btop = rb1.top + (bpos % rows) * bvertSpace;
1018 bleft = rb1.left + (bpos / rows) * bhorzSpace;
1020 if(bpos < biCount){
1022 * Resize existing buttons.
1024 MoveWindow(GetDlgItem(hDlg, built_in[bpos].id),
1025 bleft, btop, bwidth, bheight, FALSE);
1027 else{
1028 i = bpos - biCount;
1029 pB = &button_list[i];
1031 _sntprintf(caption, ncaption, TEXT("%s '%s'"), pB->label, pB->name);
1032 hBtn = CreateWindow(TEXT("BUTTON"), caption,
1033 WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
1034 bleft, btop, bwidth, bheight,
1035 hDlg, NULL, ghInstance, NULL);
1037 pB->id = BTN_FIRSTID + i;
1038 SetWindowLong(hBtn, GWL_ID, pB->id);
1039 SendMessage(hBtn, WM_SETFONT, (WPARAM)btnFont, MAKELPARAM (0, 0));
1041 /* Subclass button. */
1042 SetWindowLong(hBtn, GWL_WNDPROC, (DWORD)(FARPROC)gpBtnProc);
1043 EnableWindow(hBtn, TRUE);