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_cocoa.mm

Go to the documentation of this file.
00001 //
00002 // "$Id: Fl_cocoa.mm 6971 2009-04-13 07:32:01Z matt $"
00003 //
00004 // MacOS-Cocoa 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 
00029 // (without permission)
00030 //
00031 // "Three Compiles for 68Ks under the sky,
00032 // Seven Compiles for PPCs in their fragments of code,
00033 // Nine Compiles for Mortal Carbon doomed to die,
00034 // One Compile for Mach-O Cocoa on its Mach-O throne,
00035 // in the Land of MacOS X where the Drop-Shadows lie.
00036 // 
00037 // One Compile to link them all, One Compile to merge them,
00038 // One Compile to copy them all and in the bundle bind them,
00039 // in the Land of MacOS X where the Drop-Shadows lie."
00040 
00041 #ifdef __APPLE__
00042 
00043 #define CONSOLIDATE_MOTION 0
00044 extern "C" {
00045 #include <pthread.h>
00046 }
00047 
00048 
00049 #include <FL/Fl.H>
00050 #include <FL/x.H>
00051 #include <FL/Fl_Window.H>
00052 #include <FL/Fl_Tooltip.H>
00053 #include <FL/Fl_Sys_Menu_Bar.H>
00054 #include <FL/Fl_Printer.H>
00055 #include <FL/Fl_Input_.H>
00056 #include <FL/Fl_Text_Display.H>
00057 #include <stdio.h>
00058 #include <stdlib.h>
00059 #include "flstring.h"
00060 #include <unistd.h>
00061 #include <stdarg.h>
00062 
00063 #import <Cocoa/Cocoa.h>
00064 
00065 #ifndef NSINTEGER_DEFINED // appears with 10.5 in NSObjCRuntime.h
00066 #if defined(__LP64__) && __LP64__
00067 typedef long NSInteger;
00068 typedef unsigned long NSUInteger;
00069 #else
00070 typedef long NSInteger;
00071 typedef unsigned int NSUInteger;
00072 #endif
00073 #endif
00074 
00075 
00076 // #define DEBUG_SELECT         // UNCOMMENT FOR SELECT()/THREAD DEBUGGING
00077 #ifdef DEBUG_SELECT
00078 #include <stdio.h>              // testing
00079 #define DEBUGMSG(msg)           if ( msg ) fprintf(stderr, msg);
00080 #define DEBUGPERRORMSG(msg)     if ( msg ) perror(msg)
00081 #define DEBUGTEXT(txt)          txt
00082 #else
00083 #define DEBUGMSG(msg)
00084 #define DEBUGPERRORMSG(msg)
00085 #define DEBUGTEXT(txt)          NULL
00086 #endif /*DEBUG_SELECT*/
00087 
00088 // external functions
00089 extern void fl_fix_focus();
00090 extern Fl_Offscreen fl_create_offscreen_with_alpha(int w, int h);
00091 
00092 // forward definition of functions in this file
00093 // converting cr lf converter function
00094 static void convert_crlf(char * string, size_t len);
00095 static void createAppleMenu(void);
00096 static Fl_Region MacRegionMinusRect(Fl_Region r, int x,int y,int w,int h);
00097 static void cocoaMouseHandler(NSEvent *theEvent);
00098 
00099 static Fl_Quartz_Graphics_Driver fl_quartz_driver;
00100 static Fl_Display_Device fl_quartz_display(&fl_quartz_driver);
00101 FL_EXPORT Fl_Display_Device *fl_display_device = (Fl_Display_Device*)&fl_quartz_display; // does not change
00102 FL_EXPORT Fl_Graphics_Driver *fl_graphics_driver = (Fl_Graphics_Driver*)&fl_quartz_driver; // the current target device of graphics operations
00103 FL_EXPORT Fl_Surface_Device *fl_surface = (Fl_Surface_Device*)fl_display_device; // the current target surface of graphics operations
00104 
00105 // public variables
00106 int fl_screen;
00107 CGContextRef fl_gc = 0;
00108 void *fl_system_menu;                   // this is really a NSMenu*
00109 Fl_Sys_Menu_Bar *fl_sys_menu_bar = 0;
00110 void *fl_default_cursor;                // this is really a NSCursor*
00111 void *fl_capture = 0;                   // (NSWindow*) we need this to compensate for a missing(?) mouse capture
00112 bool fl_show_iconic;                    // true if called from iconize() - shows the next created window in collapsed state
00113 //int fl_disable_transient_for;           // secret method of removing TRANSIENT_FOR
00114 Window fl_window;
00115 Fl_Window *Fl_Window::current_;
00116 int fl_mac_os_version = 0;              // the version number of the running Mac OS X (e.g., 0x1064 for 10.6.4)
00117 
00118 // forward declarations of variables in this file
00119 static int got_events = 0;
00120 static Fl_Window* resize_from_system;
00121 
00122 #if CONSOLIDATE_MOTION
00123 static Fl_Window* send_motion;
00124 extern Fl_Window* fl_xmousewin;
00125 #endif
00126 
00127 enum { FLTKTimerEvent = 1, FLTKDataReadyEvent };
00128 
00129 
00130 /* fltk-utf8 placekeepers */
00131 void fl_reset_spot()
00132 {
00133 }
00134 
00135 void fl_set_spot(int font, int size, int X, int Y, int W, int H, Fl_Window *win)
00136 {
00137 }
00138 
00139 void fl_set_status(int x, int y, int w, int h)
00140 {
00141 }
00142 
00143 /*
00144  * Mac keyboard lookup table
00145  */
00146 static unsigned short macKeyLookUp[128] =
00147 {
00148   'a', 's', 'd', 'f', 'h', 'g', 'z', 'x',
00149   'c', 'v', '^', 'b', 'q', 'w', 'e', 'r',
00150   
00151   'y', 't', '1', '2', '3', '4', '6', '5',
00152   '=', '9', '7', '-', '8', '0', ']', 'o',
00153   
00154   'u', '[', 'i', 'p', FL_Enter, 'l', 'j', '\'',
00155   'k', ';', '\\', ',', '/', 'n', 'm', '.',
00156   
00157   FL_Tab, ' ', '`', FL_BackSpace, 
00158   FL_KP_Enter, FL_Escape, 0, 0/*FL_Meta_L*/,
00159   0/*FL_Shift_L*/, 0/*FL_Caps_Lock*/, 0/*FL_Alt_L*/, 0/*FL_Control_L*/, 
00160   0/*FL_Shift_R*/, 0/*FL_Alt_R*/, 0/*FL_Control_R*/, 0,
00161   
00162   0, FL_KP+'.', FL_Right, FL_KP+'*', 0, FL_KP+'+', FL_Left, FL_Num_Lock,
00163   FL_Down, 0, 0, FL_KP+'/', FL_KP_Enter, FL_Up, FL_KP+'-', 0,
00164   
00165   0, FL_KP+'=', FL_KP+'0', FL_KP+'1', FL_KP+'2', FL_KP+'3', FL_KP+'4', FL_KP+'5',
00166   FL_KP+'6', FL_KP+'7', 0, FL_KP+'8', FL_KP+'9', 0, 0, 0,
00167   
00168   FL_F+5, FL_F+6, FL_F+7, FL_F+3, FL_F+8, FL_F+9, 0, FL_F+11,
00169   0, 0/*FL_F+13*/, FL_Print, FL_Scroll_Lock, 0, FL_F+10, FL_Menu, FL_F+12,
00170   
00171   0, FL_Pause, FL_Help, FL_Home, FL_Page_Up, FL_Delete, FL_F+4, FL_End,
00172   FL_F+2, FL_Page_Down, FL_F+1, FL_Left, FL_Right, FL_Down, FL_Up, 0/*FL_Power*/,
00173 };
00174 
00175 /*
00176  * convert the current mouse chord into the FLTK modifier state
00177  */
00178 static unsigned int mods_to_e_state( NSUInteger mods )
00179 {
00180   long state = 0;
00181   if ( mods & NSNumericPadKeyMask ) state |= FL_NUM_LOCK;
00182   if ( mods & NSCommandKeyMask ) state |= FL_META;
00183   if ( mods & NSAlternateKeyMask ) state |= FL_ALT;
00184   if ( mods & NSControlKeyMask ) state |= FL_CTRL;
00185   if ( mods & NSShiftKeyMask ) state |= FL_SHIFT;
00186   if ( mods & NSAlphaShiftKeyMask ) state |= FL_CAPS_LOCK;
00187   unsigned int ret = ( Fl::e_state & 0xff000000 ) | state;
00188   Fl::e_state = ret;
00189   //printf( "State 0x%08x (%04x)\n", Fl::e_state, mods );
00190   return ret;
00191 }
00192 
00193 
00194 /*
00195  * convert the current key chord into the FLTK keysym
00196  */
00197 
00198  static void mods_to_e_keysym( NSUInteger mods )
00199  {
00200  if ( mods & NSCommandKeyMask ) Fl::e_keysym = FL_Meta_L;
00201  else if ( mods & NSNumericPadKeyMask ) Fl::e_keysym = FL_Num_Lock;
00202  else if ( mods & NSAlternateKeyMask ) Fl::e_keysym = FL_Alt_L;
00203  else if ( mods & NSControlKeyMask ) Fl::e_keysym = FL_Control_L;
00204  else if ( mods & NSShiftKeyMask ) Fl::e_keysym = FL_Shift_L;
00205  else if ( mods & NSAlphaShiftKeyMask ) Fl::e_keysym = FL_Caps_Lock;
00206  else Fl::e_keysym = 0;
00207  //printf( "to sym 0x%08x (%04x)\n", Fl::e_keysym, mods );
00208  }
00209 
00210 // these pointers are set by the Fl::lock() function:
00211 static void nothing() {}
00212 void (*fl_lock_function)() = nothing;
00213 void (*fl_unlock_function)() = nothing;
00214 
00215 //
00216 // Select interface -- how it's implemented:
00217 //     When the user app configures one or more file descriptors to monitor
00218 //     with Fl::add_fd(), we start a separate thread to select() the  data,
00219 //     sending a custom OSX 'FLTK data ready event' to the parent  thread's
00220 //     RunApplicationLoop(), so that it triggers the data  ready  callbacks
00221 //     in the parent thread.                               -erco 04/04/04
00222 //     
00223 #define POLLIN  1
00224 #define POLLOUT 4
00225 #define POLLERR 8
00226 
00227 // Class to handle select() 'data ready'
00228 class DataReady
00229 {
00230   struct FD
00231   {
00232     int fd;
00233     short events;
00234     void (*cb)(int, void*);
00235     void* arg;
00236   };
00237   int nfds, fd_array_size;
00238   FD *fds;
00239   pthread_t tid;                // select()'s thread id
00240   
00241   // Data that needs to be locked (all start with '_')
00242   pthread_mutex_t _datalock;    // data lock
00243   fd_set _fdsets[3];            // r/w/x sets user wants to monitor
00244   int _maxfd;                   // max fd count to monitor
00245   int _cancelpipe[2];           // pipe used to help cancel thread
00246   
00247 public:
00248   DataReady()
00249   {
00250     nfds = 0;
00251     fd_array_size = 0;
00252     fds = 0;
00253     tid = 0;
00254     
00255     pthread_mutex_init(&_datalock, NULL);
00256     FD_ZERO(&_fdsets[0]); FD_ZERO(&_fdsets[1]); FD_ZERO(&_fdsets[2]);
00257     _cancelpipe[0] = _cancelpipe[1] = 0;
00258     _maxfd = 0;
00259   }
00260   
00261   ~DataReady()
00262   {
00263     CancelThread(DEBUGTEXT("DESTRUCTOR\n"));
00264     if (fds) { free(fds); fds = 0; }
00265     nfds = 0;
00266   }
00267   
00268   // Locks
00269   //    The convention for locks: volatile vars start with '_',
00270   //    and must be locked before use. Locked code is prefixed 
00271   //    with /*LOCK*/ to make painfully obvious esp. in debuggers. -erco
00272   //
00273   void DataLock() { pthread_mutex_lock(&_datalock); }
00274   void DataUnlock() { pthread_mutex_unlock(&_datalock); }
00275   
00276   // Accessors
00277   int IsThreadRunning() { return(tid ? 1 : 0); }
00278   int GetNfds() { return(nfds); }
00279   int GetCancelPipe(int ix) { return(_cancelpipe[ix]); }
00280   fd_set GetFdset(int ix) { return(_fdsets[ix]); }
00281   
00282   // Methods
00283   void AddFD(int n, int events, void (*cb)(int, void*), void *v);
00284   void RemoveFD(int n, int events);
00285   int CheckData(fd_set& r, fd_set& w, fd_set& x);
00286   void HandleData(fd_set& r, fd_set& w, fd_set& x);
00287   static void* DataReadyThread(void *self);
00288   void StartThread(void);
00289   void CancelThread(const char *reason);
00290 };
00291 
00292 static DataReady dataready;
00293 
00294 void DataReady::AddFD(int n, int events, void (*cb)(int, void*), void *v)
00295 {
00296   RemoveFD(n, events);
00297   int i = nfds++;
00298   if (i >= fd_array_size) 
00299   {
00300     FD *temp;
00301     fd_array_size = 2*fd_array_size+1;
00302     if (!fds) { temp = (FD*)malloc(fd_array_size*sizeof(FD)); }
00303     else { temp = (FD*)realloc(fds, fd_array_size*sizeof(FD)); }
00304     if (!temp) return;
00305     fds = temp;
00306   }
00307   fds[i].cb  = cb;
00308   fds[i].arg = v;
00309   fds[i].fd  = n;
00310   fds[i].events = events;
00311   DataLock();
00312   /*LOCK*/  if (events & POLLIN)  FD_SET(n, &_fdsets[0]);
00313   /*LOCK*/  if (events & POLLOUT) FD_SET(n, &_fdsets[1]);
00314   /*LOCK*/  if (events & POLLERR) FD_SET(n, &_fdsets[2]);
00315   /*LOCK*/  if (n > _maxfd) _maxfd = n;
00316   DataUnlock();
00317 }
00318 
00319 // Remove an FD from the array
00320 void DataReady::RemoveFD(int n, int events)
00321 {
00322   int i,j;
00323   for (i=j=0; i<nfds; i++) {
00324     if (fds[i].fd == n) {
00325       int e = fds[i].events & ~events;
00326       if (!e) continue; // if no events left, delete this fd
00327       fds[i].events = e;
00328     }
00329     // move it down in the array if necessary:
00330     if (j<i) {
00331       fds[j] = fds[i];
00332     }
00333     j++;
00334   }
00335   nfds = j;
00336   DataLock();
00337   /*LOCK*/  if (events & POLLIN)  FD_CLR(n, &_fdsets[0]);
00338   /*LOCK*/  if (events & POLLOUT) FD_CLR(n, &_fdsets[1]);
00339   /*LOCK*/  if (events & POLLERR) FD_CLR(n, &_fdsets[2]);
00340   /*LOCK*/  if (n == _maxfd) _maxfd--;
00341   DataUnlock();
00342 }
00343 
00344 // CHECK IF USER DATA READY, RETURNS r/w/x INDICATING WHICH IF ANY
00345 int DataReady::CheckData(fd_set& r, fd_set& w, fd_set& x)
00346 {
00347   int ret;
00348   DataLock();
00349   /*LOCK*/  timeval t = { 0, 1 };               // quick check
00350   /*LOCK*/  r = _fdsets[0], w = _fdsets[1], x = _fdsets[2];
00351   /*LOCK*/  ret = ::select(_maxfd+1, &r, &w, &x, &t);
00352   DataUnlock();
00353   if ( ret == -1 ) {
00354     DEBUGPERRORMSG("CheckData(): select()");
00355   }
00356   return(ret);
00357 }
00358 
00359 // HANDLE DATA READY CALLBACKS
00360 void DataReady::HandleData(fd_set& r, fd_set& w, fd_set& x)
00361 {
00362   for (int i=0; i<nfds; i++) {
00363     int f = fds[i].fd;
00364     short revents = 0;
00365     if (FD_ISSET(f, &r)) revents |= POLLIN;
00366     if (FD_ISSET(f, &w)) revents |= POLLOUT;
00367     if (FD_ISSET(f, &x)) revents |= POLLERR;
00368     if (fds[i].events & revents) {
00369       DEBUGMSG("DOING CALLBACK: ");
00370       fds[i].cb(f, fds[i].arg);
00371       DEBUGMSG("DONE\n");
00372     }
00373   }
00374 }
00375 
00376 // DATA READY THREAD
00377 //    This thread watches for changes in user's file descriptors.
00378 //    Sends a 'data ready event' to the main thread if any change.
00379 //
00380 void* DataReady::DataReadyThread(void *o)
00381 {
00382   DataReady *self = (DataReady*)o;
00383   NSAutoreleasePool *localPool;
00384   localPool = [[NSAutoreleasePool alloc] init]; 
00385   while ( 1 ) {                                 // loop until thread cancel or error
00386     // Thread safe local copies of data before each select()
00387     self->DataLock();
00388     /*LOCK*/  int maxfd = self->_maxfd;
00389     /*LOCK*/  fd_set r = self->GetFdset(0);
00390     /*LOCK*/  fd_set w = self->GetFdset(1);
00391     /*LOCK*/  fd_set x = self->GetFdset(2);
00392     /*LOCK*/  int cancelpipe = self->GetCancelPipe(0);
00393     /*LOCK*/  if ( cancelpipe > maxfd ) maxfd = cancelpipe;
00394     /*LOCK*/  FD_SET(cancelpipe, &r);           // add cancelpipe to fd's to watch
00395     /*LOCK*/  FD_SET(cancelpipe, &x);
00396     self->DataUnlock();
00397     // timeval t = { 1000, 0 }; // 1000 seconds;
00398     timeval t = { 2, 0 };       // HACK: 2 secs prevents 'hanging' problem
00399     int ret = ::select(maxfd+1, &r, &w, &x, &t);
00400     pthread_testcancel();       // OSX 10.0.4 and older: needed for parent to cancel
00401     switch ( ret ) {
00402       case 0:   // NO DATA
00403         continue;
00404       case -1:  // ERROR
00405       {
00406         DEBUGPERRORMSG("CHILD THREAD: select() failed");
00407         return(NULL);           // error? exit thread
00408       }
00409       default:  // DATA READY
00410       {
00411         if (FD_ISSET(cancelpipe, &r) || FD_ISSET(cancelpipe, &x))       // cancel?
00412           { return(NULL); }                                             // just exit
00413         DEBUGMSG("CHILD THREAD: DATA IS READY\n");
00414         NSPoint pt={0,0};
00415         NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined location:pt 
00416                                        modifierFlags:0
00417                                            timestamp:0
00418                                         windowNumber:0 context:NULL 
00419                                              subtype:FLTKDataReadyEvent data1:0 data2:0];
00420         [NSApp postEvent:event atStart:NO];
00421         return(NULL);           // done with thread
00422       }
00423     }
00424   }
00425 }
00426 
00427 // START 'DATA READY' THREAD RUNNING, CREATE INTER-THREAD PIPE
00428 void DataReady::StartThread(void)
00429 {
00430   CancelThread(DEBUGTEXT("STARTING NEW THREAD\n"));
00431   DataLock();
00432   /*LOCK*/  pipe(_cancelpipe);  // pipe for sending cancel msg to thread
00433   DataUnlock();
00434   DEBUGMSG("*** START THREAD\n");
00435   pthread_create(&tid, NULL, DataReadyThread, (void*)this);
00436 }
00437 
00438 // CANCEL 'DATA READY' THREAD, CLOSE PIPE
00439 void DataReady::CancelThread(const char *reason)
00440 {
00441   if ( tid ) {
00442     DEBUGMSG("*** CANCEL THREAD: ");
00443     DEBUGMSG(reason);
00444     if ( pthread_cancel(tid) == 0 ) {           // cancel first
00445       DataLock();
00446       /*LOCK*/  write(_cancelpipe[1], "x", 1);  // wake thread from select
00447       DataUnlock();
00448       pthread_join(tid, NULL);                  // wait for thread to finish
00449     }
00450     tid = 0;
00451     DEBUGMSG("(JOINED) OK\n");
00452   }
00453   // Close pipe if open
00454   DataLock();
00455   /*LOCK*/  if ( _cancelpipe[0] ) { close(_cancelpipe[0]); _cancelpipe[0] = 0; }
00456   /*LOCK*/  if ( _cancelpipe[1] ) { close(_cancelpipe[1]); _cancelpipe[1] = 0; }
00457   DataUnlock();
00458 }
00459 
00460 void Fl::add_fd( int n, int events, void (*cb)(int, void*), void *v )
00461 {
00462   dataready.AddFD(n, events, cb, v);
00463 }
00464 
00465 void Fl::add_fd(int fd, void (*cb)(int, void*), void* v)
00466 {
00467   dataready.AddFD(fd, POLLIN, cb, v);
00468 }
00469 
00470 void Fl::remove_fd(int n, int events)
00471 {
00472   dataready.RemoveFD(n, events);
00473 }
00474 
00475 void Fl::remove_fd(int n)
00476 {
00477   dataready.RemoveFD(n, -1);
00478 }
00479 
00480 /*
00481  * Check if there is actually a message pending!
00482  */
00483 int fl_ready()
00484 {
00485   NSEvent *retval = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate dateWithTimeIntervalSinceNow:0]
00486                                     inMode:NSDefaultRunLoopMode dequeue:NO];
00487   return retval != nil;
00488 }
00489 
00490 
00491 static void processFLTKEvent(void) {
00492   dataready.CancelThread(DEBUGTEXT("DATA READY EVENT\n"));
00493   
00494   // CHILD THREAD TELLS US DATA READY
00495   //     Check to see what's ready, and invoke user's cb's
00496   //
00497   fd_set r,w,x;
00498   switch(dataready.CheckData(r,w,x)) {
00499     case 0:     // NO DATA
00500       break;
00501     case -1:    // ERROR
00502       break;
00503     default:    // DATA READY
00504       dataready.HandleData(r,w,x);
00505       break;
00506   }
00507   return;
00508 }
00509 
00510 
00511 /*
00512  * break the current event loop
00513  */
00514 static void breakMacEventLoop()
00515 {
00516   fl_lock_function();
00517   
00518   NSPoint pt={0,0};
00519   NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined location:pt 
00520                                  modifierFlags:0
00521                                      timestamp:0
00522                                   windowNumber:0 context:NULL 
00523                                        subtype:FLTKTimerEvent data1:0 data2:0];
00524   [NSApp postEvent:event atStart:NO];
00525   fl_unlock_function();
00526 }
00527 
00528 //
00529 // MacOS X timers
00530 //
00531 
00532 struct MacTimeout {
00533   Fl_Timeout_Handler callback;
00534   void* data;
00535   CFRunLoopTimerRef timer;
00536   char pending; 
00537 };
00538 static MacTimeout* mac_timers;
00539 static int mac_timer_alloc;
00540 static int mac_timer_used;
00541 
00542 static void realloc_timers()
00543 {
00544   if (mac_timer_alloc == 0) {
00545     mac_timer_alloc = 8;
00546   }
00547   mac_timer_alloc *= 2;
00548   MacTimeout* new_timers = new MacTimeout[mac_timer_alloc];
00549   memset(new_timers, 0, sizeof(MacTimeout)*mac_timer_alloc);
00550   memcpy(new_timers, mac_timers, sizeof(MacTimeout) * mac_timer_used);
00551   MacTimeout* delete_me = mac_timers;
00552   mac_timers = new_timers;
00553   delete [] delete_me;
00554 }
00555 
00556 static void delete_timer(MacTimeout& t)
00557 {
00558   if (t.timer) {
00559     CFRunLoopRemoveTimer(CFRunLoopGetCurrent(),
00560                       t.timer,
00561                       kCFRunLoopDefaultMode);
00562     CFRelease(t.timer);
00563     memset(&t, 0, sizeof(MacTimeout));
00564   }
00565 }
00566 
00567 static void do_timer(CFRunLoopTimerRef timer, void* data)
00568 {
00569   for (int i = 0;  i < mac_timer_used;  ++i) {
00570     MacTimeout& t = mac_timers[i];
00571     if (t.timer == timer  &&  t.data == data) {
00572       t.pending = 0;
00573       (*t.callback)(data);
00574       if (t.pending==0)
00575         delete_timer(t);
00576       break;
00577     }
00578   }
00579   breakMacEventLoop();
00580 }
00581 
00582 @interface FLWindow : NSWindow {
00583   Fl_Window *w;
00584   BOOL containsGLsubwindow;
00585 }
00586 - (FLWindow*)initWithFl_W:(Fl_Window *)flw 
00587               contentRect:(NSRect)rect 
00588                 styleMask:(NSUInteger)windowStyle 
00589                   backing:(NSBackingStoreType)bufferingType 
00590                     defer:(BOOL)deferCreation;
00591 - (Fl_Window *)getFl_Window;
00592 - (BOOL)windowShouldClose:(FLWindow *)w;
00593 - (BOOL)containsGLsubwindow;
00594 - (void)setContainsGLsubwindow:(BOOL)contains;
00595 @end
00596 
00597 @implementation FLWindow
00598 - (FLWindow*)initWithFl_W:(Fl_Window *)flw 
00599               contentRect:(NSRect)rect 
00600                 styleMask:(NSUInteger)windowStyle 
00601                   backing:(NSBackingStoreType)bufferingType 
00602                     defer:(BOOL)deferCreation
00603 {
00604   self = [super initWithContentRect:rect styleMask:windowStyle backing:bufferingType defer:deferCreation];
00605   if (self) {
00606     w = flw;
00607     containsGLsubwindow = NO;
00608   }
00609   return self;
00610 }
00611 - (Fl_Window *)getFl_Window;
00612 {
00613   return w;
00614 }
00615 - (BOOL)windowShouldClose:(FLWindow *)fl
00616 {
00617   Fl::handle( FL_CLOSE, [fl getFl_Window] ); // this might or might not close the window
00618   if (!Fl_X::first) return YES;
00619   Fl_Window *l = Fl::first_window();
00620   while( l != NULL && l != [fl getFl_Window]) l = Fl::next_window(l);
00621   return (l == NULL ? YES : NO);
00622 }
00623 - (BOOL)containsGLsubwindow
00624 {
00625   return containsGLsubwindow;
00626 }
00627 - (void)setContainsGLsubwindow:(BOOL)contains
00628 {
00629   containsGLsubwindow = contains;
00630 }
00631 @end
00632 
00633 /*
00634  * This function is the central event handler.
00635  * It reads events from the event queue using the given maximum time
00636  * Funny enough, it returns the same time that it got as the argument. 
00637  */
00638 static double do_queued_events( double time = 0.0 ) 
00639 {
00640   got_events = 0;
00641   
00642   // Check for re-entrant condition
00643   if ( dataready.IsThreadRunning() ) {
00644     dataready.CancelThread(DEBUGTEXT("AVOID REENTRY\n"));
00645   }
00646   
00647   // Start thread to watch for data ready
00648   if ( dataready.GetNfds() ) {
00649     dataready.StartThread();
00650   }
00651   
00652   fl_unlock_function();
00653   NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask 
00654                                       untilDate:[NSDate dateWithTimeIntervalSinceNow:time] 
00655                                          inMode:NSDefaultRunLoopMode dequeue:YES];  
00656   if (event != nil) {
00657     [NSApp sendEvent:event]; // reimplemented in [FLApplication sendevent:]
00658   }
00659   fl_lock_function();
00660   
00661 #if CONSOLIDATE_MOTION
00662   if (send_motion && send_motion == fl_xmousewin) {
00663     send_motion = 0;
00664     Fl::handle(FL_MOVE, fl_xmousewin);
00665   }
00666 #endif
00667   
00668   return time;
00669 }
00670 
00671 /*
00672  * This public function handles all events. It wait a maximum of 
00673  * 'time' seconds for an event. This version returns 1 if events
00674  * other than the timeout timer were processed.
00675  *
00676  * \todo there is no socket handling in this code whatsoever
00677  */
00678 int fl_wait( double time ) 
00679 {
00680   do_queued_events( time );
00681   return (got_events);
00682 }
00683 
00684 double fl_mac_flush_and_wait(double time_to_wait, char in_idle) {
00685   NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
00686   Fl::flush();
00687   if (Fl::idle && !in_idle) // 'idle' may have been set within flush()
00688     time_to_wait = 0.0;
00689   double retval = fl_wait(time_to_wait);
00690   [pool release];
00691   return retval;
00692 }
00693 
00694 // updates Fl::e_x, Fl::e_y, Fl::e_x_root, and Fl::e_y_root
00695 static void update_e_xy_and_e_xy_root(NSWindow *nsw)
00696 {
00697   NSPoint pt;
00698   pt = [nsw mouseLocationOutsideOfEventStream];
00699   Fl::e_x = int(pt.x);
00700   Fl::e_y = int([[nsw contentView] frame].size.height - pt.y);
00701   pt = [NSEvent mouseLocation];
00702   Fl::e_x_root = int(pt.x);
00703   Fl::e_y_root = int([[nsw screen] frame].size.height - pt.y);
00704 }
00705 
00706 /*
00707  * Cocoa Mousewheel handler
00708  */
00709 static void cocoaMouseWheelHandler(NSEvent *theEvent)
00710 {
00711   // Handle the new "MightyMouse" mouse wheel events. Please, someone explain
00712   // to me why Apple changed the API on this even though the current API
00713   // supports two wheels just fine. Matthias,
00714   fl_lock_function();
00715   
00716   Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window];
00717   if ( !window->shown() ) {
00718     fl_unlock_function();
00719     return;
00720   }
00721   Fl::first_window(window);
00722   
00723   // Under OSX, single mousewheel increments are 0.1,
00724   // so make sure they show up as at least 1..
00725   //
00726   float dx = [theEvent deltaX]; if ( fabs(dx) < 1.0 ) dx = (dx > 0) ? 1.0 : -1.0;
00727   float dy = [theEvent deltaY]; if ( fabs(dy) < 1.0 ) dy = (dy > 0) ? 1.0 : -1.0;
00728   if ([theEvent deltaX] != 0) {
00729     Fl::e_dx = (int)-dx;
00730     Fl::e_dy = 0;
00731     if ( Fl::e_dx) Fl::handle( FL_MOUSEWHEEL, window );
00732   } else if ([theEvent deltaY] != 0) {
00733     Fl::e_dx = 0;
00734     Fl::e_dy = (int)-dy;
00735     if ( Fl::e_dy) Fl::handle( FL_MOUSEWHEEL, window );
00736   } else {
00737     fl_unlock_function();
00738     return;
00739   }
00740   
00741   fl_unlock_function();
00742   
00743   //  return noErr;
00744 }
00745 
00746 /*
00747  * Cocoa Mouse Button Handler
00748  */
00749 static void cocoaMouseHandler(NSEvent *theEvent)
00750 {
00751   static int keysym[] = { 0, FL_Button+1, FL_Button+3, FL_Button+2 };
00752   static int px, py;
00753   static char suppressed = 0;
00754   
00755   fl_lock_function();
00756   
00757   Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window];
00758   if ( !window->shown() ) {
00759     fl_unlock_function();
00760     return;
00761   }
00762   Fl_Window *first = Fl::first_window();
00763   if (first != window && !(first->modal() || first->non_modal())) Fl::first_window(window);
00764   NSPoint pos = [theEvent locationInWindow];
00765   pos.y = window->h() - pos.y;
00766   NSInteger btn = [theEvent buttonNumber]  + 1;
00767   NSUInteger mods = [theEvent modifierFlags];  
00768   int sendEvent = 0;
00769   
00770   NSEventType etype = [theEvent type];
00771   if (etype == NSLeftMouseDown || etype == NSRightMouseDown || etype == NSOtherMouseDown) {
00772     if (btn == 1) Fl::e_state |= FL_BUTTON1;
00773     else if (btn == 3) Fl::e_state |= FL_BUTTON2;
00774     else if (btn == 2) Fl::e_state |= FL_BUTTON3;
00775   }
00776   else if (etype == NSLeftMouseUp || etype == NSRightMouseUp || etype == NSOtherMouseUp) {
00777     if (btn == 1) Fl::e_state &= ~FL_BUTTON1;
00778     else if (btn == 3) Fl::e_state &= ~FL_BUTTON2;
00779     else if (btn == 2) Fl::e_state &= ~FL_BUTTON3;
00780     }
00781     
00782   switch ( etype ) {
00783     case NSLeftMouseDown:
00784     case NSRightMouseDown:
00785     case NSOtherMouseDown:
00786       suppressed = 0;
00787       sendEvent = FL_PUSH;
00788       Fl::e_is_click = 1; 
00789       px = (int)pos.x; py = (int)pos.y;
00790       if ([theEvent clickCount] > 1) 
00791         Fl::e_clicks++;
00792       else
00793         Fl::e_clicks = 0;
00794       // fall through
00795     case NSLeftMouseUp:
00796     case NSRightMouseUp:
00797     case NSOtherMouseUp:
00798       if (suppressed) {
00799         suppressed = 0;
00800         break;
00801       }
00802       if ( !window ) break;
00803       if ( !sendEvent ) {
00804         sendEvent = FL_RELEASE; 
00805       }
00806       Fl::e_keysym = keysym[ btn ];
00807       // fall through
00808     case NSMouseMoved:
00809       suppressed = 0;
00810       if ( !sendEvent ) { 
00811         sendEvent = FL_MOVE; 
00812       }
00813       // fall through
00814     case NSLeftMouseDragged:
00815     case NSRightMouseDragged:
00816     case NSOtherMouseDragged: {
00817       if (suppressed) break;
00818       if ( !sendEvent ) {
00819         sendEvent = FL_MOVE; // Fl::handle will convert into FL_DRAG
00820         if (fabs(pos.x-px)>5 || fabs(pos.y-py)>5) 
00821           Fl::e_is_click = 0;
00822       }
00823       mods_to_e_state( mods );
00824       update_e_xy_and_e_xy_root([theEvent window]);
00825       Fl::handle( sendEvent, window );
00826       }
00827       break;
00828     default:
00829       break;
00830   }
00831   
00832   fl_unlock_function();
00833   
00834   return;
00835 }
00836 
00837 
00838 static void calc_e_text(CFStringRef s, char *buffer, size_t len, unsigned sym)
00839 {
00840   int i, no_text_key = false;
00841   static unsigned notext[] = { // keys that don't emit text
00842     FL_BackSpace, FL_Print, FL_Scroll_Lock, FL_Pause,
00843     FL_Insert, FL_Home, FL_Page_Up, FL_Delete, FL_End, FL_Page_Down,
00844     FL_Left, FL_Up, FL_Right, FL_Down, 
00845     FL_Menu, FL_Num_Lock, FL_Help 
00846   };
00847   int count = sizeof(notext)/sizeof(int);
00848    
00849   if (sym > FL_F && sym <= FL_F_Last) no_text_key = true;
00850   else for (i=0; i < count; i++) {
00851     if (notext[i] == sym) {
00852       no_text_key = true;
00853       break;
00854       }
00855   }
00856   
00857   if (no_text_key) {
00858     buffer[0] = 0;
00859   } else {
00860     CFStringGetCString(s, buffer, len, kCFStringEncodingUTF8);
00861   }
00862 }
00863 
00864 static int cocoaKeyboardHandler(NSEvent *theEvent);
00865 
00866 @interface FLTextView : NSTextView 
00867 // this subclass is needed under OS X <= 10.4 but not under >= 10.5 where the base class is enough
00868 {
00869 }
00870 @end
00871 @implementation FLTextView
00872 - (void)insertText:(id)aString
00873 {
00874   cocoaKeyboardHandler([NSApp currentEvent]);
00875 }
00876 - (void)doCommandBySelector:(SEL)aSelector
00877 {
00878   cocoaKeyboardHandler([NSApp currentEvent]);
00879 }
00880 @end
00881 
00882 /*
00883 Handle cocoa keyboard events
00884 Events during a character composition sequence:
00885  - keydown with deadkey -> [[theEvent characters] length] is 0
00886  - keyup -> [theEvent characters] contains the deadkey: display it temporarily
00887  - keydown with next key -> [theEvent characters] contains the composed character: 
00888     replace the temporary character by this one
00889  - keyup -> [theEvent characters] contains the standard character
00890  */
00891 static int cocoaKeyboardHandler(NSEvent *theEvent)
00892 {
00893   static char buffer[32];
00894   int sendEvent = 0, retval = 0;
00895   Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window];
00896   Fl::first_window(window);
00897   NSUInteger mods;
00898   
00899   fl_lock_function();
00900   // get the modifiers
00901   mods = [theEvent modifierFlags];
00902   // get the key code
00903   UInt32 keyCode = 0, maskedKeyCode = 0;
00904   unsigned short sym = 0;
00905   keyCode = [theEvent keyCode];
00906   NSString *s = [theEvent characters];  
00907   if ( (mods & NSShiftKeyMask) && (mods & NSCommandKeyMask) ) {
00908     s = [s uppercaseString]; // US keyboards return lowercase letter in s if cmd-shift-key is hit
00909   }
00910   // extended keyboards can also send sequences on key-up to generate Kanji etc. codes.
00911   // Some observed prefixes are 0x81 to 0x83, followed by an 8 bit keycode.
00912   // In this mode, there seem to be no key-down codes
00913   // printf("%08x %08x %08x\n", keyCode, mods, key);
00914   maskedKeyCode = keyCode & 0x7f;
00915   switch([theEvent type]) {
00916     case NSKeyDown:
00917       sendEvent = FL_KEYBOARD;
00918       // fall through
00919     case NSKeyUp:
00920       if ( !sendEvent ) {
00921         sendEvent = FL_KEYUP;
00922         Fl::e_state &= 0xbfffffff; // clear the deadkey flag
00923       }
00924       mods_to_e_state( mods ); // process modifier keys
00925       sym = macKeyLookUp[maskedKeyCode];
00926       if (sym < 0xff00) { // a "simple" key
00927         // find the result of this key without modifier
00928         NSString *sim = [theEvent charactersIgnoringModifiers];
00929         UniChar one;
00930         CFStringGetCharacters((CFStringRef)sim, CFRangeMake(0, 1), &one);
00931         // charactersIgnoringModifiers doesn't ignore shift, remove it when it's on
00932         if(one >= 'A' && one <= 'Z') one += 32;
00933         if (one > 0 && one <= 0x7f && (sym<'0' || sym>'9') ) sym = one;
00934       }
00935       Fl::e_keysym = Fl::e_original_keysym = sym;
00936       // Handle FL_KP_Enter on regular keyboards and on Powerbooks
00937       if ( maskedKeyCode==0x4c || maskedKeyCode==0x34) s = @"\r";    
00938       calc_e_text((CFStringRef)s, buffer, sizeof(buffer), sym);
00939       Fl::e_length = strlen(buffer);
00940       Fl::e_text = buffer;
00941     default:                    // prevent 'not handled in switch' warnings
00942       break;
00943   }
00944   if (sendEvent) {
00945     retval = Fl::handle(sendEvent,window);
00946   }
00947   fl_unlock_function();  
00948   return retval;
00949 }
00950 
00951 
00952 /*
00953  * Open callback function to call...
00954  */
00955 
00956 static void     (*open_cb)(const char *) = 0;
00957 
00958 
00959 /*
00960  * Install an open documents event handler...
00961  */
00962 @interface FLAppleEventHandler : NSObject
00963 {
00964 }
00965 - (void)handleAppleEvent:(NSAppleEventDescriptor *)event withReplyEvent: (NSAppleEventDescriptor *)replyEvent;
00966 @end
00967 @implementation FLAppleEventHandler
00968 - (void)handleAppleEvent:(NSAppleEventDescriptor *)event withReplyEvent: (NSAppleEventDescriptor *)replyEvent
00969 {
00970   NSAppleEventDescriptor *single = [event descriptorAtIndex:1];
00971   const AEDesc *document = [single aeDesc];
00972   long i, n;
00973   FSRef fileRef;
00974   AEKeyword keyWd;
00975   DescType typeCd;
00976   Size actSz;
00977   char filename[1024];
00978   // Lock access to FLTK in this thread...
00979   fl_lock_function();
00980   
00981   // Open the documents via the callback...
00982   if (AECountItems(document, &n) == noErr) {
00983     for (i = 1; i <= n; i ++) {
00984       AEGetNthPtr(document, i, typeFSRef, &keyWd, &typeCd,
00985                   (Ptr)&fileRef, sizeof(fileRef),
00986                   (actSz = sizeof(fileRef), &actSz));
00987       FSRefMakePath( &fileRef, (UInt8*)filename, sizeof(filename) );
00988       
00989       (*open_cb)(filename);
00990     }
00991   }
00992   // Unlock access to FLTK for all threads...
00993   fl_unlock_function();
00994 }
00995 @end
00996 
00997 void fl_open_callback(void (*cb)(const char *)) {
00998   static NSAppleEventManager *aeventmgr = nil;
00999   static FLAppleEventHandler *handler;
01000   fl_open_display();
01001   if (!aeventmgr) {
01002     aeventmgr = [NSAppleEventManager sharedAppleEventManager];
01003     handler = [[FLAppleEventHandler alloc] init];
01004   }
01005   
01006   open_cb = cb;
01007   if (cb) {
01008     [aeventmgr setEventHandler:handler andSelector:@selector(handleAppleEvent:withReplyEvent:) 
01009                  forEventClass:kCoreEventClass andEventID:kAEOpenDocuments];
01010   } else {
01011     [aeventmgr removeEventHandlerForEventClass:kCoreEventClass andEventID:kAEOpenDocuments];  
01012   }
01013 }
01014 
01015 
01016 /*
01017  * initialize the Mac toolboxes, dock status, and set the default menubar
01018  */
01019 
01020 extern "C" {
01021   extern OSErr CPSEnableForegroundOperation(ProcessSerialNumber *psn, UInt32 _arg2,
01022                                             UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
01023 }
01024 
01025 
01026 @interface FLDelegate : NSObject 
01027 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
01028 <NSWindowDelegate, NSApplicationDelegate>
01029 #endif
01030 {
01031 }
01032 - (void)windowDidMove:(NSNotification *)notif;
01033 - (void)windowDidResize:(NSNotification *)notif;
01034 - (void)windowDidBecomeKey:(NSNotification *)notif;
01035 - (void)windowDidBecomeMain:(NSNotification *)notif;
01036 - (void)windowDidDeminiaturize:(NSNotification *)notif;
01037 - (void)windowDidMiniaturize:(NSNotification *)notif;
01038 - (void)windowWillClose:(NSNotification *)notif;
01039 - (void)anywindowwillclosenotif:(NSNotification *)notif;
01040 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender;
01041 - (void)applicationDidBecomeActive:(NSNotification *)notify;
01042 - (void)applicationWillResignActive:(NSNotification *)notify;
01043 - (id)windowWillReturnFieldEditor:(NSWindow *)sender toObject:(id)client;
01044 @end
01045 @implementation FLDelegate
01046 - (void)windowDidMove:(NSNotification *)notif
01047 {
01048   FLWindow *nsw = (FLWindow*)[notif object];
01049   Fl_Window *window = [nsw getFl_Window];
01050   NSPoint pt, pt2; 
01051   pt.x = 0;
01052   pt.y = [[nsw contentView] frame].size.height;
01053   pt2 = [nsw convertBaseToScreen:pt];
01054   update_e_xy_and_e_xy_root(nsw);
01055   window->position((int)pt2.x, (int)([[nsw screen] frame].size.height - pt2.y));
01056   if ([nsw containsGLsubwindow] ) {
01057     [nsw display];// redraw window after moving if it contains OpenGL subwindows
01058   }
01059 }
01060 - (void)windowDidResize:(NSNotification *)notif
01061 {
01062   FLWindow *nsw = (FLWindow*)[notif object];
01063   Fl_Window *window = [nsw getFl_Window];
01064   NSRect r = [[nsw contentView] frame];
01065   NSPoint pt, pt2; 
01066   pt.x = 0;
01067   pt.y = [[nsw contentView] frame].size.height;
01068   pt2 = [nsw convertBaseToScreen:pt];
01069   resize_from_system = window;
01070   update_e_xy_and_e_xy_root(nsw);
01071   window->resize((int)pt2.x, 
01072                  (int)([[nsw screen] frame].size.height - pt2.y),
01073                  (int)r.size.width,
01074                  (int)r.size.height);
01075 }
01076 - (void)windowDidBecomeKey:(NSNotification *)notif
01077 {
01078   FLWindow *nsw = (FLWindow*)[notif object];
01079   Fl_Window *window = [nsw getFl_Window];
01080   Fl::handle( FL_FOCUS, window);
01081 }
01082 - (void)windowDidBecomeMain:(NSNotification *)notif
01083 {
01084   FLWindow *nsw = (FLWindow*)[notif object];
01085   Fl_Window *window = [nsw getFl_Window];
01086   Fl::first_window(window);
01087   update_e_xy_and_e_xy_root(nsw);
01088 }
01089 - (void)windowDidDeminiaturize:(NSNotification *)notif
01090 {
01091   FLWindow *nsw = (FLWindow*)[notif object];
01092   Fl_Window *window = [nsw getFl_Window];
01093   window->set_visible();
01094   update_e_xy_and_e_xy_root(nsw);
01095 }
01096 - (void)windowDidMiniaturize:(NSNotification *)notif
01097 {
01098   FLWindow *nsw = (FLWindow*)[notif object];
01099   Fl_Window *window = [nsw getFl_Window];
01100   window->clear_visible();
01101 }
01102 - (void)windowWillClose:(NSNotification *)notif
01103 {
01104   Fl_Window *w = Fl::first_window();
01105   if (!w) return;
01106   NSWindow *cw = (NSWindow*)Fl_X::i(w)->xid;
01107   if ( ![cw isMiniaturized] && ([cw styleMask] & NSTitledWindowMask) ) {
01108     if (![cw isKeyWindow]) {    // always make Fl::first_window() the key widow
01109       [cw makeKeyAndOrderFront:nil];
01110     }
01111     if (![cw isMainWindow]) {   // always make Fl::first_window() the main widow
01112       [cw makeMainWindow];
01113     }
01114   }
01115 }
01116 - (void)anywindowwillclosenotif:(NSNotification *)notif
01117 {
01118   // necessary so that after closing a non-FLTK window (e.g., Fl_Native_File_Chooser)
01119   // the front window turns key again
01120   NSWindow *closing = (NSWindow*)[notif object];
01121   if ([closing isMemberOfClass:[FLWindow class]]) return;
01122   NSWindow *nsk = [NSApp keyWindow];
01123   NSWindow *nsm = [NSApp mainWindow];
01124   if ([nsm isMemberOfClass:[FLWindow class]] && nsk == nil) {
01125     [nsm makeKeyAndOrderFront:nil];
01126   }
01127 }
01128 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender
01129 {
01130   fl_lock_function();
01131   NSApplicationTerminateReply reply = NSTerminateNow;
01132   while ( Fl_X::first ) {
01133     Fl_X *x = Fl_X::first;
01134     Fl::handle( FL_CLOSE, x->w );
01135     if ( Fl_X::first == x ) {
01136       reply = NSTerminateCancel; // FLTK has not closed all windows, so we return to the main program now
01137       break;
01138     }
01139   }
01140   fl_unlock_function();
01141   return reply;
01142 }
01148 - (void)applicationDidBecomeActive:(NSNotification *)notify
01149 {
01150   Fl_X *x;
01151   FLWindow *top = 0, *topModal = 0, *topNonModal = 0;
01152   for (x = Fl_X::first;x;x = x->next) {
01153     FLWindow *cw = (FLWindow*)x->xid;
01154     Fl_Window *win = x->w;
01155     if (win && cw) {
01156       if (win->modal()) {
01157         [cw setLevel:NSModalPanelWindowLevel];
01158         if (topModal) 
01159           [cw orderWindow:NSWindowBelow relativeTo:[topModal windowNumber]];
01160         else
01161           topModal = cw;
01162       } else if (win->non_modal()) {
01163         [cw setLevel:NSFloatingWindowLevel];
01164         if (topNonModal) 
01165           [cw orderWindow:NSWindowBelow relativeTo:[topNonModal windowNumber]];
01166         else
01167           topNonModal = cw;
01168       } else {
01169         if (top) 
01170           ;
01171         else
01172           top = cw;
01173       }
01174     }
01175   }
01176 }
01177 - (void)applicationWillResignActive:(NSNotification *)notify
01178 {
01179   Fl_X *x;
01180   FLWindow *top = 0;
01181   // sort in all regular windows
01182   for (x = Fl_X::first;x;x = x->next) {
01183     FLWindow *cw = (FLWindow*)x->xid;
01184     Fl_Window *win = x->w;
01185     if (win && cw) {
01186       if (win->modal()) {
01187       } else if (win->non_modal()) {
01188       } else {
01189         if (!top) top = cw;
01190       }
01191     }
01192   }
01193   // now sort in all modals
01194   for (x = Fl_X::first;x;x = x->next) {
01195     FLWindow *cw = (FLWindow*)x->xid;
01196     Fl_Window *win = x->w;
01197     if (win && cw) {
01198       if (win->modal()) {
01199         [cw setLevel:NSNormalWindowLevel];
01200         if (top) [cw orderWindow:NSWindowAbove relativeTo:[top windowNumber]];
01201       } else if (win->non_modal()) {
01202       } else {
01203       }
01204     }
01205   }
01206   // finally all non-modals
01207   for (x = Fl_X::first;x;x = x->next) {
01208     FLWindow *cw = (FLWindow*)x->xid;
01209     Fl_Window *win = x->w;
01210     if (win && cw) {
01211       if (win->modal()) {
01212       } else if (win->non_modal()) {
01213         [cw setLevel:NSNormalWindowLevel];
01214         if (top) [cw orderWindow:NSWindowAbove relativeTo:[top windowNumber]];
01215       } else {
01216       }
01217     }
01218   }
01219 }
01220 - (id)windowWillReturnFieldEditor:(NSWindow *)sender toObject:(id)client
01221 {
01222   NSRect rect={{0,0},{20,20}};
01223   static FLTextView *view = nil;
01224   if (!view) {
01225     view = [[FLTextView alloc] initWithFrame:rect];
01226   }
01227   return view;
01228 }
01229 @end
01230 
01231 @interface FLApplication : NSApplication
01232 {
01233 }
01234 - (void)sendEvent:(NSEvent *)theEvent;
01235 @end
01236 @implementation FLApplication
01237 - (void)sendEvent:(NSEvent *)theEvent
01238 {
01239   NSEventType type = [theEvent type];  
01240   if (type == NSLeftMouseDown) {
01241     Fl_Window *grab = Fl::grab();
01242     if (grab && grab != [(FLWindow *)[theEvent window] getFl_Window]) {
01243       // a click event out of a menu window, so we should close this menu
01244       // done here to catch also clicks on window title bar/resize box 
01245       cocoaMouseHandler(theEvent);
01246     }
01247   } else if (type == NSApplicationDefined) {
01248     if ([theEvent subtype] == FLTKDataReadyEvent) {
01249       processFLTKEvent();
01250     }
01251     return;
01252   } else if (type == NSKeyUp) {
01253     // The default sendEvent turns key downs into performKeyEquivalent when
01254     // modifiers are down, but swallows the key up if the modifiers include
01255     // command.  This one makes all modifiers consistent by always sending key ups.
01256     // FLView treats performKeyEquivalent to keyDown, but performKeyEquivalent is
01257     // still needed for the system menu.
01258     [[self keyWindow] sendEvent:theEvent];
01259     return;
01260     }
01261   [super sendEvent:theEvent]; 
01262 }
01263 @end
01264 
01265 static FLDelegate *mydelegate;
01266 
01267 void fl_open_display() {
01268   static char beenHereDoneThat = 0;
01269   if ( !beenHereDoneThat ) {
01270     beenHereDoneThat = 1;
01271           
01272     [FLApplication sharedApplication];
01273     NSAutoreleasePool *localPool;
01274     localPool = [[NSAutoreleasePool alloc] init]; // never released
01275     mydelegate = [[FLDelegate alloc] init];
01276     [NSApp setDelegate:mydelegate];
01277     [NSApp finishLaunching];
01278                 
01279     // empty the event queue but keep system events for drag&drop of files at launch
01280     NSEvent *ign_event;
01281     do ign_event = [NSApp nextEventMatchingMask:(NSAnyEventMask & ~NSSystemDefinedMask)
01282                                         untilDate:[NSDate dateWithTimeIntervalSinceNow:0] 
01283                                            inMode:NSDefaultRunLoopMode 
01284                                           dequeue:YES];
01285     while (ign_event);
01286     
01287     fl_default_cursor = [NSCursor arrowCursor];
01288     SInt32 version;
01289     Gestalt(gestaltSystemVersion, &version);
01290     fl_mac_os_version = (int)version;
01291 
01292     // bring the application into foreground without a 'CARB' resource
01293     Boolean same_psn;
01294     ProcessSerialNumber cur_psn, front_psn;
01295     if ( !GetCurrentProcess( &cur_psn ) && !GetFrontProcess( &front_psn ) &&
01296          !SameProcess( &front_psn, &cur_psn, &same_psn ) && !same_psn ) {
01297       // only transform the application type for unbundled apps
01298       CFBundleRef bundle = CFBundleGetMainBundle();
01299       if ( bundle ) {
01300         FSRef execFs;
01301         CFURLRef execUrl = CFBundleCopyExecutableURL( bundle );
01302         CFURLGetFSRef( execUrl, &execFs );
01303         
01304         FSRef bundleFs;
01305         GetProcessBundleLocation( &cur_psn, &bundleFs );
01306         
01307         if ( !FSCompareFSRefs( &execFs, &bundleFs ) )
01308           bundle = NULL;
01309         
01310         CFRelease(execUrl);
01311       }
01312             
01313       if ( !bundle )
01314       {
01315         // Earlier versions of this code tried to use weak linking, however it
01316         // appears that this does not work on 10.2.  Since 10.3 and higher provide
01317         // both TransformProcessType and CPSEnableForegroundOperation, the following
01318         // conditional code compiled on 10.2 will still work on newer releases...
01319         OSErr err;
01320 #if __LP64__
01321         err = TransformProcessType(&cur_psn, kProcessTransformToForegroundApplication);
01322 #else
01323         
01324 #if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_2
01325         if (TransformProcessType != NULL) {
01326           err = TransformProcessType(&cur_psn, kProcessTransformToForegroundApplication);
01327         } else
01328 #endif // MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_2
01329           err = CPSEnableForegroundOperation(&cur_psn, 0x03, 0x3C, 0x2C, 0x1103);
01330 #endif // __LP64__
01331         if (err == noErr) {
01332           SetFrontProcess( &cur_psn );
01333         }
01334       }
01335     }
01336     createAppleMenu();
01337     
01338     [[NSNotificationCenter defaultCenter] addObserver:mydelegate 
01339                selector:@selector(anywindowwillclosenotif:) 
01340                    name:NSWindowWillCloseNotification 
01341                  object:nil];
01342   }
01343 }
01344 
01345 
01346 /*
01347  * get rid of allocated resources
01348  */
01349 void fl_close_display() {
01350 }
01351 
01352 
01353 // Gets the border sizes and the titlebar size
01354 static void get_window_frame_sizes(int &bx, int &by, int &bt) {
01355   NSAutoreleasePool *localPool;
01356   localPool = [[NSAutoreleasePool alloc] init]; 
01357   NSRect inside = { {20,20}, {100,100} };
01358   NSRect outside = [NSWindow  frameRectForContentRect:inside styleMask:NSTitledWindowMask];
01359   bx = int(outside.origin.x - inside.origin.x);
01360   by = int(outside.origin.y - inside.origin.y);
01361   bt = int(outside.size.height - inside.size.height - by);
01362   [localPool release];
01363 }
01364 
01365 /*
01366  * smallest x ccordinate in screen space
01367  */
01368 int Fl::x() {
01369   return int([[NSScreen mainScreen] visibleFrame].origin.x);
01370 }
01371 
01372 
01373 /*
01374  * smallest y coordinate in screen space
01375  */
01376 int Fl::y() {
01377   NSRect all = [[NSScreen mainScreen] frame];
01378   NSRect visible = [[NSScreen mainScreen] visibleFrame];
01379   return int(all.size.height - (visible.origin.y + visible.size.height));
01380 }
01381 
01382 
01383 /*
01384  * screen width
01385  */
01386 int Fl::w() {
01387   return int([[NSScreen mainScreen] visibleFrame].size.width);
01388 }
01389 
01390 
01391 /*
01392  * screen height
01393  */
01394 int Fl::h() {
01395   return int([[NSScreen mainScreen] visibleFrame].size.height);
01396 }
01397 
01398 
01399 /*
01400  * get the current mouse pointer world coordinates
01401  */
01402 void Fl::get_mouse(int &x, int &y) 
01403 {
01404   fl_open_display();
01405   NSPoint pt = [NSEvent mouseLocation];
01406   x = int(pt.x);
01407   y = int([[NSScreen mainScreen] frame].size.height - pt.y);
01408 }
01409 
01410 
01411 /*
01412  * Initialize the given port for redraw and call the window's flush() to actually draw the content
01413  */ 
01414 void Fl_X::flush()
01415 {
01416   w->flush();
01417   if (fl_gc) CGContextFlush(fl_gc);
01418 }
01419 
01420 /*
01421  * Gets called when a window is created, resized, or deminiaturized
01422  */    
01423 static void handleUpdateEvent( Fl_Window *window ) 
01424 {
01425   if ( !window ) return;
01426   Fl_X *i = Fl_X::i( window );
01427   i->wait_for_expose = 0;
01428 
01429   if ( i->region ) {
01430     XDestroyRegion(i->region);
01431     i->region = 0;
01432   }
01433   
01434   for ( Fl_X *cx = i->xidChildren; cx; cx = cx->xidNext ) {
01435     if ( cx->region ) {
01436       XDestroyRegion(cx->region);
01437       cx->region = 0;
01438     }
01439     cx->w->clear_damage(FL_DAMAGE_ALL);
01440     cx->flush();
01441     cx->w->clear_damage();
01442   }
01443   window->clear_damage(FL_DAMAGE_ALL);
01444   i->flush();
01445   window->clear_damage();
01446 }     
01447 
01448 
01449 int Fl_X::fake_X_wm(const Fl_Window* w,int &X,int &Y, int &bt,int &bx, int &by) {
01450   int W, H, xoff, yoff, dx, dy;
01451   int ret = bx = by = bt = 0;
01452   if (w->border() && !w->parent()) {
01453     if (w->maxw != w->minw || w->maxh != w->minh) {
01454       ret = 2;
01455       get_window_frame_sizes(bx, by, bt);
01456     } else {
01457       ret = 1;
01458       get_window_frame_sizes(bx, by, bt);
01459     }
01460   }
01461   // The coordinates of the whole window, including non-client area
01462   xoff = bx;
01463   yoff = by + bt;
01464   dx = 2*bx;
01465   dy = 2*by + bt;
01466   X = w->x()-xoff;
01467   Y = w->y()-yoff;
01468   W = w->w()+dx;
01469   H = w->h()+dy;
01470   
01471   // Proceed to positioning the window fully inside the screen, if possible
01472   
01473   // let's get a little elaborate here. Mac OS X puts a lot of stuff on the desk
01474   // that we want to avoid when positioning our window, namely the Dock and the
01475   // top menu bar (and even more stuff in 10.4 Tiger). So we will go through the
01476   // list of all available screens and find the one that this window is most
01477   // likely to go to, and then reposition it to fit withing the 'good' area.
01478   //  Rect r;
01479   // find the screen, that the center of this window will fall into
01480   int R = X+W, B = Y+H; // right and bottom
01481   int cx = (X+R)/2, cy = (Y+B)/2; // center of window;
01482   NSScreen *gd = NULL;
01483   NSArray *a = [NSScreen screens]; int count = (int)[a count]; NSRect r; int i;
01484   for( i = 0; i < count; i++) {
01485     r = [[a objectAtIndex:i] frame];
01486     cy = int(r.size.height - cy);
01487     if (   cx >= r.origin.x && cx <= r.origin.x + r.size.width
01488         && cy >= r.origin.y && cy <= r.origin.y + r.size.height)
01489       break;
01490   }
01491   if (i < count) gd = [a objectAtIndex:i];
01492   
01493   // if the center doesn't fall on a screen, try the top left
01494   if (!gd) {
01495     for( i = 0; i < count; i++) {
01496       r = [[a objectAtIndex:i] frame];
01497       if (    X >= r.origin.x && X <= r.origin.x + r.size.width
01498           && r.size.height - Y >= r.origin.y  && r.size.height - Y <= r.origin.y + r.size.height)
01499         break;
01500     }
01501     if (i < count) gd = [a objectAtIndex:i];
01502   }
01503   // if that doesn't fall on a screen, try the top right
01504   if (!gd) {
01505     for( i = 0; i < count; i++) {
01506       r = [[a objectAtIndex:i] frame];
01507       if (    R >= r.origin.x && R <= r.origin.x + r.size.width
01508           && r.size.height - Y >= r.origin.y  && r.size.height - Y <= r.origin.y + r.size.height)
01509         break;
01510     }
01511     if (i < count) gd = [a objectAtIndex:i];
01512   }
01513   // if that doesn't fall on a screen, try the bottom left
01514   if (!gd) {
01515     for( i = 0; i < count; i++) {
01516       r = [[a objectAtIndex:i] frame];
01517       if (    X >= r.origin.x && X <= r.origin.x + r.size.width
01518           && Y-H >= r.origin.y  && Y-H <= r.origin.y + r.size.height)
01519         break;
01520     }
01521     if (i < count) gd = [a objectAtIndex:i];
01522   }
01523   // last resort, try the bottom right
01524   if (!gd) {
01525     for( i = 0; i < count; i++) {
01526       r = [[a objectAtIndex:i] frame];
01527       if (    R >= r.origin.x && R <= r.origin.x + r.size.width
01528           && Y-H >= r.origin.y  && Y-H <= r.origin.y + r.size.height)
01529         break;
01530     }
01531     if (i < count) gd = [a objectAtIndex:i];
01532   }
01533   // if we still have not found a screen, we will use the main
01534   // screen, the one that has the application menu bar.
01535   if (!gd) gd = [a objectAtIndex:0];
01536   if (gd) {
01537     r = [gd visibleFrame];
01538     int sh = int([gd frame].size.height);
01539     if ( R > r.origin.x + r.size.width ) X -= int(R - (r.origin.x + r.size.width));
01540     if ( B > sh - r.origin.y ) Y -= int(B - (sh - r.origin.y));
01541     if ( X < r.origin.x ) X = int(r.origin.x);
01542     if ( Y < sh - (r.origin.y + r.size.height) ) Y = int(sh - (r.origin.y + r.size.height));
01543   }
01544   
01545   // Return the client area's top left corner in (X,Y)
01546   X+=xoff;
01547   Y+=yoff;
01548   
01549   return ret;
01550 }
01551 
01552 
01553 Fl_Window *fl_dnd_target_window = 0;
01554 
01555 static void  q_set_window_title(NSWindow *nsw, const char * name ) {
01556   CFStringRef utf8_title = CFStringCreateWithCString(NULL, (name ? name : ""), kCFStringEncodingUTF8);
01557         [nsw setTitle:(NSString*)utf8_title ];
01558   CFRelease(utf8_title);
01559 }
01560 
01561 
01562 @interface FLView : NSView <NSTextInput> {
01563 }
01564 - (void)drawRect:(NSRect)rect;
01565 - (BOOL)acceptsFirstResponder;
01566 - (BOOL)acceptsFirstMouse:(NSEvent*)theEvent;
01567 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent;
01568 - (void)mouseUp:(NSEvent *)theEvent;
01569 - (void)rightMouseUp:(NSEvent *)theEvent;
01570 - (void)otherMouseUp:(NSEvent *)theEvent;
01571 - (void)mouseDown:(NSEvent *)theEvent;
01572 - (void)rightMouseDown:(NSEvent *)theEvent;
01573 - (void)otherMouseDown:(NSEvent *)theEvent;
01574 - (void)mouseMoved:(NSEvent *)theEvent;
01575 - (void)mouseDragged:(NSEvent *)theEvent;
01576 - (void)rightMouseDragged:(NSEvent *)theEvent;
01577 - (void)otherMouseDragged:(NSEvent *)theEvent;
01578 - (void)scrollWheel:(NSEvent *)theEvent;
01579 - (void)keyDown:(NSEvent *)theEvent;
01580 - (void)keyUp:(NSEvent *)theEvent;
01581 - (void)flagsChanged:(NSEvent *)theEvent;
01582 - (NSDragOperation)draggingEntered:(id < NSDraggingInfo >)sender;
01583 - (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender;
01584 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender;
01585 - (void)draggingExited:(id < NSDraggingInfo >)sender;
01586 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal;
01587 @end
01588 
01589 @implementation FLView
01590 - (void)drawRect:(NSRect)rect
01591 {
01592   FLWindow *cw = (FLWindow*)[self window];
01593   Fl_Window *w = [cw getFl_Window];
01594   handleUpdateEvent(w);
01595 }
01596 
01597 - (BOOL)acceptsFirstResponder
01598 {   
01599   return YES;
01600 }
01601 - (BOOL)performKeyEquivalent:(NSEvent*)theEvent
01602 {   
01603   int err = cocoaKeyboardHandler(theEvent);
01604   return (err ? YES : NO);
01605 }
01606 - (BOOL)acceptsFirstMouse:(NSEvent*)theEvent
01607 {   
01608   Fl_Window *w = [(FLWindow*)[theEvent window] getFl_Window];
01609   Fl_Window *first = Fl::first_window();
01610   return (first == w || !first->modal());
01611 }
01612 - (void)mouseUp:(NSEvent *)theEvent {
01613   cocoaMouseHandler(theEvent);
01614 }
01615 - (void)rightMouseUp:(NSEvent *)theEvent {
01616   cocoaMouseHandler(theEvent);
01617 }
01618 - (void)otherMouseUp:(NSEvent *)theEvent {
01619   cocoaMouseHandler(theEvent);
01620 }
01621 - (void)mouseDown:(NSEvent *)theEvent {
01622   cocoaMouseHandler(theEvent);
01623 }
01624 - (void)rightMouseDown:(NSEvent *)theEvent {
01625   cocoaMouseHandler(theEvent);
01626 }
01627 - (void)otherMouseDown:(NSEvent *)theEvent {
01628   cocoaMouseHandler(theEvent);
01629 }
01630 - (void)mouseMoved:(NSEvent *)theEvent {
01631   cocoaMouseHandler(theEvent);
01632 }
01633 - (void)mouseDragged:(NSEvent *)theEvent {
01634   cocoaMouseHandler(theEvent);
01635 }
01636 - (void)rightMouseDragged:(NSEvent *)theEvent {
01637   cocoaMouseHandler(theEvent);
01638 }
01639 - (void)otherMouseDragged:(NSEvent *)theEvent {
01640   cocoaMouseHandler(theEvent);
01641 }
01642 - (void)scrollWheel:(NSEvent *)theEvent {
01643   cocoaMouseWheelHandler(theEvent);
01644 }
01645 - (void)keyDown:(NSEvent *)theEvent {
01646   FLTextView *edit = (FLTextView*)[[theEvent window]  fieldEditor:YES forObject:nil];
01647   [edit interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
01648 }
01649 - (void)keyUp:(NSEvent *)theEvent {
01650   cocoaKeyboardHandler(theEvent);
01651 }
01652 - (void)flagsChanged:(NSEvent *)theEvent {
01653   fl_lock_function();
01654   static UInt32 prevMods = 0;
01655   NSUInteger mods = [theEvent modifierFlags];
01656   Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window];
01657   UInt32 tMods = prevMods ^ mods;
01658   int sendEvent = 0;
01659   if ( tMods )
01660   {
01661     mods_to_e_keysym( tMods );
01662     if ( Fl::e_keysym ) 
01663       sendEvent = ( prevMods<mods ) ? FL_KEYBOARD : FL_KEYUP;
01664     Fl::e_length = 0;
01665     Fl::e_text = (char*)"";
01666     prevMods = mods;
01667   }
01668   mods_to_e_state( mods );
01669   while (window->parent()) window = window->window();
01670   if (sendEvent) Fl::handle(sendEvent,window);
01671   fl_unlock_function();
01672 }
01673 - (NSDragOperation)draggingEntered:(id < NSDraggingInfo >)sender
01674 {
01675   Fl_Window *target = [(FLWindow*)[self window] getFl_Window];
01676   update_e_xy_and_e_xy_root([self window]);
01677   fl_dnd_target_window = target;
01678   int ret = Fl::handle( FL_DND_ENTER, target );
01679   breakMacEventLoop();
01680   return ret ? NSDragOperationCopy : NSDragOperationNone;
01681 }
01682 - (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender
01683 {
01684   Fl_Window *target = [(FLWindow*)[self window] getFl_Window];
01685   update_e_xy_and_e_xy_root([self window]);
01686   fl_dnd_target_window = target;
01687   int ret = Fl::handle( FL_DND_DRAG, target );
01688   breakMacEventLoop();
01689   return ret ? NSDragOperationCopy : NSDragOperationNone;
01690 }
01691 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender 
01692 {
01693   static char *DragData = NULL;
01694   Fl_Window *target = [(FLWindow*)[self window] getFl_Window];
01695   if ( !Fl::handle( FL_DND_RELEASE, target ) ) { 
01696     breakMacEventLoop();
01697     return NO;
01698   }
01699   NSPasteboard *pboard;
01700   // NSDragOperation sourceDragMask;
01701   // sourceDragMask = [sender draggingSourceOperationMask];
01702   pboard = [sender draggingPasteboard];
01703   update_e_xy_and_e_xy_root([self window]);
01704   if (DragData) { free(DragData); DragData = NULL; }
01705   if ( [[pboard types] containsObject:NSFilenamesPboardType] ) {
01706     CFArrayRef files = (CFArrayRef)[pboard propertyListForType:NSFilenamesPboardType];
01707     CFStringRef all = CFStringCreateByCombiningStrings(NULL, files, CFSTR("\n"));
01708     int l = CFStringGetMaximumSizeForEncoding(CFStringGetLength(all), kCFStringEncodingUTF8);
01709     DragData = (char *)malloc(l + 1);
01710     CFStringGetCString(all, DragData, l + 1, kCFStringEncodingUTF8);
01711     CFRelease(all);
01712   }
01713   else if ( [[pboard types] containsObject:NSStringPboardType] ) {
01714     NSData *data = [pboard dataForType:NSStringPboardType];
01715     DragData = (char *)malloc([data length] + 1);
01716     [data getBytes:DragData];
01717     DragData[[data length]] = 0;
01718     convert_crlf(DragData, strlen(DragData));
01719   }
01720   else {
01721     breakMacEventLoop();
01722     return NO;
01723   }
01724   Fl::e_text = DragData;
01725   Fl::e_length = strlen(DragData);
01726   int old_event = Fl::e_number;
01727   Fl::belowmouse()->handle(Fl::e_number = FL_PASTE);
01728   Fl::e_number = old_event;
01729   if (DragData) { free(DragData); DragData = NULL; }
01730   Fl::e_text = NULL;
01731   Fl::e_length = 0;
01732   fl_dnd_target_window = NULL;
01733   breakMacEventLoop();
01734   return YES;
01735 }
01736 - (void)draggingExited:(id < NSDraggingInfo >)sender
01737 {
01738   if ( fl_dnd_target_window ) {
01739     Fl::handle( FL_DND_LEAVE, fl_dnd_target_window );
01740     fl_dnd_target_window = 0;
01741   }
01742 }
01743 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
01744 {
01745   return NSDragOperationGeneric;
01746 }
01747 
01748 // These functions implement text input.
01749 // Only two-stroke character composition works at this point.
01750 // Needs much elaboration to fully support CJK text input,
01751 // but this is the way to go.
01752 - (void)doCommandBySelector:(SEL)aSelector {
01753 }
01754 
01755 - (void)insertText:(id)aString {
01756   NSEvent *event = [NSApp currentEvent];
01757   NSEventType type = [event type];
01758   NSString *str = @"";
01759   NSString *received;
01760   if ([aString isKindOfClass:[NSAttributedString class]]) {
01761     received = [(NSAttributedString*)aString string];
01762   } else {
01763     received = (NSString*)aString;
01764   }
01765 //NSLog(@"insertText: received=%@ event type=%d",received, type);
01766   if (type == NSKeyDown ) {
01767     str = [event characters];
01768     }
01769   if ([received isEqualToString:@"\b"] || [str isEqualToString:received]) {
01770     if (type == NSKeyDown ) cocoaKeyboardHandler(event);
01771   } else {
01772     fl_lock_function();
01773     Fl_Window *window = [(FLWindow*)[NSApp keyWindow] getFl_Window];
01774     Fl::e_text = (char*)[received UTF8String];
01775     Fl::e_length = strlen(Fl::e_text);
01776     Fl::handle(FL_KEYBOARD, window);
01777     Fl::handle(FL_KEYUP, window);
01778     fl_unlock_function();
01779     // for some reason, the window does not redraw until the next mouse move or button push
01780     // sending a 'redraw()' or 'awake()' does not solve the issue!
01781     Fl::flush();
01782   }
01783 }
01784 
01785 - (void)setMarkedText:(id)aString selectedRange:(NSRange)newSelection  {
01786   // NSLog(@"setMarkedText: %@ %d %d Fl::compose_state=%d",
01787   //  aString,newSelection.location,newSelection.length,newSelection.location);
01788   [self insertText:aString];
01789   Fl::compose_state = newSelection.location;
01790 }
01791 
01792 - (void)unmarkText {
01793   Fl::compose_state = 0;
01794   //NSLog(@"unmarkText");
01795 }
01796 
01797 - (NSRange)selectedRange {
01798   return NSMakeRange(NSNotFound, 0);
01799 }
01800 
01801 - (NSRange)markedRange {
01802   //NSLog(@"markedRange ?");
01803   return NSMakeRange(NSNotFound, Fl::compose_state);
01804 }
01805 
01806 - (BOOL)hasMarkedText {
01807   //NSLog(@"hasMarkedText %s", Fl::compose_state > 0?"YES":"NO");
01808   return (Fl::compose_state > 0);
01809 }
01810 
01811 - (NSAttributedString *)attributedSubstringFromRange:(NSRange)aRange {
01812   //NSLog(@"attributedSubstringFromRange: %d %d",aRange.location,aRange.length);
01813   return nil;
01814 }
01815 
01816 - (NSArray *)validAttributesForMarkedText {
01817   return nil;
01818 }
01819 
01820 - (NSRect)firstRectForCharacterRange:(NSRange)aRange {
01821   NSRect glyphRect, frame;
01822   
01823   frame = [self frame];
01824   glyphRect.origin.x = frame.size.width;
01825   glyphRect.origin.y = 0;
01826   glyphRect.size.width = glyphRect.size.height = 0;
01827   // Convert the rect to screen coordinates
01828   glyphRect.origin = [[self window] convertBaseToScreen:glyphRect.origin];
01829   return glyphRect;
01830 }
01831 
01832 - (NSUInteger)characterIndexForPoint:(NSPoint)aPoint {
01833   return 0;
01834 }
01835 
01836 - (NSInteger)conversationIdentifier {
01837   return (NSInteger)self;
01838 }
01839 
01840 @end
01841 
01842 
01843 /*
01844  * go ahead, create that (sub)window
01845  */
01846 void Fl_X::make(Fl_Window* w)
01847 {
01848   static int xyPos = 100;
01849   if ( w->parent() ) {          // create a subwindow
01850     Fl_Group::current(0);
01851     // our subwindow needs this structure to know about its clipping. 
01852     Fl_X* x = new Fl_X;
01853     x->other_xid = 0;
01854     x->region = 0;
01855     x->subRegion = 0;
01856     x->cursor = fl_default_cursor;
01857     x->gc = 0;                  // stay 0 for Quickdraw; fill with CGContext for Quartz
01858     Fl_Window *win = w->window();
01859     Fl_X *xo = Fl_X::i(win);
01860     if (xo) {
01861       x->xidNext = xo->xidChildren;
01862       x->xidChildren = 0L;
01863       xo->xidChildren = x;
01864       x->xid = win->i->xid;
01865       x->w = w; w->i = x;
01866       x->wait_for_expose = 0;
01867       {
01868         Fl_X *z = xo->next;     // we don't want a subwindow in Fl_X::first
01869         xo->next = x;
01870         x->next = z;
01871       }
01872       int old_event = Fl::e_number;
01873       w->handle(Fl::e_number = FL_SHOW);
01874       Fl::e_number = old_event;
01875       w->redraw();              // force draw to happen
01876     }
01877     fl_show_iconic = 0;
01878   }
01879   else {                        // create a desktop window
01880     NSAutoreleasePool *localPool;
01881     localPool = [[NSAutoreleasePool alloc] init]; 
01882     Fl_Group::current(0);
01883     fl_open_display();
01884     NSInteger winlevel = NSNormalWindowLevel;
01885     NSUInteger winstyle;
01886     if (w->border()) winstyle = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask;
01887     else winstyle = NSBorderlessWindowMask;
01888     int xp = w->x();
01889     int yp = w->y();
01890     int wp = w->w();
01891     int hp = w->h();
01892     if (w->size_range_set) {
01893       if ( w->minh != w->maxh || w->minw != w->maxw) {
01894         winstyle |= NSResizableWindowMask;
01895       }
01896     } else {
01897       if (w->resizable()) {
01898         Fl_Widget *o = w->resizable();
01899         int minw = o->w(); if (minw > 100) minw = 100;
01900         int minh = o->h(); if (minh > 100) minh = 100;
01901         w->size_range(w->w() - o->w() + minw, w->h() - o->h() + minh, 0, 0);
01902         winstyle |= NSResizableWindowMask;
01903       } else {
01904         w->size_range(w->w(), w->h(), w->w(), w->h());
01905       }
01906     }
01907     int xwm = xp, ywm = yp, bt, bx, by;
01908     
01909     if (!fake_X_wm(w, xwm, ywm, bt, bx, by)) {
01910       // menu windows and tooltips
01911       if (w->modal()||w->override()) {
01912         winstyle = NSBorderlessWindowMask;
01913         winlevel = NSMainMenuWindowLevel;
01914       } else {
01915         winstyle = NSBorderlessWindowMask;
01916       }
01917     } else if (w->modal()) {
01918       winstyle &= ~NSMiniaturizableWindowMask;
01919       // winstyle &= ~(NSResizableWindowMask | NSMiniaturizableWindowMask);
01920       // winlevel = NSModalPanelWindowLevel;
01921     }
01922     else if (w->non_modal()) {
01923       winlevel = NSFloatingWindowLevel;
01924     }
01925     
01926     if (by+bt) {
01927       wp += 2*bx;
01928       hp += 2*by+bt;
01929     }
01930     if (!(w->flags() & Fl_Window::FORCE_POSITION)) {
01931       // use the Carbon functions below for default window positioning
01932       w->x(xyPos+Fl::x());
01933       w->y(xyPos+Fl::y());
01934       xyPos += 25;
01935       if (xyPos>200) xyPos = 100;
01936     } else {
01937       if (!Fl::grab()) {
01938         xp = xwm; yp = ywm;
01939         w->x(xp);w->y(yp);
01940       }
01941       xp -= bx;
01942       yp -= by+bt;
01943     }
01944     
01945     if (w->non_modal() && Fl_X::first /*&& !fl_disable_transient_for*/) {
01946       // find some other window to be "transient for":
01947       Fl_Window* w = Fl_X::first->w;
01948       while (w->parent()) w = w->window(); // todo: this code does not make any sense! (w!=w??)
01949     }
01950     
01951     const char *name = w->label();
01952     
01953     Fl_X* x = new Fl_X;
01954     x->other_xid = 0; // room for doublebuffering image map. On OS X this is only used by overlay windows
01955     x->region = 0;
01956     x->subRegion = 0;
01957     x->cursor = fl_default_cursor;
01958     x->xidChildren = 0;
01959     x->xidNext = 0;
01960     x->gc = 0;
01961           
01962     NSRect srect = [[NSScreen mainScreen] frame];
01963     NSRect crect;
01964     crect.origin.x = w->x(); 
01965     crect.origin.y = srect.size.height - (w->y() + w->h());
01966     crect.size.width=w->w(); 
01967     crect.size.height=w->h();
01968     FLWindow *cw = [[FLWindow alloc] initWithFl_W:w 
01969                                       contentRect:crect  
01970                                         styleMask:winstyle  
01971                                           backing:NSBackingStoreBuffered 
01972                                             defer:NO];
01973     [cw setHasShadow:YES];
01974     [cw setAcceptsMouseMovedEvents:YES];
01975     x->xid = cw;
01976     FLView *myview = [[FLView alloc] init];
01977     [cw setContentView:myview];
01978     [cw setLevel:winlevel];
01979     
01980     q_set_window_title(cw, name);
01981     if (!(w->flags() & Fl_Window::FORCE_POSITION)) {
01982       if (w->modal()) {
01983         [cw center];
01984       } else if (w->non_modal()) {
01985         [cw center];
01986       } else {
01987         static NSPoint delta = NSZeroPoint;
01988         delta = [cw cascadeTopLeftFromPoint:delta];
01989       }
01990     }
01991     if(w->menu_window()) { // make menu windows slightly transparent
01992       [cw setAlphaValue:0.97];
01993     }
01994     x->w = w; w->i = x;
01995     x->wait_for_expose = 1;
01996     x->next = Fl_X::first;
01997     Fl_X::first = x;
01998     // Install DnD handlers 
01999     [myview registerForDraggedTypes:[NSArray arrayWithObjects:
02000                                      NSStringPboardType,  NSFilenamesPboardType, nil]];
02001     if ( ! Fl_X::first->next ) {        // if this is the first window, we need to bring the application to the front
02002       ProcessSerialNumber psn;
02003       OSErr err = GetCurrentProcess( &psn );
02004       if ( err==noErr ) SetFrontProcess( &psn );
02005     }
02006     
02007     if (w->size_range_set) w->size_range_();
02008     
02009     if (winlevel != NSMainMenuWindowLevel) {
02010       Fl_Tooltip::enter(0);
02011     }
02012     [cw makeKeyAndOrderFront:nil];
02013     Fl::first_window(w);
02014     [cw setDelegate:mydelegate];
02015     if (fl_show_iconic) { 
02016       fl_show_iconic = 0;
02017       [cw miniaturize:nil];
02018     } else {
02019       w->set_visible();
02020     }
02021     
02022     crect = [[cw contentView] frame];
02023     w->w(int(crect.size.width));
02024     w->h(int(crect.size.height));
02025     crect = [cw frame];
02026     w->x(int(crect.origin.x));
02027     srect = [[cw screen] frame];
02028     w->y(int(srect.size.height - (crect.origin.y + w->h())));
02029     
02030     int old_event = Fl::e_number;
02031     w->handle(Fl::e_number = FL_SHOW);
02032     Fl::e_number = old_event;
02033     
02034     if (w->modal()) { Fl::modal_ = w; fl_fix_focus(); }
02035     [localPool release];
02036   }
02037 }
02038 
02039 
02040 /*
02041  * Tell the OS what window sizes we want to allow
02042  */
02043 void Fl_Window::size_range_() {
02044   int bx, by, bt;
02045   get_window_frame_sizes(bx, by, bt);
02046   size_range_set = 1;
02047   NSSize minSize = { minw, minh + bt };
02048   NSSize maxSize = { maxw?maxw:32000, maxh?maxh + bt:32000 };
02049   if (i && i->xid) {
02050     [(NSWindow*)i->xid setMinSize:minSize];
02051     [(NSWindow*)i->xid setMaxSize:maxSize];
02052   }
02053 }
02054 
02055 
02056 /*
02057  * returns pointer to the filename, or null if name ends with ':'
02058  */
02059 const char *fl_filename_name( const char *name ) 
02060 {
02061   const char *p, *q;
02062   if (!name) return (0);
02063   for ( p = q = name ; *p ; ) {
02064     if ( ( p[0] == ':' ) && ( p[1] == ':' ) ) {
02065       q = p+2;
02066       p++;
02067     }
02068     else if (p[0] == '/') {
02069       q = p + 1;
02070     }
02071     p++;
02072   }
02073   return q;
02074 }
02075 
02076 
02077 /*
02078  * set the window title bar
02079  * \todo make the titlebar icon work!
02080  */
02081 void Fl_Window::label(const char *name,const char */*iname*/) {
02082   Fl_Widget::label(name);
02083   if (shown() || i) {
02084     q_set_window_title((NSWindow*)i->xid, name);
02085   }
02086 }
02087 
02088 
02089 /*
02090  * make a window visible
02091  */
02092 void Fl_Window::show() {
02093   image(Fl::scheme_bg_);
02094   if (Fl::scheme_bg_) {
02095     labeltype(FL_NORMAL_LABEL);
02096     align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE | FL_ALIGN_CLIP);
02097   } else {
02098     labeltype(FL_NO_LABEL);
02099   }
02100   Fl_Tooltip::exit(this);
02101   if (!shown() || !i) {
02102     Fl_X::make(this);
02103   } else {
02104     if ( !parent() ) {
02105       if ([(NSWindow*)i->xid isMiniaturized]) {
02106         i->w->redraw();
02107         [(NSWindow*)i->xid deminiaturize:nil];
02108       }
02109       if (!fl_capture) {
02110         [(NSWindow*)i->xid makeKeyAndOrderFront:nil];
02111       }
02112     }
02113   }
02114 }
02115 
02116 
02117 /*
02118  * resize a window
02119  */
02120 void Fl_Window::resize(int X,int Y,int W,int H) {
02121   int bx, by, bt;
02122   if ( ! this->border() ) bt = 0;
02123   else get_window_frame_sizes(bx, by, bt);
02124   if (W<=0) W = 1; // OS X does not like zero width windows
02125   if (H<=0) H = 1;
02126   int is_a_resize = (W != w() || H != h());
02127   //  printf("Fl_Window::resize(X=%d, Y=%d, W=%d, H=%d), is_a_resize=%d, resize_from_system=%p, this=%p\n",
02128   //         X, Y, W, H, is_a_resize, resize_from_system, this);
02129   if (X != x() || Y != y()) set_flag(FORCE_POSITION);
02130   else if (!is_a_resize) return;
02131   if ( (resize_from_system!=this) && (!parent()) && shown()) {
02132     if (is_a_resize) {
02133       if (resizable()) {
02134         if (W<minw) minw = W; // user request for resize takes priority
02135         if (W>maxw) maxw = W; // over a previously set size_range
02136         if (H<minh) minh = H;
02137         if (H>maxh) maxh = H;
02138         size_range(minw, minh, maxw, maxh);
02139       } else {
02140         size_range(W, H, W, H);
02141       }
02142       NSRect dim;
02143       dim.origin.x = X;
02144       dim.origin.y = [[(NSWindow*)i->xid screen] frame].size.height - (Y + H);
02145       dim.size.width = W;
02146       dim.size.height = H + bt;
02147       [(NSWindow*)i->xid setFrame:dim display:YES];
02148     } else {
02149       NSPoint pt; 
02150       pt.x = X; 
02151       pt.y = [[(NSWindow*)i->xid screen] frame].size.height - (Y + h());
02152       [(NSWindow*)i->xid setFrameOrigin:pt];
02153     }
02154   }
02155   resize_from_system = 0;
02156   if (is_a_resize) {
02157     Fl_Group::resize(X,Y,W,H);
02158     if (shown()) { 
02159       redraw(); 
02160     }
02161   } else {
02162     x(X); y(Y); 
02163   }
02164 }
02165 
02166 
02167 /*
02168  * make all drawing go into this window (called by subclass flush() impl.)
02169  */
02170 void Fl_Window::make_current() 
02171 {
02172   Fl_X::q_release_context();
02173   fl_window = i->xid;
02174   current_ = this;
02175   
02176   int xp = 0, yp = 0;
02177   Fl_Window *win = this;
02178   while ( win ) {
02179     if ( !win->window() )
02180       break;
02181     xp += win->x();
02182     yp += win->y();
02183     win = (Fl_Window*)win->window();
02184   }
02185   
02186   NSView *current_focus = [NSView focusView]; 
02187   // sometimes current_focus is set to a non-FLTK view: don't touch that
02188   if ( [current_focus isKindOfClass:[FLView class]] ) [current_focus unlockFocus];
02189   [[(NSWindow*)i->xid contentView]  lockFocus];
02190   i->gc = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
02191   fl_gc = i->gc;
02192   Fl_Region fl_window_region = XRectangleRegion(0,0,w(),h());
02193   if ( ! this->window() ) {
02194     for ( Fl_X *cx = i->xidChildren; cx; cx = cx->xidNext ) {   // clip-out all sub-windows
02195       Fl_Window *cw = cx->w;
02196       Fl_Region from = fl_window_region;
02197       fl_window_region = MacRegionMinusRect(from, cw->x(), cw->y(), cw->w(), cw->h() );
02198       XDestroyRegion(from);
02199     }
02200   }
02201   
02202   // antialiasing must be deactivated because it applies to rectangles too
02203   // and escapes even clipping!!!
02204   // it gets activated when needed (e.g., draw text)
02205   CGContextSetShouldAntialias(fl_gc, false);  
02206   CGFloat hgt = [[(NSWindow*)fl_window contentView] frame].size.height;
02207   CGContextTranslateCTM(fl_gc, 0.5, hgt-0.5f);
02208   CGContextScaleCTM(fl_gc, 1.0f, -1.0f); // now 0,0 is top-left point of the window
02209   win = this;
02210   while(win && win->window()) { // translate to subwindow origin if this is a subwindow context
02211     CGContextTranslateCTM(fl_gc, win->x(), win->y());
02212     win = win->window();
02213   }
02214   //apply window's clip
02215   CGContextClipToRects(fl_gc, fl_window_region->rects, fl_window_region->count );
02216   XDestroyRegion(fl_window_region);
02217 // this is the context with origin at top left of (sub)window clipped out of its subwindows if any
02218   CGContextSaveGState(fl_gc); 
02219 #if defined(FLTK_USE_CAIRO)
02220   if (Fl::cairo_autolink_context()) Fl::cairo_make_current(this); // capture gc changes automatically to update the cairo context adequately
02221 #endif
02222   fl_clip_region( 0 );
02223   
02224 #if defined(FLTK_USE_CAIRO)
02225   // update the cairo_t context
02226   if (Fl::cairo_autolink_context()) Fl::cairo_make_current(this);
02227 #endif
02228 }
02229 
02230 // helper function to manage the current CGContext fl_gc
02231 extern Fl_Color fl_color_;
02232 extern class Fl_Font_Descriptor *fl_fontsize;
02233 extern void fl_font(class Fl_Font_Descriptor*);
02234 extern void fl_quartz_restore_line_style_();
02235 
02236 // FLTK has only one global graphics state. This function copies the FLTK state into the
02237 // current Quartz context
02238 void Fl_X::q_fill_context() {
02239   if (!fl_gc) return;
02240   if ( ! fl_window) { // a bitmap context
02241     size_t hgt = CGBitmapContextGetHeight(fl_gc);
02242     CGContextTranslateCTM(fl_gc, 0.5, hgt-0.5f);
02243     CGContextScaleCTM(fl_gc, 1.0f, -1.0f); // now 0,0 is top-left point of the context
02244     }
02245   fl_font(fl_fontsize);
02246   fl_color(fl_color_);
02247   fl_quartz_restore_line_style_();
02248 }
02249 
02250 // The only way to reset clipping to its original state is to pop the current graphics
02251 // state and restore the global state.
02252 void Fl_X::q_clear_clipping() {
02253   if (!fl_gc) return;
02254   CGContextRestoreGState(fl_gc);
02255   CGContextSaveGState(fl_gc);
02256 }
02257 
02258 // Give the Quartz context back to the system
02259 void Fl_X::q_release_context(Fl_X *x) {
02260   if (x && x->gc!=fl_gc) return;
02261   if (!fl_gc) return;
02262   CGContextRestoreGState(fl_gc); // matches the CGContextSaveGState of make_current
02263   fl_gc = 0;
02264 #if defined(FLTK_USE_CAIRO)
02265   if (Fl::cairo_autolink_context()) Fl::cairo_make_current((Fl_Window*) 0); // capture gc changes automatically to update the cairo context adequately
02266 #endif
02267 }
02268 
02269 void Fl_X::q_begin_image(CGRect &rect, int cx, int cy, int w, int h) {
02270   CGContextSaveGState(fl_gc);
02271   CGRect r2 = rect;
02272   r2.origin.x -= 0.5f;
02273   r2.origin.y -= 0.5f;
02274   CGContextClipToRect(fl_gc, r2);
02275   // move graphics context to origin of vertically reversed image 
02276   CGContextTranslateCTM(fl_gc, rect.origin.x - cx - 0.5, rect.origin.y - cy + h - 0.5);
02277   CGContextScaleCTM(fl_gc, 1, -1);
02278   rect.origin.x = rect.origin.y = 0;
02279   rect.size.width = w;
02280   rect.size.height = h;
02281 }
02282 
02283 void Fl_X::q_end_image() {
02284   CGContextRestoreGState(fl_gc);
02285 }
02286 
02287 
02289 // Copy & Paste fltk implementation.
02291 
02292 static void convert_crlf(char * s, size_t len)
02293 {
02294   // turn all \r characters into \n:
02295   for (size_t x = 0; x < len; x++) if (s[x] == '\r') s[x] = '\n';
02296 }
02297 
02298 // fltk 1.3 clipboard support constant definitions:
02299 const CFStringRef       flavorNames[] = {
02300   CFSTR("public.utf16-plain-text"), 
02301   CFSTR("public.utf8-plain-text"),
02302   CFSTR("com.apple.traditional-mac-plain-text") };
02303 const CFStringEncoding encodings[] = { 
02304   kCFStringEncodingUnicode, 
02305   kCFStringEncodingUTF8, 
02306   kCFStringEncodingMacRoman};
02307 const size_t handledFlavorsCount = sizeof(encodings)/sizeof(CFStringEncoding);
02308 
02309 // clipboard variables definitions :
02310 char *fl_selection_buffer[2];
02311 int fl_selection_length[2];
02312 static int fl_selection_buffer_length[2];
02313 
02314 static PasteboardRef myPasteboard = 0;
02315 static void allocatePasteboard() {
02316   if (!myPasteboard)
02317     PasteboardCreate(kPasteboardClipboard, &myPasteboard);
02318 }
02319 
02320 
02321 /*
02322  * create a selection
02323  * owner: widget that created the selection
02324  * stuff: pointer to selected data
02325  * size of selected data
02326  */
02327 void Fl::copy(const char *stuff, int len, int clipboard) {
02328   if (!stuff || len<0) return;
02329   if (len+1 > fl_selection_buffer_length[clipboard]) {
02330     delete[] fl_selection_buffer[clipboard];
02331     fl_selection_buffer[clipboard] = new char[len+100];
02332     fl_selection_buffer_length[clipboard] = len+100;
02333   }
02334   memcpy(fl_selection_buffer[clipboard], stuff, len);
02335   fl_selection_buffer[clipboard][len] = 0; // needed for direct paste
02336   fl_selection_length[clipboard] = len;
02337   if (clipboard) {
02338     allocatePasteboard();
02339     OSStatus err = PasteboardClear(myPasteboard);
02340     if (err!=noErr) return; // clear did not work, maybe not owner of clipboard.
02341     PasteboardSynchronize(myPasteboard);
02342     CFDataRef text = CFDataCreate(kCFAllocatorDefault, (UInt8*)fl_selection_buffer[1], len);
02343     if (text==NULL) return; // there was a pb creating the object, abort.
02344     err=PasteboardPutItemFlavor(myPasteboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), text, 0);
02345     CFRelease(text);
02346   }
02347 }
02348 
02349 // Call this when a "paste" operation happens:
02350 void Fl::paste(Fl_Widget &receiver, int clipboard) {
02351   if (clipboard) {
02352     // see if we own the selection, if not go get it:
02353     fl_selection_length[1] = 0;
02354     OSStatus err = noErr;
02355     Boolean found = false;
02356     CFDataRef flavorData = NULL;
02357     CFStringEncoding encoding = 0;
02358     
02359     allocatePasteboard();
02360     PasteboardSynchronize(myPasteboard);
02361     ItemCount nFlavor = 0, i, j;
02362     err = PasteboardGetItemCount(myPasteboard, &nFlavor);
02363     if (err==noErr) {
02364       for (i=1; i<=nFlavor; i++) {
02365         PasteboardItemID itemID = 0;
02366         CFArrayRef flavorTypeArray = NULL;
02367         found = false;
02368         err = PasteboardGetItemIdentifier(myPasteboard, i, &itemID);
02369         if (err!=noErr) continue;
02370         err = PasteboardCopyItemFlavors(myPasteboard, itemID, &flavorTypeArray);
02371         if (err!=noErr) {
02372           if (flavorTypeArray) {CFRelease(flavorTypeArray); flavorTypeArray = NULL;}
02373           continue;
02374         }
02375         CFIndex flavorCount = CFArrayGetCount(flavorTypeArray);
02376         for (j = 0; j < handledFlavorsCount; j++) {
02377           for (CFIndex flavorIndex=0; flavorIndex<flavorCount; flavorIndex++) {
02378             CFStringRef flavorType = (CFStringRef)CFArrayGetValueAtIndex(flavorTypeArray, flavorIndex);
02379             if (UTTypeConformsTo(flavorType, flavorNames[j])) {
02380               err = PasteboardCopyItemFlavorData( myPasteboard, itemID, flavorNames[j], &flavorData );
02381               if (err != noErr) continue;
02382               encoding = encodings[j];
02383               found = true;
02384               break;
02385             }
02386           }
02387           if (found) break;
02388         }
02389         if (flavorTypeArray) {CFRelease(flavorTypeArray); flavorTypeArray = NULL;}
02390         if (found) break;
02391       }
02392       if (found) {
02393         CFIndex len = CFDataGetLength(flavorData);
02394         CFStringRef mycfs = CFStringCreateWithBytes(NULL, CFDataGetBytePtr(flavorData), len, encoding, false);
02395         CFRelease(flavorData);
02396         len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(mycfs), kCFStringEncodingUTF8) + 1;
02397         if ( len >= fl_selection_buffer_length[1] ) {
02398           fl_selection_buffer_length[1] = len;
02399           delete[] fl_selection_buffer[1];
02400           fl_selection_buffer[1] = new char[len];
02401         }
02402         CFStringGetCString(mycfs, fl_selection_buffer[1], len, kCFStringEncodingUTF8);
02403         CFRelease(mycfs);
02404         len = strlen(fl_selection_buffer[1]);
02405         fl_selection_length[1] = len;
02406         convert_crlf(fl_selection_buffer[1],len); // turn all \r characters into \n:
02407       }
02408     }
02409   }
02410   Fl::e_text = fl_selection_buffer[clipboard];
02411   Fl::e_length = fl_selection_length[clipboard];
02412   if (!Fl::e_text) Fl::e_text = (char *)"";
02413   receiver.handle(FL_PASTE);
02414 }
02415 
02416 void Fl::add_timeout(double time, Fl_Timeout_Handler cb, void* data)
02417 {
02418   // check, if this timer slot exists already
02419   for (int i = 0; i < mac_timer_used; ++i) {
02420     MacTimeout& t = mac_timers[i];
02421     // if so, simply change the fire interval
02422     if (t.callback == cb  &&  t.data == data) {
02423       CFRunLoopTimerSetNextFireDate(t.timer, CFAbsoluteTimeGetCurrent() + time );
02424       t.pending = 1;
02425       return;
02426     }
02427   }
02428   // no existing timer to use. Create a new one:
02429   int timer_id = -1;
02430   // find an empty slot in the timer array
02431   for (int i = 0; i < mac_timer_used; ++i) {
02432     if ( !mac_timers[i].timer ) {
02433       timer_id = i;
02434       break;
02435     }
02436   }
02437   // if there was no empty slot, append a new timer
02438   if (timer_id == -1) {
02439     // make space if needed
02440     if (mac_timer_used == mac_timer_alloc) {
02441       realloc_timers();
02442     }
02443     timer_id = mac_timer_used++;
02444   }
02445   // now install a brand new timer
02446   MacTimeout& t = mac_timers[timer_id];
02447   CFRunLoopTimerContext context = {0, data, NULL,NULL,NULL};
02448   CFRunLoopTimerRef timerRef = CFRunLoopTimerCreate(kCFAllocatorDefault, 
02449                                                     CFAbsoluteTimeGetCurrent() + time,
02450                                                     1E30,  
02451                                                     0,
02452                                                     0,
02453                                                     do_timer,
02454                                                     &context
02455                                                     );
02456   if (timerRef) {
02457     CFRunLoopAddTimer(CFRunLoopGetCurrent(),
02458                       timerRef,
02459                       kCFRunLoopDefaultMode);
02460     t.callback = cb;
02461     t.data     = data;
02462     t.timer    = timerRef;
02463     t.pending  = 1;
02464   }
02465 }
02466 
02467 void Fl::repeat_timeout(double time, Fl_Timeout_Handler cb, void* data)
02468 {
02469   // currently, repeat_timeout does not subtract the trigger time of the previous timer event as it should.
02470   add_timeout(time, cb, data);
02471 }
02472 
02473 int Fl::has_timeout(Fl_Timeout_Handler cb, void* data)
02474 {
02475   for (int i = 0; i < mac_timer_used; ++i) {
02476     MacTimeout& t = mac_timers[i];
02477     if (t.callback == cb  &&  t.data == data && t.pending) {
02478       return 1;
02479     }
02480   }
02481   return 0;
02482 }
02483 
02484 void Fl::remove_timeout(Fl_Timeout_Handler cb, void* data)
02485 {
02486   for (int i = 0; i < mac_timer_used; ++i) {
02487     MacTimeout& t = mac_timers[i];
02488     if (t.callback == cb  && ( t.data == data || data == NULL)) {
02489       delete_timer(t);
02490     }
02491   }
02492 }
02493 
02494 int Fl_X::unlink(Fl_X *start) {
02495   if (start) {
02496     Fl_X *pc = start;
02497     while (pc) {
02498       if (pc->xidNext == this) {
02499         pc->xidNext = xidNext;
02500         return 1;
02501       }
02502       if (pc->xidChildren) {
02503         if (pc->xidChildren == this) {
02504           pc->xidChildren = xidNext;
02505           return 1;
02506         }
02507         if (unlink(pc->xidChildren))
02508           return 1;
02509       }
02510       pc = pc->xidNext;
02511     }
02512   } else {
02513     for ( Fl_X *pc = Fl_X::first; pc; pc = pc->next ) {
02514       if (unlink(pc))
02515         return 1;
02516     }
02517   }  
02518   return 0;
02519 }
02520 
02521 void Fl_X::relink(Fl_Window *w, Fl_Window *wp) {
02522   Fl_X *x = Fl_X::i(w);
02523   Fl_X *p = Fl_X::i(wp);
02524   if (!x || !p) return;
02525   // first, check if 'x' is already registered as a child of 'p'
02526   for (Fl_X *i = p->xidChildren; i; i=i->xidNext) {
02527     if (i == x) return;
02528   }
02529   // now add 'x' as the first child of 'p'
02530   x->xidNext = p->xidChildren;
02531   p->xidChildren = x;
02532 }
02533 
02534 void Fl_X::destroy() {
02535   if (w && !w->parent() && xid) {
02536     [[(NSWindow *)xid contentView] release];
02537     [(NSWindow *)xid close];
02538   }
02539 }
02540 
02541 void Fl_X::map() {
02542   if (w && xid) {
02543     [(NSWindow *)xid orderFront:nil];
02544   }
02545   //+ link to window list
02546   if (w && w->parent()) {
02547     Fl_X::relink(w, w->window() );
02548     w->redraw();
02549   }
02550 }
02551 
02552 void Fl_X::unmap() {
02553   if (w && !w->parent() && xid) {
02554     [(NSWindow *)xid orderOut:nil];
02555   }
02556   if (w && Fl_X::i(w)) 
02557     Fl_X::i(w)->unlink();
02558 }
02559 
02560 
02561 // removes x,y,w,h rectangle from region r and returns result as a new Fl_Region
02562 static Fl_Region MacRegionMinusRect(Fl_Region r, int x,int y,int w,int h)
02563 {
02564   Fl_Region outr = (Fl_Region)malloc(sizeof(*outr));
02565   outr->rects = (CGRect*)malloc(4 * r->count * sizeof(CGRect));
02566   outr->count = 0;
02567   CGRect rect = fl_cgrectmake_cocoa(x, y, w, h);
02568   for( int i = 0; i < r->count; i++) {
02569     CGRect A = r->rects[i];
02570     CGRect test = CGRectIntersection(A, rect);
02571     if (CGRectIsEmpty(test)) {
02572       outr->rects[(outr->count)++] = A;
02573     }
02574     else {
02575       const CGFloat verylarge = 100000.;
02576       CGRect side = CGRectMake(0,0,rect.origin.x,verylarge);// W side
02577       test = CGRectIntersection(A, side);
02578       if ( ! CGRectIsEmpty(test)) {
02579         outr->rects[(outr->count)++] = test;
02580       }
02581       side = CGRectMake(0,rect.origin.y + rect.size.height,verylarge,verylarge);// N side
02582       test = CGRectIntersection(A, side);
02583       if ( ! CGRectIsEmpty(test)) {
02584         outr->rects[(outr->count)++] = test;
02585       }
02586       side = CGRectMake(rect.origin.x + rect.size.width, 0, verylarge, verylarge);// E side
02587       test = CGRectIntersection(A, side);
02588       if ( ! CGRectIsEmpty(test)) {
02589         outr->rects[(outr->count)++] = test;
02590       }
02591       side = CGRectMake(0, 0, verylarge, rect.origin.y);// S side
02592       test = CGRectIntersection(A, side);
02593       if ( ! CGRectIsEmpty(test)) {
02594         outr->rects[(outr->count)++] = test;
02595       }
02596     }
02597   }
02598   if (outr->count == 0) {
02599     free(outr->rects);
02600     free(outr);
02601     outr = XRectangleRegion(0,0,0,0);
02602   }
02603   else outr->rects = (CGRect*)realloc(outr->rects, outr->count * sizeof(CGRect));
02604   return outr;
02605 }
02606 
02607 // intersects current and x,y,w,h rectangle and returns result as a new Fl_Region
02608 Fl_Region Fl_X::intersect_region_and_rect(Fl_Region current, int x,int y,int w, int h)
02609 {
02610   if (current == NULL) return XRectangleRegion(x,y,w,h);
02611   CGRect r = fl_cgrectmake_cocoa(x, y, w, h);
02612   Fl_Region outr = (Fl_Region)malloc(sizeof(*outr));
02613   outr->count = current->count;
02614   outr->rects =(CGRect*)malloc(outr->count * sizeof(CGRect));
02615   int j = 0;
02616   for(int i = 0; i < current->count; i++) {
02617     CGRect test = CGRectIntersection(current->rects[i], r);
02618     if (!CGRectIsEmpty(test)) outr->rects[j++] = test;
02619   }
02620   if (j) {
02621     outr->count = j;
02622     outr->rects = (CGRect*)realloc(outr->rects, outr->count * sizeof(CGRect));
02623   }
02624   else {
02625     XDestroyRegion(outr);
02626     outr = XRectangleRegion(0,0,0,0);
02627   }
02628   return outr;
02629 }
02630 
02631 void Fl_X::collapse() {
02632   [(NSWindow *)xid miniaturize:nil];
02633 }
02634 
02635 static NSImage *CGBitmapContextToNSImage(CGContextRef c)
02636 // the returned NSImage is autoreleased
02637 {
02638   unsigned char *pdata = (unsigned char *)CGBitmapContextGetData(c);
02639   NSBitmapImageRep *imagerep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&pdata
02640                                                                        pixelsWide:CGBitmapContextGetWidth(c)
02641                                                                        pixelsHigh:CGBitmapContextGetHeight(c)
02642                                                                     bitsPerSample:8
02643                                                                   samplesPerPixel:4
02644                                                                          hasAlpha:YES
02645                                                                          isPlanar:NO
02646                                                                    colorSpaceName:NSDeviceRGBColorSpace
02647                                                                       bytesPerRow:CGBitmapContextGetBytesPerRow(c)
02648                                                                      bitsPerPixel:CGBitmapContextGetBitsPerPixel(c)];
02649   NSImage* image = [[NSImage alloc] initWithData: [imagerep TIFFRepresentation]];
02650   [imagerep release];
02651   return [image autorelease];
02652 }
02653 
02654 static NSCursor *PrepareCursor(NSCursor *cursor, CGContextRef (*f)() )
02655 {
02656   if (cursor == nil) {
02657     CGContextRef c = f();
02658     NSImage *image = CGBitmapContextToNSImage(c);
02659     fl_delete_offscreen( (Fl_Offscreen)c ); 
02660     NSPoint pt = {[image size].width/2, [image size].height/2};
02661     cursor = [[NSCursor alloc] initWithImage:image hotSpot:pt];
02662   }
02663   return cursor;
02664 }
02665 
02666 void Fl_X::set_cursor(Fl_Cursor c)
02667 {
02668   NSCursor *icrsr;
02669   switch (c) {
02670     case FL_CURSOR_CROSS:  icrsr = [NSCursor crosshairCursor]; break;
02671     case FL_CURSOR_WAIT:
02672       static NSCursor *watch = nil;
02673       watch = PrepareCursor(watch,  &Fl_X::watch_cursor_image);
02674       icrsr = watch;
02675       break;
02676     case FL_CURSOR_INSERT: icrsr = [NSCursor IBeamCursor]; break;
02677     case FL_CURSOR_N:      icrsr = [NSCursor resizeUpCursor]; break;
02678     case FL_CURSOR_S:      icrsr = [NSCursor resizeDownCursor]; break;
02679     case FL_CURSOR_NS:     icrsr = [NSCursor resizeUpDownCursor]; break;
02680     case FL_CURSOR_HELP:   
02681       static NSCursor *help = nil;
02682       help = PrepareCursor(help,  &Fl_X::help_cursor_image);
02683       icrsr = help;
02684       break;
02685     case FL_CURSOR_HAND:   icrsr = [NSCursor pointingHandCursor]; break;
02686     case FL_CURSOR_MOVE:   icrsr = [NSCursor openHandCursor]; break;
02687     case FL_CURSOR_NE:
02688     case FL_CURSOR_SW:
02689     case FL_CURSOR_NESW:   
02690       static NSCursor *nesw = nil;
02691       nesw = PrepareCursor(nesw,  &Fl_X::nesw_cursor_image);
02692       icrsr = nesw;
02693       break;
02694     case FL_CURSOR_E:      icrsr = [NSCursor resizeRightCursor]; break;
02695     case FL_CURSOR_W:      icrsr = [NSCursor resizeLeftCursor]; break;
02696     case FL_CURSOR_WE:     icrsr = [NSCursor resizeLeftRightCursor]; break;
02697     case FL_CURSOR_SE:
02698     case FL_CURSOR_NW:
02699     case FL_CURSOR_NWSE:   
02700       static NSCursor *nwse = nil;
02701       nwse = PrepareCursor(nwse,  &Fl_X::nwse_cursor_image);
02702       icrsr = nwse;
02703       break;
02704     case FL_CURSOR_NONE:   
02705       static NSCursor *none = nil;
02706       none = PrepareCursor(none,  &Fl_X::none_cursor_image);
02707       icrsr = none; 
02708       break;
02709     case FL_CURSOR_ARROW:
02710     case FL_CURSOR_DEFAULT:
02711     default:                       icrsr = [NSCursor arrowCursor];
02712       break;
02713   }
02714   [icrsr set];
02715   cursor = icrsr;
02716 }
02717 
02718 int Fl_X::screen_init(XRectangle screens[], float dpi[])
02719 {
02720   NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init]; 
02721   NSArray *a = [NSScreen screens]; 
02722   NSScreen *object;
02723   int count = (int)[a count]; 
02724   NSRect r; 
02725   int i, num_screens = 0;
02726   for( i = 0; i < count; i++) {
02727     object = (NSScreen*)[a objectAtIndex:i];
02728     r = [object frame];
02729     screens[num_screens].x      = int(r.origin.x);
02730     screens[num_screens].y      = int(r.size.height - (r.origin.y + r.size.height));
02731     screens[num_screens].width  = int(r.size.width);
02732     screens[num_screens].height = int(r.size.height);
02733 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
02734     if ([object respondsToSelector:@selector(userSpaceScaleFactor)]) {
02735       dpi[num_screens] = float([object userSpaceScaleFactor])*72.0f;
02736     } else 
02737 #endif
02738     {
02739       dpi[num_screens] = 72.0f;
02740     }
02741         
02742     num_screens ++;
02743     if (num_screens >= 16) break;
02744   }
02745   [localPool release];
02746   return num_screens;
02747 }
02748 
02749 @interface FLaboutItemTarget : NSObject 
02750 {
02751 }
02752 - (void)showPanel;
02753 - (void)printPanel;
02754 @end
02755 @implementation FLaboutItemTarget
02756 - (void)showPanel
02757 {
02758     NSDictionary *options;
02759     options = [NSDictionary dictionaryWithObjectsAndKeys:
02760                              [NSString stringWithFormat:@" GUI with FLTK %d.%d", FL_MAJOR_VERSION,
02761                               FL_MINOR_VERSION ], @"Copyright",
02762                              nil];
02763     [NSApp  orderFrontStandardAboutPanelWithOptions:options];
02764   }
02765 - (void)printPanel
02766 {
02767   Fl_Printer printer;
02768   //Fl_PostScript_File_Device printer;
02769   int w, h;
02770   Fl_Window *win = Fl::first_window();
02771   if(!win) return;
02772   if( printer.start_job(1) ) return;
02773   if( printer.start_page() ) return;
02774   // scale the printer device so that the window fits on the page
02775   float scale = 1;
02776   printer.printable_rect(&w, &h);
02777   if (win->w()>w || win->h()>h) {
02778     scale = (float)w/win->w();
02779     if ((float)h/win->h() < scale) scale = (float)h/win->h();
02780     printer.scale(scale, scale);
02781   }
02782 #ifdef ROTATE
02783   printer.scale(scale * 0.8, scale * 0.8);
02784   printer.printable_rect(&w, &h);
02785   printer.origin(w/2, h/2 );
02786   printer.rotate(20.);
02787   printer.print_widget( win, - win->w()/2, - win->h()/2 );
02788 #else
02789   printer.print_widget( win);
02790   //printer.print_window_part( win, 0,0, win->w(), win->h() );
02791 #endif
02792   printer.end_page();
02793   printer.end_job();
02794 }
02795 @end
02796 
02797 static NSMenu *appleMenu;
02798 static void createAppleMenu(void)
02799 {
02800   static BOOL donethat = NO;
02801   if (donethat) return;
02802   donethat = YES;
02803   NSMenu *mainmenu, *services;
02804   NSMenuItem *menuItem;
02805   NSString *title;
02806   CFStringRef nsappname;
02807   
02808   ProcessSerialNumber psn;
02809   GetCurrentProcess(&psn);
02810   CopyProcessName(&psn, &nsappname);
02811   appleMenu = [[NSMenu alloc] initWithTitle:@""];
02812   /* Add menu items */
02813   title = [@"About " stringByAppendingString:(NSString*)nsappname];
02814   menuItem = [appleMenu addItemWithTitle:title action:@selector(showPanel) keyEquivalent:@""];
02815   FLaboutItemTarget *about = [[FLaboutItemTarget alloc] init];
02816   [menuItem setTarget:about];
02817   [appleMenu addItem:[NSMenuItem separatorItem]];
02818   // Print front window
02819   menuItem = [appleMenu addItemWithTitle:@"Print front window" action:@selector(printPanel) keyEquivalent:@""];
02820   [menuItem setTarget:about];
02821   [appleMenu setAutoenablesItems:NO];
02822   [menuItem setEnabled:YES];
02823   [appleMenu addItem:[NSMenuItem separatorItem]];
02824   // Services Menu
02825   services = [[NSMenu alloc] init];
02826   [appleMenu addItemWithTitle:@"Services" action:nil keyEquivalent:@""];
02827   [appleMenu setSubmenu: services forItem: [appleMenu itemWithTitle: @"Services"]];
02828   // Hide AppName
02829   title = [@"Hide " stringByAppendingString:(NSString*)nsappname];
02830   [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
02831   // Hide Others
02832   menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) 
02833                                          keyEquivalent:@"h"];
02834   [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
02835   // Show All
02836   [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
02837   [appleMenu addItem:[NSMenuItem separatorItem]];
02838   // Quit AppName
02839   title = [@"Quit " stringByAppendingString:(NSString*)nsappname];
02840   [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
02841   /* Put menu into the menubar */
02842   menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
02843   [menuItem setSubmenu:appleMenu];
02844   mainmenu = [[NSMenu alloc] initWithTitle:@""];
02845   [mainmenu addItem:menuItem];
02846   if (fl_mac_os_version < 0x1060) {
02847     //  [NSApp setAppleMenu:appleMenu];
02848     //  to avoid compiler warning raised by use of undocumented setAppleMenu    :
02849     [NSApp performSelector:@selector(setAppleMenu:) withObject:appleMenu];
02850   }
02851   [NSApp setServicesMenu:services];
02852   [NSApp setMainMenu:mainmenu];
02853   CFRelease(nsappname);
02854   [services release];
02855   [mainmenu release];
02856   [appleMenu release];
02857   [menuItem release];
02858   fl_system_menu = [NSApp mainMenu];
02859 }
02860 
02861 @interface FLMenuItem : NSMenuItem {
02862 }
02863 - (void) doCallback:(id)unused;
02864 - (void) directCallback:(id)unused;
02865 @end
02866 @implementation FLMenuItem
02867 - (void) doCallback:(id)unused
02868 {
02869   int flRank = [self tag];
02870   const Fl_Menu_Item *items = fl_sys_menu_bar->Fl_Menu_::menu();
02871   const Fl_Menu_Item *item = items + flRank;
02872   if (item) {
02873     fl_sys_menu_bar->picked(item);
02874     if ( item->flags & FL_MENU_TOGGLE ) {       // update the menu toggle symbol
02875       [self setState:(item->value() ? NSOnState : NSOffState)];
02876     }
02877     else if ( item->flags & FL_MENU_RADIO ) {   // update the menu radio symbols
02878       int from = flRank;
02879       while( from > 0 && items[from - 1].label() && (items[from - 1].flags & FL_MENU_RADIO) &&
02880             !(items[from - 1].flags & FL_MENU_DIVIDER) ) {
02881         from--;
02882       }
02883       int to = flRank;
02884       while( !(items[to].flags & FL_MENU_DIVIDER) && items[to + 1].label() && 
02885             (items[to + 1].flags & FL_MENU_RADIO) ) {
02886         to++;
02887       }
02888       NSMenu *nsmenu = [self menu];
02889       int nsrank = (int)[nsmenu indexOfItem:self];
02890       for(int i =  from - flRank + nsrank ; i <= to - flRank + nsrank; i++) {
02891         NSMenuItem *nsitem = [nsmenu itemAtIndex:i];
02892         if (nsitem != self) [nsitem setState:NSOffState];
02893         else [nsitem setState:(item->value() ? NSOnState : NSOffState) ];
02894       }
02895     }
02896   }
02897 }
02898 - (void) directCallback:(id)unused
02899 {
02900   Fl_Menu_Item *item = (Fl_Menu_Item *)[(NSData*)[self representedObject] bytes];
02901   if ( item && item->callback() ) item->do_callback(NULL);
02902 }
02903 @end
02904 
02905 void fl_mac_set_about( Fl_Callback *cb, void *user_data, int shortcut) 
02906 {
02907   NSAutoreleasePool *localPool;
02908   localPool = [[NSAutoreleasePool alloc] init]; 
02909   fl_open_display();
02910   Fl_Menu_Item aboutItem;
02911   memset(&aboutItem, 0, sizeof(Fl_Menu_Item));
02912   aboutItem.callback(cb);
02913   aboutItem.user_data(user_data);
02914   aboutItem.shortcut(shortcut);
02915   CFStringRef cfname = CFStringCreateCopy(NULL, (CFStringRef)[[appleMenu itemAtIndex:0] title]);
02916   [appleMenu removeItemAtIndex:0];
02917   FLMenuItem *item = [[[FLMenuItem alloc] initWithTitle:(NSString*)cfname 
02918                                                  action:@selector(directCallback:) 
02919                                           keyEquivalent:@""] autorelease];
02920   if (aboutItem.shortcut()) {
02921     Fl_Sys_Menu_Bar::doMenuOrItemOperation(Fl_Sys_Menu_Bar::setKeyEquivalent, item, aboutItem.shortcut() & 0xff);
02922     Fl_Sys_Menu_Bar::doMenuOrItemOperation(Fl_Sys_Menu_Bar::setKeyEquivalentModifierMask, item, aboutItem.shortcut() );
02923   }
02924   NSData *pointer = [NSData dataWithBytes:&aboutItem length:sizeof(Fl_Menu_Item)];
02925   [item setRepresentedObject:pointer];
02926   [appleMenu insertItem:item atIndex:0];
02927   CFRelease(cfname);
02928   [item setTarget:item];
02929   [localPool release];
02930 }
02931 
02932 static char *remove_ampersand(const char *s)
02933 {
02934   char *ret = strdup(s);
02935   const char *p = s;
02936   char *q = ret;
02937   while(*p != 0) {
02938     if (p[0]=='&') {
02939       if (p[1]=='&') {
02940         *q++ = '&'; p+=2;
02941       } else {
02942         p++;
02943       }
02944     } else {
02945       *q++ = *p++;
02946     }
02947   }
02948   *q = 0;
02949   return ret;
02950 }
02951 
02952 void *Fl_Sys_Menu_Bar::doMenuOrItemOperation(Fl_Sys_Menu_Bar::menuOrItemOperation operation, ...)
02953 /* these operations apply to menus, submenus, or menu items
02954  */
02955 {
02956   NSAutoreleasePool *localPool;
02957   localPool = [[NSAutoreleasePool alloc] init]; 
02958   NSMenu *menu;
02959   NSMenuItem *item;
02960   int value;
02961   void *pter;
02962   void *retval = NULL;
02963   va_list ap;
02964   va_start(ap, operation);
02965   
02966   if (operation == Fl_Sys_Menu_Bar::itemAtIndex) {      // arguments: NSMenu*, int. Returns the item
02967     menu = va_arg(ap, NSMenu*);
02968     value = va_arg(ap, int);
02969     retval = (void *)[menu itemAtIndex:value];
02970   }
02971   else if (operation == Fl_Sys_Menu_Bar::setKeyEquivalent) {    // arguments: NSMenuItem*, int
02972     item = va_arg(ap, NSMenuItem*);
02973     value = va_arg(ap, int);
02974     char key = value;
02975     NSString *equiv = [[NSString alloc] initWithBytes:&key length:1 encoding:NSASCIIStringEncoding];
02976     [item setKeyEquivalent:equiv];
02977     [equiv release];
02978   }
02979   else if (operation == Fl_Sys_Menu_Bar::setKeyEquivalentModifierMask) {                // arguments: NSMenuItem*, int
02980     item = va_arg(ap, NSMenuItem*);
02981     value = va_arg(ap, int);
02982     NSUInteger macMod = 0;
02983     if ( value & FL_META ) macMod = NSCommandKeyMask;
02984     if ( value & FL_SHIFT || isupper(value) ) macMod |= NSShiftKeyMask;
02985     if ( value & FL_ALT ) macMod |= NSAlternateKeyMask;
02986     if ( value & FL_CTRL ) macMod |= NSControlKeyMask;
02987     [item setKeyEquivalentModifierMask:macMod];
02988   }
02989   else if (operation == Fl_Sys_Menu_Bar::setState) {    // arguments: NSMenuItem*, int
02990     item = va_arg(ap, NSMenuItem*);
02991     value = va_arg(ap, int);
02992     [item setState:(value ? NSOnState : NSOffState)];
02993   }
02994   else if (operation == Fl_Sys_Menu_Bar::initWithTitle) {       // arguments: const char*title. Returns the newly created menu
02995                                                                 // creates a new (sub)menu
02996     char *ts = remove_ampersand(va_arg(ap, char *));
02997     CFStringRef title = CFStringCreateWithCString(NULL, ts, kCFStringEncodingUTF8);
02998     free(ts);
02999     NSMenu *menu = [[NSMenu alloc] initWithTitle:(NSString*)title];
03000     CFRelease(title);
03001     [menu setAutoenablesItems:NO];
03002     retval = (void *)menu;
03003   }
03004   else if (operation == Fl_Sys_Menu_Bar::numberOfItems) {       // arguments: NSMenu *menu, int *pcount
03005                                                                 // upon return, *pcount is set to menu's item count
03006     menu = va_arg(ap, NSMenu*);
03007     pter = va_arg(ap, void *);
03008     *(int*)pter = [menu numberOfItems];
03009   }
03010   else if (operation == Fl_Sys_Menu_Bar::setSubmenu) {          // arguments: NSMenuItem *item, NSMenu *menu
03011                                                                 // sets 'menu' as submenu attached to 'item'
03012     item = va_arg(ap, NSMenuItem*);
03013     menu = va_arg(ap, NSMenu*);
03014     [item setSubmenu:menu];
03015     [menu release];
03016   }
03017   else if (operation == Fl_Sys_Menu_Bar::setEnabled) {          // arguments: NSMenuItem*, int
03018     item = va_arg(ap, NSMenuItem*);
03019     value = va_arg(ap, int);
03020     [item setEnabled:(value ? YES : NO)];
03021   }
03022   else if (operation == Fl_Sys_Menu_Bar::addSeparatorItem) {    // arguments: NSMenu*
03023     menu = va_arg(ap, NSMenu*);
03024     [menu addItem:[NSMenuItem separatorItem]];
03025   }
03026   else if (operation == Fl_Sys_Menu_Bar::setTitle) {            // arguments: NSMenuItem*, const char *
03027     item = va_arg(ap, NSMenuItem*);
03028     char *ts = remove_ampersand(va_arg(ap, char *));
03029     CFStringRef title = CFStringCreateWithCString(NULL, ts, kCFStringEncodingUTF8);
03030     free(ts);
03031     [item setTitle:(NSString*)title];
03032     CFRelease(title);
03033   }
03034   else if (operation == Fl_Sys_Menu_Bar::removeItem) {          // arguments: NSMenu*, int
03035     menu = va_arg(ap, NSMenu*);
03036     value = va_arg(ap, int);
03037     [menu removeItem:[menu itemAtIndex:value]];
03038   }
03039   else if (operation == Fl_Sys_Menu_Bar::addNewItem) {          // arguments: NSMenu *menu, int flrank, int *prank
03040     // creates a new menu item at the end of 'menu'
03041     // attaches the item of rank flrank (counted in Fl_Menu_) of fl_sys_menu_bar to it
03042     // upon return, puts the rank (counted in NSMenu) of the new item in *prank unless prank is NULL
03043     menu = va_arg(ap, NSMenu*);
03044     int flRank = va_arg(ap, int);
03045     char *name = remove_ampersand( (fl_sys_menu_bar->Fl_Menu_::menu() + flRank)->label());
03046     int *prank = va_arg(ap, int*);
03047     CFStringRef cfname = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8);
03048     free(name);
03049     FLMenuItem *item = [[FLMenuItem alloc] initWithTitle:(NSString*)cfname 
03050                                                   action:@selector(doCallback:) 
03051                                            keyEquivalent:@""];
03052     [item setTag:flRank];
03053     [menu addItem:item];
03054     CFRelease(cfname);
03055     [item setTarget:item];
03056     if (prank != NULL) *prank = [menu indexOfItem:item];
03057     [item release];
03058   }
03059   else if (operation == Fl_Sys_Menu_Bar::renameItem) {          // arguments: int rank, const char *newname
03060     // renames the system menu item numbered rank in fl_sys_menu_bar->menu()
03061     int rank = va_arg(ap, int);
03062     char *newname = remove_ampersand( va_arg(ap, const char *) );
03063     int countmenus = [(NSMenu*)fl_system_menu numberOfItems];
03064     bool found = NO;
03065     NSMenuItem *macitem = 0;
03066     for(int i = 1; (!found) && i < countmenus; i++) {
03067       NSMenuItem *item = [(NSMenu*)fl_system_menu itemAtIndex:i];
03068       NSMenu *submenu = [item submenu];
03069       if (submenu == nil) continue;
03070       int countitems = [submenu numberOfItems];
03071       for(int j = 0; j < countitems; j++) {
03072         macitem = [submenu itemAtIndex:j];
03073         if ([macitem tag] == rank) { found = YES; break; }
03074       }
03075     }
03076     if (found) {
03077       [macitem setTitle:[[[NSString alloc] initWithUTF8String:newname] autorelease]];
03078     }
03079     free(newname);
03080   }
03081   va_end(ap);
03082   [localPool release];
03083   return retval;
03084 }
03085 
03086 void Fl_X::set_key_window()
03087 {
03088   [(NSWindow*)xid makeKeyAndOrderFront:nil];
03089 }
03090 
03091 static NSImage *imageFromText(const char *text, int *pwidth, int *pheight)
03092 {
03093   const char *p, *q;
03094   int width = 0, height, w2, ltext = strlen(text);
03095   fl_font(FL_HELVETICA, 10);
03096   p = text;
03097   int nl = 0;
03098   while((q=strchr(p, '\n')) != NULL) { 
03099     nl++; 
03100     w2 = int(fl_width(p, q - p));
03101     if (w2 > width) width = w2;
03102     p = q + 1; 
03103   }
03104   if (text[ ltext - 1] != '\n') {
03105     nl++;
03106     w2 = int(fl_width(p));
03107     if (w2 > width) width = w2;
03108   }
03109   height = nl * fl_height() + 3;
03110   width += 6;
03111   Fl_Offscreen off = fl_create_offscreen_with_alpha(width, height);
03112   fl_begin_offscreen(off);
03113   CGContextSetRGBFillColor( (CGContextRef)off, 0,0,0,0);
03114   fl_rectf(0,0,width,height);
03115   fl_color(FL_BLACK);
03116   p = text;
03117   int y = fl_height();
03118   while(TRUE) {
03119     q = strchr(p, '\n');
03120     if (q) {
03121       fl_draw(p, q - p, 3, y);
03122     } else {
03123       fl_draw(p, 3, y);
03124       break;
03125     }
03126     y += fl_height();
03127     p = q + 1;
03128   }
03129   fl_end_offscreen();
03130   NSImage* image = CGBitmapContextToNSImage( (CGContextRef)off );
03131   fl_delete_offscreen( off );
03132   *pwidth = width;
03133   *pheight = height;
03134   return image;
03135 }
03136 
03137 static NSImage *defaultDragImage(int *pwidth, int *pheight)
03138 {
03139   const int width = 16, height = 16;
03140   Fl_Offscreen off = fl_create_offscreen_with_alpha(width, height);
03141   fl_begin_offscreen(off);
03142   CGContextSetRGBFillColor( (CGContextRef)off, 0,0,0,0);
03143   fl_rectf(0,0,width,height);
03144   CGContextSetRGBStrokeColor( (CGContextRef)off, 0,0,0,0.6);
03145   fl_rect(0,0,width,height);
03146   fl_rect(2,2,width-4,height-4);
03147   fl_end_offscreen();
03148   NSImage* image = CGBitmapContextToNSImage( (CGContextRef)off );
03149   fl_delete_offscreen( off );
03150   *pwidth = width;
03151   *pheight = height;
03152   return image;
03153 }
03154 
03155 int Fl::dnd(void)
03156 {
03157   CFDataRef text = CFDataCreate(kCFAllocatorDefault, (UInt8*)fl_selection_buffer[0], fl_selection_length[0]);
03158   if (text==NULL) return false;
03159   NSAutoreleasePool *localPool;
03160   localPool = [[NSAutoreleasePool alloc] init]; 
03161   NSPasteboard *mypasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
03162   [mypasteboard declareTypes:[NSArray arrayWithObjects:@"public.utf8-plain-text", nil] owner:nil];
03163   [mypasteboard setData:(NSData*)text forType:@"public.utf8-plain-text"];
03164   CFRelease(text);
03165   Fl_Widget *w = Fl::pushed();
03166   Fl_Window *win = w->window();
03167   if (win == NULL) {
03168     win = (Fl_Window*)w;
03169   } else { 
03170     while(win->window()) win = win->window();
03171   }
03172   NSView *myview = [(NSWindow*)Fl_X::i(win)->xid contentView];
03173   NSEvent *theEvent = [NSApp currentEvent];
03174   
03175   int width, height;
03176   NSImage *image;
03177   if ( dynamic_cast<Fl_Input_*>(w) != NULL ||  dynamic_cast<Fl_Text_Display*>(w) != NULL) {
03178     fl_selection_buffer[0][ fl_selection_length[0] ] = 0;
03179     image = imageFromText(fl_selection_buffer[0], &width, &height);
03180   } else {
03181     image = defaultDragImage(&width, &height);
03182   }
03183   
03184   static NSSize offset={0,0};
03185   NSPoint pt = [theEvent locationInWindow];
03186   pt.x -= width/2;
03187   pt.y -= height/2;
03188   [myview dragImage:image  at:pt  offset:offset 
03189               event:theEvent  pasteboard:mypasteboard  
03190              source:myview  slideBack:YES];
03191   if ( w ) {
03192     int old_event = Fl::e_number;
03193     w->handle(Fl::e_number = FL_RELEASE);
03194     Fl::e_number = old_event;
03195     Fl::pushed( 0 );
03196   }
03197   [localPool release];
03198   return true;
03199 }
03200 
03201 unsigned char *Fl_X::bitmap_from_window_rect(Fl_Window *win, int x, int y, int w, int h, int *bytesPerPixel)
03202 // delete the returned pointer after use
03203 {
03204   while(win->window()) {
03205     x += win->x();
03206     y += win->y();
03207     win = win->window();
03208   }
03209   CGFloat epsilon = 0;
03210   if (fl_mac_os_version >= 0x1060) epsilon = 0.001;
03211   // The epsilon offset is absolutely necessary under 10.6. Without it, the top pixel row and
03212   // left pixel column are not read, and bitmap is read shifted by one pixel in both directions. 
03213   // Under 10.5, we want no offset.
03214   NSRect rect = NSMakeRect(x - epsilon, y - epsilon, w, h);
03215   NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithFocusedViewRect:rect];
03216   *bytesPerPixel = [bitmap bitsPerPixel]/8;
03217   int bpp = (int)[bitmap bytesPerPlane];
03218   int bpr = (int)[bitmap bytesPerRow];
03219   int hh = bpp/bpr; // sometimes hh = h-1 for unclear reason
03220   int ww = bpr/(*bytesPerPixel); // sometimes ww = w-1
03221   unsigned char *data = new unsigned char[w * h *  *bytesPerPixel];
03222   if (w == ww) {
03223     memcpy(data, [bitmap bitmapData], w * hh *  *bytesPerPixel);
03224   } else {
03225     unsigned char *p = [bitmap bitmapData];
03226     unsigned char *q = data;
03227     for(int i = 0;i < hh; i++) {
03228       memcpy(q, p, *bytesPerPixel * ww);
03229       p += bpr;
03230       q += w * *bytesPerPixel;
03231       }
03232   }
03233   [bitmap release];
03234   return data;
03235 }
03236 
03237 void imgProviderReleaseData (void *info, const void *data, size_t size)
03238 {
03239   delete (unsigned char *)data;
03240 }
03241 
03242 CGImageRef Fl_X::CGImage_from_window_rect(Fl_Window *win, int x, int y, int w, int h)
03243 // CFRelease the returned CGImageRef after use
03244 {
03245   int bpp;
03246   unsigned char *bitmap = bitmap_from_window_rect(win, x, y, w, h, &bpp);
03247   CGImageRef img;
03248   CGColorSpaceRef lut = CGColorSpaceCreateDeviceRGB();
03249   CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, bitmap, w*h*bpp, imgProviderReleaseData);
03250   img = CGImageCreate(w, h, 8, 8*bpp, w*bpp, lut,
03251                       bpp == 3 ? kCGImageAlphaNone : kCGImageAlphaLast,
03252                       provider, NULL, false, kCGRenderingIntentDefault);
03253   CGColorSpaceRelease(lut);
03254   CGDataProviderRelease(provider);
03255   return img;
03256 }
03257 
03258 void Fl_X::contains_GL_subwindow() 
03259 {
03260   [(FLWindow*)xid setContainsGLsubwindow:YES];
03261 }
03262 
03263 WindowRef Fl_X::window_ref()
03264 {
03265   return (WindowRef)[(FLWindow*)xid windowRef];
03266 }
03267 
03268 // so a CGRect matches exactly what is denoted x,y,w,h for clipping purposes
03269 CGRect fl_cgrectmake_cocoa(int x, int y, int w, int h) {
03270   if ( Fl_Surface_Device::surface()->class_name() == Fl_Printer::class_id ) return CGRectMake(x, y, w-1.5 , h-1.5 ); 
03271   return CGRectMake(x, y, w > 0 ? w - 0.9 : 0, h > 0 ? h - 0.9 : 0);
03272 }
03273 
03274 Window fl_xid(const Fl_Window* w)
03275 {
03276   return Fl_X::i(w)->xid;
03277 }
03278 
03279 #include <dlfcn.h>
03280 
03281 /* Returns the address of a Carbon function after dynamically loading the Carbon library if needed.
03282  Supports old Mac OS X versions that may use a couple of Carbon calls:
03283  GetKeys used by OS X 10.3 or before (in Fl::get_key())
03284  PMSessionPageSetupDialog and PMSessionPrintDialog used by 10.4 or before (in Fl_Printer::start_job())
03285  GetWindowPort used by 10.4 or before (in Fl_Gl_Choice.cxx)
03286  */
03287 void *Fl_X::get_carbon_function(const char *function_name) {
03288   static void *carbon = NULL;
03289   void *f = NULL;
03290   if (!carbon) {
03291     carbon = dlopen("/System/Library/Frameworks/Carbon.framework/Carbon", RTLD_LAZY);
03292   }
03293   if (carbon) {
03294     f = dlsym(carbon, function_name);
03295   }
03296   return f;
03297 }
03298   
03299 #endif // __APPLE__
03300 
03301 //
03302 // End of "$Id: Fl_cocoa.mm 6971 2009-04-13 07:32:01Z matt $".
03303 //