fltk 1.3.0rc3
About: FLTK (Fast Light Tool Kit) is a cross-platform C++ GUI toolkit for UNIX/Linux (X11), Microsoft Windows, and MacOS X. Release candidate.
  SfR Fresh Dox: fltk-1.3.0rc3-source.tar.gz ("inofficial" and yet experimental doxygen-generated source code documentation)  

Fl_win32.cxx

Go to the documentation of this file.
00001 //
00002 // "$Id: Fl_win32.cxx 8205 2011-01-07 01:12:04Z matt $"
00003 //
00004 // WIN32-specific code for the Fast Light Tool Kit (FLTK).
00005 //
00006 // Copyright 1998-2010 by Bill Spitzak and others.
00007 //
00008 // This library is free software; you can redistribute it and/or
00009 // modify it under the terms of the GNU Library General Public
00010 // License as published by the Free Software Foundation; either
00011 // version 2 of the License, or (at your option) any later version.
00012 //
00013 // This library is distributed in the hope that it will be useful,
00014 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016 // Library General Public License for more details.
00017 //
00018 // You should have received a copy of the GNU Library General Public
00019 // License along with this library; if not, write to the Free Software
00020 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
00021 // USA.
00022 //
00023 // Please report all bugs and problems on the following page:
00024 //
00025 //     http://www.fltk.org/str.php
00026 //
00027 
00028 // This file contains win32-specific code for fltk which is always linked
00029 // in.  Search other files for "WIN32" or filenames ending in _win32.cxx
00030 // for other system-specific code.
00031 
00032 // This file must be #include'd in Fl.cxx and not compiled separately.
00033 
00034 #ifndef FL_DOXYGEN
00035 #include <FL/Fl.H>
00036 #include <FL/fl_utf8.h>
00037 #include <FL/Fl_Window.H>
00038 #include <FL/fl_draw.H>
00039 #include <FL/Enumerations.H>
00040 #include <FL/Fl_Tooltip.H>
00041 #include "flstring.h"
00042 #include "Fl_Font.H"
00043 #include <stdio.h>
00044 #include <stdlib.h>
00045 #include <sys/types.h>
00046 #include <time.h>
00047 #ifdef __CYGWIN__
00048 #  include <sys/time.h>
00049 #  include <unistd.h>
00050 #endif
00051 
00052 #if !defined(NO_TRACK_MOUSE)
00053 #include <commctrl.h>   // TrackMouseEvent
00054 #endif
00055 
00056 #if defined(__GNUC__)
00057 # include <wchar.h>
00058 #endif
00059 
00060 #include <ole2.h>
00061 #include <shellapi.h>
00062 
00063 #include "aimm.h"
00064 
00065 //
00066 // USE_ASYNC_SELECT - define it if you have WSAAsyncSelect()...
00067 // USE_ASYNC_SELECT is OBSOLETED in 1.3 for the following reasons:
00068 /*
00069   This feature was supposed to provide an efficient alternative to the current
00070   polling method, but as it has been discussed (Thanks Albrecht!) :
00071   - the async mode would imply to change the socket to non blocking mode.
00072     This can have unexpected side effects for 3rd party apps, especially
00073     if it is set on-the-fly when socket service is really needed, as it is 
00074     done today and on purpose, but still the 3rd party developer wouldn't easily
00075     control the sequencing of socket operations.
00076   - Finer granularity of events furthered by the async select is a plus only 
00077     for socket 3rd party impl., it is simply not needed for the 'light' fltk
00078     use we make of wsock, so here it would also be a bad point, because of all
00079     the logic add-ons necessary for using this functionality, without a clear
00080     benefit.
00081 
00082   So async mode select would not add benefits to fltk, worse, it can slowdown
00083   fltk because of this finer granularity and instrumentation code to be added
00084   for async mode proper operation, not mentioning the side effects...
00085 */
00086 
00087 static Fl_GDI_Graphics_Driver fl_gdi_driver;
00088 static Fl_Display_Device fl_gdi_display(&fl_gdi_driver);
00089 FL_EXPORT Fl_Display_Device *fl_display_device = (Fl_Display_Device*)&fl_gdi_display; // does not change
00090 FL_EXPORT Fl_Graphics_Driver *fl_graphics_driver = (Fl_Graphics_Driver*)&fl_gdi_driver; // the current target driver of graphics operations
00091 FL_EXPORT Fl_Surface_Device *fl_surface = (Fl_Surface_Device*)fl_display_device; // the current target surface of graphics operations
00092 
00093 // dynamic wsock dll handling api:
00094 #if defined(__CYGWIN__) && !defined(SOCKET)
00095 # define SOCKET int
00096 #endif
00097 
00098 // note: winsock2.h has been #include'd in Fl.cxx
00099 #define WSCK_DLL_NAME "WS2_32.DLL"
00100 
00101 typedef int (WINAPI* fl_wsk_select_f)(int, fd_set*, fd_set*, fd_set*, const struct timeval*);
00102 typedef int (WINAPI* fl_wsk_fd_is_set_f)(SOCKET, fd_set *);
00103 
00104 static HMODULE s_wsock_mod = 0;
00105 static fl_wsk_select_f s_wsock_select = 0;
00106 static fl_wsk_fd_is_set_f fl_wsk_fd_is_set = 0;
00107 
00108 static HMODULE get_wsock_mod() {
00109   if (!s_wsock_mod) {
00110     s_wsock_mod = LoadLibrary(WSCK_DLL_NAME);
00111     if (s_wsock_mod==NULL)
00112       Fl::fatal("FLTK Lib Error: %s file not found! Please check your winsock dll accessibility.\n",WSCK_DLL_NAME);
00113     s_wsock_select = (fl_wsk_select_f) GetProcAddress(s_wsock_mod, "select");
00114     fl_wsk_fd_is_set = (fl_wsk_fd_is_set_f) GetProcAddress(s_wsock_mod, "__WSAFDIsSet");
00115   }
00116   return s_wsock_mod;
00117 }
00118 
00119 /*
00120  * Dynamic linking of imm32.dll
00121  * This library is only needed for a hand full (four ATM) functions relating to 
00122  * international text rendering and locales. Dynamically loading reduces initial
00123  * size and link dependencies.
00124  */
00125 static HMODULE s_imm_module = 0;
00126 typedef HIMC (WINAPI* flTypeImmGetContext)(HWND);
00127 static flTypeImmGetContext flImmGetContext = 0;
00128 typedef BOOL (WINAPI* flTypeImmSetCompositionWindow)(HIMC, LPCOMPOSITIONFORM);
00129 static flTypeImmSetCompositionWindow flImmSetCompositionWindow = 0;
00130 typedef BOOL (WINAPI* flTypeImmReleaseContext)(HWND, HIMC);
00131 static flTypeImmReleaseContext flImmReleaseContext = 0;
00132 typedef BOOL (WINAPI* flTypeImmIsIME)(HKL);
00133 static flTypeImmIsIME flImmIsIME = 0;
00134 
00135 static HMODULE get_imm_module() {
00136   if (!s_imm_module) {
00137     s_imm_module = LoadLibrary("IMM32.DLL");
00138     if (!s_imm_module)
00139       Fl::fatal("FLTK Lib Error: IMM32.DLL file not found!\n\n"
00140         "Please check your input method manager library accessibility.");
00141     flImmGetContext = (flTypeImmGetContext)GetProcAddress(s_imm_module, "ImmGetContext");
00142     flImmSetCompositionWindow = (flTypeImmSetCompositionWindow)GetProcAddress(s_imm_module, "ImmSetCompositionWindow");
00143     flImmReleaseContext = (flTypeImmReleaseContext)GetProcAddress(s_imm_module, "ImmReleaseContext");
00144     flImmIsIME = (flTypeImmIsIME)GetProcAddress(s_imm_module, "ImmIsIME");
00145   }
00146   return s_imm_module;
00147 }
00148 
00149 // USE_TRACK_MOUSE - define NO_TRACK_MOUSE if you don't have
00150 // TrackMouseEvent()...
00151 //
00152 // Now (Dec. 2008) we can assume that current Cygwin/MinGW versions
00153 // support the TrackMouseEvent() function, but WinCE obviously doesn't
00154 // support it (STR 2095). Therefore, USE_TRACK_MOUSE is enabled by 
00155 // default, but you can disable it by defining NO_TRACK_MOUSE.
00156 //
00157 // TrackMouseEvent is only used to support window leave notifications
00158 // under Windows. It can be used to get FL_LEAVE events, when the
00159 // mouse leaves the _main_ application window (FLTK detects subwindow
00160 // leave events by using normal move events).
00161 //
00162 // Implementation note: If the mouse cursor leaves one subwindow and
00163 // enters another window, then Windows sends a WM_MOUSEMOVE message to
00164 // the new window before it sends a WM_MOUSELEAVE message to the old
00165 // (just left) window. We save the current mouse window in a static variable,
00166 // and if we get a WM_MOUSELEAVE event for the current mouse window, this
00167 // means that the top level window has been left (otherwise we would have
00168 // got another WM_MOUSEMOVE message before).
00169 
00170 // #define NO_TRACK_MOUSE
00171 
00172 #if !defined(NO_TRACK_MOUSE)
00173 # define USE_TRACK_MOUSE
00174 #endif // NO_TRACK_MOUSE
00175 
00176 static Fl_Window *track_mouse_win=0;    // current TrackMouseEvent() window
00177 
00178 // USE_CAPTURE_MOUSE_WIN - this must be defined for TrackMouseEvent to work
00179 // correctly with subwindows - otherwise a single mouse click and release
00180 // (without a move) would generate phantom leave events.
00181 // This defines, if the current mouse window (maybe a subwindow) or the 
00182 // main window should get mouse events after pushing (and holding) a mouse
00183 // button, i.e. when dragging the mouse. This is done by calling SetCapture
00184 // (see below).
00185 
00186 #ifdef USE_TRACK_MOUSE
00187 #define USE_CAPTURE_MOUSE_WIN
00188 #endif // USE_TRACK_MOUSE
00189 
00190 //
00191 // WM_SYNCPAINT is an "undocumented" message, which is finally defined in
00192 // VC++ 6.0.
00193 //
00194 
00195 #ifndef WM_SYNCPAINT
00196 #  define WM_SYNCPAINT 0x0088
00197 #endif
00198 
00199 #ifndef WM_MOUSELEAVE
00200 #  define WM_MOUSELEAVE 0x02a3
00201 #endif
00202 
00203 #ifndef WM_MOUSEWHEEL
00204 #  define WM_MOUSEWHEEL 0x020a
00205 #endif
00206 
00207 #ifndef WHEEL_DELTA
00208 #  define WHEEL_DELTA 120       // according to MSDN.
00209 #endif
00210 
00211 
00212 //
00213 // WM_FLSELECT is the user-defined message that we get when one of
00214 // the sockets has pending data, etc.
00215 //
00216 
00217 #define WM_FLSELECT     (WM_APP+1)      // WM_APP is used for hide-window
00218 
00219 
00221 // interface to poll/select call:
00222 
00223 // fd's are only implemented for sockets.  Microsoft Windows does not
00224 // have a unified IO system, so it doesn't support select() on files,
00225 // devices, or pipes...
00226 //
00227 // Microsoft provides the Berkeley select() call and an asynchronous
00228 // select function that sends a WIN32 message when the select condition
00229 // exists. However, we don't try to use the asynchronous WSAAsyncSelect()
00230 // any more for good reasons (see above).
00231 //
00232 // A.S. Dec 2009: We got reports that current winsock2.h files define
00233 // POLLIN, POLLOUT, and POLLERR with conflicting values WRT what we
00234 // used before (STR #2301).  Therefore we must not use these values
00235 // for our internal purposes, but use FL_READ, FL_WRITE, and
00236 // FL_EXCEPT, as defined for use in Fl::add_fd().
00237 //
00238 static int maxfd = 0;
00239 static fd_set fdsets[3];
00240 
00241 extern IDropTarget *flIDropTarget;
00242 
00243 static int nfds = 0;
00244 static int fd_array_size = 0;
00245 static struct FD {
00246   int fd;
00247   short events;
00248   void (*cb)(int, void*);
00249   void* arg;
00250 } *fd = 0;
00251 
00252 extern unsigned int fl_codepage;
00253 
00254 void fl_reset_spot()
00255 {
00256 }
00257 
00258 void fl_set_spot(int font, int size, int X, int Y, int W, int H, Fl_Window *win)
00259 {
00260   if (!win) return;
00261   Fl_Window* tw = win;
00262   while (tw->parent()) tw = tw->window(); // find top level window
00263 
00264   get_imm_module();
00265   HIMC himc = flImmGetContext(fl_xid(tw));
00266 
00267   if (himc) {
00268     COMPOSITIONFORM cfs;
00269     cfs.dwStyle = CFS_POINT;
00270     cfs.ptCurrentPos.x = X;
00271     cfs.ptCurrentPos.y = Y - tw->labelsize();
00272     MapWindowPoints(fl_xid(win), fl_xid(tw), &cfs.ptCurrentPos, 1);
00273     flImmSetCompositionWindow(himc, &cfs);
00274     flImmReleaseContext(fl_xid(tw), himc);
00275   }
00276 }
00277 
00278 void fl_set_status(int x, int y, int w, int h)
00279 {
00280 }
00281 
00282 void Fl::add_fd(int n, int events, void (*cb)(int, void*), void *v) {
00283   remove_fd(n,events);
00284   int i = nfds++;
00285   if (i >= fd_array_size) {
00286     fd_array_size = 2*fd_array_size+1;
00287     fd = (FD*)realloc(fd, fd_array_size*sizeof(FD));
00288   }
00289   fd[i].fd = n;
00290   fd[i].events = (short)events;
00291   fd[i].cb = cb;
00292   fd[i].arg = v;
00293 
00294   if (events & FL_READ) FD_SET((unsigned)n, &fdsets[0]);
00295   if (events & FL_WRITE) FD_SET((unsigned)n, &fdsets[1]);
00296   if (events & FL_EXCEPT) FD_SET((unsigned)n, &fdsets[2]);
00297   if (n > maxfd) maxfd = n;
00298 }
00299 
00300 void Fl::add_fd(int fd, void (*cb)(int, void*), void* v) {
00301   Fl::add_fd(fd, FL_READ, cb, v);
00302 }
00303 
00304 void Fl::remove_fd(int n, int events) {
00305   int i,j;
00306   for (i=j=0; i<nfds; i++) {
00307     if (fd[i].fd == n) {
00308       short e = fd[i].events & ~events;
00309       if (!e) continue; // if no events left, delete this fd
00310       fd[i].events = e;
00311     }
00312     // move it down in the array if necessary:
00313     if (j<i) {
00314       fd[j]=fd[i];
00315     }
00316     j++;
00317   }
00318   nfds = j;
00319 
00320   if (events & FL_READ) FD_CLR(unsigned(n), &fdsets[0]);
00321   if (events & FL_WRITE) FD_CLR(unsigned(n), &fdsets[1]);
00322   if (events & FL_EXCEPT) FD_CLR(unsigned(n), &fdsets[2]);
00323 }
00324 
00325 void Fl::remove_fd(int n) {
00326   remove_fd(n, -1);
00327 }
00328 
00329 // these pointers are set by the Fl::lock() function:
00330 static void nothing() {}
00331 void (*fl_lock_function)() = nothing;
00332 void (*fl_unlock_function)() = nothing;
00333 
00334 static void* thread_message_;
00335 void* Fl::thread_message() {
00336   void* r = thread_message_;
00337   thread_message_ = 0;
00338   return r;
00339 }
00340 
00341 IActiveIMMApp *fl_aimm = NULL;
00342 MSG fl_msg;
00343 
00344 // This is never called with time_to_wait < 0.0.
00345 // It *should* return negative on error, 0 if nothing happens before
00346 // timeout, and >0 if any callbacks were done.  This version only
00347 // returns zero if nothing happens during a 0.0 timeout, otherwise
00348 // it returns 1.
00349 int fl_wait(double time_to_wait) {
00350   int have_message = 0;
00351 
00352   run_checks();
00353 
00354   // idle processing
00355   static char in_idle;
00356   if (Fl::idle && !in_idle) {
00357     in_idle = 1;
00358     Fl::idle();
00359     in_idle = 0;
00360   }
00361   
00362   if (nfds) {
00363     // For WIN32 we need to poll for socket input FIRST, since
00364     // the event queue is not something we can select() on...
00365     timeval t;
00366     t.tv_sec = 0;
00367     t.tv_usec = 0;
00368 
00369     fd_set fdt[3];
00370     memcpy(fdt, fdsets, sizeof fdt); // one shot faster fdt init
00371     if (get_wsock_mod()&& s_wsock_select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],&t)) {
00372       // We got something - do the callback!
00373       for (int i = 0; i < nfds; i ++) {
00374         SOCKET f = fd[i].fd;
00375         short revents = 0;
00376         if (fl_wsk_fd_is_set(f, &fdt[0])) revents |= FL_READ;
00377         if (fl_wsk_fd_is_set(f, &fdt[1])) revents |= FL_WRITE;
00378         if (fl_wsk_fd_is_set(f, &fdt[2])) revents |= FL_EXCEPT;
00379         if (fd[i].events & revents) fd[i].cb(f, fd[i].arg);
00380       }
00381       time_to_wait = 0.0; // just peek for any messages
00382     } else {
00383       // we need to check them periodically, so set a short timeout:
00384       if (time_to_wait > .001) time_to_wait = .001;
00385     }
00386   }
00387 
00388   if (Fl::idle || Fl::damage()) 
00389     time_to_wait = 0.0;
00390 
00391   // if there are no more windows and this timer is set
00392   // to FOREVER, continue through or look up indefinitely
00393   if (!Fl::first_window() && time_to_wait==1e20)
00394     time_to_wait = 0.0;
00395 
00396   fl_unlock_function();
00397 
00398   time_to_wait = (time_to_wait > 10000 ? 10000 : time_to_wait);
00399   int t_msec = (int) (time_to_wait * 1000.0 + 0.5);
00400   MsgWaitForMultipleObjects(0, NULL, FALSE, t_msec, QS_ALLINPUT);
00401 
00402   fl_lock_function();
00403 
00404   // Execute the message we got, and all other pending messages:
00405   // have_message = PeekMessage(&fl_msg, NULL, 0, 0, PM_REMOVE);
00406   have_message = PeekMessageW(&fl_msg, NULL, 0, 0, PM_REMOVE);
00407   if (have_message > 0) {
00408     while (have_message != 0 && have_message != -1) {
00409       if (fl_msg.message == fl_wake_msg) {
00410         // Used for awaking wait() from another thread
00411         thread_message_ = (void*)fl_msg.wParam;
00412         Fl_Awake_Handler func;
00413         void *data;
00414         while (Fl::get_awake_handler_(func, data)==0) {
00415           func(data);
00416         }
00417       }
00418 
00419       TranslateMessage(&fl_msg);
00420       DispatchMessageW(&fl_msg);
00421       have_message = PeekMessageW(&fl_msg, NULL, 0, 0, PM_REMOVE);
00422     }
00423   }
00424   Fl::flush();
00425 
00426   // This should return 0 if only timer events were handled:
00427   return 1;
00428 }
00429 
00430 // fl_ready() is just like fl_wait(0.0) except no callbacks are done:
00431 int fl_ready() {
00432   if (PeekMessage(&fl_msg, NULL, 0, 0, PM_NOREMOVE)) return 1;
00433   if (!nfds) return 0;
00434   timeval t;
00435   t.tv_sec = 0;
00436   t.tv_usec = 0;
00437   fd_set fdt[3];
00438   memcpy(fdt, fdsets, sizeof fdt);
00439   return get_wsock_mod() ? s_wsock_select(0,&fdt[0],&fdt[1],&fdt[2],&t) : 0;
00440 }
00441 
00443 
00444 int Fl::x()
00445 {
00446   RECT r;
00447 
00448   SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
00449   return r.left;
00450 }
00451 
00452 int Fl::y()
00453 {
00454   RECT r;
00455 
00456   SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
00457   return r.top;
00458 }
00459 
00460 int Fl::h()
00461 {
00462   RECT r;
00463 
00464   SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
00465   return r.bottom - r.top;
00466 }
00467 
00468 int Fl::w()
00469 {
00470   RECT r;
00471 
00472   SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
00473   return r.right - r.left;
00474 }
00475 
00476 void Fl::get_mouse(int &x, int &y) {
00477   POINT p;
00478   GetCursorPos(&p);
00479   x = p.x;
00480   y = p.y;
00481 }
00482 
00484 // code used for selections:
00485 
00486 char *fl_selection_buffer[2];
00487 int fl_selection_length[2];
00488 int fl_selection_buffer_length[2];
00489 char fl_i_own_selection[2];
00490 
00491 UINT fl_get_lcid_codepage(LCID id)
00492 {
00493   char buf[8];
00494   buf[GetLocaleInfo(id, LOCALE_IDEFAULTANSICODEPAGE, buf, 8)] = 0;
00495   return atol(buf);
00496 }
00497 
00498 // Convert \n -> \r\n
00499 class Lf2CrlfConvert {
00500   char *out;
00501   int outlen;
00502 public:
00503   Lf2CrlfConvert(const char *in, int inlen) {
00504     outlen = 0;
00505     const char *i;
00506     char *o;
00507     int lencount;
00508     // Predict size of \r\n conversion buffer
00509     for ( i=in, lencount = inlen; lencount--; ) {
00510       if ( *i == '\r' && *(i+1) == '\n' )       // leave \r\n untranslated
00511         { i+=2; outlen+=2; }
00512       else if ( *i == '\n' )                    // \n by itself? leave room to insert \r
00513         { i++; outlen+=2; }
00514       else
00515         { ++i; ++outlen; }
00516     }
00517     // Alloc conversion buffer + NULL
00518     out = new char[outlen+1];
00519     // Handle \n -> \r\n conversion
00520     for ( i=in, o=out, lencount = inlen; lencount--; ) {
00521       if ( *i == '\r' && *(i+1) == '\n' )       // leave \r\n untranslated
00522         { *o++ = *i++; *o++ = *i++; }
00523       else if ( *i == '\n' )                    // \n by itself? insert \r
00524         { *o++ = '\r'; *o++ = *i++; }
00525       else
00526         { *o++ = *i++; }
00527     }
00528     *o++ = 0;
00529   }
00530   ~Lf2CrlfConvert() {
00531     delete[] out;
00532   }
00533   int GetLength() const { return(outlen); }
00534   const char* GetValue() const { return(out); }
00535 };
00536 
00537 // call this when you create a selection:
00538 void Fl::copy(const char *stuff, int len, int clipboard) {
00539   if (!stuff || len<0) return;
00540 
00541   // Convert \n -> \r\n (for old apps like Notepad, DOS)
00542   Lf2CrlfConvert buf(stuff, len);
00543   len = buf.GetLength();
00544   stuff = buf.GetValue();
00545 
00546   if (len+1 > fl_selection_buffer_length[clipboard]) {
00547     delete[] fl_selection_buffer[clipboard];
00548     fl_selection_buffer[clipboard] = new char[len+100];
00549     fl_selection_buffer_length[clipboard] = len+100;
00550   }
00551   memcpy(fl_selection_buffer[clipboard], stuff, len);
00552   fl_selection_buffer[clipboard][len] = 0; // needed for direct paste
00553   fl_selection_length[clipboard] = len;
00554   if (clipboard) {
00555     // set up for "delayed rendering":
00556     if (OpenClipboard(NULL)) {
00557       // if the system clipboard works, use it
00558       int utf16_len = fl_utf8toUtf16(fl_selection_buffer[clipboard], fl_selection_length[clipboard], 0, 0);
00559       EmptyClipboard();
00560       HGLOBAL hMem = GlobalAlloc(GHND, utf16_len * 2 + 2); // moveable and zero'ed mem alloc.
00561       LPVOID memLock = GlobalLock(hMem);
00562       fl_utf8toUtf16(fl_selection_buffer[clipboard], fl_selection_length[clipboard], (unsigned short*) memLock, utf16_len + 1);
00563       GlobalUnlock(hMem);
00564       SetClipboardData(CF_UNICODETEXT, hMem);
00565       CloseClipboard();
00566       GlobalFree(hMem);
00567       fl_i_own_selection[clipboard] = 0;
00568     } else {
00569       // only if it fails, instruct paste() to use the internal buffers
00570       fl_i_own_selection[clipboard] = 1;
00571     }
00572   }
00573 }
00574 
00575 // Call this when a "paste" operation happens:
00576 void Fl::paste(Fl_Widget &receiver, int clipboard) {
00577   if (!clipboard || fl_i_own_selection[clipboard]) {
00578     // We already have it, do it quickly without window server.
00579     // Notice that the text is clobbered if set_selection is
00580     // called in response to FL_PASTE!
00581 
00582     // Convert \r\n -> \n
00583     char *i = fl_selection_buffer[clipboard];
00584     if (i==0L) {
00585       Fl::e_text = 0; 
00586       return;
00587     }
00588     Fl::e_text = new char[fl_selection_length[clipboard]+1];
00589     char *o = Fl::e_text;
00590     while (*i) {
00591       if ( *i == '\r' && *(i+1) == '\n') i++;
00592       else *o++ = *i++;
00593     }
00594     *o = 0;
00595     Fl::e_length = o - Fl::e_text;
00596     receiver.handle(FL_PASTE);
00597     delete [] Fl::e_text;
00598     Fl::e_text = 0;
00599   } else {
00600     if (!OpenClipboard(NULL)) return;
00601     HANDLE h = GetClipboardData(CF_UNICODETEXT);
00602     if (h) {
00603       wchar_t *memLock = (wchar_t*) GlobalLock(h);
00604       int utf16_len = wcslen(memLock);
00605       Fl::e_text = (char*) malloc (utf16_len * 4 + 1);
00606       int utf8_len = fl_utf8fromwc(Fl::e_text, utf16_len * 4, memLock, utf16_len);
00607       *(Fl::e_text + utf8_len) = 0;
00608       LPSTR a,b;
00609       a = b = Fl::e_text;
00610       while (*a) { // strip the CRLF pairs ($%$#@^)
00611         if (*a == '\r' && a[1] == '\n') a++;
00612         else *b++ = *a++;
00613       }
00614       *b = 0;
00615       Fl::e_length = b - Fl::e_text;
00616       receiver.handle(FL_PASTE);
00617       GlobalUnlock(h);
00618       free(Fl::e_text);
00619       Fl::e_text = 0;
00620     }
00621     CloseClipboard();
00622   }
00623 }
00624 
00626 char fl_is_ime = 0;
00627 void fl_get_codepage()
00628 {
00629   HKL hkl = GetKeyboardLayout(0);
00630   TCHAR ld[8];
00631 
00632   GetLocaleInfo (LOWORD(hkl), LOCALE_IDEFAULTANSICODEPAGE, ld, 6);
00633   DWORD ccp = atol(ld);
00634   fl_is_ime = 0;
00635 
00636   fl_codepage = ccp;
00637   if (fl_aimm) {
00638     fl_aimm->GetCodePageA(GetKeyboardLayout(0), &fl_codepage);
00639   } else if (get_imm_module() && flImmIsIME(hkl)) {
00640     fl_is_ime = 1;
00641   }
00642 }
00643 
00644 HWND fl_capture;
00645 
00646 static int mouse_event(Fl_Window *window, int what, int button,
00647                        WPARAM wParam, LPARAM lParam)
00648 {
00649   static int px, py, pmx, pmy;
00650   POINT pt;
00651   Fl::e_x = pt.x = (signed short)LOWORD(lParam);
00652   Fl::e_y = pt.y = (signed short)HIWORD(lParam);
00653   ClientToScreen(fl_xid(window), &pt);
00654   Fl::e_x_root = pt.x;
00655   Fl::e_y_root = pt.y;
00656 #ifdef USE_CAPTURE_MOUSE_WIN
00657   Fl_Window *mouse_window = window;     // save "mouse window"
00658 #endif
00659   while (window->parent()) {
00660     Fl::e_x += window->x();
00661     Fl::e_y += window->y();
00662     window = window->window();
00663   }
00664 
00665   ulong state = Fl::e_state & 0xff0000; // keep shift key states
00666 #if 0
00667   // mouse event reports some shift flags, perhaps save them?
00668   if (wParam & MK_SHIFT) state |= FL_SHIFT;
00669   if (wParam & MK_CONTROL) state |= FL_CTRL;
00670 #endif
00671   if (wParam & MK_LBUTTON) state |= FL_BUTTON1;
00672   if (wParam & MK_MBUTTON) state |= FL_BUTTON2;
00673   if (wParam & MK_RBUTTON) state |= FL_BUTTON3;
00674   Fl::e_state = state;
00675 
00676   switch (what) {
00677   case 1: // double-click
00678     if (Fl::e_is_click) {Fl::e_clicks++; goto J1;}
00679   case 0: // single-click
00680     Fl::e_clicks = 0;
00681   J1:
00682 #ifdef USE_CAPTURE_MOUSE_WIN
00683     if (!fl_capture) SetCapture(fl_xid(mouse_window));  // use mouse window
00684 #else
00685     if (!fl_capture) SetCapture(fl_xid(window));        // use main window
00686 #endif
00687     Fl::e_keysym = FL_Button + button;
00688     Fl::e_is_click = 1;
00689     px = pmx = Fl::e_x_root; py = pmy = Fl::e_y_root;
00690     return Fl::handle(FL_PUSH,window);
00691 
00692   case 2: // release:
00693     if (!fl_capture) ReleaseCapture();
00694     Fl::e_keysym = FL_Button + button;
00695     return Fl::handle(FL_RELEASE,window);
00696 
00697   case 3: // move:
00698   default: // avoid compiler warning
00699     // MSWindows produces extra events even if mouse does not move, ignore em:
00700     if (Fl::e_x_root == pmx && Fl::e_y_root == pmy) return 1;
00701     pmx = Fl::e_x_root; pmy = Fl::e_y_root;
00702     if (abs(Fl::e_x_root-px)>5 || abs(Fl::e_y_root-py)>5) Fl::e_is_click = 0;
00703     return Fl::handle(FL_MOVE,window);
00704 
00705   }
00706 }
00707 
00708 // convert a MSWindows VK_x to an Fltk (X) Keysym:
00709 // See also the inverse converter in Fl_get_key_win32.cxx
00710 // This table is in numeric order by VK:
00711 static const struct {unsigned short vk, fltk, extended;} vktab[] = {
00712   {VK_BACK,     FL_BackSpace},
00713   {VK_TAB,      FL_Tab},
00714   {VK_CLEAR,    FL_KP+'5',      0xff0b/*XK_Clear*/},
00715   {VK_RETURN,   FL_Enter,       FL_KP_Enter},
00716   {VK_SHIFT,    FL_Shift_L,     FL_Shift_R},
00717   {VK_CONTROL,  FL_Control_L,   FL_Control_R},
00718   {VK_MENU,     FL_Alt_L,       FL_Alt_R},
00719   {VK_PAUSE,    FL_Pause},
00720   {VK_CAPITAL,  FL_Caps_Lock},
00721   {VK_ESCAPE,   FL_Escape},
00722   {VK_SPACE,    ' '},
00723   {VK_PRIOR,    FL_KP+'9',      FL_Page_Up},
00724   {VK_NEXT,     FL_KP+'3',      FL_Page_Down},
00725   {VK_END,      FL_KP+'1',      FL_End},
00726   {VK_HOME,     FL_KP+'7',      FL_Home},
00727   {VK_LEFT,     FL_KP+'4',      FL_Left},
00728   {VK_UP,       FL_KP+'8',      FL_Up},
00729   {VK_RIGHT,    FL_KP+'6',      FL_Right},
00730   {VK_DOWN,     FL_KP+'2',      FL_Down},
00731   {VK_SNAPSHOT, FL_Print},      // does not work on NT
00732   {VK_INSERT,   FL_KP+'0',      FL_Insert},
00733   {VK_DELETE,   FL_KP+'.',      FL_Delete},
00734   {VK_LWIN,     FL_Meta_L},
00735   {VK_RWIN,     FL_Meta_R},
00736   {VK_APPS,     FL_Menu},
00737   {VK_MULTIPLY, FL_KP+'*'},
00738   {VK_ADD,      FL_KP+'+'},
00739   {VK_SUBTRACT, FL_KP+'-'},
00740   {VK_DECIMAL,  FL_KP+'.'},
00741   {VK_DIVIDE,   FL_KP+'/'},
00742   {VK_NUMLOCK,  FL_Num_Lock},
00743   {VK_SCROLL,   FL_Scroll_Lock},
00744   {0xba,        ';'},
00745   {0xbb,        '='},
00746   {0xbc,        ','},
00747   {0xbd,        '-'},
00748   {0xbe,        '.'},
00749   {0xbf,        '/'},
00750   {0xc0,        '`'},
00751   {0xdb,        '['},
00752   {0xdc,        '\\'},
00753   {0xdd,        ']'},
00754   {0xde,        '\''}
00755 };
00756 static int ms2fltk(int vk, int extended) {
00757   static unsigned short vklut[256];
00758   static unsigned short extendedlut[256];
00759   if (!vklut[1]) { // init the table
00760     unsigned int i;
00761     for (i = 0; i < 256; i++) vklut[i] = tolower(i);
00762     for (i=VK_F1; i<=VK_F16; i++) vklut[i] = i+(FL_F-(VK_F1-1));
00763     for (i=VK_NUMPAD0; i<=VK_NUMPAD9; i++) vklut[i] = i+(FL_KP+'0'-VK_NUMPAD0);
00764     for (i = 0; i < sizeof(vktab)/sizeof(*vktab); i++) {
00765       vklut[vktab[i].vk] = vktab[i].fltk;
00766       extendedlut[vktab[i].vk] = vktab[i].extended;
00767     }
00768     for (i = 0; i < 256; i++) if (!extendedlut[i]) extendedlut[i] = vklut[i];
00769   }
00770   return extended ? extendedlut[vk] : vklut[vk];
00771 }
00772 
00773 #if USE_COLORMAP
00774 extern HPALETTE fl_select_palette(void); // in fl_color_win32.cxx
00775 #endif
00776 
00777 
00781 
00782 struct Win32Timer
00783 {
00784   UINT_PTR handle;
00785   Fl_Timeout_Handler callback;
00786   void *data;
00787 };
00788 static Win32Timer* win32_timers;
00789 static int win32_timer_alloc;
00790 static int win32_timer_used;
00791 static HWND s_TimerWnd;
00792 
00793 static void realloc_timers()
00794 {
00795   if (win32_timer_alloc == 0) {
00796     win32_timer_alloc = 8;
00797   }
00798   win32_timer_alloc *= 2;
00799   Win32Timer* new_timers = new Win32Timer[win32_timer_alloc];
00800   memset(new_timers, 0, sizeof(Win32Timer) * win32_timer_used);
00801   memcpy(new_timers, win32_timers, sizeof(Win32Timer) * win32_timer_used);
00802   Win32Timer* delete_me = win32_timers;
00803   win32_timers = new_timers;
00804   delete [] delete_me;
00805 }
00806 
00807 static void delete_timer(Win32Timer& t)
00808 {
00809   KillTimer(s_TimerWnd, t.handle);
00810   memset(&t, 0, sizeof(Win32Timer));
00811 }
00812 
00815 
00816 static Fl_Window* resize_bug_fix;
00817 
00818 extern void fl_save_pen(void);
00819 extern void fl_restore_pen(void);
00820 
00821 static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
00822 {
00823   // Copy the message to fl_msg so add_handler code can see it, it is
00824   // already there if this is called by DispatchMessage, but not if
00825   // Windows calls this directly.
00826   fl_msg.hwnd = hWnd;
00827   fl_msg.message = uMsg;
00828   fl_msg.wParam = wParam;
00829   fl_msg.lParam = lParam;
00830   //fl_msg.time = ???
00831   //fl_msg.pt = ???
00832   //fl_msg.lPrivate = ???
00833 
00834   Fl_Window *window = fl_find(hWnd);
00835 
00836   if (window) switch (uMsg) {
00837 
00838   case WM_QUIT: // this should not happen?
00839     Fl::fatal("WM_QUIT message");
00840 
00841   case WM_CLOSE: // user clicked close box
00842     Fl::handle(FL_CLOSE, window);
00843     PostQuitMessage(0);
00844     return 0;
00845 
00846   case WM_SYNCPAINT :
00847   case WM_NCPAINT :
00848   case WM_ERASEBKGND :
00849     // Andreas Weitl - WM_SYNCPAINT needs to be passed to DefWindowProc
00850     // so that Windows can generate the proper paint messages...
00851     // Similarly, WM_NCPAINT and WM_ERASEBKGND need this, too...
00852     break;
00853 
00854   case WM_PAINT: {
00855     Fl_Region R;
00856     Fl_X *i = Fl_X::i(window);
00857     i->wait_for_expose = 0;
00858     char redraw_whole_window = false;
00859     if (!i->region && window->damage()) {
00860       // Redraw the whole window...
00861       i->region = CreateRectRgn(0, 0, window->w(), window->h());
00862       redraw_whole_window = true;
00863     }
00864 
00865     // We need to merge WIN32's damage into FLTK's damage.
00866     R = CreateRectRgn(0,0,0,0);
00867     int r = GetUpdateRgn(hWnd,R,0);
00868     if (r==NULLREGION && !redraw_whole_window) {
00869       break;
00870     }
00871 
00872     if (i->region) {
00873       // Also tell WIN32 that we are drawing someplace else as well...
00874       CombineRgn(i->region, i->region, R, RGN_OR);
00875       XDestroyRegion(R);
00876     } else {
00877       i->region = R;
00878     }
00879     if (window->type() == FL_DOUBLE_WINDOW) ValidateRgn(hWnd,0);
00880     else ValidateRgn(hWnd,i->region);
00881 
00882     window->clear_damage((uchar)(window->damage()|FL_DAMAGE_EXPOSE));
00883     // These next two statements should not be here, so that all update
00884     // is deferred until Fl::flush() is called during idle.  However WIN32
00885     // apparently is very unhappy if we don't obey it and draw right now.
00886     // Very annoying!
00887     fl_GetDC(hWnd); // Make sure we have a DC for this window...
00888     fl_save_pen();
00889     i->flush();
00890     fl_restore_pen();
00891     window->clear_damage();
00892     } return 0;
00893 
00894   case WM_LBUTTONDOWN:  mouse_event(window, 0, 1, wParam, lParam); return 0;
00895   case WM_LBUTTONDBLCLK:mouse_event(window, 1, 1, wParam, lParam); return 0;
00896   case WM_LBUTTONUP:    mouse_event(window, 2, 1, wParam, lParam); return 0;
00897   case WM_MBUTTONDOWN:  mouse_event(window, 0, 2, wParam, lParam); return 0;
00898   case WM_MBUTTONDBLCLK:mouse_event(window, 1, 2, wParam, lParam); return 0;
00899   case WM_MBUTTONUP:    mouse_event(window, 2, 2, wParam, lParam); return 0;
00900   case WM_RBUTTONDOWN:  mouse_event(window, 0, 3, wParam, lParam); return 0;
00901   case WM_RBUTTONDBLCLK:mouse_event(window, 1, 3, wParam, lParam); return 0;
00902   case WM_RBUTTONUP:    mouse_event(window, 2, 3, wParam, lParam); return 0;
00903 
00904   case WM_MOUSEMOVE:
00905 #ifdef USE_TRACK_MOUSE
00906     if (track_mouse_win != window) {
00907       TRACKMOUSEEVENT tme;
00908       tme.cbSize    = sizeof(TRACKMOUSEEVENT);
00909       tme.dwFlags   = TME_LEAVE;
00910       tme.hwndTrack = hWnd;
00911       _TrackMouseEvent(&tme);
00912       track_mouse_win = window;
00913     }
00914 #endif // USE_TRACK_MOUSE
00915     mouse_event(window, 3, 0, wParam, lParam);
00916     return 0;
00917 
00918   case WM_MOUSELEAVE:
00919     if (track_mouse_win == window) { // we left the top level window !
00920       Fl_Window *tw = window;
00921       while (tw->parent()) tw = tw->window(); // find top level window
00922       Fl::belowmouse(0);
00923       Fl::handle(FL_LEAVE, tw);
00924     }
00925     track_mouse_win = 0; // force TrackMouseEvent() restart
00926     break;
00927 
00928   case WM_SETFOCUS:
00929     Fl::handle(FL_FOCUS, window);
00930     break;
00931 
00932   case WM_KILLFOCUS:
00933     Fl::handle(FL_UNFOCUS, window);
00934     Fl::flush(); // it never returns to main loop when deactivated...
00935     break;
00936 
00937   case WM_SHOWWINDOW:
00938     if (!window->parent()) {
00939       Fl::handle(wParam ? FL_SHOW : FL_HIDE, window);
00940     }
00941     break;
00942 
00943   case WM_ACTIVATEAPP:
00944     // From eric@vfx.sel.sony.com, we should process WM_ACTIVATEAPP
00945     // messages to restore the correct state of the shift/ctrl/alt/lock
00946     // keys...  Added control, shift, alt, and meta keys, and changed
00947     // to use GetAsyncKeyState and do it when wParam is 1
00948     // (that means we have focus...)
00949     if (wParam)
00950     {
00951       ulong state = 0;
00952       if (GetAsyncKeyState(VK_CAPITAL)) state |= FL_CAPS_LOCK;
00953       if (GetAsyncKeyState(VK_NUMLOCK)) state |= FL_NUM_LOCK;
00954       if (GetAsyncKeyState(VK_SCROLL)) state |= FL_SCROLL_LOCK;
00955       if (GetAsyncKeyState(VK_CONTROL)&~1) state |= FL_CTRL;
00956       if (GetAsyncKeyState(VK_SHIFT)&~1) state |= FL_SHIFT;
00957       if (GetAsyncKeyState(VK_MENU)) state |= FL_ALT;
00958       if ((GetAsyncKeyState(VK_LWIN)|GetAsyncKeyState(VK_RWIN))&~1) state |= FL_META;
00959       Fl::e_state = state;
00960       return 0;
00961     }
00962     break;
00963 
00964   case WM_INPUTLANGCHANGE:
00965     fl_get_codepage();
00966     break;
00967   case WM_IME_COMPOSITION:
00968 //      if (!fl_is_nt4() && lParam & GCS_RESULTCLAUSE) {
00969 //              HIMC himc = ImmGetContext(hWnd);
00970 //              wlen = ImmGetCompositionStringW(himc, GCS_RESULTSTR,
00971 //                      wbuf, sizeof(wbuf)) / sizeof(short);
00972 //              if (wlen < 0) wlen = 0;
00973 //              wbuf[wlen] = 0;
00974 //              ImmReleaseContext(hWnd, himc);
00975 //      }
00976         break;
00977   case WM_KEYDOWN:
00978   case WM_SYSKEYDOWN:
00979   case WM_KEYUP:
00980   case WM_SYSKEYUP:
00981     // save the keysym until we figure out the characters:
00982     Fl::e_keysym = Fl::e_original_keysym = ms2fltk(wParam,lParam&(1<<24));
00983     // See if TranslateMessage turned it into a WM_*CHAR message:
00984     if (PeekMessageW(&fl_msg, hWnd, WM_CHAR, WM_SYSDEADCHAR, PM_REMOVE))
00985     {
00986       uMsg = fl_msg.message;
00987       wParam = fl_msg.wParam;
00988       lParam = fl_msg.lParam;
00989     }
00990   case WM_DEADCHAR:
00991   case WM_SYSDEADCHAR:
00992   case WM_CHAR:
00993   case WM_SYSCHAR: {
00994     ulong state = Fl::e_state & 0xff000000; // keep the mouse button state
00995     // if GetKeyState is expensive we might want to comment some of these out:
00996     if (GetKeyState(VK_SHIFT)&~1) state |= FL_SHIFT;
00997     if (GetKeyState(VK_CAPITAL)) state |= FL_CAPS_LOCK;
00998     if (GetKeyState(VK_CONTROL)&~1) state |= FL_CTRL;
00999     // Alt gets reported for the Alt-GR switch on foreign keyboards.
01000     // so we need to check the event as well to get it right:
01001     if ((lParam&(1<<29)) //same as GetKeyState(VK_MENU)
01002         && uMsg != WM_CHAR) state |= FL_ALT;
01003     if (GetKeyState(VK_NUMLOCK)) state |= FL_NUM_LOCK;
01004     if ((GetKeyState(VK_LWIN)|GetKeyState(VK_RWIN))&~1) {
01005       // WIN32 bug?  GetKeyState returns garbage if the user hit the
01006       // meta key to pop up start menu.  Sigh.
01007       if ((GetAsyncKeyState(VK_LWIN)|GetAsyncKeyState(VK_RWIN))&~1)
01008         state |= FL_META;
01009     }
01010     if (GetKeyState(VK_SCROLL)) state |= FL_SCROLL_LOCK;
01011     Fl::e_state = state;
01012     static char buffer[1024];
01013     if (uMsg == WM_CHAR || uMsg == WM_SYSCHAR) {
01014 
01015       xchar u = (xchar) wParam;
01016 //    Fl::e_length = fl_unicode2utf(&u, 1, buffer);
01017       Fl::e_length = fl_utf8fromwc(buffer, 1024, &u, 1);
01018       buffer[Fl::e_length] = 0;
01019 
01020 
01021     } else if (Fl::e_keysym >= FL_KP && Fl::e_keysym <= FL_KP_Last) {
01022       if (state & FL_NUM_LOCK) {
01023         // Convert to regular keypress...
01024         buffer[0] = Fl::e_keysym-FL_KP;
01025         Fl::e_length = 1;
01026       } else {
01027         // Convert to special keypress...
01028         buffer[0] = 0;
01029         Fl::e_length = 0;
01030         switch (Fl::e_keysym) {
01031           case FL_KP + '0' :
01032             Fl::e_keysym = FL_Insert;
01033             break;
01034           case FL_KP + '1' :
01035             Fl::e_keysym = FL_End;
01036             break;
01037           case FL_KP + '2' :
01038             Fl::e_keysym = FL_Down;
01039             break;
01040           case FL_KP + '3' :
01041             Fl::e_keysym = FL_Page_Down;
01042             break;
01043           case FL_KP + '4' :
01044             Fl::e_keysym = FL_Left;
01045             break;
01046           case FL_KP + '6' :
01047             Fl::e_keysym = FL_Right;
01048             break;
01049           case FL_KP + '7' :
01050             Fl::e_keysym = FL_Home;
01051             break;
01052           case FL_KP + '8' :
01053             Fl::e_keysym = FL_Up;
01054             break;
01055           case FL_KP + '9' :
01056             Fl::e_keysym = FL_Page_Up;
01057             break;
01058           case FL_KP + '.' :
01059             Fl::e_keysym = FL_Delete;
01060             break;
01061           case FL_KP + '/' :
01062           case FL_KP + '*' :
01063           case FL_KP + '-' :
01064           case FL_KP + '+' :
01065             buffer[0] = Fl::e_keysym-FL_KP;
01066             Fl::e_length = 1;
01067             break;
01068         }
01069       }
01070     } else if ((lParam & (1<<31))==0){
01071       //buffer[0] = 0;
01072       //Fl::e_length = 0;
01073       xchar u = (xchar) wParam;
01074 //    Fl::e_length = fl_unicode2utf(&u, 1, buffer);
01075       Fl::e_length = fl_utf8fromwc(buffer, 1024, &u, 1);
01076       buffer[Fl::e_length] = 0;
01077     }
01078     Fl::e_text = buffer;
01079     if (lParam & (1<<31)) { // key up events.
01080       if (Fl::handle(FL_KEYUP, window)) return 0;
01081       break;
01082     }
01083     // for (int i = lParam&0xff; i--;)
01084     while (window->parent()) window = window->window();
01085     if (Fl::handle(FL_KEYBOARD,window)) {
01086           if (uMsg==WM_DEADCHAR || uMsg==WM_SYSDEADCHAR)
01087                 Fl::compose_state = 1;
01088           return 0;
01089         }
01090     break;}
01091 
01092   case WM_MOUSEWHEEL: {
01093     static int delta = 0; // running total of all motion
01094     delta += (SHORT)(HIWORD(wParam));
01095     Fl::e_dy = -delta / WHEEL_DELTA;
01096     delta += Fl::e_dy * WHEEL_DELTA;
01097     if (Fl::e_dy) Fl::handle(FL_MOUSEWHEEL, window);
01098     return 0;
01099   }
01100 
01101   case WM_GETMINMAXINFO:
01102     Fl_X::i(window)->set_minmax((LPMINMAXINFO)lParam);
01103     break;
01104 
01105   case WM_SIZE:
01106     if (!window->parent()) {
01107       if (wParam == SIZE_MINIMIZED || wParam == SIZE_MAXHIDE) {
01108         Fl::handle(FL_HIDE, window);
01109       } else {
01110         Fl::handle(FL_SHOW, window);
01111         resize_bug_fix = window;
01112         window->size(LOWORD(lParam), HIWORD(lParam));
01113       }
01114     }
01115     break;
01116 
01117   case WM_MOVE: {
01118     resize_bug_fix = window;
01119     int nx = LOWORD(lParam);
01120     int ny = HIWORD(lParam);
01121     if (nx & 0x8000) nx -= 65536;
01122     if (ny & 0x8000) ny -= 65536;
01123     window->position(nx, ny); }
01124     break;
01125 
01126   case WM_SETCURSOR:
01127     if (LOWORD(lParam) == HTCLIENT) {
01128       while (window->parent()) window = window->window();
01129       SetCursor(Fl_X::i(window)->cursor);
01130       return 0;
01131     }
01132     break;
01133 
01134 #if USE_COLORMAP
01135   case WM_QUERYNEWPALETTE :
01136     fl_GetDC(hWnd);
01137     if (fl_select_palette()) InvalidateRect(hWnd, NULL, FALSE);
01138     break;
01139 
01140   case WM_PALETTECHANGED:
01141     fl_GetDC(hWnd);
01142     if ((HWND)wParam != hWnd && fl_select_palette()) UpdateColors(fl_gc);
01143     break;
01144 
01145   case WM_CREATE :
01146     fl_GetDC(hWnd);
01147     fl_select_palette();
01148     break;
01149 #endif
01150 
01151   case WM_DESTROYCLIPBOARD:
01152     fl_i_own_selection[1] = 0;
01153     return 1;
01154 
01155   case WM_RENDERALLFORMATS:
01156     fl_i_own_selection[1] = 0;
01157     // Windoze seems unhappy unless I do these two steps. Documentation
01158     // seems to vary on whether opening the clipboard is necessary or
01159     // is in fact wrong:
01160     CloseClipboard();
01161     OpenClipboard(NULL);
01162     // fall through...
01163   case WM_RENDERFORMAT: {
01164     HANDLE h;
01165 
01166 //  int l = fl_utf_nb_char((unsigned char*)fl_selection_buffer[1], fl_selection_length[1]);
01167     int l = fl_utf8toUtf16(fl_selection_buffer[1], fl_selection_length[1], NULL, 0); // Pass NULL buffer to query length required
01168     h = GlobalAlloc(GHND, (l+1) * sizeof(unsigned short));
01169     if (h) {
01170       unsigned short *g = (unsigned short*) GlobalLock(h);
01171 //    fl_utf2unicode((unsigned char *)fl_selection_buffer[1], fl_selection_length[1], (xchar*)g);
01172       l = fl_utf8toUtf16(fl_selection_buffer[1], fl_selection_length[1], g, (l+1));
01173       g[l] = 0;
01174       GlobalUnlock(h);
01175       SetClipboardData(CF_UNICODETEXT, h);
01176     }
01177 
01178     // Windoze also seems unhappy if I don't do this. Documentation very
01179     // unclear on what is correct:
01180     if (fl_msg.message == WM_RENDERALLFORMATS) CloseClipboard();
01181     return 1;}
01182 
01183   default:
01184     if (Fl::handle(0,0)) return 0;
01185     break;
01186   }
01187 
01188 
01189   return DefWindowProcW(hWnd, uMsg, wParam, lParam);
01190 }
01191 
01193 // This function gets the dimensions of the top/left borders and
01194 // the title bar, if there is one, based on the FL_BORDER, FL_MODAL
01195 // and FL_NONMODAL flags, and on the window's size range.
01196 // It returns the following values:
01197 //
01198 // value | border | title bar
01199 //   0   |  none  |   no
01200 //   1   |  fix   |   yes
01201 //   2   |  size  |   yes
01202 
01203 int Fl_X::fake_X_wm(const Fl_Window* w,int &X,int &Y, int &bt,int &bx, int &by) {
01204   int W, H, xoff, yoff, dx, dy;
01205   int ret = bx = by = bt = 0;
01206 
01207   int fallback = 1;
01208   if (!w->parent()) {
01209     HWND hwnd = fl_xid(w);
01210     if (hwnd) {
01211       // The block below calculates the window borders by requesting the
01212       // required decorated window rectangle for a desired client rectangle.
01213       // If any part of the function above fails, we will drop to a 
01214       // fallback to get the best guess which is always available.
01215       HWND hwnd = fl_xid(w);
01216       // request the style flags of this window, as WIN32 sees them
01217       LONG style = GetWindowLong(hwnd, GWL_STYLE);
01218       LONG exstyle = GetWindowLong(hwnd, GWL_EXSTYLE);
01219       RECT r;
01220       r.left = w->x();
01221       r.top = w->y();
01222       r.right = w->x()+w->w();
01223       r.bottom = w->y()+w->h();
01224       // get the decoration rectangle for the desired client rectangle
01225       BOOL ok = AdjustWindowRectEx(&r, style, FALSE, exstyle);
01226       if (ok) {
01227         X = r.left;
01228         Y = r.top;
01229         W = r.right - r.left;
01230         H = r.bottom - r.top;
01231         bx = w->x() - r.left;
01232         by = r.bottom - w->y() - w->h(); // height of the bootm frame
01233         bt = w->y() - r.top - by; // height of top caption bar
01234         xoff = bx;
01235         yoff = by + bt;
01236         dx = W - w->w();
01237         dy = H - w->h();
01238         if (w->size_range_set && (w->maxw != w->minw || w->maxh != w->minh))
01239           ret = 2;
01240         else
01241           ret = 1;
01242         fallback = 0;
01243       }
01244     }
01245   }
01246   // This is the original (pre 1.1.7) routine to calculate window border sizes.
01247   if (fallback) {
01248     if (w->border() && !w->parent()) {
01249       if (w->size_range_set && (w->maxw != w->minw || w->maxh != w->minh)) {
01250         ret = 2;
01251         bx = GetSystemMetrics(SM_CXSIZEFRAME);
01252         by = GetSystemMetrics(SM_CYSIZEFRAME);
01253       } else {
01254         ret = 1;
01255         bx = GetSystemMetrics(SM_CXFIXEDFRAME);
01256         by = GetSystemMetrics(SM_CYFIXEDFRAME);
01257       }
01258       bt = GetSystemMetrics(SM_CYCAPTION);
01259     }
01260     //The coordinates of the whole window, including non-client area
01261     xoff = bx;
01262     yoff = by + bt;
01263     dx = 2*bx;
01264     dy = 2*by + bt;
01265     X = w->x()-xoff;
01266     Y = w->y()-yoff;
01267     W = w->w()+dx;
01268     H = w->h()+dy;
01269   }
01270 
01271   //Proceed to positioning the window fully inside the screen, if possible
01272   //Make border's lower right corner visible
01273   int scr_x, scr_y, scr_w, scr_h;
01274   Fl::screen_xywh(scr_x, scr_y, scr_w, scr_h, X, Y);
01275   if (scr_x+scr_w < X+W) X = scr_x+scr_w - W;
01276   if (scr_y+scr_h < Y+H) Y = scr_y+scr_h - H;
01277   //Make border's upper left corner visible
01278   if (X<scr_x) X = scr_x;
01279   if (Y<scr_y) Y = scr_y;
01280   //Make client area's lower right corner visible
01281   if (scr_x+scr_w < X+dx+ w->w()) X = scr_x+scr_w - w->w() - dx;
01282   if (scr_y+scr_h < Y+dy+ w->h()) Y = scr_y+scr_h - w->h() - dy;
01283   //Make client area's upper left corner visible
01284   if (X+xoff < scr_x) X = scr_x-xoff;
01285   if (Y+yoff < scr_y) Y = scr_y-yoff;
01286   //Return the client area's top left corner in (X,Y)
01287   X+=xoff;
01288   Y+=yoff;
01289 
01290   return ret;
01291 }
01292 
01294 
01295 void Fl_Window::resize(int X,int Y,int W,int H) {
01296   UINT flags = SWP_NOSENDCHANGING | SWP_NOZORDER 
01297              | SWP_NOACTIVATE | SWP_NOOWNERZORDER;
01298   int is_a_resize = (W != w() || H != h());
01299   int resize_from_program = (this != resize_bug_fix);
01300   if (!resize_from_program) resize_bug_fix = 0;
01301   if (X != x() || Y != y()) {
01302     force_position(1);
01303   } else {
01304     if (!is_a_resize) return;
01305     flags |= SWP_NOMOVE;
01306   }
01307   if (is_a_resize) {
01308     Fl_Group::resize(X,Y,W,H);
01309     if (visible_r()) {
01310       redraw(); 
01311       // only wait for exposure if this window has a size - a window 
01312       // with no width or height will never get an exposure event
01313       if (i && W>0 && H>0)
01314         i->wait_for_expose = 1;
01315     }
01316   } else {
01317     x(X); y(Y);
01318     flags |= SWP_NOSIZE;
01319   }
01320   if (!border()) flags |= SWP_NOACTIVATE;
01321   if (resize_from_program && shown()) {
01322     if (!resizable()) size_range(w(),h(),w(),h());
01323     int dummy_x, dummy_y, bt, bx, by;
01324     //Ignore window managing when resizing, so that windows (and more
01325     //specifically menus) can be moved offscreen.
01326     if (Fl_X::fake_X_wm(this, dummy_x, dummy_y, bt, bx, by)) {
01327       X -= bx;
01328       Y -= by+bt;
01329       W += 2*bx;
01330       H += 2*by+bt;
01331     }
01332     // avoid zero size windows. A zero sized window on Win32
01333     // will cause continouly  new redraw events.
01334     if (W<=0) W = 1;
01335     if (H<=0) H = 1;
01336     SetWindowPos(i->xid, 0, X, Y, W, H, flags);
01337   }
01338 }
01339 
01341 
01342 /*
01343  * This silly little class remembers the name of all window classes 
01344  * we register to avoid double registration. It has the added bonus 
01345  * of freeing everything on application close as well.
01346  */
01347 class NameList {
01348 public:
01349   NameList() { name = (char**)malloc(sizeof(char**)); NName = 1; nName = 0; }
01350   ~NameList() { 
01351     int i;
01352     for (i=0; i<nName; i++) free(name[i]);
01353     if (name) free(name); 
01354   }
01355   void add_name(const char *n) {
01356     if (NName==nName) {
01357       NName += 5;
01358       name = (char**)realloc(name, NName * sizeof(char*));
01359     }
01360     name[nName++] = strdup(n);
01361   }
01362   char has_name(const char *n) {
01363     int i;
01364     for (i=0; i<nName; i++) {
01365       if (strcmp(name[i], n)==0) return 1;
01366     }
01367     return 0;
01368   }
01369 private:
01370   char **name;
01371   int nName, NName;
01372 };
01373 
01374 void fl_fix_focus(); // in Fl.cxx
01375 
01376 char fl_show_iconic;    // hack for Fl_Window::iconic()
01377 // int fl_background_pixel = -1; // color to use for background
01378 HCURSOR fl_default_cursor;
01379 UINT fl_wake_msg = 0;
01380 int fl_disable_transient_for; // secret method of removing TRANSIENT_FOR
01381 
01382 Fl_X* Fl_X::make(Fl_Window* w) {
01383   Fl_Group::current(0); // get rid of very common user bug: forgot end()
01384 
01385   // if the window is a subwindow and our parent is not mapped yet, we
01386   // mark this window visible, so that mapping the parent at a later
01387   // point in time will call this function again to finally map the subwindow.
01388   if (w->parent() && !Fl_X::i(w->window())) {
01389     w->set_visible();
01390     return 0L;
01391   }
01392 
01393   static NameList class_name_list;
01394   static const char *first_class_name = 0L;
01395   const char *class_name = w->xclass();
01396   if (!class_name) class_name = first_class_name; // reuse first class name used
01397   if (!class_name) class_name = "FLTK"; // default to create a "FLTK" WNDCLASS
01398   if (!first_class_name) {
01399     first_class_name = class_name;
01400   }
01401 
01402   wchar_t class_namew[100]; // (limited) buffer for Windows class name
01403 
01404   // convert UTF-8 class_name to wchar_t for RegisterClassExW and CreateWindowExW
01405 
01406   fl_utf8toUtf16(class_name,strlen(class_name),         // in
01407                  (unsigned short*)class_namew,          // out
01408                  sizeof(class_namew)/sizeof(wchar_t));  // max. size
01409 
01410   if (!class_name_list.has_name(class_name)) {
01411     WNDCLASSEXW wcw;
01412     memset(&wcw, 0, sizeof(wcw));
01413     wcw.cbSize = sizeof(WNDCLASSEXW);
01414 
01415     // Documentation states a device context consumes about 800 bytes
01416     // of memory... so who cares? If 800 bytes per window is what it
01417     // takes to speed things up, I'm game.
01418     //wc.style = CS_HREDRAW | CS_VREDRAW | CS_CLASSDC | CS_DBLCLKS;
01419     wcw.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS;
01420     wcw.lpfnWndProc = (WNDPROC)WndProc;
01421     wcw.cbClsExtra = wcw.cbWndExtra = 0;
01422     wcw.hInstance = fl_display;
01423     if (!w->icon())
01424       w->icon((void *)LoadIcon(NULL, IDI_APPLICATION));
01425     wcw.hIcon = wcw.hIconSm = (HICON)w->icon();
01426     wcw.hCursor = fl_default_cursor = LoadCursor(NULL, IDC_ARROW);
01427     //uchar r,g,b; Fl::get_color(FL_GRAY,r,g,b);
01428     //wc.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(r,g,b));
01429     wcw.hbrBackground = NULL;
01430     wcw.lpszMenuName = NULL;
01431     wcw.lpszClassName = class_namew;
01432     RegisterClassExW(&wcw);
01433     class_name_list.add_name(class_name);
01434   }
01435 
01436   const wchar_t* message_namew = L"FLTK::ThreadWakeup";
01437   if (!fl_wake_msg) fl_wake_msg = RegisterWindowMessageW(message_namew);
01438 
01439   HWND parent;
01440   DWORD style = WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
01441   DWORD styleEx = WS_EX_LEFT;
01442 
01443   int xp = w->x();
01444   int yp = w->y();
01445   int wp = w->w();
01446   int hp = w->h();
01447 
01448   int showit = 1;
01449 
01450   if (w->parent()) {
01451     style |= WS_CHILD;
01452     styleEx |= WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT;
01453     parent = fl_xid(w->window());
01454   } else {
01455     if (!w->size_range_set) {
01456       if (w->resizable()) {
01457         Fl_Widget *o = w->resizable();
01458         int minw = o->w(); if (minw > 100) minw = 100;
01459         int minh = o->h(); if (minh > 100) minh = 100;
01460         w->size_range(w->w() - o->w() + minw, w->h() - o->h() + minh, 0, 0);
01461       } else {
01462         w->size_range(w->w(), w->h(), w->w(), w->h());
01463       }
01464     }
01465     styleEx |= WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT;
01466     int xwm = xp , ywm = yp , bt, bx, by;
01467     switch (fake_X_wm(w, xwm, ywm, bt, bx, by)) {
01468       // No border (used for menus)
01469       case 0: style |= WS_POPUP;
01470               styleEx |= WS_EX_TOOLWINDOW;
01471               break;
01472 
01473       // Thin border and title bar
01474       case 1: style |= WS_DLGFRAME | WS_CAPTION; break;
01475 
01476       // Thick, resizable border and title bar, with maximize button
01477       case 2: style |= WS_THICKFRAME | WS_MAXIMIZEBOX | WS_CAPTION ; break;
01478     }
01479     if (by+bt) {
01480       if (!w->modal()) style |= WS_SYSMENU | WS_MINIMIZEBOX;
01481       wp += 2*bx;
01482       hp += 2*by+bt;
01483     }
01484     if (!w->force_position()) {
01485       xp = yp = CW_USEDEFAULT;
01486     } else {
01487       if (!Fl::grab()) {
01488         xp = xwm; yp = ywm;
01489         w->x(xp);w->y(yp);
01490       }
01491       xp -= bx;
01492       yp -= by+bt;
01493     }
01494 
01495     parent = 0;
01496     if (w->non_modal() && Fl_X::first && !fl_disable_transient_for) {
01497       // find some other window to be "transient for":
01498       Fl_Window* w = Fl_X::first->w;
01499       while (w->parent()) w = w->window();
01500       parent = fl_xid(w);
01501       if (!w->visible()) showit = 0;
01502     } else if (Fl::grab()) parent = fl_xid(Fl::grab());
01503   }
01504 
01505   Fl_X* x = new Fl_X;
01506   x->other_xid = 0;
01507   x->setwindow(w);
01508   x->region = 0;
01509   x->private_dc = 0;
01510   x->cursor = fl_default_cursor;
01511   if (!fl_codepage) fl_get_codepage();
01512 
01513   WCHAR *lab = NULL;
01514   if (w->label()) {
01515     int l = strlen(w->label());
01516 //  lab = (WCHAR*) malloc((l + 1) * sizeof(short));
01517 //  l = fl_utf2unicode((unsigned char*)w->label(), l, (xchar*)lab);
01518 //  lab[l] = 0;
01519     unsigned wlen = fl_utf8toUtf16(w->label(), l, NULL, 0); // Pass NULL to query length
01520     wlen++;
01521     lab = (WCHAR *) malloc(sizeof(WCHAR)*wlen);
01522     wlen = fl_utf8toUtf16(w->label(), l, (unsigned short*)lab, wlen);
01523     lab[wlen] = 0;
01524   }
01525   x->xid = CreateWindowExW(
01526     styleEx,
01527     class_namew, lab, style,
01528     xp, yp, wp, hp,
01529     parent,
01530     NULL, // menu
01531     fl_display,
01532     NULL // creation parameters
01533   );
01534   if (lab) free(lab);
01535 
01536   x->next = Fl_X::first;
01537   Fl_X::first = x;
01538 
01539   x->wait_for_expose = 1;
01540   if (fl_show_iconic) {showit = 0; fl_show_iconic = 0;}
01541   if (showit) {
01542     w->set_visible();
01543     int old_event = Fl::e_number;
01544     w->handle(Fl::e_number = FL_SHOW); // get child windows to appear
01545     Fl::e_number = old_event;
01546     w->redraw(); // force draw to happen
01547   }
01548   // If we've captured the mouse, we dont want to activate any
01549   // other windows from the code, or we loose the capture.
01550   ShowWindow(x->xid, !showit ? SW_SHOWMINNOACTIVE :
01551              (Fl::grab() || (style & WS_POPUP)) ? SW_SHOWNOACTIVATE : SW_SHOWNORMAL);
01552 
01553   // Register all windows for potential drag'n'drop operations
01554   fl_OleInitialize();
01555   RegisterDragDrop(x->xid, flIDropTarget);
01556 
01557   if (!fl_aimm) {
01558     CoCreateInstance(CLSID_CActiveIMM, NULL, CLSCTX_INPROC_SERVER,
01559                      IID_IActiveIMMApp, (void**) &fl_aimm);
01560     if (fl_aimm) {
01561       fl_aimm->Activate(TRUE);
01562     }
01563   }
01564 
01565   if (w->modal()) {Fl::modal_ = w; fl_fix_focus();}
01566   return x;
01567 }
01568 
01569 
01570 
01571 
01575 
01576 
01577 static LRESULT CALLBACK s_TimerProc(HWND hwnd, UINT msg,
01578                                     WPARAM wParam, LPARAM lParam)
01579 {
01580   switch (msg) {
01581   case WM_TIMER:
01582     {
01583       unsigned int id = wParam - 1;
01584       if (id < (unsigned int)win32_timer_used && win32_timers[id].handle) {
01585         Fl_Timeout_Handler cb   = win32_timers[id].callback;
01586         void*              data = win32_timers[id].data;
01587         delete_timer(win32_timers[id]);
01588         if (cb) {
01589           (*cb)(data);
01590         }
01591       }
01592     }
01593     return 0;
01594 
01595   default:
01596     break;
01597   }
01598 
01599   return DefWindowProc(hwnd, msg, wParam, lParam);
01600 }
01601 
01602 void Fl::add_timeout(double time, Fl_Timeout_Handler cb, void* data)
01603 {
01604   repeat_timeout(time, cb, data);
01605 }
01606 
01607 void Fl::repeat_timeout(double time, Fl_Timeout_Handler cb, void* data)
01608 {
01609   int timer_id = -1;
01610   for (int i = 0;  i < win32_timer_used;  ++i) {
01611     if ( !win32_timers[i].handle ) {
01612       timer_id = i;
01613       break;
01614     }
01615   }
01616   if (timer_id == -1) {
01617     if (win32_timer_used == win32_timer_alloc) {
01618       realloc_timers();
01619     }
01620     timer_id = win32_timer_used++;
01621   }
01622   unsigned int elapsed = (unsigned int)(time * 1000);
01623 
01624   if ( !s_TimerWnd ) {
01625     const char* timer_class = "FLTimer";
01626     WNDCLASSEX wc;
01627     memset(&wc, 0, sizeof(wc));
01628     wc.cbSize = sizeof (wc);
01629     wc.style = CS_CLASSDC;
01630     wc.lpfnWndProc = (WNDPROC)s_TimerProc;
01631     wc.hInstance = fl_display;
01632     wc.lpszClassName = timer_class;
01633     /*ATOM atom =*/ RegisterClassEx(&wc);
01634     // create a zero size window to handle timer events
01635     s_TimerWnd = CreateWindowEx(WS_EX_LEFT | WS_EX_TOOLWINDOW,
01636                                 timer_class, "",
01637                                 WS_POPUP,
01638                                 0, 0, 0, 0,
01639                                 NULL, NULL, fl_display, NULL);
01640     // just in case this OS won't let us create a 0x0 size window:
01641     if (!s_TimerWnd)
01642       s_TimerWnd = CreateWindowEx(WS_EX_LEFT | WS_EX_TOOLWINDOW,
01643                                   timer_class, "",
01644                                   WS_POPUP,
01645                                   0, 0, 1, 1,
01646                                   NULL, NULL, fl_display, NULL);
01647     ShowWindow(s_TimerWnd, SW_SHOWNOACTIVATE);
01648   }
01649 
01650   win32_timers[timer_id].callback = cb;
01651   win32_timers[timer_id].data     = data;
01652 
01653   win32_timers[timer_id].handle =
01654     SetTimer(s_TimerWnd, timer_id + 1, elapsed, NULL);
01655 }
01656 
01657 int Fl::has_timeout(Fl_Timeout_Handler cb, void* data)
01658 {
01659   for (int i = 0;  i < win32_timer_used;  ++i) {
01660     Win32Timer& t = win32_timers[i];
01661     if (t.handle  &&  t.callback == cb  &&  t.data == data) {
01662       return 1;
01663     }
01664   }
01665   return 0;
01666 }
01667 
01668 void Fl::remove_timeout(Fl_Timeout_Handler cb, void* data)
01669 {
01670   int i;
01671   for (i = 0;  i < win32_timer_used;  ++i) {
01672     Win32Timer& t = win32_timers[i];
01673     if (t.handle  &&  t.callback == cb  &&
01674       (t.data == data  ||  data == NULL)) {
01675       delete_timer(t);
01676     }
01677   }
01678 }
01679 
01682 
01683 
01684 
01686 
01687 HINSTANCE fl_display = GetModuleHandle(NULL);
01688 
01689 void Fl_Window::size_range_() {
01690   size_range_set = 1;
01691 }
01692 
01693 void Fl_X::set_minmax(LPMINMAXINFO minmax)
01694 {
01695   int td, wd, hd, dummy_x, dummy_y;
01696 
01697   fake_X_wm(w, dummy_x, dummy_y, td, wd, hd);
01698   wd *= 2;
01699   hd *= 2;
01700   hd += td;
01701 
01702   minmax->ptMinTrackSize.x = w->minw + wd;
01703   minmax->ptMinTrackSize.y = w->minh + hd;
01704   if (w->maxw) {
01705     minmax->ptMaxTrackSize.x = w->maxw + wd;
01706     minmax->ptMaxSize.x = w->maxw + wd;
01707   }
01708   if (w->maxh) {
01709     minmax->ptMaxTrackSize.y = w->maxh + hd;
01710     minmax->ptMaxSize.y = w->maxh + hd;
01711   }
01712 }
01713 
01715 
01716 #include <FL/filename.H> // need so FL_EXPORT fl_filename_name works
01717 
01718 // returns pointer to the filename, or null if name ends with '/'
01719 const char *fl_filename_name(const char *name) {
01720   const char *p,*q;
01721   if (!name) return (0);
01722   q = name;
01723   if (q[0] && q[1]==':') q += 2; // skip leading drive letter
01724   for (p = q; *p; p++) if (*p == '/' || *p == '\\') q = p+1;
01725   return q;
01726 }
01727 
01728 void Fl_Window::label(const char *name,const char *iname) {
01729   Fl_Widget::label(name);
01730   iconlabel_ = iname;
01731   if (shown() && !parent()) {
01732     if (!name) name = "";
01733     int l = strlen(name);
01734 //  WCHAR *lab = (WCHAR*) malloc((l + 1) * sizeof(short));
01735 //  l = fl_utf2unicode((unsigned char*)name, l, (xchar*)lab);
01736     unsigned wlen = fl_utf8toUtf16(name, l, NULL, 0); // Pass NULL to query length
01737     wlen++;
01738     unsigned short * lab = (unsigned short*)malloc(sizeof(unsigned short)*wlen);
01739     wlen = fl_utf8toUtf16(name, l, lab, wlen);
01740     lab[wlen] = 0;
01741     SetWindowTextW(i->xid, (WCHAR *)lab);
01742     free(lab);
01743   }
01744 }
01745 
01747 // Implement the virtual functions for the base Fl_Window class:
01748 
01749 // If the box is a filled rectangle, we can make the redisplay *look*
01750 // faster by using X's background pixel erasing.  We can make it
01751 // actually *be* faster by drawing the frame only, this is done by
01752 // setting fl_boxcheat, which is seen by code in fl_drawbox.cxx:
01753 // For WIN32 it looks like all windows share a background color, so
01754 // I use FL_GRAY for this and only do this cheat for windows that are
01755 // that color.
01756 // Actually it is totally disabled.
01757 // Fl_Widget *fl_boxcheat;
01758 //static inline int can_boxcheat(uchar b) {return (b==1 || (b&2) && b<=15);}
01759 
01760 void Fl_Window::show() {
01761   image(Fl::scheme_bg_);
01762   if (Fl::scheme_bg_) {
01763     labeltype(FL_NORMAL_LABEL);
01764     align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE | FL_ALIGN_CLIP);
01765   } else {
01766     labeltype(FL_NO_LABEL);
01767   }
01768   Fl_Tooltip::exit(this);
01769   if (!shown()) {
01770     // if (can_boxcheat(box())) fl_background_pixel = fl_xpixel(color());
01771     Fl_X::make(this);
01772   } else {
01773     // Once again, we would lose the capture if we activated the window.
01774     if (IsIconic(i->xid)) OpenIcon(i->xid);
01775     if (!fl_capture) BringWindowToTop(i->xid);
01776     //ShowWindow(i->xid,fl_capture?SW_SHOWNOACTIVATE:SW_RESTORE);
01777   }
01778 #ifdef USE_PRINT_BUTTON
01779 void preparePrintFront(void);
01780 preparePrintFront();
01781 #endif
01782 }
01783 
01784 Fl_Window *Fl_Window::current_;
01785 // the current context
01786 HDC fl_gc = 0;
01787 // the current window handle, initially set to -1 so we can correctly
01788 // allocate fl_GetDC(0)
01789 HWND fl_window = NULL;
01790 
01791 // Here we ensure only one GetDC is ever in place.
01792 HDC fl_GetDC(HWND w) {
01793   if (fl_gc) {
01794     if (w == fl_window  &&  fl_window != NULL) return fl_gc;
01795     if (fl_window) fl_release_dc(fl_window, fl_gc); // ReleaseDC
01796   }
01797   fl_gc = GetDC(w);
01798   fl_save_dc(w, fl_gc);
01799   fl_window = w;
01800   // calling GetDC seems to always reset these: (?)
01801   SetTextAlign(fl_gc, TA_BASELINE|TA_LEFT);
01802   SetBkMode(fl_gc, TRANSPARENT);
01803 
01804   return fl_gc;
01805 }
01806 
01807 // make X drawing go into this window (called by subclass flush() impl.)
01808 void Fl_Window::make_current() {
01809   fl_GetDC(fl_xid(this));
01810 
01811 #if USE_COLORMAP
01812   // Windows maintains a hardware and software color palette; the
01813   // SelectPalette() call updates the current soft->hard mapping
01814   // for all drawing calls, so we must select it here before any
01815   // code does any drawing...
01816 
01817   fl_select_palette();
01818 #endif // USE_COLORMAP
01819 
01820   current_ = this;
01821   fl_clip_region(0);
01822 
01823 
01824 }
01825 
01826 /* Make sure that all allocated fonts are released. This works only if 
01827    Fl::run() is allowed to exit by closing all windows. Calling 'exit(int)'
01828    will not automatically free any fonts. */
01829 void fl_free_fonts(void)
01830 {
01831 // remove the Fl_Font_Descriptor chains
01832   int i;
01833   Fl_Fontdesc * s;
01834   Fl_Font_Descriptor * f;
01835   Fl_Font_Descriptor * ff;
01836   for (i=0; i<FL_FREE_FONT; i++) {
01837     s = fl_fonts + i;
01838     for (f=s->first; f; f=ff) {
01839       ff = f->next;
01840       delete f;
01841       s->first = ff;
01842     }
01843   }
01844 }
01845 
01846 
01848 //
01849 //  The following routines help fix a problem with the leaking of Windows
01850 //  Device Context (DC) objects. The 'proper' protocol is for a program to
01851 //  acquire a DC, save its state, do the modifications needed for drawing,
01852 //  perform the drawing, restore the initial state, and release the DC. In
01853 //  FLTK, the save and restore steps have previously been omitted and DCs are
01854 //  not properly released, leading to a great number of DC leaks. As some
01855 //  Windows "OSs" will hang when any process exceeds roughly 10,000 GDI objects,
01856 //  it is important to control GDI leaks, which are much more important than memory
01857 //  leaks. The following struct, global variable, and routines help implement
01858 //  the above protocol for those cases where the GetDC and RestoreDC are not in
01859 //  the same routine. For each GetDC, fl_save_dc is used to create an entry in 
01860 //  a linked list that saves the window handle, the DC handle, and the initial
01861 //  state. When the DC is to be released, 'fl_release_dc' is called. It restores
01862 //  the initial state and releases the DC. When the program exits, 'fl_cleanup_dc_list'
01863 //  frees any remaining nodes in the list.
01864 
01865 struct Win_DC_List {      // linked list 
01866   HWND    window;         // window handle
01867   HDC     dc;             // device context handle
01868   int     saved_dc;       // initial state of DC
01869   Win_DC_List * next;     // pointer to next item
01870 };
01871 
01872 static Win_DC_List * win_DC_list = 0;
01873 
01874 void fl_save_dc( HWND w, HDC dc) {
01875   Win_DC_List * t;
01876   t = new Win_DC_List;
01877   t->window = w;
01878   t->dc = dc;
01879   t->saved_dc = SaveDC(dc);
01880   if (win_DC_list)
01881     t->next = win_DC_list;
01882   else
01883     t->next = NULL;
01884   win_DC_list = t;
01885 }
01886 
01887 void fl_release_dc(HWND w, HDC dc) {
01888   Win_DC_List * t= win_DC_list;
01889   Win_DC_List * prev = 0;
01890   if (!t)
01891     return;
01892   do {
01893     if (t->dc == dc) {
01894       RestoreDC(dc, t->saved_dc);
01895       ReleaseDC(w, dc);
01896       if (!prev) {
01897         win_DC_list = t->next;   // delete first item
01898       } else {
01899         prev->next = t->next;       // one in the middle
01900       }
01901       delete (t);
01902       return;
01903     }
01904     prev = t;
01905     t = t->next;
01906   } while (t);
01907 }
01908 
01909 void fl_cleanup_dc_list(void) {          // clean up the list
01910   Win_DC_List * t = win_DC_list;
01911   if (!t)return;
01912   do {
01913     RestoreDC(t->dc, t->saved_dc);
01914     ReleaseDC(t->window, t->dc);
01915     win_DC_list = t->next;
01916     delete (t);
01917     t = win_DC_list;
01918   } while(t);
01919 }
01920 
01921 Fl_Region XRectangleRegion(int x, int y, int w, int h) {
01922   if (Fl_Surface_Device::surface()->class_name() == Fl_Display_Device::class_id) return CreateRectRgn(x,y,x+w,y+h);
01923   // because rotation may apply, the rectangle becomes a polygon in device coords
01924   POINT pt[4] = { {x, y}, {x + w, y}, {x + w, y + h}, {x, y + h} };
01925   LPtoDP(fl_gc, pt, 4);
01926   return CreatePolygonRgn(pt, 4, ALTERNATE);
01927 }
01928 
01929 #ifdef USE_PRINT_BUTTON
01930 // to test the Fl_Printer class creating a "Print front window" button in a separate window
01931 // contains also preparePrintFront call above
01932 #include <FL/Fl_Printer.H>
01933 #include <FL/Fl_Button.H>
01934 void printFront(Fl_Widget *o, void *data)
01935 {
01936   Fl_Printer printer;
01937   o->window()->hide();
01938   Fl_Window *win = Fl::first_window();
01939   if(!win) return;
01940   int w, h;
01941   if( printer.start_job(1) ) { o->window()->show(); return; }
01942   if( printer.start_page() ) { o->window()->show(); return; }
01943   printer.printable_rect(&w,&h);
01944   // scale the printer device so that the window fits on the page
01945   float scale = 1;
01946   if (win->w() > w || win->h() > h) {
01947     scale = (float)w/win->w();
01948     if ((float)h/win->h() < scale) scale = (float)h/win->h();
01949     printer.scale(scale, scale);
01950   }
01951 // #define ROTATE 20.0
01952 #ifdef ROTATE
01953   printer.scale(scale * 0.8, scale * 0.8);
01954   printer.printable_rect(&w, &h);
01955   printer.origin(w/2, h/2 );
01956   printer.rotate(ROTATE);
01957   printer.print_widget( win, - win->w()/2, - win->h()/2 );
01958   //printer.print_window_part( win, 0,0, win->w(), win->h(), - win->w()/2, - win->h()/2 );
01959 #else
01960   printer.print_widget( win );
01961   //printer.print_window_part( win, 0,0, win->w(), win->h() );
01962 #endif
01963   printer.end_page();
01964   printer.end_job();
01965   o->window()->show();
01966 }
01967 
01968 void preparePrintFront(void)
01969 {
01970   static BOOL first=TRUE;
01971   if(!first) return;
01972   first=FALSE;
01973   static Fl_Window w(0,0,120,30);
01974   static Fl_Button b(0,0,w.w(),w.h(), "Print front window");
01975   b.callback(printFront);
01976   w.end();
01977   w.show();
01978 }
01979 #endif // USE_PRINT_BUTTON
01980 
01981 #endif // FL_DOXYGEN
01982 
01983 //
01984 // End of "$Id: Fl_win32.cxx 8205 2011-01-07 01:12:04Z matt $".
01985 //