1 /*
2 
3     Minimum Profit - A Text Editor
4     Win32 driver.
5 
6     ttcdt <dev@triptico.com> et al.
7 
8     This software is released into the public domain.
9     NO WARRANTY. See file LICENSE for details.
10 
11 */
12 
13 #include "config.h"
14 
15 #ifdef CONFOPT_WIN32
16 
17 #include <stdio.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <wchar.h>
21 
22 #include <windows.h>
23 #include <commctrl.h>
24 #include <shlobj.h>
25 
26 #include "mpdm.h"
27 #include "mpsl.h"
28 
29 #include "mp.h"
30 
31 /** data **/
32 
33 /* the instance */
34 HINSTANCE hinst;
35 
36 /* the windows */
37 HWND hwnd = NULL;
38 HWND hwtabs = NULL;
39 HWND hwstatus = NULL;
40 
41 /* font handlers and metrics */
42 HFONT font_normal = NULL;
43 HFONT font_underline = NULL;
44 int font_width = 0;
45 int font_height = 0;
46 
47 /* height of the tab set */
48 int tab_height = 28;
49 
50 /* height of the status bar */
51 int status_height = 16;
52 
53 int is_wm_keydown = 0;
54 
55 /* colors */
56 #define MAX_COLORS 100
57 static COLORREF inks[MAX_COLORS];
58 static COLORREF papers[MAX_COLORS];
59 int underlines[MAX_COLORS];
60 HBRUSH bgbrush;
61 
62 /* code for the 'normal' attribute */
63 static int normal_attr = 0;
64 
65 /* the menu */
66 static HMENU menu = NULL;
67 
68 /* mp.drv.form() controls */
69 
70 static mpdm_t form_args = NULL;
71 static mpdm_t form_values = NULL;
72 
73 /* mouse down flag */
74 static int mouse_down = 0;
75 
76 /** code **/
77 
update_window_size(void)78 static void update_window_size(void)
79 /* updates the viewport size in characters */
80 {
81     RECT rect;
82     int tx, ty;
83     mpdm_t v;
84 
85     if (font_width && font_height) {
86         GetClientRect(hwnd, &rect);
87 
88         /* calculate the size in chars */
89         tx = ((rect.right - rect.left) / font_width) + 1;
90         ty = (rect.bottom - rect.top - tab_height) / font_height;
91 
92         /* store the 'window' size */
93         v = mpdm_get_wcs(MP, L"window");
94         mpdm_set_wcs(v, MPDM_I(tx), L"tx");
95         mpdm_set_wcs(v, MPDM_I(ty), L"ty");
96     }
97 }
98 
99 
build_fonts(HDC hdc)100 static void build_fonts(HDC hdc)
101 /* build the fonts */
102 {
103     TEXTMETRIC tm;
104     int n;
105     mpdm_t v = NULL;
106     mpdm_t c;
107     int font_size      = 10;
108     char *font_face    = "Lucida Console";
109     double font_weight = 0.0;
110 
111     if (font_normal != NULL) {
112         SelectObject(hdc, GetStockObject(SYSTEM_FONT));
113         DeleteObject(font_normal);
114     }
115 
116     /* get current configuration */
117     if ((c = mpdm_get_wcs(MP, L"config")) != NULL) {
118         if ((v = mpdm_get_wcs(c, L"font_size")) != NULL)
119             font_size = mpdm_ival(v);
120         else
121             mpdm_set_wcs(c, MPDM_I(font_size), L"font_size");
122 
123         if ((v = mpdm_get_wcs(c, L"font_weight")) != NULL)
124             font_weight = mpdm_rval(v) * 1000.0;
125         else
126             mpdm_set_wcs(c, MPDM_R(font_weight / 1000.0), L"font_weight");
127 
128         if ((v = mpdm_get_wcs(c, L"font_face")) != NULL) {
129             v = mpdm_ref(MPDM_2MBS(v->data));
130             font_face = (char *) v->data;
131         }
132         else
133             mpdm_set_wcs(c, MPDM_MBS(font_face), L"font_face");
134     }
135 
136     /* create fonts */
137     n = -MulDiv(font_size, GetDeviceCaps(hdc, LOGPIXELSY), 72);
138 
139     if ((font_normal = CreateFont(n, 0, 0, 0, (int) font_weight, 0, 0,
140                              0, 0, 0, 0, 0, 0, font_face)) == NULL)
141         font_normal = CreateFont(n, 0, 0, 0, 0, 0, 0,
142                              0, 0, 0, 0, 0, 0, font_face);
143 
144     if ((font_underline = CreateFont(n, 0, 0, 0, (int) font_weight, 0, 1,
145                                 0, 0, 0, 0, 0, 0, font_face)) == NULL)
146         font_underline = CreateFont(n, 0, 0, 0, 0, 0, 1,
147                                 0, 0, 0, 0, 0, 0, font_face);
148 
149     SelectObject(hdc, font_normal);
150     GetTextMetrics(hdc, &tm);
151 
152     /* store sizes */
153     font_height = tm.tmHeight;
154     font_width  = tm.tmAveCharWidth;
155 
156     update_window_size();
157 
158     mpdm_unref(v);
159 }
160 
161 
build_colors(void)162 static void build_colors(void)
163 /* builds the colors */
164 {
165     mpdm_t colors;
166     mpdm_t v, i;
167     int n, c;
168 
169     /* gets the color definitions and attribute names */
170     colors = mpdm_get_wcs(MP, L"colors");
171 
172     /* loop the colors */
173     n = c = 0;
174     while (mpdm_iterator(colors, &c, &v, &i)) {
175         int m;
176         mpdm_t w = mpdm_get_wcs(v, L"gui");
177 
178         /* store the 'normal' attribute */
179         if (wcscmp(mpdm_string(i), L"normal") == 0)
180             normal_attr = n;
181 
182         /* store the attr */
183         mpdm_set_wcs(v, MPDM_I(n), L"attr");
184 
185         m = mpdm_ival(mpdm_get_i(w, 0));
186         inks[n] = ((m & 0x000000ff) << 16) | ((m & 0x0000ff00)) | ((m & 0x00ff0000) >> 16);
187         m = mpdm_ival(mpdm_get_i(w, 1));
188         papers[n] = ((m & 0x000000ff) << 16) | ((m & 0x0000ff00)) | ((m & 0x00ff0000) >> 16);
189 
190         /* flags */
191         w = mpdm_get_wcs(v, L"flags");
192 
193         underlines[n] = mpdm_seek_wcs(w, L"underline", 1) != -1 ? 1 : 0;
194 
195         if (mpdm_seek_wcs(w, L"reverse", 1) != -1) {
196             COLORREF t;
197 
198             t = inks[n];
199             inks[n] = papers[n];
200             papers[n] = t;
201         }
202 
203         n++;
204     }
205 
206     /* create the background brush */
207     bgbrush = CreateSolidBrush(papers[normal_attr]);
208 }
209 
210 
build_menu(void)211 static void build_menu(void)
212 /* builds the menu */
213 {
214     int n;
215     mpdm_t m;
216     int win32_menu_id = 1000;
217 
218     /* gets the current menu */
219     m = mpdm_get_wcs(MP, L"menu");
220 
221     if (menu != NULL)
222         DestroyMenu(menu);
223 
224     menu = CreateMenu();
225 
226     for (n = 0; n < mpdm_size(m); n++) {
227         mpdm_t mi, v, l;
228         int i;
229         HMENU submenu = CreatePopupMenu();
230 
231         /* get the label and the items */
232         mi = mpdm_get_i(m, n);
233         v = mpdm_gettext(mpdm_get_i(mi, 0));
234         l = mpdm_get_i(mi, 1);
235 
236         /* create the submenus */
237         for (i = 0; i < mpdm_size(l); i++) {
238             /* get the action */
239             mpdm_t v = mpdm_get_i(l, i);
240 
241             /* if the action is a separator... */
242             if (*((wchar_t *) v->data) == L'-')
243                 AppendMenu(submenu, MF_SEPARATOR, 0, NULL);
244             else {
245                 MENUITEMINFO mi;
246                 mpdm_t d = mpdm_ref(mp_menu_label(v));
247 
248                 /* set the string */
249                 AppendMenuW(submenu, MF_STRING, win32_menu_id, mpdm_string(d));
250 
251                 mpdm_unref(d);
252 
253                 /* store the action inside the menu */
254                 memset(&mi, '\0', sizeof(mi));
255                 mi.cbSize = sizeof(mi);
256                 mi.fMask = MIIM_DATA;
257                 mi.dwItemData = (unsigned long) v;
258 
259                 SetMenuItemInfo(submenu, win32_menu_id, FALSE, &mi);
260 
261                 win32_menu_id++;
262             }
263         }
264 
265         /* now store the popup inside the menu */
266         AppendMenuW(menu, MF_STRING | MF_POPUP, (UINT) submenu, mpdm_string(v));
267     }
268 
269     SetMenu(hwnd, menu);
270 }
271 
272 
draw_filetabs(void)273 static void draw_filetabs(void)
274 /* draws the filetabs */
275 {
276     static mpdm_t prev = NULL;
277     mpdm_t names;
278     int n;
279 
280     names = mpdm_ref(mp_get_doc_names());
281 
282     /* is the list different from the previous one? */
283     if (mpdm_cmp(names, prev) != 0) {
284         TabCtrl_DeleteAllItems(hwtabs);
285 
286         for (n = 0; n < mpdm_size(names); n++) {
287             TCITEM ti;
288             char *ptr;
289             mpdm_t v = mpdm_get_i(names, n);
290 
291             /* convert to mbs */
292             ptr = mpdm_wcstombs(v->data, NULL);
293 
294             ti.mask = TCIF_TEXT;
295             ti.pszText = ptr;
296 
297             /* create it */
298             TabCtrl_InsertItem(hwtabs, n, &ti);
299 
300             free(ptr);
301         }
302 
303         /* store for the next time */
304         mpdm_store(&prev, names);
305     }
306 
307     mpdm_unref(names);
308 
309     /* set the active one */
310     TabCtrl_SetCurSel(hwtabs, mpdm_ival(mpdm_get_wcs(MP, L"active_i")));
311 }
312 
313 
draw_scrollbar(void)314 static void draw_scrollbar(void)
315 /* updates the scrollbar */
316 {
317     mpdm_t d;
318     mpdm_t v;
319     int pos, size, max;
320     SCROLLINFO si;
321 
322     d = mp_active();
323 
324     /* get the coordinates */
325     v = mpdm_get_wcs(d, L"txt");
326     pos = mpdm_ival(mpdm_get_wcs(v, L"vy"));
327     max = mpdm_size(mpdm_get_wcs(v, L"lines"));
328 
329     v = mpdm_get_wcs(MP, L"window");
330     size = mpdm_ival(mpdm_get_wcs(v, L"ty"));
331 
332     si.cbSize = sizeof(si);
333     si.fMask  = SIF_ALL;
334     si.nMin   = 0;
335     si.nMax   = max;
336     si.nPage  = size;
337     si.nPos   = pos;
338 
339     SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
340 }
341 
342 
draw_status(void)343 void draw_status(void)
344 /* draws the status line */
345 {
346     mpdm_t t;
347 
348     if (hwstatus != NULL && (t = mpdm_ref(mp_build_status_line())) != NULL) {
349         mpdm_t v = mpdm_ref(MPDM_2MBS(t->data));
350 
351         if (v->data != NULL)
352             SetWindowText(hwstatus, v->data);
353 
354         mpdm_unref(v);
355         mpdm_unref(t);
356     }
357 }
358 
359 
win32_draw(HWND hwnd)360 static void win32_draw(HWND hwnd)
361 /* win32 document draw function */
362 {
363     HDC hdc;
364     PAINTSTRUCT ps;
365     RECT rect;
366     RECT r2;
367     mpdm_t d = NULL;
368     int n, m;
369 
370     /* start painting */
371     hdc = BeginPaint(hwnd, &ps);
372 
373     /* no font? construct it */
374     if (font_normal == NULL) {
375         build_fonts(hdc);
376         build_colors();
377     }
378 
379     d = mp_draw(mp_active(), 0);
380 
381     mpdm_ref(d);
382 
383     /* select defaults to start painting */
384     SelectObject(hdc, font_normal);
385 
386     GetClientRect(hwnd, &rect);
387     r2 = rect;
388 
389     r2.top += tab_height;
390     r2.bottom = r2.top + font_height;
391 
392     for (n = 0; n < mpdm_size(d); n++) {
393         mpdm_t l = mpdm_get_i(d, n);
394 
395         r2.left = rect.left;
396 
397         for (m = 0; m < mpdm_size(l); m++) {
398             int attr;
399             mpdm_t s;
400 
401             /* get the attribute and the string */
402             attr = mpdm_ival(mpdm_get_i(l, m++));
403             s = mpdm_get_i(l, m);
404 
405             SetTextColor(hdc, inks[attr]);
406             SetBkColor(hdc, papers[attr]);
407 
408             SelectObject(hdc, underlines[attr] ?
409                          font_underline : font_normal);
410 
411             TextOutW(hdc, r2.left, r2.top, s->data, mpdm_size(s));
412             r2.left += mpdm_size(s) * font_width;
413         }
414 
415         /* fills the rest of the line */
416         FillRect(hdc, &r2, bgbrush);
417 
418         r2.top += font_height;
419         r2.bottom += font_height;
420     }
421 
422     EndPaint(hwnd, &ps);
423 
424     mpdm_unref(d);
425 
426     draw_filetabs();
427     draw_scrollbar();
428     draw_status();
429 }
430 
431 
redraw(void)432 static void redraw(void)
433 {
434     InvalidateRect(hwnd, NULL, TRUE);
435 }
436 
437 
win32_vkey(int c)438 static void win32_vkey(int c)
439 /* win32 virtual key processing */
440 {
441     wchar_t *ptr = NULL;
442     static int maxed = 0;
443 
444     /* set mp.shift_pressed */
445     if (GetKeyState(VK_SHIFT) & 0x8000)
446         mpdm_set_wcs(MP, MPDM_I(1), L"shift_pressed");
447 
448     if (GetKeyState(VK_SHIFT) & 0x8000) {
449         switch (c) {
450         case VK_F1:
451             ptr = L"shift-f1";
452             break;
453         case VK_F2:
454             ptr = L"shift-f2";
455             break;
456         case VK_F3:
457             ptr = L"shift-f3";
458             break;
459         case VK_F4:
460             ptr = L"shift-f4";
461             break;
462         case VK_F5:
463             ptr = L"shift-f5";
464             break;
465         case VK_F6:
466             ptr = L"shift-f6";
467             break;
468         case VK_F7:
469             ptr = L"shift-f7";
470             break;
471         case VK_F8:
472             ptr = L"shift-f8";
473             break;
474         case VK_F9:
475             ptr = L"shift-f9";
476             break;
477         case VK_F10:
478             ptr = L"shift-f10";
479             break;
480         case VK_F11:
481             ptr = L"shift-f11";
482             break;
483         case VK_F12:
484             ptr = L"shift-f12";
485             break;
486         }
487     }
488 
489     if (ptr == NULL && (GetKeyState(VK_CONTROL) & 0x8000)) {
490         switch (c) {
491         case VK_UP:
492             ptr = L"ctrl-cursor-up";
493             break;
494         case VK_DOWN:
495             ptr = L"ctrl-cursor-down";
496             break;
497         case VK_LEFT:
498             ptr = L"ctrl-cursor-left";
499             break;
500         case VK_RIGHT:
501             ptr = L"ctrl-cursor-right";
502             break;
503         case VK_PRIOR:
504             ptr = L"ctrl-page-up";
505             break;
506         case VK_NEXT:
507             ptr = L"ctrl-page-down";
508             break;
509         case VK_HOME:
510             ptr = L"ctrl-home";
511             break;
512         case VK_END:
513             ptr = L"ctrl-end";
514             break;
515         case VK_SPACE:
516             ptr = L"ctrl-space";
517             break;
518         case VK_DIVIDE:
519             ptr = L"ctrl-kp-divide";
520             break;
521         case VK_MULTIPLY:
522             ptr = L"ctrl-kp-multiply";
523             break;
524         case VK_SUBTRACT:
525             ptr = L"ctrl-kp-minus";
526             break;
527         case VK_ADD:
528             ptr = L"ctrl-kp-plus";
529             break;
530         case VK_RETURN:
531             ptr = L"ctrl-enter";
532             break;
533         case VK_F1:
534             ptr = L"ctrl-f1";
535             break;
536         case VK_F2:
537             ptr = L"ctrl-f2";
538             break;
539         case VK_F3:
540             ptr = L"ctrl-f3";
541             break;
542         case VK_F4:
543             ptr = L"ctrl-f4";
544             break;
545         case VK_F5:
546             ptr = L"ctrl-f5";
547             break;
548         case VK_F6:
549             ptr = L"ctrl-f6";
550             break;
551         case VK_F7:
552             ptr = L"ctrl-f7";
553             break;
554         case VK_F8:
555             ptr = L"ctrl-f8";
556             break;
557         case VK_F9:
558             ptr = L"ctrl-f9";
559             break;
560         case VK_F10:
561             ptr = L"ctrl-f10";
562             break;
563         case VK_F11:
564             ptr = L"ctrl-f11";
565             break;
566         case VK_F12:
567             SendMessage(hwnd, WM_SYSCOMMAND,
568                         maxed ? SC_RESTORE : SC_MAXIMIZE, 0);
569 
570             maxed ^= 1;
571 
572             break;
573         }
574     }
575     else
576     if (ptr == NULL && (GetKeyState(VK_LMENU) & 0x8000)) {
577         switch (c) {
578         case VK_UP:
579             ptr = L"alt-cursor-up";
580             break;
581         case VK_DOWN:
582             ptr = L"alt-cursor-down";
583             break;
584         case VK_LEFT:
585             ptr = L"alt-cursor-left";
586             break;
587         case VK_RIGHT:
588             ptr = L"alt-cursor-right";
589             break;
590         case VK_PRIOR:
591             ptr = L"alt-page-up";
592             break;
593         case VK_NEXT:
594             ptr = L"alt-page-down";
595             break;
596         case VK_HOME:
597             ptr = L"alt-home";
598             break;
599         case VK_END:
600             ptr = L"alt-end";
601             break;
602         case VK_SPACE:
603             ptr = L"alt-space";
604             break;
605         case VK_DIVIDE:
606             ptr = L"alt-kp-divide";
607             break;
608         case VK_MULTIPLY:
609             ptr = L"alt-kp-multiply";
610             break;
611         case VK_SUBTRACT:
612             ptr = L"alt-kp-minus";
613             break;
614         case VK_ADD:
615             ptr = L"alt-kp-plus";
616             break;
617         case VK_RETURN:
618             ptr = L"alt-enter";
619             break;
620         case VK_F1:
621             ptr = L"alt-f1";
622             break;
623         case VK_F2:
624             ptr = L"alt-f2";
625             break;
626         case VK_F3:
627             ptr = L"alt-f3";
628             break;
629         case VK_F4:
630             ptr = L"alt-f4";
631             break;
632         case VK_F5:
633             ptr = L"alt-f5";
634             break;
635         case VK_F6:
636             ptr = L"alt-f6";
637             break;
638         case VK_F7:
639             ptr = L"alt-f7";
640             break;
641         case VK_F8:
642             ptr = L"alt-f8";
643             break;
644         case VK_F9:
645             ptr = L"alt-f9";
646             break;
647         case VK_F10:
648             ptr = L"alt-f10";
649             break;
650         case VK_F11:
651             ptr = L"alt-f11";
652             break;
653         case VK_F12:
654             ptr = L"alt-f12";
655             break;
656         case 0xbd: /* VK_OEM_MINUS */
657             ptr = L"alt-minus";
658             break;
659         }
660     }
661     else
662     if (ptr == NULL) {
663         switch (c) {
664         case VK_UP:
665             ptr = L"cursor-up";
666             break;
667         case VK_DOWN:
668             ptr = L"cursor-down";
669             break;
670         case VK_LEFT:
671             ptr = L"cursor-left";
672             break;
673         case VK_RIGHT:
674             ptr = L"cursor-right";
675             break;
676         case VK_PRIOR:
677             ptr = L"page-up";
678             break;
679         case VK_NEXT:
680             ptr = L"page-down";
681             break;
682         case VK_HOME:
683             ptr = L"home";
684             break;
685         case VK_END:
686             ptr = L"end";
687             break;
688         case VK_RETURN:
689             ptr = L"enter";
690             break;
691         case VK_BACK:
692             ptr = L"backspace";
693             break;
694         case VK_DELETE:
695             ptr = L"delete";
696             break;
697         case VK_INSERT:
698             ptr = L"insert";
699             break;
700         case VK_DIVIDE:
701             ptr = L"kp-divide";
702             break;
703         case VK_MULTIPLY:
704             ptr = L"kp-multiply";
705             break;
706         case VK_SUBTRACT:
707             ptr = L"kp-minus";
708             break;
709         case VK_ADD:
710             ptr = L"kp-plus";
711             break;
712         case VK_F1:
713             ptr = L"f1";
714             break;
715         case VK_F2:
716             ptr = L"f2";
717             break;
718         case VK_F3:
719             ptr = L"f3";
720             break;
721         case VK_F4:
722             ptr = L"f4";
723             break;
724         case VK_F5:
725             ptr = L"f5";
726             break;
727         case VK_F6:
728             ptr = L"f6";
729             break;
730         case VK_F7:
731             ptr = L"f7";
732             break;
733         case VK_F8:
734             ptr = L"f8";
735             break;
736         case VK_F9:
737             ptr = L"f9";
738             break;
739         case VK_F10:
740             ptr = L"f10";
741             break;
742         case VK_F11:
743             ptr = L"f11";
744             break;
745         case VK_F12:
746             ptr = L"f12";
747             break;
748         }
749     }
750 
751     if (ptr != NULL) {
752         mp_process_event(MPDM_S(ptr));
753 
754         is_wm_keydown = 1;
755         mp_active();
756 
757         if (mp_keypress_throttle(1))
758             redraw();
759     }
760 }
761 
762 
763 #define ctrl(c) ((c) & 31)
764 
win32_akey(int k)765 static void win32_akey(int k)
766 /* win32 alphanumeric key processing */
767 {
768     wchar_t c[2];
769     wchar_t *ptr = NULL;
770 
771     /* set mp.shift_pressed */
772     if (GetKeyState(VK_SHIFT) & 0x8000)
773         mpdm_set_wcs(MP, MPDM_I(1), L"shift_pressed");
774 
775     switch (k) {
776     case ctrl(' '):
777         ptr = L"ctrl-space";
778         break;
779     case ctrl('a'):
780         ptr = L"ctrl-a";
781         break;
782     case ctrl('b'):
783         ptr = L"ctrl-b";
784         break;
785     case ctrl('c'):
786         ptr = L"ctrl-c";
787         break;
788     case ctrl('d'):
789         ptr = L"ctrl-d";
790         break;
791     case ctrl('e'):
792         ptr = L"ctrl-e";
793         break;
794     case ctrl('f'):
795         ptr = L"ctrl-f";
796         break;
797     case ctrl('g'):
798         ptr = L"ctrl-g";
799         break;
800     case ctrl('h'):         /* same as backspace */
801         break;
802     case ctrl('i'):         /* same as tab */
803         ptr = (GetKeyState(VK_SHIFT) & 0x8000) ? L"shift-tab" : L"tab";
804         break;
805     case ctrl('j'):
806         ptr = L"ctrl-j";
807         break;
808     case ctrl('k'):
809         ptr = L"ctrl-k";
810         break;
811     case ctrl('l'):
812         ptr = L"ctrl-l";
813         break;
814     case ctrl('m'):            /* same as ENTER */
815         break;
816     case ctrl('n'):
817         ptr = L"ctrl-n";
818         break;
819     case ctrl('o'):
820         ptr = L"ctrl-o";
821         break;
822     case ctrl('p'):
823         ptr = L"ctrl-p";
824         break;
825     case ctrl('q'):
826         ptr = L"ctrl-q";
827         break;
828     case ctrl('r'):
829         ptr = L"ctrl-r";
830         break;
831     case ctrl('s'):
832         ptr = L"ctrl-s";
833         break;
834     case ctrl('t'):
835         ptr = L"ctrl-t";
836         break;
837     case ctrl('u'):
838         ptr = L"ctrl-u";
839         break;
840     case ctrl('v'):
841         ptr = L"ctrl-v";
842         break;
843     case ctrl('w'):
844         ptr = L"ctrl-w";
845         break;
846     case ctrl('x'):
847         ptr = L"ctrl-x";
848         break;
849     case ctrl('y'):
850         ptr = L"ctrl-y";
851         break;
852     case ctrl('z'):
853         ptr = L"ctrl-z";
854         break;
855     case ' ':
856         ptr = L"space";
857         break;
858     case 27:
859         ptr = L"escape";
860         break;
861     case '-':
862         ptr = (GetKeyState(VK_LMENU) & 0x8000) ? L"alt-minus" : L"-";
863         break;
864 
865     default:
866         /* this is probably very bad */
867         c[0] = (wchar_t) k;
868         c[1] = L'\0';
869         ptr = c;
870 
871         break;
872     }
873 
874     if (ptr != NULL) {
875         mp_process_event(MPDM_S(ptr));
876 
877         mp_active();
878         redraw();
879     }
880 }
881 
882 
win32_vscroll(UINT wparam)883 static void win32_vscroll(UINT wparam)
884 /* scrollbar messages handler */
885 {
886     wchar_t *ptr = NULL;
887     mpdm_t txt;
888 
889     switch (LOWORD(wparam)) {
890     case SB_PAGEUP:
891         ptr = L"page-up";
892         break;
893     case SB_PAGEDOWN:
894         ptr = L"page-down";
895         break;
896     case SB_LINEUP:
897         ptr = L"cursor-up";
898         break;
899     case SB_LINEDOWN:
900         ptr = L"cursor-down";
901         break;
902     case SB_THUMBPOSITION:
903     case SB_THUMBTRACK:
904         /* set both y and vy */
905         txt = mpdm_get_wcs(mp_active(), L"txt");
906         mp_set_y(mp_active(), HIWORD(wparam));
907         mpdm_set_wcs(txt, MPDM_I(HIWORD(wparam)), L"vy");
908         redraw();
909         break;
910     }
911 
912     if (ptr != NULL) {
913         mp_process_event(MPDM_S(ptr));
914 
915         redraw();
916     }
917 }
918 
919 
action_by_menu(int item)920 static void action_by_menu(int item)
921 /* execute an action triggered by the menu */
922 {
923     MENUITEMINFO mi;
924 
925     memset(&mi, '\0', sizeof(mi));
926     mi.cbSize = sizeof(mi);
927     mi.fMask = MIIM_DATA;
928 
929     if (GetMenuItemInfo(menu, item, FALSE, &mi)) {
930         if (mi.dwItemData != 0) {
931             mp_process_action((mpdm_t) mi.dwItemData);
932             mp_active();
933         }
934     }
935 }
936 
937 
dropped_files(HDROP hDrop)938 static void dropped_files(HDROP hDrop)
939 /* fill the mp.dropped_files array with the dropped files */
940 {
941     mpdm_t a = MPDM_A(0);
942     char tmp[1024];
943     int n;
944 
945     mpdm_ref(a);
946 
947     n = DragQueryFile(hDrop, 0xffffffff, NULL, sizeof(tmp) - 1);
948 
949     while (--n >= 0) {
950         DragQueryFile(hDrop, n, tmp, sizeof(tmp) - 1);
951         mpdm_push(a, MPDM_MBS(tmp));
952     }
953 
954     DragFinish(hDrop);
955 
956     mpdm_set_wcs(MP, a, L"dropped_files");
957 
958     mpdm_unref(a);
959 
960     mp_process_event(MPDM_S(L"dropped-files"));
961 
962     redraw();
963 }
964 
965 
966 #ifndef WM_MOUSEWHEEL
967 #define WM_MOUSEWHEEL           0x020A
968 #endif
969 
WndProc(HWND hwnd,UINT msg,UINT wparam,LONG lparam)970 long CALLBACK WndProc(HWND hwnd, UINT msg, UINT wparam, LONG lparam)
971 /* main window Proc */
972 {
973     int x, y;
974     LPNMHDR p;
975     wchar_t *ptr = NULL;
976 
977     switch (msg) {
978     case WM_CREATE:
979 
980         is_wm_keydown = 0;
981         DragAcceptFiles(hwnd, TRUE);
982         return 0;
983 
984     case WM_DROPFILES:
985 
986         dropped_files((HDROP) wparam);
987         return 0;
988 
989     case WM_SYSKEYUP:
990     case WM_KEYUP:
991 
992         is_wm_keydown = 0;
993 
994         if (mp_keypress_throttle(0))
995             redraw();
996 
997         return 0;
998 
999     case WM_SYSKEYDOWN:
1000     case WM_KEYDOWN:
1001 
1002         win32_vkey(wparam);
1003         return 0;
1004 
1005     case WM_CHAR:
1006 
1007         win32_akey(wparam);
1008         return 0;
1009 
1010     case WM_VSCROLL:
1011 
1012         win32_vscroll(wparam);
1013         return 0;
1014 
1015     case WM_PAINT:
1016 
1017         if (mpdm_size(mpdm_get_wcs(MP, L"docs")))
1018             win32_draw(hwnd);
1019 
1020         return 0;
1021 
1022     case WM_SIZE:
1023 
1024         if (!IsIconic(hwnd)) {
1025             update_window_size();
1026 
1027             MoveWindow(hwtabs, 0, 0, LOWORD(lparam), tab_height, FALSE);
1028 
1029             MoveWindow(hwstatus, 0, HIWORD(lparam) - status_height,
1030                        LOWORD(lparam), status_height, FALSE);
1031 
1032             redraw();
1033         }
1034 
1035         return 0;
1036 
1037     case WM_LBUTTONDOWN:
1038     case WM_LBUTTONDBLCLK:
1039 
1040         mouse_down = 1;
1041         /* fallthrough */
1042 
1043     case WM_RBUTTONDOWN:
1044     case WM_MBUTTONDOWN:
1045 
1046         x = (LOWORD(lparam)) / font_width;
1047         y = (HIWORD(lparam) - tab_height) / font_height;
1048 
1049         mpdm_set_wcs(MP, MPDM_I(x), L"mouse_x");
1050         mpdm_set_wcs(MP, MPDM_I(y), L"mouse_y");
1051 
1052         switch (msg) {
1053         case WM_LBUTTONDOWN:
1054             ptr = L"mouse-left-button";
1055             break;
1056         case WM_RBUTTONDOWN:
1057             ptr = L"mouse-right-button";
1058             break;
1059         case WM_MBUTTONDOWN:
1060             ptr = L"mouse-middle-button";
1061             break;
1062         case WM_LBUTTONDBLCLK:
1063             ptr = L"mouse-left-dblclick";
1064             break;
1065         }
1066 
1067         if (ptr != NULL) {
1068             mp_process_event(MPDM_S(ptr));
1069 
1070             redraw();
1071         }
1072 
1073         return 0;
1074 
1075     case WM_LBUTTONUP:
1076 
1077         mouse_down = 0;
1078         return 0;
1079 
1080     case WM_MOUSEMOVE:
1081 
1082         if (mouse_down) {
1083             x = (LOWORD(lparam)) / font_width;
1084             y = (HIWORD(lparam) - tab_height) / font_height;
1085 
1086             mpdm_set_wcs(MP, MPDM_I(x), L"mouse_to_x");
1087             mpdm_set_wcs(MP, MPDM_I(y), L"mouse_to_y");
1088 
1089             mp_process_event(MPDM_S(L"mouse-drag"));
1090 
1091             redraw();
1092         }
1093 
1094         return 0;
1095 
1096     case WM_MOUSEWHEEL:
1097 
1098         if ((int) wparam > 0)
1099             ptr = L"mouse-wheel-up";
1100         else
1101             ptr = L"mouse-wheel-down";
1102 
1103         if (ptr != NULL) {
1104             mp_process_event(MPDM_S(ptr));
1105 
1106             redraw();
1107         }
1108 
1109         return 0;
1110 
1111     case WM_COMMAND:
1112 
1113         action_by_menu(LOWORD(wparam));
1114         redraw();
1115 
1116         return 0;
1117 
1118     case WM_CLOSE:
1119 
1120         {
1121             RECT r;
1122             mpdm_t v;
1123 
1124             GetWindowRect(hwnd, &r);
1125 
1126             v = mpdm_get_wcs(MP, L"state");
1127             v = mpdm_set_wcs(v, MPDM_O(), L"window");
1128             mpdm_set_wcs(v, MPDM_I(r.left),           L"x");
1129             mpdm_set_wcs(v, MPDM_I(r.top),            L"y");
1130             mpdm_set_wcs(v, MPDM_I(r.right - r.left), L"w");
1131             mpdm_set_wcs(v, MPDM_I(r.bottom - r.top), L"h");
1132         }
1133 
1134         if (!mp_exit_requested)
1135             mp_process_event(MPDM_S(L"close-window"));
1136 
1137         if (mp_exit_requested)
1138             DestroyWindow(hwnd);
1139 
1140         return 0;
1141 
1142     case WM_DESTROY:
1143         PostQuitMessage(0);
1144         return 0;
1145 
1146     case WM_NOTIFY:
1147         p = (LPNMHDR) lparam;
1148 
1149         if (p->code == TCN_SELCHANGE) {
1150             /* tab selected by clicking on it */
1151             int n = TabCtrl_GetCurSel(hwtabs);
1152 
1153             /* set mp.active_i to this */
1154             mpdm_set_wcs(MP, MPDM_I(n), L"active_i");
1155 
1156             redraw();
1157         }
1158 
1159         return 0;
1160 
1161     case WM_TIMER:
1162         mp_process_event(MPDM_S(L"idle"));
1163         redraw();
1164 
1165         return 0;
1166     }
1167 
1168     if (mp_exit_requested)
1169         PostMessage(hwnd, WM_CLOSE, 0, 0);
1170 
1171     return DefWindowProcW(hwnd, msg, wparam, lparam);
1172 }
1173 
1174 
win32_drv_clip_to_sys(mpdm_t a,mpdm_t ctxt)1175 static mpdm_t win32_drv_clip_to_sys(mpdm_t a, mpdm_t ctxt)
1176 /* driver-dependent mp to system clipboard */
1177 {
1178     HGLOBAL hclp;
1179     mpdm_t d, v;
1180     char *ptr;
1181     char *clpptr;
1182     int s;
1183 
1184     /* convert the clipboard to DOS text */
1185     d = mpdm_get_wcs(MP, L"clipboard");
1186 
1187     if (mpdm_size(d)) {
1188         v = mpdm_ref(mpdm_join_wcs(d, L"\r\n"));
1189         ptr = mpdm_wcstombs(v->data, &s);
1190 
1191         /* allocates a handle and copies */
1192         hclp = GlobalAlloc(GHND, s + 1);
1193         clpptr = (char *) GlobalLock(hclp);
1194         memcpy(clpptr, ptr, s);
1195         clpptr[s] = '\0';
1196         GlobalUnlock(hclp);
1197 
1198         free(ptr);
1199 
1200         OpenClipboard(NULL);
1201         EmptyClipboard();
1202         SetClipboardData(CF_TEXT, hclp);
1203         CloseClipboard();
1204 
1205         mpdm_unref(v);
1206     }
1207 
1208     return NULL;
1209 }
1210 
1211 
win32_drv_sys_to_clip(mpdm_t a,mpdm_t ctxt)1212 static mpdm_t win32_drv_sys_to_clip(mpdm_t a, mpdm_t ctxt)
1213 /* driver-dependent system to mp clipboard */
1214 {
1215     HGLOBAL hclp;
1216     char *ptr;
1217 
1218     OpenClipboard(NULL);
1219     hclp = GetClipboardData(CF_TEXT);
1220     CloseClipboard();
1221 
1222     if (hclp && (ptr = GlobalLock(hclp)) != NULL) {
1223         mpdm_t d, v;
1224 
1225         /* create a value and split */
1226         v = mpdm_ref(MPDM_MBS(ptr));
1227         d = mpdm_ref(mpdm_split_wcs(v, L"\r\n"));
1228 
1229         /* and set as the clipboard */
1230         mpdm_set_wcs(MP, d, L"clipboard");
1231 
1232         GlobalUnlock(hclp);
1233 
1234         mpdm_unref(d);
1235         mpdm_unref(v);
1236     }
1237 
1238     return NULL;
1239 }
1240 
1241 
win32_drv_main_loop(mpdm_t a,mpdm_t ctxt)1242 static mpdm_t win32_drv_main_loop(mpdm_t a, mpdm_t ctxt)
1243 {
1244     MSG msg;
1245 
1246     if (!mp_exit_requested) {
1247         mp_active();
1248 
1249         while (GetMessage(&msg, NULL, 0, 0)) {
1250             TranslateMessage(&msg);
1251             DispatchMessage(&msg);
1252         }
1253     }
1254 
1255     return NULL;
1256 }
1257 
1258 
win32_drv_shutdown(mpdm_t a,mpdm_t ctxt)1259 static mpdm_t win32_drv_shutdown(mpdm_t a, mpdm_t ctxt)
1260 {
1261     mpdm_t v;
1262 
1263     SendMessage(hwnd, WM_CLOSE, 0, 0);
1264 
1265     if ((v = mpdm_get_wcs(MP, L"exit_message")) != NULL)
1266         MessageBoxW(NULL, mpdm_string(v), L"mp " VERSION, MB_ICONWARNING | MB_OK);
1267 
1268     return NULL;
1269 }
1270 
1271 
win32_drv_alert(mpdm_t a,mpdm_t ctxt)1272 static mpdm_t win32_drv_alert(mpdm_t a, mpdm_t ctxt)
1273 /* alert driver function */
1274 {
1275     /* 1# arg: prompt */
1276     MessageBoxW(hwnd, mpdm_string(mpdm_get_i(a, 0)), L"mp " VERSION, MB_ICONWARNING | MB_OK);
1277 
1278     return NULL;
1279 }
1280 
1281 
win32_drv_confirm(mpdm_t a,mpdm_t ctxt)1282 static mpdm_t win32_drv_confirm(mpdm_t a, mpdm_t ctxt)
1283 /* confirm driver function */
1284 {
1285     int ret = 0;
1286 
1287     /* 1# arg: prompt */
1288     ret = MessageBoxW(hwnd, mpdm_string(mpdm_get_i(a, 0)), L"mp " VERSION,
1289                        MB_ICONQUESTION | MB_YESNOCANCEL);
1290 
1291     if (ret == IDYES)
1292         ret = 1;
1293     else
1294     if (ret == IDNO)
1295         ret = 2;
1296     else
1297         ret = 0;
1298 
1299     return MPDM_I(ret);
1300 }
1301 
1302 
lpwAlign(LPWORD lpIn)1303 static LPWORD lpwAlign(LPWORD lpIn)
1304 /* aligns a pointer to DWORD boundary (for dialog templates) */
1305 {
1306     ULONG ul;
1307 
1308     ul = (ULONG) lpIn;
1309     ul++;
1310     ul >>= 1;
1311     ul <<= 1;
1312     return (LPWORD) ul;
1313 }
1314 
1315 
1316 #define LABEL_ID    1000
1317 #define CTRL_ID     2000
1318 
formDlgProc(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam)1319 BOOL CALLBACK formDlgProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
1320 /* mp.drv.form() dialog proc */
1321 {
1322     int n;
1323     HFONT hf;
1324 
1325     switch (msg) {
1326     case WM_INITDIALOG:
1327 
1328         SetWindowText(hwnd, "mp " VERSION);
1329 
1330         hf = GetStockObject(DEFAULT_GUI_FONT);
1331 
1332         /* fill controls with its initial data */
1333         for (n = 0; n < mpdm_size(form_args); n++) {
1334             mpdm_t w = mpdm_get_i(form_args, n);
1335             wchar_t *type;
1336             mpdm_t t;
1337             int ctrl = CTRL_ID + n;
1338             wchar_t *wptr;
1339             char *ptr;
1340 
1341             if ((t = mpdm_get_wcs(w, L"label")) != NULL) {
1342                 SetDlgItemTextW(hwnd, LABEL_ID + n, mpdm_string(t));
1343 
1344                 SendDlgItemMessage(hwnd, LABEL_ID + n, WM_SETFONT,
1345                                    (WPARAM) hf, MAKELPARAM(FALSE, 0));
1346             }
1347 
1348             SendDlgItemMessage(hwnd, ctrl, WM_SETFONT,
1349                                (WPARAM) hf, MAKELPARAM(FALSE, 0));
1350 
1351             type = mpdm_string(mpdm_get_wcs(w, L"type"));
1352 
1353             if (wcscmp(type, L"text") == 0) {
1354                 if ((t = mpdm_get_wcs(w, L"value")) != NULL) {
1355                     SetDlgItemTextW(hwnd, ctrl, mpdm_string(t));
1356                 }
1357 
1358                 /* store the history into combo_items */
1359                 if ((t = mpdm_get_wcs(w, L"history")) != NULL) {
1360                     t = mp_get_history(t);
1361                     int i;
1362 
1363                     for (i = 0; i < mpdm_size(t); i++) {
1364                         mpdm_t v = mpdm_get_i(t, i);
1365 
1366                         if ((ptr = mpdm_wcstombs(v->data, NULL)) != NULL) {
1367                             SendDlgItemMessage(hwnd,
1368                                                ctrl,
1369                                                CB_INSERTSTRING, 0,
1370                                                (LPARAM) ptr);
1371                             free(ptr);
1372                         }
1373                     }
1374                 }
1375             }
1376             else
1377             if (wcscmp(type, L"password") == 0) {
1378                 SendDlgItemMessage(hwnd, ctrl,
1379                                    EM_SETPASSWORDCHAR, (WPARAM) '*',
1380                                    (LPARAM) 0);
1381             }
1382             else
1383             if (wcscmp(type, L"checkbox") == 0) {
1384                 if ((t = mpdm_get_wcs(w, L"value")) != NULL)
1385                     SendDlgItemMessage(hwnd, ctrl,
1386                                        BM_SETCHECK, mpdm_ival(t) ?
1387                                        BST_CHECKED : BST_UNCHECKED, 0);
1388             }
1389             else
1390             if (wcscmp(type, L"list") == 0) {
1391                 int i;
1392                 int ts[] = { 250, 20 };
1393 
1394                 t = mpdm_get_wcs(w, L"list");
1395 
1396                 /* fill the list */
1397                 for (i = 0; i < mpdm_size(t); i++) {
1398                     wptr = mpdm_string(mpdm_get_i(t, i));
1399                     if ((ptr = mpdm_wcstombs(wptr, NULL)) != NULL) {
1400                         SendDlgItemMessage(hwnd, ctrl,
1401                                            LB_ADDSTRING, 0, (LPARAM) ptr);
1402                         free(ptr);
1403                     }
1404                 }
1405 
1406                 SendDlgItemMessage(hwnd, ctrl, LB_SETTABSTOPS, 2, (LPARAM) ts);
1407 
1408                 /* set position */
1409                 SendDlgItemMessage(hwnd, ctrl, LB_SETCURSEL,
1410                                    mpdm_ival(mpdm_get_wcs(w, L"value")), 0);
1411             }
1412         }
1413 
1414         /* FIXME: untranslated strings */
1415 
1416         SetDlgItemText(hwnd, IDOK, "OK");
1417         SendDlgItemMessage(hwnd, IDOK, WM_SETFONT,
1418                            (WPARAM) hf, MAKELPARAM(FALSE, 0));
1419 
1420         SetDlgItemText(hwnd, IDCANCEL, "Cancel");
1421         SendDlgItemMessage(hwnd, IDCANCEL, WM_SETFONT,
1422                            (WPARAM) hf, MAKELPARAM(FALSE, 0));
1423 
1424         return TRUE;
1425 
1426     case WM_COMMAND:
1427 
1428         if (LOWORD(wparam) == IDCANCEL) {
1429             EndDialog(hwnd, 0);
1430             return TRUE;
1431         }
1432 
1433         if (LOWORD(wparam) != IDOK)
1434             break;
1435 
1436         /* fill all return values */
1437         for (n = 0; n < mpdm_size(form_args); n++) {
1438             mpdm_t w = mpdm_get_i(form_args, n);
1439             wchar_t *type = mpdm_string(mpdm_get_wcs(w, L"type"));
1440             int ctrl = CTRL_ID + n;
1441 
1442             if (wcscmp(type, L"text") == 0) {
1443                 char tmp[2048];
1444                 mpdm_t v;
1445                 mpdm_t h;
1446 
1447                 GetDlgItemText(hwnd, ctrl, tmp, sizeof(tmp) - 1);
1448                 v = MPDM_MBS(tmp);
1449 
1450                 mpdm_set_i(form_values, v, n);
1451 
1452                 /* if it has history, fill it */
1453                 if (v && (h = mpdm_get_wcs(w, L"history")) && mpdm_cmp_wcs(v, L"")) {
1454                     h = mp_get_history(h);
1455 
1456                     if (mpdm_cmp(v, mpdm_get_i(h, -1)) != 0)
1457                         mpdm_push(h, v);
1458                 }
1459             }
1460             if (wcscmp(type, L"password") == 0) {
1461                 char tmp[2048];
1462 
1463                 GetDlgItemText(hwnd, ctrl, tmp, sizeof(tmp) - 1);
1464                 mpdm_set_i(form_values, MPDM_MBS(tmp), n);
1465             }
1466             else
1467             if (wcscmp(type, L"checkbox") == 0) {
1468                 mpdm_set_i(form_values,
1469                           MPDM_I(SendDlgItemMessage(hwnd, ctrl,
1470                                                     BM_GETCHECK, 0, 0)),
1471                           n);
1472             }
1473             else
1474             if (wcscmp(type, L"list") == 0) {
1475                 mpdm_set_i(form_values,
1476                           MPDM_I(SendDlgItemMessage(hwnd, ctrl,
1477                                                     LB_GETCURSEL, 0, 0)),
1478                           n);
1479             }
1480         }
1481 
1482         EndDialog(hwnd, 1);
1483         return TRUE;
1484     }
1485 
1486     return FALSE;
1487 }
1488 
1489 
build_form_data(mpdm_t widget_list)1490 static void build_form_data(mpdm_t widget_list)
1491 /* builds the necessary information for a list of widgets */
1492 {
1493     mpdm_unref(form_args);
1494     form_args = mpdm_ref(widget_list);
1495 
1496     mpdm_unref(form_values);
1497     form_values = widget_list == NULL ? NULL :
1498         mpdm_ref(MPDM_A(mpdm_size(form_args)));
1499 }
1500 
1501 
build_control(LPWORD lpw,int x,int y,int cx,int cy,int id,int w_class,int style)1502 LPWORD static build_control(LPWORD lpw, int x, int y,
1503                             int cx, int cy, int id, int w_class, int style)
1504 /* fills a control structure in a hand-made dialog template */
1505 {
1506     LPDLGITEMTEMPLATE lpdit;
1507 
1508     lpw          = lpwAlign(lpw);
1509     lpdit        = (LPDLGITEMTEMPLATE) lpw;
1510     lpdit->x     = x;
1511     lpdit->y     = y;
1512     lpdit->cx    = cx;
1513     lpdit->cy    = cy;
1514     lpdit->id    = id;
1515     lpdit->style = style;
1516 
1517     lpw    = (LPWORD) (lpdit + 1);
1518     *lpw++ = 0xFFFF;
1519     *lpw++ = w_class;
1520 
1521     /* no text (will be set on dialog setup) */
1522     *lpw++ = 0;
1523     *lpw++ = 0;
1524 
1525     /* Align creation data on DWORD boundary */
1526     lpw = lpwAlign(lpw);
1527     /* No creation data */
1528     *lpw++ = 0;
1529 
1530     return lpw;
1531 }
1532 
1533 
win32_drv_form(mpdm_t a,mpdm_t ctxt)1534 static mpdm_t win32_drv_form(mpdm_t a, mpdm_t ctxt)
1535 /* mp.drv.form() function */
1536 {
1537     HGLOBAL hgbl;
1538     LPDLGTEMPLATE lpdt;
1539     LPWORD lpw;
1540     int n, y;
1541     int line_height = 12;
1542     int label_width = 0;
1543     int dialog_width = 260;
1544     int button_width = 40;
1545     int spacing = 5;
1546 
1547     /* first argument: list of widgets */
1548     build_form_data(mpdm_get_i(a, 0));
1549 
1550     /* On-the-fly dialog template creation */
1551     /* Note: all this crap is taken from MSDN, no less */
1552 
1553     /* magic size; looking for problems */
1554     hgbl = GlobalAlloc(GMEM_ZEROINIT, 4096);
1555     lpdt = (LPDLGTEMPLATE) GlobalLock(hgbl);
1556 
1557     lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU | DS_MODALFRAME | WS_CAPTION;
1558     lpdt->cdit  = (2 * mpdm_size(form_args)) + 2;
1559     lpdt->x     = 20;
1560     lpdt->y     = 20;
1561     lpdt->cx    = dialog_width;
1562 
1563     lpw    = (LPWORD) (lpdt + 1);
1564     *lpw++ = 0;                 /* No menu */
1565     *lpw++ = 0;                 /* Predefined dialog box class (by default) */
1566     *lpw++ = 0;                 /* No title */
1567 
1568     /* first pass: calculate maximum size of labels */
1569     for (n = 0; n < mpdm_size(form_args); n++) {
1570         mpdm_t w = mpdm_get_i(form_args, n);
1571         int l = mpdm_size(mpdm_get_wcs(w, L"label"));
1572 
1573         if (label_width < l)
1574             label_width = l;
1575     }
1576 
1577     y = line_height / 2;
1578     label_width *= 3;
1579 
1580     /* second pass: create the dialog controls */
1581     for (n = 0; n < mpdm_size(form_args); n++) {
1582         mpdm_t w = mpdm_get_i(form_args, n);
1583         wchar_t *type;
1584         int w_class;
1585         int style;
1586         int inc = 1;
1587         int sz = 1;
1588 
1589         type = mpdm_string(mpdm_get_wcs(w, L"type"));
1590 
1591         if (wcscmp(type, L"text") == 0) {
1592             w_class = 0x0085;
1593             style = WS_CHILD | WS_VISIBLE | WS_TABSTOP |
1594                 CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL;
1595 
1596             /* size */
1597             sz = 5;
1598         }
1599         else
1600         if (wcscmp(type, L"password") == 0) {
1601             w_class = 0x0081;
1602             style = WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP;
1603         }
1604         else
1605         if (wcscmp(type, L"checkbox") == 0) {
1606             w_class = 0x0080;
1607             style = WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX | WS_TABSTOP;
1608         }
1609         else
1610         if (wcscmp(type, L"list") == 0) {
1611             w_class = 0x0083;
1612             style = WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER |
1613                 LBS_NOINTEGRALHEIGHT | WS_VSCROLL |
1614                 LBS_NOTIFY | LBS_USETABSTOPS;
1615 
1616             /* height */
1617             inc = 14;
1618         }
1619 
1620         if (mpdm_size(form_args) == 1) {
1621             /* label control */
1622             lpw = build_control(lpw, spacing, y,
1623                             dialog_width - spacing * 2,
1624                             line_height,
1625                             LABEL_ID + n, 0x0082,
1626                             WS_CHILD | WS_VISIBLE | SS_LEFT);
1627 
1628             /* the control */
1629             lpw = build_control(lpw, spacing, y + line_height,
1630                             dialog_width - spacing * 2,
1631                             inc * line_height * sz,
1632                             CTRL_ID + n, w_class,
1633                             style);
1634 
1635             inc++;
1636         }
1637         else {
1638             /* label control */
1639             lpw = build_control(lpw, spacing, y,
1640                             label_width,
1641                             line_height,
1642                             LABEL_ID + n, 0x0082,
1643                             WS_CHILD | WS_VISIBLE | SS_LEFT);
1644 
1645             /* the control */
1646             lpw = build_control(lpw, spacing + label_width, y,
1647                             dialog_width - label_width - spacing * 2,
1648                             inc * line_height * sz,
1649                             CTRL_ID + n, w_class,
1650                             style);
1651         }
1652 
1653         /* next position */
1654         y += inc * line_height;
1655     }
1656 
1657     /* set total height */
1658     lpdt->cy = line_height * 2 + y;
1659 
1660     y += line_height / 2;
1661 
1662     /* OK */
1663     lpw = build_control(lpw, dialog_width - button_width * 2 - spacing * 2, y,
1664                         button_width, line_height,
1665                         IDOK, 0x0080,
1666                         WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON | WS_TABSTOP);
1667 
1668     /* Cancel */
1669     lpw = build_control(lpw, dialog_width - button_width - spacing, y,
1670                         button_width, line_height,
1671                         IDCANCEL, 0x0080,
1672                         WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP);
1673 
1674     GlobalUnlock(hgbl);
1675     n = DialogBoxIndirect(hinst, (LPDLGTEMPLATE) hgbl,
1676                           hwnd, (DLGPROC) formDlgProc);
1677 
1678     GlobalFree(hgbl);
1679 
1680     return n ? form_values : NULL;
1681 }
1682 
1683 
open_or_save(int o,mpdm_t a)1684 static mpdm_t open_or_save(int o, mpdm_t a)
1685 /* manages an open or save file dialog */
1686 {
1687     OPENFILENAME ofn;
1688     wchar_t *wptr;
1689     char *ptr;
1690     char buf[1024] = "";
1691     char buf2[1024];
1692     int r;
1693 
1694     /* 1# arg: prompt */
1695     wptr = mpdm_string(mpdm_get_i(a, 0));
1696     ptr = mpdm_wcstombs(wptr, NULL);
1697 
1698     memset(&ofn, '\0', sizeof(OPENFILENAME));
1699     ofn.lStructSize  = sizeof(OPENFILENAME);
1700     ofn.hwndOwner    = hwnd;
1701     ofn.lpstrFilter  = "*.*\0*.*\0";
1702     ofn.nFilterIndex = 1;
1703     ofn.lpstrFile    = buf;
1704     ofn.nMaxFile     = sizeof(buf);
1705     ofn.lpstrTitle   = ptr;
1706     ofn.lpstrDefExt  = "";
1707 
1708     GetCurrentDirectory(sizeof(buf2), buf2);
1709     ofn.lpstrInitialDir = buf2;
1710 
1711     if (o) {
1712         ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY |
1713             OFN_NOCHANGEDIR | OFN_FILEMUSTEXIST;
1714 
1715         r = GetOpenFileName(&ofn);
1716     }
1717     else {
1718         ofn.Flags = OFN_HIDEREADONLY;
1719 
1720         r = GetSaveFileName(&ofn);
1721     }
1722 
1723     free(ptr);
1724 
1725     return r ? MPDM_MBS(buf) : NULL;
1726 }
1727 
1728 
win32_drv_openfile(mpdm_t a,mpdm_t ctxt)1729 static mpdm_t win32_drv_openfile(mpdm_t a, mpdm_t ctxt)
1730 /* openfile driver function */
1731 {
1732     return open_or_save(1, a);
1733 }
1734 
1735 
win32_drv_savefile(mpdm_t a,mpdm_t ctxt)1736 static mpdm_t win32_drv_savefile(mpdm_t a, mpdm_t ctxt)
1737 /* savefile driver function */
1738 {
1739     return open_or_save(0, a);
1740 }
1741 
1742 
win32_drv_openfolder(mpdm_t a,mpdm_t ctxt)1743 static mpdm_t win32_drv_openfolder(mpdm_t a, mpdm_t ctxt)
1744 /* openfolder driver function */
1745 {
1746     mpdm_t r = NULL;
1747     BROWSEINFO bi;
1748     char tmp[16384];
1749     char *ptr;
1750     LPITEMIDLIST i;
1751 
1752     /* 1# arg: prompt */
1753     ptr = mpdm_wcstombs(mpdm_string(mpdm_get_i(a, 0)), NULL);
1754 
1755     memset(&bi, '\0', sizeof(bi));
1756     bi.hwndOwner      = hwnd;
1757     bi.pidlRoot       = NULL;
1758     bi.pszDisplayName = tmp;
1759     bi.lpszTitle      = ptr;
1760     bi.ulFlags        = BIF_RETURNONLYFSDIRS;
1761 
1762     if ((i = SHBrowseForFolder(&bi)) != NULL) {
1763         if (SHGetPathFromIDList(i, tmp) != 0)
1764             r = MPDM_MBS(tmp);
1765     }
1766 
1767     free(ptr);
1768 
1769     return r;
1770 }
1771 
1772 
win32_drv_update_ui(mpdm_t a,mpdm_t ctxt)1773 static mpdm_t win32_drv_update_ui(mpdm_t a, mpdm_t ctxt)
1774 {
1775     build_fonts(GetDC(hwnd));
1776     build_colors();
1777     build_menu();
1778 
1779     return NULL;
1780 }
1781 
1782 
win32_drv_idle(mpdm_t a,mpdm_t ctxt)1783 static mpdm_t win32_drv_idle(mpdm_t a, mpdm_t ctxt)
1784 {
1785     int idle_msecs = (int) (mpdm_rval(mpdm_get_i(a, 0)) * 1000);
1786 
1787     KillTimer(hwnd, 1);
1788 
1789     if (idle_msecs > 0)
1790         SetTimer(hwnd, 1, idle_msecs, NULL);
1791 }
1792 
1793 
win32_drv_busy(mpdm_t a,mpdm_t ctxt)1794 static mpdm_t win32_drv_busy(mpdm_t a, mpdm_t ctxt)
1795 {
1796     int onoff = mpdm_ival(mpdm_get_i(a, 0));
1797 
1798     SetCursor(LoadCursor(NULL, onoff ? IDC_WAIT : IDC_ARROW));
1799 
1800     return NULL;
1801 }
1802 
1803 
register_functions(void)1804 static void register_functions(void)
1805 {
1806     mpdm_t drv;
1807 
1808     drv = mpdm_get_wcs(mpdm_root(), L"mp_drv");
1809     mpdm_set_wcs(drv, MPDM_X(win32_drv_main_loop),   L"main_loop");
1810     mpdm_set_wcs(drv, MPDM_X(win32_drv_shutdown),    L"shutdown");
1811     mpdm_set_wcs(drv, MPDM_X(win32_drv_clip_to_sys), L"clip_to_sys");
1812     mpdm_set_wcs(drv, MPDM_X(win32_drv_sys_to_clip), L"sys_to_clip");
1813     mpdm_set_wcs(drv, MPDM_X(win32_drv_update_ui),   L"update_ui");
1814     mpdm_set_wcs(drv, MPDM_X(win32_drv_idle),        L"idle");
1815     mpdm_set_wcs(drv, MPDM_X(win32_drv_busy),        L"busy");
1816     mpdm_set_wcs(drv, MPDM_X(win32_drv_alert),       L"alert");
1817     mpdm_set_wcs(drv, MPDM_X(win32_drv_confirm),     L"confirm");
1818     mpdm_set_wcs(drv, MPDM_X(win32_drv_openfile),    L"openfile");
1819     mpdm_set_wcs(drv, MPDM_X(win32_drv_savefile),    L"savefile");
1820     mpdm_set_wcs(drv, MPDM_X(win32_drv_form),        L"form");
1821     mpdm_set_wcs(drv, MPDM_X(win32_drv_openfolder),  L"openfolder");
1822 }
1823 
1824 
win32_drv_startup(mpdm_t a,mpdm_t ctxt)1825 static mpdm_t win32_drv_startup(mpdm_t a, mpdm_t ctxt)
1826 {
1827     WNDCLASSW wc;
1828     RECT r;
1829     mpdm_t v;
1830 
1831     register_functions();
1832 
1833     InitCommonControls();
1834 
1835     hinst = GetModuleHandle(NULL);
1836 
1837     /* register the window */
1838     wc.style         = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
1839     wc.lpfnWndProc   = WndProc;
1840     wc.cbClsExtra    = 0;
1841     wc.cbWndExtra    = 0;
1842     wc.hInstance     = hinst;
1843     wc.hIcon         = LoadIcon(hinst, "MP_ICON");
1844     wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
1845     wc.hbrBackground = NULL;
1846     wc.lpszMenuName  = NULL;
1847     wc.lpszClassName = L"minimumprofit5.x";
1848 
1849     RegisterClassW(&wc);
1850 
1851     v = mpdm_get_wcs(MP, L"state");
1852     if ((v = mpdm_get_wcs(v, L"window")) == NULL) {
1853         v = mpdm_set_wcs(mpdm_get_wcs(MP, L"state"), MPDM_O(), L"window");
1854         mpdm_set_wcs(v, MPDM_I(10),  L"x");
1855         mpdm_set_wcs(v, MPDM_I(10),  L"y");
1856         mpdm_set_wcs(v, MPDM_I(600), L"w");
1857         mpdm_set_wcs(v, MPDM_I(400), L"h");
1858     }
1859 
1860     /* create the window */
1861     hwnd = CreateWindowW(L"minimumprofit5.x", L"mp " VERSION,
1862                          WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_VSCROLL,
1863                          mpdm_ival(mpdm_get_wcs(v, L"x")),
1864                          mpdm_ival(mpdm_get_wcs(v, L"y")),
1865                          mpdm_ival(mpdm_get_wcs(v, L"w")),
1866                          mpdm_ival(mpdm_get_wcs(v, L"h")),
1867                          NULL, NULL, hinst, NULL);
1868 
1869     ShowWindow(hwnd, SW_SHOW);
1870     UpdateWindow(hwnd);
1871 
1872     GetClientRect(hwnd, &r);
1873 
1874     hwtabs = CreateWindow(WC_TABCONTROL, "tab",
1875                           WS_CHILD | TCS_TABS | TCS_SINGLELINE |
1876                           TCS_FOCUSNEVER, 0, 0, r.right - r.left,
1877                           tab_height, hwnd, NULL, hinst, NULL);
1878 
1879     SendMessage(hwtabs, WM_SETFONT,
1880                 (WPARAM) GetStockObject(DEFAULT_GUI_FONT), 0);
1881 
1882     ShowWindow(hwtabs, SW_SHOW);
1883     UpdateWindow(hwtabs);
1884 
1885     hwstatus = CreateWindow(WC_STATIC, "status",
1886                             WS_CHILD,
1887                             0, r.bottom - r.top - status_height,
1888                             r.right - r.left, status_height, hwnd, NULL,
1889                             hinst, NULL);
1890 
1891     win32_drv_update_ui(NULL, NULL);
1892 
1893     SendMessage(hwstatus, WM_SETFONT,
1894                 (WPARAM) GetStockObject(DEFAULT_GUI_FONT), 0);
1895 
1896     ShowWindow(hwstatus, SW_SHOW);
1897     UpdateWindow(hwstatus);
1898 
1899     if ((v = mpdm_get_wcs(MP, L"config")) != NULL &&
1900         mpdm_ival(mpdm_get_wcs(v, L"maximize")) > 0)
1901         SendMessage(hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
1902 
1903     return NULL;
1904 }
1905 
1906 
win32_drv_detect(int * argc,char *** argv)1907 int win32_drv_detect(int *argc, char ***argv)
1908 {
1909     int n, ret = 1;
1910 
1911     for (n = 0; n < *argc; n++) {
1912         if (strcmp(argv[0][n], "-txt") == 0)
1913             ret = 0;
1914     }
1915 
1916     if (ret) {
1917         mpdm_t drv;
1918 
1919         drv = mpdm_set_wcs(mpdm_root(), MPDM_O(), L"mp_drv");
1920 
1921         mpdm_set_wcs(drv, MPDM_S(sizeof(char *) == 8 ? L"win64" : L"win32"), L"id");
1922         mpdm_set_wcs(drv, MPDM_X(win32_drv_startup), L"startup");
1923     }
1924 
1925     return ret;
1926 }
1927 
1928 #endif                          /* CONFOPT_WIN32 */
1929