Reference to the appropriate document regarding character encoding.
[mp-5.x.git] / mpv_win32.c
blob9829889fbc55b6f9a40b07c90a180c5cdcf7df44
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_RETURN: ptr = L"enter"; break;
516 case VK_BACK: ptr = L"backspace"; break;
517 case VK_DELETE: ptr = L"delete"; break;
518 case VK_INSERT: ptr = L"insert"; break;
519 case VK_DIVIDE: ptr = L"kp-divide"; break;
520 case VK_MULTIPLY: ptr = L"kp-multiply"; break;
521 case VK_SUBTRACT: ptr = L"kp-minus"; break;
522 case VK_ADD: ptr = L"kp-plus"; break;
523 case VK_F1: ptr = L"f1"; break;
524 case VK_F2: ptr = L"f2"; break;
525 case VK_F3: ptr = L"f3"; break;
526 case VK_F4: ptr = L"f4"; break;
527 case VK_F5: ptr = L"f5"; break;
528 case VK_F6: ptr = L"f6"; break;
529 case VK_F7: ptr = L"f7"; break;
530 case VK_F8: ptr = L"f8"; break;
531 case VK_F9: ptr = L"f9"; break;
532 case VK_F10: ptr = L"f10"; break;
533 case VK_F11: ptr = L"f11"; break;
534 case VK_F12: ptr = L"f12"; break;
538 if (ptr != NULL) {
539 mp_process_event(MPDM_S(ptr));
540 is_wm_keydown = 1;
541 mp_active();
543 if (mp_keypress_throttle(1))
544 redraw();
549 #define ctrl(c) ((c) & 31)
551 static void win32_akey(int k)
552 /* win32 alphanumeric key processing */
554 wchar_t c[2];
555 wchar_t * ptr = NULL;
557 /* set mp.shift_pressed */
558 if (GetKeyState(VK_SHIFT) & 0x8000)
559 mpdm_hset_s(mp, L"shift_pressed", MPDM_I(1));
561 switch (k) {
562 case ctrl(' '): ptr = L"ctrl-space"; break;
563 case ctrl('a'): ptr = L"ctrl-a"; break;
564 case ctrl('b'): ptr = L"ctrl-b"; break;
565 case ctrl('c'): ptr = L"ctrl-c"; break;
566 case ctrl('d'): ptr = L"ctrl-d"; break;
567 case ctrl('e'): ptr = L"ctrl-e"; break;
568 case ctrl('f'): ptr = L"ctrl-f"; break;
569 case ctrl('g'): ptr = L"ctrl-g"; break;
570 case ctrl('h'): /* same as backspace */ break;
571 case ctrl('i'): ptr = L"ctrl-i"; break;
572 case ctrl('j'): ptr = L"ctrl-j"; break;
573 case ctrl('k'): ptr = L"ctrl-k"; break;
574 case ctrl('l'): ptr = L"ctrl-l"; break;
575 case ctrl('m'): /* same as ENTER */ break;
576 case ctrl('n'): ptr = L"ctrl-n"; break;
577 case ctrl('o'): ptr = L"ctrl-o"; break;
578 case ctrl('p'): ptr = L"ctrl-p"; break;
579 case ctrl('q'): ptr = L"ctrl-q"; break;
580 case ctrl('r'): ptr = L"ctrl-r"; break;
581 case ctrl('s'): ptr = L"ctrl-s"; break;
582 case ctrl('t'): ptr = L"ctrl-t"; break;
583 case ctrl('u'): ptr = L"ctrl-u"; break;
584 case ctrl('v'): ptr = L"ctrl-v"; break;
585 case ctrl('w'): ptr = L"ctrl-w"; break;
586 case ctrl('x'): ptr = L"ctrl-x"; break;
587 case ctrl('y'): ptr = L"ctrl-y"; break;
588 case ctrl('z'): ptr = L"ctrl-z"; break;
589 case ' ': ptr = L"space"; break;
590 case 27: ptr = L"escape"; break;
592 default:
593 /* this is probably very bad */
594 c[0] = (wchar_t) k;
595 c[1] = L'\0';
596 ptr = c;
598 break;
601 if (ptr != NULL) {
602 mp_process_event(MPDM_S(ptr));
603 mp_active();
604 redraw();
609 static void win32_vscroll(UINT wparam)
610 /* scrollbar messages handler */
612 wchar_t * ptr = NULL;
613 mpdm_t txt;
615 switch (LOWORD(wparam)) {
616 case SB_PAGEUP: ptr = L"page-up"; break;
617 case SB_PAGEDOWN: ptr = L"page-down"; break;
618 case SB_LINEUP: ptr = L"cursor-up"; break;
619 case SB_LINEDOWN: ptr = L"cursor-down"; break;
620 case SB_THUMBPOSITION:
621 case SB_THUMBTRACK:
622 /* set both y and vy */
623 txt = mpdm_hget_s(mp_active(), L"txt");
624 mp_set_y(mp_active(), HIWORD(wparam));
625 mpdm_hset_s(txt, L"vy", MPDM_I(HIWORD(wparam)));
626 redraw();
627 break;
630 if (ptr != NULL) {
631 mp_process_event(MPDM_S(ptr));
632 redraw();
637 static void action_by_menu(int item)
638 /* execute an action triggered by the menu */
640 MENUITEMINFO mi;
642 memset(&mi, '\0', sizeof(mi));
643 mi.cbSize = sizeof(mi);
644 mi.fMask = MIIM_DATA;
646 if (GetMenuItemInfo(menu, item, FALSE, &mi)) {
647 if (mi.dwItemData != 0) {
648 mp_process_action((mpdm_t)mi.dwItemData);
649 mp_active();
655 static void dropped_files(HDROP hDrop)
656 /* fill the mp.dropped_files array with the dropped files */
658 mpdm_t a = MPDM_A(0);
659 char tmp[1024];
660 int n;
662 n = DragQueryFile(hDrop, 0xffffffff, NULL, sizeof(tmp) - 1);
664 while (--n >= 0) {
665 DragQueryFile(hDrop, n, tmp, sizeof(tmp) - 1);
666 mpdm_push(a, MPDM_MBS(tmp));
669 DragFinish(hDrop);
671 mpdm_hset_s(mp, L"dropped_files", a);
672 mp_process_event(MPDM_LS(L"dropped-files"));
673 redraw();
677 #ifndef WM_MOUSEWHEEL
678 #define WM_MOUSEWHEEL 0x020A
679 #endif
681 long STDCALL WndProc(HWND hwnd, UINT msg, UINT wparam, LONG lparam)
682 /* main window Proc */
684 int x, y;
685 LPNMHDR p;
686 wchar_t * ptr = NULL;
688 switch (msg) {
689 case WM_CREATE:
691 is_wm_keydown = 0;
692 DragAcceptFiles(hwnd, TRUE);
693 return 0;
695 case WM_DROPFILES:
697 dropped_files((HDROP) wparam);
698 return 0;
700 case WM_KEYUP:
702 is_wm_keydown = 0;
704 if (mp_keypress_throttle(0))
705 redraw();
707 return 0;
709 case WM_KEYDOWN:
711 win32_vkey(wparam);
712 return 0;
714 case WM_CHAR:
716 win32_akey(wparam);
717 return 0;
719 case WM_VSCROLL:
721 win32_vscroll(wparam);
722 return 0;
724 case WM_PAINT:
726 if (mpdm_size(mpdm_hget_s(mp, L"docs")))
727 win32_draw(hwnd, mp_active());
729 return 0;
731 case WM_SIZE:
733 if (!IsIconic(hwnd)) {
734 update_window_size();
736 MoveWindow(hwtabs, 0, 0, LOWORD(lparam), tab_height, FALSE);
738 MoveWindow(hwstatus, 0, HIWORD(lparam) - status_height,
739 LOWORD(lparam), status_height, FALSE);
741 redraw();
744 return 0;
746 case WM_LBUTTONDOWN:
748 mouse_down = 1;
749 /* fallthrough */
751 case WM_RBUTTONDOWN:
752 case WM_MBUTTONDOWN:
754 x = (LOWORD(lparam)) / font_width;
755 y = (HIWORD(lparam) - tab_height) / font_height;
757 mpdm_hset_s(mp, L"mouse_x", MPDM_I(x));
758 mpdm_hset_s(mp, L"mouse_y", MPDM_I(y));
760 switch (msg) {
761 case WM_LBUTTONDOWN: ptr = L"mouse-left-button"; break;
762 case WM_RBUTTONDOWN: ptr = L"mouse-right-button"; break;
763 case WM_MBUTTONDOWN: ptr = L"mouse-middle-button"; break;
766 if (ptr != NULL) {
767 mp_process_event(MPDM_S(ptr));
768 redraw();
771 return 0;
773 case WM_LBUTTONUP:
775 mouse_down = 0;
776 return 0;
778 case WM_MOUSEMOVE:
780 if (mouse_down) {
781 x = (LOWORD(lparam)) / font_width;
782 y = (HIWORD(lparam) - tab_height) / font_height;
784 mpdm_hset_s(mp, L"mouse_to_x", MPDM_I(x));
785 mpdm_hset_s(mp, L"mouse_to_y", MPDM_I(y));
787 mp_process_event(MPDM_LS(L"mouse-drag"));
788 redraw();
791 return 0;
793 case WM_MOUSEWHEEL:
795 if ((int) wparam > 0)
796 ptr = L"mouse-wheel-up";
797 else
798 ptr = L"mouse-wheel-down";
800 if (ptr != NULL) {
801 mp_process_event(MPDM_S(ptr));
802 redraw();
805 return 0;
807 case WM_COMMAND:
809 action_by_menu(LOWORD(wparam));
810 redraw();
812 return 0;
814 case WM_CLOSE:
816 if (!mp_exit_requested)
817 mp_process_event(MPDM_LS(L"close-window"));
819 if (mp_exit_requested)
820 DestroyWindow(hwnd);
822 return 0;
824 case WM_DESTROY:
825 PostQuitMessage(0);
826 return 0;
828 case WM_NOTIFY:
829 p = (LPNMHDR)lparam;
831 if (p->code == TCN_SELCHANGE) {
832 /* tab selected by clicking on it */
833 int n = TabCtrl_GetCurSel(hwtabs);
835 /* set mp.active_i to this */
836 mpdm_hset_s(mp, L"active_i", MPDM_I(n));
838 redraw();
841 return 0;
843 case WM_TIMER:
844 mpdm_exec(timer_func, NULL);
846 return 0;
849 if (mp_exit_requested)
850 PostMessage(hwnd, WM_CLOSE, 0, 0);
852 return DefWindowProcW(hwnd, msg, wparam, lparam);
856 static mpdm_t win32_drv_clip_to_sys(mpdm_t a)
857 /* driver-dependent mp to system clipboard */
859 HGLOBAL hclp;
860 mpdm_t d;
861 char * ptr;
862 char * clpptr;
863 int s;
865 /* convert the clipboard to DOS text */
866 d = mpdm_hget_s(mp, L"clipboard");
868 if (mpdm_size(d) == 0)
869 return NULL;
871 d = mpdm_join(MPDM_LS(L"\r\n"), d);
872 ptr = mpdm_wcstombs(d->data, &s);
874 /* allocates a handle and copies */
875 hclp = GlobalAlloc(GHND, s + 1);
876 clpptr = (char *)GlobalLock(hclp);
877 memcpy(clpptr, ptr, s);
878 clpptr[s] = '\0';
879 GlobalUnlock(hclp);
881 free(ptr);
883 OpenClipboard(NULL);
884 EmptyClipboard();
885 SetClipboardData(CF_TEXT, hclp);
886 CloseClipboard();
888 return NULL;
892 static mpdm_t win32_drv_sys_to_clip(mpdm_t a)
893 /* driver-dependent system to mp clipboard */
895 HGLOBAL hclp;
896 char * ptr;
898 OpenClipboard(NULL);
899 hclp = GetClipboardData(CF_TEXT);
900 CloseClipboard();
902 if (hclp && (ptr = GlobalLock(hclp)) != NULL) {
903 mpdm_t d;
905 /* create a value and split */
906 d = MPDM_MBS(ptr);
907 d = mpdm_split(MPDM_LS(L"\r\n"), d);
909 /* and set as the clipboard */
910 mpdm_hset_s(mp, L"clipboard", d);
911 mpdm_hset_s(mp, L"clipboard_vertical", MPDM_I(0));
913 GlobalUnlock(hclp);
916 return NULL;
920 static mpdm_t win32_drv_main_loop(mpdm_t a)
922 MSG msg;
924 if (!mp_exit_requested) {
925 mp_active();
927 while (GetMessage(&msg, NULL, 0, 0)) {
928 TranslateMessage(&msg);
929 DispatchMessage(&msg);
933 return NULL;
937 static mpdm_t win32_drv_shutdown(mpdm_t a)
939 mpdm_t v;
941 SendMessage(hwnd, WM_CLOSE, 0, 0);
943 if ((v = mpdm_hget_s(mp, L"exit_message")) != NULL) {
944 char * ptr = mpdm_wcstombs(mpdm_string(v), NULL);
945 MessageBox(NULL, ptr, "mp " VERSION, MB_ICONWARNING|MB_OK);
946 free(ptr);
949 return NULL;
953 static mpdm_t win32_drv_alert(mpdm_t a)
954 /* alert driver function */
956 wchar_t * wptr;
957 char * ptr;
959 /* 1# arg: prompt */
960 wptr = mpdm_string(mpdm_aget(a, 0));
962 if ((ptr = mpdm_wcstombs(wptr, NULL)) != NULL) {
963 MessageBox(hwnd, ptr, "mp " VERSION, MB_ICONWARNING|MB_OK);
964 free(ptr);
967 return NULL;
971 static mpdm_t win32_drv_confirm(mpdm_t a)
972 /* confirm driver function */
974 wchar_t * wptr;
975 char * ptr;
976 int ret = 0;
978 /* 1# arg: prompt */
979 wptr = mpdm_string(mpdm_aget(a, 0));
981 if ((ptr = mpdm_wcstombs(wptr, NULL)) != NULL) {
982 ret = MessageBox(hwnd, ptr, "mp " VERSION, MB_ICONQUESTION|MB_YESNOCANCEL);
983 free(ptr);
985 if (ret == IDYES)
986 ret = 1;
987 else
988 if (ret == IDNO)
989 ret = 2;
990 else
991 ret = 0;
994 return MPDM_I(ret);
998 static LPWORD lpwAlign(LPWORD lpIn)
999 /* aligns a pointer to DWORD boundary (for dialog templates) */
1001 ULONG ul;
1003 ul = (ULONG)lpIn;
1004 ul ++;
1005 ul >>= 1;
1006 ul <<= 1;
1007 return (LPWORD)ul;
1011 #define LABEL_ID 1000
1012 #define CTRL_ID 2000
1014 BOOL CALLBACK formDlgProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
1015 /* mp.drv.form() dialog proc */
1017 int n;
1018 HFONT hf;
1020 switch (msg) {
1021 case WM_INITDIALOG:
1023 SetWindowText(hwnd, "mp " VERSION);
1025 hf = GetStockObject(DEFAULT_GUI_FONT);
1027 /* fill controls with its initial data */
1028 for (n = 0; n < mpdm_size(form_args); n++) {
1029 mpdm_t w = mpdm_aget(form_args, n);
1030 wchar_t * type;
1031 mpdm_t t;
1032 int ctrl = CTRL_ID + n;
1033 wchar_t * wptr;
1034 char * ptr;
1036 if ((t = mpdm_hget_s(w, L"label")) != NULL) {
1037 if ((ptr = mpdm_wcstombs(mpdm_string(t), NULL)) != NULL) {
1038 SetDlgItemText(hwnd, LABEL_ID + n, ptr);
1039 free(ptr);
1041 SendDlgItemMessage(hwnd, LABEL_ID + n, WM_SETFONT,
1042 (WPARAM) hf, MAKELPARAM(FALSE, 0));
1046 SendDlgItemMessage(hwnd, ctrl, WM_SETFONT,
1047 (WPARAM) hf, MAKELPARAM(FALSE, 0));
1049 type = mpdm_string(mpdm_hget_s(w, L"type"));
1051 if (wcscmp(type, L"text") == 0) {
1052 if ((t = mpdm_hget_s(w, L"value")) != NULL &&
1053 (ptr = mpdm_wcstombs(mpdm_string(t), NULL)) != NULL) {
1054 SetDlgItemText(hwnd, ctrl, ptr);
1055 free(ptr);
1058 /* store the history into combo_items */
1059 if ((t = mpdm_hget_s(w, L"history")) != NULL) {
1060 t = mp_get_history(t);
1061 int i;
1063 for (i = 0; i < mpdm_size(t); i++) {
1064 mpdm_t v = mpdm_aget(t, i);
1066 if ((ptr = mpdm_wcstombs(v->data,
1067 NULL)) != NULL) {
1068 SendDlgItemMessage(hwnd,
1069 ctrl,
1070 CB_INSERTSTRING, 0,
1071 (LPARAM)ptr);
1072 free(ptr);
1077 else
1078 if (wcscmp(type, L"password") == 0) {
1079 SendDlgItemMessage(hwnd, ctrl,
1080 EM_SETPASSWORDCHAR, (WPARAM)'*', (LPARAM)0);
1082 else
1083 if (wcscmp(type, L"checkbox") == 0) {
1084 if ((t = mpdm_hget_s(w, L"value")) != NULL)
1085 SendDlgItemMessage(hwnd, ctrl,
1086 BM_SETCHECK, mpdm_ival(t) ?
1087 BST_CHECKED : BST_UNCHECKED,
1090 else
1091 if (wcscmp(type, L"list") == 0) {
1092 int i;
1094 t = mpdm_hget_s(w, L"list");
1096 /* fill the list */
1097 for (i = 0; i < mpdm_size(t); i++) {
1098 wptr = mpdm_string(mpdm_aget(t, i));
1099 if ((ptr = mpdm_wcstombs(wptr, NULL)) != NULL) {
1100 SendDlgItemMessage(hwnd, ctrl,
1101 LB_ADDSTRING, 0, (LPARAM) ptr);
1102 free(ptr);
1106 /* set position */
1107 SendDlgItemMessage(hwnd, ctrl, LB_SETCURSEL,
1108 mpdm_ival(mpdm_hget_s(w, L"value")), 0);
1112 /* FIXME: untranslated strings */
1114 SetDlgItemText(hwnd, IDOK, "OK");
1115 SendDlgItemMessage(hwnd, IDOK, WM_SETFONT,
1116 (WPARAM) hf, MAKELPARAM(FALSE, 0));
1118 SetDlgItemText(hwnd, IDCANCEL, "Cancel");
1119 SendDlgItemMessage(hwnd, IDCANCEL, WM_SETFONT,
1120 (WPARAM) hf, MAKELPARAM(FALSE, 0));
1122 return TRUE;
1124 case WM_COMMAND:
1126 if (LOWORD(wparam) == IDCANCEL) {
1127 EndDialog(hwnd, 0);
1128 return TRUE;
1131 if (LOWORD(wparam) != IDOK)
1132 break;
1134 /* fill all return values */
1135 for (n = 0; n < mpdm_size(form_args); n++) {
1136 mpdm_t w = mpdm_aget(form_args, n);
1137 wchar_t * type = mpdm_string(mpdm_hget_s(w, L"type"));
1138 int ctrl = CTRL_ID + n;
1140 if (wcscmp(type, L"text") == 0) {
1141 char tmp[2048];
1142 mpdm_t v;
1143 mpdm_t h;
1145 GetDlgItemText(hwnd, ctrl, tmp, sizeof(tmp) - 1);
1146 v = MPDM_MBS(tmp);
1148 mpdm_aset(form_values, v, n);
1150 /* if it has history, fill it */
1151 if ((h = mpdm_hget_s(w, L"history")) != NULL &&
1152 v != NULL && mpdm_cmp(v, MPDM_LS(L"")) != 0) {
1153 h = mp_get_history(h);
1155 if (mpdm_cmp(v, mpdm_aget(h, -1)) != 0)
1156 mpdm_push(h, v);
1159 if (wcscmp(type, L"password") == 0) {
1160 char tmp[2048];
1162 GetDlgItemText(hwnd, ctrl, tmp, sizeof(tmp) - 1);
1163 mpdm_aset(form_values, MPDM_MBS(tmp), n);
1165 else
1166 if (wcscmp(type, L"checkbox") == 0) {
1167 mpdm_aset(form_values,
1168 MPDM_I(SendDlgItemMessage(hwnd, ctrl,
1169 BM_GETCHECK, 0, 0)), n);
1171 else
1172 if (wcscmp(type, L"list") == 0) {
1173 mpdm_aset(form_values,
1174 MPDM_I(SendDlgItemMessage(hwnd, ctrl,
1175 LB_GETCURSEL, 0, 0)), n);
1179 EndDialog(hwnd, 1);
1180 return TRUE;
1183 return FALSE;
1187 static void build_form_data(mpdm_t widget_list)
1188 /* builds the necessary information for a list of widgets */
1190 mpdm_unref(form_args);
1191 form_args = mpdm_ref(widget_list);
1193 mpdm_unref(form_values);
1194 form_values = widget_list == NULL ? NULL :
1195 mpdm_ref(MPDM_A(mpdm_size(form_args)));
1199 LPWORD static build_control(LPWORD lpw, int x, int y,
1200 int cx, int cy, int id, int class, int style)
1201 /* fills a control structure in a hand-made dialog template */
1203 LPDLGITEMTEMPLATE lpdit;
1205 lpw = lpwAlign(lpw);
1206 lpdit = (LPDLGITEMTEMPLATE)lpw;
1207 lpdit->x = x; lpdit->y = y;
1208 lpdit->cx = cx; lpdit->cy = cy;
1209 lpdit->id = id;
1210 lpdit->style = style;
1212 lpw = (LPWORD)(lpdit + 1);
1213 *lpw++ = 0xFFFF;
1214 *lpw++ = class;
1216 /* no text (will be set on dialog setup) */
1217 *lpw++ = 0;
1218 *lpw++ = 0;
1220 /* Align creation data on DWORD boundary */
1221 lpw = lpwAlign(lpw);
1222 /* No creation data */
1223 *lpw++ = 0;
1225 return lpw;
1229 static mpdm_t win32_drv_form(mpdm_t a)
1230 /* mp.drv.form() function */
1232 HGLOBAL hgbl;
1233 LPDLGTEMPLATE lpdt;
1234 LPWORD lpw;
1235 int n, p;
1236 int il = 10;
1237 int lbl = 0;
1239 /* first argument: list of widgets */
1240 build_form_data(mpdm_aget(a, 0));
1242 /* On-the-fly dialog template creation */
1243 /* Note: all this crap is taken from MSDN, no less */
1245 /* magic size; looking for problems */
1246 hgbl = GlobalAlloc(GMEM_ZEROINIT, 4096);
1247 lpdt = (LPDLGTEMPLATE)GlobalLock(hgbl);
1249 lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU | DS_MODALFRAME | WS_CAPTION;
1250 lpdt->cdit = (2 * mpdm_size(form_args)) + 2;
1251 lpdt->x = 20; lpdt->y = 20;
1252 lpdt->cx = 260;
1254 lpw = (LPWORD)(lpdt + 1);
1255 *lpw++ = 0; /* No menu */
1256 *lpw++ = 0; /* Predefined dialog box class (by default) */
1257 *lpw++ = 0; /* No title */
1259 /* first pass: calculate maximum size of labels */
1260 for (n = 0; n < mpdm_size(form_args); n++) {
1261 mpdm_t w = mpdm_aget(form_args, n);
1262 int l = mpdm_size(mpdm_hget_s(w, L"label"));
1264 if (lbl < l)
1265 lbl = l;
1268 /* second pass: create the dialog controls */
1269 for (n = p = 0; n < mpdm_size(form_args); n++) {
1270 mpdm_t w = mpdm_aget(form_args, n);
1271 wchar_t * type;
1272 int class;
1273 int style;
1274 int inc = 1;
1275 int sz = 1;
1277 /* label control */
1278 lpw = build_control(lpw, 0, 5 + p * il,
1279 lbl * 3, 20, LABEL_ID + n, 0x0082,
1280 WS_CHILD | WS_VISIBLE | SS_RIGHT);
1282 type = mpdm_string(mpdm_hget_s(w, L"type"));
1284 if (wcscmp(type, L"text") == 0) {
1285 class = 0x0085;
1286 style = WS_CHILD | WS_VISIBLE | WS_TABSTOP |
1287 CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL;
1289 /* size */
1290 sz = 5;
1292 else
1293 if (wcscmp(type, L"password") == 0) {
1294 class = 0x0081;
1295 style = WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP;
1297 else
1298 if (wcscmp(type, L"checkbox") == 0) {
1299 class = 0x0080;
1300 style = WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX | WS_TABSTOP;
1302 else
1303 if (wcscmp(type, L"list") == 0) {
1304 class = 0x0083;
1305 style = WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER |
1306 LBS_NOINTEGRALHEIGHT | WS_VSCROLL |
1307 LBS_NOTIFY | LBS_USETABSTOPS;
1309 /* height */
1310 inc = 5;
1313 /* the control */
1314 lpw = build_control(lpw, 10 + lbl * 3, 5 + p * il,
1315 245 - lbl * 3, inc * il * sz, CTRL_ID + n,
1316 class, style);
1318 /* next position */
1319 p += inc;
1322 /* set total height */
1323 lpdt->cy = 30 + p * il;
1325 /* OK */
1326 lpw = build_control(lpw, 170, 10 + p * il, 40, 15, IDOK,
1327 0x0080, WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON | WS_TABSTOP);
1329 /* Cancel */
1330 lpw = build_control(lpw, 215, 10 + p * il, 40, 15, IDCANCEL,
1331 0x0080, WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP);
1333 GlobalUnlock(hgbl);
1334 n = DialogBoxIndirect(hinst, (LPDLGTEMPLATE)hgbl,
1335 hwnd, (DLGPROC)formDlgProc);
1337 GlobalFree(hgbl);
1339 return n ? form_values : NULL;
1343 static mpdm_t open_or_save(int o, mpdm_t a)
1344 /* manages an open or save file dialog */
1346 OPENFILENAME ofn;
1347 wchar_t * wptr;
1348 char * ptr;
1349 char buf[1024] = "";
1350 char buf2[1024];
1351 int r;
1353 /* 1# arg: prompt */
1354 wptr = mpdm_string(mpdm_aget(a, 0));
1355 ptr = mpdm_wcstombs(wptr, NULL);
1357 memset(&ofn, '\0', sizeof(OPENFILENAME));
1358 ofn.lStructSize = sizeof(OPENFILENAME);
1359 ofn.hwndOwner = hwnd;
1360 ofn.lpstrFilter = "*.*\0*.*\0";
1361 ofn.nFilterIndex = 1;
1362 ofn.lpstrFile = buf;
1363 ofn.nMaxFile = sizeof(buf);
1364 ofn.lpstrTitle = ptr;
1365 ofn.lpstrDefExt = "";
1367 GetCurrentDirectory(sizeof(buf2), buf2);
1368 ofn.lpstrInitialDir = buf2;
1370 /* ofn.lpstrDefExt=(def==NULL ? "" : def);*/
1372 if (o) {
1373 ofn.Flags = OFN_PATHMUSTEXIST|OFN_HIDEREADONLY|
1374 OFN_NOCHANGEDIR|OFN_FILEMUSTEXIST;
1376 r = GetOpenFileName(&ofn);
1378 else {
1379 ofn.Flags = OFN_HIDEREADONLY;
1381 r = GetSaveFileName(&ofn);
1384 free(ptr);
1386 if (r)
1387 return MPDM_MBS(buf);
1389 return NULL;
1393 static mpdm_t win32_drv_openfile(mpdm_t a)
1394 /* openfile driver function */
1396 return open_or_save(1, a);
1400 static mpdm_t win32_drv_savefile(mpdm_t a)
1401 /* savefile driver function */
1403 return open_or_save(0, a);
1407 static mpdm_t win32_drv_update_ui(mpdm_t a)
1409 build_fonts(GetDC(hwnd));
1410 build_colors();
1411 build_menu();
1413 return NULL;
1417 static mpdm_t win32_drv_timer(mpdm_t a)
1419 int msecs = mpdm_ival(mpdm_aget(a, 0));
1420 mpdm_t func = mpdm_aget(a, 1);
1421 mpdm_t r;
1423 /* previously defined one? remove */
1424 if (timer_func != NULL)
1425 KillTimer(hwnd, 1);
1427 /* if msecs and func are set, program timer */
1428 if (msecs > 0 && func != NULL)
1429 SetTimer(hwnd, 1, msecs, NULL);
1431 r = mpdm_unref(timer_func);
1432 timer_func = mpdm_ref(func);
1434 return r;
1438 static mpdm_t win32_drv_busy(mpdm_t a)
1440 int onoff = mpdm_ival(mpdm_aget(a, 0));
1442 SetCursor(LoadCursor(NULL, onoff ? IDC_WAIT : IDC_ARROW));
1444 return NULL;
1448 static void register_functions(void)
1450 mpdm_t drv;
1452 drv = mpdm_hget_s(mp, L"drv");
1453 mpdm_hset_s(drv, L"main_loop", MPDM_X(win32_drv_main_loop));
1454 mpdm_hset_s(drv, L"shutdown", MPDM_X(win32_drv_shutdown));
1456 mpdm_hset_s(drv, L"clip_to_sys", MPDM_X(win32_drv_clip_to_sys));
1457 mpdm_hset_s(drv, L"sys_to_clip", MPDM_X(win32_drv_sys_to_clip));
1458 mpdm_hset_s(drv, L"update_ui", MPDM_X(win32_drv_update_ui));
1459 mpdm_hset_s(drv, L"timer", MPDM_X(win32_drv_timer));
1460 mpdm_hset_s(drv, L"busy", MPDM_X(win32_drv_busy));
1462 mpdm_hset_s(drv, L"alert", MPDM_X(win32_drv_alert));
1463 mpdm_hset_s(drv, L"confirm", MPDM_X(win32_drv_confirm));
1464 mpdm_hset_s(drv, L"openfile", MPDM_X(win32_drv_openfile));
1465 mpdm_hset_s(drv, L"savefile", MPDM_X(win32_drv_savefile));
1466 mpdm_hset_s(drv, L"form", MPDM_X(win32_drv_form));
1470 static mpdm_t win32_drv_startup(mpdm_t a)
1472 WNDCLASSW wc;
1473 RECT r;
1474 mpdm_t v;
1476 register_functions();
1478 InitCommonControls();
1480 /* register the window */
1481 wc.style = CS_HREDRAW|CS_VREDRAW;
1482 wc.lpfnWndProc = WndProc;
1483 wc.cbClsExtra = 0;
1484 wc.cbWndExtra = 0;
1485 wc.hInstance = hinst;
1486 wc.hIcon = LoadIcon(hinst,"MP_ICON");
1487 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
1488 wc.hbrBackground = NULL;
1489 wc.lpszMenuName = NULL;
1490 wc.lpszClassName = L"minimumprofit5.x";
1492 RegisterClassW(&wc);
1494 /* create the window */
1495 hwnd = CreateWindowW(L"minimumprofit5.x", L"mp " VERSION,
1496 WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_VSCROLL,
1497 CW_USEDEFAULT, CW_USEDEFAULT,
1498 CW_USEDEFAULT, CW_USEDEFAULT,
1499 NULL, NULL, hinst, NULL);
1501 ShowWindow(hwnd, SW_SHOW);
1502 UpdateWindow(hwnd);
1504 GetClientRect(hwnd, &r);
1506 hwtabs = CreateWindow(WC_TABCONTROL, "tab",
1507 WS_CHILD | TCS_TABS | TCS_SINGLELINE | TCS_FOCUSNEVER,
1508 0, 0, r.right - r.left, tab_height, hwnd, NULL, hinst, NULL);
1510 SendMessage(hwtabs, WM_SETFONT,
1511 (WPARAM) GetStockObject(DEFAULT_GUI_FONT), 0);
1513 ShowWindow(hwtabs, SW_SHOW);
1514 UpdateWindow(hwtabs);
1516 hwstatus = CreateWindow(WC_STATIC, "status",
1517 WS_CHILD,
1518 0, r.bottom - r.top - status_height,
1519 r.right - r.left, status_height, hwnd, NULL, hinst, NULL);
1521 win32_drv_update_ui(NULL);
1523 SendMessage(hwstatus, WM_SETFONT,
1524 (WPARAM) GetStockObject(DEFAULT_GUI_FONT), 0);
1526 ShowWindow(hwstatus, SW_SHOW);
1527 UpdateWindow(hwstatus);
1529 if ((v = mpdm_hget_s(mp, L"config")) != NULL &&
1530 mpdm_ival(mpdm_hget_s(v, L"maximize")) > 0)
1531 SendMessage(hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
1533 return NULL;
1537 int win32_drv_detect(int * argc, char *** argv)
1539 mpdm_t drv;
1541 drv = mpdm_hget_s(mp, L"drv");
1542 mpdm_hset_s(drv, L"id", MPDM_LS(L"win32"));
1543 mpdm_hset_s(drv, L"startup", MPDM_X(win32_drv_startup));
1545 return 1;
1548 #endif /* CONFOPT_WIN32 */