Updated RELEASE_NOTES.
[mp-5.x.git] / mpv_win32.c
blob80f732735828967992142030df6bfefc61a353dd
1 /*
3 Minimum Profit - Programmer Text Editor
5 Win32 driver.
7 Copyright (C) 1991-2009 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 /** data **/
46 /* the instance */
47 HINSTANCE hinst;
49 /* the windows */
50 HWND hwnd = NULL;
51 HWND hwtabs = NULL;
52 HWND hwstatus = NULL;
54 /* font handlers and metrics */
55 HFONT font_normal = NULL;
56 HFONT font_underline = NULL;
57 int font_width = 0;
58 int font_height = 0;
60 /* height of the tab set */
61 int tab_height = 28;
63 /* height of the status bar */
64 int status_height = 16;
66 int is_wm_keydown = 0;
68 /* colors */
69 static COLORREF *inks = NULL;
70 static COLORREF *papers = NULL;
71 int *underlines = 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 /** code **/
93 static void update_window_size(void)
94 /* updates the viewport size in characters */
96 RECT rect;
97 int tx, ty;
98 mpdm_t v;
100 /* no font information? go */
101 if (font_width == 0 || font_height == 0)
102 return;
104 GetClientRect(hwnd, &rect);
106 /* calculate the size in chars */
107 tx = ((rect.right - rect.left) / font_width) + 1;
108 ty = ((rect.bottom - rect.top - tab_height) / font_height) + 1;
110 /* store the 'window' size */
111 v = mpdm_hget_s(mp, L"window");
112 mpdm_hset_s(v, L"tx", MPDM_I(tx));
113 mpdm_hset_s(v, L"ty", MPDM_I(ty));
117 static void build_fonts(HDC hdc)
118 /* build the fonts */
120 TEXTMETRIC tm;
121 int n;
122 int font_size = 10;
123 char * font_face = "Lucida Console";
124 mpdm_t c;
126 if (font_normal != NULL) {
127 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
128 DeleteObject(font_normal);
131 /* get current configuration */
132 if ((c = mpdm_hget_s(mp, L"config")) != NULL) {
133 mpdm_t v;
135 if ((v = mpdm_hget_s(c, L"font_size")) != NULL)
136 font_size = mpdm_ival(v);
137 else
138 mpdm_hset_s(c, L"font_size", MPDM_I(font_size));
140 if ((v = mpdm_hget_s(c, L"font_face")) != NULL) {
141 v = MPDM_2MBS(v->data);
142 font_face = (char *)v->data;
144 else
145 mpdm_hset_s(c, L"font_face", MPDM_MBS(font_face));
148 /* create fonts */
149 n = -MulDiv(font_size, GetDeviceCaps(hdc, LOGPIXELSY), 72);
151 font_normal = CreateFont(n, 0, 0, 0, 0, 0, 0,
152 0, 0, 0, 0, 0, 0, font_face);
154 font_underline = CreateFont(n, 0, 0, 0, 0, 0, 1,
155 0, 0, 0, 0, 0, 0, font_face);
157 SelectObject(hdc, font_normal);
158 GetTextMetrics(hdc, &tm);
160 /* store sizes */
161 font_height = tm.tmHeight;
162 font_width = tm.tmAveCharWidth;
164 update_window_size();
168 static void build_colors(void)
169 /* builds the colors */
171 mpdm_t colors;
172 mpdm_t l;
173 mpdm_t c;
174 int n, s;
176 /* gets the color definitions and attribute names */
177 colors = mpdm_hget_s(mp, L"colors");
178 l = mpdm_keys(colors);
179 s = mpdm_size(l);
181 /* redim the structures */
182 inks = realloc(inks, sizeof(COLORREF) * s);
183 papers = realloc(papers, sizeof(COLORREF) * s);
184 underlines = realloc(underlines, sizeof(int) * s);
186 /* loop the colors */
187 for (n = 0; n < s && (c = mpdm_aget(l, n)) != NULL; n++) {
188 mpdm_t d = mpdm_hget(colors, c);
189 mpdm_t v = mpdm_hget_s(d, L"gui");
190 int m;
192 /* store the 'normal' attribute */
193 if (wcscmp(mpdm_string(c), L"normal") == 0)
194 normal_attr = n;
196 /* store the attr */
197 mpdm_hset_s(d, L"attr", MPDM_I(n));
199 m = mpdm_ival(mpdm_aget(v, 0));
200 inks[n] = ((m & 0x000000ff) << 16)|
201 ((m & 0x0000ff00)) |
202 ((m & 0x00ff0000) >> 16);
203 m = mpdm_ival(mpdm_aget(v, 1));
204 papers[n] = ((m & 0x000000ff) << 16)|
205 ((m & 0x0000ff00)) |
206 ((m & 0x00ff0000) >> 16);
208 /* flags */
209 v = mpdm_hget_s(d, L"flags");
211 underlines[n] = mpdm_seek_s(v, L"underline", 1) != -1 ? 1 : 0;
213 if (mpdm_seek_s(v, L"reverse", 1) != -1) {
214 COLORREF t;
216 t = inks[n];
217 inks[n] = papers[n];
218 papers[n] = t;
222 /* create the background brush */
223 bgbrush = CreateSolidBrush(papers[normal_attr]);
227 static void build_menu(void)
228 /* builds the menu */
230 int n;
231 mpdm_t m;
232 int win32_menu_id = 1000;
234 /* gets the current menu */
235 if ((m = mpdm_hget_s(mp, L"menu")) == NULL)
236 return;
238 if (menu != NULL)
239 DestroyMenu(menu);
241 menu = CreateMenu();
243 for (n = 0; n < mpdm_size(m); n++) {
244 char * ptr;
245 mpdm_t mi, v, l;
246 int i;
247 HMENU submenu = CreatePopupMenu();
249 /* get the label and the items */
250 mi = mpdm_aget(m, n);
251 v = mpdm_gettext(mpdm_aget(mi, 0));
252 l = mpdm_aget(mi, 1);
254 /* create the submenus */
255 for (i = 0; i < mpdm_size(l); i++) {
256 /* get the action */
257 mpdm_t v = mpdm_aget(l, i);
259 /* if the action is a separator... */
260 if (*((wchar_t *)v->data) == L'-')
261 AppendMenu(submenu, MF_SEPARATOR, 0, NULL);
262 else {
263 MENUITEMINFO mi;
264 mpdm_t d = mp_menu_label(v);
266 /* set the string */
267 ptr = mpdm_wcstombs(mpdm_string(d), NULL);
268 AppendMenu(submenu, MF_STRING, win32_menu_id, ptr);
269 free(ptr);
271 /* store the action inside the menu */
272 memset(&mi, '\0', sizeof(mi));
273 mi.cbSize = sizeof(mi);
274 mi.fMask = MIIM_DATA;
275 mi.dwItemData = (unsigned long)v;
277 SetMenuItemInfo(submenu, win32_menu_id, FALSE, &mi);
279 win32_menu_id++;
283 /* now store the popup inside the menu */
284 ptr = mpdm_wcstombs(mpdm_string(v), NULL);
285 AppendMenu(menu, MF_STRING|MF_POPUP, (UINT)submenu, ptr);
286 free(ptr);
289 SetMenu(hwnd, menu);
293 static void draw_filetabs(void)
294 /* draws the filetabs */
296 static mpdm_t last = NULL;
297 mpdm_t names;
298 int n;
300 if (hwtabs == NULL)
301 return;
303 names = mp_get_doc_names();
305 /* is the list different from the previous one? */
306 if (mpdm_cmp(names, last) != 0) {
307 TabCtrl_DeleteAllItems(hwtabs);
309 for (n = 0; n < mpdm_size(names); n++) {
310 TCITEM ti;
311 char * ptr;
312 mpdm_t v = mpdm_aget(names, n);
314 /* convert to mbs */
315 ptr = mpdm_wcstombs(v->data, NULL);
317 ti.mask = TCIF_TEXT;
318 ti.pszText = ptr;
320 /* create it */
321 TabCtrl_InsertItem(hwtabs, n, &ti);
323 free(ptr);
326 /* store for the next time */
327 mpdm_unref(last); last = mpdm_ref(names);
330 /* set the active one */
331 TabCtrl_SetCurSel(hwtabs, mpdm_ival(mpdm_hget_s(mp, L"active_i")));
335 static void draw_scrollbar(void)
336 /* updates the scrollbar */
338 mpdm_t d;
339 mpdm_t v;
340 int pos, size, max;
341 SCROLLINFO si;
343 /* gets the active document */
344 if ((d = mp_active()) == NULL)
345 return;
347 /* get the coordinates */
348 v = mpdm_hget_s(d, L"txt");
349 pos = mpdm_ival(mpdm_hget_s(v, L"vy"));
350 max = mpdm_size(mpdm_hget_s(v, L"lines"));
352 v = mpdm_hget_s(mp, L"window");
353 size = mpdm_ival(mpdm_hget_s(v, L"ty"));
355 si.cbSize = sizeof(si);
356 si.fMask = SIF_ALL;
357 si.nMin = 0;
358 si.nMax = max;
359 si.nPage = size;
360 si.nPos = pos;
362 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
366 void draw_status(void)
367 /* draws the status line */
369 mpdm_t t;
371 if (hwstatus != NULL && (t = mp_build_status_line()) != NULL) {
372 t = MPDM_2MBS(t->data);
374 if (t->data != NULL)
375 SetWindowText(hwstatus, t->data);
380 static void win32_draw(HWND hwnd, mpdm_t doc)
381 /* win32 document draw function */
383 HDC hdc;
384 PAINTSTRUCT ps;
385 RECT rect;
386 RECT r2;
387 mpdm_t d = NULL;
388 int n, m;
390 /* start painting */
391 hdc = BeginPaint(hwnd, &ps);
393 /* no font? construct it */
394 if (font_normal == NULL) {
395 build_fonts(hdc);
396 build_colors();
399 /* no document? end */
400 if ((d = mp_draw(doc, 0)) == NULL) {
401 EndPaint(hwnd, &ps);
402 return;
405 /* select defaults to start painting */
406 SelectObject(hdc, font_normal);
408 GetClientRect(hwnd, &rect);
409 r2 = rect;
411 r2.top += tab_height;
412 r2.bottom = r2.top + font_height;
414 for (n = 0; n < mpdm_size(d); n++) {
415 mpdm_t l = mpdm_aget(d, n);
417 r2.left = rect.left;
419 for (m = 0; m < mpdm_size(l); m++) {
420 int attr;
421 mpdm_t s;
423 /* get the attribute and the string */
424 attr = mpdm_ival(mpdm_aget(l, m++));
425 s = mpdm_aget(l, m);
427 SetTextColor(hdc, inks[attr]);
428 SetBkColor(hdc, papers[attr]);
430 SelectObject(hdc, underlines[attr] ?
431 font_underline : font_normal);
433 TextOutW(hdc, r2.left, r2.top, s->data, mpdm_size(s));
434 r2.left += mpdm_size(s) * font_width;
437 /* fills the rest of the line */
438 FillRect(hdc, &r2, bgbrush);
440 r2.top += font_height;
441 r2.bottom += font_height;
444 EndPaint(hwnd, &ps);
446 draw_filetabs();
447 draw_scrollbar();
448 draw_status();
452 static void redraw(void)
454 InvalidateRect(hwnd, NULL, TRUE);
458 static void win32_vkey(int c)
459 /* win32 virtual key processing */
461 wchar_t * ptr = NULL;
462 static int maxed = 0;
464 /* set mp.shift_pressed */
465 if (GetKeyState(VK_SHIFT) & 0x8000)
466 mpdm_hset_s(mp, L"shift_pressed", MPDM_I(1));
468 if (GetKeyState(VK_CONTROL) & 0x8000 ||
469 GetKeyState(VK_MENU) & 0x8000) {
470 switch (c) {
471 case VK_UP: ptr = L"ctrl-cursor-up"; break;
472 case VK_DOWN: ptr = L"ctrl-cursor-down"; break;
473 case VK_LEFT: ptr = L"ctrl-cursor-left"; break;
474 case VK_RIGHT: ptr = L"ctrl-cursor-right"; break;
475 case VK_PRIOR: ptr = L"ctrl-page-up"; break;
476 case VK_NEXT: ptr = L"ctrl-page-down"; break;
477 case VK_HOME: ptr = L"ctrl-home"; break;
478 case VK_END: ptr = L"ctrl-end"; break;
479 case VK_SPACE: ptr = L"ctrl-space"; break;
480 case VK_DIVIDE: ptr = L"ctrl-kp-divide"; break;
481 case VK_MULTIPLY: ptr = L"ctrl-kp-multiply"; break;
482 case VK_SUBTRACT: ptr = L"ctrl-kp-minus"; break;
483 case VK_ADD: ptr = L"ctrl-kp-plus"; break;
484 case VK_RETURN: ptr = L"ctrl-enter"; break;
485 case VK_F1: ptr = L"ctrl-f1"; break;
486 case VK_F2: ptr = L"ctrl-f2"; break;
487 case VK_F3: ptr = L"ctrl-f3"; break;
488 case VK_F4: ptr = L"ctrl-f4"; break;
489 case VK_F5: ptr = L"ctrl-f5"; break;
490 case VK_F6: ptr = L"ctrl-f6"; break;
491 case VK_F7: ptr = L"ctrl-f7"; break;
492 case VK_F8: ptr = L"ctrl-f8"; break;
493 case VK_F9: ptr = L"ctrl-f9"; break;
494 case VK_F10: ptr = L"ctrl-f10"; break;
495 case VK_F11: ptr = L"ctrl-f11"; break;
496 case VK_F12:
497 SendMessage(hwnd, WM_SYSCOMMAND,
498 maxed ? SC_RESTORE : SC_MAXIMIZE, 0);
500 maxed ^= 1;
502 break;
505 else {
506 switch (c) {
507 case VK_UP: ptr = L"cursor-up"; break;
508 case VK_DOWN: ptr = L"cursor-down"; break;
509 case VK_LEFT: ptr = L"cursor-left"; break;
510 case VK_RIGHT: ptr = L"cursor-right"; break;
511 case VK_PRIOR: ptr = L"page-up"; break;
512 case VK_NEXT: ptr = L"page-down"; break;
513 case VK_HOME: ptr = L"home"; break;
514 case VK_END: ptr = L"end"; break;
515 case VK_TAB: ptr = L"tab"; break;
516 case VK_RETURN: ptr = L"enter"; break;
517 case VK_BACK: ptr = L"backspace"; break;
518 case VK_DELETE: ptr = L"delete"; break;
519 case VK_INSERT: ptr = L"insert"; break;
520 case VK_DIVIDE: ptr = L"kp-divide"; break;
521 case VK_MULTIPLY: ptr = L"kp-multiply"; break;
522 case VK_SUBTRACT: ptr = L"kp-minus"; break;
523 case VK_ADD: ptr = L"kp-plus"; break;
524 case VK_F1: ptr = L"f1"; break;
525 case VK_F2: ptr = L"f2"; break;
526 case VK_F3: ptr = L"f3"; break;
527 case VK_F4: ptr = L"f4"; break;
528 case VK_F5: ptr = L"f5"; break;
529 case VK_F6: ptr = L"f6"; break;
530 case VK_F7: ptr = L"f7"; break;
531 case VK_F8: ptr = L"f8"; break;
532 case VK_F9: ptr = L"f9"; break;
533 case VK_F10: ptr = L"f10"; break;
534 case VK_F11: ptr = L"f11"; break;
535 case VK_F12: ptr = L"f12"; break;
539 if (ptr != NULL) {
540 mp_process_event(MPDM_S(ptr));
541 is_wm_keydown = 1;
542 mp_active();
544 if (mp_keypress_throttle(1))
545 redraw();
550 #define ctrl(c) ((c) & 31)
552 static void win32_akey(int k)
553 /* win32 alphanumeric key processing */
555 wchar_t c[2];
556 wchar_t * ptr = NULL;
558 if (is_wm_keydown)
559 return;
561 /* set mp.shift_pressed */
562 if (GetKeyState(VK_SHIFT) & 0x8000)
563 mpdm_hset_s(mp, L"shift_pressed", MPDM_I(1));
565 switch (k) {
566 case ctrl(' '): ptr = L"ctrl-space"; break;
567 case ctrl('a'): ptr = L"ctrl-a"; break;
568 case ctrl('b'): ptr = L"ctrl-b"; break;
569 case ctrl('c'): ptr = L"ctrl-c"; break;
570 case ctrl('d'): ptr = L"ctrl-d"; break;
571 case ctrl('e'): ptr = L"ctrl-e"; break;
572 case ctrl('f'): ptr = L"ctrl-f"; break;
573 case ctrl('g'): ptr = L"ctrl-g"; break;
574 case ctrl('h'): ptr = L"ctrl-h"; break;
575 case ctrl('i'): ptr = L"ctrl-i"; break;
576 case ctrl('j'): ptr = L"ctrl-j"; break;
577 case ctrl('k'): ptr = L"ctrl-k"; break;
578 case ctrl('l'): ptr = L"ctrl-l"; break;
579 case ctrl('m'): ptr = L"ctrl-m"; break;
580 case ctrl('n'): ptr = L"ctrl-n"; break;
581 case ctrl('o'): ptr = L"ctrl-o"; break;
582 case ctrl('p'): ptr = L"ctrl-p"; break;
583 case ctrl('q'): ptr = L"ctrl-q"; break;
584 case ctrl('r'): ptr = L"ctrl-r"; break;
585 case ctrl('s'): ptr = L"ctrl-s"; break;
586 case ctrl('t'): ptr = L"ctrl-t"; break;
587 case ctrl('u'): ptr = L"ctrl-u"; break;
588 case ctrl('v'): ptr = L"ctrl-v"; break;
589 case ctrl('w'): ptr = L"ctrl-w"; break;
590 case ctrl('x'): ptr = L"ctrl-x"; break;
591 case ctrl('y'): ptr = L"ctrl-y"; break;
592 case ctrl('z'): ptr = L"ctrl-z"; break;
593 case ' ': ptr = L"space"; break;
594 case 27: ptr = L"escape"; break;
596 default:
597 /* this is probably very bad */
598 c[0] = (wchar_t) k;
599 c[1] = L'\0';
600 ptr = c;
602 break;
605 if (ptr != NULL) {
606 mp_process_event(MPDM_S(ptr));
607 mp_active();
608 redraw();
613 static void win32_vscroll(UINT wparam)
614 /* scrollbar messages handler */
616 wchar_t * ptr = NULL;
617 mpdm_t txt;
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 /* set both y and vy */
627 txt = mpdm_hget_s(mp_active(), L"txt");
628 mp_set_y(mp_active(), HIWORD(wparam));
629 mpdm_hset_s(txt, L"vy", MPDM_I(HIWORD(wparam)));
630 redraw();
631 break;
634 if (ptr != NULL) {
635 mp_process_event(MPDM_S(ptr));
636 redraw();
641 static void action_by_menu(int item)
642 /* execute an action triggered by the menu */
644 MENUITEMINFO mi;
646 memset(&mi, '\0', sizeof(mi));
647 mi.cbSize = sizeof(mi);
648 mi.fMask = MIIM_DATA;
650 if (GetMenuItemInfo(menu, item, FALSE, &mi)) {
651 if (mi.dwItemData != 0) {
652 mp_process_action((mpdm_t)mi.dwItemData);
653 mp_active();
659 #ifndef WM_MOUSEWHEEL
660 #define WM_MOUSEWHEEL 0x020A
661 #endif
663 long STDCALL WndProc(HWND hwnd, UINT msg, UINT wparam, LONG lparam)
664 /* main window Proc */
666 int x, y;
667 LPNMHDR p;
668 wchar_t * ptr = NULL;
670 switch (msg) {
671 case WM_CREATE:
673 is_wm_keydown = 0;
674 DragAcceptFiles(hwnd, TRUE);
675 return 0;
677 /* case WM_DROPFILES:
679 (void) load_dropped_files ((HANDLE) wparam, hwnd);
680 return(0);
682 case WM_KEYUP:
684 is_wm_keydown = 0;
686 if (mp_keypress_throttle(0))
687 redraw();
689 return 0;
691 case WM_KEYDOWN:
693 win32_vkey(wparam);
694 return 0;
696 case WM_CHAR:
698 win32_akey(wparam);
699 return 0;
701 case WM_VSCROLL:
703 win32_vscroll(wparam);
704 return 0;
706 case WM_PAINT:
708 if (mpdm_size(mpdm_hget_s(mp, L"docs")))
709 win32_draw(hwnd, mp_active());
711 return 0;
713 case WM_SIZE:
715 if (!IsIconic(hwnd)) {
716 update_window_size();
718 MoveWindow(hwtabs, 0, 0, LOWORD(lparam), tab_height, FALSE);
720 MoveWindow(hwstatus, 0, HIWORD(lparam) - status_height,
721 LOWORD(lparam), status_height, FALSE);
723 redraw();
726 return 0;
728 case WM_LBUTTONDOWN:
730 mouse_down = 1;
731 /* fallthrough */
733 case WM_RBUTTONDOWN:
734 case WM_MBUTTONDOWN:
736 x = (LOWORD(lparam)) / font_width;
737 y = (HIWORD(lparam) - tab_height) / font_height;
739 mpdm_hset_s(mp, L"mouse_x", MPDM_I(x));
740 mpdm_hset_s(mp, L"mouse_y", MPDM_I(y));
742 switch (msg) {
743 case WM_LBUTTONDOWN: ptr = L"mouse-left-button"; break;
744 case WM_RBUTTONDOWN: ptr = L"mouse-right-button"; break;
745 case WM_MBUTTONDOWN: ptr = L"mouse-middle-button"; break;
748 if (ptr != NULL) {
749 mp_process_event(MPDM_S(ptr));
750 redraw();
753 return 0;
755 case WM_LBUTTONUP:
757 mouse_down = 0;
758 return 0;
760 case WM_MOUSEMOVE:
762 if (mouse_down) {
763 x = (LOWORD(lparam)) / font_width;
764 y = (HIWORD(lparam) - tab_height) / font_height;
766 mpdm_hset_s(mp, L"mouse_to_x", MPDM_I(x));
767 mpdm_hset_s(mp, L"mouse_to_y", MPDM_I(y));
769 mp_process_event(MPDM_LS(L"mouse-drag"));
770 redraw();
773 return 0;
775 case WM_MOUSEWHEEL:
777 if ((int) wparam > 0)
778 ptr = L"mouse-wheel-up";
779 else
780 ptr = L"mouse-wheel-down";
782 if (ptr != NULL) {
783 mp_process_event(MPDM_S(ptr));
784 redraw();
787 return 0;
789 case WM_COMMAND:
791 action_by_menu(LOWORD(wparam));
792 redraw();
794 return 0;
796 case WM_CLOSE:
798 if (!mp_exit_requested)
799 mp_process_event(MPDM_LS(L"close-window"));
801 if (mp_exit_requested)
802 DestroyWindow(hwnd);
804 return 0;
806 case WM_DESTROY:
807 PostQuitMessage(0);
808 return 0;
810 case WM_NOTIFY:
811 p = (LPNMHDR)lparam;
813 if (p->code == TCN_SELCHANGE) {
814 /* tab selected by clicking on it */
815 int n = TabCtrl_GetCurSel(hwtabs);
817 /* set mp.active_i to this */
818 mpdm_hset_s(mp, L"active_i", MPDM_I(n));
820 redraw();
823 return 0;
825 case WM_TIMER:
826 mpdm_exec(timer_func, NULL);
828 return 0;
831 if (mp_exit_requested)
832 PostMessage(hwnd, WM_CLOSE, 0, 0);
834 return DefWindowProc(hwnd, msg, wparam, lparam);
838 static mpdm_t win32_drv_clip_to_sys(mpdm_t a)
839 /* driver-dependent mp to system clipboard */
841 HGLOBAL hclp;
842 mpdm_t d;
843 char * ptr;
844 char * clpptr;
845 int s;
847 /* convert the clipboard to DOS text */
848 d = mpdm_hget_s(mp, L"clipboard");
850 if (mpdm_size(d) == 0)
851 return NULL;
853 d = mpdm_join(MPDM_LS(L"\r\n"), d);
854 ptr = mpdm_wcstombs(d->data, &s);
856 /* allocates a handle and copies */
857 hclp = GlobalAlloc(GHND, s + 1);
858 clpptr = (char *)GlobalLock(hclp);
859 memcpy(clpptr, ptr, s);
860 clpptr[s] = '\0';
861 GlobalUnlock(hclp);
863 free(ptr);
865 OpenClipboard(NULL);
866 EmptyClipboard();
867 SetClipboardData(CF_TEXT, hclp);
868 CloseClipboard();
870 return NULL;
874 static mpdm_t win32_drv_sys_to_clip(mpdm_t a)
875 /* driver-dependent system to mp clipboard */
877 HGLOBAL hclp;
878 char * ptr;
880 OpenClipboard(NULL);
881 hclp = GetClipboardData(CF_TEXT);
882 CloseClipboard();
884 if (hclp && (ptr = GlobalLock(hclp)) != NULL) {
885 mpdm_t d;
887 /* create a value and split */
888 d = MPDM_MBS(ptr);
889 d = mpdm_split(MPDM_LS(L"\r\n"), d);
891 /* and set as the clipboard */
892 mpdm_hset_s(mp, L"clipboard", d);
893 mpdm_hset_s(mp, L"clipboard_vertical", MPDM_I(0));
895 GlobalUnlock(hclp);
898 return NULL;
902 static mpdm_t win32_drv_main_loop(mpdm_t a)
904 MSG msg;
906 if (!mp_exit_requested) {
907 mp_active();
909 while (GetMessage(&msg, NULL, 0, 0)) {
910 TranslateMessage(&msg);
911 DispatchMessage(&msg);
915 return NULL;
919 static mpdm_t win32_drv_shutdown(mpdm_t a)
921 mpdm_t v;
923 SendMessage(hwnd, WM_CLOSE, 0, 0);
925 if ((v = mpdm_hget_s(mp, L"exit_message")) != NULL) {
926 char * ptr = mpdm_wcstombs(mpdm_string(v), NULL);
927 MessageBox(NULL, ptr, "mp " VERSION, MB_ICONWARNING|MB_OK);
928 free(ptr);
931 return NULL;
935 static mpdm_t win32_drv_alert(mpdm_t a)
936 /* alert driver function */
938 wchar_t * wptr;
939 char * ptr;
941 /* 1# arg: prompt */
942 wptr = mpdm_string(mpdm_aget(a, 0));
944 if ((ptr = mpdm_wcstombs(wptr, NULL)) != NULL) {
945 MessageBox(hwnd, ptr, "mp " VERSION, MB_ICONWARNING|MB_OK);
946 free(ptr);
949 return NULL;
953 static mpdm_t win32_drv_confirm(mpdm_t a)
954 /* confirm driver function */
956 wchar_t * wptr;
957 char * ptr;
958 int ret = 0;
960 /* 1# arg: prompt */
961 wptr = mpdm_string(mpdm_aget(a, 0));
963 if ((ptr = mpdm_wcstombs(wptr, NULL)) != NULL) {
964 ret = MessageBox(hwnd, ptr, "mp " VERSION, MB_ICONQUESTION|MB_YESNOCANCEL);
965 free(ptr);
967 if (ret == IDYES)
968 ret = 1;
969 else
970 if (ret == IDNO)
971 ret = 2;
972 else
973 ret = 0;
976 return MPDM_I(ret);
980 static LPWORD lpwAlign(LPWORD lpIn)
981 /* aligns a pointer to DWORD boundary (for dialog templates) */
983 ULONG ul;
985 ul = (ULONG)lpIn;
986 ul ++;
987 ul >>= 1;
988 ul <<= 1;
989 return (LPWORD)ul;
993 #define LABEL_ID 1000
994 #define CTRL_ID 2000
996 BOOL CALLBACK formDlgProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
997 /* mp.drv.form() dialog proc */
999 int n;
1000 HFONT hf;
1002 switch (msg) {
1003 case WM_INITDIALOG:
1005 SetWindowText(hwnd, "mp " VERSION);
1007 hf = GetStockObject(DEFAULT_GUI_FONT);
1009 /* fill controls with its initial data */
1010 for (n = 0; n < mpdm_size(form_args); n++) {
1011 mpdm_t w = mpdm_aget(form_args, n);
1012 wchar_t * type;
1013 mpdm_t t;
1014 int ctrl = CTRL_ID + n;
1015 wchar_t * wptr;
1016 char * ptr;
1018 if ((t = mpdm_hget_s(w, L"label")) != NULL) {
1019 if ((ptr = mpdm_wcstombs(mpdm_string(t), NULL)) != NULL) {
1020 SetDlgItemText(hwnd, LABEL_ID + n, ptr);
1021 free(ptr);
1023 SendDlgItemMessage(hwnd, LABEL_ID + n, WM_SETFONT,
1024 (WPARAM) hf, MAKELPARAM(FALSE, 0));
1028 SendDlgItemMessage(hwnd, ctrl, WM_SETFONT,
1029 (WPARAM) hf, MAKELPARAM(FALSE, 0));
1031 type = mpdm_string(mpdm_hget_s(w, L"type"));
1033 if (wcscmp(type, L"text") == 0) {
1034 if ((t = mpdm_hget_s(w, L"value")) != NULL &&
1035 (ptr = mpdm_wcstombs(mpdm_string(t), NULL)) != NULL) {
1036 SetDlgItemText(hwnd, ctrl, ptr);
1037 free(ptr);
1040 /* store the history into combo_items */
1041 if ((t = mpdm_hget_s(w, L"history")) != NULL) {
1042 t = mp_get_history(t);
1043 int i;
1045 for (i = 0; i < mpdm_size(t); i++) {
1046 mpdm_t v = mpdm_aget(t, i);
1048 if ((ptr = mpdm_wcstombs(v->data,
1049 NULL)) != NULL) {
1050 SendDlgItemMessage(hwnd,
1051 ctrl,
1052 CB_INSERTSTRING, 0,
1053 (LPARAM)ptr);
1054 free(ptr);
1059 else
1060 if (wcscmp(type, L"password") == 0) {
1061 SendDlgItemMessage(hwnd, ctrl,
1062 EM_SETPASSWORDCHAR, (WPARAM)'*', (LPARAM)0);
1064 else
1065 if (wcscmp(type, L"checkbox") == 0) {
1066 if ((t = mpdm_hget_s(w, L"value")) != NULL)
1067 SendDlgItemMessage(hwnd, ctrl,
1068 BM_SETCHECK, mpdm_ival(t) ?
1069 BST_CHECKED : BST_UNCHECKED,
1072 else
1073 if (wcscmp(type, L"list") == 0) {
1074 int i;
1076 t = mpdm_hget_s(w, L"list");
1078 /* fill the list */
1079 for (i = 0; i < mpdm_size(t); i++) {
1080 wptr = mpdm_string(mpdm_aget(t, i));
1081 if ((ptr = mpdm_wcstombs(wptr, NULL)) != NULL) {
1082 SendDlgItemMessage(hwnd, ctrl,
1083 LB_ADDSTRING, 0, (LPARAM) ptr);
1084 free(ptr);
1088 /* set position */
1089 SendDlgItemMessage(hwnd, ctrl, LB_SETCURSEL,
1090 mpdm_ival(mpdm_hget_s(w, L"value")), 0);
1094 /* FIXME: untranslated strings */
1096 SetDlgItemText(hwnd, IDOK, "OK");
1097 SendDlgItemMessage(hwnd, IDOK, WM_SETFONT,
1098 (WPARAM) hf, MAKELPARAM(FALSE, 0));
1100 SetDlgItemText(hwnd, IDCANCEL, "Cancel");
1101 SendDlgItemMessage(hwnd, IDCANCEL, WM_SETFONT,
1102 (WPARAM) hf, MAKELPARAM(FALSE, 0));
1104 return TRUE;
1106 case WM_COMMAND:
1108 if (LOWORD(wparam) == IDCANCEL) {
1109 EndDialog(hwnd, 0);
1110 return TRUE;
1113 if (LOWORD(wparam) != IDOK)
1114 break;
1116 /* fill all return values */
1117 for (n = 0; n < mpdm_size(form_args); n++) {
1118 mpdm_t w = mpdm_aget(form_args, n);
1119 wchar_t * type = mpdm_string(mpdm_hget_s(w, L"type"));
1120 int ctrl = CTRL_ID + n;
1122 if (wcscmp(type, L"text") == 0) {
1123 char tmp[2048];
1124 mpdm_t v;
1125 mpdm_t h;
1127 GetDlgItemText(hwnd, ctrl, tmp, sizeof(tmp) - 1);
1128 v = MPDM_MBS(tmp);
1130 mpdm_aset(form_values, v, n);
1132 /* if it has history, fill it */
1133 if ((h = mpdm_hget_s(w, L"history")) != NULL &&
1134 v != NULL && mpdm_cmp(v, MPDM_LS(L"")) != 0) {
1135 h = mp_get_history(h);
1137 if (mpdm_cmp(v, mpdm_aget(h, -1)) != 0)
1138 mpdm_push(h, v);
1141 if (wcscmp(type, L"password") == 0) {
1142 char tmp[2048];
1144 GetDlgItemText(hwnd, ctrl, tmp, sizeof(tmp) - 1);
1145 mpdm_aset(form_values, MPDM_MBS(tmp), n);
1147 else
1148 if (wcscmp(type, L"checkbox") == 0) {
1149 mpdm_aset(form_values,
1150 MPDM_I(SendDlgItemMessage(hwnd, ctrl,
1151 BM_GETCHECK, 0, 0)), n);
1153 else
1154 if (wcscmp(type, L"list") == 0) {
1155 mpdm_aset(form_values,
1156 MPDM_I(SendDlgItemMessage(hwnd, ctrl,
1157 LB_GETCURSEL, 0, 0)), n);
1161 EndDialog(hwnd, 1);
1162 return TRUE;
1165 return FALSE;
1169 static void build_form_data(mpdm_t widget_list)
1170 /* builds the necessary information for a list of widgets */
1172 mpdm_unref(form_args);
1173 form_args = mpdm_ref(widget_list);
1175 mpdm_unref(form_values);
1176 form_values = widget_list == NULL ? NULL :
1177 mpdm_ref(MPDM_A(mpdm_size(form_args)));
1181 LPWORD static build_control(LPWORD lpw, int x, int y,
1182 int cx, int cy, int id, int class, int style)
1183 /* fills a control structure in a hand-made dialog template */
1185 LPDLGITEMTEMPLATE lpdit;
1187 lpw = lpwAlign(lpw);
1188 lpdit = (LPDLGITEMTEMPLATE)lpw;
1189 lpdit->x = x; lpdit->y = y;
1190 lpdit->cx = cx; lpdit->cy = cy;
1191 lpdit->id = id;
1192 lpdit->style = style;
1194 lpw = (LPWORD)(lpdit + 1);
1195 *lpw++ = 0xFFFF;
1196 *lpw++ = class;
1198 /* no text (will be set on dialog setup) */
1199 *lpw++ = 0;
1200 *lpw++ = 0;
1202 /* Align creation data on DWORD boundary */
1203 lpw = lpwAlign(lpw);
1204 /* No creation data */
1205 *lpw++ = 0;
1207 return lpw;
1211 static mpdm_t win32_drv_form(mpdm_t a)
1212 /* mp.drv.form() function */
1214 HGLOBAL hgbl;
1215 LPDLGTEMPLATE lpdt;
1216 LPWORD lpw;
1217 int n, p;
1218 int il = 10;
1219 int lbl = 0;
1221 /* first argument: list of widgets */
1222 build_form_data(mpdm_aget(a, 0));
1224 /* On-the-fly dialog template creation */
1225 /* Note: all this crap is taken from MSDN, no less */
1227 /* magic size; looking for problems */
1228 hgbl = GlobalAlloc(GMEM_ZEROINIT, 4096);
1229 lpdt = (LPDLGTEMPLATE)GlobalLock(hgbl);
1231 lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU | DS_MODALFRAME | WS_CAPTION;
1232 lpdt->cdit = (2 * mpdm_size(form_args)) + 2;
1233 lpdt->x = 20; lpdt->y = 20;
1234 lpdt->cx = 260;
1236 lpw = (LPWORD)(lpdt + 1);
1237 *lpw++ = 0; /* No menu */
1238 *lpw++ = 0; /* Predefined dialog box class (by default) */
1239 *lpw++ = 0; /* No title */
1241 /* first pass: calculate maximum size of labels */
1242 for (n = 0; n < mpdm_size(form_args); n++) {
1243 mpdm_t w = mpdm_aget(form_args, n);
1244 int l = mpdm_size(mpdm_hget_s(w, L"label"));
1246 if (lbl < l)
1247 lbl = l;
1250 /* second pass: create the dialog controls */
1251 for (n = p = 0; n < mpdm_size(form_args); n++) {
1252 mpdm_t w = mpdm_aget(form_args, n);
1253 wchar_t * type;
1254 int class;
1255 int style;
1256 int inc = 1;
1257 int sz = 1;
1259 /* label control */
1260 lpw = build_control(lpw, 0, 5 + p * il,
1261 lbl * 3, 20, LABEL_ID + n, 0x0082,
1262 WS_CHILD | WS_VISIBLE | SS_RIGHT);
1264 type = mpdm_string(mpdm_hget_s(w, L"type"));
1266 if (wcscmp(type, L"text") == 0) {
1267 class = 0x0085;
1268 style = WS_CHILD | WS_VISIBLE | WS_TABSTOP |
1269 CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL;
1271 /* size */
1272 sz = 5;
1274 else
1275 if (wcscmp(type, L"password") == 0) {
1276 class = 0x0081;
1277 style = WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP;
1279 else
1280 if (wcscmp(type, L"checkbox") == 0) {
1281 class = 0x0080;
1282 style = WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX | WS_TABSTOP;
1284 else
1285 if (wcscmp(type, L"list") == 0) {
1286 class = 0x0083;
1287 style = WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER |
1288 LBS_NOINTEGRALHEIGHT | WS_VSCROLL |
1289 LBS_NOTIFY | LBS_USETABSTOPS;
1291 /* height */
1292 inc = 5;
1295 /* the control */
1296 lpw = build_control(lpw, 10 + lbl * 3, 5 + p * il,
1297 245 - lbl * 3, inc * il * sz, CTRL_ID + n,
1298 class, style);
1300 /* next position */
1301 p += inc;
1304 /* set total height */
1305 lpdt->cy = 30 + p * il;
1307 /* OK */
1308 lpw = build_control(lpw, 170, 10 + p * il, 40, 15, IDOK,
1309 0x0080, WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON | WS_TABSTOP);
1311 /* Cancel */
1312 lpw = build_control(lpw, 215, 10 + p * il, 40, 15, IDCANCEL,
1313 0x0080, WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP);
1315 GlobalUnlock(hgbl);
1316 n = DialogBoxIndirect(hinst, (LPDLGTEMPLATE)hgbl,
1317 hwnd, (DLGPROC)formDlgProc);
1319 GlobalFree(hgbl);
1321 return n ? form_values : NULL;
1325 static mpdm_t open_or_save(int o, mpdm_t a)
1326 /* manages an open or save file dialog */
1328 OPENFILENAME ofn;
1329 wchar_t * wptr;
1330 char * ptr;
1331 char buf[1024] = "";
1332 char buf2[1024];
1333 int r;
1335 /* 1# arg: prompt */
1336 wptr = mpdm_string(mpdm_aget(a, 0));
1337 ptr = mpdm_wcstombs(wptr, NULL);
1339 memset(&ofn, '\0', sizeof(OPENFILENAME));
1340 ofn.lStructSize = sizeof(OPENFILENAME);
1341 ofn.hwndOwner = hwnd;
1342 ofn.lpstrFilter = "*.*\0*.*\0";
1343 ofn.nFilterIndex = 1;
1344 ofn.lpstrFile = buf;
1345 ofn.nMaxFile = sizeof(buf);
1346 ofn.lpstrTitle = ptr;
1347 ofn.lpstrDefExt = "";
1349 GetCurrentDirectory(sizeof(buf2), buf2);
1350 ofn.lpstrInitialDir = buf2;
1352 /* ofn.lpstrDefExt=(def==NULL ? "" : def);*/
1354 if (o) {
1355 ofn.Flags = OFN_PATHMUSTEXIST|OFN_HIDEREADONLY|
1356 OFN_NOCHANGEDIR|OFN_FILEMUSTEXIST;
1358 r = GetOpenFileName(&ofn);
1360 else {
1361 ofn.Flags = OFN_HIDEREADONLY;
1363 r = GetSaveFileName(&ofn);
1366 free(ptr);
1368 if (r)
1369 return MPDM_MBS(buf);
1371 return NULL;
1375 static mpdm_t win32_drv_openfile(mpdm_t a)
1376 /* openfile driver function */
1378 return open_or_save(1, a);
1382 static mpdm_t win32_drv_savefile(mpdm_t a)
1383 /* savefile driver function */
1385 return open_or_save(0, a);
1389 static mpdm_t win32_drv_update_ui(mpdm_t a)
1391 build_fonts(GetDC(hwnd));
1392 build_colors();
1393 build_menu();
1395 return NULL;
1399 static mpdm_t win32_drv_timer(mpdm_t a)
1401 int msecs = mpdm_ival(mpdm_aget(a, 0));
1402 mpdm_t func = mpdm_aget(a, 1);
1403 mpdm_t r;
1405 /* previously defined one? remove */
1406 if (timer_func != NULL)
1407 KillTimer(hwnd, 1);
1409 /* if msecs and func are set, program timer */
1410 if (msecs > 0 && func != NULL)
1411 SetTimer(hwnd, 1, msecs, NULL);
1413 r = mpdm_unref(timer_func);
1414 timer_func = mpdm_ref(func);
1416 return r;
1420 static mpdm_t win32_drv_busy(mpdm_t a)
1422 int onoff = mpdm_ival(mpdm_aget(a, 0));
1424 SetCursor(LoadCursor(NULL, onoff ? IDC_WAIT : IDC_ARROW));
1426 return NULL;
1430 static void register_functions(void)
1432 mpdm_t drv;
1434 drv = mpdm_hget_s(mp, L"drv");
1435 mpdm_hset_s(drv, L"main_loop", MPDM_X(win32_drv_main_loop));
1436 mpdm_hset_s(drv, L"shutdown", MPDM_X(win32_drv_shutdown));
1438 mpdm_hset_s(drv, L"clip_to_sys", MPDM_X(win32_drv_clip_to_sys));
1439 mpdm_hset_s(drv, L"sys_to_clip", MPDM_X(win32_drv_sys_to_clip));
1440 mpdm_hset_s(drv, L"update_ui", MPDM_X(win32_drv_update_ui));
1441 mpdm_hset_s(drv, L"timer", MPDM_X(win32_drv_timer));
1442 mpdm_hset_s(drv, L"busy", MPDM_X(win32_drv_busy));
1444 mpdm_hset_s(drv, L"alert", MPDM_X(win32_drv_alert));
1445 mpdm_hset_s(drv, L"confirm", MPDM_X(win32_drv_confirm));
1446 mpdm_hset_s(drv, L"openfile", MPDM_X(win32_drv_openfile));
1447 mpdm_hset_s(drv, L"savefile", MPDM_X(win32_drv_savefile));
1448 mpdm_hset_s(drv, L"form", MPDM_X(win32_drv_form));
1452 static mpdm_t win32_drv_startup(mpdm_t a)
1454 WNDCLASS wc;
1455 RECT r;
1456 mpdm_t v;
1458 register_functions();
1460 InitCommonControls();
1462 /* register the window */
1463 wc.style = CS_HREDRAW|CS_VREDRAW;
1464 wc.lpfnWndProc = WndProc;
1465 wc.cbClsExtra = 0;
1466 wc.cbWndExtra = 0;
1467 wc.hInstance = hinst;
1468 wc.hIcon = LoadIcon(hinst,"MP_ICON");
1469 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
1470 wc.hbrBackground = NULL;
1471 wc.lpszMenuName = NULL;
1472 wc.lpszClassName = "minimumprofit5.x";
1474 RegisterClass(&wc);
1476 /* create the window */
1477 hwnd = CreateWindow("minimumprofit5.x", "mp " VERSION,
1478 WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_VSCROLL,
1479 CW_USEDEFAULT, CW_USEDEFAULT,
1480 CW_USEDEFAULT, CW_USEDEFAULT,
1481 NULL, NULL, hinst, NULL);
1483 ShowWindow(hwnd, SW_SHOW);
1484 UpdateWindow(hwnd);
1486 GetClientRect(hwnd, &r);
1488 hwtabs = CreateWindow(WC_TABCONTROL, "tab",
1489 WS_CHILD | TCS_TABS | TCS_SINGLELINE | TCS_FOCUSNEVER,
1490 0, 0, r.right - r.left, tab_height, hwnd, NULL, hinst, NULL);
1492 SendMessage(hwtabs, WM_SETFONT,
1493 (WPARAM) GetStockObject(DEFAULT_GUI_FONT), 0);
1495 ShowWindow(hwtabs, SW_SHOW);
1496 UpdateWindow(hwtabs);
1498 hwstatus = CreateWindow(WC_STATIC, "status",
1499 WS_CHILD,
1500 0, r.bottom - r.top - status_height,
1501 r.right - r.left, status_height, hwnd, NULL, hinst, NULL);
1503 win32_drv_update_ui(NULL);
1505 SendMessage(hwstatus, WM_SETFONT,
1506 (WPARAM) GetStockObject(DEFAULT_GUI_FONT), 0);
1508 ShowWindow(hwstatus, SW_SHOW);
1509 UpdateWindow(hwstatus);
1511 if ((v = mpdm_hget_s(mp, L"config")) != NULL &&
1512 mpdm_ival(mpdm_hget_s(v, L"maximize")) > 0)
1513 SendMessage(hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
1515 return NULL;
1519 int win32_drv_detect(int * argc, char *** argv)
1521 mpdm_t drv;
1523 drv = mpdm_hget_s(mp, L"drv");
1524 mpdm_hset_s(drv, L"id", MPDM_LS(L"win32"));
1525 mpdm_hset_s(drv, L"startup", MPDM_X(win32_drv_startup));
1527 return 1;
1530 #endif /* CONFOPT_WIN32 */