Updated RELEASE_NOTES.
[mp-5.x.git] / mpv_win32.c
blob0ddb499c130e649bf7ebc09b83bf2259301d7012
1 /*
3 Minimum Profit - Programmer Text Editor
5 Win32 driver.
7 Copyright (C) 1991-2007 Angel Ortega <angel@triptico.com>
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 http://www.triptico.com
27 #include "config.h"
29 #ifdef CONFOPT_WIN32
31 #include <stdio.h>
32 #include <string.h>
33 #include <stdlib.h>
35 #include <windows.h>
36 #include <commctrl.h>
37 #include <shlobj.h>
39 #include "mpdm.h"
40 #include "mpsl.h"
42 #include "mp.h"
44 /*******************
45 Data
46 ********************/
48 /* the instance */
49 HINSTANCE hinst;
51 /* the windows */
52 HWND hwnd = NULL;
53 HWND hwtabs = NULL;
54 HWND hwstatus = NULL;
56 /* font handlers and metrics */
57 HFONT font_normal = NULL;
58 int font_width = 0;
59 int font_height = 0;
61 /* height of the tab set */
62 int tab_height = 28;
64 /* height of the status bar */
65 int status_height = 16;
67 int is_wm_keydown = 0;
69 /* colors */
70 static COLORREF * inks = NULL;
71 static COLORREF * papers = NULL;
72 HBRUSH bgbrush;
74 /* code for the 'normal' attribute */
75 static int normal_attr = 0;
77 /* the menu */
78 static HMENU menu = NULL;
80 /* mp.drv.form() controls */
82 static mpdm_t form_args = NULL;
83 static mpdm_t form_values = NULL;
85 /* mouse down flag */
86 static int mouse_down = 0;
88 /* timer function */
89 static mpdm_t timer_func = NULL;
91 /*******************
92 Code
93 ********************/
95 static void update_window_size(void)
96 /* updates the viewport size in characters */
98 RECT rect;
99 int tx, ty;
100 mpdm_t v;
102 /* no font information? go */
103 if (font_width == 0 || font_height == 0)
104 return;
106 GetClientRect(hwnd, &rect);
108 /* calculate the size in chars */
109 tx = ((rect.right - rect.left) / font_width) + 1;
110 ty = ((rect.bottom - rect.top - tab_height) / font_height) + 1;
112 /* store the 'window' size */
113 v = mpdm_hget_s(mp, L"window");
114 mpdm_hset_s(v, L"tx", MPDM_I(tx));
115 mpdm_hset_s(v, L"ty", MPDM_I(ty));
119 static void build_fonts(HDC hdc)
120 /* build the fonts */
122 TEXTMETRIC tm;
123 int n;
124 int font_size = 14;
125 char * font_face = "Lucida Console";
126 mpdm_t c;
128 if (font_normal != NULL) {
129 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
130 DeleteObject(font_normal);
133 /* get current configuration */
134 if ((c = mpdm_hget_s(mp, L"config")) != NULL) {
135 mpdm_t v;
137 if ((v = mpdm_hget_s(c, L"font_size")) != NULL)
138 font_size = mpdm_ival(v);
139 else
140 mpdm_hset_s(c, L"font_size", MPDM_I(font_size));
142 if ((v = mpdm_hget_s(c, L"font_face")) != NULL) {
143 v = MPDM_2MBS(v->data);
144 font_face = (char *)v->data;
146 else
147 mpdm_hset_s(c, L"font_face", MPDM_MBS(font_face));
150 /* create fonts */
151 n = -MulDiv(font_size, GetDeviceCaps(hdc, LOGPIXELSY), 72);
153 font_normal = CreateFont(n, 0, 0, 0, 0, 0, 0,
154 0, 0, 0, 0, 0, 0, font_face);
156 SelectObject(hdc, font_normal);
157 GetTextMetrics(hdc, &tm);
159 /* store sizes */
160 font_height = tm.tmHeight;
161 font_width = tm.tmAveCharWidth;
163 update_window_size();
167 static void build_colors(void)
168 /* builds the colors */
170 mpdm_t colors;
171 mpdm_t l;
172 mpdm_t c;
173 int n, s;
175 /* gets the color definitions and attribute names */
176 colors = mpdm_hget_s(mp, L"colors");
177 l = mpdm_keys(colors);
178 s = mpdm_size(l);
180 /* redim the structures */
181 inks = realloc(inks, sizeof(COLORREF) * s);
182 papers = realloc(papers, sizeof(COLORREF) * s);
184 /* loop the colors */
185 for (n = 0; n < s && (c = mpdm_aget(l, n)) != NULL; n++) {
186 mpdm_t d = mpdm_hget(colors, c);
187 mpdm_t v = mpdm_hget_s(d, L"gui");
188 int m;
190 /* store the 'normal' attribute */
191 if (wcscmp(mpdm_string(c), L"normal") == 0)
192 normal_attr = n;
194 /* store the attr */
195 mpdm_hset_s(d, L"attr", MPDM_I(n));
197 m = mpdm_ival(mpdm_aget(v, 0));
198 inks[n] = ((m & 0x000000ff) << 16)|
199 ((m & 0x0000ff00)) |
200 ((m & 0x00ff0000) >> 16);
201 m = mpdm_ival(mpdm_aget(v, 1));
202 papers[n] = ((m & 0x000000ff) << 16)|
203 ((m & 0x0000ff00)) |
204 ((m & 0x00ff0000) >> 16);
206 /* flags */
207 v = mpdm_hget_s(d, L"flags");
208 /* underlines[attr] = mpdm_seek_s(v, L"underline", 1) != -1 ? 1 : 0;
210 if (mpdm_seek_s(v, L"reverse", 1) != -1) {
211 COLORREF t;
213 t = inks[n];
214 inks[n] = papers[n];
215 papers[n] = t;
219 /* create the background brush */
220 bgbrush = CreateSolidBrush(papers[normal_attr]);
224 static void build_menu(void)
225 /* builds the menu */
227 int n;
228 mpdm_t m;
229 int win32_menu_id = 1000;
231 /* gets the current menu */
232 if ((m = mpdm_hget_s(mp, L"menu")) == NULL)
233 return;
235 if (menu != NULL)
236 DestroyMenu(menu);
238 menu = CreateMenu();
240 for (n = 0; n < mpdm_size(m); n++) {
241 char * ptr;
242 mpdm_t mi, v, l;
243 int i;
244 HMENU submenu = CreatePopupMenu();
246 /* get the label and the items */
247 mi = mpdm_aget(m, n);
248 v = mpdm_gettext(mpdm_aget(mi, 0));
249 l = mpdm_aget(mi, 1);
251 /* create the submenus */
252 for (i = 0; i < mpdm_size(l); i++) {
253 /* get the action */
254 mpdm_t v = mpdm_aget(l, i);
256 /* if the action is a separator... */
257 if (*((wchar_t *)v->data) == L'-')
258 AppendMenu(submenu, MF_SEPARATOR, 0, NULL);
259 else {
260 MENUITEMINFO mi;
261 mpdm_t d = mp_menu_label(v);
263 /* set the string */
264 ptr = mpdm_wcstombs(mpdm_string(d), NULL);
265 AppendMenu(submenu, MF_STRING, win32_menu_id, ptr);
266 free(ptr);
268 /* store the action inside the menu */
269 memset(&mi, '\0', sizeof(mi));
270 mi.cbSize = sizeof(mi);
271 mi.fMask = MIIM_DATA;
272 mi.dwItemData = (unsigned long)v;
274 SetMenuItemInfo(submenu, win32_menu_id, FALSE, &mi);
276 win32_menu_id++;
280 /* now store the popup inside the menu */
281 ptr = mpdm_wcstombs(mpdm_string(v), NULL);
282 AppendMenu(menu, MF_STRING|MF_POPUP, (UINT)submenu, ptr);
283 free(ptr);
286 SetMenu(hwnd, menu);
290 static void draw_filetabs(void)
291 /* draws the filetabs */
293 static mpdm_t last = NULL;
294 mpdm_t names;
295 int n;
297 if (hwtabs == NULL)
298 return;
300 names = mp_get_doc_names();
302 /* is the list different from the previous one? */
303 if (mpdm_cmp(names, last) != 0) {
304 TabCtrl_DeleteAllItems(hwtabs);
306 for (n = 0; n < mpdm_size(names); n++) {
307 TCITEM ti;
308 char * ptr;
309 mpdm_t v = mpdm_aget(names, n);
311 /* convert to mbs */
312 ptr = mpdm_wcstombs(v->data, NULL);
314 ti.mask = TCIF_TEXT;
315 ti.pszText = ptr;
317 /* create it */
318 TabCtrl_InsertItem(hwtabs, n, &ti);
320 free(ptr);
323 /* store for the next time */
324 mpdm_unref(last); last = mpdm_ref(names);
327 /* set the active one */
328 TabCtrl_SetCurSel(hwtabs, mpdm_ival(mpdm_hget_s(mp, L"active_i")));
332 static void draw_scrollbar(void)
333 /* updates the scrollbar */
335 mpdm_t d;
336 mpdm_t v;
337 int pos, size, max;
338 SCROLLINFO si;
340 /* gets the active document */
341 if ((d = mp_active()) == NULL)
342 return;
344 /* get the coordinates */
345 v = mpdm_hget_s(d, L"txt");
346 pos = mpdm_ival(mpdm_hget_s(v, L"y"));
347 max = mpdm_size(mpdm_hget_s(v, L"lines"));
349 v = mpdm_hget_s(mp, L"window");
350 size = mpdm_ival(mpdm_hget_s(v, L"ty"));
352 si.cbSize=sizeof(si);
353 si.fMask=SIF_ALL;
354 si.nMin=1;
355 si.nMax=max;
356 si.nPage=size;
357 si.nPos=pos;
359 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
363 void draw_status(void)
364 /* draws the status line */
366 mpdm_t t;
368 if (hwstatus != NULL && (t = mp_build_status_line()) != NULL) {
369 t = MPDM_2MBS(t->data);
371 if (t->data != NULL)
372 SetWindowText(hwstatus, t->data);
377 static void win32_draw(HWND hwnd, mpdm_t doc)
378 /* win32 document draw function */
380 HDC hdc;
381 PAINTSTRUCT ps;
382 RECT rect;
383 RECT r2;
384 mpdm_t d = NULL;
385 int n, m;
387 /* start painting */
388 hdc = BeginPaint(hwnd, &ps);
390 /* no font? construct it */
391 if (font_normal == NULL) {
392 build_fonts(hdc);
393 build_colors();
396 /* no document? end */
397 if ((d = mp_draw(doc, 0)) == NULL) {
398 EndPaint(hwnd, &ps);
399 return;
402 /* select defaults to start painting */
403 SelectObject(hdc, font_normal);
405 GetClientRect(hwnd, &rect);
406 r2 = rect;
408 r2.top += tab_height;
409 r2.bottom = r2.top + font_height;
411 for (n = 0; n < mpdm_size(d); n++) {
412 mpdm_t l = mpdm_aget(d, n);
414 r2.left = rect.left;
416 for (m = 0; m < mpdm_size(l); m++) {
417 int attr;
418 mpdm_t s;
420 /* get the attribute and the string */
421 attr = mpdm_ival(mpdm_aget(l, m++));
422 s = mpdm_aget(l, m);
424 SetTextColor(hdc, inks[attr]);
425 SetBkColor(hdc, papers[attr]);
427 SelectObject(hdc, color==MP_COLOR_COMMENT ?
428 _font_italic :
429 color==MP_COLOR_LOCAL ? _font_underline :
430 _font_normal);
432 /* DrawTextW(hdc, (wchar_t *)s->data,
433 -1, &r2, DT_SINGLELINE|DT_NOPREFIX);*/
434 TextOutW(hdc, r2.left, r2.top, s->data, mpdm_size(s));
435 r2.left += mpdm_size(s) * font_width;
438 /* fills the rest of the line */
439 FillRect(hdc, &r2, bgbrush);
441 r2.top += font_height;
442 r2.bottom += font_height;
445 EndPaint(hwnd, &ps);
447 draw_filetabs();
448 draw_scrollbar();
449 draw_status();
453 static void redraw(void)
455 InvalidateRect(hwnd, NULL, TRUE);
459 static void win32_vkey(int c)
460 /* win32 virtual key processing */
462 wchar_t * ptr = NULL;
463 static int maxed = 0;
465 /* set mp.shift_pressed */
466 if (GetKeyState(VK_SHIFT) & 0x8000)
467 mpdm_hset_s(mp, L"shift_pressed", MPDM_I(1));
469 if (GetKeyState(VK_CONTROL) & 0x8000 ||
470 GetKeyState(VK_MENU) & 0x8000) {
471 switch (c) {
472 case VK_UP: ptr = L"ctrl-cursor-up"; break;
473 case VK_DOWN: ptr = L"ctrl-cursor-down"; break;
474 case VK_LEFT: ptr = L"ctrl-cursor-left"; break;
475 case VK_RIGHT: ptr = L"ctrl-cursor-right"; break;
476 case VK_PRIOR: ptr = L"ctrl-page-up"; break;
477 case VK_NEXT: ptr = L"ctrl-page-down"; break;
478 case VK_HOME: ptr = L"ctrl-home"; break;
479 case VK_END: ptr = L"ctrl-end"; break;
480 case VK_SPACE: ptr = L"ctrl-space"; break;
481 case VK_DIVIDE: ptr = L"ctrl-kp-divide"; break;
482 case VK_MULTIPLY: ptr = L"ctrl-kp-multiply"; break;
483 case VK_SUBTRACT: ptr = L"ctrl-kp-minus"; break;
484 case VK_ADD: ptr = L"ctrl-kp-plus"; break;
485 case VK_RETURN: ptr = L"ctrl-enter"; break;
486 case VK_F1: ptr = L"ctrl-f1"; break;
487 case VK_F2: ptr = L"ctrl-f2"; break;
488 case VK_F3: ptr = L"ctrl-f3"; break;
489 case VK_F4: ptr = L"ctrl-f4"; break;
490 case VK_F5: ptr = L"ctrl-f5"; break;
491 case VK_F6: ptr = L"ctrl-f6"; break;
492 case VK_F7: ptr = L"ctrl-f7"; break;
493 case VK_F8: ptr = L"ctrl-f8"; break;
494 case VK_F9: ptr = L"ctrl-f9"; break;
495 case VK_F10: ptr = L"ctrl-f10"; break;
496 case VK_F11: ptr = L"ctrl-f11"; break;
497 case VK_F12:
498 SendMessage(hwnd, WM_SYSCOMMAND,
499 maxed ? SC_RESTORE : SC_MAXIMIZE, 0);
501 maxed ^= 1;
503 break;
506 else {
507 switch (c) {
508 case VK_UP: ptr = L"cursor-up"; break;
509 case VK_DOWN: ptr = L"cursor-down"; break;
510 case VK_LEFT: ptr = L"cursor-left"; break;
511 case VK_RIGHT: ptr = L"cursor-right"; break;
512 case VK_PRIOR: ptr = L"page-up"; break;
513 case VK_NEXT: ptr = L"page-down"; break;
514 case VK_HOME: ptr = L"home"; break;
515 case VK_END: ptr = L"end"; break;
516 case VK_TAB: ptr = L"tab"; break;
517 case VK_RETURN: ptr = L"enter"; break;
518 case VK_BACK: ptr = L"backspace"; break;
519 case VK_DELETE: ptr = L"delete"; break;
520 case VK_INSERT: ptr = L"insert"; break;
521 case VK_DIVIDE: ptr = L"kp-divide"; break;
522 case VK_MULTIPLY: ptr = L"kp-multiply"; break;
523 case VK_SUBTRACT: ptr = L"kp-minus"; break;
524 case VK_ADD: ptr = L"kp-plus"; break;
525 case VK_F1: ptr = L"f1"; break;
526 case VK_F2: ptr = L"f2"; break;
527 case VK_F3: ptr = L"f3"; break;
528 case VK_F4: ptr = L"f4"; break;
529 case VK_F5: ptr = L"f5"; break;
530 case VK_F6: ptr = L"f6"; break;
531 case VK_F7: ptr = L"f7"; break;
532 case VK_F8: ptr = L"f8"; break;
533 case VK_F9: ptr = L"f9"; break;
534 case VK_F10: ptr = L"f10"; break;
535 case VK_F11: ptr = L"f11"; break;
536 case VK_F12: ptr = L"f12"; break;
540 if (ptr != NULL) {
541 mp_process_event(MPDM_S(ptr));
542 is_wm_keydown = 1;
543 mp_active();
545 if (mp_keypress_throttle(1))
546 redraw();
551 #define ctrl(c) ((c) & 31)
553 static void win32_akey(int k)
554 /* win32 alphanumeric key processing */
556 wchar_t c[2];
557 wchar_t * ptr = NULL;
559 if (is_wm_keydown)
560 return;
562 /* set mp.shift_pressed */
563 if (GetKeyState(VK_SHIFT) & 0x8000)
564 mpdm_hset_s(mp, L"shift_pressed", MPDM_I(1));
566 switch (k) {
567 case ctrl(' '): ptr = L"ctrl-space"; break;
568 case ctrl('a'): ptr = L"ctrl-a"; break;
569 case ctrl('b'): ptr = L"ctrl-b"; break;
570 case ctrl('c'): ptr = L"ctrl-c"; break;
571 case ctrl('d'): ptr = L"ctrl-d"; break;
572 case ctrl('e'): ptr = L"ctrl-e"; break;
573 case ctrl('f'): ptr = L"ctrl-f"; break;
574 case ctrl('g'): ptr = L"ctrl-g"; break;
575 case ctrl('h'): ptr = L"ctrl-h"; break;
576 case ctrl('i'): ptr = L"ctrl-i"; break;
577 case ctrl('j'): ptr = L"ctrl-j"; break;
578 case ctrl('k'): ptr = L"ctrl-k"; break;
579 case ctrl('l'): ptr = L"ctrl-l"; break;
580 case ctrl('m'): ptr = L"ctrl-m"; break;
581 case ctrl('n'): ptr = L"ctrl-n"; break;
582 case ctrl('o'): ptr = L"ctrl-o"; break;
583 case ctrl('p'): ptr = L"ctrl-p"; break;
584 case ctrl('q'): ptr = L"ctrl-q"; break;
585 case ctrl('r'): ptr = L"ctrl-r"; break;
586 case ctrl('s'): ptr = L"ctrl-s"; break;
587 case ctrl('t'): ptr = L"ctrl-t"; break;
588 case ctrl('u'): ptr = L"ctrl-u"; break;
589 case ctrl('v'): ptr = L"ctrl-v"; break;
590 case ctrl('w'): ptr = L"ctrl-w"; break;
591 case ctrl('x'): ptr = L"ctrl-x"; break;
592 case ctrl('y'): ptr = L"ctrl-y"; break;
593 case ctrl('z'): ptr = L"ctrl-z"; break;
594 case ' ': ptr = L"space"; break;
595 case 27: ptr = L"escape"; break;
597 default:
598 /* this is probably very bad */
599 c[0] = (wchar_t) k;
600 c[1] = L'\0';
601 ptr = c;
603 break;
606 if (ptr != NULL) {
607 mp_process_event(MPDM_S(ptr));
608 mp_active();
609 redraw();
614 static void win32_vscroll(UINT wparam)
615 /* scrollbar messages handler */
617 wchar_t * ptr = NULL;
619 switch (LOWORD(wparam)) {
620 case SB_PAGEUP: ptr = L"page-up"; break;
621 case SB_PAGEDOWN: ptr = L"page-down"; break;
622 case SB_LINEUP: ptr = L"cursor-up"; break;
623 case SB_LINEDOWN: ptr = L"cursor-down"; break;
624 case SB_THUMBPOSITION:
625 case SB_THUMBTRACK:
626 mp_set_y(mp_active(), HIWORD(wparam));
627 redraw();
628 break;
631 if (ptr != NULL) {
632 mp_process_event(MPDM_S(ptr));
633 redraw();
638 static void action_by_menu(int item)
639 /* execute an action triggered by the menu */
641 MENUITEMINFO mi;
643 memset(&mi, '\0', sizeof(mi));
644 mi.cbSize = sizeof(mi);
645 mi.fMask = MIIM_DATA;
647 if (GetMenuItemInfo(menu, item, FALSE, &mi)) {
648 if (mi.dwItemData != 0) {
649 mp_process_action((mpdm_t)mi.dwItemData);
650 mp_active();
656 #ifndef WM_MOUSEWHEEL
657 #define WM_MOUSEWHEEL 0x020A
658 #endif
660 long STDCALL WndProc(HWND hwnd, UINT msg, UINT wparam, LONG lparam)
661 /* main window Proc */
663 int x, y;
664 LPNMHDR p;
665 wchar_t * ptr = NULL;
667 switch (msg) {
668 case WM_CREATE:
670 is_wm_keydown = 0;
671 DragAcceptFiles(hwnd, TRUE);
672 return 0;
674 /* case WM_DROPFILES:
676 (void) load_dropped_files ((HANDLE) wparam, hwnd);
677 return(0);
679 case WM_KEYUP:
681 is_wm_keydown = 0;
683 if (mp_keypress_throttle(0))
684 redraw();
686 return 0;
688 case WM_KEYDOWN:
690 win32_vkey(wparam);
691 return 0;
693 case WM_CHAR:
695 win32_akey(wparam);
696 return 0;
698 case WM_VSCROLL:
700 win32_vscroll(wparam);
701 return 0;
703 case WM_PAINT:
705 if (mpdm_size(mpdm_hget_s(mp, L"docs")))
706 win32_draw(hwnd, mp_active());
708 return 0;
710 case WM_SIZE:
712 if (!IsIconic(hwnd)) {
713 update_window_size();
715 MoveWindow(hwtabs, 0, 0, LOWORD(lparam), tab_height, FALSE);
717 MoveWindow(hwstatus, 0, HIWORD(lparam) - status_height,
718 LOWORD(lparam), status_height, FALSE);
720 redraw();
723 return 0;
725 case WM_LBUTTONDOWN:
727 mouse_down = 1;
728 /* fallthrough */
730 case WM_RBUTTONDOWN:
731 case WM_MBUTTONDOWN:
733 x = (LOWORD(lparam)) / font_width;
734 y = (HIWORD(lparam) - tab_height) / font_height;
736 mpdm_hset_s(mp, L"mouse_x", MPDM_I(x));
737 mpdm_hset_s(mp, L"mouse_y", MPDM_I(y));
739 switch (msg) {
740 case WM_LBUTTONDOWN: ptr = L"mouse-left-button"; break;
741 case WM_RBUTTONDOWN: ptr = L"mouse-right-button"; break;
742 case WM_MBUTTONDOWN: ptr = L"mouse-middle-button"; break;
745 if (ptr != NULL) {
746 mp_process_event(MPDM_S(ptr));
747 redraw();
750 return 0;
752 case WM_LBUTTONUP:
754 mouse_down = 0;
755 return 0;
757 case WM_MOUSEMOVE:
759 if (mouse_down) {
760 x = (LOWORD(lparam)) / font_width;
761 y = (HIWORD(lparam) - tab_height) / font_height;
763 mpdm_hset_s(mp, L"mouse_to_x", MPDM_I(x));
764 mpdm_hset_s(mp, L"mouse_to_y", MPDM_I(y));
766 mp_process_event(MPDM_LS(L"mouse-drag"));
767 redraw();
770 return 0;
772 case WM_MOUSEWHEEL:
774 if ((int) wparam > 0)
775 ptr = L"mouse-wheel-up";
776 else
777 ptr = L"mouse-wheel-down";
779 if (ptr != NULL) {
780 mp_process_event(MPDM_S(ptr));
781 redraw();
784 return 0;
786 case WM_COMMAND:
788 action_by_menu(LOWORD(wparam));
789 redraw();
791 return 0;
793 case WM_CLOSE:
795 if (!mp_exit_requested)
796 mp_process_event(MPDM_LS(L"close-window"));
798 if (mp_exit_requested)
799 DestroyWindow(hwnd);
801 return 0;
803 case WM_DESTROY:
804 PostQuitMessage(0);
805 return 0;
807 case WM_NOTIFY:
808 p = (LPNMHDR)lparam;
810 if (p->code == TCN_SELCHANGE) {
811 /* tab selected by clicking on it */
812 int n = TabCtrl_GetCurSel(hwtabs);
814 /* set mp.active_i to this */
815 mpdm_hset_s(mp, L"active_i", MPDM_I(n));
817 redraw();
820 return 0;
822 case WM_TIMER:
823 mpdm_exec(timer_func, NULL);
825 return 0;
828 if (mp_exit_requested)
829 PostMessage(hwnd, WM_CLOSE, 0, 0);
831 return DefWindowProc(hwnd, msg, wparam, lparam);
835 static mpdm_t win32_drv_clip_to_sys(mpdm_t a)
836 /* driver-dependent mp to system clipboard */
838 HGLOBAL hclp;
839 mpdm_t d;
840 char * ptr;
841 char * clpptr;
842 int s;
844 /* convert the clipboard to DOS text */
845 d = mpdm_hget_s(mp, L"clipboard");
846 d = mpdm_join(MPDM_LS(L"\r\n"), d);
847 ptr = mpdm_wcstombs(d->data, &s);
849 /* allocates a handle and copies */
850 hclp = GlobalAlloc(GHND, s + 1);
851 clpptr = (char *)GlobalLock(hclp);
852 memcpy(clpptr, ptr, s);
853 clpptr[s] = '\0';
854 GlobalUnlock(hclp);
856 free(ptr);
858 OpenClipboard(NULL);
859 EmptyClipboard();
860 SetClipboardData(CF_TEXT, hclp);
861 CloseClipboard();
863 return NULL;
867 static mpdm_t win32_drv_sys_to_clip(mpdm_t a)
868 /* driver-dependent system to mp clipboard */
870 HGLOBAL hclp;
871 char * ptr;
873 OpenClipboard(NULL);
874 hclp = GetClipboardData(CF_TEXT);
875 CloseClipboard();
877 if (hclp && (ptr = GlobalLock(hclp)) != NULL) {
878 mpdm_t d;
880 /* create a value and split */
881 d = MPDM_MBS(ptr);
882 d = mpdm_split(MPDM_LS(L"\r\n"), d);
884 /* and set as the clipboard */
885 mpdm_hset_s(mp, L"clipboard", d);
886 mpdm_hset_s(mp, L"clipboard_vertical", MPDM_I(0));
888 GlobalUnlock(hclp);
891 return NULL;
895 static mpdm_t win32_drv_main_loop(mpdm_t a)
897 MSG msg;
899 if (!mp_exit_requested) {
900 mp_active();
902 while (GetMessage(&msg, NULL, 0, 0)) {
903 TranslateMessage(&msg);
904 DispatchMessage(&msg);
908 return NULL;
912 static mpdm_t win32_drv_shutdown(mpdm_t a)
914 mpdm_t v;
916 SendMessage(hwnd, WM_CLOSE, 0, 0);
918 if ((v = mpdm_hget_s(mp, L"exit_message")) != NULL) {
919 char * ptr = mpdm_wcstombs(mpdm_string(v), NULL);
920 MessageBox(NULL, ptr, "mp " VERSION, MB_ICONWARNING|MB_OK);
921 free(ptr);
924 return NULL;
928 static mpdm_t win32_drv_alert(mpdm_t a)
929 /* alert driver function */
931 wchar_t * wptr;
932 char * ptr;
934 /* 1# arg: prompt */
935 wptr = mpdm_string(mpdm_aget(a, 0));
937 if ((ptr = mpdm_wcstombs(wptr, NULL)) != NULL) {
938 MessageBox(hwnd, ptr, "mp " VERSION, MB_ICONWARNING|MB_OK);
939 free(ptr);
942 return NULL;
946 static mpdm_t win32_drv_confirm(mpdm_t a)
947 /* confirm driver function */
949 wchar_t * wptr;
950 char * ptr;
951 int ret = 0;
953 /* 1# arg: prompt */
954 wptr = mpdm_string(mpdm_aget(a, 0));
956 if ((ptr = mpdm_wcstombs(wptr, NULL)) != NULL) {
957 ret = MessageBox(hwnd, ptr, "mp " VERSION, MB_ICONQUESTION|MB_YESNOCANCEL);
958 free(ptr);
960 if (ret == IDYES)
961 ret = 1;
962 else
963 if (ret == IDNO)
964 ret = 2;
965 else
966 ret = 0;
969 return MPDM_I(ret);
973 static LPWORD lpwAlign(LPWORD lpIn)
974 /* aligns a pointer to DWORD boundary (for dialog templates) */
976 ULONG ul;
978 ul = (ULONG)lpIn;
979 ul ++;
980 ul >>= 1;
981 ul <<= 1;
982 return (LPWORD)ul;
986 #define LABEL_ID 1000
987 #define CTRL_ID 2000
989 BOOL CALLBACK formDlgProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
990 /* mp.drv.form() dialog proc */
992 int n;
993 HFONT hf;
995 switch (msg) {
996 case WM_INITDIALOG:
998 SetWindowText(hwnd, "mp " VERSION);
1000 hf = GetStockObject(DEFAULT_GUI_FONT);
1002 /* fill controls with its initial data */
1003 for (n = 0; n < mpdm_size(form_args); n++) {
1004 mpdm_t w = mpdm_aget(form_args, n);
1005 wchar_t * type;
1006 mpdm_t t;
1007 int ctrl = CTRL_ID + n;
1008 wchar_t * wptr;
1009 char * ptr;
1011 if ((t = mpdm_hget_s(w, L"label")) != NULL) {
1012 if ((ptr = mpdm_wcstombs(mpdm_string(t), NULL)) != NULL) {
1013 SetDlgItemText(hwnd, LABEL_ID + n, ptr);
1014 free(ptr);
1016 SendDlgItemMessage(hwnd, LABEL_ID + n, WM_SETFONT,
1017 (WPARAM) hf, MAKELPARAM(FALSE, 0));
1021 SendDlgItemMessage(hwnd, ctrl, WM_SETFONT,
1022 (WPARAM) hf, MAKELPARAM(FALSE, 0));
1024 type = mpdm_string(mpdm_hget_s(w, L"type"));
1026 if (wcscmp(type, L"text") == 0) {
1027 if ((t = mpdm_hget_s(w, L"value")) != NULL &&
1028 (ptr = mpdm_wcstombs(mpdm_string(t), NULL)) != NULL) {
1029 SetDlgItemText(hwnd, ctrl, ptr);
1030 free(ptr);
1033 /* store the history into combo_items */
1034 if ((t = mpdm_hget_s(w, L"history")) != NULL) {
1035 t = mp_get_history(t);
1036 int i;
1038 for (i = 0; i < mpdm_size(t); i++) {
1039 mpdm_t v = mpdm_aget(t, i);
1041 if ((ptr = mpdm_wcstombs(v->data,
1042 NULL)) != NULL) {
1043 SendDlgItemMessage(hwnd,
1044 ctrl,
1045 CB_INSERTSTRING, 0,
1046 (LPARAM)ptr);
1047 free(ptr);
1052 else
1053 if (wcscmp(type, L"password") == 0) {
1054 SendDlgItemMessage(hwnd, ctrl,
1055 EM_SETPASSWORDCHAR, (WPARAM)'*', (LPARAM)0);
1057 else
1058 if (wcscmp(type, L"checkbox") == 0) {
1059 if ((t = mpdm_hget_s(w, L"value")) != NULL)
1060 SendDlgItemMessage(hwnd, ctrl,
1061 BM_SETCHECK, mpdm_ival(t) ?
1062 BST_CHECKED : BST_UNCHECKED,
1065 else
1066 if (wcscmp(type, L"list") == 0) {
1067 int i;
1069 t = mpdm_hget_s(w, L"list");
1071 /* fill the list */
1072 for (i = 0; i < mpdm_size(t); i++) {
1073 wptr = mpdm_string(mpdm_aget(t, i));
1074 if ((ptr = mpdm_wcstombs(wptr, NULL)) != NULL) {
1075 SendDlgItemMessage(hwnd, ctrl,
1076 LB_ADDSTRING, 0, (LPARAM) ptr);
1077 free(ptr);
1081 /* set position */
1082 SendDlgItemMessage(hwnd, ctrl, LB_SETCURSEL,
1083 mpdm_ival(mpdm_hget_s(w, L"value")), 0);
1087 /* FIXME: untranslated strings */
1089 SetDlgItemText(hwnd, IDOK, "OK");
1090 SendDlgItemMessage(hwnd, IDOK, WM_SETFONT,
1091 (WPARAM) hf, MAKELPARAM(FALSE, 0));
1093 SetDlgItemText(hwnd, IDCANCEL, "Cancel");
1094 SendDlgItemMessage(hwnd, IDCANCEL, WM_SETFONT,
1095 (WPARAM) hf, MAKELPARAM(FALSE, 0));
1097 return TRUE;
1099 case WM_COMMAND:
1101 if (LOWORD(wparam) == IDCANCEL) {
1102 EndDialog(hwnd, 0);
1103 return TRUE;
1106 if (LOWORD(wparam) != IDOK)
1107 break;
1109 /* fill all return values */
1110 for (n = 0; n < mpdm_size(form_args); n++) {
1111 mpdm_t w = mpdm_aget(form_args, n);
1112 wchar_t * type = mpdm_string(mpdm_hget_s(w, L"type"));
1113 int ctrl = CTRL_ID + n;
1115 if (wcscmp(type, L"text") == 0) {
1116 char tmp[2048];
1117 mpdm_t v;
1118 mpdm_t h;
1120 GetDlgItemText(hwnd, ctrl, tmp, sizeof(tmp) - 1);
1121 v = MPDM_MBS(tmp);
1123 mpdm_aset(form_values, v, n);
1125 /* if it has history, fill it */
1126 if ((h = mpdm_hget_s(w, L"history")) != NULL &&
1127 v != NULL && mpdm_cmp(v, MPDM_LS(L"")) != 0) {
1128 h = mp_get_history(h);
1130 if (mpdm_cmp(v, mpdm_aget(h, -1)) != 0)
1131 mpdm_push(h, v);
1134 if (wcscmp(type, L"password") == 0) {
1135 char tmp[2048];
1137 GetDlgItemText(hwnd, ctrl, tmp, sizeof(tmp) - 1);
1138 mpdm_aset(form_values, MPDM_MBS(tmp), n);
1140 else
1141 if (wcscmp(type, L"checkbox") == 0) {
1142 mpdm_aset(form_values,
1143 MPDM_I(SendDlgItemMessage(hwnd, ctrl,
1144 BM_GETCHECK, 0, 0)), n);
1146 else
1147 if (wcscmp(type, L"list") == 0) {
1148 mpdm_aset(form_values,
1149 MPDM_I(SendDlgItemMessage(hwnd, ctrl,
1150 LB_GETCURSEL, 0, 0)), n);
1154 EndDialog(hwnd, 1);
1155 return TRUE;
1158 return FALSE;
1162 static void build_form_data(mpdm_t widget_list)
1163 /* builds the necessary information for a list of widgets */
1165 mpdm_unref(form_args);
1166 form_args = mpdm_ref(widget_list);
1168 mpdm_unref(form_values);
1169 form_values = widget_list == NULL ? NULL :
1170 mpdm_ref(MPDM_A(mpdm_size(form_args)));
1174 LPWORD static build_control(LPWORD lpw, int x, int y,
1175 int cx, int cy, int id, int class, int style)
1176 /* fills a control structure in a hand-made dialog template */
1178 LPDLGITEMTEMPLATE lpdit;
1180 lpw = lpwAlign(lpw);
1181 lpdit = (LPDLGITEMTEMPLATE)lpw;
1182 lpdit->x = x; lpdit->y = y;
1183 lpdit->cx = cx; lpdit->cy = cy;
1184 lpdit->id = id;
1185 lpdit->style = style;
1187 lpw = (LPWORD)(lpdit + 1);
1188 *lpw++ = 0xFFFF;
1189 *lpw++ = class;
1191 /* no text (will be set on dialog setup) */
1192 *lpw++ = 0;
1193 *lpw++ = 0;
1195 /* Align creation data on DWORD boundary */
1196 lpw = lpwAlign(lpw);
1197 /* No creation data */
1198 *lpw++ = 0;
1200 return lpw;
1204 static mpdm_t win32_drv_form(mpdm_t a)
1205 /* mp.drv.form() function */
1207 HGLOBAL hgbl;
1208 LPDLGTEMPLATE lpdt;
1209 LPWORD lpw;
1210 int n, p;
1211 int il = 10;
1212 int lbl = 0;
1214 /* first argument: list of widgets */
1215 build_form_data(mpdm_aget(a, 0));
1217 /* On-the-fly dialog template creation */
1218 /* Note: all this crap is taken from MSDN, no less */
1220 /* magic size; looking for problems */
1221 hgbl = GlobalAlloc(GMEM_ZEROINIT, 4096);
1222 lpdt = (LPDLGTEMPLATE)GlobalLock(hgbl);
1224 lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU | DS_MODALFRAME | WS_CAPTION;
1225 lpdt->cdit = (2 * mpdm_size(form_args)) + 2;
1226 lpdt->x = 20; lpdt->y = 20;
1227 lpdt->cx = 260;
1229 lpw = (LPWORD)(lpdt + 1);
1230 *lpw++ = 0; /* No menu */
1231 *lpw++ = 0; /* Predefined dialog box class (by default) */
1232 *lpw++ = 0; /* No title */
1234 /* first pass: calculate maximum size of labels */
1235 for (n = 0; n < mpdm_size(form_args); n++) {
1236 mpdm_t w = mpdm_aget(form_args, n);
1237 int l = mpdm_size(mpdm_hget_s(w, L"label"));
1239 if (lbl < l)
1240 lbl = l;
1243 /* second pass: create the dialog controls */
1244 for (n = p = 0; n < mpdm_size(form_args); n++) {
1245 mpdm_t w = mpdm_aget(form_args, n);
1246 wchar_t * type;
1247 int class;
1248 int style;
1249 int inc = 1;
1251 /* label control */
1252 lpw = build_control(lpw, 0, 5 + p * il,
1253 lbl * 3, 20, LABEL_ID + n, 0x0082,
1254 WS_CHILD | WS_VISIBLE | SS_RIGHT);
1256 type = mpdm_string(mpdm_hget_s(w, L"type"));
1258 if (wcscmp(type, L"text") == 0) {
1259 class = 0x0085;
1260 style = WS_CHILD | WS_VISIBLE | WS_TABSTOP |
1261 CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL;
1263 else
1264 if (wcscmp(type, L"password") == 0) {
1265 class = 0x0081;
1266 style = WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP;
1268 else
1269 if (wcscmp(type, L"checkbox") == 0) {
1270 class = 0x0080;
1271 style = WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX | WS_TABSTOP;
1273 else
1274 if (wcscmp(type, L"list") == 0) {
1275 class = 0x0083;
1276 style = WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER |
1277 LBS_NOINTEGRALHEIGHT | WS_VSCROLL |
1278 LBS_NOTIFY | LBS_USETABSTOPS;
1280 /* height */
1281 inc = 5;
1284 /* the control */
1285 lpw = build_control(lpw, 10 + lbl * 3, 5 + p * il,
1286 245 - lbl * 3, inc * il, CTRL_ID + n, class, style);
1288 /* next position */
1289 p += inc;
1292 /* set total height */
1293 lpdt->cy = 30 + p * il;
1295 /* OK */
1296 lpw = build_control(lpw, 170, 10 + p * il, 40, 15, IDOK,
1297 0x0080, WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON | WS_TABSTOP);
1299 /* Cancel */
1300 lpw = build_control(lpw, 215, 10 + p * il, 40, 15, IDCANCEL,
1301 0x0080, WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP);
1303 GlobalUnlock(hgbl);
1304 n = DialogBoxIndirect(hinst, (LPDLGTEMPLATE)hgbl,
1305 hwnd, (DLGPROC)formDlgProc);
1307 GlobalFree(hgbl);
1309 return n ? form_values : NULL;
1313 static mpdm_t open_or_save(int o, mpdm_t a)
1314 /* manages an open or save file dialog */
1316 OPENFILENAME ofn;
1317 wchar_t * wptr;
1318 char * ptr;
1319 char buf[1024] = "";
1320 int r;
1322 /* 1# arg: prompt */
1323 wptr = mpdm_string(mpdm_aget(a, 0));
1324 ptr = mpdm_wcstombs(wptr, NULL);
1326 memset(&ofn, '\0', sizeof(OPENFILENAME));
1327 ofn.lStructSize = sizeof(OPENFILENAME);
1328 ofn.hwndOwner = hwnd;
1329 ofn.lpstrFilter = "*.*\0*.*\0";
1330 ofn.nFilterIndex = 1;
1331 ofn.lpstrFile = buf;
1332 ofn.nMaxFile = sizeof(buf);
1333 ofn.lpstrTitle = ptr;
1334 ofn.lpstrDefExt = "";
1335 /* ofn.lpstrDefExt=(def==NULL ? "" : def);*/
1337 if (o) {
1338 ofn.Flags = OFN_PATHMUSTEXIST|OFN_HIDEREADONLY|
1339 OFN_NOCHANGEDIR|OFN_FILEMUSTEXIST;
1341 r = GetOpenFileName(&ofn);
1343 else {
1344 ofn.Flags = OFN_HIDEREADONLY;
1346 r = GetSaveFileName(&ofn);
1349 free(ptr);
1351 if (r)
1352 return MPDM_MBS(buf);
1354 return NULL;
1358 static mpdm_t win32_drv_openfile(mpdm_t a)
1359 /* openfile driver function */
1361 return open_or_save(1, a);
1365 static mpdm_t win32_drv_savefile(mpdm_t a)
1366 /* savefile driver function */
1368 return open_or_save(0, a);
1372 static mpdm_t win32_drv_update_ui(mpdm_t a)
1374 build_fonts(GetDC(hwnd));
1375 build_colors();
1376 build_menu();
1378 return NULL;
1382 static mpdm_t win32_drv_timer(mpdm_t a)
1384 int msecs = mpdm_ival(mpdm_aget(a, 0));
1385 mpdm_t func = mpdm_aget(a, 1);
1386 mpdm_t r;
1388 /* previously defined one? remove */
1389 if (timer_func != NULL)
1390 KillTimer(hwnd, 1);
1392 /* if msecs and func are set, program timer */
1393 if (msecs > 0 && func != NULL)
1394 SetTimer(hwnd, 1, msecs, NULL);
1396 r = mpdm_unref(timer_func);
1397 timer_func = mpdm_ref(func);
1399 return r;
1403 static mpdm_t win32_drv_busy(mpdm_t a)
1405 int onoff = mpdm_ival(mpdm_aget(a, 0));
1407 SetCursor(LoadCursor(NULL, onoff ? IDC_WAIT : IDC_ARROW));
1409 return NULL;
1413 static void register_functions(void)
1415 mpdm_t drv;
1417 drv = mpdm_hget_s(mp, L"drv");
1418 mpdm_hset_s(drv, L"main_loop", MPDM_X(win32_drv_main_loop));
1419 mpdm_hset_s(drv, L"shutdown", MPDM_X(win32_drv_shutdown));
1421 mpdm_hset_s(drv, L"clip_to_sys", MPDM_X(win32_drv_clip_to_sys));
1422 mpdm_hset_s(drv, L"sys_to_clip", MPDM_X(win32_drv_sys_to_clip));
1423 mpdm_hset_s(drv, L"update_ui", MPDM_X(win32_drv_update_ui));
1424 mpdm_hset_s(drv, L"timer", MPDM_X(win32_drv_timer));
1425 mpdm_hset_s(drv, L"busy", MPDM_X(win32_drv_busy));
1427 mpdm_hset_s(drv, L"alert", MPDM_X(win32_drv_alert));
1428 mpdm_hset_s(drv, L"confirm", MPDM_X(win32_drv_confirm));
1429 mpdm_hset_s(drv, L"openfile", MPDM_X(win32_drv_openfile));
1430 mpdm_hset_s(drv, L"savefile", MPDM_X(win32_drv_savefile));
1431 mpdm_hset_s(drv, L"form", MPDM_X(win32_drv_form));
1435 static mpdm_t win32_drv_startup(mpdm_t a)
1437 WNDCLASS wc;
1438 RECT r;
1439 mpdm_t v;
1441 register_functions();
1443 InitCommonControls();
1445 /* register the window */
1446 wc.style = CS_HREDRAW|CS_VREDRAW;
1447 wc.lpfnWndProc = WndProc;
1448 wc.cbClsExtra = 0;
1449 wc.cbWndExtra = 0;
1450 wc.hInstance = hinst;
1451 wc.hIcon = LoadIcon(hinst,"MP_ICON");
1452 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
1453 wc.hbrBackground = NULL;
1454 wc.lpszMenuName = NULL;
1455 wc.lpszClassName = "minimumprofit5.x";
1457 RegisterClass(&wc);
1459 /* create the window */
1460 hwnd = CreateWindow("minimumprofit5.x", "mp " VERSION,
1461 WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_VSCROLL,
1462 CW_USEDEFAULT, CW_USEDEFAULT,
1463 CW_USEDEFAULT, CW_USEDEFAULT,
1464 NULL, NULL, hinst, NULL);
1466 ShowWindow(hwnd, SW_SHOW);
1467 UpdateWindow(hwnd);
1469 GetClientRect(hwnd, &r);
1471 hwtabs = CreateWindow(WC_TABCONTROL, "tab",
1472 WS_CHILD | TCS_TABS | TCS_SINGLELINE | TCS_FOCUSNEVER,
1473 0, 0, r.right - r.left, tab_height, hwnd, NULL, hinst, NULL);
1475 SendMessage(hwtabs, WM_SETFONT,
1476 (WPARAM) GetStockObject(DEFAULT_GUI_FONT), 0);
1478 ShowWindow(hwtabs, SW_SHOW);
1479 UpdateWindow(hwtabs);
1481 hwstatus = CreateWindow(WC_STATIC, "status",
1482 WS_CHILD,
1483 0, r.bottom - r.top - status_height,
1484 r.right - r.left, status_height, hwnd, NULL, hinst, NULL);
1486 win32_drv_update_ui(NULL);
1488 ShowWindow(hwstatus, SW_SHOW);
1489 UpdateWindow(hwstatus);
1491 if ((v = mpdm_hget_s(mp, L"config")) != NULL &&
1492 mpdm_ival(mpdm_hget_s(v, L"maximize")) > 0)
1493 SendMessage(hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
1495 return NULL;
1499 int win32_drv_detect(int * argc, char *** argv)
1501 mpdm_t drv;
1503 drv = mpdm_hget_s(mp, L"drv");
1504 mpdm_hset_s(drv, L"id", MPDM_LS(L"win32"));
1505 mpdm_hset_s(drv, L"startup", MPDM_X(win32_drv_startup));
1507 return 1;
1510 #endif /* CONFOPT_WIN32 */