blob: d4ada8b18c9b79db4a69eb41b0b4abfc6ebf1280 [file] [log] [blame]
DRC2ff39b82011-07-28 08:38:59 +00001//
2// "$Id: Fl_win32.cxx 8759 2011-05-30 12:33:51Z manolo $"
3//
4// WIN32-specific code for the Fast Light Tool Kit (FLTK).
5//
6// Copyright 1998-2010 by Bill Spitzak and others.
7//
8// This library is free software; you can redistribute it and/or
9// modify it under the terms of the GNU Library General Public
10// License as published by the Free Software Foundation; either
11// version 2 of the License, or (at your option) any later version.
12//
13// This library is distributed in the hope that it will be useful,
14// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16// Library General Public License for more details.
17//
18// You should have received a copy of the GNU Library General Public
19// License along with this library; if not, write to the Free Software
20// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21// USA.
22//
23// Please report all bugs and problems on the following page:
24//
25// http://www.fltk.org/str.php
26//
27
28// This file contains win32-specific code for fltk which is always linked
29// in. Search other files for "WIN32" or filenames ending in _win32.cxx
30// for other system-specific code.
31
32// This file must be #include'd in Fl.cxx and not compiled separately.
33
34#ifndef FL_DOXYGEN
35#include <FL/Fl.H>
36#include <FL/fl_utf8.h>
37#include <FL/Fl_Window.H>
38#include <FL/fl_draw.H>
39#include <FL/Enumerations.H>
40#include <FL/Fl_Tooltip.H>
41#include <FL/Fl_Paged_Device.H>
42#include "flstring.h"
43#include "Fl_Font.H"
44#include <stdio.h>
45#include <stdlib.h>
46#include <sys/types.h>
47#include <time.h>
48#ifdef __CYGWIN__
49# include <sys/time.h>
50# include <unistd.h>
51#endif
52
53#if !defined(NO_TRACK_MOUSE)
54# include <commctrl.h> // TrackMouseEvent
55// fabien: Ms Visual Studio >= 2003 permit embedded lib reference
56// that makes fltk use easier as only fltk libs are now requested
57// This idea could be extended to fltk libs themselves,
58// implementer should then care about DLL linkage flags ...
59# if (_MSC_VER>=1310)
60# pragma comment (lib, "comctl32.lib")
61# endif
62#endif
63
64#if defined(__GNUC__)
65# include <wchar.h>
66#endif
67
68#include <ole2.h>
69#include <shellapi.h>
70
71#include "aimm.h"
72
73//
74// USE_ASYNC_SELECT - define it if you have WSAAsyncSelect()...
75// USE_ASYNC_SELECT is OBSOLETED in 1.3 for the following reasons:
76/*
77 This feature was supposed to provide an efficient alternative to the current
78 polling method, but as it has been discussed (Thanks Albrecht!) :
79 - the async mode would imply to change the socket to non blocking mode.
80 This can have unexpected side effects for 3rd party apps, especially
81 if it is set on-the-fly when socket service is really needed, as it is
82 done today and on purpose, but still the 3rd party developer wouldn't easily
83 control the sequencing of socket operations.
84 - Finer granularity of events furthered by the async select is a plus only
85 for socket 3rd party impl., it is simply not needed for the 'light' fltk
86 use we make of wsock, so here it would also be a bad point, because of all
87 the logic add-ons necessary for using this functionality, without a clear
88 benefit.
89
90 So async mode select would not add benefits to fltk, worse, it can slowdown
91 fltk because of this finer granularity and instrumentation code to be added
92 for async mode proper operation, not mentioning the side effects...
93*/
94
95static Fl_GDI_Graphics_Driver fl_gdi_driver;
96static Fl_Display_Device fl_gdi_display(&fl_gdi_driver);
97FL_EXPORT Fl_Graphics_Driver *fl_graphics_driver = (Fl_Graphics_Driver*)&fl_gdi_driver; // the current target driver of graphics operations
98Fl_Surface_Device* Fl_Surface_Device::_surface = (Fl_Surface_Device*)&fl_gdi_display; // the current target surface of graphics operations
99Fl_Display_Device *Fl_Display_Device::_display = &fl_gdi_display; // the platform display
100
DRC685f17e2011-07-28 09:23:00 +0000101bool use_simple_keyboard = false;
102
DRC2ff39b82011-07-28 08:38:59 +0000103// dynamic wsock dll handling api:
104#if defined(__CYGWIN__) && !defined(SOCKET)
105# define SOCKET int
106#endif
107
108// note: winsock2.h has been #include'd in Fl.cxx
109#define WSCK_DLL_NAME "WS2_32.DLL"
110
111typedef int (WINAPI* fl_wsk_select_f)(int, fd_set*, fd_set*, fd_set*, const struct timeval*);
112typedef int (WINAPI* fl_wsk_fd_is_set_f)(SOCKET, fd_set *);
113
114static HMODULE s_wsock_mod = 0;
115static fl_wsk_select_f s_wsock_select = 0;
116static fl_wsk_fd_is_set_f fl_wsk_fd_is_set = 0;
117
118static HMODULE get_wsock_mod() {
119 if (!s_wsock_mod) {
120 s_wsock_mod = LoadLibrary(WSCK_DLL_NAME);
121 if (s_wsock_mod==NULL)
122 Fl::fatal("FLTK Lib Error: %s file not found! Please check your winsock dll accessibility.\n",WSCK_DLL_NAME);
123 s_wsock_select = (fl_wsk_select_f) GetProcAddress(s_wsock_mod, "select");
124 fl_wsk_fd_is_set = (fl_wsk_fd_is_set_f) GetProcAddress(s_wsock_mod, "__WSAFDIsSet");
125 }
126 return s_wsock_mod;
127}
128
129/*
130 * Dynamic linking of imm32.dll
131 * This library is only needed for a hand full (four ATM) functions relating to
132 * international text rendering and locales. Dynamically loading reduces initial
133 * size and link dependencies.
134 */
135static HMODULE s_imm_module = 0;
DRC685f17e2011-07-28 09:23:00 +0000136typedef BOOL (WINAPI* flTypeImmAssociateContextEx)(HWND, HIMC, DWORD);
137static flTypeImmAssociateContextEx flImmAssociateContextEx = 0;
DRC2ff39b82011-07-28 08:38:59 +0000138typedef HIMC (WINAPI* flTypeImmGetContext)(HWND);
139static flTypeImmGetContext flImmGetContext = 0;
140typedef BOOL (WINAPI* flTypeImmSetCompositionWindow)(HIMC, LPCOMPOSITIONFORM);
141static flTypeImmSetCompositionWindow flImmSetCompositionWindow = 0;
142typedef BOOL (WINAPI* flTypeImmReleaseContext)(HWND, HIMC);
143static flTypeImmReleaseContext flImmReleaseContext = 0;
144typedef BOOL (WINAPI* flTypeImmIsIME)(HKL);
145static flTypeImmIsIME flImmIsIME = 0;
146
147static HMODULE get_imm_module() {
148 if (!s_imm_module) {
149 s_imm_module = LoadLibrary("IMM32.DLL");
150 if (!s_imm_module)
151 Fl::fatal("FLTK Lib Error: IMM32.DLL file not found!\n\n"
152 "Please check your input method manager library accessibility.");
DRC685f17e2011-07-28 09:23:00 +0000153 flImmAssociateContextEx = (flTypeImmAssociateContextEx)GetProcAddress(s_imm_module, "ImmAssociateContextEx");
DRC2ff39b82011-07-28 08:38:59 +0000154 flImmGetContext = (flTypeImmGetContext)GetProcAddress(s_imm_module, "ImmGetContext");
155 flImmSetCompositionWindow = (flTypeImmSetCompositionWindow)GetProcAddress(s_imm_module, "ImmSetCompositionWindow");
156 flImmReleaseContext = (flTypeImmReleaseContext)GetProcAddress(s_imm_module, "ImmReleaseContext");
157 flImmIsIME = (flTypeImmIsIME)GetProcAddress(s_imm_module, "ImmIsIME");
158 }
159 return s_imm_module;
160}
161
162// USE_TRACK_MOUSE - define NO_TRACK_MOUSE if you don't have
163// TrackMouseEvent()...
164//
165// Now (Dec. 2008) we can assume that current Cygwin/MinGW versions
166// support the TrackMouseEvent() function, but WinCE obviously doesn't
167// support it (STR 2095). Therefore, USE_TRACK_MOUSE is enabled by
168// default, but you can disable it by defining NO_TRACK_MOUSE.
169//
170// TrackMouseEvent is only used to support window leave notifications
171// under Windows. It can be used to get FL_LEAVE events, when the
172// mouse leaves the _main_ application window (FLTK detects subwindow
173// leave events by using normal move events).
174//
175// Implementation note: If the mouse cursor leaves one subwindow and
176// enters another window, then Windows sends a WM_MOUSEMOVE message to
177// the new window before it sends a WM_MOUSELEAVE message to the old
178// (just left) window. We save the current mouse window in a static variable,
179// and if we get a WM_MOUSELEAVE event for the current mouse window, this
180// means that the top level window has been left (otherwise we would have
181// got another WM_MOUSEMOVE message before).
182
183// #define NO_TRACK_MOUSE
184
185#if !defined(NO_TRACK_MOUSE)
186# define USE_TRACK_MOUSE
187#endif // NO_TRACK_MOUSE
188
189static Fl_Window *track_mouse_win=0; // current TrackMouseEvent() window
190
191// USE_CAPTURE_MOUSE_WIN - this must be defined for TrackMouseEvent to work
192// correctly with subwindows - otherwise a single mouse click and release
193// (without a move) would generate phantom leave events.
194// This defines, if the current mouse window (maybe a subwindow) or the
195// main window should get mouse events after pushing (and holding) a mouse
196// button, i.e. when dragging the mouse. This is done by calling SetCapture
197// (see below).
198
199#ifdef USE_TRACK_MOUSE
200#define USE_CAPTURE_MOUSE_WIN
201#endif // USE_TRACK_MOUSE
202
203//
204// WM_SYNCPAINT is an "undocumented" message, which is finally defined in
205// VC++ 6.0.
206//
207
208#ifndef WM_SYNCPAINT
209# define WM_SYNCPAINT 0x0088
210#endif
211
212#ifndef WM_MOUSELEAVE
213# define WM_MOUSELEAVE 0x02a3
214#endif
215
216#ifndef WM_MOUSEWHEEL
217# define WM_MOUSEWHEEL 0x020a
218#endif
219
220#ifndef WHEEL_DELTA
221# define WHEEL_DELTA 120 // according to MSDN.
222#endif
223
224
225//
226// WM_FLSELECT is the user-defined message that we get when one of
227// the sockets has pending data, etc.
228//
229
230#define WM_FLSELECT (WM_APP+1) // WM_APP is used for hide-window
231
232
233////////////////////////////////////////////////////////////////
234// interface to poll/select call:
235
236// fd's are only implemented for sockets. Microsoft Windows does not
237// have a unified IO system, so it doesn't support select() on files,
238// devices, or pipes...
239//
240// Microsoft provides the Berkeley select() call and an asynchronous
241// select function that sends a WIN32 message when the select condition
242// exists. However, we don't try to use the asynchronous WSAAsyncSelect()
243// any more for good reasons (see above).
244//
245// A.S. Dec 2009: We got reports that current winsock2.h files define
246// POLLIN, POLLOUT, and POLLERR with conflicting values WRT what we
247// used before (STR #2301). Therefore we must not use these values
248// for our internal purposes, but use FL_READ, FL_WRITE, and
249// FL_EXCEPT, as defined for use in Fl::add_fd().
250//
251static int maxfd = 0;
252static fd_set fdsets[3];
253
254extern IDropTarget *flIDropTarget;
255
256static int nfds = 0;
257static int fd_array_size = 0;
258static struct FD {
259 int fd;
260 short events;
261 void (*cb)(int, void*);
262 void* arg;
263} *fd = 0;
264
265extern unsigned int fl_codepage;
266
267void fl_reset_spot()
268{
269}
270
271void fl_set_spot(int font, int size, int X, int Y, int W, int H, Fl_Window *win)
272{
273 if (!win) return;
274 Fl_Window* tw = win;
275 while (tw->parent()) tw = tw->window(); // find top level window
276
277 get_imm_module();
278 HIMC himc = flImmGetContext(fl_xid(tw));
279
280 if (himc) {
281 COMPOSITIONFORM cfs;
282 cfs.dwStyle = CFS_POINT;
283 cfs.ptCurrentPos.x = X;
284 cfs.ptCurrentPos.y = Y - tw->labelsize();
285 MapWindowPoints(fl_xid(win), fl_xid(tw), &cfs.ptCurrentPos, 1);
286 flImmSetCompositionWindow(himc, &cfs);
287 flImmReleaseContext(fl_xid(tw), himc);
288 }
289}
290
291void fl_set_status(int x, int y, int w, int h)
292{
293}
294
295void Fl::add_fd(int n, int events, void (*cb)(int, void*), void *v) {
296 remove_fd(n,events);
297 int i = nfds++;
298 if (i >= fd_array_size) {
299 fd_array_size = 2*fd_array_size+1;
300 fd = (FD*)realloc(fd, fd_array_size*sizeof(FD));
301 }
302 fd[i].fd = n;
303 fd[i].events = (short)events;
304 fd[i].cb = cb;
305 fd[i].arg = v;
306
307 if (events & FL_READ) FD_SET((unsigned)n, &fdsets[0]);
308 if (events & FL_WRITE) FD_SET((unsigned)n, &fdsets[1]);
309 if (events & FL_EXCEPT) FD_SET((unsigned)n, &fdsets[2]);
310 if (n > maxfd) maxfd = n;
311}
312
313void Fl::add_fd(int fd, void (*cb)(int, void*), void* v) {
314 Fl::add_fd(fd, FL_READ, cb, v);
315}
316
317void Fl::remove_fd(int n, int events) {
318 int i,j;
319 for (i=j=0; i<nfds; i++) {
320 if (fd[i].fd == n) {
321 short e = fd[i].events & ~events;
322 if (!e) continue; // if no events left, delete this fd
323 fd[i].events = e;
324 }
325 // move it down in the array if necessary:
326 if (j<i) {
327 fd[j]=fd[i];
328 }
329 j++;
330 }
331 nfds = j;
332
333 if (events & FL_READ) FD_CLR(unsigned(n), &fdsets[0]);
334 if (events & FL_WRITE) FD_CLR(unsigned(n), &fdsets[1]);
335 if (events & FL_EXCEPT) FD_CLR(unsigned(n), &fdsets[2]);
336}
337
338void Fl::remove_fd(int n) {
339 remove_fd(n, -1);
340}
341
342// these pointers are set by the Fl::lock() function:
343static void nothing() {}
344void (*fl_lock_function)() = nothing;
345void (*fl_unlock_function)() = nothing;
346
347static void* thread_message_;
348void* Fl::thread_message() {
349 void* r = thread_message_;
350 thread_message_ = 0;
351 return r;
352}
353
354IActiveIMMApp *fl_aimm = NULL;
355MSG fl_msg;
356
357// This is never called with time_to_wait < 0.0.
358// It *should* return negative on error, 0 if nothing happens before
359// timeout, and >0 if any callbacks were done. This version only
360// returns zero if nothing happens during a 0.0 timeout, otherwise
361// it returns 1.
362int fl_wait(double time_to_wait) {
363 int have_message = 0;
364
365 run_checks();
366
367 // idle processing
368 static char in_idle;
369 if (Fl::idle && !in_idle) {
370 in_idle = 1;
371 Fl::idle();
372 in_idle = 0;
373 }
374
375 if (nfds) {
376 // For WIN32 we need to poll for socket input FIRST, since
377 // the event queue is not something we can select() on...
378 timeval t;
379 t.tv_sec = 0;
380 t.tv_usec = 0;
381
382 fd_set fdt[3];
383 memcpy(fdt, fdsets, sizeof fdt); // one shot faster fdt init
384 if (get_wsock_mod()&& s_wsock_select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],&t)) {
385 // We got something - do the callback!
386 for (int i = 0; i < nfds; i ++) {
387 SOCKET f = fd[i].fd;
388 short revents = 0;
389 if (fl_wsk_fd_is_set(f, &fdt[0])) revents |= FL_READ;
390 if (fl_wsk_fd_is_set(f, &fdt[1])) revents |= FL_WRITE;
391 if (fl_wsk_fd_is_set(f, &fdt[2])) revents |= FL_EXCEPT;
392 if (fd[i].events & revents) fd[i].cb(f, fd[i].arg);
393 }
394 time_to_wait = 0.0; // just peek for any messages
395 } else {
396 // we need to check them periodically, so set a short timeout:
397 if (time_to_wait > .001) time_to_wait = .001;
398 }
399 }
400
401 if (Fl::idle || Fl::damage())
402 time_to_wait = 0.0;
403
404 // if there are no more windows and this timer is set
405 // to FOREVER, continue through or look up indefinitely
406 if (!Fl::first_window() && time_to_wait==1e20)
407 time_to_wait = 0.0;
408
409 fl_unlock_function();
410
411 time_to_wait = (time_to_wait > 10000 ? 10000 : time_to_wait);
412 int t_msec = (int) (time_to_wait * 1000.0 + 0.5);
413 MsgWaitForMultipleObjects(0, NULL, FALSE, t_msec, QS_ALLINPUT);
414
415 fl_lock_function();
416
417 // Execute the message we got, and all other pending messages:
418 // have_message = PeekMessage(&fl_msg, NULL, 0, 0, PM_REMOVE);
419 have_message = PeekMessageW(&fl_msg, NULL, 0, 0, PM_REMOVE);
420 if (have_message > 0) {
421 while (have_message != 0 && have_message != -1) {
422 if (fl_msg.message == fl_wake_msg) {
423 // Used for awaking wait() from another thread
424 thread_message_ = (void*)fl_msg.wParam;
425 Fl_Awake_Handler func;
426 void *data;
427 while (Fl::get_awake_handler_(func, data)==0) {
428 func(data);
429 }
430 }
431
DRC685f17e2011-07-28 09:23:00 +0000432 // Don't bother with key to character translation as we do
433 // it manually for simpley keyboard widgets. In fact, calling
434 // TranslateMessage() just makes it more difficult as it sets
435 // a bunch of internal state.
436 if (!use_simple_keyboard)
437 TranslateMessage(&fl_msg);
DRC2ff39b82011-07-28 08:38:59 +0000438 DispatchMessageW(&fl_msg);
439 have_message = PeekMessageW(&fl_msg, NULL, 0, 0, PM_REMOVE);
440 }
441 }
442 Fl::flush();
443
444 // This should return 0 if only timer events were handled:
445 return 1;
446}
447
448// fl_ready() is just like fl_wait(0.0) except no callbacks are done:
449int fl_ready() {
450 if (PeekMessage(&fl_msg, NULL, 0, 0, PM_NOREMOVE)) return 1;
451 if (!nfds) return 0;
452 timeval t;
453 t.tv_sec = 0;
454 t.tv_usec = 0;
455 fd_set fdt[3];
456 memcpy(fdt, fdsets, sizeof fdt);
457 return get_wsock_mod() ? s_wsock_select(0,&fdt[0],&fdt[1],&fdt[2],&t) : 0;
458}
459
460////////////////////////////////////////////////////////////////
461
462int Fl::x()
463{
464 RECT r;
465
466 SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
467 return r.left;
468}
469
470int Fl::y()
471{
472 RECT r;
473
474 SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
475 return r.top;
476}
477
478int Fl::h()
479{
480 RECT r;
481
482 SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
483 return r.bottom - r.top;
484}
485
486int Fl::w()
487{
488 RECT r;
489
490 SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
491 return r.right - r.left;
492}
493
494void Fl::get_mouse(int &x, int &y) {
495 POINT p;
496 GetCursorPos(&p);
497 x = p.x;
498 y = p.y;
499}
500
501////////////////////////////////////////////////////////////////
502// code used for selections:
503
504char *fl_selection_buffer[2];
505int fl_selection_length[2];
506int fl_selection_buffer_length[2];
507char fl_i_own_selection[2];
508
509UINT fl_get_lcid_codepage(LCID id)
510{
511 char buf[8];
512 buf[GetLocaleInfo(id, LOCALE_IDEFAULTANSICODEPAGE, buf, 8)] = 0;
513 return atol(buf);
514}
515
516// Convert \n -> \r\n
517class Lf2CrlfConvert {
518 char *out;
519 int outlen;
520public:
521 Lf2CrlfConvert(const char *in, int inlen) {
522 outlen = 0;
523 const char *i;
524 char *o;
525 int lencount;
526 // Predict size of \r\n conversion buffer
527 for ( i=in, lencount = inlen; lencount--; ) {
528 if ( *i == '\r' && *(i+1) == '\n' ) // leave \r\n untranslated
529 { i+=2; outlen+=2; }
530 else if ( *i == '\n' ) // \n by itself? leave room to insert \r
531 { i++; outlen+=2; }
532 else
533 { ++i; ++outlen; }
534 }
535 // Alloc conversion buffer + NULL
536 out = new char[outlen+1];
537 // Handle \n -> \r\n conversion
538 for ( i=in, o=out, lencount = inlen; lencount--; ) {
539 if ( *i == '\r' && *(i+1) == '\n' ) // leave \r\n untranslated
540 { *o++ = *i++; *o++ = *i++; }
541 else if ( *i == '\n' ) // \n by itself? insert \r
542 { *o++ = '\r'; *o++ = *i++; }
543 else
544 { *o++ = *i++; }
545 }
546 *o++ = 0;
547 }
548 ~Lf2CrlfConvert() {
549 delete[] out;
550 }
551 int GetLength() const { return(outlen); }
552 const char* GetValue() const { return(out); }
553};
554
DRC685f17e2011-07-28 09:23:00 +0000555void fl_update_clipboard(void) {
556 HWND hwnd = fl_xid(Fl::first_window());
557
558 if (!hwnd)
559 return;
560
561 if (!OpenClipboard(hwnd))
562 return;
563
564 EmptyClipboard();
565
566 int utf16_len = fl_utf8toUtf16(fl_selection_buffer[1],
567 fl_selection_length[1], 0, 0);
568
569 HGLOBAL hMem = GlobalAlloc(GHND, utf16_len * 2 + 2); // moveable and zero'ed mem alloc.
570 LPVOID memLock = GlobalLock(hMem);
571
572 fl_utf8toUtf16(fl_selection_buffer[1], fl_selection_length[1],
573 (unsigned short*) memLock, utf16_len + 1);
574
575 GlobalUnlock(hMem);
576 SetClipboardData(CF_UNICODETEXT, hMem);
577
578 CloseClipboard();
579
580 // In case Windows managed to lob of a WM_DESTROYCLIPBOARD during
581 // the above.
582 fl_i_own_selection[1] = 1;
583}
584
DRC2ff39b82011-07-28 08:38:59 +0000585// call this when you create a selection:
586void Fl::copy(const char *stuff, int len, int clipboard) {
587 if (!stuff || len<0) return;
588
589 // Convert \n -> \r\n (for old apps like Notepad, DOS)
590 Lf2CrlfConvert buf(stuff, len);
591 len = buf.GetLength();
592 stuff = buf.GetValue();
593
594 if (len+1 > fl_selection_buffer_length[clipboard]) {
595 delete[] fl_selection_buffer[clipboard];
596 fl_selection_buffer[clipboard] = new char[len+100];
597 fl_selection_buffer_length[clipboard] = len+100;
598 }
599 memcpy(fl_selection_buffer[clipboard], stuff, len);
600 fl_selection_buffer[clipboard][len] = 0; // needed for direct paste
601 fl_selection_length[clipboard] = len;
DRC685f17e2011-07-28 09:23:00 +0000602 fl_i_own_selection[clipboard] = 1;
603 if (clipboard)
604 fl_update_clipboard();
DRC2ff39b82011-07-28 08:38:59 +0000605}
606
607// Call this when a "paste" operation happens:
608void Fl::paste(Fl_Widget &receiver, int clipboard) {
609 if (!clipboard || fl_i_own_selection[clipboard]) {
610 // We already have it, do it quickly without window server.
611 // Notice that the text is clobbered if set_selection is
612 // called in response to FL_PASTE!
613
614 // Convert \r\n -> \n
615 char *i = fl_selection_buffer[clipboard];
616 if (i==0L) {
617 Fl::e_text = 0;
618 return;
619 }
620 Fl::e_text = new char[fl_selection_length[clipboard]+1];
621 char *o = Fl::e_text;
622 while (*i) {
623 if ( *i == '\r' && *(i+1) == '\n') i++;
624 else *o++ = *i++;
625 }
626 *o = 0;
627 Fl::e_length = o - Fl::e_text;
628 receiver.handle(FL_PASTE);
629 delete [] Fl::e_text;
630 Fl::e_text = 0;
631 } else {
632 if (!OpenClipboard(NULL)) return;
633 HANDLE h = GetClipboardData(CF_UNICODETEXT);
634 if (h) {
635 wchar_t *memLock = (wchar_t*) GlobalLock(h);
636 int utf16_len = wcslen(memLock);
637 Fl::e_text = (char*) malloc (utf16_len * 4 + 1);
638 int utf8_len = fl_utf8fromwc(Fl::e_text, utf16_len * 4, memLock, utf16_len);
639 *(Fl::e_text + utf8_len) = 0;
640 LPSTR a,b;
641 a = b = Fl::e_text;
642 while (*a) { // strip the CRLF pairs ($%$#@^)
643 if (*a == '\r' && a[1] == '\n') a++;
644 else *b++ = *a++;
645 }
646 *b = 0;
647 Fl::e_length = b - Fl::e_text;
648 receiver.handle(FL_PASTE);
649 GlobalUnlock(h);
650 free(Fl::e_text);
651 Fl::e_text = 0;
652 }
653 CloseClipboard();
654 }
655}
656
DRC685f17e2011-07-28 09:23:00 +0000657static HWND clipboard_wnd = 0;
658static HWND next_clipboard_wnd = 0;
659
660static bool initial_clipboard = true;
661
662void fl_clipboard_notify_change() {
663 // No need to do anything here...
664}
665
666void fl_clipboard_notify_target(HWND wnd) {
667 if (clipboard_wnd)
668 return;
669
670 // We get one fake WM_DRAWCLIPBOARD immediately, which we therefore
671 // need to ignore.
672 initial_clipboard = true;
673
674 clipboard_wnd = wnd;
675 next_clipboard_wnd = SetClipboardViewer(wnd);
676}
677
678void fl_clipboard_notify_untarget(HWND wnd) {
679 if (wnd != clipboard_wnd)
680 return;
681
682 ChangeClipboardChain(wnd, next_clipboard_wnd);
683 clipboard_wnd = next_clipboard_wnd = 0;
684
685 if (Fl::first_window())
686 fl_clipboard_notify_target(fl_xid(Fl::first_window()));
687}
688
DRC2ff39b82011-07-28 08:38:59 +0000689////////////////////////////////////////////////////////////////
690char fl_is_ime = 0;
691void fl_get_codepage()
692{
693 HKL hkl = GetKeyboardLayout(0);
694 TCHAR ld[8];
695
696 GetLocaleInfo (LOWORD(hkl), LOCALE_IDEFAULTANSICODEPAGE, ld, 6);
697 DWORD ccp = atol(ld);
698 fl_is_ime = 0;
699
700 fl_codepage = ccp;
701 if (fl_aimm) {
702 fl_aimm->GetCodePageA(GetKeyboardLayout(0), &fl_codepage);
703 } else if (get_imm_module() && flImmIsIME(hkl)) {
704 fl_is_ime = 1;
705 }
706}
707
DRC685f17e2011-07-28 09:23:00 +0000708void fl_update_focus(void)
709{
710 Fl_Widget *focus;
711 Fl_Window *win;
712
713 get_imm_module();
714
715 focus = Fl::grab();
716 if (!focus)
717 focus = Fl::focus();
718 if (!focus)
719 return;
720
721 // Grabs are special in that events are sent to the first
722 // available window
723 if (focus == Fl::grab())
724 win = Fl::first_window();
725 else {
726 win = focus->as_window();
727 if (!win)
728 win = focus->window();
729 }
730
731 if (!win) {
732 Fl::warning("Cannot find window for widget receiving focus");
733 return;
734 }
735
736 // No Win32 window created yet
737 if (!Fl_X::i(win) || !fl_xid(win))
738 return;
739
740 if (focus->simple_keyboard()) {
741 use_simple_keyboard = true;
742 if (flImmGetContext(fl_xid(win)) != 0)
743 flImmAssociateContextEx(fl_xid(win), 0, 0);
744 } else {
745 use_simple_keyboard = false;
746 if (flImmGetContext(fl_xid(win)) == 0)
747 flImmAssociateContextEx(fl_xid(win), 0, IACE_DEFAULT);
748 }
749}
750
DRC2ff39b82011-07-28 08:38:59 +0000751HWND fl_capture;
752
753static int mouse_event(Fl_Window *window, int what, int button,
754 WPARAM wParam, LPARAM lParam)
755{
756 static int px, py, pmx, pmy;
757 POINT pt;
758 Fl::e_x = pt.x = (signed short)LOWORD(lParam);
759 Fl::e_y = pt.y = (signed short)HIWORD(lParam);
760 ClientToScreen(fl_xid(window), &pt);
761 Fl::e_x_root = pt.x;
762 Fl::e_y_root = pt.y;
763#ifdef USE_CAPTURE_MOUSE_WIN
764 Fl_Window *mouse_window = window; // save "mouse window"
765#endif
766 while (window->parent()) {
767 Fl::e_x += window->x();
768 Fl::e_y += window->y();
769 window = window->window();
770 }
771
772 ulong state = Fl::e_state & 0xff0000; // keep shift key states
773#if 0
774 // mouse event reports some shift flags, perhaps save them?
775 if (wParam & MK_SHIFT) state |= FL_SHIFT;
776 if (wParam & MK_CONTROL) state |= FL_CTRL;
777#endif
778 if (wParam & MK_LBUTTON) state |= FL_BUTTON1;
779 if (wParam & MK_MBUTTON) state |= FL_BUTTON2;
780 if (wParam & MK_RBUTTON) state |= FL_BUTTON3;
781 Fl::e_state = state;
782
783 switch (what) {
784 case 1: // double-click
785 if (Fl::e_is_click) {Fl::e_clicks++; goto J1;}
786 case 0: // single-click
787 Fl::e_clicks = 0;
788 J1:
789#ifdef USE_CAPTURE_MOUSE_WIN
790 if (!fl_capture) SetCapture(fl_xid(mouse_window)); // use mouse window
791#else
792 if (!fl_capture) SetCapture(fl_xid(window)); // use main window
793#endif
794 Fl::e_keysym = FL_Button + button;
795 Fl::e_is_click = 1;
796 px = pmx = Fl::e_x_root; py = pmy = Fl::e_y_root;
797 return Fl::handle(FL_PUSH,window);
798
799 case 2: // release:
800 if (!fl_capture) ReleaseCapture();
801 Fl::e_keysym = FL_Button + button;
802 return Fl::handle(FL_RELEASE,window);
803
804 case 3: // move:
805 default: // avoid compiler warning
806 // MSWindows produces extra events even if mouse does not move, ignore em:
807 if (Fl::e_x_root == pmx && Fl::e_y_root == pmy) return 1;
808 pmx = Fl::e_x_root; pmy = Fl::e_y_root;
809 if (abs(Fl::e_x_root-px)>5 || abs(Fl::e_y_root-py)>5) Fl::e_is_click = 0;
810 return Fl::handle(FL_MOVE,window);
811
812 }
813}
814
815// convert a MSWindows VK_x to an Fltk (X) Keysym:
816// See also the inverse converter in Fl_get_key_win32.cxx
817// This table is in numeric order by VK:
818static const struct {unsigned short vk, fltk, extended;} vktab[] = {
819 {VK_BACK, FL_BackSpace},
820 {VK_TAB, FL_Tab},
821 {VK_CLEAR, FL_KP+'5', 0xff0b/*XK_Clear*/},
822 {VK_RETURN, FL_Enter, FL_KP_Enter},
823 {VK_SHIFT, FL_Shift_L, FL_Shift_R},
824 {VK_CONTROL, FL_Control_L, FL_Control_R},
825 {VK_MENU, FL_Alt_L, FL_Alt_R},
826 {VK_PAUSE, FL_Pause},
827 {VK_CAPITAL, FL_Caps_Lock},
828 {VK_ESCAPE, FL_Escape},
829 {VK_SPACE, ' '},
830 {VK_PRIOR, FL_KP+'9', FL_Page_Up},
831 {VK_NEXT, FL_KP+'3', FL_Page_Down},
832 {VK_END, FL_KP+'1', FL_End},
833 {VK_HOME, FL_KP+'7', FL_Home},
834 {VK_LEFT, FL_KP+'4', FL_Left},
835 {VK_UP, FL_KP+'8', FL_Up},
836 {VK_RIGHT, FL_KP+'6', FL_Right},
837 {VK_DOWN, FL_KP+'2', FL_Down},
838 {VK_SNAPSHOT, FL_Print}, // does not work on NT
839 {VK_INSERT, FL_KP+'0', FL_Insert},
840 {VK_DELETE, FL_KP+'.', FL_Delete},
841 {VK_LWIN, FL_Meta_L},
842 {VK_RWIN, FL_Meta_R},
843 {VK_APPS, FL_Menu},
844 {VK_SLEEP, FL_Sleep},
845 {VK_MULTIPLY, FL_KP+'*'},
846 {VK_ADD, FL_KP+'+'},
847 {VK_SUBTRACT, FL_KP+'-'},
848 {VK_DECIMAL, FL_KP+'.'},
849 {VK_DIVIDE, FL_KP+'/'},
850 {VK_NUMLOCK, FL_Num_Lock},
851 {VK_SCROLL, FL_Scroll_Lock},
852# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0500)
853 {VK_BROWSER_BACK, FL_Back},
854 {VK_BROWSER_FORWARD, FL_Forward},
855 {VK_BROWSER_REFRESH, FL_Refresh},
856 {VK_BROWSER_STOP, FL_Stop},
857 {VK_BROWSER_SEARCH, FL_Search},
858 {VK_BROWSER_FAVORITES, FL_Favorites},
859 {VK_BROWSER_HOME, FL_Home_Page},
860 {VK_VOLUME_MUTE, FL_Volume_Mute},
861 {VK_VOLUME_DOWN, FL_Volume_Down},
862 {VK_VOLUME_UP, FL_Volume_Up},
863 {VK_MEDIA_NEXT_TRACK, FL_Media_Next},
864 {VK_MEDIA_PREV_TRACK, FL_Media_Prev},
865 {VK_MEDIA_STOP, FL_Media_Stop},
866 {VK_MEDIA_PLAY_PAUSE, FL_Media_Play},
867 {VK_LAUNCH_MAIL, FL_Mail},
868#endif
869 {0xba, ';'},
870 {0xbb, '='},
871 {0xbc, ','},
872 {0xbd, '-'},
873 {0xbe, '.'},
874 {0xbf, '/'},
875 {0xc0, '`'},
876 {0xdb, '['},
877 {0xdc, '\\'},
878 {0xdd, ']'},
879 {0xde, '\''}
880};
881static int ms2fltk(int vk, int extended) {
882 static unsigned short vklut[256];
883 static unsigned short extendedlut[256];
884 if (!vklut[1]) { // init the table
885 unsigned int i;
886 for (i = 0; i < 256; i++) vklut[i] = tolower(i);
887 for (i=VK_F1; i<=VK_F16; i++) vklut[i] = i+(FL_F-(VK_F1-1));
888 for (i=VK_NUMPAD0; i<=VK_NUMPAD9; i++) vklut[i] = i+(FL_KP+'0'-VK_NUMPAD0);
889 for (i = 0; i < sizeof(vktab)/sizeof(*vktab); i++) {
890 vklut[vktab[i].vk] = vktab[i].fltk;
891 extendedlut[vktab[i].vk] = vktab[i].extended;
892 }
893 for (i = 0; i < 256; i++) if (!extendedlut[i]) extendedlut[i] = vklut[i];
894 }
895 return extended ? extendedlut[vk] : vklut[vk];
896}
897
DRC685f17e2011-07-28 09:23:00 +0000898static xchar msdead2fltk(xchar in)
899{
900 switch (in) {
901 case 0x0060: // GRAVE ACCENT
902 return 0x0300; // COMBINING GRAVE ACCENT
903 case 0x00b4: // ACUTE ACCENT
904 return 0x0301; // COMBINING ACUTE ACCENT
905 case 0x005e: // CIRCUMFLEX ACCENT
906 return 0x0302; // COMBINING CIRCUMFLEX ACCENT
907 case 0x007e: // TILDE
908 return 0x0303; // COMBINING TILDE
909 case 0x00a8: // DIAERESIS
910 return 0x0308; // COMBINING DIAERESIS
911 // FIXME: Windows dead key behaviour isn't documented and I don't have
912 // any more keyboards to test with...
913 }
914
915 // hope that Windows gave us something proper to begin with
916 return in;
917}
918
DRC2ff39b82011-07-28 08:38:59 +0000919#if USE_COLORMAP
920extern HPALETTE fl_select_palette(void); // in fl_color_win32.cxx
921#endif
922
923
924/////////////////////////////////////////////////////////////////////////////
925/// Win32 timers
926///
927
928struct Win32Timer
929{
930 UINT_PTR handle;
931 Fl_Timeout_Handler callback;
932 void *data;
933};
934static Win32Timer* win32_timers;
935static int win32_timer_alloc;
936static int win32_timer_used;
937static HWND s_TimerWnd;
938
939static void realloc_timers()
940{
941 if (win32_timer_alloc == 0) {
942 win32_timer_alloc = 8;
943 }
944 win32_timer_alloc *= 2;
945 Win32Timer* new_timers = new Win32Timer[win32_timer_alloc];
946 memset(new_timers, 0, sizeof(Win32Timer) * win32_timer_used);
947 memcpy(new_timers, win32_timers, sizeof(Win32Timer) * win32_timer_used);
948 Win32Timer* delete_me = win32_timers;
949 win32_timers = new_timers;
950 delete [] delete_me;
951}
952
953static void delete_timer(Win32Timer& t)
954{
955 KillTimer(s_TimerWnd, t.handle);
956 memset(&t, 0, sizeof(Win32Timer));
957}
958
959/// END TIMERS
960/////////////////////////////////////////////////////////////////////////////
961
962static Fl_Window* resize_bug_fix;
963
964extern void fl_save_pen(void);
965extern void fl_restore_pen(void);
966
967static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
968{
969 // Copy the message to fl_msg so add_handler code can see it, it is
970 // already there if this is called by DispatchMessage, but not if
971 // Windows calls this directly.
972 fl_msg.hwnd = hWnd;
973 fl_msg.message = uMsg;
974 fl_msg.wParam = wParam;
975 fl_msg.lParam = lParam;
976 //fl_msg.time = ???
977 //fl_msg.pt = ???
978 //fl_msg.lPrivate = ???
979
DRC685f17e2011-07-28 09:23:00 +0000980 MSG fl_orig_msg = fl_msg;
981
DRC2ff39b82011-07-28 08:38:59 +0000982 Fl_Window *window = fl_find(hWnd);
983
984 if (window) switch (uMsg) {
985
986 case WM_QUIT: // this should not happen?
987 Fl::fatal("WM_QUIT message");
988
989 case WM_CLOSE: // user clicked close box
990 Fl::handle(FL_CLOSE, window);
991 PostQuitMessage(0);
992 return 0;
993
994 case WM_SYNCPAINT :
995 case WM_NCPAINT :
996 case WM_ERASEBKGND :
997 // Andreas Weitl - WM_SYNCPAINT needs to be passed to DefWindowProc
998 // so that Windows can generate the proper paint messages...
999 // Similarly, WM_NCPAINT and WM_ERASEBKGND need this, too...
1000 break;
1001
1002 case WM_PAINT: {
1003 Fl_Region R;
1004 Fl_X *i = Fl_X::i(window);
1005 i->wait_for_expose = 0;
1006 char redraw_whole_window = false;
1007 if (!i->region && window->damage()) {
1008 // Redraw the whole window...
1009 i->region = CreateRectRgn(0, 0, window->w(), window->h());
1010 redraw_whole_window = true;
1011 }
1012
1013 // We need to merge WIN32's damage into FLTK's damage.
1014 R = CreateRectRgn(0,0,0,0);
1015 int r = GetUpdateRgn(hWnd,R,0);
1016 if (r==NULLREGION && !redraw_whole_window) {
1017 break;
1018 }
1019
1020 if (i->region) {
1021 // Also tell WIN32 that we are drawing someplace else as well...
1022 CombineRgn(i->region, i->region, R, RGN_OR);
1023 XDestroyRegion(R);
1024 } else {
1025 i->region = R;
1026 }
1027 if (window->type() == FL_DOUBLE_WINDOW) ValidateRgn(hWnd,0);
1028 else ValidateRgn(hWnd,i->region);
1029
1030 window->clear_damage((uchar)(window->damage()|FL_DAMAGE_EXPOSE));
1031 // These next two statements should not be here, so that all update
1032 // is deferred until Fl::flush() is called during idle. However WIN32
1033 // apparently is very unhappy if we don't obey it and draw right now.
1034 // Very annoying!
1035 fl_GetDC(hWnd); // Make sure we have a DC for this window...
1036 fl_save_pen();
1037 i->flush();
1038 fl_restore_pen();
1039 window->clear_damage();
1040 } return 0;
1041
1042 case WM_LBUTTONDOWN: mouse_event(window, 0, 1, wParam, lParam); return 0;
1043 case WM_LBUTTONDBLCLK:mouse_event(window, 1, 1, wParam, lParam); return 0;
1044 case WM_LBUTTONUP: mouse_event(window, 2, 1, wParam, lParam); return 0;
1045 case WM_MBUTTONDOWN: mouse_event(window, 0, 2, wParam, lParam); return 0;
1046 case WM_MBUTTONDBLCLK:mouse_event(window, 1, 2, wParam, lParam); return 0;
1047 case WM_MBUTTONUP: mouse_event(window, 2, 2, wParam, lParam); return 0;
1048 case WM_RBUTTONDOWN: mouse_event(window, 0, 3, wParam, lParam); return 0;
1049 case WM_RBUTTONDBLCLK:mouse_event(window, 1, 3, wParam, lParam); return 0;
1050 case WM_RBUTTONUP: mouse_event(window, 2, 3, wParam, lParam); return 0;
1051
1052 case WM_MOUSEMOVE:
1053#ifdef USE_TRACK_MOUSE
1054 if (track_mouse_win != window) {
1055 TRACKMOUSEEVENT tme;
1056 tme.cbSize = sizeof(TRACKMOUSEEVENT);
1057 tme.dwFlags = TME_LEAVE;
1058 tme.hwndTrack = hWnd;
1059 _TrackMouseEvent(&tme);
1060 track_mouse_win = window;
1061 }
1062#endif // USE_TRACK_MOUSE
1063 mouse_event(window, 3, 0, wParam, lParam);
1064 return 0;
1065
1066 case WM_MOUSELEAVE:
1067 if (track_mouse_win == window) { // we left the top level window !
1068 Fl_Window *tw = window;
1069 while (tw->parent()) tw = tw->window(); // find top level window
1070 Fl::belowmouse(0);
1071 Fl::handle(FL_LEAVE, tw);
1072 }
1073 track_mouse_win = 0; // force TrackMouseEvent() restart
1074 break;
1075
1076 case WM_SETFOCUS:
1077 Fl::handle(FL_FOCUS, window);
1078 break;
1079
1080 case WM_KILLFOCUS:
1081 Fl::handle(FL_UNFOCUS, window);
1082 Fl::flush(); // it never returns to main loop when deactivated...
1083 break;
1084
1085 case WM_SHOWWINDOW:
1086 if (!window->parent()) {
1087 Fl::handle(wParam ? FL_SHOW : FL_HIDE, window);
1088 }
1089 break;
1090
1091 case WM_ACTIVATEAPP:
1092 // From eric@vfx.sel.sony.com, we should process WM_ACTIVATEAPP
1093 // messages to restore the correct state of the shift/ctrl/alt/lock
1094 // keys... Added control, shift, alt, and meta keys, and changed
1095 // to use GetAsyncKeyState and do it when wParam is 1
1096 // (that means we have focus...)
1097 if (wParam)
1098 {
1099 ulong state = 0;
1100 if (GetAsyncKeyState(VK_CAPITAL)) state |= FL_CAPS_LOCK;
1101 if (GetAsyncKeyState(VK_NUMLOCK)) state |= FL_NUM_LOCK;
1102 if (GetAsyncKeyState(VK_SCROLL)) state |= FL_SCROLL_LOCK;
1103 if (GetAsyncKeyState(VK_CONTROL)&~1) state |= FL_CTRL;
1104 if (GetAsyncKeyState(VK_SHIFT)&~1) state |= FL_SHIFT;
1105 if (GetAsyncKeyState(VK_MENU)) state |= FL_ALT;
1106 if ((GetAsyncKeyState(VK_LWIN)|GetAsyncKeyState(VK_RWIN))&~1) state |= FL_META;
1107 Fl::e_state = state;
1108 return 0;
1109 }
1110 break;
1111
1112 case WM_INPUTLANGCHANGE:
1113 fl_get_codepage();
1114 break;
1115 case WM_IME_COMPOSITION:
1116// if (!fl_is_nt4() && lParam & GCS_RESULTCLAUSE) {
1117// HIMC himc = ImmGetContext(hWnd);
1118// wlen = ImmGetCompositionStringW(himc, GCS_RESULTSTR,
1119// wbuf, sizeof(wbuf)) / sizeof(short);
1120// if (wlen < 0) wlen = 0;
1121// wbuf[wlen] = 0;
1122// ImmReleaseContext(hWnd, himc);
1123// }
1124 break;
1125 case WM_KEYDOWN:
1126 case WM_SYSKEYDOWN:
1127 case WM_KEYUP:
1128 case WM_SYSKEYUP:
1129 // save the keysym until we figure out the characters:
1130 Fl::e_keysym = Fl::e_original_keysym = ms2fltk(wParam,lParam&(1<<24));
1131 // See if TranslateMessage turned it into a WM_*CHAR message:
1132 if (PeekMessageW(&fl_msg, hWnd, WM_CHAR, WM_SYSDEADCHAR, PM_REMOVE))
1133 {
1134 uMsg = fl_msg.message;
1135 wParam = fl_msg.wParam;
1136 lParam = fl_msg.lParam;
1137 }
1138 case WM_DEADCHAR:
1139 case WM_SYSDEADCHAR:
1140 case WM_CHAR:
1141 case WM_SYSCHAR: {
1142 ulong state = Fl::e_state & 0xff000000; // keep the mouse button state
1143 // if GetKeyState is expensive we might want to comment some of these out:
1144 if (GetKeyState(VK_SHIFT)&~1) state |= FL_SHIFT;
1145 if (GetKeyState(VK_CAPITAL)) state |= FL_CAPS_LOCK;
1146 if (GetKeyState(VK_CONTROL)&~1) state |= FL_CTRL;
1147 // Alt gets reported for the Alt-GR switch on foreign keyboards.
1148 // so we need to check the event as well to get it right:
1149 if ((lParam&(1<<29)) //same as GetKeyState(VK_MENU)
1150 && uMsg != WM_CHAR) state |= FL_ALT;
1151 if (GetKeyState(VK_NUMLOCK)) state |= FL_NUM_LOCK;
1152 if ((GetKeyState(VK_LWIN)|GetKeyState(VK_RWIN))&~1) {
1153 // WIN32 bug? GetKeyState returns garbage if the user hit the
1154 // meta key to pop up start menu. Sigh.
1155 if ((GetAsyncKeyState(VK_LWIN)|GetAsyncKeyState(VK_RWIN))&~1)
1156 state |= FL_META;
1157 }
1158 if (GetKeyState(VK_SCROLL)) state |= FL_SCROLL_LOCK;
1159 Fl::e_state = state;
1160 static char buffer[1024];
DRC2ff39b82011-07-28 08:38:59 +00001161
DRC685f17e2011-07-28 09:23:00 +00001162 if (use_simple_keyboard) {
1163 BYTE keystate[256];
1164 WCHAR wbuf[8];
1165 int ret;
1166
1167 // I'm not sure if we ever get WM_CHAR (& friends) without an initial
1168 // WM_KEYDOWN (& friends), but if we do then we should not send such
1169 // side band events to simple keyboard widgets.
1170 if ((fl_orig_msg.message != WM_KEYDOWN) &&
1171 (fl_orig_msg.message != WM_SYSKEYDOWN) &&
1172 (fl_orig_msg.message != WM_KEYUP) &&
1173 (fl_orig_msg.message != WM_SYSKEYUP))
1174 break;
1175
1176 GetKeyboardState(keystate);
1177
1178 // Pressing Ctrl wreaks havoc with the symbol lookup, so turn that off.
1179 // But AltGr shows up as Ctrl+Alt in Windows, so keep Ctrl if Alt is
1180 // active.
1181 if (!(keystate[VK_MENU] & (1<<31)))
1182 keystate[VK_CONTROL] = keystate[VK_LCONTROL] = keystate[VK_RCONTROL] = 0;
1183
1184 // We cannot inspect or modify Windows' internal state of the keyboard
1185 // so we have to try to infer information from ToUnicode() and wedge
1186 // things into a known state.
1187 for (int i = 0;i < 2;i++) {
1188 ret = ToUnicode(fl_orig_msg.wParam, 0, keystate, wbuf,
1189 sizeof(wbuf)/sizeof(wbuf[0]), 0);
1190
1191 // No symbol for this key (or unexpected length)
1192 if ((ret == 0) || (ret < -1)) {
1193 buffer[0] = 0;
1194 Fl::e_length = 0;
1195 break;
1196 }
1197
1198 // A dead key. Convert this to a Unicode combining character so
1199 // that the application can tell the difference between dead and
1200 // normal keys.
1201 if (ret == -1) {
1202 xchar u = (xchar) msdead2fltk(wbuf[0]);
1203 Fl::e_length = fl_utf8fromwc(buffer, 1024, &u, 1);
1204 buffer[Fl::e_length] = 0;
1205 break;
1206 }
1207
1208 // If we have two characters (or more) from ToUnicode(), that's
1209 // an invalid sequence. One character chould mean a proper symbol,
1210 // or it could mean a composed one. In both cases we need to call
1211 // ToUnicode() again to get something sane.
1212 if (i == 0)
1213 continue;
1214
1215 // We should now have something sane. Give whatever we have to the
1216 // application.
1217 Fl::e_length = fl_utf8fromwc(buffer, 1024, wbuf, ret);
1218 buffer[Fl::e_length] = 0;
1219 }
1220 } else if (uMsg == WM_CHAR || uMsg == WM_SYSCHAR) {
DRC2ff39b82011-07-28 08:38:59 +00001221 xchar u = (xchar) wParam;
1222// Fl::e_length = fl_unicode2utf(&u, 1, buffer);
1223 Fl::e_length = fl_utf8fromwc(buffer, 1024, &u, 1);
1224 buffer[Fl::e_length] = 0;
DRC685f17e2011-07-28 09:23:00 +00001225 } else {
1226 buffer[0] = 0;
1227 Fl::e_length = 0;
1228 }
DRC2ff39b82011-07-28 08:38:59 +00001229
DRC685f17e2011-07-28 09:23:00 +00001230 // The keypad area is a bit odd in that we need to change the keysym
1231 // to properly indicate what the user meant, unlike other keys where
1232 // we normally change the text and keep keysym stable.
1233 if (Fl::e_keysym >= FL_KP && Fl::e_keysym <= FL_KP_Last) {
1234 // The initial mapping tables give us a keysym that corresponds to
1235 // numlock being on, so we only do something when it is off.
1236 if (!(state & FL_NUM_LOCK)) {
DRC2ff39b82011-07-28 08:38:59 +00001237 switch (Fl::e_keysym) {
1238 case FL_KP + '0' :
1239 Fl::e_keysym = FL_Insert;
1240 break;
1241 case FL_KP + '1' :
1242 Fl::e_keysym = FL_End;
1243 break;
1244 case FL_KP + '2' :
1245 Fl::e_keysym = FL_Down;
1246 break;
1247 case FL_KP + '3' :
1248 Fl::e_keysym = FL_Page_Down;
1249 break;
1250 case FL_KP + '4' :
1251 Fl::e_keysym = FL_Left;
1252 break;
1253 case FL_KP + '6' :
1254 Fl::e_keysym = FL_Right;
1255 break;
1256 case FL_KP + '7' :
1257 Fl::e_keysym = FL_Home;
1258 break;
1259 case FL_KP + '8' :
1260 Fl::e_keysym = FL_Up;
1261 break;
1262 case FL_KP + '9' :
1263 Fl::e_keysym = FL_Page_Up;
1264 break;
1265 case FL_KP + '.' :
1266 Fl::e_keysym = FL_Delete;
1267 break;
DRC2ff39b82011-07-28 08:38:59 +00001268 }
1269 }
DRC2ff39b82011-07-28 08:38:59 +00001270 }
DRC685f17e2011-07-28 09:23:00 +00001271
DRC2ff39b82011-07-28 08:38:59 +00001272 Fl::e_text = buffer;
1273 if (lParam & (1<<31)) { // key up events.
1274 if (Fl::handle(FL_KEYUP, window)) return 0;
1275 break;
1276 }
1277 // for (int i = lParam&0xff; i--;)
1278 while (window->parent()) window = window->window();
1279 if (Fl::handle(FL_KEYBOARD,window)) {
1280 if (uMsg==WM_DEADCHAR || uMsg==WM_SYSDEADCHAR)
1281 Fl::compose_state = 1;
1282 return 0;
1283 }
1284 break;}
1285
1286 case WM_MOUSEWHEEL: {
1287 static int delta = 0; // running total of all motion
1288 delta += (SHORT)(HIWORD(wParam));
DRC685f17e2011-07-28 09:23:00 +00001289 Fl::e_dx = 0;
DRC2ff39b82011-07-28 08:38:59 +00001290 Fl::e_dy = -delta / WHEEL_DELTA;
1291 delta += Fl::e_dy * WHEEL_DELTA;
1292 if (Fl::e_dy) Fl::handle(FL_MOUSEWHEEL, window);
1293 return 0;
1294 }
1295
DRC685f17e2011-07-28 09:23:00 +00001296// This is only defined on Vista and upwards...
1297#ifndef WM_MOUSEHWHEEL
1298#define WM_MOUSEHWHEEL 0x020E
1299#endif
1300
1301 case WM_MOUSEHWHEEL: {
1302 static int delta = 0; // running total of all motion
1303 delta += (SHORT)(HIWORD(wParam));
1304 Fl::e_dy = 0;
1305 Fl::e_dx = delta / WHEEL_DELTA;
1306 delta -= Fl::e_dx * WHEEL_DELTA;
1307 if (Fl::e_dx) Fl::handle(FL_MOUSEWHEEL, window);
1308 return 0;
1309 }
1310
DRC2ff39b82011-07-28 08:38:59 +00001311 case WM_GETMINMAXINFO:
1312 Fl_X::i(window)->set_minmax((LPMINMAXINFO)lParam);
1313 break;
1314
1315 case WM_SIZE:
1316 if (!window->parent()) {
1317 if (wParam == SIZE_MINIMIZED || wParam == SIZE_MAXHIDE) {
1318 Fl::handle(FL_HIDE, window);
1319 } else {
1320 Fl::handle(FL_SHOW, window);
1321 resize_bug_fix = window;
1322 window->size(LOWORD(lParam), HIWORD(lParam));
1323 }
1324 }
1325 break;
1326
1327 case WM_MOVE: {
1328 resize_bug_fix = window;
1329 int nx = LOWORD(lParam);
1330 int ny = HIWORD(lParam);
1331 if (nx & 0x8000) nx -= 65536;
1332 if (ny & 0x8000) ny -= 65536;
1333 window->position(nx, ny); }
1334 break;
1335
1336 case WM_SETCURSOR:
1337 if (LOWORD(lParam) == HTCLIENT) {
1338 while (window->parent()) window = window->window();
1339 SetCursor(Fl_X::i(window)->cursor);
1340 return 0;
1341 }
1342 break;
1343
1344#if USE_COLORMAP
1345 case WM_QUERYNEWPALETTE :
1346 fl_GetDC(hWnd);
1347 if (fl_select_palette()) InvalidateRect(hWnd, NULL, FALSE);
1348 break;
1349
1350 case WM_PALETTECHANGED:
1351 fl_GetDC(hWnd);
1352 if ((HWND)wParam != hWnd && fl_select_palette()) UpdateColors(fl_gc);
1353 break;
1354
1355 case WM_CREATE :
1356 fl_GetDC(hWnd);
1357 fl_select_palette();
1358 break;
1359#endif
1360
1361 case WM_DESTROYCLIPBOARD:
1362 fl_i_own_selection[1] = 0;
1363 return 1;
1364
DRC685f17e2011-07-28 09:23:00 +00001365 case WM_CHANGECBCHAIN:
1366 if ((hWnd == clipboard_wnd) &&
1367 (next_clipboard_wnd == (HWND)wParam)) {
1368 next_clipboard_wnd = (HWND)lParam;
1369 return 0;
DRC2ff39b82011-07-28 08:38:59 +00001370 }
DRC685f17e2011-07-28 09:23:00 +00001371 break;
DRC2ff39b82011-07-28 08:38:59 +00001372
DRC685f17e2011-07-28 09:23:00 +00001373 case WM_DRAWCLIPBOARD:
1374 // When the clipboard moves between two FLTK windows,
1375 // fl_i_own_selection will temporarily be false as we are
1376 // processing this message. Hence the need to use fl_find().
1377 if (!initial_clipboard && !fl_find(GetClipboardOwner()))
1378 fl_trigger_clipboard_notify(1);
1379 initial_clipboard = false;
1380
1381 if (next_clipboard_wnd)
1382 SendMessage(next_clipboard_wnd, WM_DRAWCLIPBOARD, wParam, lParam);
1383
1384 return 0;
DRC2ff39b82011-07-28 08:38:59 +00001385
1386 default:
1387 if (Fl::handle(0,0)) return 0;
1388 break;
1389 }
1390
1391
1392 return DefWindowProcW(hWnd, uMsg, wParam, lParam);
1393}
1394
1395////////////////////////////////////////////////////////////////
1396// This function gets the dimensions of the top/left borders and
1397// the title bar, if there is one, based on the FL_BORDER, FL_MODAL
1398// and FL_NONMODAL flags, and on the window's size range.
1399// It returns the following values:
1400//
1401// value | border | title bar
1402// 0 | none | no
1403// 1 | fix | yes
1404// 2 | size | yes
1405
1406int Fl_X::fake_X_wm(const Fl_Window* w,int &X,int &Y, int &bt,int &bx, int &by) {
1407 int W, H, xoff, yoff, dx, dy;
1408 int ret = bx = by = bt = 0;
1409
1410 int fallback = 1;
1411 if (!w->parent()) {
1412 HWND hwnd = fl_xid(w);
1413 if (hwnd) {
1414 // The block below calculates the window borders by requesting the
1415 // required decorated window rectangle for a desired client rectangle.
1416 // If any part of the function above fails, we will drop to a
1417 // fallback to get the best guess which is always available.
1418 HWND hwnd = fl_xid(w);
1419 // request the style flags of this window, as WIN32 sees them
1420 LONG style = GetWindowLong(hwnd, GWL_STYLE);
1421 LONG exstyle = GetWindowLong(hwnd, GWL_EXSTYLE);
1422 RECT r;
1423 r.left = w->x();
1424 r.top = w->y();
1425 r.right = w->x()+w->w();
1426 r.bottom = w->y()+w->h();
1427 // get the decoration rectangle for the desired client rectangle
1428 BOOL ok = AdjustWindowRectEx(&r, style, FALSE, exstyle);
1429 if (ok) {
1430 X = r.left;
1431 Y = r.top;
1432 W = r.right - r.left;
1433 H = r.bottom - r.top;
1434 bx = w->x() - r.left;
1435 by = r.bottom - w->y() - w->h(); // height of the bootm frame
1436 bt = w->y() - r.top - by; // height of top caption bar
1437 xoff = bx;
1438 yoff = by + bt;
1439 dx = W - w->w();
1440 dy = H - w->h();
1441 if (w->size_range_set && (w->maxw != w->minw || w->maxh != w->minh))
1442 ret = 2;
1443 else
1444 ret = 1;
1445 fallback = 0;
1446 }
1447 }
1448 }
1449 // This is the original (pre 1.1.7) routine to calculate window border sizes.
1450 if (fallback) {
1451 if (w->border() && !w->parent()) {
1452 if (w->size_range_set && (w->maxw != w->minw || w->maxh != w->minh)) {
1453 ret = 2;
1454 bx = GetSystemMetrics(SM_CXSIZEFRAME);
1455 by = GetSystemMetrics(SM_CYSIZEFRAME);
1456 } else {
1457 ret = 1;
1458 bx = GetSystemMetrics(SM_CXFIXEDFRAME);
1459 by = GetSystemMetrics(SM_CYFIXEDFRAME);
1460 }
1461 bt = GetSystemMetrics(SM_CYCAPTION);
1462 }
1463 //The coordinates of the whole window, including non-client area
1464 xoff = bx;
1465 yoff = by + bt;
1466 dx = 2*bx;
1467 dy = 2*by + bt;
1468 X = w->x()-xoff;
1469 Y = w->y()-yoff;
1470 W = w->w()+dx;
1471 H = w->h()+dy;
1472 }
1473
1474 //Proceed to positioning the window fully inside the screen, if possible
1475 //Make border's lower right corner visible
1476 int scr_x, scr_y, scr_w, scr_h;
1477 Fl::screen_xywh(scr_x, scr_y, scr_w, scr_h, X, Y);
1478 if (scr_x+scr_w < X+W) X = scr_x+scr_w - W;
1479 if (scr_y+scr_h < Y+H) Y = scr_y+scr_h - H;
1480 //Make border's upper left corner visible
1481 if (X<scr_x) X = scr_x;
1482 if (Y<scr_y) Y = scr_y;
1483 //Make client area's lower right corner visible
1484 if (scr_x+scr_w < X+dx+ w->w()) X = scr_x+scr_w - w->w() - dx;
1485 if (scr_y+scr_h < Y+dy+ w->h()) Y = scr_y+scr_h - w->h() - dy;
1486 //Make client area's upper left corner visible
1487 if (X+xoff < scr_x) X = scr_x-xoff;
1488 if (Y+yoff < scr_y) Y = scr_y-yoff;
1489 //Return the client area's top left corner in (X,Y)
1490 X+=xoff;
1491 Y+=yoff;
1492
DRC685f17e2011-07-28 09:23:00 +00001493 if (w->flags() & Fl_Widget::FULLSCREEN) {
1494 X = Y = 0;
1495 bx = by = bt = 0;
1496 }
1497
DRC2ff39b82011-07-28 08:38:59 +00001498 return ret;
1499}
1500
1501////////////////////////////////////////////////////////////////
1502
1503void Fl_Window::resize(int X,int Y,int W,int H) {
1504 UINT flags = SWP_NOSENDCHANGING | SWP_NOZORDER
1505 | SWP_NOACTIVATE | SWP_NOOWNERZORDER;
1506 int is_a_resize = (W != w() || H != h());
1507 int resize_from_program = (this != resize_bug_fix);
1508 if (!resize_from_program) resize_bug_fix = 0;
1509 if (X != x() || Y != y()) {
1510 force_position(1);
1511 } else {
1512 if (!is_a_resize) return;
1513 flags |= SWP_NOMOVE;
1514 }
1515 if (is_a_resize) {
1516 Fl_Group::resize(X,Y,W,H);
1517 if (visible_r()) {
1518 redraw();
1519 // only wait for exposure if this window has a size - a window
1520 // with no width or height will never get an exposure event
1521 if (i && W>0 && H>0)
1522 i->wait_for_expose = 1;
1523 }
1524 } else {
1525 x(X); y(Y);
1526 flags |= SWP_NOSIZE;
1527 }
1528 if (!border()) flags |= SWP_NOACTIVATE;
1529 if (resize_from_program && shown()) {
1530 if (!resizable()) size_range(w(),h(),w(),h());
1531 int dummy_x, dummy_y, bt, bx, by;
1532 //Ignore window managing when resizing, so that windows (and more
1533 //specifically menus) can be moved offscreen.
1534 if (Fl_X::fake_X_wm(this, dummy_x, dummy_y, bt, bx, by)) {
1535 X -= bx;
1536 Y -= by+bt;
1537 W += 2*bx;
1538 H += 2*by+bt;
1539 }
1540 // avoid zero size windows. A zero sized window on Win32
1541 // will cause continouly new redraw events.
1542 if (W<=0) W = 1;
1543 if (H<=0) H = 1;
1544 SetWindowPos(i->xid, 0, X, Y, W, H, flags);
1545 }
1546}
1547
DRC685f17e2011-07-28 09:23:00 +00001548static void make_fullscreen(Fl_Window *w, Window xid, int X, int Y, int W, int H) {
1549 int sx, sy, sw, sh;
1550 Fl::screen_xywh(sx, sy, sw, sh, X, Y, W, H);
1551 DWORD flags = GetWindowLong(xid, GWL_STYLE);
1552 flags = flags & ~(WS_THICKFRAME|WS_CAPTION);
1553 SetWindowLong(xid, GWL_STYLE, flags);
1554 // SWP_NOSENDCHANGING is so that we can override size limits
1555 SetWindowPos(xid, HWND_TOP, sx, sy, sw, sh, SWP_NOSENDCHANGING | SWP_FRAMECHANGED);
1556}
1557
1558void fullscreen_x(Fl_Window *w) {
1559 w->_set_fullscreen();
1560 make_fullscreen(w, fl_xid(w), w->x(), w->y(), w->w(), w->h());
1561 Fl::handle(FL_FULLSCREEN, w);
1562}
1563
1564void fullscreen_off_x(Fl_Window *w, int X, int Y, int W, int H) {
1565 w->_clear_fullscreen();
1566 DWORD style = GetWindowLong(fl_xid(w), GWL_STYLE);
1567 // Remove the xid temporarily so that Fl_X::fake_X_wm() behaves like it
1568 // does in Fl_X::make().
1569 HWND xid = fl_xid(w);
1570 Fl_X::i(w)->xid = NULL;
1571 int x, y, bt, bx, by;
1572 switch (Fl_X::fake_X_wm(w, x, y, bt, bx, by)) {
1573 case 0:
1574 break;
1575 case 1:
1576 style |= WS_CAPTION;
1577 break;
1578 case 2:
1579 if (w->border()) {
1580 style |= WS_THICKFRAME | WS_CAPTION;
1581 }
1582 break;
1583 }
1584 Fl_X::i(w)->xid = xid;
1585 // Adjust for decorations (but not if that puts the decorations
1586 // outside the screen)
1587 if ((X != w->x()) || (Y != w->y())) {
1588 X -= bx;
1589 Y -= by+bt;
1590 }
1591 W += bx*2;
1592 H += by*2+bt;
1593 SetWindowLong(fl_xid(w), GWL_STYLE, style);
1594 SetWindowPos(fl_xid(w), 0, X, Y, W, H,
1595 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED);
1596 Fl::handle(FL_FULLSCREEN, w);
1597}
1598
1599
DRC2ff39b82011-07-28 08:38:59 +00001600////////////////////////////////////////////////////////////////
1601
1602/*
1603 * This silly little class remembers the name of all window classes
1604 * we register to avoid double registration. It has the added bonus
1605 * of freeing everything on application close as well.
1606 */
1607class NameList {
1608public:
1609 NameList() { name = (char**)malloc(sizeof(char**)); NName = 1; nName = 0; }
1610 ~NameList() {
1611 int i;
1612 for (i=0; i<nName; i++) free(name[i]);
1613 if (name) free(name);
1614 }
1615 void add_name(const char *n) {
1616 if (NName==nName) {
1617 NName += 5;
1618 name = (char**)realloc(name, NName * sizeof(char*));
1619 }
1620 name[nName++] = strdup(n);
1621 }
1622 char has_name(const char *n) {
1623 int i;
1624 for (i=0; i<nName; i++) {
1625 if (strcmp(name[i], n)==0) return 1;
1626 }
1627 return 0;
1628 }
1629private:
1630 char **name;
1631 int nName, NName;
1632};
1633
1634void fl_fix_focus(); // in Fl.cxx
1635
1636char fl_show_iconic; // hack for Fl_Window::iconic()
1637// int fl_background_pixel = -1; // color to use for background
DRC2ff39b82011-07-28 08:38:59 +00001638UINT fl_wake_msg = 0;
1639int fl_disable_transient_for; // secret method of removing TRANSIENT_FOR
1640
1641Fl_X* Fl_X::make(Fl_Window* w) {
1642 Fl_Group::current(0); // get rid of very common user bug: forgot end()
1643
1644 // if the window is a subwindow and our parent is not mapped yet, we
1645 // mark this window visible, so that mapping the parent at a later
1646 // point in time will call this function again to finally map the subwindow.
1647 if (w->parent() && !Fl_X::i(w->window())) {
1648 w->set_visible();
1649 return 0L;
1650 }
1651
1652 static NameList class_name_list;
1653 static const char *first_class_name = 0L;
1654 const char *class_name = w->xclass();
1655 if (!class_name) class_name = first_class_name; // reuse first class name used
1656 if (!class_name) class_name = "FLTK"; // default to create a "FLTK" WNDCLASS
1657 if (!first_class_name) {
1658 first_class_name = class_name;
1659 }
1660
1661 wchar_t class_namew[100]; // (limited) buffer for Windows class name
1662
1663 // convert UTF-8 class_name to wchar_t for RegisterClassExW and CreateWindowExW
1664
1665 fl_utf8toUtf16(class_name,strlen(class_name), // in
1666 (unsigned short*)class_namew, // out
1667 sizeof(class_namew)/sizeof(wchar_t)); // max. size
1668
1669 if (!class_name_list.has_name(class_name)) {
1670 WNDCLASSEXW wcw;
1671 memset(&wcw, 0, sizeof(wcw));
1672 wcw.cbSize = sizeof(WNDCLASSEXW);
1673
1674 // Documentation states a device context consumes about 800 bytes
1675 // of memory... so who cares? If 800 bytes per window is what it
1676 // takes to speed things up, I'm game.
1677 //wc.style = CS_HREDRAW | CS_VREDRAW | CS_CLASSDC | CS_DBLCLKS;
1678 wcw.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS;
1679 wcw.lpfnWndProc = (WNDPROC)WndProc;
1680 wcw.cbClsExtra = wcw.cbWndExtra = 0;
1681 wcw.hInstance = fl_display;
1682 if (!w->icon())
1683 w->icon((void *)LoadIcon(NULL, IDI_APPLICATION));
1684 wcw.hIcon = wcw.hIconSm = (HICON)w->icon();
DRC685f17e2011-07-28 09:23:00 +00001685 wcw.hCursor = LoadCursor(NULL, IDC_ARROW);
DRC2ff39b82011-07-28 08:38:59 +00001686 //uchar r,g,b; Fl::get_color(FL_GRAY,r,g,b);
1687 //wc.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(r,g,b));
1688 wcw.hbrBackground = NULL;
1689 wcw.lpszMenuName = NULL;
1690 wcw.lpszClassName = class_namew;
1691 RegisterClassExW(&wcw);
1692 class_name_list.add_name(class_name);
1693 }
1694
1695 const wchar_t* message_namew = L"FLTK::ThreadWakeup";
1696 if (!fl_wake_msg) fl_wake_msg = RegisterWindowMessageW(message_namew);
1697
1698 HWND parent;
1699 DWORD style = WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
1700 DWORD styleEx = WS_EX_LEFT;
1701
1702 int xp = w->x();
1703 int yp = w->y();
1704 int wp = w->w();
1705 int hp = w->h();
1706
1707 int showit = 1;
1708
1709 if (w->parent()) {
1710 style |= WS_CHILD;
1711 styleEx |= WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT;
1712 parent = fl_xid(w->window());
1713 } else {
1714 if (!w->size_range_set) {
1715 if (w->resizable()) {
1716 Fl_Widget *o = w->resizable();
1717 int minw = o->w(); if (minw > 100) minw = 100;
1718 int minh = o->h(); if (minh > 100) minh = 100;
1719 w->size_range(w->w() - o->w() + minw, w->h() - o->h() + minh, 0, 0);
1720 } else {
1721 w->size_range(w->w(), w->h(), w->w(), w->h());
1722 }
1723 }
1724 styleEx |= WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT;
1725 int xwm = xp , ywm = yp , bt, bx, by;
1726 switch (fake_X_wm(w, xwm, ywm, bt, bx, by)) {
1727 // No border (used for menus)
DRC685f17e2011-07-28 09:23:00 +00001728 case 0:
1729 style |= WS_POPUP;
1730 styleEx |= WS_EX_TOOLWINDOW;
DRC2ff39b82011-07-28 08:38:59 +00001731 break;
1732
1733 // Thin border and title bar
DRC685f17e2011-07-28 09:23:00 +00001734 case 1:
1735 style |= WS_DLGFRAME | WS_CAPTION;
1736 if (!w->modal())
1737 style |= WS_SYSMENU | WS_MINIMIZEBOX;
1738 break;
DRC2ff39b82011-07-28 08:38:59 +00001739
1740 // Thick, resizable border and title bar, with maximize button
DRC685f17e2011-07-28 09:23:00 +00001741 case 2:
1742 style |= WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_CAPTION;
1743 if (!w->modal())
1744 style |= WS_MINIMIZEBOX;
1745 break;
DRC2ff39b82011-07-28 08:38:59 +00001746 }
1747 if (by+bt) {
DRC2ff39b82011-07-28 08:38:59 +00001748 wp += 2*bx;
1749 hp += 2*by+bt;
1750 }
1751 if (!w->force_position()) {
1752 xp = yp = CW_USEDEFAULT;
1753 } else {
1754 if (!Fl::grab()) {
1755 xp = xwm; yp = ywm;
1756 w->x(xp);w->y(yp);
1757 }
1758 xp -= bx;
1759 yp -= by+bt;
1760 }
1761
1762 parent = 0;
1763 if (w->non_modal() && Fl_X::first && !fl_disable_transient_for) {
1764 // find some other window to be "transient for":
1765 Fl_Window* w = Fl_X::first->w;
1766 while (w->parent()) w = w->window();
1767 parent = fl_xid(w);
1768 if (!w->visible()) showit = 0;
1769 } else if (Fl::grab()) parent = fl_xid(Fl::grab());
1770 }
1771
1772 Fl_X* x = new Fl_X;
1773 x->other_xid = 0;
1774 x->setwindow(w);
1775 x->region = 0;
1776 x->private_dc = 0;
DRC685f17e2011-07-28 09:23:00 +00001777 x->cursor = LoadCursor(NULL, IDC_ARROW);
DRC2ff39b82011-07-28 08:38:59 +00001778 if (!fl_codepage) fl_get_codepage();
1779
1780 WCHAR *lab = NULL;
1781 if (w->label()) {
1782 int l = strlen(w->label());
1783// lab = (WCHAR*) malloc((l + 1) * sizeof(short));
1784// l = fl_utf2unicode((unsigned char*)w->label(), l, (xchar*)lab);
1785// lab[l] = 0;
1786 unsigned wlen = fl_utf8toUtf16(w->label(), l, NULL, 0); // Pass NULL to query length
1787 wlen++;
1788 lab = (WCHAR *) malloc(sizeof(WCHAR)*wlen);
1789 wlen = fl_utf8toUtf16(w->label(), l, (unsigned short*)lab, wlen);
1790 lab[wlen] = 0;
1791 }
1792 x->xid = CreateWindowExW(
1793 styleEx,
1794 class_namew, lab, style,
1795 xp, yp, wp, hp,
1796 parent,
1797 NULL, // menu
1798 fl_display,
1799 NULL // creation parameters
1800 );
1801 if (lab) free(lab);
1802
DRC685f17e2011-07-28 09:23:00 +00001803 if (w->flags() & Fl_Widget::FULLSCREEN) {
1804 /* We need to make sure that the fullscreen is created on the
1805 default monitor, ie the desktop where the shortcut is located
1806 etc. This requires that CreateWindow is called with CW_USEDEFAULT
1807 for x and y. We can then use GetWindowRect to determine which
1808 monitor the window was placed on. */
1809 RECT rect;
1810 GetWindowRect(x->xid, &rect);
1811 make_fullscreen(w, x->xid, rect.left, rect.top,
1812 rect.right - rect.left, rect.bottom - rect.top);
1813 }
1814
DRC2ff39b82011-07-28 08:38:59 +00001815 x->next = Fl_X::first;
1816 Fl_X::first = x;
1817
DRC685f17e2011-07-28 09:23:00 +00001818 fl_clipboard_notify_target(x->xid);
1819
DRC2ff39b82011-07-28 08:38:59 +00001820 x->wait_for_expose = 1;
1821 if (fl_show_iconic) {showit = 0; fl_show_iconic = 0;}
1822 if (showit) {
1823 w->set_visible();
1824 int old_event = Fl::e_number;
1825 w->handle(Fl::e_number = FL_SHOW); // get child windows to appear
1826 Fl::e_number = old_event;
1827 w->redraw(); // force draw to happen
1828 }
1829 // If we've captured the mouse, we dont want to activate any
1830 // other windows from the code, or we lose the capture.
1831 ShowWindow(x->xid, !showit ? SW_SHOWMINNOACTIVE :
DRC685f17e2011-07-28 09:23:00 +00001832 (Fl::grab() || (styleEx & WS_EX_TOOLWINDOW)) ? SW_SHOWNOACTIVATE : SW_SHOWNORMAL);
DRC2ff39b82011-07-28 08:38:59 +00001833
1834 // Register all windows for potential drag'n'drop operations
1835 fl_OleInitialize();
1836 RegisterDragDrop(x->xid, flIDropTarget);
1837
1838 if (!fl_aimm) {
1839 CoCreateInstance(CLSID_CActiveIMM, NULL, CLSCTX_INPROC_SERVER,
1840 IID_IActiveIMMApp, (void**) &fl_aimm);
1841 if (fl_aimm) {
1842 fl_aimm->Activate(TRUE);
1843 }
1844 }
1845
1846 if (w->modal()) {Fl::modal_ = w; fl_fix_focus();}
1847 return x;
1848}
1849
1850
1851
1852
1853/////////////////////////////////////////////////////////////////////////////
1854/// Win32 timers
1855///
1856
1857
1858static LRESULT CALLBACK s_TimerProc(HWND hwnd, UINT msg,
1859 WPARAM wParam, LPARAM lParam)
1860{
1861 switch (msg) {
1862 case WM_TIMER:
1863 {
1864 unsigned int id = wParam - 1;
1865 if (id < (unsigned int)win32_timer_used && win32_timers[id].handle) {
1866 Fl_Timeout_Handler cb = win32_timers[id].callback;
1867 void* data = win32_timers[id].data;
1868 delete_timer(win32_timers[id]);
1869 if (cb) {
1870 (*cb)(data);
1871 }
1872 }
1873 }
1874 return 0;
1875
1876 default:
1877 break;
1878 }
1879
1880 return DefWindowProc(hwnd, msg, wParam, lParam);
1881}
1882
1883void Fl::add_timeout(double time, Fl_Timeout_Handler cb, void* data)
1884{
1885 repeat_timeout(time, cb, data);
1886}
1887
1888void Fl::repeat_timeout(double time, Fl_Timeout_Handler cb, void* data)
1889{
1890 int timer_id = -1;
1891 for (int i = 0; i < win32_timer_used; ++i) {
1892 if ( !win32_timers[i].handle ) {
1893 timer_id = i;
1894 break;
1895 }
1896 }
1897 if (timer_id == -1) {
1898 if (win32_timer_used == win32_timer_alloc) {
1899 realloc_timers();
1900 }
1901 timer_id = win32_timer_used++;
1902 }
1903 unsigned int elapsed = (unsigned int)(time * 1000);
1904
1905 if ( !s_TimerWnd ) {
1906 const char* timer_class = "FLTimer";
1907 WNDCLASSEX wc;
1908 memset(&wc, 0, sizeof(wc));
1909 wc.cbSize = sizeof (wc);
1910 wc.style = CS_CLASSDC;
1911 wc.lpfnWndProc = (WNDPROC)s_TimerProc;
1912 wc.hInstance = fl_display;
1913 wc.lpszClassName = timer_class;
1914 /*ATOM atom =*/ RegisterClassEx(&wc);
1915 // create a zero size window to handle timer events
1916 s_TimerWnd = CreateWindowEx(WS_EX_LEFT | WS_EX_TOOLWINDOW,
1917 timer_class, "",
1918 WS_POPUP,
1919 0, 0, 0, 0,
1920 NULL, NULL, fl_display, NULL);
1921 // just in case this OS won't let us create a 0x0 size window:
1922 if (!s_TimerWnd)
1923 s_TimerWnd = CreateWindowEx(WS_EX_LEFT | WS_EX_TOOLWINDOW,
1924 timer_class, "",
1925 WS_POPUP,
1926 0, 0, 1, 1,
1927 NULL, NULL, fl_display, NULL);
1928 ShowWindow(s_TimerWnd, SW_SHOWNOACTIVATE);
1929 }
1930
1931 win32_timers[timer_id].callback = cb;
1932 win32_timers[timer_id].data = data;
1933
1934 win32_timers[timer_id].handle =
1935 SetTimer(s_TimerWnd, timer_id + 1, elapsed, NULL);
1936}
1937
1938int Fl::has_timeout(Fl_Timeout_Handler cb, void* data)
1939{
1940 for (int i = 0; i < win32_timer_used; ++i) {
1941 Win32Timer& t = win32_timers[i];
1942 if (t.handle && t.callback == cb && t.data == data) {
1943 return 1;
1944 }
1945 }
1946 return 0;
1947}
1948
1949void Fl::remove_timeout(Fl_Timeout_Handler cb, void* data)
1950{
1951 int i;
1952 for (i = 0; i < win32_timer_used; ++i) {
1953 Win32Timer& t = win32_timers[i];
1954 if (t.handle && t.callback == cb &&
1955 (t.data == data || data == NULL)) {
1956 delete_timer(t);
1957 }
1958 }
1959}
1960
1961/// END TIMERS
1962/////////////////////////////////////////////////////////////////////////////
1963
1964
1965
1966////////////////////////////////////////////////////////////////
1967
1968HINSTANCE fl_display = GetModuleHandle(NULL);
1969
1970void Fl_Window::size_range_() {
1971 size_range_set = 1;
1972}
1973
1974void Fl_X::set_minmax(LPMINMAXINFO minmax)
1975{
1976 int td, wd, hd, dummy_x, dummy_y;
1977
1978 fake_X_wm(w, dummy_x, dummy_y, td, wd, hd);
1979 wd *= 2;
1980 hd *= 2;
1981 hd += td;
1982
1983 minmax->ptMinTrackSize.x = w->minw + wd;
1984 minmax->ptMinTrackSize.y = w->minh + hd;
1985 if (w->maxw) {
1986 minmax->ptMaxTrackSize.x = w->maxw + wd;
1987 minmax->ptMaxSize.x = w->maxw + wd;
1988 }
1989 if (w->maxh) {
1990 minmax->ptMaxTrackSize.y = w->maxh + hd;
1991 minmax->ptMaxSize.y = w->maxh + hd;
1992 }
1993}
1994
1995////////////////////////////////////////////////////////////////
1996
1997#include <FL/filename.H> // need so FL_EXPORT fl_filename_name works
1998
1999// returns pointer to the filename, or null if name ends with '/'
2000const char *fl_filename_name(const char *name) {
2001 const char *p,*q;
2002 if (!name) return (0);
2003 q = name;
2004 if (q[0] && q[1]==':') q += 2; // skip leading drive letter
2005 for (p = q; *p; p++) if (*p == '/' || *p == '\\') q = p+1;
2006 return q;
2007}
2008
2009void Fl_Window::label(const char *name,const char *iname) {
2010 Fl_Widget::label(name);
2011 iconlabel_ = iname;
2012 if (shown() && !parent()) {
2013 if (!name) name = "";
2014 int l = strlen(name);
2015// WCHAR *lab = (WCHAR*) malloc((l + 1) * sizeof(short));
2016// l = fl_utf2unicode((unsigned char*)name, l, (xchar*)lab);
2017 unsigned wlen = fl_utf8toUtf16(name, l, NULL, 0); // Pass NULL to query length
2018 wlen++;
2019 unsigned short * lab = (unsigned short*)malloc(sizeof(unsigned short)*wlen);
2020 wlen = fl_utf8toUtf16(name, l, lab, wlen);
2021 lab[wlen] = 0;
2022 SetWindowTextW(i->xid, (WCHAR *)lab);
2023 free(lab);
2024 }
2025}
2026
2027////////////////////////////////////////////////////////////////
DRC685f17e2011-07-28 09:23:00 +00002028
2029#ifndef IDC_HAND
2030# define IDC_HAND MAKEINTRESOURCE(32649)
2031#endif // !IDC_HAND
2032
2033int Fl_X::set_cursor(Fl_Cursor c) {
2034 LPSTR n;
2035
2036 if (c == FL_CURSOR_NONE)
2037 cursor = NULL;
2038 else {
2039 switch (c) {
2040 case FL_CURSOR_ARROW: n = IDC_ARROW; break;
2041 case FL_CURSOR_CROSS: n = IDC_CROSS; break;
2042 case FL_CURSOR_WAIT: n = IDC_WAIT; break;
2043 case FL_CURSOR_INSERT: n = IDC_IBEAM; break;
2044 case FL_CURSOR_HAND: n = IDC_HAND; break;
2045 case FL_CURSOR_HELP: n = IDC_HELP; break;
2046 case FL_CURSOR_MOVE: n = IDC_SIZEALL; break;
2047 case FL_CURSOR_N:
2048 case FL_CURSOR_S:
2049 // FIXME: Should probably have fallbacks for these instead
2050 case FL_CURSOR_NS: n = IDC_SIZENS; break;
2051 case FL_CURSOR_NE:
2052 case FL_CURSOR_SW:
2053 // FIXME: Dito.
2054 case FL_CURSOR_NESW: n = IDC_SIZENESW; break;
2055 case FL_CURSOR_E:
2056 case FL_CURSOR_W:
2057 // FIXME: Dito.
2058 case FL_CURSOR_WE: n = IDC_SIZEWE; break;
2059 case FL_CURSOR_SE:
2060 case FL_CURSOR_NW:
2061 // FIXME: Dito.
2062 case FL_CURSOR_NWSE: n = IDC_SIZENWSE; break;
2063 default:
2064 return 0;
2065 }
2066
2067 cursor = LoadCursor(NULL, n);
2068 if (cursor == NULL)
2069 return 0;
2070 }
2071
2072 SetCursor(cursor);
2073
2074 return 1;
2075}
2076
2077int Fl_X::set_cursor(const Fl_RGB_Image *image, int hotx, int hoty) {
2078 BITMAPV5HEADER bi;
2079 HBITMAP bitmap, mask;
2080 DWORD *bits;
2081
2082 if ((hotx < 0) || (hotx >= image->w()))
2083 return 0;
2084 if ((hoty < 0) || (hoty >= image->h()))
2085 return 0;
2086
2087 memset(&bi, 0, sizeof(BITMAPV5HEADER));
2088
2089 bi.bV5Size = sizeof(BITMAPV5HEADER);
2090 bi.bV5Width = image->w();
2091 bi.bV5Height = image->h();
2092 bi.bV5Planes = 1;
2093 bi.bV5BitCount = 32;
2094 bi.bV5Compression = BI_BITFIELDS;
2095 bi.bV5RedMask = 0x00FF0000;
2096 bi.bV5GreenMask = 0x0000FF00;
2097 bi.bV5BlueMask = 0x000000FF;
2098 bi.bV5AlphaMask = 0xFF000000;
2099
2100 HDC hdc;
2101
2102 hdc = GetDC(NULL);
2103 bitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bi, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
2104 ReleaseDC(NULL, hdc);
2105
2106 const uchar *i = (const uchar*)*image->data();
2107 for (int y = 0;y < image->h();y++) {
2108 for (int x = 0;x < image->w();x++) {
2109 switch (image->d()) {
2110 case 1:
2111 *bits = (0xff<<24) | (i[0]<<16) | (i[0]<<8) | i[0];
2112 break;
2113 case 2:
2114 *bits = (i[1]<<24) | (i[0]<<16) | (i[0]<<8) | i[0];
2115 break;
2116 case 3:
2117 *bits = (0xff<<24) | (i[0]<<16) | (i[1]<<8) | i[2];
2118 break;
2119 case 4:
2120 *bits = (i[3]<<24) | (i[0]<<16) | (i[1]<<8) | i[2];
2121 break;
2122 }
2123 i += image->d();
2124 bits++;
2125 }
2126 i += image->ld();
2127 }
2128
2129 // A mask bitmap is still needed even though it isn't used
2130 mask = CreateBitmap(image->w(),image->h(),1,1,NULL);
2131
2132 ICONINFO ii;
2133
2134 ii.fIcon = FALSE;
2135 ii.xHotspot = hotx;
2136 ii.yHotspot = hoty;
2137 ii.hbmMask = mask;
2138 ii.hbmColor = bitmap;
2139
2140 cursor = CreateIconIndirect(&ii);
2141
2142 DeleteObject(bitmap);
2143 DeleteObject(mask);
2144
2145 SetCursor(cursor);
2146
2147 return 1;
2148}
2149
2150////////////////////////////////////////////////////////////////
DRC2ff39b82011-07-28 08:38:59 +00002151// Implement the virtual functions for the base Fl_Window class:
2152
2153// If the box is a filled rectangle, we can make the redisplay *look*
2154// faster by using X's background pixel erasing. We can make it
2155// actually *be* faster by drawing the frame only, this is done by
2156// setting fl_boxcheat, which is seen by code in fl_drawbox.cxx:
2157// For WIN32 it looks like all windows share a background color, so
2158// I use FL_GRAY for this and only do this cheat for windows that are
2159// that color.
2160// Actually it is totally disabled.
2161// Fl_Widget *fl_boxcheat;
2162//static inline int can_boxcheat(uchar b) {return (b==1 || (b&2) && b<=15);}
2163
2164void Fl_Window::show() {
2165 image(Fl::scheme_bg_);
2166 if (Fl::scheme_bg_) {
2167 labeltype(FL_NORMAL_LABEL);
2168 align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE | FL_ALIGN_CLIP);
2169 } else {
2170 labeltype(FL_NO_LABEL);
2171 }
2172 Fl_Tooltip::exit(this);
2173 if (!shown()) {
2174 // if (can_boxcheat(box())) fl_background_pixel = fl_xpixel(color());
2175 Fl_X::make(this);
2176 } else {
2177 // Once again, we would lose the capture if we activated the window.
2178 if (IsIconic(i->xid)) OpenIcon(i->xid);
2179 if (!fl_capture) BringWindowToTop(i->xid);
2180 //ShowWindow(i->xid,fl_capture?SW_SHOWNOACTIVATE:SW_RESTORE);
2181 }
2182#ifdef USE_PRINT_BUTTON
2183void preparePrintFront(void);
2184preparePrintFront();
2185#endif
2186}
2187
2188Fl_Window *Fl_Window::current_;
2189// the current context
2190HDC fl_gc = 0;
2191// the current window handle, initially set to -1 so we can correctly
2192// allocate fl_GetDC(0)
2193HWND fl_window = NULL;
2194
2195// Here we ensure only one GetDC is ever in place.
2196HDC fl_GetDC(HWND w) {
2197 if (fl_gc) {
2198 if (w == fl_window && fl_window != NULL) return fl_gc;
2199 if (fl_window) fl_release_dc(fl_window, fl_gc); // ReleaseDC
2200 }
2201 fl_gc = GetDC(w);
2202 fl_save_dc(w, fl_gc);
2203 fl_window = w;
2204 // calling GetDC seems to always reset these: (?)
2205 SetTextAlign(fl_gc, TA_BASELINE|TA_LEFT);
2206 SetBkMode(fl_gc, TRANSPARENT);
2207
2208 return fl_gc;
2209}
2210
2211// make X drawing go into this window (called by subclass flush() impl.)
2212void Fl_Window::make_current() {
2213 fl_GetDC(fl_xid(this));
2214
2215#if USE_COLORMAP
2216 // Windows maintains a hardware and software color palette; the
2217 // SelectPalette() call updates the current soft->hard mapping
2218 // for all drawing calls, so we must select it here before any
2219 // code does any drawing...
2220
2221 fl_select_palette();
2222#endif // USE_COLORMAP
2223
2224 current_ = this;
2225 fl_clip_region(0);
2226
2227
2228}
2229
2230/* Make sure that all allocated fonts are released. This works only if
2231 Fl::run() is allowed to exit by closing all windows. Calling 'exit(int)'
2232 will not automatically free any fonts. */
2233void fl_free_fonts(void)
2234{
2235// remove the Fl_Font_Descriptor chains
2236 int i;
2237 Fl_Fontdesc * s;
2238 Fl_Font_Descriptor * f;
2239 Fl_Font_Descriptor * ff;
2240 for (i=0; i<FL_FREE_FONT; i++) {
2241 s = fl_fonts + i;
2242 for (f=s->first; f; f=ff) {
2243 ff = f->next;
2244 delete f;
2245 s->first = ff;
2246 }
2247 }
2248}
2249
2250
2251///////////////////////////////////////////////////////////////////////
2252//
2253// The following routines help fix a problem with the leaking of Windows
2254// Device Context (DC) objects. The 'proper' protocol is for a program to
2255// acquire a DC, save its state, do the modifications needed for drawing,
2256// perform the drawing, restore the initial state, and release the DC. In
2257// FLTK, the save and restore steps have previously been omitted and DCs are
2258// not properly released, leading to a great number of DC leaks. As some
2259// Windows "OSs" will hang when any process exceeds roughly 10,000 GDI objects,
2260// it is important to control GDI leaks, which are much more important than memory
2261// leaks. The following struct, global variable, and routines help implement
2262// the above protocol for those cases where the GetDC and RestoreDC are not in
2263// the same routine. For each GetDC, fl_save_dc is used to create an entry in
2264// a linked list that saves the window handle, the DC handle, and the initial
2265// state. When the DC is to be released, 'fl_release_dc' is called. It restores
2266// the initial state and releases the DC. When the program exits, 'fl_cleanup_dc_list'
2267// frees any remaining nodes in the list.
2268
2269struct Win_DC_List { // linked list
2270 HWND window; // window handle
2271 HDC dc; // device context handle
2272 int saved_dc; // initial state of DC
2273 Win_DC_List * next; // pointer to next item
2274};
2275
2276static Win_DC_List * win_DC_list = 0;
2277
2278void fl_save_dc( HWND w, HDC dc) {
2279 Win_DC_List * t;
2280 t = new Win_DC_List;
2281 t->window = w;
2282 t->dc = dc;
2283 t->saved_dc = SaveDC(dc);
2284 if (win_DC_list)
2285 t->next = win_DC_list;
2286 else
2287 t->next = NULL;
2288 win_DC_list = t;
2289}
2290
2291void fl_release_dc(HWND w, HDC dc) {
2292 Win_DC_List * t= win_DC_list;
2293 Win_DC_List * prev = 0;
2294 if (!t)
2295 return;
2296 do {
2297 if (t->dc == dc) {
2298 RestoreDC(dc, t->saved_dc);
2299 ReleaseDC(w, dc);
2300 if (!prev) {
2301 win_DC_list = t->next; // delete first item
2302 } else {
2303 prev->next = t->next; // one in the middle
2304 }
2305 delete (t);
2306 return;
2307 }
2308 prev = t;
2309 t = t->next;
2310 } while (t);
2311}
2312
2313void fl_cleanup_dc_list(void) { // clean up the list
2314 Win_DC_List * t = win_DC_list;
2315 if (!t)return;
2316 do {
2317 RestoreDC(t->dc, t->saved_dc);
2318 ReleaseDC(t->window, t->dc);
2319 win_DC_list = t->next;
2320 delete (t);
2321 t = win_DC_list;
2322 } while(t);
2323}
2324
2325Fl_Region XRectangleRegion(int x, int y, int w, int h) {
2326 if (Fl_Surface_Device::surface()->class_name() == Fl_Display_Device::class_id) return CreateRectRgn(x,y,x+w,y+h);
2327 // because rotation may apply, the rectangle becomes a polygon in device coords
2328 POINT pt[4] = { {x, y}, {x + w, y}, {x + w, y + h}, {x, y + h} };
2329 LPtoDP(fl_gc, pt, 4);
2330 return CreatePolygonRgn(pt, 4, ALTERNATE);
2331}
2332
2333Window fl_xid_(const Fl_Window *w) {
2334 Fl_X *temp = Fl_X::i(w);
2335 return temp ? temp->xid : 0;
2336}
2337
2338int Fl_Window::decorated_w()
2339{
2340 if (!shown() || parent() || !border() || !visible()) return w();
2341 int X, Y, bt, bx, by;
2342 Fl_X::fake_X_wm(this, X, Y, bt, bx, by);
2343 return w() + 2 * bx;
2344}
2345
2346int Fl_Window::decorated_h()
2347{
2348 if (!shown() || parent() || !border() || !visible()) return h();
2349 int X, Y, bt, bx, by;
2350 Fl_X::fake_X_wm(this, X, Y, bt, bx, by);
2351 return h() + bt + 2 * by;
2352}
2353
2354void Fl_Paged_Device::print_window(Fl_Window *win, int x_offset, int y_offset)
2355{
2356 if (!win->shown() || win->parent() || !win->border() || !win->visible()) {
2357 this->print_widget(win, x_offset, y_offset);
2358 return;
2359 }
2360 int X, Y, bt, bx, by, ww, wh; // compute the window border sizes
2361 Fl_X::fake_X_wm(win, X, Y, bt, bx, by);
2362 ww = win->w() + 2 * bx;
2363 wh = win->h() + bt + 2 * by;
2364 Fl_Display_Device::display_device()->set_current(); // make window current
2365 win->show();
2366 Fl::check();
2367 win->make_current();
2368 HDC save_gc = fl_gc;
2369 fl_gc = GetDC(NULL); // get the screen device context
2370 // capture the 4 window sides from screen
2371 RECT r; GetWindowRect(fl_window, &r);
2372 uchar *top_image = fl_read_image(NULL, r.left, r.top, ww, bt + by);
2373 uchar *left_image = fl_read_image(NULL, r.left, r.top, bx, wh);
2374 uchar *right_image = fl_read_image(NULL, r.right - bx, r.top, bx, wh);
2375 uchar *bottom_image = fl_read_image(NULL, r.left, r.bottom-by, ww, by);
2376 ReleaseDC(NULL, fl_gc); fl_gc = save_gc;
2377 this->set_current();
2378 // print the 4 window sides
2379 fl_draw_image(top_image, x_offset, y_offset, ww, bt + by, 3);
2380 fl_draw_image(left_image, x_offset, y_offset, bx, wh, 3);
2381 fl_draw_image(right_image, x_offset + win->w() + bx, y_offset, bx, wh, 3);
2382 fl_draw_image(bottom_image, x_offset, y_offset + win->h() + bt + by, ww, by, 3);
2383 delete[] top_image;
2384 delete[] left_image;
2385 delete[] right_image;
2386 delete[] bottom_image;
2387 // print the window inner part
2388 this->print_widget(win, x_offset + bx, y_offset + bt + by);
2389 fl_gc = GetDC(fl_xid(win));
2390 ReleaseDC(fl_xid(win), fl_gc);
2391}
2392
2393#ifdef USE_PRINT_BUTTON
2394// to test the Fl_Printer class creating a "Print front window" button in a separate window
2395// contains also preparePrintFront call above
2396#include <FL/Fl_Printer.H>
2397#include <FL/Fl_Button.H>
2398void printFront(Fl_Widget *o, void *data)
2399{
2400 Fl_Printer printer;
2401 o->window()->hide();
2402 Fl_Window *win = Fl::first_window();
2403 if(!win) return;
2404 int w, h;
2405 if( printer.start_job(1) ) { o->window()->show(); return; }
2406 if( printer.start_page() ) { o->window()->show(); return; }
2407 printer.printable_rect(&w,&h);
2408 int wh, ww;
2409 wh = win->decorated_h();
2410 ww = win->decorated_w();
2411 // scale the printer device so that the window fits on the page
2412 float scale = 1;
2413 if (ww > w || wh > h) {
2414 scale = (float)w/ww;
2415 if ((float)h/wh < scale) scale = (float)h/wh;
2416 printer.scale(scale, scale);
2417 }
2418// #define ROTATE 20.0
2419#ifdef ROTATE
2420 printer.scale(scale * 0.8, scale * 0.8);
2421 printer.printable_rect(&w, &h);
2422 printer.origin(w/2, h/2 );
2423 printer.rotate(ROTATE);
2424 printer.print_widget( win, - win->w()/2, - win->h()/2 );
2425 //printer.print_window_part( win, 0,0, win->w(), win->h(), - win->w()/2, - win->h()/2 );
2426#else
2427 printer.print_window(win);
2428#endif
2429 printer.end_page();
2430 printer.end_job();
2431 o->window()->show();
2432}
2433
2434void preparePrintFront(void)
2435{
2436 static BOOL first=TRUE;
2437 if(!first) return;
2438 first=FALSE;
2439 static Fl_Window w(0,0,120,30);
2440 static Fl_Button b(0,0,w.w(),w.h(), "Print front window");
2441 b.callback(printFront);
2442 w.end();
2443 w.show();
2444}
2445#endif // USE_PRINT_BUTTON
2446
2447#endif // FL_DOXYGEN
2448
2449//
2450// End of "$Id: Fl_win32.cxx 8759 2011-05-30 12:33:51Z manolo $".
2451//