All functions with a doc as first argument returns it in mp_search.mpsl (except for...
[mp-5.x.git] / mpv_win32.c
blob8b174a557f09b4dff9468860e2f3df41ad59d607
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;
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 /* set mp.shift_pressed */
559 if (GetKeyState(VK_SHIFT) & 0x8000)
560 mpdm_hset_s(mp, L"shift_pressed", MPDM_I(1));
562 switch (k) {
563 case ctrl(' '): ptr = L"ctrl-space"; break;
564 case ctrl('a'): ptr = L"ctrl-a"; break;
565 case ctrl('b'): ptr = L"ctrl-b"; break;
566 case ctrl('c'): ptr = L"ctrl-c"; break;
567 case ctrl('d'): ptr = L"ctrl-d"; break;
568 case ctrl('e'): ptr = L"ctrl-e"; break;
569 case ctrl('f'): ptr = L"ctrl-f"; break;
570 case ctrl('g'): ptr = L"ctrl-g"; break;
571 case ctrl('h'): /* same as backspace */ break;
572 case ctrl('i'): ptr = L"ctrl-i"; break;
573 case ctrl('j'): ptr = L"ctrl-j"; break;
574 case ctrl('k'): ptr = L"ctrl-k"; break;
575 case ctrl('l'): ptr = L"ctrl-l"; break;
576 case ctrl('m'): /* same as ENTER */ break;
577 case ctrl('n'): ptr = L"ctrl-n"; break;
578 case ctrl('o'): ptr = L"ctrl-o"; break;
579 case ctrl('p'): ptr = L"ctrl-p"; break;
580 case ctrl('q'): ptr = L"ctrl-q"; break;
581 case ctrl('r'): ptr = L"ctrl-r"; break;
582 case ctrl('s'): ptr = L"ctrl-s"; break;
583 case ctrl('t'): ptr = L"ctrl-t"; break;
584 case ctrl('u'): ptr = L"ctrl-u"; break;
585 case ctrl('v'): ptr = L"ctrl-v"; break;
586 case ctrl('w'): ptr = L"ctrl-w"; break;
587 case ctrl('x'): ptr = L"ctrl-x"; break;
588 case ctrl('y'): ptr = L"ctrl-y"; break;
589 case ctrl('z'): ptr = L"ctrl-z"; break;
590 case ' ': ptr = L"space"; break;
591 case 27: ptr = L"escape"; break;
593 default:
594 /* this is probably very bad */
595 c[0] = (wchar_t) k;
596 c[1] = L'\0';
597 ptr = c;
599 break;
602 if (ptr != NULL) {
603 mp_process_event(MPDM_S(ptr));
604 mp_active();
605 redraw();
610 static void win32_vscroll(UINT wparam)
611 /* scrollbar messages handler */
613 wchar_t * ptr = NULL;
614 mpdm_t txt;
616 switch (LOWORD(wparam)) {
617 case SB_PAGEUP: ptr = L"page-up"; break;
618 case SB_PAGEDOWN: ptr = L"page-down"; break;
619 case SB_LINEUP: ptr = L"cursor-up"; break;
620 case SB_LINEDOWN: ptr = L"cursor-down"; break;
621 case SB_THUMBPOSITION:
622 case SB_THUMBTRACK:
623 /* set both y and vy */
624 txt = mpdm_hget_s(mp_active(), L"txt");
625 mp_set_y(mp_active(), HIWORD(wparam));
626 mpdm_hset_s(txt, L"vy", MPDM_I(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 static void dropped_files(HDROP hDrop)
657 /* fill the mp.dropped_files array with the dropped files */
659 mpdm_t a = MPDM_A(0);
660 char tmp[1024];
661 int n;
663 n = DragQueryFile(hDrop, 0xffffffff, NULL, sizeof(tmp) - 1);
665 while (--n >= 0) {
666 DragQueryFile(hDrop, n, tmp, sizeof(tmp) - 1);
667 mpdm_push(a, MPDM_MBS(tmp));
670 DragFinish(hDrop);
672 mpdm_hset_s(mp, L"dropped_files", a);
673 mp_process_event(MPDM_LS(L"dropped-files"));
674 redraw();
678 #ifndef WM_MOUSEWHEEL
679 #define WM_MOUSEWHEEL 0x020A
680 #endif
682 long STDCALL WndProc(HWND hwnd, UINT msg, UINT wparam, LONG lparam)
683 /* main window Proc */
685 int x, y;
686 LPNMHDR p;
687 wchar_t * ptr = NULL;
689 switch (msg) {
690 case WM_CREATE:
692 is_wm_keydown = 0;
693 DragAcceptFiles(hwnd, TRUE);
694 return 0;
696 case WM_DROPFILES:
698 dropped_files((HDROP) wparam);
699 return 0;
701 case WM_KEYUP:
703 is_wm_keydown = 0;
705 if (mp_keypress_throttle(0))
706 redraw();
708 return 0;
710 case WM_KEYDOWN:
712 win32_vkey(wparam);
713 return 0;
715 case WM_CHAR:
717 win32_akey(wparam);
718 return 0;
720 case WM_VSCROLL:
722 win32_vscroll(wparam);
723 return 0;
725 case WM_PAINT:
727 if (mpdm_size(mpdm_hget_s(mp, L"docs")))
728 win32_draw(hwnd, mp_active());
730 return 0;
732 case WM_SIZE:
734 if (!IsIconic(hwnd)) {
735 update_window_size();
737 MoveWindow(hwtabs, 0, 0, LOWORD(lparam), tab_height, FALSE);
739 MoveWindow(hwstatus, 0, HIWORD(lparam) - status_height,
740 LOWORD(lparam), status_height, FALSE);
742 redraw();
745 return 0;
747 case WM_LBUTTONDOWN:
749 mouse_down = 1;
750 /* fallthrough */
752 case WM_RBUTTONDOWN:
753 case WM_MBUTTONDOWN:
755 x = (LOWORD(lparam)) / font_width;
756 y = (HIWORD(lparam) - tab_height) / font_height;
758 mpdm_hset_s(mp, L"mouse_x", MPDM_I(x));
759 mpdm_hset_s(mp, L"mouse_y", MPDM_I(y));
761 switch (msg) {
762 case WM_LBUTTONDOWN: ptr = L"mouse-left-button"; break;
763 case WM_RBUTTONDOWN: ptr = L"mouse-right-button"; break;
764 case WM_MBUTTONDOWN: ptr = L"mouse-middle-button"; break;
767 if (ptr != NULL) {
768 mp_process_event(MPDM_S(ptr));
769 redraw();
772 return 0;
774 case WM_LBUTTONUP:
776 mouse_down = 0;
777 return 0;
779 case WM_MOUSEMOVE:
781 if (mouse_down) {
782 x = (LOWORD(lparam)) / font_width;
783 y = (HIWORD(lparam) - tab_height) / font_height;
785 mpdm_hset_s(mp, L"mouse_to_x", MPDM_I(x));
786 mpdm_hset_s(mp, L"mouse_to_y", MPDM_I(y));
788 mp_process_event(MPDM_LS(L"mouse-drag"));
789 redraw();
792 return 0;
794 case WM_MOUSEWHEEL:
796 if ((int) wparam > 0)
797 ptr = L"mouse-wheel-up";
798 else
799 ptr = L"mouse-wheel-down";
801 if (ptr != NULL) {
802 mp_process_event(MPDM_S(ptr));
803 redraw();
806 return 0;
808 case WM_COMMAND:
810 action_by_menu(LOWORD(wparam));
811 redraw();
813 return 0;
815 case WM_CLOSE:
817 if (!mp_exit_requested)
818 mp_process_event(MPDM_LS(L"close-window"));
820 if (mp_exit_requested)
821 DestroyWindow(hwnd);
823 return 0;
825 case WM_DESTROY:
826 PostQuitMessage(0);
827 return 0;
829 case WM_NOTIFY:
830 p = (LPNMHDR)lparam;
832 if (p->code == TCN_SELCHANGE) {
833 /* tab selected by clicking on it */
834 int n = TabCtrl_GetCurSel(hwtabs);
836 /* set mp.active_i to this */
837 mpdm_hset_s(mp, L"active_i", MPDM_I(n));
839 redraw();
842 return 0;
844 case WM_TIMER:
845 mpdm_exec(timer_func, NULL);
847 return 0;
850 if (mp_exit_requested)
851 PostMessage(hwnd, WM_CLOSE, 0, 0);
853 return DefWindowProcW(hwnd, msg, wparam, lparam);
857 static mpdm_t win32_drv_clip_to_sys(mpdm_t a)
858 /* driver-dependent mp to system clipboard */
860 HGLOBAL hclp;
861 mpdm_t d;
862 char * ptr;
863 char * clpptr;
864 int s;
866 /* convert the clipboard to DOS text */
867 d = mpdm_hget_s(mp, L"clipboard");
869 if (mpdm_size(d) == 0)
870 return NULL;
872 d = mpdm_join(MPDM_LS(L"\r\n"), d);
873 ptr = mpdm_wcstombs(d->data, &s);
875 /* allocates a handle and copies */
876 hclp = GlobalAlloc(GHND, s + 1);
877 clpptr = (char *)GlobalLock(hclp);
878 memcpy(clpptr, ptr, s);
879 clpptr[s] = '\0';
880 GlobalUnlock(hclp);
882 free(ptr);
884 OpenClipboard(NULL);
885 EmptyClipboard();
886 SetClipboardData(CF_TEXT, hclp);
887 CloseClipboard();
889 return NULL;
893 static mpdm_t win32_drv_sys_to_clip(mpdm_t a)
894 /* driver-dependent system to mp clipboard */
896 HGLOBAL hclp;
897 char * ptr;
899 OpenClipboard(NULL);
900 hclp = GetClipboardData(CF_TEXT);
901 CloseClipboard();
903 if (hclp && (ptr = GlobalLock(hclp)) != NULL) {
904 mpdm_t d;
906 /* create a value and split */
907 d = MPDM_MBS(ptr);
908 d = mpdm_split(MPDM_LS(L"\r\n"), d);
910 /* and set as the clipboard */
911 mpdm_hset_s(mp, L"clipboard", d);
912 mpdm_hset_s(mp, L"clipboard_vertical", MPDM_I(0));
914 GlobalUnlock(hclp);
917 return NULL;
921 static mpdm_t win32_drv_main_loop(mpdm_t a)
923 MSG msg;
925 if (!mp_exit_requested) {
926 mp_active();
928 while (GetMessage(&msg, NULL, 0, 0)) {
929 TranslateMessage(&msg);
930 DispatchMessage(&msg);
934 return NULL;
938 static mpdm_t win32_drv_shutdown(mpdm_t a)
940 mpdm_t v;
942 SendMessage(hwnd, WM_CLOSE, 0, 0);
944 if ((v = mpdm_hget_s(mp, L"exit_message")) != NULL) {
945 char * ptr = mpdm_wcstombs(mpdm_string(v), NULL);
946 MessageBox(NULL, ptr, "mp " VERSION, MB_ICONWARNING|MB_OK);
947 free(ptr);
950 return NULL;
954 static mpdm_t win32_drv_alert(mpdm_t a)
955 /* alert driver function */
957 wchar_t * wptr;
958 char * ptr;
960 /* 1# arg: prompt */
961 wptr = mpdm_string(mpdm_aget(a, 0));
963 if ((ptr = mpdm_wcstombs(wptr, NULL)) != NULL) {
964 MessageBox(hwnd, ptr, "mp " VERSION, MB_ICONWARNING|MB_OK);
965 free(ptr);
968 return NULL;
972 static mpdm_t win32_drv_confirm(mpdm_t a)
973 /* confirm driver function */
975 wchar_t * wptr;
976 char * ptr;
977 int ret = 0;
979 /* 1# arg: prompt */
980 wptr = mpdm_string(mpdm_aget(a, 0));
982 if ((ptr = mpdm_wcstombs(wptr, NULL)) != NULL) {
983 ret = MessageBox(hwnd, ptr, "mp " VERSION, MB_ICONQUESTION|MB_YESNOCANCEL);
984 free(ptr);
986 if (ret == IDYES)
987 ret = 1;
988 else
989 if (ret == IDNO)
990 ret = 2;
991 else
992 ret = 0;
995 return MPDM_I(ret);
999 static LPWORD lpwAlign(LPWORD lpIn)
1000 /* aligns a pointer to DWORD boundary (for dialog templates) */
1002 ULONG ul;
1004 ul = (ULONG)lpIn;
1005 ul ++;
1006 ul >>= 1;
1007 ul <<= 1;
1008 return (LPWORD)ul;
1012 #define LABEL_ID 1000
1013 #define CTRL_ID 2000
1015 BOOL CALLBACK formDlgProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
1016 /* mp.drv.form() dialog proc */
1018 int n;
1019 HFONT hf;
1021 switch (msg) {
1022 case WM_INITDIALOG:
1024 SetWindowText(hwnd, "mp " VERSION);
1026 hf = GetStockObject(DEFAULT_GUI_FONT);
1028 /* fill controls with its initial data */
1029 for (n = 0; n < mpdm_size(form_args); n++) {
1030 mpdm_t w = mpdm_aget(form_args, n);
1031 wchar_t * type;
1032 mpdm_t t;
1033 int ctrl = CTRL_ID + n;
1034 wchar_t * wptr;
1035 char * ptr;
1037 if ((t = mpdm_hget_s(w, L"label")) != NULL) {
1038 if ((ptr = mpdm_wcstombs(mpdm_string(t), NULL)) != NULL) {
1039 SetDlgItemText(hwnd, LABEL_ID + n, ptr);
1040 free(ptr);
1042 SendDlgItemMessage(hwnd, LABEL_ID + n, WM_SETFONT,
1043 (WPARAM) hf, MAKELPARAM(FALSE, 0));
1047 SendDlgItemMessage(hwnd, ctrl, WM_SETFONT,
1048 (WPARAM) hf, MAKELPARAM(FALSE, 0));
1050 type = mpdm_string(mpdm_hget_s(w, L"type"));
1052 if (wcscmp(type, L"text") == 0) {
1053 if ((t = mpdm_hget_s(w, L"value")) != NULL &&
1054 (ptr = mpdm_wcstombs(mpdm_string(t), NULL)) != NULL) {
1055 SetDlgItemText(hwnd, ctrl, ptr);
1056 free(ptr);
1059 /* store the history into combo_items */
1060 if ((t = mpdm_hget_s(w, L"history")) != NULL) {
1061 t = mp_get_history(t);
1062 int i;
1064 for (i = 0; i < mpdm_size(t); i++) {
1065 mpdm_t v = mpdm_aget(t, i);
1067 if ((ptr = mpdm_wcstombs(v->data,
1068 NULL)) != NULL) {
1069 SendDlgItemMessage(hwnd,
1070 ctrl,
1071 CB_INSERTSTRING, 0,
1072 (LPARAM)ptr);
1073 free(ptr);
1078 else
1079 if (wcscmp(type, L"password") == 0) {
1080 SendDlgItemMessage(hwnd, ctrl,
1081 EM_SETPASSWORDCHAR, (WPARAM)'*', (LPARAM)0);
1083 else
1084 if (wcscmp(type, L"checkbox") == 0) {
1085 if ((t = mpdm_hget_s(w, L"value")) != NULL)
1086 SendDlgItemMessage(hwnd, ctrl,
1087 BM_SETCHECK, mpdm_ival(t) ?
1088 BST_CHECKED : BST_UNCHECKED,
1091 else
1092 if (wcscmp(type, L"list") == 0) {
1093 int i;
1095 t = mpdm_hget_s(w, L"list");
1097 /* fill the list */
1098 for (i = 0; i < mpdm_size(t); i++) {
1099 wptr = mpdm_string(mpdm_aget(t, i));
1100 if ((ptr = mpdm_wcstombs(wptr, NULL)) != NULL) {
1101 SendDlgItemMessage(hwnd, ctrl,
1102 LB_ADDSTRING, 0, (LPARAM) ptr);
1103 free(ptr);
1107 /* set position */
1108 SendDlgItemMessage(hwnd, ctrl, LB_SETCURSEL,
1109 mpdm_ival(mpdm_hget_s(w, L"value")), 0);
1113 /* FIXME: untranslated strings */
1115 SetDlgItemText(hwnd, IDOK, "OK");
1116 SendDlgItemMessage(hwnd, IDOK, WM_SETFONT,
1117 (WPARAM) hf, MAKELPARAM(FALSE, 0));
1119 SetDlgItemText(hwnd, IDCANCEL, "Cancel");
1120 SendDlgItemMessage(hwnd, IDCANCEL, WM_SETFONT,
1121 (WPARAM) hf, MAKELPARAM(FALSE, 0));
1123 return TRUE;
1125 case WM_COMMAND:
1127 if (LOWORD(wparam) == IDCANCEL) {
1128 EndDialog(hwnd, 0);
1129 return TRUE;
1132 if (LOWORD(wparam) != IDOK)
1133 break;
1135 /* fill all return values */
1136 for (n = 0; n < mpdm_size(form_args); n++) {
1137 mpdm_t w = mpdm_aget(form_args, n);
1138 wchar_t * type = mpdm_string(mpdm_hget_s(w, L"type"));
1139 int ctrl = CTRL_ID + n;
1141 if (wcscmp(type, L"text") == 0) {
1142 char tmp[2048];
1143 mpdm_t v;
1144 mpdm_t h;
1146 GetDlgItemText(hwnd, ctrl, tmp, sizeof(tmp) - 1);
1147 v = MPDM_MBS(tmp);
1149 mpdm_aset(form_values, v, n);
1151 /* if it has history, fill it */
1152 if ((h = mpdm_hget_s(w, L"history")) != NULL &&
1153 v != NULL && mpdm_cmp(v, MPDM_LS(L"")) != 0) {
1154 h = mp_get_history(h);
1156 if (mpdm_cmp(v, mpdm_aget(h, -1)) != 0)
1157 mpdm_push(h, v);
1160 if (wcscmp(type, L"password") == 0) {
1161 char tmp[2048];
1163 GetDlgItemText(hwnd, ctrl, tmp, sizeof(tmp) - 1);
1164 mpdm_aset(form_values, MPDM_MBS(tmp), n);
1166 else
1167 if (wcscmp(type, L"checkbox") == 0) {
1168 mpdm_aset(form_values,
1169 MPDM_I(SendDlgItemMessage(hwnd, ctrl,
1170 BM_GETCHECK, 0, 0)), n);
1172 else
1173 if (wcscmp(type, L"list") == 0) {
1174 mpdm_aset(form_values,
1175 MPDM_I(SendDlgItemMessage(hwnd, ctrl,
1176 LB_GETCURSEL, 0, 0)), n);
1180 EndDialog(hwnd, 1);
1181 return TRUE;
1184 return FALSE;
1188 static void build_form_data(mpdm_t widget_list)
1189 /* builds the necessary information for a list of widgets */
1191 mpdm_unref(form_args);
1192 form_args = mpdm_ref(widget_list);
1194 mpdm_unref(form_values);
1195 form_values = widget_list == NULL ? NULL :
1196 mpdm_ref(MPDM_A(mpdm_size(form_args)));
1200 LPWORD static build_control(LPWORD lpw, int x, int y,
1201 int cx, int cy, int id, int class, int style)
1202 /* fills a control structure in a hand-made dialog template */
1204 LPDLGITEMTEMPLATE lpdit;
1206 lpw = lpwAlign(lpw);
1207 lpdit = (LPDLGITEMTEMPLATE)lpw;
1208 lpdit->x = x; lpdit->y = y;
1209 lpdit->cx = cx; lpdit->cy = cy;
1210 lpdit->id = id;
1211 lpdit->style = style;
1213 lpw = (LPWORD)(lpdit + 1);
1214 *lpw++ = 0xFFFF;
1215 *lpw++ = class;
1217 /* no text (will be set on dialog setup) */
1218 *lpw++ = 0;
1219 *lpw++ = 0;
1221 /* Align creation data on DWORD boundary */
1222 lpw = lpwAlign(lpw);
1223 /* No creation data */
1224 *lpw++ = 0;
1226 return lpw;
1230 static mpdm_t win32_drv_form(mpdm_t a)
1231 /* mp.drv.form() function */
1233 HGLOBAL hgbl;
1234 LPDLGTEMPLATE lpdt;
1235 LPWORD lpw;
1236 int n, p;
1237 int il = 10;
1238 int lbl = 0;
1240 /* first argument: list of widgets */
1241 build_form_data(mpdm_aget(a, 0));
1243 /* On-the-fly dialog template creation */
1244 /* Note: all this crap is taken from MSDN, no less */
1246 /* magic size; looking for problems */
1247 hgbl = GlobalAlloc(GMEM_ZEROINIT, 4096);
1248 lpdt = (LPDLGTEMPLATE)GlobalLock(hgbl);
1250 lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU | DS_MODALFRAME | WS_CAPTION;
1251 lpdt->cdit = (2 * mpdm_size(form_args)) + 2;
1252 lpdt->x = 20; lpdt->y = 20;
1253 lpdt->cx = 260;
1255 lpw = (LPWORD)(lpdt + 1);
1256 *lpw++ = 0; /* No menu */
1257 *lpw++ = 0; /* Predefined dialog box class (by default) */
1258 *lpw++ = 0; /* No title */
1260 /* first pass: calculate maximum size of labels */
1261 for (n = 0; n < mpdm_size(form_args); n++) {
1262 mpdm_t w = mpdm_aget(form_args, n);
1263 int l = mpdm_size(mpdm_hget_s(w, L"label"));
1265 if (lbl < l)
1266 lbl = l;
1269 /* second pass: create the dialog controls */
1270 for (n = p = 0; n < mpdm_size(form_args); n++) {
1271 mpdm_t w = mpdm_aget(form_args, n);
1272 wchar_t * type;
1273 int class;
1274 int style;
1275 int inc = 1;
1276 int sz = 1;
1278 /* label control */
1279 lpw = build_control(lpw, 0, 5 + p * il,
1280 lbl * 3, 20, LABEL_ID + n, 0x0082,
1281 WS_CHILD | WS_VISIBLE | SS_RIGHT);
1283 type = mpdm_string(mpdm_hget_s(w, L"type"));
1285 if (wcscmp(type, L"text") == 0) {
1286 class = 0x0085;
1287 style = WS_CHILD | WS_VISIBLE | WS_TABSTOP |
1288 CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL;
1290 /* size */
1291 sz = 5;
1293 else
1294 if (wcscmp(type, L"password") == 0) {
1295 class = 0x0081;
1296 style = WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP;
1298 else
1299 if (wcscmp(type, L"checkbox") == 0) {
1300 class = 0x0080;
1301 style = WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX | WS_TABSTOP;
1303 else
1304 if (wcscmp(type, L"list") == 0) {
1305 class = 0x0083;
1306 style = WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER |
1307 LBS_NOINTEGRALHEIGHT | WS_VSCROLL |
1308 LBS_NOTIFY | LBS_USETABSTOPS;
1310 /* height */
1311 inc = 5;
1314 /* the control */
1315 lpw = build_control(lpw, 10 + lbl * 3, 5 + p * il,
1316 245 - lbl * 3, inc * il * sz, CTRL_ID + n,
1317 class, style);
1319 /* next position */
1320 p += inc;
1323 /* set total height */
1324 lpdt->cy = 30 + p * il;
1326 /* OK */
1327 lpw = build_control(lpw, 170, 10 + p * il, 40, 15, IDOK,
1328 0x0080, WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON | WS_TABSTOP);
1330 /* Cancel */
1331 lpw = build_control(lpw, 215, 10 + p * il, 40, 15, IDCANCEL,
1332 0x0080, WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP);
1334 GlobalUnlock(hgbl);
1335 n = DialogBoxIndirect(hinst, (LPDLGTEMPLATE)hgbl,
1336 hwnd, (DLGPROC)formDlgProc);
1338 GlobalFree(hgbl);
1340 return n ? form_values : NULL;
1344 static mpdm_t open_or_save(int o, mpdm_t a)
1345 /* manages an open or save file dialog */
1347 OPENFILENAME ofn;
1348 wchar_t * wptr;
1349 char * ptr;
1350 char buf[1024] = "";
1351 char buf2[1024];
1352 int r;
1354 /* 1# arg: prompt */
1355 wptr = mpdm_string(mpdm_aget(a, 0));
1356 ptr = mpdm_wcstombs(wptr, NULL);
1358 memset(&ofn, '\0', sizeof(OPENFILENAME));
1359 ofn.lStructSize = sizeof(OPENFILENAME);
1360 ofn.hwndOwner = hwnd;
1361 ofn.lpstrFilter = "*.*\0*.*\0";
1362 ofn.nFilterIndex = 1;
1363 ofn.lpstrFile = buf;
1364 ofn.nMaxFile = sizeof(buf);
1365 ofn.lpstrTitle = ptr;
1366 ofn.lpstrDefExt = "";
1368 GetCurrentDirectory(sizeof(buf2), buf2);
1369 ofn.lpstrInitialDir = buf2;
1371 /* ofn.lpstrDefExt=(def==NULL ? "" : def);*/
1373 if (o) {
1374 ofn.Flags = OFN_PATHMUSTEXIST|OFN_HIDEREADONLY|
1375 OFN_NOCHANGEDIR|OFN_FILEMUSTEXIST;
1377 r = GetOpenFileName(&ofn);
1379 else {
1380 ofn.Flags = OFN_HIDEREADONLY;
1382 r = GetSaveFileName(&ofn);
1385 free(ptr);
1387 if (r)
1388 return MPDM_MBS(buf);
1390 return NULL;
1394 static mpdm_t win32_drv_openfile(mpdm_t a)
1395 /* openfile driver function */
1397 return open_or_save(1, a);
1401 static mpdm_t win32_drv_savefile(mpdm_t a)
1402 /* savefile driver function */
1404 return open_or_save(0, a);
1408 static mpdm_t win32_drv_update_ui(mpdm_t a)
1410 build_fonts(GetDC(hwnd));
1411 build_colors();
1412 build_menu();
1414 return NULL;
1418 static mpdm_t win32_drv_timer(mpdm_t a)
1420 int msecs = mpdm_ival(mpdm_aget(a, 0));
1421 mpdm_t func = mpdm_aget(a, 1);
1422 mpdm_t r;
1424 /* previously defined one? remove */
1425 if (timer_func != NULL)
1426 KillTimer(hwnd, 1);
1428 /* if msecs and func are set, program timer */
1429 if (msecs > 0 && func != NULL)
1430 SetTimer(hwnd, 1, msecs, NULL);
1432 r = mpdm_unref(timer_func);
1433 timer_func = mpdm_ref(func);
1435 return r;
1439 static mpdm_t win32_drv_busy(mpdm_t a)
1441 int onoff = mpdm_ival(mpdm_aget(a, 0));
1443 SetCursor(LoadCursor(NULL, onoff ? IDC_WAIT : IDC_ARROW));
1445 return NULL;
1449 static void register_functions(void)
1451 mpdm_t drv;
1453 drv = mpdm_hget_s(mp, L"drv");
1454 mpdm_hset_s(drv, L"main_loop", MPDM_X(win32_drv_main_loop));
1455 mpdm_hset_s(drv, L"shutdown", MPDM_X(win32_drv_shutdown));
1457 mpdm_hset_s(drv, L"clip_to_sys", MPDM_X(win32_drv_clip_to_sys));
1458 mpdm_hset_s(drv, L"sys_to_clip", MPDM_X(win32_drv_sys_to_clip));
1459 mpdm_hset_s(drv, L"update_ui", MPDM_X(win32_drv_update_ui));
1460 mpdm_hset_s(drv, L"timer", MPDM_X(win32_drv_timer));
1461 mpdm_hset_s(drv, L"busy", MPDM_X(win32_drv_busy));
1463 mpdm_hset_s(drv, L"alert", MPDM_X(win32_drv_alert));
1464 mpdm_hset_s(drv, L"confirm", MPDM_X(win32_drv_confirm));
1465 mpdm_hset_s(drv, L"openfile", MPDM_X(win32_drv_openfile));
1466 mpdm_hset_s(drv, L"savefile", MPDM_X(win32_drv_savefile));
1467 mpdm_hset_s(drv, L"form", MPDM_X(win32_drv_form));
1471 static mpdm_t win32_drv_startup(mpdm_t a)
1473 WNDCLASSW wc;
1474 RECT r;
1475 mpdm_t v;
1477 register_functions();
1479 InitCommonControls();
1481 /* register the window */
1482 wc.style = CS_HREDRAW|CS_VREDRAW;
1483 wc.lpfnWndProc = WndProc;
1484 wc.cbClsExtra = 0;
1485 wc.cbWndExtra = 0;
1486 wc.hInstance = hinst;
1487 wc.hIcon = LoadIcon(hinst,"MP_ICON");
1488 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
1489 wc.hbrBackground = NULL;
1490 wc.lpszMenuName = NULL;
1491 wc.lpszClassName = L"minimumprofit5.x";
1493 RegisterClassW(&wc);
1495 /* create the window */
1496 hwnd = CreateWindowW(L"minimumprofit5.x", L"mp " VERSION,
1497 WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_VSCROLL,
1498 CW_USEDEFAULT, CW_USEDEFAULT,
1499 CW_USEDEFAULT, CW_USEDEFAULT,
1500 NULL, NULL, hinst, NULL);
1502 ShowWindow(hwnd, SW_SHOW);
1503 UpdateWindow(hwnd);
1505 GetClientRect(hwnd, &r);
1507 hwtabs = CreateWindow(WC_TABCONTROL, "tab",
1508 WS_CHILD | TCS_TABS | TCS_SINGLELINE | TCS_FOCUSNEVER,
1509 0, 0, r.right - r.left, tab_height, hwnd, NULL, hinst, NULL);
1511 SendMessage(hwtabs, WM_SETFONT,
1512 (WPARAM) GetStockObject(DEFAULT_GUI_FONT), 0);
1514 ShowWindow(hwtabs, SW_SHOW);
1515 UpdateWindow(hwtabs);
1517 hwstatus = CreateWindow(WC_STATIC, "status",
1518 WS_CHILD,
1519 0, r.bottom - r.top - status_height,
1520 r.right - r.left, status_height, hwnd, NULL, hinst, NULL);
1522 win32_drv_update_ui(NULL);
1524 SendMessage(hwstatus, WM_SETFONT,
1525 (WPARAM) GetStockObject(DEFAULT_GUI_FONT), 0);
1527 ShowWindow(hwstatus, SW_SHOW);
1528 UpdateWindow(hwstatus);
1530 if ((v = mpdm_hget_s(mp, L"config")) != NULL &&
1531 mpdm_ival(mpdm_hget_s(v, L"maximize")) > 0)
1532 SendMessage(hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
1534 return NULL;
1538 int win32_drv_detect(int * argc, char *** argv)
1540 mpdm_t drv;
1542 drv = mpdm_hget_s(mp, L"drv");
1543 mpdm_hset_s(drv, L"id", MPDM_LS(L"win32"));
1544 mpdm_hset_s(drv, L"startup", MPDM_X(win32_drv_startup));
1546 return 1;
1549 #endif /* CONFOPT_WIN32 */