DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1 | // |
| 2 | // "$Id: Fl_cocoa.mm 8807 2011-06-16 12:35:32Z manolo $" |
| 3 | // |
| 4 | // MacOS-Cocoa specific code for the Fast Light Tool Kit (FLTK). |
| 5 | // |
| 6 | // Copyright 1998-2011 by Bill Spitzak and others. |
| 7 | // |
| 8 | // This library is free software; you can redistribute it and/or |
| 9 | // modify it under the terms of the GNU Library General Public |
| 10 | // License as published by the Free Software Foundation; either |
| 11 | // version 2 of the License, or (at your option) any later version. |
| 12 | // |
| 13 | // This library is distributed in the hope that it will be useful, |
| 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 16 | // Library General Public License for more details. |
| 17 | // |
| 18 | // You should have received a copy of the GNU Library General Public |
| 19 | // License along with this library; if not, write to the Free Software |
| 20 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
| 21 | // USA. |
| 22 | // |
| 23 | // Please report all bugs and problems on the following page: |
| 24 | // |
| 25 | // http://www.fltk.org/str.php |
| 26 | // |
| 27 | |
| 28 | //// From the inner edge of a MetroWerks CodeWarrior CD: |
| 29 | // (without permission) |
| 30 | // |
| 31 | // "Three Compiles for 68Ks under the sky, |
| 32 | // Seven Compiles for PPCs in their fragments of code, |
| 33 | // Nine Compiles for Mortal Carbon doomed to die, |
| 34 | // One Compile for Mach-O Cocoa on its Mach-O throne, |
| 35 | // in the Land of MacOS X where the Drop-Shadows lie. |
| 36 | // |
| 37 | // One Compile to link them all, One Compile to merge them, |
| 38 | // One Compile to copy them all and in the bundle bind them, |
| 39 | // in the Land of MacOS X where the Drop-Shadows lie." |
| 40 | |
| 41 | #ifdef __APPLE__ |
| 42 | |
| 43 | #define CONSOLIDATE_MOTION 0 |
| 44 | extern "C" { |
| 45 | #include <pthread.h> |
| 46 | } |
| 47 | |
| 48 | |
| 49 | #include <FL/Fl.H> |
| 50 | #include <FL/x.H> |
| 51 | #include <FL/Fl_Window.H> |
| 52 | #include <FL/Fl_Tooltip.H> |
| 53 | #include <FL/Fl_Sys_Menu_Bar.H> |
| 54 | #include <FL/Fl_Printer.H> |
| 55 | #include <FL/Fl_Input_.H> |
| 56 | #include <FL/Fl_Text_Display.H> |
| 57 | #include <stdio.h> |
| 58 | #include <stdlib.h> |
| 59 | #include "flstring.h" |
| 60 | #include <unistd.h> |
| 61 | #include <stdarg.h> |
| 62 | |
| 63 | #import <Cocoa/Cocoa.h> |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 64 | #import <Carbon/Carbon.h> |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 65 | |
| 66 | #ifndef NSINTEGER_DEFINED // appears with 10.5 in NSObjCRuntime.h |
| 67 | #if defined(__LP64__) && __LP64__ |
| 68 | typedef long NSInteger; |
| 69 | typedef unsigned long NSUInteger; |
| 70 | #else |
| 71 | typedef long NSInteger; |
| 72 | typedef unsigned int NSUInteger; |
| 73 | #endif |
| 74 | #endif |
| 75 | |
| 76 | |
| 77 | // #define DEBUG_SELECT // UNCOMMENT FOR SELECT()/THREAD DEBUGGING |
| 78 | #ifdef DEBUG_SELECT |
| 79 | #include <stdio.h> // testing |
| 80 | #define DEBUGMSG(msg) if ( msg ) fprintf(stderr, msg); |
| 81 | #define DEBUGPERRORMSG(msg) if ( msg ) perror(msg) |
| 82 | #define DEBUGTEXT(txt) txt |
| 83 | #else |
| 84 | #define DEBUGMSG(msg) |
| 85 | #define DEBUGPERRORMSG(msg) |
| 86 | #define DEBUGTEXT(txt) NULL |
| 87 | #endif /*DEBUG_SELECT*/ |
| 88 | |
| 89 | // external functions |
| 90 | extern void fl_fix_focus(); |
| 91 | extern Fl_Offscreen fl_create_offscreen_with_alpha(int w, int h); |
| 92 | |
| 93 | // forward definition of functions in this file |
| 94 | // converting cr lf converter function |
| 95 | static void convert_crlf(char * string, size_t len); |
| 96 | static void createAppleMenu(void); |
| 97 | static Fl_Region MacRegionMinusRect(Fl_Region r, int x,int y,int w,int h); |
| 98 | static void cocoaMouseHandler(NSEvent *theEvent); |
| 99 | |
| 100 | static Fl_Quartz_Graphics_Driver fl_quartz_driver; |
| 101 | static Fl_Display_Device fl_quartz_display(&fl_quartz_driver); |
| 102 | FL_EXPORT Fl_Graphics_Driver *fl_graphics_driver = (Fl_Graphics_Driver*)&fl_quartz_driver; // the current target device of graphics operations |
| 103 | Fl_Surface_Device* Fl_Surface_Device::_surface = (Fl_Surface_Device*)&fl_quartz_display; // the current target surface of graphics operations |
| 104 | Fl_Display_Device *Fl_Display_Device::_display = &fl_quartz_display; // the platform display |
| 105 | |
| 106 | // public variables |
| 107 | int fl_screen; |
| 108 | CGContextRef fl_gc = 0; |
| 109 | void *fl_system_menu; // this is really a NSMenu* |
| 110 | Fl_Sys_Menu_Bar *fl_sys_menu_bar = 0; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 111 | void *fl_capture = 0; // (NSWindow*) we need this to compensate for a missing(?) mouse capture |
| 112 | bool fl_show_iconic; // true if called from iconize() - shows the next created window in collapsed state |
| 113 | //int fl_disable_transient_for; // secret method of removing TRANSIENT_FOR |
| 114 | Window fl_window; |
| 115 | Fl_Window *Fl_Window::current_; |
| 116 | int fl_mac_os_version = 0; // the version number of the running Mac OS X (e.g., 100604 for 10.6.4) |
| 117 | |
| 118 | // forward declarations of variables in this file |
| 119 | static int got_events = 0; |
| 120 | static Fl_Window* resize_from_system; |
| 121 | |
| 122 | #if CONSOLIDATE_MOTION |
| 123 | static Fl_Window* send_motion; |
| 124 | extern Fl_Window* fl_xmousewin; |
| 125 | #endif |
| 126 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 127 | bool use_simple_keyboard = false; |
| 128 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 129 | enum { FLTKTimerEvent = 1, FLTKDataReadyEvent }; |
| 130 | |
| 131 | |
| 132 | /* fltk-utf8 placekeepers */ |
| 133 | void fl_reset_spot() |
| 134 | { |
| 135 | } |
| 136 | |
| 137 | void fl_set_spot(int font, int size, int X, int Y, int W, int H, Fl_Window *win) |
| 138 | { |
| 139 | } |
| 140 | |
| 141 | void fl_set_status(int x, int y, int w, int h) |
| 142 | { |
| 143 | } |
| 144 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 145 | // Undocumented voodoo. Taken from Mozilla. |
| 146 | #define ENABLE_ROMAN_KYBDS_ONLY -23 |
| 147 | |
| 148 | void fl_update_focus(void) |
| 149 | { |
| 150 | Fl_Widget *focus; |
| 151 | |
| 152 | focus = Fl::grab(); |
| 153 | if (!focus) |
| 154 | focus = Fl::focus(); |
| 155 | if (!focus) |
| 156 | return; |
| 157 | |
| 158 | if (focus->simple_keyboard()) |
| 159 | use_simple_keyboard = true; |
| 160 | else |
| 161 | use_simple_keyboard = false; |
| 162 | |
| 163 | // Force a "Roman" or "ASCII" keyboard, which both the Mozilla and |
| 164 | // Safari people seem to think implies turning off advanced IME stuff |
| 165 | // (see nsTSMManager::SyncKeyScript in Mozilla and enableSecureTextInput |
| 166 | // in Safari/Webcore). Should be good enough for us then... |
| 167 | #if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) |
| 168 | CFArrayRef inputSources = TISCreateASCIICapableInputSourceList(); |
| 169 | TSMSetDocumentProperty(TSMGetActiveDocument(), |
| 170 | kTSMDocumentEnabledInputSourcesPropertyTag, |
| 171 | sizeof(CFArrayRef), &inputSources); |
| 172 | CFRelease(inputSources); |
| 173 | #else |
| 174 | KeyScript(use_simple_keyboard ? ENABLE_ROMAN_KYBDS_ONLY : smKeyEnableKybds); |
| 175 | #endif |
| 176 | } |
| 177 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 178 | /* |
| 179 | * Mac keyboard lookup table |
| 180 | * See also the inverse converter vktab in Fl_get_key_mac.cxx |
| 181 | */ |
| 182 | static unsigned short macKeyLookUp[128] = |
| 183 | { |
| 184 | 'a', 's', 'd', 'f', 'h', 'g', 'z', 'x', |
| 185 | 'c', 'v', '^', 'b', 'q', 'w', 'e', 'r', |
| 186 | |
| 187 | 'y', 't', '1', '2', '3', '4', '6', '5', |
| 188 | '=', '9', '7', '-', '8', '0', ']', 'o', |
| 189 | |
| 190 | 'u', '[', 'i', 'p', FL_Enter, 'l', 'j', '\'', |
| 191 | 'k', ';', '\\', ',', '/', 'n', 'm', '.', |
| 192 | |
| 193 | FL_Tab, ' ', '`', FL_BackSpace, |
| 194 | FL_KP_Enter, FL_Escape, FL_Meta_R, FL_Meta_L, |
| 195 | FL_Shift_L, FL_Caps_Lock, FL_Alt_L, FL_Control_L, |
| 196 | FL_Shift_R, FL_Alt_R, FL_Control_R, 0/*FL_F*/, |
| 197 | |
| 198 | 0, FL_KP+'.', FL_Right, FL_KP+'*', 0, FL_KP+'+', FL_Left, FL_Num_Lock, |
| 199 | FL_Down, 0, 0, FL_KP+'/', FL_KP_Enter, FL_Up, FL_KP+'-', 0, |
| 200 | |
| 201 | 0, FL_KP+'=', FL_KP+'0', FL_KP+'1', FL_KP+'2', FL_KP+'3', FL_KP+'4', FL_KP+'5', |
| 202 | FL_KP+'6', FL_KP+'7', 0, FL_KP+'8', FL_KP+'9', 0, 0, 0, |
| 203 | |
| 204 | FL_F+5, FL_F+6, FL_F+7, FL_F+3, FL_F+8, FL_F+9, 0, FL_F+11, |
| 205 | 0, FL_F+13, FL_F+16, FL_F+14, 0, FL_F+10, FL_Menu, FL_F+12, |
| 206 | |
| 207 | 0, FL_F+15, FL_Help, FL_Home, FL_Page_Up, FL_Delete, FL_F+4, FL_End, |
| 208 | FL_F+2, FL_Page_Down, FL_F+1, FL_Left, FL_Right, FL_Down, FL_Up, 0/*FL_Power*/, |
| 209 | }; |
| 210 | |
| 211 | /* |
| 212 | * convert the current mouse chord into the FLTK modifier state |
| 213 | */ |
| 214 | static unsigned int mods_to_e_state( NSUInteger mods ) |
| 215 | { |
| 216 | long state = 0; |
| 217 | if ( mods & NSCommandKeyMask ) state |= FL_META; |
| 218 | if ( mods & NSAlternateKeyMask ) state |= FL_ALT; |
| 219 | if ( mods & NSControlKeyMask ) state |= FL_CTRL; |
| 220 | if ( mods & NSShiftKeyMask ) state |= FL_SHIFT; |
| 221 | if ( mods & NSAlphaShiftKeyMask ) state |= FL_CAPS_LOCK; |
| 222 | unsigned int ret = ( Fl::e_state & 0xff000000 ) | state; |
| 223 | Fl::e_state = ret; |
| 224 | //printf( "State 0x%08x (%04x)\n", Fl::e_state, mods ); |
| 225 | return ret; |
| 226 | } |
| 227 | |
| 228 | // these pointers are set by the Fl::lock() function: |
| 229 | static void nothing() {} |
| 230 | void (*fl_lock_function)() = nothing; |
| 231 | void (*fl_unlock_function)() = nothing; |
| 232 | |
| 233 | // |
| 234 | // Select interface -- how it's implemented: |
| 235 | // When the user app configures one or more file descriptors to monitor |
| 236 | // with Fl::add_fd(), we start a separate thread to select() the data, |
| 237 | // sending a custom OSX 'FLTK data ready event' to the parent thread's |
| 238 | // RunApplicationLoop(), so that it triggers the data ready callbacks |
| 239 | // in the parent thread. -erco 04/04/04 |
| 240 | // |
| 241 | #define POLLIN 1 |
| 242 | #define POLLOUT 4 |
| 243 | #define POLLERR 8 |
| 244 | |
| 245 | // Class to handle select() 'data ready' |
| 246 | class DataReady |
| 247 | { |
| 248 | struct FD |
| 249 | { |
| 250 | int fd; |
| 251 | short events; |
| 252 | void (*cb)(int, void*); |
| 253 | void* arg; |
| 254 | }; |
| 255 | int nfds, fd_array_size; |
| 256 | FD *fds; |
| 257 | pthread_t tid; // select()'s thread id |
| 258 | |
| 259 | // Data that needs to be locked (all start with '_') |
| 260 | pthread_mutex_t _datalock; // data lock |
| 261 | fd_set _fdsets[3]; // r/w/x sets user wants to monitor |
| 262 | int _maxfd; // max fd count to monitor |
| 263 | int _cancelpipe[2]; // pipe used to help cancel thread |
| 264 | |
| 265 | public: |
| 266 | DataReady() |
| 267 | { |
| 268 | nfds = 0; |
| 269 | fd_array_size = 0; |
| 270 | fds = 0; |
| 271 | tid = 0; |
| 272 | |
| 273 | pthread_mutex_init(&_datalock, NULL); |
| 274 | FD_ZERO(&_fdsets[0]); FD_ZERO(&_fdsets[1]); FD_ZERO(&_fdsets[2]); |
| 275 | _cancelpipe[0] = _cancelpipe[1] = 0; |
| 276 | _maxfd = -1; |
| 277 | } |
| 278 | |
| 279 | ~DataReady() |
| 280 | { |
| 281 | CancelThread(DEBUGTEXT("DESTRUCTOR\n")); |
| 282 | if (fds) { free(fds); fds = 0; } |
| 283 | nfds = 0; |
| 284 | } |
| 285 | |
| 286 | // Locks |
| 287 | // The convention for locks: volatile vars start with '_', |
| 288 | // and must be locked before use. Locked code is prefixed |
| 289 | // with /*LOCK*/ to make painfully obvious esp. in debuggers. -erco |
| 290 | // |
| 291 | void DataLock() { pthread_mutex_lock(&_datalock); } |
| 292 | void DataUnlock() { pthread_mutex_unlock(&_datalock); } |
| 293 | |
| 294 | // Accessors |
| 295 | int IsThreadRunning() { return(tid ? 1 : 0); } |
| 296 | int GetNfds() { return(nfds); } |
| 297 | int GetCancelPipe(int ix) { return(_cancelpipe[ix]); } |
| 298 | fd_set GetFdset(int ix) { return(_fdsets[ix]); } |
| 299 | |
| 300 | // Methods |
| 301 | void AddFD(int n, int events, void (*cb)(int, void*), void *v); |
| 302 | void RemoveFD(int n, int events); |
| 303 | int CheckData(fd_set& r, fd_set& w, fd_set& x); |
| 304 | void HandleData(fd_set& r, fd_set& w, fd_set& x); |
| 305 | static void* DataReadyThread(void *self); |
| 306 | void StartThread(void); |
| 307 | void CancelThread(const char *reason); |
| 308 | }; |
| 309 | |
| 310 | static DataReady dataready; |
| 311 | |
| 312 | void DataReady::AddFD(int n, int events, void (*cb)(int, void*), void *v) |
| 313 | { |
| 314 | RemoveFD(n, events); |
| 315 | int i = nfds++; |
| 316 | if (i >= fd_array_size) |
| 317 | { |
| 318 | fl_open_display(); // necessary for NSApp to be defined and the event loop to work |
| 319 | FD *temp; |
| 320 | fd_array_size = 2*fd_array_size+1; |
| 321 | if (!fds) { temp = (FD*)malloc(fd_array_size*sizeof(FD)); } |
| 322 | else { temp = (FD*)realloc(fds, fd_array_size*sizeof(FD)); } |
| 323 | if (!temp) return; |
| 324 | fds = temp; |
| 325 | } |
| 326 | fds[i].cb = cb; |
| 327 | fds[i].arg = v; |
| 328 | fds[i].fd = n; |
| 329 | fds[i].events = events; |
| 330 | DataLock(); |
| 331 | /*LOCK*/ if (events & POLLIN) FD_SET(n, &_fdsets[0]); |
| 332 | /*LOCK*/ if (events & POLLOUT) FD_SET(n, &_fdsets[1]); |
| 333 | /*LOCK*/ if (events & POLLERR) FD_SET(n, &_fdsets[2]); |
| 334 | /*LOCK*/ if (n > _maxfd) _maxfd = n; |
| 335 | DataUnlock(); |
| 336 | } |
| 337 | |
| 338 | // Remove an FD from the array |
| 339 | void DataReady::RemoveFD(int n, int events) |
| 340 | { |
| 341 | int i,j; |
| 342 | _maxfd = -1; // recalculate maxfd on the fly |
| 343 | for (i=j=0; i<nfds; i++) { |
| 344 | if (fds[i].fd == n) { |
| 345 | int e = fds[i].events & ~events; |
| 346 | if (!e) continue; // if no events left, delete this fd |
| 347 | fds[i].events = e; |
| 348 | } |
| 349 | if (fds[i].fd > _maxfd) _maxfd = fds[i].fd; |
| 350 | // move it down in the array if necessary: |
| 351 | if (j<i) { |
| 352 | fds[j] = fds[i]; |
| 353 | } |
| 354 | j++; |
| 355 | } |
| 356 | nfds = j; |
| 357 | DataLock(); |
| 358 | /*LOCK*/ if (events & POLLIN) FD_CLR(n, &_fdsets[0]); |
| 359 | /*LOCK*/ if (events & POLLOUT) FD_CLR(n, &_fdsets[1]); |
| 360 | /*LOCK*/ if (events & POLLERR) FD_CLR(n, &_fdsets[2]); |
| 361 | DataUnlock(); |
| 362 | } |
| 363 | |
| 364 | // CHECK IF USER DATA READY, RETURNS r/w/x INDICATING WHICH IF ANY |
| 365 | int DataReady::CheckData(fd_set& r, fd_set& w, fd_set& x) |
| 366 | { |
| 367 | int ret; |
| 368 | DataLock(); |
| 369 | /*LOCK*/ timeval t = { 0, 1 }; // quick check |
| 370 | /*LOCK*/ r = _fdsets[0], w = _fdsets[1], x = _fdsets[2]; |
| 371 | /*LOCK*/ ret = ::select(_maxfd+1, &r, &w, &x, &t); |
| 372 | DataUnlock(); |
| 373 | if ( ret == -1 ) { |
| 374 | DEBUGPERRORMSG("CheckData(): select()"); |
| 375 | } |
| 376 | return(ret); |
| 377 | } |
| 378 | |
| 379 | // HANDLE DATA READY CALLBACKS |
| 380 | void DataReady::HandleData(fd_set& r, fd_set& w, fd_set& x) |
| 381 | { |
| 382 | for (int i=0; i<nfds; i++) { |
| 383 | int f = fds[i].fd; |
| 384 | short revents = 0; |
| 385 | if (FD_ISSET(f, &r)) revents |= POLLIN; |
| 386 | if (FD_ISSET(f, &w)) revents |= POLLOUT; |
| 387 | if (FD_ISSET(f, &x)) revents |= POLLERR; |
| 388 | if (fds[i].events & revents) { |
| 389 | DEBUGMSG("DOING CALLBACK: "); |
| 390 | fds[i].cb(f, fds[i].arg); |
| 391 | DEBUGMSG("DONE\n"); |
| 392 | } |
| 393 | } |
| 394 | } |
| 395 | |
| 396 | // DATA READY THREAD |
| 397 | // This thread watches for changes in user's file descriptors. |
| 398 | // Sends a 'data ready event' to the main thread if any change. |
| 399 | // |
| 400 | void* DataReady::DataReadyThread(void *o) |
| 401 | { |
| 402 | DataReady *self = (DataReady*)o; |
| 403 | NSAutoreleasePool *localPool; |
| 404 | localPool = [[NSAutoreleasePool alloc] init]; |
| 405 | while ( 1 ) { // loop until thread cancel or error |
| 406 | // Thread safe local copies of data before each select() |
| 407 | self->DataLock(); |
| 408 | /*LOCK*/ int maxfd = self->_maxfd; |
| 409 | /*LOCK*/ fd_set r = self->GetFdset(0); |
| 410 | /*LOCK*/ fd_set w = self->GetFdset(1); |
| 411 | /*LOCK*/ fd_set x = self->GetFdset(2); |
| 412 | /*LOCK*/ int cancelpipe = self->GetCancelPipe(0); |
| 413 | /*LOCK*/ if ( cancelpipe > maxfd ) maxfd = cancelpipe; |
| 414 | /*LOCK*/ FD_SET(cancelpipe, &r); // add cancelpipe to fd's to watch |
| 415 | /*LOCK*/ FD_SET(cancelpipe, &x); |
| 416 | self->DataUnlock(); |
| 417 | // timeval t = { 1000, 0 }; // 1000 seconds; |
| 418 | timeval t = { 2, 0 }; // HACK: 2 secs prevents 'hanging' problem |
| 419 | int ret = ::select(maxfd+1, &r, &w, &x, &t); |
| 420 | pthread_testcancel(); // OSX 10.0.4 and older: needed for parent to cancel |
| 421 | switch ( ret ) { |
| 422 | case 0: // NO DATA |
| 423 | continue; |
| 424 | case -1: // ERROR |
| 425 | { |
| 426 | DEBUGPERRORMSG("CHILD THREAD: select() failed"); |
| 427 | return(NULL); // error? exit thread |
| 428 | } |
| 429 | default: // DATA READY |
| 430 | { |
| 431 | if (FD_ISSET(cancelpipe, &r) || FD_ISSET(cancelpipe, &x)) // cancel? |
| 432 | { return(NULL); } // just exit |
| 433 | DEBUGMSG("CHILD THREAD: DATA IS READY\n"); |
| 434 | NSPoint pt={0,0}; |
| 435 | NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined location:pt |
| 436 | modifierFlags:0 |
| 437 | timestamp:0 |
| 438 | windowNumber:0 context:NULL |
| 439 | subtype:FLTKDataReadyEvent data1:0 data2:0]; |
| 440 | [NSApp postEvent:event atStart:NO]; |
| 441 | return(NULL); // done with thread |
| 442 | } |
| 443 | } |
| 444 | } |
| 445 | } |
| 446 | |
| 447 | // START 'DATA READY' THREAD RUNNING, CREATE INTER-THREAD PIPE |
| 448 | void DataReady::StartThread(void) |
| 449 | { |
| 450 | CancelThread(DEBUGTEXT("STARTING NEW THREAD\n")); |
| 451 | DataLock(); |
| 452 | /*LOCK*/ pipe(_cancelpipe); // pipe for sending cancel msg to thread |
| 453 | DataUnlock(); |
| 454 | DEBUGMSG("*** START THREAD\n"); |
| 455 | pthread_create(&tid, NULL, DataReadyThread, (void*)this); |
| 456 | } |
| 457 | |
| 458 | // CANCEL 'DATA READY' THREAD, CLOSE PIPE |
| 459 | void DataReady::CancelThread(const char *reason) |
| 460 | { |
| 461 | if ( tid ) { |
| 462 | DEBUGMSG("*** CANCEL THREAD: "); |
| 463 | DEBUGMSG(reason); |
| 464 | if ( pthread_cancel(tid) == 0 ) { // cancel first |
| 465 | DataLock(); |
| 466 | /*LOCK*/ write(_cancelpipe[1], "x", 1); // wake thread from select |
| 467 | DataUnlock(); |
| 468 | pthread_join(tid, NULL); // wait for thread to finish |
| 469 | } |
| 470 | tid = 0; |
| 471 | DEBUGMSG("(JOINED) OK\n"); |
| 472 | } |
| 473 | // Close pipe if open |
| 474 | DataLock(); |
| 475 | /*LOCK*/ if ( _cancelpipe[0] ) { close(_cancelpipe[0]); _cancelpipe[0] = 0; } |
| 476 | /*LOCK*/ if ( _cancelpipe[1] ) { close(_cancelpipe[1]); _cancelpipe[1] = 0; } |
| 477 | DataUnlock(); |
| 478 | } |
| 479 | |
| 480 | void Fl::add_fd( int n, int events, void (*cb)(int, void*), void *v ) |
| 481 | { |
| 482 | dataready.AddFD(n, events, cb, v); |
| 483 | } |
| 484 | |
| 485 | void Fl::add_fd(int fd, void (*cb)(int, void*), void* v) |
| 486 | { |
| 487 | dataready.AddFD(fd, POLLIN, cb, v); |
| 488 | } |
| 489 | |
| 490 | void Fl::remove_fd(int n, int events) |
| 491 | { |
| 492 | dataready.RemoveFD(n, events); |
| 493 | } |
| 494 | |
| 495 | void Fl::remove_fd(int n) |
| 496 | { |
| 497 | dataready.RemoveFD(n, -1); |
| 498 | } |
| 499 | |
| 500 | /* |
| 501 | * Check if there is actually a message pending! |
| 502 | */ |
| 503 | int fl_ready() |
| 504 | { |
| 505 | NSEvent *retval = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate dateWithTimeIntervalSinceNow:0] |
| 506 | inMode:NSDefaultRunLoopMode dequeue:NO]; |
| 507 | return retval != nil; |
| 508 | } |
| 509 | |
| 510 | |
| 511 | static void processFLTKEvent(void) { |
| 512 | fl_lock_function(); |
| 513 | dataready.CancelThread(DEBUGTEXT("DATA READY EVENT\n")); |
| 514 | |
| 515 | // CHILD THREAD TELLS US DATA READY |
| 516 | // Check to see what's ready, and invoke user's cb's |
| 517 | // |
| 518 | fd_set r,w,x; |
| 519 | switch(dataready.CheckData(r,w,x)) { |
| 520 | case 0: // NO DATA |
| 521 | break; |
| 522 | case -1: // ERROR |
| 523 | break; |
| 524 | default: // DATA READY |
| 525 | dataready.HandleData(r,w,x); |
| 526 | break; |
| 527 | } |
| 528 | fl_unlock_function(); |
| 529 | return; |
| 530 | } |
| 531 | |
| 532 | |
| 533 | /* |
| 534 | * break the current event loop |
| 535 | */ |
| 536 | static void breakMacEventLoop() |
| 537 | { |
| 538 | fl_lock_function(); |
| 539 | |
| 540 | NSPoint pt={0,0}; |
| 541 | NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined location:pt |
| 542 | modifierFlags:0 |
| 543 | timestamp:0 |
| 544 | windowNumber:0 context:NULL |
| 545 | subtype:FLTKTimerEvent data1:0 data2:0]; |
| 546 | [NSApp postEvent:event atStart:NO]; |
| 547 | fl_unlock_function(); |
| 548 | } |
| 549 | |
| 550 | // |
| 551 | // MacOS X timers |
| 552 | // |
| 553 | |
| 554 | struct MacTimeout { |
| 555 | Fl_Timeout_Handler callback; |
| 556 | void* data; |
| 557 | CFRunLoopTimerRef timer; |
| 558 | char pending; |
| 559 | }; |
| 560 | static MacTimeout* mac_timers; |
| 561 | static int mac_timer_alloc; |
| 562 | static int mac_timer_used; |
| 563 | |
| 564 | static void realloc_timers() |
| 565 | { |
| 566 | if (mac_timer_alloc == 0) { |
| 567 | mac_timer_alloc = 8; |
| 568 | fl_open_display(); // needed because the timer creates an event |
| 569 | } |
| 570 | mac_timer_alloc *= 2; |
| 571 | MacTimeout* new_timers = new MacTimeout[mac_timer_alloc]; |
| 572 | memset(new_timers, 0, sizeof(MacTimeout)*mac_timer_alloc); |
| 573 | memcpy(new_timers, mac_timers, sizeof(MacTimeout) * mac_timer_used); |
| 574 | MacTimeout* delete_me = mac_timers; |
| 575 | mac_timers = new_timers; |
| 576 | delete [] delete_me; |
| 577 | } |
| 578 | |
| 579 | static void delete_timer(MacTimeout& t) |
| 580 | { |
| 581 | if (t.timer) { |
| 582 | CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), |
| 583 | t.timer, |
| 584 | kCFRunLoopDefaultMode); |
| 585 | CFRelease(t.timer); |
| 586 | memset(&t, 0, sizeof(MacTimeout)); |
| 587 | } |
| 588 | } |
| 589 | |
| 590 | static void do_timer(CFRunLoopTimerRef timer, void* data) |
| 591 | { |
| 592 | for (int i = 0; i < mac_timer_used; ++i) { |
| 593 | MacTimeout& t = mac_timers[i]; |
| 594 | if (t.timer == timer && t.data == data) { |
| 595 | t.pending = 0; |
| 596 | (*t.callback)(data); |
| 597 | if (t.pending==0) |
| 598 | delete_timer(t); |
| 599 | break; |
| 600 | } |
| 601 | } |
| 602 | breakMacEventLoop(); |
| 603 | } |
| 604 | |
| 605 | @interface FLWindow : NSWindow { |
| 606 | Fl_Window *w; |
| 607 | BOOL containsGLsubwindow; |
| 608 | } |
| 609 | - (FLWindow*)initWithFl_W:(Fl_Window *)flw |
| 610 | contentRect:(NSRect)rect |
| 611 | styleMask:(NSUInteger)windowStyle; |
| 612 | - (Fl_Window *)getFl_Window; |
| 613 | - (BOOL)windowShouldClose:(FLWindow *)w; |
| 614 | - (BOOL)containsGLsubwindow; |
| 615 | - (void)setContainsGLsubwindow:(BOOL)contains; |
| 616 | @end |
| 617 | |
| 618 | @implementation FLWindow |
| 619 | - (FLWindow*)initWithFl_W:(Fl_Window *)flw |
| 620 | contentRect:(NSRect)rect |
| 621 | styleMask:(NSUInteger)windowStyle |
| 622 | { |
| 623 | self = [super initWithContentRect:rect styleMask:windowStyle backing:NSBackingStoreBuffered defer:NO]; |
| 624 | if (self) { |
| 625 | w = flw; |
| 626 | containsGLsubwindow = NO; |
| 627 | } |
| 628 | return self; |
| 629 | } |
| 630 | - (Fl_Window *)getFl_Window; |
| 631 | { |
| 632 | return w; |
| 633 | } |
| 634 | - (BOOL)windowShouldClose:(FLWindow *)fl |
| 635 | { |
| 636 | fl_lock_function(); |
| 637 | Fl::handle( FL_CLOSE, [fl getFl_Window] ); // this might or might not close the window |
| 638 | if (!Fl_X::first) return YES; |
| 639 | Fl_Window *l = Fl::first_window(); |
| 640 | while( l != NULL && l != [fl getFl_Window]) l = Fl::next_window(l); |
| 641 | fl_unlock_function(); |
| 642 | return (l == NULL ? YES : NO); |
| 643 | } |
| 644 | - (BOOL)containsGLsubwindow |
| 645 | { |
| 646 | return containsGLsubwindow; |
| 647 | } |
| 648 | - (void)setContainsGLsubwindow:(BOOL)contains |
| 649 | { |
| 650 | containsGLsubwindow = contains; |
| 651 | } |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 652 | - (BOOL)canBecomeKeyWindow |
| 653 | { |
| 654 | return YES; |
| 655 | } |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 656 | @end |
| 657 | |
| 658 | @interface FLApplication : NSObject |
| 659 | { |
| 660 | } |
| 661 | + (void)sendEvent:(NSEvent *)theEvent; |
| 662 | @end |
| 663 | |
| 664 | /* |
| 665 | * This function is the central event handler. |
| 666 | * It reads events from the event queue using the given maximum time |
| 667 | * Funny enough, it returns the same time that it got as the argument. |
| 668 | */ |
| 669 | static double do_queued_events( double time = 0.0 ) |
| 670 | { |
| 671 | got_events = 0; |
| 672 | |
| 673 | // Check for re-entrant condition |
| 674 | if ( dataready.IsThreadRunning() ) { |
| 675 | dataready.CancelThread(DEBUGTEXT("AVOID REENTRY\n")); |
| 676 | } |
| 677 | |
| 678 | // Start thread to watch for data ready |
| 679 | if ( dataready.GetNfds() ) { |
| 680 | dataready.StartThread(); |
| 681 | } |
| 682 | |
| 683 | fl_unlock_function(); |
| 684 | NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask |
| 685 | untilDate:[NSDate dateWithTimeIntervalSinceNow:time] |
| 686 | inMode:NSDefaultRunLoopMode dequeue:YES]; |
| 687 | if (event != nil) { |
| 688 | got_events = 1; |
| 689 | [FLApplication sendEvent:event]; // will then call [NSApplication sendevent:] |
| 690 | } |
| 691 | fl_lock_function(); |
| 692 | |
| 693 | #if CONSOLIDATE_MOTION |
| 694 | if (send_motion && send_motion == fl_xmousewin) { |
| 695 | send_motion = 0; |
| 696 | Fl::handle(FL_MOVE, fl_xmousewin); |
| 697 | } |
| 698 | #endif |
| 699 | |
| 700 | return time; |
| 701 | } |
| 702 | |
| 703 | /* |
| 704 | * This public function handles all events. It wait a maximum of |
| 705 | * 'time' seconds for an event. This version returns 1 if events |
| 706 | * other than the timeout timer were processed. |
| 707 | * |
| 708 | * \todo there is no socket handling in this code whatsoever |
| 709 | */ |
| 710 | int fl_wait( double time ) |
| 711 | { |
| 712 | do_queued_events( time ); |
| 713 | return (got_events); |
| 714 | } |
| 715 | |
| 716 | double fl_mac_flush_and_wait(double time_to_wait, char in_idle) { |
| 717 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; |
| 718 | Fl::flush(); |
| 719 | if (Fl::idle && !in_idle) // 'idle' may have been set within flush() |
| 720 | time_to_wait = 0.0; |
| 721 | double retval = fl_wait(time_to_wait); |
| 722 | [pool release]; |
| 723 | return retval; |
| 724 | } |
| 725 | |
| 726 | // updates Fl::e_x, Fl::e_y, Fl::e_x_root, and Fl::e_y_root |
| 727 | static void update_e_xy_and_e_xy_root(NSWindow *nsw) |
| 728 | { |
| 729 | NSPoint pt; |
| 730 | pt = [nsw mouseLocationOutsideOfEventStream]; |
| 731 | Fl::e_x = int(pt.x); |
| 732 | Fl::e_y = int([[nsw contentView] frame].size.height - pt.y); |
| 733 | pt = [NSEvent mouseLocation]; |
| 734 | Fl::e_x_root = int(pt.x); |
| 735 | Fl::e_y_root = int([[nsw screen] frame].size.height - pt.y); |
| 736 | } |
| 737 | |
| 738 | /* |
| 739 | * Cocoa Mousewheel handler |
| 740 | */ |
| 741 | static void cocoaMouseWheelHandler(NSEvent *theEvent) |
| 742 | { |
| 743 | // Handle the new "MightyMouse" mouse wheel events. Please, someone explain |
| 744 | // to me why Apple changed the API on this even though the current API |
| 745 | // supports two wheels just fine. Matthias, |
| 746 | fl_lock_function(); |
| 747 | |
| 748 | Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window]; |
| 749 | if ( !window->shown() ) { |
| 750 | fl_unlock_function(); |
| 751 | return; |
| 752 | } |
| 753 | Fl::first_window(window); |
| 754 | |
| 755 | // Under OSX, single mousewheel increments are 0.1, |
| 756 | // so make sure they show up as at least 1.. |
| 757 | // |
| 758 | float dx = [theEvent deltaX]; if ( fabs(dx) < 1.0 ) dx = (dx > 0) ? 1.0 : -1.0; |
| 759 | float dy = [theEvent deltaY]; if ( fabs(dy) < 1.0 ) dy = (dy > 0) ? 1.0 : -1.0; |
| 760 | if ([theEvent deltaX] != 0) { |
| 761 | Fl::e_dx = (int)-dx; |
| 762 | Fl::e_dy = 0; |
| 763 | if ( Fl::e_dx) Fl::handle( FL_MOUSEWHEEL, window ); |
| 764 | } else if ([theEvent deltaY] != 0) { |
| 765 | Fl::e_dx = 0; |
| 766 | Fl::e_dy = (int)-dy; |
| 767 | if ( Fl::e_dy) Fl::handle( FL_MOUSEWHEEL, window ); |
| 768 | } else { |
| 769 | fl_unlock_function(); |
| 770 | return; |
| 771 | } |
| 772 | |
| 773 | fl_unlock_function(); |
| 774 | |
| 775 | // return noErr; |
| 776 | } |
| 777 | |
| 778 | /* |
| 779 | * Cocoa Mouse Button Handler |
| 780 | */ |
| 781 | static void cocoaMouseHandler(NSEvent *theEvent) |
| 782 | { |
| 783 | static int keysym[] = { 0, FL_Button+1, FL_Button+3, FL_Button+2 }; |
| 784 | static int px, py; |
| 785 | static char suppressed = 0; |
| 786 | |
| 787 | fl_lock_function(); |
| 788 | |
| 789 | Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window]; |
| 790 | if ( !window->shown() ) { |
| 791 | fl_unlock_function(); |
| 792 | return; |
| 793 | } |
| 794 | Fl_Window *first = Fl::first_window(); |
| 795 | if (first != window && !(first->modal() || first->non_modal())) Fl::first_window(window); |
| 796 | NSPoint pos = [theEvent locationInWindow]; |
| 797 | pos.y = window->h() - pos.y; |
| 798 | NSInteger btn = [theEvent buttonNumber] + 1; |
| 799 | NSUInteger mods = [theEvent modifierFlags]; |
| 800 | int sendEvent = 0; |
| 801 | |
| 802 | NSEventType etype = [theEvent type]; |
| 803 | if (etype == NSLeftMouseDown || etype == NSRightMouseDown || etype == NSOtherMouseDown) { |
| 804 | if (btn == 1) Fl::e_state |= FL_BUTTON1; |
| 805 | else if (btn == 3) Fl::e_state |= FL_BUTTON2; |
| 806 | else if (btn == 2) Fl::e_state |= FL_BUTTON3; |
| 807 | } |
| 808 | else if (etype == NSLeftMouseUp || etype == NSRightMouseUp || etype == NSOtherMouseUp) { |
| 809 | if (btn == 1) Fl::e_state &= ~FL_BUTTON1; |
| 810 | else if (btn == 3) Fl::e_state &= ~FL_BUTTON2; |
| 811 | else if (btn == 2) Fl::e_state &= ~FL_BUTTON3; |
| 812 | } |
| 813 | |
| 814 | switch ( etype ) { |
| 815 | case NSLeftMouseDown: |
| 816 | case NSRightMouseDown: |
| 817 | case NSOtherMouseDown: |
| 818 | suppressed = 0; |
| 819 | sendEvent = FL_PUSH; |
| 820 | Fl::e_is_click = 1; |
| 821 | px = (int)pos.x; py = (int)pos.y; |
| 822 | if ([theEvent clickCount] > 1) |
| 823 | Fl::e_clicks++; |
| 824 | else |
| 825 | Fl::e_clicks = 0; |
| 826 | // fall through |
| 827 | case NSLeftMouseUp: |
| 828 | case NSRightMouseUp: |
| 829 | case NSOtherMouseUp: |
| 830 | if (suppressed) { |
| 831 | suppressed = 0; |
| 832 | break; |
| 833 | } |
| 834 | if ( !window ) break; |
| 835 | if ( !sendEvent ) { |
| 836 | sendEvent = FL_RELEASE; |
| 837 | } |
| 838 | Fl::e_keysym = keysym[ btn ]; |
| 839 | // fall through |
| 840 | case NSMouseMoved: |
| 841 | suppressed = 0; |
| 842 | if ( !sendEvent ) { |
| 843 | sendEvent = FL_MOVE; |
| 844 | } |
| 845 | // fall through |
| 846 | case NSLeftMouseDragged: |
| 847 | case NSRightMouseDragged: |
| 848 | case NSOtherMouseDragged: { |
| 849 | if (suppressed) break; |
| 850 | if ( !sendEvent ) { |
| 851 | sendEvent = FL_MOVE; // Fl::handle will convert into FL_DRAG |
| 852 | if (fabs(pos.x-px)>5 || fabs(pos.y-py)>5) |
| 853 | Fl::e_is_click = 0; |
| 854 | } |
| 855 | mods_to_e_state( mods ); |
| 856 | update_e_xy_and_e_xy_root([theEvent window]); |
| 857 | Fl::handle( sendEvent, window ); |
| 858 | } |
| 859 | break; |
| 860 | default: |
| 861 | break; |
| 862 | } |
| 863 | |
| 864 | fl_unlock_function(); |
| 865 | |
| 866 | return; |
| 867 | } |
| 868 | |
| 869 | @interface FLTextView : NSTextView |
| 870 | // this subclass is needed under OS X <= 10.5 but not under >= 10.6 where the base class is enough |
| 871 | { |
| 872 | } |
| 873 | @end |
| 874 | @implementation FLTextView |
| 875 | - (void)insertText:(id)aString |
| 876 | { |
| 877 | [[[NSApp keyWindow] contentView] insertText:aString]; |
| 878 | } |
| 879 | - (void)doCommandBySelector:(SEL)aSelector |
| 880 | { |
| 881 | [[[NSApp keyWindow] contentView] doCommandBySelector:aSelector]; |
| 882 | } |
| 883 | @end |
| 884 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 885 | static const char* cocoaDead2FLTK(const char *in) |
| 886 | { |
| 887 | if (strcmp(in, "\140") == 0) // GRAVE ACCENT |
| 888 | return "\314\200"; // COMBINING GRAVE ACCENT |
| 889 | if (strcmp(in, "\302\264") == 0) // ACUTE ACCENT |
| 890 | return "\314\201"; // COMBINING ACUTE ACCENT |
| 891 | if (strcmp(in, "\136") == 0) // CIRCUMFLEX ACCENT |
| 892 | return "\314\202"; // COMBINING CIRCUMFLEX ACCENT |
| 893 | if (strcmp(in, "\176") == 0) // TILDE |
| 894 | return "\314\203"; // COMBINING TILDE |
| 895 | if (strcmp(in, "\302\250") == 0) // DIAERESIS |
| 896 | return "\314\210"; // COMBINING DIAERESIS |
| 897 | // FIXME: OS X dead key behaviour isn't documented and I don't have |
| 898 | // any more keyboards to test with... |
| 899 | |
| 900 | // hope that OS X gave us something proper to begin with |
| 901 | return in; |
| 902 | } |
| 903 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 904 | /* |
| 905 | Handle cocoa keyboard events |
| 906 | Events during a character composition sequence: |
| 907 | - keydown with deadkey -> [[theEvent characters] length] is 0 |
| 908 | - keyup -> [theEvent characters] contains the deadkey |
| 909 | - keydown with next key -> [theEvent characters] contains the composed character |
| 910 | - keyup -> [theEvent characters] contains the standard character |
| 911 | */ |
| 912 | static void cocoaKeyboardHandler(NSEvent *theEvent) |
| 913 | { |
| 914 | NSUInteger mods; |
| 915 | |
| 916 | // get the modifiers |
| 917 | mods = [theEvent modifierFlags]; |
| 918 | // get the key code |
| 919 | UInt32 keyCode = 0, maskedKeyCode = 0; |
| 920 | unsigned short sym = 0; |
| 921 | keyCode = [theEvent keyCode]; |
| 922 | NSString *s = [theEvent characters]; |
| 923 | if ( (mods & NSShiftKeyMask) && (mods & NSCommandKeyMask) ) { |
| 924 | s = [s uppercaseString]; // US keyboards return lowercase letter in s if cmd-shift-key is hit |
| 925 | } |
| 926 | // extended keyboards can also send sequences on key-up to generate Kanji etc. codes. |
| 927 | // Some observed prefixes are 0x81 to 0x83, followed by an 8 bit keycode. |
| 928 | // In this mode, there seem to be no key-down codes |
| 929 | // printf("%08x %08x %08x\n", keyCode, mods, key); |
| 930 | maskedKeyCode = keyCode & 0x7f; |
| 931 | |
| 932 | if ([theEvent type] == NSKeyUp) { |
| 933 | Fl::e_state &= 0xbfffffff; // clear the deadkey flag |
| 934 | } |
| 935 | |
| 936 | mods_to_e_state( mods ); // process modifier keys |
| 937 | sym = macKeyLookUp[maskedKeyCode]; |
| 938 | if (sym < 0xff00) { // a "simple" key |
| 939 | // find the result of this key without modifier |
| 940 | NSString *sim = [theEvent charactersIgnoringModifiers]; |
| 941 | UniChar one; |
| 942 | CFStringGetCharacters((CFStringRef)sim, CFRangeMake(0, 1), &one); |
| 943 | // charactersIgnoringModifiers doesn't ignore shift, remove it when it's on |
| 944 | if(one >= 'A' && one <= 'Z') one += 32; |
| 945 | if (one > 0 && one <= 0x7f && (sym<'0' || sym>'9') ) sym = one; |
| 946 | } |
| 947 | Fl::e_keysym = Fl::e_original_keysym = sym; |
| 948 | |
| 949 | //NSLog(@"cocoaKeyboardHandler: keycode=%08x keysym=%08x mods=%08x symbol=%@ (%@)", |
| 950 | // keyCode, sym, mods, [theEvent characters], [theEvent charactersIgnoringModifiers]); |
| 951 | |
| 952 | // If there is text associated with this key, it will be filled in later. |
| 953 | Fl::e_length = 0; |
| 954 | Fl::e_text = (char*)""; |
| 955 | } |
| 956 | |
| 957 | |
| 958 | /* |
| 959 | * Open callback function to call... |
| 960 | */ |
| 961 | |
| 962 | static void (*open_cb)(const char *) = 0; |
| 963 | |
| 964 | |
| 965 | /* |
| 966 | * Install an open documents event handler... |
| 967 | */ |
| 968 | @interface FLAppleEventHandler : NSObject |
| 969 | { |
| 970 | } |
| 971 | - (void)handleAppleEvent:(NSAppleEventDescriptor *)event withReplyEvent: (NSAppleEventDescriptor *)replyEvent; |
| 972 | @end |
| 973 | @implementation FLAppleEventHandler |
| 974 | - (void)handleAppleEvent:(NSAppleEventDescriptor *)event withReplyEvent: (NSAppleEventDescriptor *)replyEvent |
| 975 | { |
| 976 | NSAppleEventDescriptor *single = [event descriptorAtIndex:1]; |
| 977 | const AEDesc *document = [single aeDesc]; |
| 978 | long i, n; |
| 979 | FSRef fileRef; |
| 980 | AEKeyword keyWd; |
| 981 | DescType typeCd; |
| 982 | Size actSz; |
| 983 | char filename[1024]; |
| 984 | // Lock access to FLTK in this thread... |
| 985 | fl_lock_function(); |
| 986 | |
| 987 | // Open the documents via the callback... |
| 988 | if (AECountItems(document, &n) == noErr) { |
| 989 | for (i = 1; i <= n; i ++) { |
| 990 | AEGetNthPtr(document, i, typeFSRef, &keyWd, &typeCd, |
| 991 | (Ptr)&fileRef, sizeof(fileRef), |
| 992 | (actSz = sizeof(fileRef), &actSz)); |
| 993 | FSRefMakePath( &fileRef, (UInt8*)filename, sizeof(filename) ); |
| 994 | |
| 995 | (*open_cb)(filename); |
| 996 | } |
| 997 | } |
| 998 | // Unlock access to FLTK for all threads... |
| 999 | fl_unlock_function(); |
| 1000 | } |
| 1001 | @end |
| 1002 | |
| 1003 | void fl_open_callback(void (*cb)(const char *)) { |
| 1004 | static NSAppleEventManager *aeventmgr = nil; |
| 1005 | static FLAppleEventHandler *handler; |
| 1006 | fl_open_display(); |
| 1007 | if (!aeventmgr) { |
| 1008 | aeventmgr = [NSAppleEventManager sharedAppleEventManager]; |
| 1009 | handler = [[FLAppleEventHandler alloc] init]; |
| 1010 | } |
| 1011 | |
| 1012 | open_cb = cb; |
| 1013 | if (cb) { |
| 1014 | [aeventmgr setEventHandler:handler andSelector:@selector(handleAppleEvent:withReplyEvent:) |
| 1015 | forEventClass:kCoreEventClass andEventID:kAEOpenDocuments]; |
| 1016 | } else { |
| 1017 | [aeventmgr removeEventHandlerForEventClass:kCoreEventClass andEventID:kAEOpenDocuments]; |
| 1018 | } |
| 1019 | } |
| 1020 | |
| 1021 | |
| 1022 | /* |
| 1023 | * initialize the Mac toolboxes, dock status, and set the default menubar |
| 1024 | */ |
| 1025 | |
| 1026 | extern "C" { |
| 1027 | extern OSErr CPSEnableForegroundOperation(ProcessSerialNumber *psn, UInt32 _arg2, |
| 1028 | UInt32 _arg3, UInt32 _arg4, UInt32 _arg5); |
| 1029 | } |
| 1030 | |
| 1031 | |
| 1032 | @interface FLDelegate : NSObject |
| 1033 | #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 |
| 1034 | <NSWindowDelegate, NSApplicationDelegate> |
| 1035 | #endif |
| 1036 | { |
| 1037 | } |
| 1038 | - (void)windowDidMove:(NSNotification *)notif; |
| 1039 | - (void)windowDidResize:(NSNotification *)notif; |
| 1040 | - (void)windowDidResignKey:(NSNotification *)notif; |
| 1041 | - (void)windowDidBecomeKey:(NSNotification *)notif; |
| 1042 | - (void)windowDidBecomeMain:(NSNotification *)notif; |
| 1043 | - (void)windowDidDeminiaturize:(NSNotification *)notif; |
| 1044 | - (void)windowDidMiniaturize:(NSNotification *)notif; |
| 1045 | - (void)windowWillClose:(NSNotification *)notif; |
| 1046 | - (void)anywindowwillclosenotif:(NSNotification *)notif; |
| 1047 | - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender; |
| 1048 | - (void)applicationDidBecomeActive:(NSNotification *)notify; |
| 1049 | - (void)applicationWillResignActive:(NSNotification *)notify; |
| 1050 | - (void)applicationWillHide:(NSNotification *)notify; |
| 1051 | - (void)applicationWillUnhide:(NSNotification *)notify; |
| 1052 | - (id)windowWillReturnFieldEditor:(NSWindow *)sender toObject:(id)client; |
| 1053 | @end |
| 1054 | @implementation FLDelegate |
| 1055 | - (void)windowDidMove:(NSNotification *)notif |
| 1056 | { |
| 1057 | fl_lock_function(); |
| 1058 | FLWindow *nsw = (FLWindow*)[notif object]; |
| 1059 | Fl_Window *window = [nsw getFl_Window]; |
| 1060 | NSPoint pt, pt2; |
| 1061 | pt.x = 0; |
| 1062 | pt.y = [[nsw contentView] frame].size.height; |
| 1063 | pt2 = [nsw convertBaseToScreen:pt]; |
| 1064 | update_e_xy_and_e_xy_root(nsw); |
Pierre Ossman | f5e59af | 2011-11-25 09:18:28 +0000 | [diff] [blame^] | 1065 | resize_from_system = window; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1066 | window->position((int)pt2.x, (int)([[nsw screen] frame].size.height - pt2.y)); |
| 1067 | if ([nsw containsGLsubwindow] ) { |
| 1068 | [nsw display];// redraw window after moving if it contains OpenGL subwindows |
| 1069 | } |
| 1070 | fl_unlock_function(); |
| 1071 | } |
| 1072 | - (void)windowDidResize:(NSNotification *)notif |
| 1073 | { |
| 1074 | fl_lock_function(); |
| 1075 | FLWindow *nsw = (FLWindow*)[notif object]; |
| 1076 | Fl_Window *window = [nsw getFl_Window]; |
| 1077 | NSRect r = [[nsw contentView] frame]; |
| 1078 | NSPoint pt, pt2; |
| 1079 | pt.x = 0; |
| 1080 | pt.y = [[nsw contentView] frame].size.height; |
| 1081 | pt2 = [nsw convertBaseToScreen:pt]; |
| 1082 | resize_from_system = window; |
| 1083 | update_e_xy_and_e_xy_root(nsw); |
| 1084 | window->resize((int)pt2.x, |
| 1085 | (int)([[nsw screen] frame].size.height - pt2.y), |
| 1086 | (int)r.size.width, |
| 1087 | (int)r.size.height); |
| 1088 | fl_unlock_function(); |
| 1089 | } |
| 1090 | - (void)windowDidResignKey:(NSNotification *)notif |
| 1091 | { |
| 1092 | fl_lock_function(); |
| 1093 | FLWindow *nsw = (FLWindow*)[notif object]; |
| 1094 | Fl_Window *window = [nsw getFl_Window]; |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1095 | /* Fullscreen windows obscure all other windows so we need to return |
| 1096 | to a "normal" level when the user switches to another window */ |
| 1097 | if (window->fullscreen_active()) |
| 1098 | [nsw setLevel:NSNormalWindowLevel]; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1099 | Fl::handle( FL_UNFOCUS, window); |
| 1100 | fl_unlock_function(); |
| 1101 | } |
| 1102 | - (void)windowDidBecomeKey:(NSNotification *)notif |
| 1103 | { |
| 1104 | fl_lock_function(); |
| 1105 | FLWindow *nsw = (FLWindow*)[notif object]; |
| 1106 | Fl_Window *w = [nsw getFl_Window]; |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1107 | /* Restore previous fullscreen level */ |
| 1108 | if (w->fullscreen_active()) |
| 1109 | [nsw setLevel:NSStatusWindowLevel]; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1110 | if ( w->border() || (!w->modal() && !w->tooltip_window()) ) Fl::handle( FL_FOCUS, w); |
| 1111 | fl_unlock_function(); |
| 1112 | } |
| 1113 | - (void)windowDidBecomeMain:(NSNotification *)notif |
| 1114 | { |
| 1115 | fl_lock_function(); |
| 1116 | FLWindow *nsw = (FLWindow*)[notif object]; |
| 1117 | Fl_Window *window = [nsw getFl_Window]; |
| 1118 | Fl::first_window(window); |
| 1119 | update_e_xy_and_e_xy_root(nsw); |
| 1120 | fl_unlock_function(); |
| 1121 | } |
| 1122 | - (void)windowDidDeminiaturize:(NSNotification *)notif |
| 1123 | { |
| 1124 | fl_lock_function(); |
| 1125 | FLWindow *nsw = (FLWindow*)[notif object]; |
| 1126 | Fl_Window *window = [nsw getFl_Window]; |
| 1127 | Fl::handle(FL_SHOW, window); |
| 1128 | update_e_xy_and_e_xy_root(nsw); |
| 1129 | fl_unlock_function(); |
| 1130 | } |
| 1131 | - (void)windowDidMiniaturize:(NSNotification *)notif |
| 1132 | { |
| 1133 | fl_lock_function(); |
| 1134 | FLWindow *nsw = (FLWindow*)[notif object]; |
| 1135 | Fl_Window *window = [nsw getFl_Window]; |
| 1136 | Fl::handle(FL_HIDE, window); |
| 1137 | fl_unlock_function(); |
| 1138 | } |
| 1139 | - (void)windowWillClose:(NSNotification *)notif |
| 1140 | { |
| 1141 | fl_lock_function(); |
| 1142 | Fl_Window *w = Fl::first_window(); |
| 1143 | if (w) { |
| 1144 | NSWindow *cw = (NSWindow*)Fl_X::i(w)->xid; |
| 1145 | if ( ![cw isMiniaturized] && ([cw styleMask] & NSTitledWindowMask) ) { |
| 1146 | if (![cw isKeyWindow]) { // always make Fl::first_window() the key widow |
| 1147 | [cw makeKeyAndOrderFront:nil]; |
| 1148 | } |
| 1149 | if (![cw isMainWindow]) { // always make Fl::first_window() the main widow |
| 1150 | [cw makeMainWindow]; |
| 1151 | } |
| 1152 | } |
| 1153 | } |
| 1154 | fl_unlock_function(); |
| 1155 | } |
| 1156 | - (void)anywindowwillclosenotif:(NSNotification *)notif |
| 1157 | { |
| 1158 | // necessary so that after closing a non-FLTK window (e.g., Fl_Native_File_Chooser) |
| 1159 | // the front window turns key again |
| 1160 | NSWindow *closing = (NSWindow*)[notif object]; |
| 1161 | if ([closing isMemberOfClass:[FLWindow class]]) return; |
| 1162 | NSWindow *nsk = [NSApp keyWindow]; |
| 1163 | NSWindow *nsm = [NSApp mainWindow]; |
| 1164 | if ([nsm isMemberOfClass:[FLWindow class]] && nsk == nil) { |
| 1165 | [nsm makeKeyAndOrderFront:nil]; |
| 1166 | } |
| 1167 | } |
| 1168 | - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender |
| 1169 | { |
| 1170 | fl_lock_function(); |
| 1171 | NSApplicationTerminateReply reply = NSTerminateNow; |
| 1172 | while ( Fl_X::first ) { |
| 1173 | Fl_X *x = Fl_X::first; |
| 1174 | Fl::handle( FL_CLOSE, x->w ); |
| 1175 | if ( Fl_X::first == x ) { |
| 1176 | reply = NSTerminateCancel; // FLTK has not closed all windows, so we return to the main program now |
| 1177 | break; |
| 1178 | } |
| 1179 | } |
| 1180 | fl_unlock_function(); |
| 1181 | return reply; |
| 1182 | } |
| 1183 | /** |
| 1184 | * Cocoa organizes the Z depth of windows on a global priority. FLTK however |
| 1185 | * expects the window manager to organize Z level by application. The trickery |
| 1186 | * below will change Z order during activation and deactivation. |
| 1187 | */ |
| 1188 | - (void)applicationDidBecomeActive:(NSNotification *)notify |
| 1189 | { |
| 1190 | fl_lock_function(); |
| 1191 | Fl_X *x; |
| 1192 | FLWindow *top = 0, *topModal = 0, *topNonModal = 0; |
| 1193 | for (x = Fl_X::first;x;x = x->next) { |
| 1194 | FLWindow *cw = (FLWindow*)x->xid; |
| 1195 | Fl_Window *win = x->w; |
| 1196 | if (win && cw) { |
| 1197 | if (win->modal()) { |
| 1198 | [cw setLevel:NSModalPanelWindowLevel]; |
| 1199 | if (topModal) |
| 1200 | [cw orderWindow:NSWindowBelow relativeTo:[topModal windowNumber]]; |
| 1201 | else |
| 1202 | topModal = cw; |
| 1203 | } else if (win->non_modal()) { |
| 1204 | [cw setLevel:NSFloatingWindowLevel]; |
| 1205 | if (topNonModal) |
| 1206 | [cw orderWindow:NSWindowBelow relativeTo:[topNonModal windowNumber]]; |
| 1207 | else |
| 1208 | topNonModal = cw; |
| 1209 | } else { |
| 1210 | if (top) |
| 1211 | ; |
| 1212 | else |
| 1213 | top = cw; |
| 1214 | } |
| 1215 | } |
| 1216 | } |
| 1217 | fl_unlock_function(); |
| 1218 | } |
| 1219 | - (void)applicationWillResignActive:(NSNotification *)notify |
| 1220 | { |
| 1221 | fl_lock_function(); |
| 1222 | Fl_X *x; |
| 1223 | FLWindow *top = 0; |
| 1224 | // sort in all regular windows |
| 1225 | for (x = Fl_X::first;x;x = x->next) { |
| 1226 | FLWindow *cw = (FLWindow*)x->xid; |
| 1227 | Fl_Window *win = x->w; |
| 1228 | if (win && cw) { |
| 1229 | if (win->modal()) { |
| 1230 | } else if (win->non_modal()) { |
| 1231 | } else { |
| 1232 | if (!top) top = cw; |
| 1233 | } |
| 1234 | } |
| 1235 | } |
| 1236 | // now sort in all modals |
| 1237 | for (x = Fl_X::first;x;x = x->next) { |
| 1238 | FLWindow *cw = (FLWindow*)x->xid; |
| 1239 | Fl_Window *win = x->w; |
| 1240 | if (win && cw) { |
| 1241 | if (win->modal()) { |
| 1242 | [cw setLevel:NSNormalWindowLevel]; |
| 1243 | if (top) [cw orderWindow:NSWindowAbove relativeTo:[top windowNumber]]; |
| 1244 | } |
| 1245 | } |
| 1246 | } |
| 1247 | // finally all non-modals |
| 1248 | for (x = Fl_X::first;x;x = x->next) { |
| 1249 | FLWindow *cw = (FLWindow*)x->xid; |
| 1250 | Fl_Window *win = x->w; |
| 1251 | if (win && cw) { |
| 1252 | if (win->non_modal()) { |
| 1253 | [cw setLevel:NSNormalWindowLevel]; |
| 1254 | if (top) [cw orderWindow:NSWindowAbove relativeTo:[top windowNumber]]; |
| 1255 | } |
| 1256 | } |
| 1257 | } |
| 1258 | fl_unlock_function(); |
| 1259 | } |
| 1260 | - (void)applicationWillHide:(NSNotification *)notify |
| 1261 | { |
| 1262 | fl_lock_function(); |
| 1263 | Fl_X *x; |
| 1264 | for (x = Fl_X::first;x;x = x->next) { |
| 1265 | Fl_Window *window = x->w; |
| 1266 | if ( !window->parent() ) Fl::handle( FL_HIDE, window); |
| 1267 | } |
| 1268 | fl_unlock_function(); |
| 1269 | } |
| 1270 | - (void)applicationWillUnhide:(NSNotification *)notify |
| 1271 | { |
| 1272 | fl_lock_function(); |
| 1273 | Fl_X *x; |
| 1274 | for (x = Fl_X::first;x;x = x->next) { |
| 1275 | Fl_Window *w = x->w; |
| 1276 | if ( !w->parent() ) { |
| 1277 | if ( w->border() || (!w->modal() && !w->tooltip_window()) ) Fl::handle( FL_FOCUS, w); |
| 1278 | Fl::handle( FL_SHOW, w); |
| 1279 | } |
| 1280 | } |
| 1281 | fl_unlock_function(); |
| 1282 | } |
| 1283 | - (id)windowWillReturnFieldEditor:(NSWindow *)sender toObject:(id)client |
| 1284 | { |
| 1285 | if (fl_mac_os_version < 100600) { |
| 1286 | static FLTextView *view = nil; |
| 1287 | if (!view) { |
| 1288 | NSRect rect={{0,0},{20,20}}; |
| 1289 | view = [[FLTextView alloc] initWithFrame:rect]; |
| 1290 | } |
| 1291 | return view; |
| 1292 | } |
| 1293 | return nil; |
| 1294 | } |
| 1295 | @end |
| 1296 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1297 | static void clipboard_check(void); |
| 1298 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1299 | @implementation FLApplication |
| 1300 | + (void)sendEvent:(NSEvent *)theEvent |
| 1301 | { |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1302 | // update clipboard status |
| 1303 | clipboard_check(); |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1304 | NSEventType type = [theEvent type]; |
| 1305 | if (type == NSLeftMouseDown) { |
| 1306 | fl_lock_function(); |
| 1307 | Fl_Window *grab = Fl::grab(); |
| 1308 | if (grab) { |
| 1309 | FLWindow *win = (FLWindow *)[theEvent window]; |
| 1310 | if ( [win isKindOfClass:[FLWindow class]] && grab != [win getFl_Window]) { |
| 1311 | // a click event out of a menu window, so we should close this menu |
| 1312 | // done here to catch also clicks on window title bar/resize box |
| 1313 | cocoaMouseHandler(theEvent); |
| 1314 | } |
| 1315 | } |
| 1316 | fl_unlock_function(); |
| 1317 | } else if (type == NSApplicationDefined) { |
| 1318 | if ([theEvent subtype] == FLTKDataReadyEvent) { |
| 1319 | processFLTKEvent(); |
| 1320 | } |
| 1321 | return; |
| 1322 | } else if (type == NSKeyUp) { |
| 1323 | // The default sendEvent turns key downs into performKeyEquivalent when |
| 1324 | // modifiers are down, but swallows the key up if the modifiers include |
| 1325 | // command. This one makes all modifiers consistent by always sending key ups. |
| 1326 | // FLView treats performKeyEquivalent to keyDown, but performKeyEquivalent is |
| 1327 | // still needed for the system menu. |
| 1328 | [[NSApp keyWindow] sendEvent:theEvent]; |
| 1329 | return; |
| 1330 | } |
| 1331 | [NSApp sendEvent:theEvent]; |
| 1332 | } |
| 1333 | @end |
| 1334 | |
| 1335 | static FLDelegate *mydelegate; |
| 1336 | |
| 1337 | void fl_open_display() { |
| 1338 | static char beenHereDoneThat = 0; |
| 1339 | if ( !beenHereDoneThat ) { |
| 1340 | beenHereDoneThat = 1; |
| 1341 | |
| 1342 | BOOL need_new_nsapp = (NSApp == nil); |
| 1343 | if (need_new_nsapp) [NSApplication sharedApplication]; |
| 1344 | NSAutoreleasePool *localPool; |
| 1345 | localPool = [[NSAutoreleasePool alloc] init]; // never released |
| 1346 | mydelegate = [[FLDelegate alloc] init]; |
| 1347 | [NSApp setDelegate:mydelegate]; |
| 1348 | if (need_new_nsapp) [NSApp finishLaunching]; |
| 1349 | |
| 1350 | // empty the event queue but keep system events for drag&drop of files at launch |
| 1351 | NSEvent *ign_event; |
| 1352 | do ign_event = [NSApp nextEventMatchingMask:(NSAnyEventMask & ~NSSystemDefinedMask) |
| 1353 | untilDate:[NSDate dateWithTimeIntervalSinceNow:0] |
| 1354 | inMode:NSDefaultRunLoopMode |
| 1355 | dequeue:YES]; |
| 1356 | while (ign_event); |
| 1357 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1358 | // bring the application into foreground without a 'CARB' resource |
| 1359 | Boolean same_psn; |
| 1360 | ProcessSerialNumber cur_psn, front_psn; |
| 1361 | if ( !GetCurrentProcess( &cur_psn ) && !GetFrontProcess( &front_psn ) && |
| 1362 | !SameProcess( &front_psn, &cur_psn, &same_psn ) && !same_psn ) { |
| 1363 | // only transform the application type for unbundled apps |
| 1364 | CFBundleRef bundle = CFBundleGetMainBundle(); |
| 1365 | if ( bundle ) { |
| 1366 | FSRef execFs; |
| 1367 | CFURLRef execUrl = CFBundleCopyExecutableURL( bundle ); |
| 1368 | CFURLGetFSRef( execUrl, &execFs ); |
| 1369 | |
| 1370 | FSRef bundleFs; |
| 1371 | GetProcessBundleLocation( &cur_psn, &bundleFs ); |
| 1372 | |
| 1373 | if ( !FSCompareFSRefs( &execFs, &bundleFs ) ) |
| 1374 | bundle = NULL; |
| 1375 | |
| 1376 | CFRelease(execUrl); |
| 1377 | } |
| 1378 | |
| 1379 | if ( !bundle ) |
| 1380 | { |
| 1381 | // Earlier versions of this code tried to use weak linking, however it |
| 1382 | // appears that this does not work on 10.2. Since 10.3 and higher provide |
| 1383 | // both TransformProcessType and CPSEnableForegroundOperation, the following |
| 1384 | // conditional code compiled on 10.2 will still work on newer releases... |
| 1385 | OSErr err; |
| 1386 | #if __LP64__ |
| 1387 | err = TransformProcessType(&cur_psn, kProcessTransformToForegroundApplication); |
| 1388 | #else |
| 1389 | |
| 1390 | #if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_2 |
| 1391 | if (TransformProcessType != NULL) { |
| 1392 | err = TransformProcessType(&cur_psn, kProcessTransformToForegroundApplication); |
| 1393 | } else |
| 1394 | #endif // MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_2 |
| 1395 | err = CPSEnableForegroundOperation(&cur_psn, 0x03, 0x3C, 0x2C, 0x1103); |
| 1396 | #endif // __LP64__ |
| 1397 | if (err == noErr) { |
| 1398 | SetFrontProcess( &cur_psn ); |
| 1399 | } |
| 1400 | } |
| 1401 | } |
| 1402 | if (![NSApp servicesMenu]) createAppleMenu(); |
| 1403 | fl_system_menu = [NSApp mainMenu]; |
| 1404 | |
| 1405 | [[NSNotificationCenter defaultCenter] addObserver:mydelegate |
| 1406 | selector:@selector(anywindowwillclosenotif:) |
| 1407 | name:NSWindowWillCloseNotification |
| 1408 | object:nil]; |
| 1409 | } |
| 1410 | } |
| 1411 | |
| 1412 | |
| 1413 | /* |
| 1414 | * get rid of allocated resources |
| 1415 | */ |
| 1416 | void fl_close_display() { |
| 1417 | } |
| 1418 | |
| 1419 | |
| 1420 | // Gets the border sizes and the titlebar size |
| 1421 | static void get_window_frame_sizes(int &bx, int &by, int &bt) { |
| 1422 | static bool first = true; |
| 1423 | static int top, left, bottom; |
| 1424 | if (first) { |
| 1425 | first = false; |
| 1426 | if (NSApp == nil) fl_open_display(); |
| 1427 | NSRect inside = { {20,20}, {100,100} }; |
| 1428 | NSRect outside = [NSWindow frameRectForContentRect:inside styleMask:NSTitledWindowMask]; |
| 1429 | left = int(outside.origin.x - inside.origin.x); |
| 1430 | bottom = int(outside.origin.y - inside.origin.y); |
| 1431 | top = int(outside.size.height - inside.size.height) - bottom; |
| 1432 | } |
| 1433 | bx = left; |
| 1434 | by = bottom; |
| 1435 | bt = top; |
| 1436 | } |
| 1437 | |
| 1438 | /* |
| 1439 | * smallest x ccordinate in screen space |
| 1440 | */ |
| 1441 | int Fl::x() { |
| 1442 | return int([[NSScreen mainScreen] visibleFrame].origin.x); |
| 1443 | } |
| 1444 | |
| 1445 | |
| 1446 | /* |
| 1447 | * smallest y coordinate in screen space |
| 1448 | */ |
| 1449 | int Fl::y() { |
| 1450 | NSRect all = [[NSScreen mainScreen] frame]; |
| 1451 | NSRect visible = [[NSScreen mainScreen] visibleFrame]; |
| 1452 | return int(all.size.height - (visible.origin.y + visible.size.height)); |
| 1453 | } |
| 1454 | |
| 1455 | |
| 1456 | /* |
| 1457 | * screen width |
| 1458 | */ |
| 1459 | int Fl::w() { |
| 1460 | return int([[NSScreen mainScreen] visibleFrame].size.width); |
| 1461 | } |
| 1462 | |
| 1463 | |
| 1464 | /* |
| 1465 | * screen height |
| 1466 | */ |
| 1467 | int Fl::h() { |
| 1468 | return int([[NSScreen mainScreen] visibleFrame].size.height); |
| 1469 | } |
| 1470 | |
| 1471 | |
| 1472 | /* |
| 1473 | * get the current mouse pointer world coordinates |
| 1474 | */ |
| 1475 | void Fl::get_mouse(int &x, int &y) |
| 1476 | { |
| 1477 | fl_open_display(); |
| 1478 | NSPoint pt = [NSEvent mouseLocation]; |
| 1479 | x = int(pt.x); |
| 1480 | y = int([[NSScreen mainScreen] frame].size.height - pt.y); |
| 1481 | } |
| 1482 | |
| 1483 | |
| 1484 | /* |
| 1485 | * Initialize the given port for redraw and call the window's flush() to actually draw the content |
| 1486 | */ |
| 1487 | void Fl_X::flush() |
| 1488 | { |
| 1489 | w->flush(); |
| 1490 | if (fl_gc) CGContextFlush(fl_gc); |
| 1491 | } |
| 1492 | |
| 1493 | /* |
| 1494 | * Gets called when a window is created, resized, or deminiaturized |
| 1495 | */ |
| 1496 | static void handleUpdateEvent( Fl_Window *window ) |
| 1497 | { |
| 1498 | if ( !window ) return; |
| 1499 | Fl_X *i = Fl_X::i( window ); |
| 1500 | i->wait_for_expose = 0; |
| 1501 | |
| 1502 | if ( i->region ) { |
| 1503 | XDestroyRegion(i->region); |
| 1504 | i->region = 0; |
| 1505 | } |
| 1506 | |
| 1507 | for ( Fl_X *cx = i->xidChildren; cx; cx = cx->xidNext ) { |
| 1508 | if ( cx->region ) { |
| 1509 | XDestroyRegion(cx->region); |
| 1510 | cx->region = 0; |
| 1511 | } |
| 1512 | cx->w->clear_damage(FL_DAMAGE_ALL); |
| 1513 | cx->flush(); |
| 1514 | cx->w->clear_damage(); |
| 1515 | } |
| 1516 | window->clear_damage(FL_DAMAGE_ALL); |
| 1517 | i->flush(); |
| 1518 | window->clear_damage(); |
| 1519 | } |
| 1520 | |
| 1521 | |
| 1522 | int Fl_X::fake_X_wm(const Fl_Window* w,int &X,int &Y, int &bt,int &bx, int &by) { |
| 1523 | int W, H, xoff, yoff, dx, dy; |
| 1524 | int ret = bx = by = bt = 0; |
| 1525 | if (w->border() && !w->parent()) { |
| 1526 | if (w->maxw != w->minw || w->maxh != w->minh) { |
| 1527 | ret = 2; |
| 1528 | } else { |
| 1529 | ret = 1; |
| 1530 | } |
| 1531 | get_window_frame_sizes(bx, by, bt); |
| 1532 | } |
| 1533 | // The coordinates of the whole window, including non-client area |
| 1534 | xoff = bx; |
| 1535 | yoff = by + bt; |
| 1536 | dx = 2*bx; |
| 1537 | dy = 2*by + bt; |
| 1538 | X = w->x()-xoff; |
| 1539 | Y = w->y()-yoff; |
| 1540 | W = w->w()+dx; |
| 1541 | H = w->h()+dy; |
| 1542 | |
| 1543 | // Proceed to positioning the window fully inside the screen, if possible |
| 1544 | |
| 1545 | // let's get a little elaborate here. Mac OS X puts a lot of stuff on the desk |
| 1546 | // that we want to avoid when positioning our window, namely the Dock and the |
| 1547 | // top menu bar (and even more stuff in 10.4 Tiger). So we will go through the |
| 1548 | // list of all available screens and find the one that this window is most |
| 1549 | // likely to go to, and then reposition it to fit withing the 'good' area. |
| 1550 | // Rect r; |
| 1551 | // find the screen, that the center of this window will fall into |
| 1552 | int R = X+W, B = Y+H; // right and bottom |
| 1553 | int cx = (X+R)/2, cy = (Y+B)/2; // center of window; |
| 1554 | NSScreen *gd = NULL; |
| 1555 | NSArray *a = [NSScreen screens]; int count = (int)[a count]; NSRect r; int i; |
| 1556 | for( i = 0; i < count; i++) { |
| 1557 | r = [[a objectAtIndex:i] frame]; |
| 1558 | cy = int(r.size.height - cy); |
| 1559 | if ( cx >= r.origin.x && cx <= r.origin.x + r.size.width |
| 1560 | && cy >= r.origin.y && cy <= r.origin.y + r.size.height) |
| 1561 | break; |
| 1562 | } |
| 1563 | if (i < count) gd = [a objectAtIndex:i]; |
| 1564 | |
| 1565 | // if the center doesn't fall on a screen, try the top left |
| 1566 | if (!gd) { |
| 1567 | for( i = 0; i < count; i++) { |
| 1568 | r = [[a objectAtIndex:i] frame]; |
| 1569 | if ( X >= r.origin.x && X <= r.origin.x + r.size.width |
| 1570 | && r.size.height - Y >= r.origin.y && r.size.height - Y <= r.origin.y + r.size.height) |
| 1571 | break; |
| 1572 | } |
| 1573 | if (i < count) gd = [a objectAtIndex:i]; |
| 1574 | } |
| 1575 | // if that doesn't fall on a screen, try the top right |
| 1576 | if (!gd) { |
| 1577 | for( i = 0; i < count; i++) { |
| 1578 | r = [[a objectAtIndex:i] frame]; |
| 1579 | if ( R >= r.origin.x && R <= r.origin.x + r.size.width |
| 1580 | && r.size.height - Y >= r.origin.y && r.size.height - Y <= r.origin.y + r.size.height) |
| 1581 | break; |
| 1582 | } |
| 1583 | if (i < count) gd = [a objectAtIndex:i]; |
| 1584 | } |
| 1585 | // if that doesn't fall on a screen, try the bottom left |
| 1586 | if (!gd) { |
| 1587 | for( i = 0; i < count; i++) { |
| 1588 | r = [[a objectAtIndex:i] frame]; |
| 1589 | if ( X >= r.origin.x && X <= r.origin.x + r.size.width |
| 1590 | && Y-H >= r.origin.y && Y-H <= r.origin.y + r.size.height) |
| 1591 | break; |
| 1592 | } |
| 1593 | if (i < count) gd = [a objectAtIndex:i]; |
| 1594 | } |
| 1595 | // last resort, try the bottom right |
| 1596 | if (!gd) { |
| 1597 | for( i = 0; i < count; i++) { |
| 1598 | r = [[a objectAtIndex:i] frame]; |
| 1599 | if ( R >= r.origin.x && R <= r.origin.x + r.size.width |
| 1600 | && Y-H >= r.origin.y && Y-H <= r.origin.y + r.size.height) |
| 1601 | break; |
| 1602 | } |
| 1603 | if (i < count) gd = [a objectAtIndex:i]; |
| 1604 | } |
| 1605 | // if we still have not found a screen, we will use the main |
| 1606 | // screen, the one that has the application menu bar. |
| 1607 | if (!gd) gd = [a objectAtIndex:0]; |
| 1608 | if (gd) { |
| 1609 | r = [gd visibleFrame]; |
| 1610 | int sh = int([gd frame].size.height); |
| 1611 | if ( R > r.origin.x + r.size.width ) X -= int(R - (r.origin.x + r.size.width)); |
| 1612 | if ( B > sh - r.origin.y ) Y -= int(B - (sh - r.origin.y)); |
| 1613 | if ( X < r.origin.x ) X = int(r.origin.x); |
| 1614 | if ( Y < sh - (r.origin.y + r.size.height) ) Y = int(sh - (r.origin.y + r.size.height)); |
| 1615 | } |
| 1616 | |
| 1617 | // Return the client area's top left corner in (X,Y) |
| 1618 | X+=xoff; |
| 1619 | Y+=yoff; |
| 1620 | |
| 1621 | return ret; |
| 1622 | } |
| 1623 | |
| 1624 | |
| 1625 | Fl_Window *fl_dnd_target_window = 0; |
| 1626 | |
| 1627 | static void q_set_window_title(NSWindow *nsw, const char * name, const char *mininame) { |
| 1628 | CFStringRef title = CFStringCreateWithCString(NULL, (name ? name : ""), kCFStringEncodingUTF8); |
| 1629 | if(!title) { // fallback when name contains malformed UTF-8 |
| 1630 | int l = strlen(name); |
| 1631 | unsigned short* utf16 = new unsigned short[l + 1]; |
| 1632 | l = fl_utf8toUtf16(name, l, utf16, l + 1); |
| 1633 | title = CFStringCreateWithCharacters(NULL, utf16, l); |
| 1634 | delete[] utf16; |
| 1635 | } |
| 1636 | [nsw setTitle:(NSString*)title]; |
| 1637 | CFRelease(title); |
| 1638 | if (mininame && strlen(mininame)) { |
| 1639 | CFStringRef minititle = CFStringCreateWithCString(NULL, mininame, kCFStringEncodingUTF8); |
| 1640 | if (minititle) { |
| 1641 | [nsw setMiniwindowTitle:(NSString*)minititle]; |
| 1642 | CFRelease(minititle); |
| 1643 | } |
| 1644 | } |
| 1645 | } |
| 1646 | |
| 1647 | |
| 1648 | @interface FLView : NSView <NSTextInput> { |
| 1649 | int next_compose_length; |
| 1650 | bool in_key_event; |
| 1651 | } |
| 1652 | + (void)prepareEtext:(NSString*)aString; |
| 1653 | - (id)init; |
| 1654 | - (void)drawRect:(NSRect)rect; |
| 1655 | - (BOOL)acceptsFirstResponder; |
| 1656 | - (BOOL)acceptsFirstMouse:(NSEvent*)theEvent; |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1657 | - (void)resetCursorRects; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1658 | - (BOOL)performKeyEquivalent:(NSEvent*)theEvent; |
| 1659 | - (void)mouseUp:(NSEvent *)theEvent; |
| 1660 | - (void)rightMouseUp:(NSEvent *)theEvent; |
| 1661 | - (void)otherMouseUp:(NSEvent *)theEvent; |
| 1662 | - (void)mouseDown:(NSEvent *)theEvent; |
| 1663 | - (void)rightMouseDown:(NSEvent *)theEvent; |
| 1664 | - (void)otherMouseDown:(NSEvent *)theEvent; |
| 1665 | - (void)mouseMoved:(NSEvent *)theEvent; |
| 1666 | - (void)mouseDragged:(NSEvent *)theEvent; |
| 1667 | - (void)rightMouseDragged:(NSEvent *)theEvent; |
| 1668 | - (void)otherMouseDragged:(NSEvent *)theEvent; |
| 1669 | - (void)scrollWheel:(NSEvent *)theEvent; |
Henrik Andersson | 485f419 | 2011-09-16 11:51:32 +0000 | [diff] [blame] | 1670 | + (NSString *)keyTranslate:(UInt16)keyCode withModifierFlags:(UInt32)modifierFlags; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1671 | - (BOOL)handleKeyDown:(NSEvent *)theEvent; |
| 1672 | - (void)keyDown:(NSEvent *)theEvent; |
| 1673 | - (void)keyUp:(NSEvent *)theEvent; |
| 1674 | - (void)flagsChanged:(NSEvent *)theEvent; |
| 1675 | - (NSDragOperation)draggingEntered:(id < NSDraggingInfo >)sender; |
| 1676 | - (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender; |
| 1677 | - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender; |
| 1678 | - (void)draggingExited:(id < NSDraggingInfo >)sender; |
| 1679 | - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal; |
| 1680 | @end |
| 1681 | |
| 1682 | @implementation FLView |
| 1683 | - (id)init |
| 1684 | { |
| 1685 | self = [super init]; |
| 1686 | if (self) { |
| 1687 | next_compose_length = -1; |
| 1688 | in_key_event = false; |
| 1689 | } |
| 1690 | return self; |
| 1691 | } |
| 1692 | - (void)drawRect:(NSRect)rect |
| 1693 | { |
| 1694 | fl_lock_function(); |
| 1695 | FLWindow *cw = (FLWindow*)[self window]; |
| 1696 | Fl_Window *w = [cw getFl_Window]; |
| 1697 | handleUpdateEvent(w); |
| 1698 | fl_unlock_function(); |
| 1699 | } |
| 1700 | |
| 1701 | - (BOOL)acceptsFirstResponder |
| 1702 | { |
| 1703 | return YES; |
| 1704 | } |
| 1705 | - (BOOL)performKeyEquivalent:(NSEvent*)theEvent |
| 1706 | { |
| 1707 | //NSLog(@"performKeyEquivalent:"); |
| 1708 | return [self handleKeyDown:theEvent]; |
| 1709 | } |
| 1710 | - (BOOL)acceptsFirstMouse:(NSEvent*)theEvent |
| 1711 | { |
| 1712 | Fl_Window *w = [(FLWindow*)[theEvent window] getFl_Window]; |
| 1713 | Fl_Window *first = Fl::first_window(); |
| 1714 | return (first == w || !first->modal()); |
| 1715 | } |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1716 | - (void)resetCursorRects { |
| 1717 | Fl_Window *w = [(FLWindow*)[self window] getFl_Window]; |
| 1718 | Fl_X *i = Fl_X::i(w); |
| 1719 | // We have to have at least one cursor rect for invalidateCursorRectsForView |
| 1720 | // to work, hence the "else" clause. |
| 1721 | if (i->cursor) |
| 1722 | [self addCursorRect:[self visibleRect] cursor:(NSCursor*)i->cursor]; |
| 1723 | else |
| 1724 | [self addCursorRect:[self visibleRect] cursor:[NSCursor arrowCursor]]; |
| 1725 | } |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1726 | - (void)mouseUp:(NSEvent *)theEvent { |
| 1727 | cocoaMouseHandler(theEvent); |
| 1728 | } |
| 1729 | - (void)rightMouseUp:(NSEvent *)theEvent { |
| 1730 | cocoaMouseHandler(theEvent); |
| 1731 | } |
| 1732 | - (void)otherMouseUp:(NSEvent *)theEvent { |
| 1733 | cocoaMouseHandler(theEvent); |
| 1734 | } |
| 1735 | - (void)mouseDown:(NSEvent *)theEvent { |
| 1736 | cocoaMouseHandler(theEvent); |
| 1737 | } |
| 1738 | - (void)rightMouseDown:(NSEvent *)theEvent { |
| 1739 | cocoaMouseHandler(theEvent); |
| 1740 | } |
| 1741 | - (void)otherMouseDown:(NSEvent *)theEvent { |
| 1742 | cocoaMouseHandler(theEvent); |
| 1743 | } |
| 1744 | - (void)mouseMoved:(NSEvent *)theEvent { |
| 1745 | cocoaMouseHandler(theEvent); |
| 1746 | } |
| 1747 | - (void)mouseDragged:(NSEvent *)theEvent { |
| 1748 | cocoaMouseHandler(theEvent); |
| 1749 | } |
| 1750 | - (void)rightMouseDragged:(NSEvent *)theEvent { |
| 1751 | cocoaMouseHandler(theEvent); |
| 1752 | } |
| 1753 | - (void)otherMouseDragged:(NSEvent *)theEvent { |
| 1754 | cocoaMouseHandler(theEvent); |
| 1755 | } |
| 1756 | - (void)scrollWheel:(NSEvent *)theEvent { |
| 1757 | cocoaMouseWheelHandler(theEvent); |
| 1758 | } |
Henrik Andersson | 485f419 | 2011-09-16 11:51:32 +0000 | [diff] [blame] | 1759 | + (NSString *)keyTranslate:(UInt16)keyCode withModifierFlags:(UInt32)modifierFlags { |
| 1760 | const UCKeyboardLayout *layout; |
| 1761 | OSStatus err; |
| 1762 | |
| 1763 | layout = NULL; |
| 1764 | |
| 1765 | #if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) |
| 1766 | TISInputSourceRef keyboard; |
| 1767 | CFDataRef uchr; |
| 1768 | |
| 1769 | keyboard = TISCopyCurrentKeyboardInputSource(); |
| 1770 | uchr = (CFDataRef)TISGetInputSourceProperty(keyboard, |
| 1771 | kTISPropertyUnicodeKeyLayoutData); |
| 1772 | if (uchr == NULL) |
| 1773 | return nil; |
| 1774 | |
| 1775 | layout = (const UCKeyboardLayout*)CFDataGetBytePtr(uchr); |
| 1776 | #else |
| 1777 | KeyboardLayoutRef old_layout; |
| 1778 | int kind; |
| 1779 | |
| 1780 | err = KLGetCurrentKeyboardLayout(&old_layout); |
| 1781 | if (err != noErr) |
| 1782 | return nil; |
| 1783 | |
| 1784 | err = KLGetKeyboardLayoutProperty(old_layout, kKLKind, |
| 1785 | (const void**)&kind); |
| 1786 | if (err != noErr) |
| 1787 | return nil; |
| 1788 | |
| 1789 | // Old, crufty layout format? |
| 1790 | if (kind == kKLKCHRKind) { |
| 1791 | void *kchr_layout; |
| 1792 | |
| 1793 | UInt32 chars, state; |
| 1794 | char buf[3]; |
| 1795 | |
| 1796 | unichar result[16]; |
| 1797 | ByteCount in_len, out_len; |
| 1798 | |
| 1799 | err = KLGetKeyboardLayoutProperty(old_layout, kKLKCHRData, |
| 1800 | (const void**)&kchr_layout); |
| 1801 | if (err != noErr) |
| 1802 | return nil; |
| 1803 | |
| 1804 | state = 0; |
| 1805 | |
| 1806 | keyCode &= 0x7f; |
| 1807 | modifierFlags &= 0xff00; |
| 1808 | |
| 1809 | chars = KeyTranslate(kchr_layout, keyCode | modifierFlags, &state); |
| 1810 | |
| 1811 | buf[0] = (chars >> 16) & 0xff; |
| 1812 | buf[1] = chars & 0xff; |
| 1813 | buf[2] = '\0'; |
| 1814 | |
| 1815 | if (buf[0] == '\0') { |
| 1816 | buf[0] = buf[1]; |
| 1817 | buf[1] = '\0'; |
| 1818 | } |
| 1819 | |
| 1820 | // The data is now in some layout specific encoding. Need to convert |
| 1821 | // this to unicode. |
| 1822 | |
| 1823 | ScriptCode script; |
| 1824 | TextEncoding encoding; |
| 1825 | TECObjectRef converter; |
| 1826 | |
| 1827 | script = (ScriptCode)GetScriptManagerVariable(smKeyScript); |
| 1828 | |
| 1829 | err = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, |
| 1830 | kTextRegionDontCare, NULL, |
| 1831 | &encoding); |
| 1832 | if (err != noErr) |
| 1833 | return nil; |
| 1834 | |
| 1835 | err = TECCreateConverter(&converter, encoding, kTextEncodingUnicodeV4_0); |
| 1836 | if (err != noErr) |
| 1837 | return nil; |
| 1838 | |
| 1839 | in_len = strlen(buf); |
| 1840 | out_len = sizeof(result); |
| 1841 | |
| 1842 | err = TECConvertText(converter, (ConstTextPtr)buf, in_len, &in_len, |
| 1843 | (TextPtr)result, out_len, &out_len); |
| 1844 | |
| 1845 | TECDisposeConverter(converter); |
| 1846 | |
| 1847 | if (err != noErr) |
| 1848 | return nil; |
| 1849 | |
| 1850 | return [NSString stringWithCharacters:result |
| 1851 | length:(out_len / sizeof(unichar))]; |
| 1852 | } |
| 1853 | |
| 1854 | if ((kind != kKLKCHRuchrKind) && (kind != kKLuchrKind)) |
| 1855 | return nil; |
| 1856 | |
| 1857 | err = KLGetKeyboardLayoutProperty(old_layout, kKLuchrData, |
| 1858 | (const void**)&layout); |
| 1859 | if (err != noErr) |
| 1860 | return nil; |
| 1861 | #endif |
| 1862 | |
| 1863 | if (layout == NULL) |
| 1864 | return nil; |
| 1865 | |
| 1866 | UInt32 dead_state; |
| 1867 | UniCharCount max_len, actual_len; |
| 1868 | UniChar string[255]; |
| 1869 | |
| 1870 | dead_state = 0; |
| 1871 | max_len = sizeof(string)/sizeof(*string); |
| 1872 | |
| 1873 | modifierFlags = (modifierFlags >> 8) & 0xff; |
| 1874 | |
| 1875 | err = UCKeyTranslate(layout, keyCode, kUCKeyActionDown, modifierFlags, |
| 1876 | LMGetKbdType(), 0, &dead_state, max_len, &actual_len, |
| 1877 | string); |
| 1878 | if (err != noErr) |
| 1879 | return nil; |
| 1880 | |
| 1881 | return [NSString stringWithCharacters:string length:actual_len]; |
| 1882 | } |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1883 | - (BOOL)handleKeyDown:(NSEvent *)theEvent { |
| 1884 | //NSLog(@"handleKeyDown"); |
| 1885 | fl_lock_function(); |
| 1886 | |
| 1887 | Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window]; |
| 1888 | Fl::first_window(window); |
| 1889 | |
| 1890 | next_compose_length = -1; |
| 1891 | // First let's process the raw key press |
| 1892 | cocoaKeyboardHandler(theEvent); |
| 1893 | |
| 1894 | int no_text_key = false; |
| 1895 | static const int notext[] = { // keys that don't emit text |
| 1896 | FL_BackSpace, FL_Print, FL_Scroll_Lock, FL_Pause, |
| 1897 | FL_Insert, FL_Home, FL_Page_Up, FL_Delete, FL_End, FL_Page_Down, |
| 1898 | FL_Left, FL_Up, FL_Right, FL_Down, |
| 1899 | FL_Menu, FL_Num_Lock, FL_Help |
| 1900 | }; |
| 1901 | static const int count = sizeof(notext)/sizeof(int); |
| 1902 | if (Fl::e_keysym > FL_F && Fl::e_keysym <= FL_F_Last) no_text_key = true; |
| 1903 | else for (int i=0; i < count; i++) { |
| 1904 | if (notext[i] == Fl::e_keysym) { |
| 1905 | no_text_key = true; |
| 1906 | break; |
| 1907 | } |
| 1908 | } |
Henrik Andersson | 485f419 | 2011-09-16 11:51:32 +0000 | [diff] [blame] | 1909 | if (!no_text_key) { |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1910 | // The simple keyboard model will ignore insertText, so we need to grab |
| 1911 | // the symbol directly from the event. Note that we still use setMarkedText. |
Henrik Andersson | 485f419 | 2011-09-16 11:51:32 +0000 | [diff] [blame] | 1912 | if (use_simple_keyboard) { |
| 1913 | NSString *simple_chars; |
| 1914 | UInt32 modifiers; |
| 1915 | |
| 1916 | // We want a "normal" symbol out of the event, which basically means |
| 1917 | // we only respect the shift and alt/altgr modifiers. Cocoa can help |
| 1918 | // us if we only wanted shift, but as we also want alt/altgr, we'll |
| 1919 | // have to do some lookup ourselves. This matches our behaviour on |
| 1920 | // other platforms. |
| 1921 | |
| 1922 | modifiers = 0; |
| 1923 | if ([theEvent modifierFlags] & NSAlphaShiftKeyMask) |
| 1924 | modifiers |= alphaLock; |
| 1925 | if ([theEvent modifierFlags] & NSShiftKeyMask) |
| 1926 | modifiers |= shiftKey; |
| 1927 | if ([theEvent modifierFlags] & NSAlternateKeyMask) |
| 1928 | modifiers |= optionKey; |
| 1929 | |
| 1930 | simple_chars = [FLView keyTranslate:[theEvent keyCode] |
| 1931 | withModifierFlags:modifiers]; |
| 1932 | if (simple_chars == nil) { |
| 1933 | // Something went wrong. Fall back to what Cocoa gave us... |
| 1934 | simple_chars = [theEvent charactersIgnoringModifiers]; |
| 1935 | } |
| 1936 | |
| 1937 | [FLView prepareEtext:simple_chars]; |
| 1938 | } |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1939 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1940 | // Then we can let the OS have a stab at it and see if it thinks it |
| 1941 | // should result in some text |
Henrik Andersson | 485f419 | 2011-09-16 11:51:32 +0000 | [diff] [blame] | 1942 | |
| 1943 | // Don't send cmd-<key> to interpretKeyEvents because it beeps. |
| 1944 | if (!(Fl::e_state & FL_META)) { |
| 1945 | NSText *edit = [[theEvent window] fieldEditor:YES forObject:nil]; |
| 1946 | in_key_event = true; |
| 1947 | [edit interpretKeyEvents:[NSArray arrayWithObject:theEvent]]; |
| 1948 | in_key_event = false; |
| 1949 | } |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1950 | } |
| 1951 | //NSLog(@"to text=%@ l=%d", [NSString stringWithUTF8String:Fl::e_text], Fl::e_length); |
| 1952 | int handled = Fl::handle(FL_KEYDOWN, window); |
| 1953 | // We have to update this after Fl::handle as it says what to do on the |
| 1954 | // _next_ input |
| 1955 | if (next_compose_length != -1) |
| 1956 | Fl::compose_state = next_compose_length; |
| 1957 | |
| 1958 | fl_unlock_function(); |
| 1959 | return (handled ? YES : NO); |
| 1960 | } |
| 1961 | - (void)keyDown:(NSEvent *)theEvent { |
| 1962 | //NSLog(@"keyDown: "); |
| 1963 | [self handleKeyDown:theEvent]; |
| 1964 | } |
| 1965 | - (void)keyUp:(NSEvent *)theEvent { |
| 1966 | //NSLog(@"keyUp: "); |
| 1967 | fl_lock_function(); |
| 1968 | Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window]; |
| 1969 | Fl::first_window(window); |
| 1970 | cocoaKeyboardHandler(theEvent); |
| 1971 | NSString *s = [theEvent characters]; |
| 1972 | if ([s length] >= 1) [FLView prepareEtext:[s substringToIndex:1]]; |
| 1973 | Fl::handle(FL_KEYUP,window); |
| 1974 | fl_unlock_function(); |
| 1975 | } |
| 1976 | - (void)flagsChanged:(NSEvent *)theEvent { |
| 1977 | //NSLog(@"flagsChanged: "); |
| 1978 | fl_lock_function(); |
| 1979 | static UInt32 prevMods = 0; |
| 1980 | NSUInteger mods = [theEvent modifierFlags]; |
| 1981 | Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window]; |
| 1982 | UInt32 tMods = prevMods ^ mods; |
| 1983 | int sendEvent = 0; |
| 1984 | if ( tMods ) |
| 1985 | { |
| 1986 | unsigned short keycode = [theEvent keyCode]; |
| 1987 | Fl::e_keysym = Fl::e_original_keysym = macKeyLookUp[keycode & 0x7f]; |
| 1988 | if ( Fl::e_keysym ) |
| 1989 | sendEvent = ( prevMods<mods ) ? FL_KEYBOARD : FL_KEYUP; |
| 1990 | Fl::e_length = 0; |
| 1991 | Fl::e_text = (char*)""; |
| 1992 | prevMods = mods; |
| 1993 | } |
| 1994 | mods_to_e_state( mods ); |
| 1995 | while (window->parent()) window = window->window(); |
| 1996 | if (sendEvent) Fl::handle(sendEvent,window); |
| 1997 | fl_unlock_function(); |
| 1998 | } |
| 1999 | - (NSDragOperation)draggingEntered:(id < NSDraggingInfo >)sender |
| 2000 | { |
| 2001 | fl_lock_function(); |
| 2002 | Fl_Window *target = [(FLWindow*)[self window] getFl_Window]; |
| 2003 | update_e_xy_and_e_xy_root([self window]); |
| 2004 | fl_dnd_target_window = target; |
| 2005 | int ret = Fl::handle( FL_DND_ENTER, target ); |
| 2006 | breakMacEventLoop(); |
| 2007 | fl_unlock_function(); |
| 2008 | return ret ? NSDragOperationCopy : NSDragOperationNone; |
| 2009 | } |
| 2010 | - (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender |
| 2011 | { |
| 2012 | fl_lock_function(); |
| 2013 | Fl_Window *target = [(FLWindow*)[self window] getFl_Window]; |
| 2014 | update_e_xy_and_e_xy_root([self window]); |
| 2015 | fl_dnd_target_window = target; |
| 2016 | int ret = Fl::handle( FL_DND_DRAG, target ); |
| 2017 | breakMacEventLoop(); |
| 2018 | fl_unlock_function(); |
| 2019 | return ret ? NSDragOperationCopy : NSDragOperationNone; |
| 2020 | } |
| 2021 | - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender |
| 2022 | { |
| 2023 | static char *DragData = NULL; |
| 2024 | fl_lock_function(); |
| 2025 | Fl_Window *target = [(FLWindow*)[self window] getFl_Window]; |
| 2026 | if ( !Fl::handle( FL_DND_RELEASE, target ) ) { |
| 2027 | breakMacEventLoop(); |
| 2028 | fl_unlock_function(); |
| 2029 | return NO; |
| 2030 | } |
| 2031 | NSPasteboard *pboard; |
| 2032 | // NSDragOperation sourceDragMask; |
| 2033 | // sourceDragMask = [sender draggingSourceOperationMask]; |
| 2034 | pboard = [sender draggingPasteboard]; |
| 2035 | update_e_xy_and_e_xy_root([self window]); |
| 2036 | if (DragData) { free(DragData); DragData = NULL; } |
| 2037 | if ( [[pboard types] containsObject:NSFilenamesPboardType] ) { |
| 2038 | CFArrayRef files = (CFArrayRef)[pboard propertyListForType:NSFilenamesPboardType]; |
| 2039 | CFStringRef all = CFStringCreateByCombiningStrings(NULL, files, CFSTR("\n")); |
| 2040 | int l = CFStringGetMaximumSizeForEncoding(CFStringGetLength(all), kCFStringEncodingUTF8); |
| 2041 | DragData = (char *)malloc(l + 1); |
| 2042 | CFStringGetCString(all, DragData, l + 1, kCFStringEncodingUTF8); |
| 2043 | CFRelease(all); |
| 2044 | } |
| 2045 | else if ( [[pboard types] containsObject:NSStringPboardType] ) { |
| 2046 | NSData *data = [pboard dataForType:NSStringPboardType]; |
| 2047 | DragData = (char *)malloc([data length] + 1); |
| 2048 | [data getBytes:DragData]; |
| 2049 | DragData[[data length]] = 0; |
| 2050 | convert_crlf(DragData, strlen(DragData)); |
| 2051 | } |
| 2052 | else { |
| 2053 | breakMacEventLoop(); |
| 2054 | fl_unlock_function(); |
| 2055 | return NO; |
| 2056 | } |
| 2057 | Fl::e_text = DragData; |
| 2058 | Fl::e_length = strlen(DragData); |
| 2059 | int old_event = Fl::e_number; |
| 2060 | Fl::belowmouse()->handle(Fl::e_number = FL_PASTE); |
| 2061 | Fl::e_number = old_event; |
| 2062 | if (DragData) { free(DragData); DragData = NULL; } |
| 2063 | Fl::e_text = NULL; |
| 2064 | Fl::e_length = 0; |
| 2065 | fl_dnd_target_window = NULL; |
| 2066 | breakMacEventLoop(); |
| 2067 | fl_unlock_function(); |
| 2068 | return YES; |
| 2069 | } |
| 2070 | - (void)draggingExited:(id < NSDraggingInfo >)sender |
| 2071 | { |
| 2072 | fl_lock_function(); |
| 2073 | if ( fl_dnd_target_window ) { |
| 2074 | Fl::handle( FL_DND_LEAVE, fl_dnd_target_window ); |
| 2075 | fl_dnd_target_window = 0; |
| 2076 | } |
| 2077 | fl_unlock_function(); |
| 2078 | } |
| 2079 | - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal |
| 2080 | { |
| 2081 | return NSDragOperationGeneric; |
| 2082 | } |
| 2083 | |
| 2084 | + (void)prepareEtext:(NSString*)aString { |
| 2085 | // fills Fl::e_text with UTF-8 encoded aString using an adequate memory allocation |
| 2086 | static char *received_utf8 = NULL; |
| 2087 | static int lreceived = 0; |
| 2088 | char *p = (char*)[aString UTF8String]; |
| 2089 | int l = strlen(p); |
| 2090 | if (l > 0) { |
| 2091 | if (lreceived == 0) { |
| 2092 | received_utf8 = (char*)malloc(l + 1); |
| 2093 | lreceived = l; |
| 2094 | } |
| 2095 | else if (l > lreceived) { |
| 2096 | received_utf8 = (char*)realloc(received_utf8, l + 1); |
| 2097 | lreceived = l; |
| 2098 | } |
| 2099 | strcpy(received_utf8, p); |
| 2100 | Fl::e_text = received_utf8; |
| 2101 | } |
| 2102 | Fl::e_length = l; |
| 2103 | } |
| 2104 | |
| 2105 | // These functions implement text input. |
| 2106 | // Only two-stroke character composition works at this point. |
| 2107 | // Needs much elaboration to fully support CJK text input, |
| 2108 | // but this is the way to go. |
| 2109 | - (void)doCommandBySelector:(SEL)aSelector { |
| 2110 | } |
| 2111 | |
| 2112 | - (void)insertText:(id)aString { |
| 2113 | NSString *received; |
| 2114 | if ([aString isKindOfClass:[NSAttributedString class]]) { |
| 2115 | received = [(NSAttributedString*)aString string]; |
| 2116 | } else { |
| 2117 | received = (NSString*)aString; |
| 2118 | } |
| 2119 | //NSLog(@"insertText: received=%@",received); |
| 2120 | |
| 2121 | if (!in_key_event) fl_lock_function(); |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 2122 | |
| 2123 | // Simple keyboard widgets do not want these side channel inputs. |
| 2124 | if (use_simple_keyboard) |
| 2125 | goto end; |
| 2126 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 2127 | [FLView prepareEtext:received]; |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 2128 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 2129 | // We can get called outside of key events (e.g. from the character |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 2130 | // palette). We need to fake our own key event at that point. |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 2131 | if (!in_key_event) { |
| 2132 | Fl_Window *target = [(FLWindow*)[self window] getFl_Window]; |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 2133 | Fl::e_keysym = Fl::e_original_keysym = 0; |
| 2134 | Fl::handle(FL_KEYDOWN, target); |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 2135 | // for some reason, the window does not redraw until the next mouse move or button push |
| 2136 | // sending a 'redraw()' or 'awake()' does not solve the issue! |
| 2137 | Fl::flush(); |
| 2138 | } |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 2139 | |
| 2140 | end: |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 2141 | if (!in_key_event) fl_unlock_function(); |
| 2142 | } |
| 2143 | |
| 2144 | - (void)setMarkedText:(id)aString selectedRange:(NSRange)newSelection { |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 2145 | NSString *received, *current, *aggregate; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 2146 | if (newSelection.location == 0) { |
| 2147 | [self unmarkText]; |
| 2148 | return; |
| 2149 | } |
| 2150 | if ([aString isKindOfClass:[NSAttributedString class]]) { |
| 2151 | received = [(NSAttributedString*)aString string]; |
| 2152 | } else { |
| 2153 | received = (NSString*)aString; |
| 2154 | } |
| 2155 | //NSLog(@"setMarkedText: %@ %d %d",received,newSelection.location,newSelection.length); |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 2156 | |
| 2157 | fl_lock_function(); |
| 2158 | |
| 2159 | // Simple keyboard widgets generally do not want these side channel |
| 2160 | // inputs, but we have no other way of getting dead keys so we make |
| 2161 | // an exception in that case. |
| 2162 | if (use_simple_keyboard) { |
| 2163 | if (in_key_event && (Fl::e_length == 0)) { |
| 2164 | [FLView prepareEtext:received]; |
| 2165 | |
| 2166 | Fl::e_text = (char*)cocoaDead2FLTK(Fl::e_text); |
| 2167 | Fl::e_length = strlen(Fl::e_text); |
| 2168 | } |
| 2169 | goto end; |
| 2170 | } |
| 2171 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 2172 | // This code creates the OS X behaviour of seeing dead keys as things |
| 2173 | // are being composed. |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 2174 | // |
| 2175 | // Note: The concatenation thing is because of how OS X deals with |
| 2176 | // invalid sequences. At that point it will spit out one call |
| 2177 | // to insertText with the now aborted sequence, and one new |
| 2178 | // call to setMarkedText with the new sequence. Since we want |
| 2179 | // both to be visible, we need to concatenate. |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 2180 | next_compose_length = newSelection.location; |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 2181 | current = [NSString stringWithUTF8String:Fl::e_text]; |
| 2182 | aggregate = [current stringByAppendingString:received]; |
| 2183 | |
| 2184 | [FLView prepareEtext:aggregate]; |
| 2185 | //NSLog(@"Fl::e_text=%@ Fl::e_length=%d next_compose_length=%d", aggregate, Fl::e_length, next_compose_length); |
| 2186 | |
| 2187 | // We can get called outside of key events (e.g. from the character |
| 2188 | // palette). We need to fake our own key event at that point. |
| 2189 | if (!in_key_event) { |
| 2190 | Fl_Window *target = [(FLWindow*)[self window] getFl_Window]; |
| 2191 | Fl::e_keysym = Fl::e_original_keysym = 0; |
| 2192 | Fl::handle(FL_KEYDOWN, target); |
| 2193 | } |
| 2194 | |
| 2195 | end: |
| 2196 | fl_unlock_function(); |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 2197 | } |
| 2198 | |
| 2199 | - (void)unmarkText { |
| 2200 | fl_lock_function(); |
| 2201 | Fl::compose_state = 0; |
| 2202 | fl_unlock_function(); |
| 2203 | //NSLog(@"unmarkText"); |
| 2204 | } |
| 2205 | |
| 2206 | - (NSRange)selectedRange { |
| 2207 | return NSMakeRange(NSNotFound, 0); |
| 2208 | } |
| 2209 | |
| 2210 | - (NSRange)markedRange { |
| 2211 | //NSLog(@"markedRange ?"); |
| 2212 | return NSMakeRange(NSNotFound, Fl::compose_state); |
| 2213 | } |
| 2214 | |
| 2215 | - (BOOL)hasMarkedText { |
| 2216 | //NSLog(@"hasMarkedText %s", Fl::compose_state > 0?"YES":"NO"); |
| 2217 | return (Fl::compose_state > 0); |
| 2218 | } |
| 2219 | |
| 2220 | - (NSAttributedString *)attributedSubstringFromRange:(NSRange)aRange { |
| 2221 | //NSLog(@"attributedSubstringFromRange: %d %d",aRange.location,aRange.length); |
| 2222 | return nil; |
| 2223 | } |
| 2224 | |
| 2225 | - (NSArray *)validAttributesForMarkedText { |
| 2226 | return nil; |
| 2227 | } |
| 2228 | |
| 2229 | - (NSRect)firstRectForCharacterRange:(NSRange)aRange { |
| 2230 | NSRect glyphRect; |
| 2231 | fl_lock_function(); |
| 2232 | Fl_Widget *focus = Fl::focus(); |
| 2233 | Fl_Window *wfocus = focus->window(); |
| 2234 | while (wfocus->window()) wfocus = wfocus->window(); |
| 2235 | glyphRect.size.width = 0; |
| 2236 | |
| 2237 | if (dynamic_cast<Fl_Text_Display*>(focus) != NULL) { |
| 2238 | int x, y; |
| 2239 | Fl_Text_Display *current = (Fl_Text_Display*)focus; |
| 2240 | current->position_to_xy( current->insert_position(), &x, &y ); |
| 2241 | glyphRect.origin.x = (CGFloat)x; |
| 2242 | glyphRect.origin.y = (CGFloat)y + current->textsize(); |
| 2243 | glyphRect.size.height = current->textsize(); |
| 2244 | } else { |
| 2245 | glyphRect.origin.x = focus->x(); |
| 2246 | glyphRect.origin.y = focus->y() + focus->h(); |
| 2247 | glyphRect.size.height = 12; |
| 2248 | } |
| 2249 | // Convert the rect to screen coordinates |
| 2250 | glyphRect.origin.y = wfocus->h() - glyphRect.origin.y; |
| 2251 | glyphRect.origin = [[self window] convertBaseToScreen:glyphRect.origin]; |
| 2252 | fl_unlock_function(); |
| 2253 | return glyphRect; |
| 2254 | } |
| 2255 | |
| 2256 | - (NSUInteger)characterIndexForPoint:(NSPoint)aPoint { |
| 2257 | return 0; |
| 2258 | } |
| 2259 | |
| 2260 | - (NSInteger)conversationIdentifier { |
| 2261 | return (NSInteger)self; |
| 2262 | } |
| 2263 | |
| 2264 | @end |
| 2265 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 2266 | void fullscreen_x(Fl_Window *w) { |
| 2267 | w->_set_fullscreen(); |
| 2268 | /* On OS X < 10.6, it is necessary to recreate the window. This is done |
| 2269 | with hide+show. */ |
| 2270 | w->hide(); |
| 2271 | w->show(); |
| 2272 | Fl::handle(FL_FULLSCREEN, w); |
| 2273 | } |
| 2274 | |
| 2275 | void fullscreen_off_x(Fl_Window *w, int X, int Y, int W, int H) { |
| 2276 | w->_clear_fullscreen(); |
| 2277 | w->hide(); |
| 2278 | w->resize(X, Y, W, H); |
| 2279 | w->show(); |
| 2280 | Fl::handle(FL_FULLSCREEN, w); |
| 2281 | } |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 2282 | |
| 2283 | /* |
| 2284 | * go ahead, create that (sub)window |
| 2285 | */ |
| 2286 | void Fl_X::make(Fl_Window* w) |
| 2287 | { |
| 2288 | static int xyPos = 100; |
| 2289 | if ( w->parent() ) { // create a subwindow |
| 2290 | Fl_Group::current(0); |
| 2291 | // our subwindow needs this structure to know about its clipping. |
| 2292 | Fl_X* x = new Fl_X; |
| 2293 | x->subwindow = true; |
| 2294 | x->other_xid = 0; |
| 2295 | x->region = 0; |
| 2296 | x->subRegion = 0; |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 2297 | x->cursor = NULL; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 2298 | x->gc = 0; // stay 0 for Quickdraw; fill with CGContext for Quartz |
| 2299 | Fl_Window *win = w->window(); |
| 2300 | Fl_X *xo = Fl_X::i(win); |
| 2301 | if (xo) { |
| 2302 | x->xidNext = xo->xidChildren; |
| 2303 | x->xidChildren = 0L; |
| 2304 | xo->xidChildren = x; |
| 2305 | x->xid = win->i->xid; |
| 2306 | x->w = w; w->i = x; |
| 2307 | x->wait_for_expose = 0; |
| 2308 | { |
| 2309 | Fl_X *z = xo->next; // we don't want a subwindow in Fl_X::first |
| 2310 | xo->next = x; |
| 2311 | x->next = z; |
| 2312 | } |
| 2313 | int old_event = Fl::e_number; |
| 2314 | w->handle(Fl::e_number = FL_SHOW); |
| 2315 | Fl::e_number = old_event; |
| 2316 | w->redraw(); // force draw to happen |
| 2317 | } |
| 2318 | if (w->as_gl_window()) { // if creating a sub-GL-window |
| 2319 | while (win->window()) win = win->window(); |
| 2320 | [(FLWindow*)Fl_X::i(win)->xid setContainsGLsubwindow:YES]; |
| 2321 | } |
| 2322 | fl_show_iconic = 0; |
| 2323 | } |
| 2324 | else { // create a desktop window |
| 2325 | Fl_Group::current(0); |
| 2326 | fl_open_display(); |
| 2327 | NSInteger winlevel = NSNormalWindowLevel; |
| 2328 | NSUInteger winstyle; |
| 2329 | if (w->border()) winstyle = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask; |
| 2330 | else winstyle = NSBorderlessWindowMask; |
| 2331 | int xp = w->x(); |
| 2332 | int yp = w->y(); |
| 2333 | int wp = w->w(); |
| 2334 | int hp = w->h(); |
| 2335 | if (w->size_range_set) { |
| 2336 | if ( w->minh != w->maxh || w->minw != w->maxw) { |
| 2337 | winstyle |= NSResizableWindowMask; |
| 2338 | } |
| 2339 | } else { |
| 2340 | if (w->resizable()) { |
| 2341 | Fl_Widget *o = w->resizable(); |
| 2342 | int minw = o->w(); if (minw > 100) minw = 100; |
| 2343 | int minh = o->h(); if (minh > 100) minh = 100; |
| 2344 | w->size_range(w->w() - o->w() + minw, w->h() - o->h() + minh, 0, 0); |
| 2345 | winstyle |= NSResizableWindowMask; |
| 2346 | } else { |
| 2347 | w->size_range(w->w(), w->h(), w->w(), w->h()); |
| 2348 | } |
| 2349 | } |
| 2350 | int xwm = xp, ywm = yp, bt, bx, by; |
| 2351 | |
| 2352 | if (!fake_X_wm(w, xwm, ywm, bt, bx, by)) { |
| 2353 | // menu windows and tooltips |
| 2354 | if (w->modal()||w->tooltip_window()) { |
| 2355 | winstyle = NSBorderlessWindowMask; |
| 2356 | winlevel = NSModalPanelWindowLevel; |
| 2357 | } else { |
| 2358 | winstyle = NSBorderlessWindowMask; |
| 2359 | } |
| 2360 | } else if (w->modal()) { |
| 2361 | winstyle &= ~NSMiniaturizableWindowMask; |
| 2362 | // winstyle &= ~(NSResizableWindowMask | NSMiniaturizableWindowMask); |
| 2363 | winlevel = NSModalPanelWindowLevel; |
| 2364 | } |
| 2365 | else if (w->non_modal()) { |
| 2366 | winlevel = NSFloatingWindowLevel; |
| 2367 | } |
| 2368 | |
| 2369 | if (by+bt) { |
| 2370 | wp += 2*bx; |
| 2371 | hp += 2*by+bt; |
| 2372 | } |
| 2373 | if (!(w->flags() & Fl_Window::FORCE_POSITION)) { |
| 2374 | // use the Carbon functions below for default window positioning |
| 2375 | w->x(xyPos+Fl::x()); |
| 2376 | w->y(xyPos+Fl::y()); |
| 2377 | xyPos += 25; |
| 2378 | if (xyPos>200) xyPos = 100; |
| 2379 | } else { |
| 2380 | if (!Fl::grab()) { |
| 2381 | xp = xwm; yp = ywm; |
| 2382 | w->x(xp);w->y(yp); |
| 2383 | } |
| 2384 | xp -= bx; |
| 2385 | yp -= by+bt; |
| 2386 | } |
| 2387 | |
| 2388 | if (w->non_modal() && Fl_X::first /*&& !fl_disable_transient_for*/) { |
| 2389 | // find some other window to be "transient for": |
| 2390 | Fl_Window* w = Fl_X::first->w; |
| 2391 | while (w->parent()) w = w->window(); // todo: this code does not make any sense! (w!=w??) |
| 2392 | } |
| 2393 | |
| 2394 | Fl_X* x = new Fl_X; |
| 2395 | x->subwindow = false; |
| 2396 | x->other_xid = 0; // room for doublebuffering image map. On OS X this is only used by overlay windows |
| 2397 | x->region = 0; |
| 2398 | x->subRegion = 0; |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 2399 | x->cursor = NULL; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 2400 | x->xidChildren = 0; |
| 2401 | x->xidNext = 0; |
| 2402 | x->gc = 0; |
| 2403 | |
| 2404 | NSRect srect = [[NSScreen mainScreen] frame]; |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 2405 | if (w->flags() & Fl_Widget::FULLSCREEN) { |
| 2406 | int sx, sy, sw, sh; |
| 2407 | Fl::screen_xywh(sx, sy, sw, sh, w->x(), w->y(), w->w(), w->h()); |
| 2408 | w->resize(sx, sy, sw, sh); |
| 2409 | winstyle = NSBorderlessWindowMask; |
| 2410 | winlevel = NSStatusWindowLevel; |
| 2411 | } |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 2412 | NSRect crect; |
| 2413 | crect.origin.x = w->x(); |
| 2414 | crect.origin.y = srect.size.height - (w->y() + w->h()); |
| 2415 | crect.size.width=w->w(); |
| 2416 | crect.size.height=w->h(); |
| 2417 | FLWindow *cw = [[FLWindow alloc] initWithFl_W:w |
| 2418 | contentRect:crect |
| 2419 | styleMask:winstyle]; |
| 2420 | [cw setHasShadow:YES]; |
| 2421 | [cw setAcceptsMouseMovedEvents:YES]; |
| 2422 | x->xid = cw; |
| 2423 | FLView *myview = [[FLView alloc] init]; |
| 2424 | [cw setContentView:myview]; |
| 2425 | [cw setLevel:winlevel]; |
| 2426 | |
| 2427 | q_set_window_title(cw, w->label(), w->iconlabel()); |
| 2428 | if (!(w->flags() & Fl_Window::FORCE_POSITION)) { |
| 2429 | if (w->modal()) { |
| 2430 | [cw center]; |
| 2431 | } else if (w->non_modal()) { |
| 2432 | [cw center]; |
| 2433 | } else { |
| 2434 | static NSPoint delta = NSZeroPoint; |
| 2435 | delta = [cw cascadeTopLeftFromPoint:delta]; |
| 2436 | } |
| 2437 | } |
| 2438 | if(w->menu_window()) { // make menu windows slightly transparent |
| 2439 | [cw setAlphaValue:0.97]; |
| 2440 | } |
| 2441 | x->w = w; w->i = x; |
| 2442 | x->wait_for_expose = 1; |
| 2443 | x->next = Fl_X::first; |
| 2444 | Fl_X::first = x; |
| 2445 | // Install DnD handlers |
| 2446 | [myview registerForDraggedTypes:[NSArray arrayWithObjects: |
| 2447 | NSStringPboardType, NSFilenamesPboardType, nil]]; |
| 2448 | if ( ! Fl_X::first->next ) { |
| 2449 | // if this is the first window, we need to bring the application to the front |
| 2450 | ProcessSerialNumber psn = { 0, kCurrentProcess }; |
| 2451 | SetFrontProcess( &psn ); |
| 2452 | } |
| 2453 | |
| 2454 | if (w->size_range_set) w->size_range_(); |
| 2455 | |
| 2456 | if ( w->border() || (!w->modal() && !w->tooltip_window()) ) { |
| 2457 | Fl_Tooltip::enter(0); |
| 2458 | } |
| 2459 | w->set_visible(); |
| 2460 | if ( w->border() || (!w->modal() && !w->tooltip_window()) ) Fl::handle(FL_FOCUS, w); |
| 2461 | Fl::first_window(w); |
| 2462 | [cw setDelegate:mydelegate]; |
| 2463 | if (fl_show_iconic) { |
| 2464 | fl_show_iconic = 0; |
| 2465 | [cw miniaturize:nil]; |
| 2466 | } else { |
| 2467 | [cw makeKeyAndOrderFront:nil]; |
| 2468 | } |
| 2469 | |
| 2470 | crect = [[cw contentView] frame]; |
| 2471 | w->w(int(crect.size.width)); |
| 2472 | w->h(int(crect.size.height)); |
| 2473 | crect = [cw frame]; |
| 2474 | w->x(int(crect.origin.x)); |
| 2475 | srect = [[cw screen] frame]; |
| 2476 | w->y(int(srect.size.height - (crect.origin.y + w->h()))); |
| 2477 | |
| 2478 | int old_event = Fl::e_number; |
| 2479 | w->handle(Fl::e_number = FL_SHOW); |
| 2480 | Fl::e_number = old_event; |
| 2481 | |
| 2482 | if (w->modal()) { Fl::modal_ = w; fl_fix_focus(); } |
| 2483 | } |
| 2484 | } |
| 2485 | |
| 2486 | |
| 2487 | /* |
| 2488 | * Tell the OS what window sizes we want to allow |
| 2489 | */ |
| 2490 | void Fl_Window::size_range_() { |
| 2491 | int bx, by, bt; |
| 2492 | get_window_frame_sizes(bx, by, bt); |
| 2493 | size_range_set = 1; |
| 2494 | NSSize minSize = { minw, minh + bt }; |
| 2495 | NSSize maxSize = { maxw?maxw:32000, maxh?maxh + bt:32000 }; |
| 2496 | if (i && i->xid) { |
| 2497 | [(NSWindow*)i->xid setMinSize:minSize]; |
| 2498 | [(NSWindow*)i->xid setMaxSize:maxSize]; |
| 2499 | } |
| 2500 | } |
| 2501 | |
| 2502 | |
| 2503 | /* |
| 2504 | * returns pointer to the filename, or null if name ends with ':' |
| 2505 | */ |
| 2506 | const char *fl_filename_name( const char *name ) |
| 2507 | { |
| 2508 | const char *p, *q; |
| 2509 | if (!name) return (0); |
| 2510 | for ( p = q = name ; *p ; ) { |
| 2511 | if ( ( p[0] == ':' ) && ( p[1] == ':' ) ) { |
| 2512 | q = p+2; |
| 2513 | p++; |
| 2514 | } |
| 2515 | else if (p[0] == '/') { |
| 2516 | q = p + 1; |
| 2517 | } |
| 2518 | p++; |
| 2519 | } |
| 2520 | return q; |
| 2521 | } |
| 2522 | |
| 2523 | |
| 2524 | /* |
| 2525 | * set the window title bar name |
| 2526 | */ |
| 2527 | void Fl_Window::label(const char *name, const char *mininame) { |
| 2528 | Fl_Widget::label(name); |
| 2529 | iconlabel_ = mininame; |
| 2530 | if (shown() || i) { |
| 2531 | NSWindow* nsw = (NSWindow*)i->xid; |
| 2532 | q_set_window_title(nsw, name, mininame); |
| 2533 | } |
| 2534 | } |
| 2535 | |
| 2536 | |
| 2537 | /* |
| 2538 | * make a window visible |
| 2539 | */ |
| 2540 | void Fl_Window::show() { |
| 2541 | image(Fl::scheme_bg_); |
| 2542 | if (Fl::scheme_bg_) { |
| 2543 | labeltype(FL_NORMAL_LABEL); |
| 2544 | align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE | FL_ALIGN_CLIP); |
| 2545 | } else { |
| 2546 | labeltype(FL_NO_LABEL); |
| 2547 | } |
| 2548 | Fl_Tooltip::exit(this); |
| 2549 | if (!shown() || !i) { |
| 2550 | Fl_X::make(this); |
| 2551 | } else { |
| 2552 | if ( !parent() ) { |
| 2553 | if ([(NSWindow*)i->xid isMiniaturized]) { |
| 2554 | i->w->redraw(); |
| 2555 | [(NSWindow*)i->xid deminiaturize:nil]; |
| 2556 | } |
| 2557 | if (!fl_capture) { |
| 2558 | [(NSWindow*)i->xid makeKeyAndOrderFront:nil]; |
| 2559 | } |
| 2560 | } |
| 2561 | } |
| 2562 | } |
| 2563 | |
| 2564 | |
| 2565 | /* |
| 2566 | * resize a window |
| 2567 | */ |
| 2568 | void Fl_Window::resize(int X,int Y,int W,int H) { |
| 2569 | if (W<=0) W = 1; // OS X does not like zero width windows |
| 2570 | if (H<=0) H = 1; |
| 2571 | int is_a_resize = (W != w() || H != h()); |
| 2572 | // printf("Fl_Window::resize(X=%d, Y=%d, W=%d, H=%d), is_a_resize=%d, resize_from_system=%p, this=%p\n", |
| 2573 | // X, Y, W, H, is_a_resize, resize_from_system, this); |
| 2574 | if (X != x() || Y != y()) set_flag(FORCE_POSITION); |
| 2575 | else if (!is_a_resize) return; |
| 2576 | if ( (resize_from_system!=this) && (!parent()) && shown()) { |
| 2577 | if (is_a_resize) { |
| 2578 | if (resizable()) { |
| 2579 | if (W<minw) minw = W; // user request for resize takes priority |
| 2580 | if (maxw && W>maxw) maxw = W; // over a previously set size_range |
| 2581 | if (H<minh) minh = H; |
| 2582 | if (maxh && H>maxh) maxh = H; |
| 2583 | size_range(minw, minh, maxw, maxh); |
| 2584 | } else { |
| 2585 | size_range(W, H, W, H); |
| 2586 | } |
| 2587 | int bx, by, bt; |
| 2588 | if ( ! this->border() ) bt = 0; |
| 2589 | else get_window_frame_sizes(bx, by, bt); |
| 2590 | NSRect dim; |
| 2591 | dim.origin.x = X; |
| 2592 | dim.origin.y = [[(NSWindow*)i->xid screen] frame].size.height - (Y + H); |
| 2593 | dim.size.width = W; |
| 2594 | dim.size.height = H + bt; |
Pierre Ossman | f5e59af | 2011-11-25 09:18:28 +0000 | [diff] [blame^] | 2595 | [(NSWindow*)i->xid setFrame:dim display:YES]; // calls windowDidResize |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 2596 | } else { |
| 2597 | NSPoint pt; |
| 2598 | pt.x = X; |
| 2599 | pt.y = [[(NSWindow*)i->xid screen] frame].size.height - (Y + h()); |
Pierre Ossman | f5e59af | 2011-11-25 09:18:28 +0000 | [diff] [blame^] | 2600 | [(NSWindow*)i->xid setFrameOrigin:pt]; // calls windowDidMove |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 2601 | } |
Pierre Ossman | dd321ad | 2011-11-21 14:04:29 +0000 | [diff] [blame] | 2602 | return; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 2603 | } |
| 2604 | resize_from_system = 0; |
| 2605 | if (is_a_resize) { |
| 2606 | Fl_Group::resize(X,Y,W,H); |
| 2607 | if (shown()) { |
| 2608 | redraw(); |
| 2609 | } |
| 2610 | } else { |
| 2611 | x(X); y(Y); |
| 2612 | } |
| 2613 | } |
| 2614 | |
| 2615 | |
| 2616 | /* |
| 2617 | * make all drawing go into this window (called by subclass flush() impl.) |
| 2618 | */ |
| 2619 | void Fl_Window::make_current() |
| 2620 | { |
| 2621 | Fl_X::q_release_context(); |
| 2622 | fl_window = i->xid; |
| 2623 | current_ = this; |
| 2624 | |
| 2625 | int xp = 0, yp = 0; |
| 2626 | Fl_Window *win = this; |
| 2627 | while ( win ) { |
| 2628 | if ( !win->window() ) |
| 2629 | break; |
| 2630 | xp += win->x(); |
| 2631 | yp += win->y(); |
| 2632 | win = (Fl_Window*)win->window(); |
| 2633 | } |
| 2634 | |
| 2635 | NSView *current_focus = [NSView focusView]; |
| 2636 | // sometimes current_focus is set to a non-FLTK view: don't touch that |
| 2637 | if ( [current_focus isKindOfClass:[FLView class]] ) [current_focus unlockFocus]; |
| 2638 | [[(NSWindow*)i->xid contentView] lockFocus]; |
| 2639 | i->gc = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; |
| 2640 | fl_gc = i->gc; |
| 2641 | Fl_Region fl_window_region = XRectangleRegion(0,0,w(),h()); |
| 2642 | if ( ! this->window() ) { |
| 2643 | for ( Fl_X *cx = i->xidChildren; cx; cx = cx->xidNext ) { // clip-out all sub-windows |
| 2644 | Fl_Window *cw = cx->w; |
| 2645 | Fl_Region from = fl_window_region; |
| 2646 | fl_window_region = MacRegionMinusRect(from, cw->x(), cw->y(), cw->w(), cw->h() ); |
| 2647 | XDestroyRegion(from); |
| 2648 | } |
| 2649 | } |
| 2650 | |
| 2651 | // antialiasing must be deactivated because it applies to rectangles too |
| 2652 | // and escapes even clipping!!! |
| 2653 | // it gets activated when needed (e.g., draw text) |
| 2654 | CGContextSetShouldAntialias(fl_gc, false); |
| 2655 | CGFloat hgt = [[(NSWindow*)fl_window contentView] frame].size.height; |
| 2656 | CGContextTranslateCTM(fl_gc, 0.5, hgt-0.5f); |
| 2657 | CGContextScaleCTM(fl_gc, 1.0f, -1.0f); // now 0,0 is top-left point of the window |
| 2658 | win = this; |
| 2659 | while(win && win->window()) { // translate to subwindow origin if this is a subwindow context |
| 2660 | CGContextTranslateCTM(fl_gc, win->x(), win->y()); |
| 2661 | win = win->window(); |
| 2662 | } |
| 2663 | //apply window's clip |
| 2664 | CGContextClipToRects(fl_gc, fl_window_region->rects, fl_window_region->count ); |
| 2665 | XDestroyRegion(fl_window_region); |
| 2666 | // this is the context with origin at top left of (sub)window clipped out of its subwindows if any |
| 2667 | CGContextSaveGState(fl_gc); |
| 2668 | #if defined(FLTK_USE_CAIRO) |
| 2669 | if (Fl::cairo_autolink_context()) Fl::cairo_make_current(this); // capture gc changes automatically to update the cairo context adequately |
| 2670 | #endif |
| 2671 | fl_clip_region( 0 ); |
| 2672 | |
| 2673 | #if defined(FLTK_USE_CAIRO) |
| 2674 | // update the cairo_t context |
| 2675 | if (Fl::cairo_autolink_context()) Fl::cairo_make_current(this); |
| 2676 | #endif |
| 2677 | } |
| 2678 | |
| 2679 | // helper function to manage the current CGContext fl_gc |
| 2680 | extern void fl_quartz_restore_line_style_(); |
| 2681 | |
| 2682 | // FLTK has only one global graphics state. This function copies the FLTK state into the |
| 2683 | // current Quartz context |
| 2684 | void Fl_X::q_fill_context() { |
| 2685 | if (!fl_gc) return; |
| 2686 | if ( ! fl_window) { // a bitmap context |
| 2687 | size_t hgt = CGBitmapContextGetHeight(fl_gc); |
| 2688 | CGContextTranslateCTM(fl_gc, 0.5, hgt-0.5f); |
| 2689 | CGContextScaleCTM(fl_gc, 1.0f, -1.0f); // now 0,0 is top-left point of the context |
| 2690 | } |
| 2691 | fl_color(fl_graphics_driver->color()); |
| 2692 | fl_quartz_restore_line_style_(); |
| 2693 | } |
| 2694 | |
| 2695 | // The only way to reset clipping to its original state is to pop the current graphics |
| 2696 | // state and restore the global state. |
| 2697 | void Fl_X::q_clear_clipping() { |
| 2698 | if (!fl_gc) return; |
| 2699 | CGContextRestoreGState(fl_gc); |
| 2700 | CGContextSaveGState(fl_gc); |
| 2701 | } |
| 2702 | |
| 2703 | // Give the Quartz context back to the system |
| 2704 | void Fl_X::q_release_context(Fl_X *x) { |
| 2705 | if (x && x->gc!=fl_gc) return; |
| 2706 | if (!fl_gc) return; |
| 2707 | CGContextRestoreGState(fl_gc); // matches the CGContextSaveGState of make_current |
| 2708 | fl_gc = 0; |
| 2709 | #if defined(FLTK_USE_CAIRO) |
| 2710 | if (Fl::cairo_autolink_context()) Fl::cairo_make_current((Fl_Window*) 0); // capture gc changes automatically to update the cairo context adequately |
| 2711 | #endif |
| 2712 | } |
| 2713 | |
| 2714 | void Fl_X::q_begin_image(CGRect &rect, int cx, int cy, int w, int h) { |
| 2715 | CGContextSaveGState(fl_gc); |
| 2716 | CGRect r2 = rect; |
| 2717 | r2.origin.x -= 0.5f; |
| 2718 | r2.origin.y -= 0.5f; |
| 2719 | CGContextClipToRect(fl_gc, r2); |
| 2720 | // move graphics context to origin of vertically reversed image |
| 2721 | CGContextTranslateCTM(fl_gc, rect.origin.x - cx - 0.5, rect.origin.y - cy + h - 0.5); |
| 2722 | CGContextScaleCTM(fl_gc, 1, -1); |
| 2723 | rect.origin.x = rect.origin.y = 0; |
| 2724 | rect.size.width = w; |
| 2725 | rect.size.height = h; |
| 2726 | } |
| 2727 | |
| 2728 | void Fl_X::q_end_image() { |
| 2729 | CGContextRestoreGState(fl_gc); |
| 2730 | } |
| 2731 | |
| 2732 | |
| 2733 | //////////////////////////////////////////////////////////////// |
| 2734 | // Copy & Paste fltk implementation. |
| 2735 | //////////////////////////////////////////////////////////////// |
| 2736 | |
| 2737 | static void convert_crlf(char * s, size_t len) |
| 2738 | { |
| 2739 | // turn all \r characters into \n: |
| 2740 | for (size_t x = 0; x < len; x++) if (s[x] == '\r') s[x] = '\n'; |
| 2741 | } |
| 2742 | |
| 2743 | // fltk 1.3 clipboard support constant definitions: |
| 2744 | const CFStringRef flavorNames[] = { |
| 2745 | CFSTR("public.utf16-plain-text"), |
| 2746 | CFSTR("public.utf8-plain-text"), |
| 2747 | CFSTR("com.apple.traditional-mac-plain-text") }; |
| 2748 | const CFStringEncoding encodings[] = { |
| 2749 | kCFStringEncodingUnicode, |
| 2750 | kCFStringEncodingUTF8, |
| 2751 | kCFStringEncodingMacRoman}; |
| 2752 | const size_t handledFlavorsCount = sizeof(encodings)/sizeof(CFStringEncoding); |
| 2753 | |
| 2754 | // clipboard variables definitions : |
| 2755 | char *fl_selection_buffer[2]; |
| 2756 | int fl_selection_length[2]; |
| 2757 | static int fl_selection_buffer_length[2]; |
| 2758 | |
| 2759 | static PasteboardRef myPasteboard = 0; |
| 2760 | static void allocatePasteboard() { |
| 2761 | if (!myPasteboard) |
| 2762 | PasteboardCreate(kPasteboardClipboard, &myPasteboard); |
| 2763 | } |
| 2764 | |
| 2765 | |
| 2766 | /* |
| 2767 | * create a selection |
| 2768 | * owner: widget that created the selection |
| 2769 | * stuff: pointer to selected data |
| 2770 | * size of selected data |
| 2771 | */ |
| 2772 | void Fl::copy(const char *stuff, int len, int clipboard) { |
| 2773 | if (!stuff || len<0) return; |
| 2774 | if (len+1 > fl_selection_buffer_length[clipboard]) { |
| 2775 | delete[] fl_selection_buffer[clipboard]; |
| 2776 | fl_selection_buffer[clipboard] = new char[len+100]; |
| 2777 | fl_selection_buffer_length[clipboard] = len+100; |
| 2778 | } |
| 2779 | memcpy(fl_selection_buffer[clipboard], stuff, len); |
| 2780 | fl_selection_buffer[clipboard][len] = 0; // needed for direct paste |
| 2781 | fl_selection_length[clipboard] = len; |
| 2782 | if (clipboard) { |
| 2783 | allocatePasteboard(); |
| 2784 | OSStatus err = PasteboardClear(myPasteboard); |
| 2785 | if (err!=noErr) return; // clear did not work, maybe not owner of clipboard. |
| 2786 | PasteboardSynchronize(myPasteboard); |
| 2787 | CFDataRef text = CFDataCreate(kCFAllocatorDefault, (UInt8*)fl_selection_buffer[1], len); |
| 2788 | if (text==NULL) return; // there was a pb creating the object, abort. |
| 2789 | err=PasteboardPutItemFlavor(myPasteboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), text, 0); |
| 2790 | CFRelease(text); |
| 2791 | } |
| 2792 | } |
| 2793 | |
| 2794 | // Call this when a "paste" operation happens: |
| 2795 | void Fl::paste(Fl_Widget &receiver, int clipboard) { |
| 2796 | if (clipboard) { |
| 2797 | // see if we own the selection, if not go get it: |
| 2798 | fl_selection_length[1] = 0; |
| 2799 | OSStatus err = noErr; |
| 2800 | Boolean found = false; |
| 2801 | CFDataRef flavorData = NULL; |
| 2802 | CFStringEncoding encoding = 0; |
| 2803 | |
| 2804 | allocatePasteboard(); |
| 2805 | PasteboardSynchronize(myPasteboard); |
| 2806 | ItemCount nFlavor = 0, i, j; |
| 2807 | err = PasteboardGetItemCount(myPasteboard, &nFlavor); |
| 2808 | if (err==noErr) { |
| 2809 | for (i=1; i<=nFlavor; i++) { |
| 2810 | PasteboardItemID itemID = 0; |
| 2811 | CFArrayRef flavorTypeArray = NULL; |
| 2812 | found = false; |
| 2813 | err = PasteboardGetItemIdentifier(myPasteboard, i, &itemID); |
| 2814 | if (err!=noErr) continue; |
| 2815 | err = PasteboardCopyItemFlavors(myPasteboard, itemID, &flavorTypeArray); |
| 2816 | if (err!=noErr) { |
| 2817 | if (flavorTypeArray) {CFRelease(flavorTypeArray); flavorTypeArray = NULL;} |
| 2818 | continue; |
| 2819 | } |
| 2820 | CFIndex flavorCount = CFArrayGetCount(flavorTypeArray); |
| 2821 | for (j = 0; j < handledFlavorsCount; j++) { |
| 2822 | for (CFIndex flavorIndex=0; flavorIndex<flavorCount; flavorIndex++) { |
| 2823 | CFStringRef flavorType = (CFStringRef)CFArrayGetValueAtIndex(flavorTypeArray, flavorIndex); |
| 2824 | if (UTTypeConformsTo(flavorType, flavorNames[j])) { |
| 2825 | err = PasteboardCopyItemFlavorData( myPasteboard, itemID, flavorNames[j], &flavorData ); |
| 2826 | if (err != noErr) continue; |
| 2827 | encoding = encodings[j]; |
| 2828 | found = true; |
| 2829 | break; |
| 2830 | } |
| 2831 | } |
| 2832 | if (found) break; |
| 2833 | } |
| 2834 | if (flavorTypeArray) {CFRelease(flavorTypeArray); flavorTypeArray = NULL;} |
| 2835 | if (found) break; |
| 2836 | } |
| 2837 | if (found) { |
| 2838 | CFIndex len = CFDataGetLength(flavorData); |
| 2839 | CFStringRef mycfs = CFStringCreateWithBytes(NULL, CFDataGetBytePtr(flavorData), len, encoding, false); |
| 2840 | CFRelease(flavorData); |
| 2841 | len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(mycfs), kCFStringEncodingUTF8) + 1; |
| 2842 | if ( len >= fl_selection_buffer_length[1] ) { |
| 2843 | fl_selection_buffer_length[1] = len; |
| 2844 | delete[] fl_selection_buffer[1]; |
| 2845 | fl_selection_buffer[1] = new char[len]; |
| 2846 | } |
| 2847 | CFStringGetCString(mycfs, fl_selection_buffer[1], len, kCFStringEncodingUTF8); |
| 2848 | CFRelease(mycfs); |
| 2849 | len = strlen(fl_selection_buffer[1]); |
| 2850 | fl_selection_length[1] = len; |
| 2851 | convert_crlf(fl_selection_buffer[1],len); // turn all \r characters into \n: |
| 2852 | } |
| 2853 | } |
| 2854 | } |
| 2855 | Fl::e_text = fl_selection_buffer[clipboard]; |
| 2856 | Fl::e_length = fl_selection_length[clipboard]; |
| 2857 | if (!Fl::e_text) Fl::e_text = (char *)""; |
| 2858 | receiver.handle(FL_PASTE); |
| 2859 | } |
| 2860 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 2861 | extern void fl_trigger_clipboard_notify(int source); |
| 2862 | |
| 2863 | void fl_clipboard_notify_change() { |
| 2864 | // No need to do anything here... |
| 2865 | } |
| 2866 | |
| 2867 | static void clipboard_check(void) |
| 2868 | { |
| 2869 | PasteboardSyncFlags flags; |
| 2870 | |
| 2871 | allocatePasteboard(); |
| 2872 | flags = PasteboardSynchronize(myPasteboard); |
| 2873 | |
| 2874 | if (!(flags & kPasteboardModified)) |
| 2875 | return; |
| 2876 | if (flags & kPasteboardClientIsOwner) |
| 2877 | return; |
| 2878 | |
| 2879 | fl_trigger_clipboard_notify(1); |
| 2880 | } |
| 2881 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 2882 | void Fl::add_timeout(double time, Fl_Timeout_Handler cb, void* data) |
| 2883 | { |
| 2884 | // check, if this timer slot exists already |
| 2885 | for (int i = 0; i < mac_timer_used; ++i) { |
| 2886 | MacTimeout& t = mac_timers[i]; |
| 2887 | // if so, simply change the fire interval |
| 2888 | if (t.callback == cb && t.data == data) { |
| 2889 | CFRunLoopTimerSetNextFireDate(t.timer, CFAbsoluteTimeGetCurrent() + time ); |
| 2890 | t.pending = 1; |
| 2891 | return; |
| 2892 | } |
| 2893 | } |
| 2894 | // no existing timer to use. Create a new one: |
| 2895 | int timer_id = -1; |
| 2896 | // find an empty slot in the timer array |
| 2897 | for (int i = 0; i < mac_timer_used; ++i) { |
| 2898 | if ( !mac_timers[i].timer ) { |
| 2899 | timer_id = i; |
| 2900 | break; |
| 2901 | } |
| 2902 | } |
| 2903 | // if there was no empty slot, append a new timer |
| 2904 | if (timer_id == -1) { |
| 2905 | // make space if needed |
| 2906 | if (mac_timer_used == mac_timer_alloc) { |
| 2907 | realloc_timers(); |
| 2908 | } |
| 2909 | timer_id = mac_timer_used++; |
| 2910 | } |
| 2911 | // now install a brand new timer |
| 2912 | MacTimeout& t = mac_timers[timer_id]; |
| 2913 | CFRunLoopTimerContext context = {0, data, NULL,NULL,NULL}; |
| 2914 | CFRunLoopTimerRef timerRef = CFRunLoopTimerCreate(kCFAllocatorDefault, |
| 2915 | CFAbsoluteTimeGetCurrent() + time, |
| 2916 | 1E30, |
| 2917 | 0, |
| 2918 | 0, |
| 2919 | do_timer, |
| 2920 | &context |
| 2921 | ); |
| 2922 | if (timerRef) { |
| 2923 | CFRunLoopAddTimer(CFRunLoopGetCurrent(), |
| 2924 | timerRef, |
| 2925 | kCFRunLoopDefaultMode); |
| 2926 | t.callback = cb; |
| 2927 | t.data = data; |
| 2928 | t.timer = timerRef; |
| 2929 | t.pending = 1; |
| 2930 | } |
| 2931 | } |
| 2932 | |
| 2933 | void Fl::repeat_timeout(double time, Fl_Timeout_Handler cb, void* data) |
| 2934 | { |
| 2935 | // currently, repeat_timeout does not subtract the trigger time of the previous timer event as it should. |
| 2936 | add_timeout(time, cb, data); |
| 2937 | } |
| 2938 | |
| 2939 | int Fl::has_timeout(Fl_Timeout_Handler cb, void* data) |
| 2940 | { |
| 2941 | for (int i = 0; i < mac_timer_used; ++i) { |
| 2942 | MacTimeout& t = mac_timers[i]; |
| 2943 | if (t.callback == cb && t.data == data && t.pending) { |
| 2944 | return 1; |
| 2945 | } |
| 2946 | } |
| 2947 | return 0; |
| 2948 | } |
| 2949 | |
| 2950 | void Fl::remove_timeout(Fl_Timeout_Handler cb, void* data) |
| 2951 | { |
| 2952 | for (int i = 0; i < mac_timer_used; ++i) { |
| 2953 | MacTimeout& t = mac_timers[i]; |
| 2954 | if (t.callback == cb && ( t.data == data || data == NULL)) { |
| 2955 | delete_timer(t); |
| 2956 | } |
| 2957 | } |
| 2958 | } |
| 2959 | |
| 2960 | int Fl_X::unlink(Fl_X *start) { |
| 2961 | if (start) { |
| 2962 | Fl_X *pc = start; |
| 2963 | while (pc) { |
| 2964 | if (pc->xidNext == this) { |
| 2965 | pc->xidNext = xidNext; |
| 2966 | return 1; |
| 2967 | } |
| 2968 | if (pc->xidChildren) { |
| 2969 | if (pc->xidChildren == this) { |
| 2970 | pc->xidChildren = xidNext; |
| 2971 | return 1; |
| 2972 | } |
| 2973 | if (unlink(pc->xidChildren)) |
| 2974 | return 1; |
| 2975 | } |
| 2976 | pc = pc->xidNext; |
| 2977 | } |
| 2978 | } else { |
| 2979 | for ( Fl_X *pc = Fl_X::first; pc; pc = pc->next ) { |
| 2980 | if (unlink(pc)) |
| 2981 | return 1; |
| 2982 | } |
| 2983 | } |
| 2984 | return 0; |
| 2985 | } |
| 2986 | |
| 2987 | void Fl_X::relink(Fl_Window *w, Fl_Window *wp) { |
| 2988 | Fl_X *x = Fl_X::i(w); |
| 2989 | Fl_X *p = Fl_X::i(wp); |
| 2990 | if (!x || !p) return; |
| 2991 | // first, check if 'x' is already registered as a child of 'p' |
| 2992 | for (Fl_X *i = p->xidChildren; i; i=i->xidNext) { |
| 2993 | if (i == x) return; |
| 2994 | } |
| 2995 | // now add 'x' as the first child of 'p' |
| 2996 | x->xidNext = p->xidChildren; |
| 2997 | p->xidChildren = x; |
| 2998 | } |
| 2999 | |
| 3000 | void Fl_X::destroy() { |
| 3001 | // subwindows share their xid with their parent window, so should not close it |
| 3002 | if (!subwindow && w && !w->parent() && xid) { |
| 3003 | [[(NSWindow *)xid contentView] release]; |
| 3004 | [(NSWindow *)xid close]; |
| 3005 | } |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 3006 | if (cursor) { |
| 3007 | [(NSCursor*)cursor release]; |
| 3008 | cursor = NULL; |
| 3009 | } |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 3010 | } |
| 3011 | |
| 3012 | void Fl_X::map() { |
| 3013 | if (w && xid) { |
| 3014 | [(NSWindow *)xid orderFront:nil]; |
| 3015 | } |
| 3016 | //+ link to window list |
| 3017 | if (w && w->parent()) { |
| 3018 | Fl_X::relink(w, w->window() ); |
| 3019 | w->redraw(); |
| 3020 | } |
| 3021 | } |
| 3022 | |
| 3023 | void Fl_X::unmap() { |
| 3024 | if (w && !w->parent() && xid) { |
| 3025 | [(NSWindow *)xid orderOut:nil]; |
| 3026 | } |
| 3027 | if (w && Fl_X::i(w)) |
| 3028 | Fl_X::i(w)->unlink(); |
| 3029 | } |
| 3030 | |
| 3031 | |
| 3032 | // removes x,y,w,h rectangle from region r and returns result as a new Fl_Region |
| 3033 | static Fl_Region MacRegionMinusRect(Fl_Region r, int x,int y,int w,int h) |
| 3034 | { |
| 3035 | Fl_Region outr = (Fl_Region)malloc(sizeof(*outr)); |
| 3036 | outr->rects = (CGRect*)malloc(4 * r->count * sizeof(CGRect)); |
| 3037 | outr->count = 0; |
| 3038 | CGRect rect = fl_cgrectmake_cocoa(x, y, w, h); |
| 3039 | for( int i = 0; i < r->count; i++) { |
| 3040 | CGRect A = r->rects[i]; |
| 3041 | CGRect test = CGRectIntersection(A, rect); |
| 3042 | if (CGRectIsEmpty(test)) { |
| 3043 | outr->rects[(outr->count)++] = A; |
| 3044 | } |
| 3045 | else { |
| 3046 | const CGFloat verylarge = 100000.; |
| 3047 | CGRect side = CGRectMake(0,0,rect.origin.x,verylarge);// W side |
| 3048 | test = CGRectIntersection(A, side); |
| 3049 | if ( ! CGRectIsEmpty(test)) { |
| 3050 | outr->rects[(outr->count)++] = test; |
| 3051 | } |
| 3052 | side = CGRectMake(0,rect.origin.y + rect.size.height,verylarge,verylarge);// N side |
| 3053 | test = CGRectIntersection(A, side); |
| 3054 | if ( ! CGRectIsEmpty(test)) { |
| 3055 | outr->rects[(outr->count)++] = test; |
| 3056 | } |
| 3057 | side = CGRectMake(rect.origin.x + rect.size.width, 0, verylarge, verylarge);// E side |
| 3058 | test = CGRectIntersection(A, side); |
| 3059 | if ( ! CGRectIsEmpty(test)) { |
| 3060 | outr->rects[(outr->count)++] = test; |
| 3061 | } |
| 3062 | side = CGRectMake(0, 0, verylarge, rect.origin.y);// S side |
| 3063 | test = CGRectIntersection(A, side); |
| 3064 | if ( ! CGRectIsEmpty(test)) { |
| 3065 | outr->rects[(outr->count)++] = test; |
| 3066 | } |
| 3067 | } |
| 3068 | } |
| 3069 | if (outr->count == 0) { |
| 3070 | free(outr->rects); |
| 3071 | free(outr); |
| 3072 | outr = XRectangleRegion(0,0,0,0); |
| 3073 | } |
| 3074 | else outr->rects = (CGRect*)realloc(outr->rects, outr->count * sizeof(CGRect)); |
| 3075 | return outr; |
| 3076 | } |
| 3077 | |
| 3078 | // intersects current and x,y,w,h rectangle and returns result as a new Fl_Region |
| 3079 | Fl_Region Fl_X::intersect_region_and_rect(Fl_Region current, int x,int y,int w, int h) |
| 3080 | { |
| 3081 | if (current == NULL) return XRectangleRegion(x,y,w,h); |
| 3082 | CGRect r = fl_cgrectmake_cocoa(x, y, w, h); |
| 3083 | Fl_Region outr = (Fl_Region)malloc(sizeof(*outr)); |
| 3084 | outr->count = current->count; |
| 3085 | outr->rects =(CGRect*)malloc(outr->count * sizeof(CGRect)); |
| 3086 | int j = 0; |
| 3087 | for(int i = 0; i < current->count; i++) { |
| 3088 | CGRect test = CGRectIntersection(current->rects[i], r); |
| 3089 | if (!CGRectIsEmpty(test)) outr->rects[j++] = test; |
| 3090 | } |
| 3091 | if (j) { |
| 3092 | outr->count = j; |
| 3093 | outr->rects = (CGRect*)realloc(outr->rects, outr->count * sizeof(CGRect)); |
| 3094 | } |
| 3095 | else { |
| 3096 | XDestroyRegion(outr); |
| 3097 | outr = XRectangleRegion(0,0,0,0); |
| 3098 | } |
| 3099 | return outr; |
| 3100 | } |
| 3101 | |
| 3102 | void Fl_X::collapse() { |
| 3103 | [(NSWindow *)xid miniaturize:nil]; |
| 3104 | } |
| 3105 | |
| 3106 | static NSImage *CGBitmapContextToNSImage(CGContextRef c) |
| 3107 | // the returned NSImage is autoreleased |
| 3108 | { |
| 3109 | unsigned char *pdata = (unsigned char *)CGBitmapContextGetData(c); |
| 3110 | NSBitmapImageRep *imagerep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&pdata |
| 3111 | pixelsWide:CGBitmapContextGetWidth(c) |
| 3112 | pixelsHigh:CGBitmapContextGetHeight(c) |
| 3113 | bitsPerSample:8 |
| 3114 | samplesPerPixel:4 |
| 3115 | hasAlpha:YES |
| 3116 | isPlanar:NO |
| 3117 | colorSpaceName:NSDeviceRGBColorSpace |
| 3118 | bytesPerRow:CGBitmapContextGetBytesPerRow(c) |
| 3119 | bitsPerPixel:CGBitmapContextGetBitsPerPixel(c)]; |
| 3120 | NSImage* image = [[NSImage alloc] initWithData: [imagerep TIFFRepresentation]]; |
| 3121 | [imagerep release]; |
| 3122 | return [image autorelease]; |
| 3123 | } |
| 3124 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 3125 | int Fl_X::set_cursor(Fl_Cursor c) |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 3126 | { |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 3127 | if (cursor) { |
| 3128 | [(NSCursor*)cursor release]; |
| 3129 | cursor = NULL; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 3130 | } |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 3131 | |
| 3132 | switch (c) { |
| 3133 | case FL_CURSOR_ARROW: cursor = [NSCursor arrowCursor]; break; |
| 3134 | case FL_CURSOR_CROSS: cursor = [NSCursor crosshairCursor]; break; |
| 3135 | case FL_CURSOR_INSERT: cursor = [NSCursor IBeamCursor]; break; |
| 3136 | case FL_CURSOR_HAND: cursor = [NSCursor pointingHandCursor]; break; |
| 3137 | case FL_CURSOR_MOVE: cursor = [NSCursor openHandCursor]; break; |
| 3138 | case FL_CURSOR_NS: cursor = [NSCursor resizeUpDownCursor]; break; |
| 3139 | case FL_CURSOR_WE: cursor = [NSCursor resizeLeftRightCursor]; break; |
| 3140 | case FL_CURSOR_N: cursor = [NSCursor resizeUpCursor]; break; |
| 3141 | case FL_CURSOR_E: cursor = [NSCursor resizeRightCursor]; break; |
| 3142 | case FL_CURSOR_W: cursor = [NSCursor resizeLeftCursor]; break; |
| 3143 | case FL_CURSOR_S: cursor = [NSCursor resizeDownCursor]; break; |
| 3144 | default: |
| 3145 | return 0; |
| 3146 | } |
| 3147 | |
| 3148 | [(NSCursor*)cursor retain]; |
| 3149 | |
| 3150 | [(NSWindow*)xid invalidateCursorRectsForView:[(NSWindow*)xid contentView]]; |
| 3151 | |
| 3152 | return 1; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 3153 | } |
| 3154 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 3155 | int Fl_X::set_cursor(const Fl_RGB_Image *image, int hotx, int hoty) { |
| 3156 | if (cursor) { |
| 3157 | [(NSCursor*)cursor release]; |
| 3158 | cursor = NULL; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 3159 | } |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 3160 | |
| 3161 | if ((hotx < 0) || (hotx >= image->w())) |
| 3162 | return 0; |
| 3163 | if ((hoty < 0) || (hoty >= image->h())) |
| 3164 | return 0; |
| 3165 | |
| 3166 | // OS X >= 10.6 can create a NSImage from a CGImage, but we need to |
| 3167 | // support older versions, hence this pesky handling. |
| 3168 | |
| 3169 | NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] |
| 3170 | initWithBitmapDataPlanes:NULL |
| 3171 | pixelsWide:image->w() |
| 3172 | pixelsHigh:image->h() |
| 3173 | bitsPerSample:8 |
| 3174 | samplesPerPixel:image->d() |
| 3175 | hasAlpha:!(image->d() & 1) |
| 3176 | isPlanar:NO |
| 3177 | colorSpaceName:(image->d()<=2) ? NSDeviceWhiteColorSpace : NSDeviceRGBColorSpace |
| 3178 | bytesPerRow:(image->w() * image->d()) |
| 3179 | bitsPerPixel:(image->d()*8)]; |
| 3180 | |
| 3181 | // Alpha needs to be premultiplied for this format |
| 3182 | |
| 3183 | const uchar *i = (const uchar*)*image->data(); |
| 3184 | unsigned char *o = [bitmap bitmapData]; |
| 3185 | for (int y = 0;y < image->h();y++) { |
| 3186 | if (image->d() & 1) { |
| 3187 | for (int x = 0;x < image->w();x++) { |
| 3188 | unsigned int alpha; |
| 3189 | if (image->d() == 4) { |
| 3190 | alpha = i[3]; |
| 3191 | *o++ = (unsigned char)((unsigned int)*i++ * alpha / 255); |
| 3192 | *o++ = (unsigned char)((unsigned int)*i++ * alpha / 255); |
| 3193 | } |
| 3194 | |
| 3195 | alpha = i[1]; |
| 3196 | *o++ = (unsigned char)((unsigned int)*i++ * alpha / 255); |
| 3197 | *o++ = alpha; |
| 3198 | i++; |
| 3199 | } |
| 3200 | } else { |
| 3201 | // No alpha, so we can just copy everything directly. |
| 3202 | int len = image->w() * image->d(); |
| 3203 | memcpy(o, i, len); |
| 3204 | o += len; |
| 3205 | i += len; |
| 3206 | } |
| 3207 | i += image->ld(); |
| 3208 | } |
| 3209 | |
| 3210 | NSImage *nsimage = [[NSImage alloc] |
| 3211 | initWithSize:NSMakeSize(image->w(), image->h())]; |
| 3212 | |
| 3213 | [nsimage addRepresentation:bitmap]; |
| 3214 | |
| 3215 | cursor = [[NSCursor alloc] |
| 3216 | initWithImage:nsimage |
| 3217 | hotSpot:NSMakePoint(hotx, hoty)]; |
| 3218 | |
| 3219 | [(NSWindow*)xid invalidateCursorRectsForView:[(NSWindow*)xid contentView]]; |
| 3220 | |
| 3221 | [bitmap release]; |
| 3222 | [nsimage release]; |
| 3223 | |
| 3224 | return 1; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 3225 | } |
| 3226 | |
| 3227 | @interface FLaboutItemTarget : NSObject |
| 3228 | { |
| 3229 | } |
| 3230 | - (void)showPanel; |
| 3231 | - (void)printPanel; |
| 3232 | @end |
| 3233 | @implementation FLaboutItemTarget |
| 3234 | - (void)showPanel |
| 3235 | { |
Henrik Andersson | 485f419 | 2011-09-16 11:51:32 +0000 | [diff] [blame] | 3236 | if ((Fl_Mac_App_Menu::copyright == NULL) || |
| 3237 | (strlen(Fl_Mac_App_Menu::copyright) > 0)) { |
| 3238 | NSString *copyright; |
| 3239 | |
| 3240 | if (Fl_Mac_App_Menu::copyright == NULL) |
| 3241 | copyright = [NSString stringWithFormat:@" GUI with FLTK %d.%d", |
| 3242 | FL_MAJOR_VERSION, FL_MINOR_VERSION ]; |
| 3243 | else |
| 3244 | copyright = [NSString stringWithUTF8String:Fl_Mac_App_Menu::copyright]; |
| 3245 | |
| 3246 | NSDictionary *options; |
| 3247 | options = [NSDictionary dictionaryWithObjectsAndKeys: |
| 3248 | copyright, @"Copyright", |
| 3249 | nil]; |
| 3250 | |
| 3251 | [NSApp orderFrontStandardAboutPanelWithOptions:options]; |
| 3252 | } else { |
| 3253 | [NSApp orderFrontStandardAboutPanelWithOptions:nil]; |
| 3254 | } |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 3255 | } |
| 3256 | //#include <FL/Fl_PostScript.H> |
| 3257 | - (void)printPanel |
| 3258 | { |
| 3259 | Fl_Printer printer; |
| 3260 | //Fl_PostScript_File_Device printer; |
| 3261 | int w, h, ww, wh; |
| 3262 | Fl_Window *win = Fl::first_window(); |
| 3263 | if(!win) return; |
| 3264 | if( printer.start_job(1) ) return; |
| 3265 | if( printer.start_page() ) return; |
| 3266 | // scale the printer device so that the window fits on the page |
| 3267 | float scale = 1; |
| 3268 | printer.printable_rect(&w, &h); |
| 3269 | ww = win->decorated_w(); |
| 3270 | wh = win->decorated_h(); |
| 3271 | if (ww>w || wh>h) { |
| 3272 | scale = (float)w/win->w(); |
| 3273 | if ((float)h/wh < scale) scale = (float)h/wh; |
| 3274 | printer.scale(scale); |
| 3275 | } |
| 3276 | //#define ROTATE 1 |
| 3277 | #ifdef ROTATE |
| 3278 | printer.scale(scale * 0.8, scale * 0.8); |
| 3279 | printer.printable_rect(&w, &h); |
| 3280 | printer.origin(w/2, h/2 ); |
| 3281 | printer.rotate(20.); |
| 3282 | printer.print_widget( win, - win->w()/2, - win->h()/2 ); |
| 3283 | #else |
| 3284 | printer.print_window(win); |
| 3285 | #endif |
| 3286 | printer.end_page(); |
| 3287 | printer.end_job(); |
| 3288 | } |
| 3289 | @end |
| 3290 | |
| 3291 | static void createAppleMenu(void) |
| 3292 | { |
| 3293 | static BOOL donethat = NO; |
| 3294 | if (donethat) return; |
| 3295 | donethat = YES; |
| 3296 | NSMenu *mainmenu, *services, *appleMenu; |
| 3297 | NSMenuItem *menuItem; |
| 3298 | NSString *title; |
| 3299 | |
| 3300 | NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"]; |
| 3301 | if (nsappname == nil) |
| 3302 | nsappname = [[NSProcessInfo processInfo] processName]; |
| 3303 | appleMenu = [[NSMenu alloc] initWithTitle:@""]; |
| 3304 | /* Add menu items */ |
| 3305 | title = [[NSString stringWithUTF8String:Fl_Mac_App_Menu::about] stringByAppendingString:nsappname]; |
| 3306 | menuItem = [appleMenu addItemWithTitle:title action:@selector(showPanel) keyEquivalent:@""]; |
| 3307 | FLaboutItemTarget *about = [[FLaboutItemTarget alloc] init]; |
| 3308 | [menuItem setTarget:about]; |
| 3309 | [appleMenu addItem:[NSMenuItem separatorItem]]; |
| 3310 | // Print front window |
| 3311 | if (strlen(Fl_Mac_App_Menu::print) > 0) { |
| 3312 | menuItem = [appleMenu |
| 3313 | addItemWithTitle:[NSString stringWithUTF8String:Fl_Mac_App_Menu::print] |
| 3314 | action:@selector(printPanel) |
| 3315 | keyEquivalent:@""]; |
| 3316 | [menuItem setTarget:about]; |
| 3317 | [appleMenu setAutoenablesItems:NO]; |
| 3318 | [menuItem setEnabled:YES]; |
| 3319 | [appleMenu addItem:[NSMenuItem separatorItem]]; |
| 3320 | } |
| 3321 | // Services Menu |
| 3322 | services = [[NSMenu alloc] init]; |
| 3323 | menuItem = [appleMenu |
| 3324 | addItemWithTitle:[NSString stringWithUTF8String:Fl_Mac_App_Menu::services] |
| 3325 | action:nil |
| 3326 | keyEquivalent:@""]; |
| 3327 | [appleMenu setSubmenu:services forItem:menuItem]; |
| 3328 | [appleMenu addItem:[NSMenuItem separatorItem]]; |
| 3329 | // Hide AppName |
| 3330 | title = [[NSString stringWithUTF8String:Fl_Mac_App_Menu::hide] stringByAppendingString:nsappname]; |
| 3331 | [appleMenu addItemWithTitle:title |
| 3332 | action:@selector(hide:) |
| 3333 | keyEquivalent:@"h"]; |
| 3334 | // Hide Others |
| 3335 | menuItem = [appleMenu |
| 3336 | addItemWithTitle:[NSString stringWithUTF8String:Fl_Mac_App_Menu::hide_others] |
| 3337 | action:@selector(hideOtherApplications:) |
| 3338 | keyEquivalent:@"h"]; |
| 3339 | [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; |
| 3340 | // Show All |
| 3341 | [appleMenu addItemWithTitle:[NSString stringWithUTF8String:Fl_Mac_App_Menu::show] |
| 3342 | action:@selector(unhideAllApplications:) keyEquivalent:@""]; |
| 3343 | [appleMenu addItem:[NSMenuItem separatorItem]]; |
| 3344 | // Quit AppName |
| 3345 | title = [[NSString stringWithUTF8String:Fl_Mac_App_Menu::quit] |
| 3346 | stringByAppendingString:nsappname]; |
| 3347 | [appleMenu addItemWithTitle:title |
| 3348 | action:@selector(terminate:) |
| 3349 | keyEquivalent:@"q"]; |
| 3350 | /* Put menu into the menubar */ |
| 3351 | menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; |
| 3352 | [menuItem setSubmenu:appleMenu]; |
| 3353 | mainmenu = [[NSMenu alloc] initWithTitle:@""]; |
| 3354 | [mainmenu addItem:menuItem]; |
| 3355 | if (fl_mac_os_version < 100600) { |
| 3356 | // [NSApp setAppleMenu:appleMenu]; |
| 3357 | // to avoid compiler warning raised by use of undocumented setAppleMenu : |
| 3358 | [NSApp performSelector:@selector(setAppleMenu:) withObject:appleMenu]; |
| 3359 | } |
| 3360 | [NSApp setServicesMenu:services]; |
| 3361 | [NSApp setMainMenu:mainmenu]; |
| 3362 | [services release]; |
| 3363 | [mainmenu release]; |
| 3364 | [appleMenu release]; |
| 3365 | [menuItem release]; |
| 3366 | } |
| 3367 | |
| 3368 | @interface FLMenuItem : NSMenuItem { |
| 3369 | } |
| 3370 | - (void) doCallback:(id)unused; |
| 3371 | - (void) directCallback:(id)unused; |
| 3372 | @end |
| 3373 | @implementation FLMenuItem |
| 3374 | - (void) doCallback:(id)unused |
| 3375 | { |
| 3376 | int flRank = [self tag]; |
| 3377 | const Fl_Menu_Item *items = fl_sys_menu_bar->Fl_Menu_::menu(); |
| 3378 | const Fl_Menu_Item *item = items + flRank; |
| 3379 | if (item) { |
| 3380 | fl_sys_menu_bar->picked(item); |
| 3381 | if ( item->flags & FL_MENU_TOGGLE ) { // update the menu toggle symbol |
| 3382 | [self setState:(item->value() ? NSOnState : NSOffState)]; |
| 3383 | } |
| 3384 | else if ( item->flags & FL_MENU_RADIO ) { // update the menu radio symbols |
| 3385 | int from = flRank; |
| 3386 | while( from > 0 && items[from - 1].label() && (items[from - 1].flags & FL_MENU_RADIO) && |
| 3387 | !(items[from - 1].flags & FL_MENU_DIVIDER) ) { |
| 3388 | from--; |
| 3389 | } |
| 3390 | int to = flRank; |
| 3391 | while( !(items[to].flags & FL_MENU_DIVIDER) && items[to + 1].label() && |
| 3392 | (items[to + 1].flags & FL_MENU_RADIO) ) { |
| 3393 | to++; |
| 3394 | } |
| 3395 | NSMenu *nsmenu = [self menu]; |
| 3396 | int nsrank = (int)[nsmenu indexOfItem:self]; |
| 3397 | for(int i = from - flRank + nsrank ; i <= to - flRank + nsrank; i++) { |
| 3398 | NSMenuItem *nsitem = [nsmenu itemAtIndex:i]; |
| 3399 | if (nsitem != self) [nsitem setState:NSOffState]; |
| 3400 | else [nsitem setState:(item->value() ? NSOnState : NSOffState) ]; |
| 3401 | } |
| 3402 | } |
| 3403 | } |
| 3404 | } |
| 3405 | - (void) directCallback:(id)unused |
| 3406 | { |
| 3407 | Fl_Menu_Item *item = (Fl_Menu_Item *)[(NSData*)[self representedObject] bytes]; |
| 3408 | if ( item && item->callback() ) item->do_callback(NULL); |
| 3409 | } |
| 3410 | @end |
| 3411 | |
| 3412 | void fl_mac_set_about( Fl_Callback *cb, void *user_data, int shortcut) |
| 3413 | { |
| 3414 | fl_open_display(); |
| 3415 | Fl_Menu_Item aboutItem; |
| 3416 | memset(&aboutItem, 0, sizeof(Fl_Menu_Item)); |
| 3417 | aboutItem.callback(cb); |
| 3418 | aboutItem.user_data(user_data); |
| 3419 | aboutItem.shortcut(shortcut); |
| 3420 | NSMenu *appleMenu = [[[NSApp mainMenu] itemAtIndex:0] submenu]; |
| 3421 | CFStringRef cfname = CFStringCreateCopy(NULL, (CFStringRef)[[appleMenu itemAtIndex:0] title]); |
| 3422 | [appleMenu removeItemAtIndex:0]; |
| 3423 | FLMenuItem *item = [[[FLMenuItem alloc] initWithTitle:(NSString*)cfname |
| 3424 | action:@selector(directCallback:) |
| 3425 | keyEquivalent:@""] autorelease]; |
| 3426 | if (aboutItem.shortcut()) { |
| 3427 | Fl_Sys_Menu_Bar::doMenuOrItemOperation(Fl_Sys_Menu_Bar::setKeyEquivalent, item, aboutItem.shortcut() & 0xff); |
| 3428 | Fl_Sys_Menu_Bar::doMenuOrItemOperation(Fl_Sys_Menu_Bar::setKeyEquivalentModifierMask, item, aboutItem.shortcut() ); |
| 3429 | } |
| 3430 | NSData *pointer = [NSData dataWithBytes:&aboutItem length:sizeof(Fl_Menu_Item)]; |
| 3431 | [item setRepresentedObject:pointer]; |
| 3432 | [appleMenu insertItem:item atIndex:0]; |
| 3433 | CFRelease(cfname); |
| 3434 | [item setTarget:item]; |
| 3435 | } |
| 3436 | |
| 3437 | static char *remove_ampersand(const char *s) |
| 3438 | { |
| 3439 | char *ret = strdup(s); |
| 3440 | const char *p = s; |
| 3441 | char *q = ret; |
| 3442 | while(*p != 0) { |
| 3443 | if (p[0]=='&') { |
| 3444 | if (p[1]=='&') { |
| 3445 | *q++ = '&'; p+=2; |
| 3446 | } else { |
| 3447 | p++; |
| 3448 | } |
| 3449 | } else { |
| 3450 | *q++ = *p++; |
| 3451 | } |
| 3452 | } |
| 3453 | *q = 0; |
| 3454 | return ret; |
| 3455 | } |
| 3456 | |
| 3457 | void *Fl_Sys_Menu_Bar::doMenuOrItemOperation(Fl_Sys_Menu_Bar::menuOrItemOperation operation, ...) |
| 3458 | /* these operations apply to menus, submenus, or menu items |
| 3459 | */ |
| 3460 | { |
| 3461 | NSAutoreleasePool *localPool; |
| 3462 | localPool = [[NSAutoreleasePool alloc] init]; |
| 3463 | NSMenu *menu; |
| 3464 | NSMenuItem *item; |
| 3465 | int value; |
| 3466 | void *pter; |
| 3467 | void *retval = NULL; |
| 3468 | va_list ap; |
| 3469 | va_start(ap, operation); |
| 3470 | |
| 3471 | if (operation == Fl_Sys_Menu_Bar::itemAtIndex) { // arguments: NSMenu*, int. Returns the item |
| 3472 | menu = va_arg(ap, NSMenu*); |
| 3473 | value = va_arg(ap, int); |
| 3474 | retval = (void *)[menu itemAtIndex:value]; |
| 3475 | } |
| 3476 | else if (operation == Fl_Sys_Menu_Bar::setKeyEquivalent) { // arguments: NSMenuItem*, int |
| 3477 | item = va_arg(ap, NSMenuItem*); |
| 3478 | value = va_arg(ap, int); |
| 3479 | char key = value; |
| 3480 | NSString *equiv = [[NSString alloc] initWithBytes:&key length:1 encoding:NSASCIIStringEncoding]; |
| 3481 | [item setKeyEquivalent:equiv]; |
| 3482 | [equiv release]; |
| 3483 | } |
| 3484 | else if (operation == Fl_Sys_Menu_Bar::setKeyEquivalentModifierMask) { // arguments: NSMenuItem*, int |
| 3485 | item = va_arg(ap, NSMenuItem*); |
| 3486 | value = va_arg(ap, int); |
| 3487 | NSUInteger macMod = 0; |
| 3488 | if ( value & FL_META ) macMod = NSCommandKeyMask; |
| 3489 | if ( value & FL_SHIFT || isupper(value) ) macMod |= NSShiftKeyMask; |
| 3490 | if ( value & FL_ALT ) macMod |= NSAlternateKeyMask; |
| 3491 | if ( value & FL_CTRL ) macMod |= NSControlKeyMask; |
| 3492 | [item setKeyEquivalentModifierMask:macMod]; |
| 3493 | } |
| 3494 | else if (operation == Fl_Sys_Menu_Bar::setState) { // arguments: NSMenuItem*, int |
| 3495 | item = va_arg(ap, NSMenuItem*); |
| 3496 | value = va_arg(ap, int); |
| 3497 | [item setState:(value ? NSOnState : NSOffState)]; |
| 3498 | } |
| 3499 | else if (operation == Fl_Sys_Menu_Bar::initWithTitle) { // arguments: const char*title. Returns the newly created menu |
| 3500 | // creates a new (sub)menu |
| 3501 | char *ts = remove_ampersand(va_arg(ap, char *)); |
| 3502 | CFStringRef title = CFStringCreateWithCString(NULL, ts, kCFStringEncodingUTF8); |
| 3503 | free(ts); |
| 3504 | NSMenu *menu = [[NSMenu alloc] initWithTitle:(NSString*)title]; |
| 3505 | CFRelease(title); |
| 3506 | [menu setAutoenablesItems:NO]; |
| 3507 | retval = (void *)menu; |
| 3508 | } |
| 3509 | else if (operation == Fl_Sys_Menu_Bar::numberOfItems) { // arguments: NSMenu *menu, int *pcount |
| 3510 | // upon return, *pcount is set to menu's item count |
| 3511 | menu = va_arg(ap, NSMenu*); |
| 3512 | pter = va_arg(ap, void *); |
| 3513 | *(int*)pter = [menu numberOfItems]; |
| 3514 | } |
| 3515 | else if (operation == Fl_Sys_Menu_Bar::setSubmenu) { // arguments: NSMenuItem *item, NSMenu *menu |
| 3516 | // sets 'menu' as submenu attached to 'item' |
| 3517 | item = va_arg(ap, NSMenuItem*); |
| 3518 | menu = va_arg(ap, NSMenu*); |
| 3519 | [item setSubmenu:menu]; |
| 3520 | [menu release]; |
| 3521 | } |
| 3522 | else if (operation == Fl_Sys_Menu_Bar::setEnabled) { // arguments: NSMenuItem*, int |
| 3523 | item = va_arg(ap, NSMenuItem*); |
| 3524 | value = va_arg(ap, int); |
| 3525 | [item setEnabled:(value ? YES : NO)]; |
| 3526 | } |
| 3527 | else if (operation == Fl_Sys_Menu_Bar::addSeparatorItem) { // arguments: NSMenu* |
| 3528 | menu = va_arg(ap, NSMenu*); |
| 3529 | [menu addItem:[NSMenuItem separatorItem]]; |
| 3530 | } |
| 3531 | else if (operation == Fl_Sys_Menu_Bar::setTitle) { // arguments: NSMenuItem*, const char * |
| 3532 | item = va_arg(ap, NSMenuItem*); |
| 3533 | char *ts = remove_ampersand(va_arg(ap, char *)); |
| 3534 | CFStringRef title = CFStringCreateWithCString(NULL, ts, kCFStringEncodingUTF8); |
| 3535 | free(ts); |
| 3536 | [item setTitle:(NSString*)title]; |
| 3537 | CFRelease(title); |
| 3538 | } |
| 3539 | else if (operation == Fl_Sys_Menu_Bar::removeItem) { // arguments: NSMenu*, int |
| 3540 | menu = va_arg(ap, NSMenu*); |
| 3541 | value = va_arg(ap, int); |
| 3542 | [menu removeItem:[menu itemAtIndex:value]]; |
| 3543 | } |
| 3544 | else if (operation == Fl_Sys_Menu_Bar::addNewItem) { // arguments: NSMenu *menu, int flrank, int *prank |
| 3545 | // creates a new menu item at the end of 'menu' |
| 3546 | // attaches the item of rank flrank (counted in Fl_Menu_) of fl_sys_menu_bar to it |
| 3547 | // upon return, puts the rank (counted in NSMenu) of the new item in *prank unless prank is NULL |
| 3548 | menu = va_arg(ap, NSMenu*); |
| 3549 | int flRank = va_arg(ap, int); |
| 3550 | char *name = remove_ampersand( (fl_sys_menu_bar->Fl_Menu_::menu() + flRank)->label()); |
| 3551 | int *prank = va_arg(ap, int*); |
| 3552 | CFStringRef cfname = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8); |
| 3553 | free(name); |
| 3554 | FLMenuItem *item = [[FLMenuItem alloc] initWithTitle:(NSString*)cfname |
| 3555 | action:@selector(doCallback:) |
| 3556 | keyEquivalent:@""]; |
| 3557 | [item setTag:flRank]; |
| 3558 | [menu addItem:item]; |
| 3559 | CFRelease(cfname); |
| 3560 | [item setTarget:item]; |
| 3561 | if (prank != NULL) *prank = [menu indexOfItem:item]; |
| 3562 | [item release]; |
| 3563 | } |
| 3564 | else if (operation == Fl_Sys_Menu_Bar::renameItem) { // arguments: int rank, const char *newname |
| 3565 | // renames the system menu item numbered rank in fl_sys_menu_bar->menu() |
| 3566 | int rank = va_arg(ap, int); |
| 3567 | char *newname = remove_ampersand( va_arg(ap, const char *) ); |
| 3568 | int countmenus = [[NSApp mainMenu] numberOfItems]; |
| 3569 | bool found = NO; |
| 3570 | NSMenuItem *macitem = 0; |
| 3571 | for(int i = 1; (!found) && i < countmenus; i++) { |
| 3572 | NSMenuItem *item = [[NSApp mainMenu] itemAtIndex:i]; |
| 3573 | NSMenu *submenu = [item submenu]; |
| 3574 | if (submenu == nil) continue; |
| 3575 | int countitems = [submenu numberOfItems]; |
| 3576 | for(int j = 0; j < countitems; j++) { |
| 3577 | macitem = [submenu itemAtIndex:j]; |
| 3578 | if ([macitem tag] == rank) { found = YES; break; } |
| 3579 | } |
| 3580 | } |
| 3581 | if (found) { |
| 3582 | [macitem setTitle:[[[NSString alloc] initWithUTF8String:newname] autorelease]]; |
| 3583 | } |
| 3584 | free(newname); |
| 3585 | } |
| 3586 | va_end(ap); |
| 3587 | [localPool release]; |
| 3588 | return retval; |
| 3589 | } |
| 3590 | |
| 3591 | void Fl_X::set_key_window() |
| 3592 | { |
| 3593 | [(NSWindow*)xid makeKeyWindow]; |
| 3594 | } |
| 3595 | |
| 3596 | static NSImage *imageFromText(const char *text, int *pwidth, int *pheight) |
| 3597 | { |
| 3598 | const char *p, *q; |
| 3599 | int width = 0, height, w2, ltext = strlen(text); |
| 3600 | fl_font(FL_HELVETICA, 10); |
| 3601 | p = text; |
| 3602 | int nl = 0; |
| 3603 | while((q=strchr(p, '\n')) != NULL) { |
| 3604 | nl++; |
| 3605 | w2 = int(fl_width(p, q - p)); |
| 3606 | if (w2 > width) width = w2; |
| 3607 | p = q + 1; |
| 3608 | } |
| 3609 | if (text[ ltext - 1] != '\n') { |
| 3610 | nl++; |
| 3611 | w2 = int(fl_width(p)); |
| 3612 | if (w2 > width) width = w2; |
| 3613 | } |
| 3614 | height = nl * fl_height() + 3; |
| 3615 | width += 6; |
| 3616 | Fl_Offscreen off = fl_create_offscreen_with_alpha(width, height); |
| 3617 | fl_begin_offscreen(off); |
| 3618 | CGContextSetRGBFillColor( (CGContextRef)off, 0,0,0,0); |
| 3619 | fl_rectf(0,0,width,height); |
| 3620 | fl_color(FL_BLACK); |
| 3621 | p = text; |
| 3622 | int y = fl_height(); |
| 3623 | while(TRUE) { |
| 3624 | q = strchr(p, '\n'); |
| 3625 | if (q) { |
| 3626 | fl_draw(p, q - p, 3, y); |
| 3627 | } else { |
| 3628 | fl_draw(p, 3, y); |
| 3629 | break; |
| 3630 | } |
| 3631 | y += fl_height(); |
| 3632 | p = q + 1; |
| 3633 | } |
| 3634 | fl_end_offscreen(); |
| 3635 | NSImage* image = CGBitmapContextToNSImage( (CGContextRef)off ); |
| 3636 | fl_delete_offscreen( off ); |
| 3637 | *pwidth = width; |
| 3638 | *pheight = height; |
| 3639 | return image; |
| 3640 | } |
| 3641 | |
| 3642 | static NSImage *defaultDragImage(int *pwidth, int *pheight) |
| 3643 | { |
| 3644 | const int width = 16, height = 16; |
| 3645 | Fl_Offscreen off = fl_create_offscreen_with_alpha(width, height); |
| 3646 | fl_begin_offscreen(off); |
| 3647 | CGContextSetRGBFillColor( (CGContextRef)off, 0,0,0,0); |
| 3648 | fl_rectf(0,0,width,height); |
| 3649 | CGContextSetRGBStrokeColor( (CGContextRef)off, 0,0,0,0.6); |
| 3650 | fl_rect(0,0,width,height); |
| 3651 | fl_rect(2,2,width-4,height-4); |
| 3652 | fl_end_offscreen(); |
| 3653 | NSImage* image = CGBitmapContextToNSImage( (CGContextRef)off ); |
| 3654 | fl_delete_offscreen( off ); |
| 3655 | *pwidth = width; |
| 3656 | *pheight = height; |
| 3657 | return image; |
| 3658 | } |
| 3659 | |
| 3660 | int Fl::dnd(void) |
| 3661 | { |
| 3662 | CFDataRef text = CFDataCreate(kCFAllocatorDefault, (UInt8*)fl_selection_buffer[0], fl_selection_length[0]); |
| 3663 | if (text==NULL) return false; |
| 3664 | NSAutoreleasePool *localPool; |
| 3665 | localPool = [[NSAutoreleasePool alloc] init]; |
| 3666 | NSPasteboard *mypasteboard = [NSPasteboard pasteboardWithName:NSDragPboard]; |
| 3667 | [mypasteboard declareTypes:[NSArray arrayWithObjects:@"public.utf8-plain-text", nil] owner:nil]; |
| 3668 | [mypasteboard setData:(NSData*)text forType:@"public.utf8-plain-text"]; |
| 3669 | CFRelease(text); |
| 3670 | Fl_Widget *w = Fl::pushed(); |
| 3671 | Fl_Window *win = w->window(); |
| 3672 | if (win == NULL) { |
| 3673 | win = (Fl_Window*)w; |
| 3674 | } else { |
| 3675 | while(win->window()) win = win->window(); |
| 3676 | } |
| 3677 | NSView *myview = [(NSWindow*)Fl_X::i(win)->xid contentView]; |
| 3678 | NSEvent *theEvent = [NSApp currentEvent]; |
| 3679 | |
| 3680 | int width, height; |
| 3681 | NSImage *image; |
| 3682 | if ( dynamic_cast<Fl_Input_*>(w) != NULL || dynamic_cast<Fl_Text_Display*>(w) != NULL) { |
| 3683 | fl_selection_buffer[0][ fl_selection_length[0] ] = 0; |
| 3684 | image = imageFromText(fl_selection_buffer[0], &width, &height); |
| 3685 | } else { |
| 3686 | image = defaultDragImage(&width, &height); |
| 3687 | } |
| 3688 | |
| 3689 | static NSSize offset={0,0}; |
| 3690 | NSPoint pt = [theEvent locationInWindow]; |
| 3691 | pt.x -= width/2; |
| 3692 | pt.y -= height/2; |
| 3693 | [myview dragImage:image at:pt offset:offset |
| 3694 | event:theEvent pasteboard:mypasteboard |
| 3695 | source:myview slideBack:YES]; |
| 3696 | if ( w ) { |
| 3697 | int old_event = Fl::e_number; |
| 3698 | w->handle(Fl::e_number = FL_RELEASE); |
| 3699 | Fl::e_number = old_event; |
| 3700 | Fl::pushed( 0 ); |
| 3701 | } |
| 3702 | [localPool release]; |
| 3703 | return true; |
| 3704 | } |
| 3705 | |
| 3706 | unsigned char *Fl_X::bitmap_from_window_rect(Fl_Window *win, int x, int y, int w, int h, int *bytesPerPixel) |
| 3707 | // delete the returned pointer after use |
| 3708 | { |
| 3709 | while(win->window()) { |
| 3710 | x += win->x(); |
| 3711 | y += win->y(); |
| 3712 | win = win->window(); |
| 3713 | } |
| 3714 | CGFloat epsilon = 0; |
| 3715 | if (fl_mac_os_version >= 100600) epsilon = 0.001; |
| 3716 | // The epsilon offset is absolutely necessary under 10.6. Without it, the top pixel row and |
| 3717 | // left pixel column are not read, and bitmap is read shifted by one pixel in both directions. |
| 3718 | // Under 10.5, we want no offset. |
| 3719 | NSRect rect = NSMakeRect(x - epsilon, y - epsilon, w, h); |
| 3720 | NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithFocusedViewRect:rect]; |
| 3721 | *bytesPerPixel = [bitmap bitsPerPixel]/8; |
| 3722 | int bpp = (int)[bitmap bytesPerPlane]; |
| 3723 | int bpr = (int)[bitmap bytesPerRow]; |
| 3724 | int hh = bpp/bpr; // sometimes hh = h-1 for unclear reason |
| 3725 | int ww = bpr/(*bytesPerPixel); // sometimes ww = w-1 |
| 3726 | unsigned char *data = new unsigned char[w * h * *bytesPerPixel]; |
| 3727 | if (w == ww) { |
| 3728 | memcpy(data, [bitmap bitmapData], w * hh * *bytesPerPixel); |
| 3729 | } else { |
| 3730 | unsigned char *p = [bitmap bitmapData]; |
| 3731 | unsigned char *q = data; |
| 3732 | for(int i = 0;i < hh; i++) { |
| 3733 | memcpy(q, p, *bytesPerPixel * ww); |
| 3734 | p += bpr; |
| 3735 | q += w * *bytesPerPixel; |
| 3736 | } |
| 3737 | } |
| 3738 | [bitmap release]; |
| 3739 | return data; |
| 3740 | } |
| 3741 | |
| 3742 | static void imgProviderReleaseData (void *info, const void *data, size_t size) |
| 3743 | { |
| 3744 | delete[] (unsigned char *)data; |
| 3745 | } |
| 3746 | |
| 3747 | CGImageRef Fl_X::CGImage_from_window_rect(Fl_Window *win, int x, int y, int w, int h) |
| 3748 | // CFRelease the returned CGImageRef after use |
| 3749 | { |
| 3750 | int bpp; |
| 3751 | unsigned char *bitmap = bitmap_from_window_rect(win, x, y, w, h, &bpp); |
| 3752 | CGImageRef img; |
| 3753 | CGColorSpaceRef lut = CGColorSpaceCreateDeviceRGB(); |
| 3754 | CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, bitmap, w*h*bpp, imgProviderReleaseData); |
| 3755 | img = CGImageCreate(w, h, 8, 8*bpp, w*bpp, lut, |
| 3756 | bpp == 3 ? kCGImageAlphaNone : kCGImageAlphaLast, |
| 3757 | provider, NULL, false, kCGRenderingIntentDefault); |
| 3758 | CGColorSpaceRelease(lut); |
| 3759 | CGDataProviderRelease(provider); |
| 3760 | return img; |
| 3761 | } |
| 3762 | |
| 3763 | WindowRef Fl_X::window_ref() |
| 3764 | { |
| 3765 | return (WindowRef)[(FLWindow*)xid windowRef]; |
| 3766 | } |
| 3767 | |
| 3768 | // so a CGRect matches exactly what is denoted x,y,w,h for clipping purposes |
| 3769 | CGRect fl_cgrectmake_cocoa(int x, int y, int w, int h) { |
| 3770 | if (Fl_Surface_Device::surface()->class_name() == Fl_Printer::class_id) return CGRectMake(x-0.5, y-0.5, w, h); |
| 3771 | return CGRectMake(x, y, w > 0 ? w - 0.9 : 0, h > 0 ? h - 0.9 : 0); |
| 3772 | } |
| 3773 | |
| 3774 | Window fl_xid(const Fl_Window* w) |
| 3775 | { |
| 3776 | Fl_X *temp = Fl_X::i(w); |
| 3777 | return temp ? temp->xid : 0; |
| 3778 | } |
| 3779 | |
| 3780 | int Fl_Window::decorated_w() |
| 3781 | { |
| 3782 | if (!shown() || parent() || !border() || !visible()) return w(); |
| 3783 | int bx, by, bt; |
| 3784 | get_window_frame_sizes(bx, by, bt); |
| 3785 | return w() + 2 * bx; |
| 3786 | } |
| 3787 | |
| 3788 | int Fl_Window::decorated_h() |
| 3789 | { |
| 3790 | if (!shown() || parent() || !border() || !visible()) return h(); |
| 3791 | int bx, by, bt; |
| 3792 | get_window_frame_sizes(bx, by, bt); |
| 3793 | return h() + bt + by; |
| 3794 | } |
| 3795 | |
| 3796 | void Fl_Paged_Device::print_window(Fl_Window *win, int x_offset, int y_offset) |
| 3797 | { |
| 3798 | if (!win->shown() || win->parent() || !win->border() || !win->visible()) { |
| 3799 | this->print_widget(win, x_offset, y_offset); |
| 3800 | return; |
| 3801 | } |
| 3802 | int bx, by, bt; |
| 3803 | get_window_frame_sizes(bx, by, bt); |
| 3804 | Fl_Display_Device::display_device()->set_current(); // send win to front and make it current |
| 3805 | win->show(); |
| 3806 | fl_gc = NULL; |
| 3807 | Fl::check(); |
| 3808 | win->make_current(); |
| 3809 | // capture the window title bar from screen |
| 3810 | CGImageRef img = Fl_X::CGImage_from_window_rect(win, 0, -bt, win->w(), bt); |
| 3811 | this->set_current(); // back to the Fl_Paged_Device |
| 3812 | CGRect rect = { { 0, 0 }, { win->w(), bt } }; // print the title bar |
| 3813 | Fl_X::q_begin_image(rect, 0, 0, win->w(), bt); |
| 3814 | CGContextDrawImage(fl_gc, rect, img); |
| 3815 | Fl_X::q_end_image(); |
| 3816 | CGImageRelease(img); |
| 3817 | this->print_widget(win, x_offset, y_offset + bt); // print the window inner part |
| 3818 | } |
| 3819 | |
| 3820 | #include <dlfcn.h> |
| 3821 | |
| 3822 | /* Returns the address of a Carbon function after dynamically loading the Carbon library if needed. |
| 3823 | Supports old Mac OS X versions that may use a couple of Carbon calls: |
| 3824 | GetKeys used by OS X 10.3 or before (in Fl::get_key()) |
| 3825 | PMSessionPageSetupDialog and PMSessionPrintDialog used by 10.4 or before (in Fl_Printer::start_job()) |
| 3826 | GetWindowPort used by 10.4 or before (in Fl_Gl_Choice.cxx) |
| 3827 | */ |
| 3828 | void *Fl_X::get_carbon_function(const char *function_name) { |
| 3829 | static void *carbon = NULL; |
| 3830 | void *f = NULL; |
| 3831 | if (!carbon) { |
| 3832 | carbon = dlopen("/System/Library/Frameworks/Carbon.framework/Carbon", RTLD_LAZY); |
| 3833 | } |
| 3834 | if (carbon) { |
| 3835 | f = dlsym(carbon, function_name); |
| 3836 | } |
| 3837 | return f; |
| 3838 | } |
| 3839 | |
| 3840 | #endif // __APPLE__ |
| 3841 | |
| 3842 | // |
| 3843 | // End of "$Id: Fl_cocoa.mm 8807 2011-06-16 12:35:32Z manolo $". |
| 3844 | // |