|
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) ![]() |
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 //