summaryrefslogtreecommitdiff
path: root/src/Fl_cocoa.mm
diff options
context:
space:
mode:
authorAaron M. Ucko <ucko@debian.org>2015-07-06 18:51:56 -0400
committerAaron M. Ucko <ucko@debian.org>2015-07-06 18:51:56 -0400
commita3d0ced57399c9fd8075377b7310d545f968e524 (patch)
tree63fa5d8b883c8c1235222810a0ebc99c74ac6610 /src/Fl_cocoa.mm
parent786f49e9d19180e6bf1c7d0fe0d9da89f953a330 (diff)
Imported Upstream version 1.3.3
Diffstat (limited to 'src/Fl_cocoa.mm')
-rw-r--r--src/Fl_cocoa.mm2173
1 files changed, 1391 insertions, 782 deletions
diff --git a/src/Fl_cocoa.mm b/src/Fl_cocoa.mm
index a361f29..6f5b8b1 100644
--- a/src/Fl_cocoa.mm
+++ b/src/Fl_cocoa.mm
@@ -1,9 +1,9 @@
//
-// "$Id: Fl_cocoa.mm 9734 2012-11-30 18:20:36Z manolo $"
+// "$Id: Fl_cocoa.mm 10427 2014-11-02 21:06:07Z manolo $"
//
// MacOS-Cocoa specific code for the Fast Light Tool Kit (FLTK).
//
-// Copyright 1998-2012 by Bill Spitzak and others.
+// Copyright 1998-2014 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
@@ -41,7 +41,6 @@ extern "C" {
#include <FL/x.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Tooltip.H>
-#include <FL/Fl_Sys_Menu_Bar.H>
#include <FL/Fl_Printer.H>
#include <FL/Fl_Input_.H>
#include <FL/Fl_Text_Display.H>
@@ -51,6 +50,8 @@ extern "C" {
#include <unistd.h>
#include <stdarg.h>
#include <math.h>
+#include <limits.h>
+#include <dlfcn.h>
#import <Cocoa/Cocoa.h>
@@ -80,6 +81,7 @@ typedef unsigned int NSUInteger;
// external functions
extern void fl_fix_focus();
extern unsigned short *fl_compute_macKeyLookUp();
+extern int fl_send_system_handlers(void *e);
// forward definition of functions in this file
// converting cr lf converter function
@@ -88,27 +90,34 @@ static void createAppleMenu(void);
static Fl_Region MacRegionMinusRect(Fl_Region r, int x,int y,int w,int h);
static void cocoaMouseHandler(NSEvent *theEvent);
static int calc_mac_os_version();
+static void clipboard_check(void);
+static NSString *calc_utf8_format(void);
+static void im_update(void);
+static unsigned make_current_counts = 0; // if > 0, then Fl_Window::make_current() can be called only once
+static Fl_X *fl_x_to_redraw = NULL; // set by Fl_X::flush() to the Fl_X object of the window to be redrawn
-static Fl_Quartz_Graphics_Driver fl_quartz_driver;
-static Fl_Display_Device fl_quartz_display(&fl_quartz_driver);
-Fl_Display_Device *Fl_Display_Device::_display = &fl_quartz_display; // the platform display
+Fl_Display_Device *Fl_Display_Device::_display = new Fl_Display_Device(new Fl_Quartz_Graphics_Driver); // the platform display
// public variables
CGContextRef fl_gc = 0;
-void *fl_system_menu; // this is really a NSMenu*
-Fl_Sys_Menu_Bar *fl_sys_menu_bar = 0;
-void *fl_default_cursor; // this is really a NSCursor*
void *fl_capture = 0; // (NSWindow*) we need this to compensate for a missing(?) mouse capture
bool fl_show_iconic; // true if called from iconize() - shows the next created window in collapsed state
//int fl_disable_transient_for; // secret method of removing TRANSIENT_FOR
Window fl_window;
Fl_Window *Fl_Window::current_;
int fl_mac_os_version = calc_mac_os_version(); // the version number of the running Mac OS X (e.g., 100604 for 10.6.4)
+static SEL inputContextSEL = (fl_mac_os_version >= 100600 ? @selector(inputContext) : @selector(FLinputContext));
+Fl_Fontdesc* fl_fonts = Fl_X::calc_fl_fonts();
+static NSString *utf8_format = calc_utf8_format();
// forward declarations of variables in this file
static int got_events = 0;
static Fl_Window* resize_from_system;
static int main_screen_height; // height of menubar-containing screen used to convert between Cocoa and FLTK global screen coordinates
+// through_drawRect = YES means the drawRect: message was sent to the view,
+// thus the graphics context was prepared by the system
+static BOOL through_drawRect = NO;
+static int im_enabled = -1;
#if CONSOLIDATE_MOTION
static Fl_Window* send_motion;
@@ -117,6 +126,29 @@ extern Fl_Window* fl_xmousewin;
enum { FLTKTimerEvent = 1, FLTKDataReadyEvent };
+// Carbon functions and definitions
+
+typedef void *TSMDocumentID;
+
+extern "C" enum {
+ kTSMDocumentEnabledInputSourcesPropertyTag = 'enis' // from Carbon/TextServices.h
+};
+
+// Undocumented voodoo. Taken from Mozilla.
+static const int smEnableRomanKybdsOnly = -23;
+
+typedef TSMDocumentID (*TSMGetActiveDocument_type)(void);
+static TSMGetActiveDocument_type TSMGetActiveDocument;
+typedef OSStatus (*TSMSetDocumentProperty_type)(TSMDocumentID, OSType, UInt32, void*);
+static TSMSetDocumentProperty_type TSMSetDocumentProperty;
+typedef OSStatus (*TSMRemoveDocumentProperty_type)(TSMDocumentID, OSType);
+static TSMRemoveDocumentProperty_type TSMRemoveDocumentProperty;
+typedef CFArrayRef (*TISCreateASCIICapableInputSourceList_type)(void);
+static TISCreateASCIICapableInputSourceList_type TISCreateASCIICapableInputSourceList;
+
+typedef void (*KeyScript_type)(short);
+static KeyScript_type KeyScript;
+
/* fltk-utf8 placekeepers */
void fl_reset_spot()
@@ -462,9 +494,7 @@ static void processFLTKEvent(void) {
* break the current event loop
*/
static void breakMacEventLoop()
-{
- fl_lock_function();
-
+{
NSPoint pt={0,0};
NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined location:pt
modifierFlags:0
@@ -472,7 +502,6 @@ static void breakMacEventLoop()
windowNumber:0 context:NULL
subtype:FLTKTimerEvent data1:0 data2:0];
[NSApp postEvent:event atStart:NO];
- fl_unlock_function();
}
//
@@ -520,6 +549,7 @@ static void delete_timer(MacTimeout& t)
static void do_timer(CFRunLoopTimerRef timer, void* data)
{
+ fl_lock_function();
current_timer = (MacTimeout*)data;
current_timer->pending = 0;
(current_timer->callback)(current_timer->data);
@@ -528,6 +558,7 @@ static void do_timer(CFRunLoopTimerRef timer, void* data)
current_timer = NULL;
breakMacEventLoop();
+ fl_unlock_function();
}
void Fl::add_timeout(double time, Fl_Timeout_Handler cb, void* data)
@@ -628,11 +659,35 @@ void Fl::remove_timeout(Fl_Timeout_Handler cb, void* data)
contentRect:(NSRect)rect
styleMask:(NSUInteger)windowStyle;
- (Fl_Window *)getFl_Window;
+/* These two functions allow to check if a window contains OpenGL-subwindows.
+ This is useful only for Mac OS < 10.7 to repair a problem apparent with the "cube" test program:
+ if the cube window is moved around rapidly (with OS < 10.7), the GL pixels leak away from where they should be.
+ The repair is performed by [FLWindowDelegate windowDidMove:], only if OS < 10.7.
+ */
- (BOOL)containsGLsubwindow;
-- (void)setContainsGLsubwindow:(BOOL)contains;
+- (void)containsGLsubwindow:(BOOL)contains;
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
+- (NSPoint)convertBaseToScreen:(NSPoint)aPoint;
+#endif
@end
@implementation FLWindow
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
+- (NSPoint)convertBaseToScreen:(NSPoint)aPoint
+{
+ if (fl_mac_os_version >= 100700) {
+ NSRect r = [self convertRectToScreen:NSMakeRect(aPoint.x, aPoint.y, 0, 0)];
+ return r.origin;
+ }
+ else {
+ // replaces return [super convertBaseToScreen:aPoint] that may trigger a compiler warning
+ typedef NSPoint (*convertIMP)(id, SEL, NSPoint);
+ convertIMP addr = (convertIMP)[NSWindow instanceMethodForSelector:@selector(convertBaseToScreen:)];
+ return addr(self, @selector(convertBaseToScreen:), aPoint);
+ }
+}
+#endif
+
- (FLWindow*)initWithFl_W:(Fl_Window *)flw
contentRect:(NSRect)rect
styleMask:(NSUInteger)windowStyle
@@ -641,6 +696,12 @@ void Fl::remove_timeout(Fl_Timeout_Handler cb, void* data)
if (self) {
w = flw;
containsGLsubwindow = NO;
+ if (fl_mac_os_version >= 100700) {
+ // replaces [self setRestorable:NO] that may trigger a compiler warning
+ typedef void (*setIMP)(id, SEL, BOOL);
+ setIMP addr = (setIMP)[self methodForSelector:@selector(setRestorable:)];
+ addr(self, @selector(setRestorable:), NO);
+ }
}
return self;
}
@@ -652,7 +713,7 @@ void Fl::remove_timeout(Fl_Timeout_Handler cb, void* data)
{
return containsGLsubwindow;
}
-- (void)setContainsGLsubwindow:(BOOL)contains
+- (void)containsGLsubwindow:(BOOL)contains
{
containsGLsubwindow = contains;
}
@@ -666,9 +727,6 @@ void Fl::remove_timeout(Fl_Timeout_Handler cb, void* data)
return !(w->tooltip_window() || w->menu_window());
}
-// TODO see if we really need a canBecomeMainWindow ...
-#if 0
-
- (BOOL)canBecomeMainWindow
{
if (Fl::modal_ && (Fl::modal_ != w))
@@ -677,7 +735,6 @@ void Fl::remove_timeout(Fl_Timeout_Handler cb, void* data)
return !(w->tooltip_window() || w->menu_window());
}
-#endif
@end
@@ -726,6 +783,7 @@ static double do_queued_events( double time = 0.0 )
return time;
}
+
/*
* This public function handles all events. It wait a maximum of
* 'time' seconds for an event. This version returns 1 if events
@@ -739,17 +797,132 @@ int fl_wait( double time )
return (got_events);
}
-double fl_mac_flush_and_wait(double time_to_wait, char in_idle) {
+double fl_mac_flush_and_wait(double time_to_wait) {
+ static int in_idle = 0;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ if (Fl::idle) {
+ if (!in_idle) {
+ in_idle = 1;
+ Fl::idle();
+ in_idle = 0;
+ }
+ // the idle function may turn off idle, we can then wait:
+ if (Fl::idle) time_to_wait = 0.0;
+ }
Fl::flush();
if (Fl::idle && !in_idle) // 'idle' may have been set within flush()
time_to_wait = 0.0;
double retval = fl_wait(time_to_wait);
+ if (fl_gc) {
+ CGContextFlush(fl_gc);
+ fl_gc = 0;
+ }
[pool release];
return retval;
}
+static NSInteger max_normal_window_level(void)
+{
+ Fl_X *x;
+ NSInteger max_level;
+
+ max_level = 0;
+
+ for (x = Fl_X::first;x;x = x->next) {
+ NSInteger level;
+ FLWindow *cw = x->xid;
+ Fl_Window *win = x->w;
+ if (!win || !cw || ![cw isVisible])
+ continue;
+ if (win->modal() || win->non_modal())
+ continue;
+ level = [cw level];
+ if (level >= max_level)
+ max_level = level;
+ }
+
+ return max_level;
+}
+
+// appropriate window level for modal windows
+static NSInteger modal_window_level(void)
+{
+ NSInteger level;
+
+ level = max_normal_window_level();
+ if (level < NSModalPanelWindowLevel)
+ return NSModalPanelWindowLevel;
+
+ // Need some room for non-modal windows
+ level += 2;
+
+ // We cannot exceed this
+ if (level > CGShieldingWindowLevel())
+ return CGShieldingWindowLevel();
+
+ return level;
+}
+
+// appropriate window level for non-modal windows
+static NSInteger non_modal_window_level(void)
+{
+ NSInteger level;
+
+ level = max_normal_window_level();
+ if (level < NSFloatingWindowLevel)
+ return NSFloatingWindowLevel;
+
+ level += 1;
+
+ if (level > CGShieldingWindowLevel())
+ return CGShieldingWindowLevel();
+
+ return level;
+}
+
+// makes sure modal and non-modal windows stay on top
+static void fixup_window_levels(void)
+{
+ NSInteger modal_level, non_modal_level;
+
+ Fl_X *x;
+ FLWindow *prev_modal, *prev_non_modal;
+
+ modal_level = modal_window_level();
+ non_modal_level = non_modal_window_level();
+
+ prev_modal = NULL;
+ prev_non_modal = NULL;
+
+ for (x = Fl_X::first;x;x = x->next) {
+ FLWindow *cw = x->xid;
+ Fl_Window *win = x->w;
+ if (!win || !cw || ![cw isVisible])
+ continue;
+ if (win->modal()) {
+ if ([cw level] != modal_level) {
+ [cw setLevel:modal_level];
+ // changing level puts then in front, so make sure the
+ // stacking isn't messed up
+ if (prev_modal != NULL)
+ [cw orderWindow:NSWindowBelow
+ relativeTo:[prev_modal windowNumber]];
+ }
+ prev_modal = cw;
+ } else if (win->non_modal()) {
+ if ([cw level] != non_modal_level) {
+ [cw setLevel:non_modal_level];
+ if (prev_non_modal != NULL)
+ [cw orderWindow:NSWindowBelow
+ relativeTo:[prev_non_modal windowNumber]];
+ }
+ prev_non_modal = cw;
+ }
+ }
+}
+
+
// updates Fl::e_x, Fl::e_y, Fl::e_x_root, and Fl::e_y_root
static void update_e_xy_and_e_xy_root(NSWindow *nsw)
{
@@ -893,97 +1066,35 @@ static void cocoaMouseHandler(NSEvent *theEvent)
return;
}
-@interface FLTextView : NSTextView
-// this subclass is needed under OS X <= 10.5 but not under >= 10.6 where the base class is enough
+@interface FLTextView : NSTextView // this subclass is only needed under OS X < 10.6
{
+ BOOL isActive;
}
+- (void)insertText:(id)aString;
+- (void)doCommandBySelector:(SEL)aSelector;
+- (void)setActive:(BOOL)a;
@end
@implementation FLTextView
- (void)insertText:(id)aString
{
- [[[NSApp keyWindow] contentView] insertText:aString];
+ if (isActive) [[[NSApp keyWindow] contentView] insertText:aString];
}
- (void)doCommandBySelector:(SEL)aSelector
{
[[[NSApp keyWindow] contentView] doCommandBySelector:aSelector];
}
-@end
-
-/*
-Handle cocoa keyboard events
-Events during a character composition sequence:
- - keydown with deadkey -> [[theEvent characters] length] is 0
- - keyup -> [theEvent characters] contains the deadkey
- - keydown with next key -> [theEvent characters] contains the composed character
- - keyup -> [theEvent characters] contains the standard character
- */
-static void cocoaKeyboardHandler(NSEvent *theEvent)
+- (void)setActive:(BOOL)a
{
- NSUInteger mods;
-
- // get the modifiers
- mods = [theEvent modifierFlags];
- // get the key code
- UInt32 keyCode = 0, maskedKeyCode = 0;
- unsigned short sym = 0;
- keyCode = [theEvent keyCode];
- NSString *s = [theEvent characters];
- if ( (mods & NSShiftKeyMask) && (mods & NSCommandKeyMask) ) {
- s = [s uppercaseString]; // US keyboards return lowercase letter in s if cmd-shift-key is hit
- }
- // extended keyboards can also send sequences on key-up to generate Kanji etc. codes.
- // Some observed prefixes are 0x81 to 0x83, followed by an 8 bit keycode.
- // In this mode, there seem to be no key-down codes
- // printf("%08x %08x %08x\n", keyCode, mods, key);
- maskedKeyCode = keyCode & 0x7f;
-
- if ([theEvent type] == NSKeyUp) {
- Fl::e_state &= 0xbfffffff; // clear the deadkey flag
- }
-
- mods_to_e_state( mods ); // process modifier keys
- sym = macKeyLookUp[maskedKeyCode];
- if (sym < 0xff00) { // a "simple" key
- // find the result of this key without modifier
- NSString *sim = [theEvent charactersIgnoringModifiers];
- UniChar one;
- CFStringGetCharacters((CFStringRef)sim, CFRangeMake(0, 1), &one);
- // charactersIgnoringModifiers doesn't ignore shift, remove it when it's on
- if(one >= 'A' && one <= 'Z') one += 32;
- if (one > 0 && one <= 0x7f && (sym<'0' || sym>'9') ) sym = one;
- }
- Fl::e_keysym = Fl::e_original_keysym = sym;
-
- //NSLog(@"cocoaKeyboardHandler: keycode=%08x keysym=%08x mods=%08x symbol=%@ (%@)",
- // keyCode, sym, mods, [theEvent characters], [theEvent charactersIgnoringModifiers]);
-
- // If there is text associated with this key, it will be filled in later.
- Fl::e_length = 0;
- Fl::e_text = (char*)"";
-}
-
-
-/*
- * Open callback function to call...
- */
-static void (*open_cb)(const char *) = 0;
-
-/*
- * Install an open documents event handler...
- */
-void fl_open_callback(void (*cb)(const char *)) {
- fl_open_display();
- open_cb = cb;
+ isActive = a;
}
+@end
-@interface FLDelegate : NSObject
+@interface FLWindowDelegate : NSObject
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
-<NSWindowDelegate, NSApplicationDelegate>
+<NSWindowDelegate>
#endif
-{
- BOOL seen_open_file;
-}
++ (FLWindowDelegate*)createOnce;
- (void)windowDidMove:(NSNotification *)notif;
- (void)windowDidResize:(NSNotification *)notif;
- (void)windowDidResignKey:(NSNotification *)notif;
@@ -992,18 +1103,18 @@ void fl_open_callback(void (*cb)(const char *)) {
- (void)windowDidDeminiaturize:(NSNotification *)notif;
- (void)windowDidMiniaturize:(NSNotification *)notif;
- (BOOL)windowShouldClose:(id)fl;
-- (void)anyWindowWillClose:(NSNotification *)notif;
-- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender;
-- (void)applicationDidBecomeActive:(NSNotification *)notify;
-- (void)applicationDidChangeScreenParameters:(NSNotification *)aNotification;
-- (void)applicationWillResignActive:(NSNotification *)notify;
-- (void)applicationWillHide:(NSNotification *)notify;
-- (void)applicationWillUnhide:(NSNotification *)notify;
- (id)windowWillReturnFieldEditor:(NSWindow *)sender toObject:(id)client;
-- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename;
-- (void)applicationDidFinishLaunching:(NSNotification *)aNotification;
+- (void)anyWindowWillClose:(NSNotification *)notif;
@end
-@implementation FLDelegate
+@implementation FLWindowDelegate
++ (FLWindowDelegate*)createOnce
+{
+ static FLWindowDelegate* delegate = nil;
+ if (!delegate) {
+ delegate = [[FLWindowDelegate alloc] init];
+ }
+ return delegate;
+}
- (void)windowDidMove:(NSNotification *)notif
{
fl_lock_function();
@@ -1016,8 +1127,8 @@ void fl_open_callback(void (*cb)(const char *)) {
update_e_xy_and_e_xy_root(nsw);
resize_from_system = window;
window->position((int)pt2.x, (int)(main_screen_height - pt2.y));
- if ([nsw containsGLsubwindow] ) {
- [nsw display];// redraw window after moving if it contains OpenGL subwindows
+ if ([nsw containsGLsubwindow] && fl_mac_os_version < 100700) {
+ [nsw display];// with OS < 10.7, redraw window after moving if it contains OpenGL subwindows
}
fl_unlock_function();
}
@@ -1045,9 +1156,11 @@ void fl_open_callback(void (*cb)(const char *)) {
FLWindow *nsw = (FLWindow*)[notif object];
Fl_Window *window = [nsw getFl_Window];
/* Fullscreen windows obscure all other windows so we need to return
- to a "normal" level when the user switches to another window */
- if (window->fullscreen_active())
+ to a "normal" level when the user switches to another window */
+ if (window->fullscreen_active()) {
[nsw setLevel:NSNormalWindowLevel];
+ fixup_window_levels();
+ }
Fl::handle( FL_UNFOCUS, window);
fl_unlock_function();
}
@@ -1057,8 +1170,10 @@ void fl_open_callback(void (*cb)(const char *)) {
FLWindow *nsw = (FLWindow*)[notif object];
Fl_Window *w = [nsw getFl_Window];
/* Restore previous fullscreen level */
- if (w->fullscreen_active())
+ if (w->fullscreen_active()) {
[nsw setLevel:NSStatusWindowLevel];
+ fixup_window_levels();
+ }
if ( w->border() || (!w->modal() && !w->tooltip_window()) ) Fl::handle( FL_FOCUS, w);
fl_unlock_function();
}
@@ -1096,6 +1211,18 @@ void fl_open_callback(void (*cb)(const char *)) {
// the system doesn't need to send [fl close] because FLTK does it when needed
return NO;
}
+- (id)windowWillReturnFieldEditor:(NSWindow *)sender toObject:(id)client
+{
+ if (fl_mac_os_version < 100600) {
+ static FLTextView *view = nil;
+ if (!view) {
+ NSRect rect={{0,0},{20,20}};
+ view = [[FLTextView alloc] initWithFrame:rect];
+ }
+ return view;
+ }
+ return nil;
+}
- (void)anyWindowWillClose:(NSNotification *)notif
{
fl_lock_function();
@@ -1105,13 +1232,34 @@ void fl_open_callback(void (*cb)(const char *)) {
Fl_Window *w = Fl::first_window();
while (w && (w->parent() || !w->border() || !w->visible())) {
w = Fl::next_window(w);
- }
+ }
if (w) {
[Fl_X::i(w)->xid makeKeyWindow];
}
}
fl_unlock_function();
}
+@end
+
+@interface FLAppDelegate : NSObject
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
+<NSApplicationDelegate>
+#endif
+{
+ void (*open_cb)(const char*);
+ TSMDocumentID currentDoc;
+}
+- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender;
+- (void)applicationDidBecomeActive:(NSNotification *)notify;
+- (void)applicationDidChangeScreenParameters:(NSNotification *)aNotification;
+- (void)applicationDidUpdate:(NSNotification *)aNotification;
+- (void)applicationWillResignActive:(NSNotification *)notify;
+- (void)applicationWillHide:(NSNotification *)notify;
+- (void)applicationWillUnhide:(NSNotification *)notify;
+- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename;
+- (void)open_cb:(void (*)(const char*))cb;
+@end
+@implementation FLAppDelegate
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender
{
fl_lock_function();
@@ -1128,44 +1276,25 @@ void fl_open_callback(void (*cb)(const char *)) {
fl_unlock_function();
return reply;
}
-/**
- * Cocoa organizes the Z depth of windows on a global priority. FLTK however
- * expects the window manager to organize Z level by application. The trickery
- * below will change Z order during activation and deactivation.
- */
- (void)applicationDidBecomeActive:(NSNotification *)notify
{
fl_lock_function();
- Fl_X *x;
- FLWindow *top = 0, *topModal = 0, *topNonModal = 0;
- for (x = Fl_X::first;x;x = x->next) {
- FLWindow *cw = x->xid;
- Fl_Window *win = x->w;
- if (win && cw && [cw isVisible]) {
- if (win->modal()) {
- [cw setLevel:NSModalPanelWindowLevel];
- if (topModal)
- [cw orderWindow:NSWindowBelow relativeTo:[topModal windowNumber]];
- else
- topModal = cw;
- } else if (win->non_modal()) {
- [cw setLevel:NSFloatingWindowLevel];
- if (topNonModal)
- [cw orderWindow:NSWindowBelow relativeTo:[topNonModal windowNumber]];
- else
- topNonModal = cw;
- } else {
- if (top)
- ;
- else
- top = cw;
- }
- }
- }
+
+ // update clipboard status
+ clipboard_check();
+
+ /**
+ * Cocoa organizes the Z depth of windows on a global priority. FLTK however
+ * expects the window manager to organize Z level by application. The trickery
+ * below will change Z order during activation and deactivation.
+ */
+ fixup_window_levels();
+
fl_unlock_function();
}
- (void)applicationDidChangeScreenParameters:(NSNotification *)unused
{ // react to changes in screen numbers and positions
+ fl_lock_function();
main_screen_height = [[[NSScreen screens] objectAtIndex:0] frame].size.height;
Fl::call_screen_init();
// FLTK windows have already been notified they were moved,
@@ -1179,6 +1308,24 @@ void fl_open_callback(void (*cb)(const char *)) {
}
}
Fl::handle(FL_SCREEN_CONFIGURATION_CHANGED, NULL);
+ fl_unlock_function();
+}
+- (void)applicationDidUpdate:(NSNotification *)aNotification
+{
+ if ((fl_mac_os_version >= 100500) && (im_enabled != -1) &&
+ (TSMGetActiveDocument != NULL)) {
+ TSMDocumentID newDoc;
+ // It is extremely unclear when Cocoa decides to create/update
+ // the input context, but debugging reveals that it is done
+ // by NSApplication:updateWindows. So check if the input context
+ // has shifted after each such run so that we can update our
+ // input methods status.
+ newDoc = TSMGetActiveDocument();
+ if (newDoc != currentDoc) {
+ im_update();
+ currentDoc = newDoc;
+ }
+ }
}
- (void)applicationWillResignActive:(NSNotification *)notify
{
@@ -1243,21 +1390,11 @@ void fl_open_callback(void (*cb)(const char *)) {
}
fl_unlock_function();
}
-- (id)windowWillReturnFieldEditor:(NSWindow *)sender toObject:(id)client
-{
- if (fl_mac_os_version < 100600) {
- static FLTextView *view = nil;
- if (!view) {
- NSRect rect={{0,0},{20,20}};
- view = [[FLTextView alloc] initWithFrame:rect];
- }
- return view;
- }
- return nil;
-}
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
{
- seen_open_file = YES;
+ // without the next statement, the opening of the 1st window is delayed by several seconds
+ // under Mac OS ≥ 10.8 when a file is dragged on the application icon
+ [[theApplication mainWindow] orderFront:self];
if (open_cb) {
fl_lock_function();
(*open_cb)([filename UTF8String]);
@@ -1266,17 +1403,26 @@ void fl_open_callback(void (*cb)(const char *)) {
}
return NO;
}
-- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
+- (void)open_cb:(void (*)(const char*))cb
{
- // without this, the opening of the 1st window is delayed by several seconds
- // under Mac OS 10.8 when a file is dragged on the application icon
- if (fl_mac_os_version >= 100800 && seen_open_file) [[NSApp mainWindow] orderFront:self];
+ open_cb = cb;
}
@end
+/*
+ * Install an open documents event handler...
+ */
+void fl_open_callback(void (*cb)(const char *)) {
+ fl_open_display();
+ [(FLAppDelegate*)[NSApp delegate] open_cb:cb];
+}
+
@implementation FLApplication
+ (void)sendEvent:(NSEvent *)theEvent
{
+ if (fl_send_system_handlers(theEvent))
+ return;
+
NSEventType type = [theEvent type];
if (type == NSLeftMouseDown) {
fl_lock_function();
@@ -1308,21 +1454,29 @@ void fl_open_callback(void (*cb)(const char *)) {
}
@end
-extern "C" {
- OSErr CPSEnableForegroundOperation(ProcessSerialNumber *psn, UInt32 _arg2,
- UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
+/* Prototype of undocumented function needed to support Mac OS 10.2 or earlier
+ extern "C" {
+ OSErr CPSEnableForegroundOperation(ProcessSerialNumber*, UInt32, UInt32, UInt32, UInt32);
}
+*/
void fl_open_display() {
static char beenHereDoneThat = 0;
if ( !beenHereDoneThat ) {
beenHereDoneThat = 1;
+
+ TSMGetActiveDocument = (TSMGetActiveDocument_type)Fl_X::get_carbon_function("TSMGetActiveDocument");
+ TSMSetDocumentProperty = (TSMSetDocumentProperty_type)Fl_X::get_carbon_function("TSMSetDocumentProperty");
+ TSMRemoveDocumentProperty = (TSMRemoveDocumentProperty_type)Fl_X::get_carbon_function("TSMRemoveDocumentProperty");
+ TISCreateASCIICapableInputSourceList = (TISCreateASCIICapableInputSourceList_type)Fl_X::get_carbon_function("TISCreateASCIICapableInputSourceList");
+
+ KeyScript = (KeyScript_type)Fl_X::get_carbon_function("KeyScript");
BOOL need_new_nsapp = (NSApp == nil);
if (need_new_nsapp) [NSApplication sharedApplication];
NSAutoreleasePool *localPool;
localPool = [[NSAutoreleasePool alloc] init]; // never released
- [NSApp setDelegate:[[FLDelegate alloc] init]];
+ [(NSApplication*)NSApp setDelegate:[[FLAppDelegate alloc] init]];
if (need_new_nsapp) [NSApp finishLaunching];
// empty the event queue but keep system events for drag&drop of files at launch
@@ -1333,44 +1487,48 @@ void fl_open_display() {
dequeue:YES];
while (ign_event);
- fl_default_cursor = [NSCursor arrowCursor];
-
// bring the application into foreground without a 'CARB' resource
- Boolean same_psn;
- ProcessSerialNumber cur_psn, front_psn;
- if ( !GetCurrentProcess( &cur_psn ) && !GetFrontProcess( &front_psn ) &&
- !SameProcess( &front_psn, &cur_psn, &same_psn ) && !same_psn ) {
+ bool i_am_in_front;
+ ProcessSerialNumber cur_psn = { 0, kCurrentProcess };
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
+ if (fl_mac_os_version >= 100600) {
+ i_am_in_front = [[NSRunningApplication currentApplication] isActive];
+ }
+ else
+#endif
+ {
+ Boolean same_psn;
+ ProcessSerialNumber front_psn;
+ //avoid compilation warnings triggered by GetFrontProcess() and SameProcess()
+ void* h = dlopen(NULL, RTLD_LAZY);
+ typedef OSErr (*GetFrontProcess_type)(ProcessSerialNumber*);
+ GetFrontProcess_type GetFrontProcess_ = (GetFrontProcess_type)dlsym(h, "GetFrontProcess");
+ typedef OSErr (*SameProcess_type)(ProcessSerialNumber*, ProcessSerialNumber*, Boolean*);
+ SameProcess_type SameProcess_ = (SameProcess_type)dlsym(h, "SameProcess");
+ i_am_in_front = (!GetFrontProcess_( &front_psn ) &&
+ !SameProcess_( &front_psn, &cur_psn, &same_psn ) && same_psn );
+ }
+ if (!i_am_in_front) {
// only transform the application type for unbundled apps
NSBundle *bundle = [NSBundle mainBundle];
if (bundle) {
- NSString *exe = [[bundle executablePath] stringByStandardizingPath];
- NSString *bpath = [bundle bundlePath];
- NSString *exe_dir = [exe stringByDeletingLastPathComponent];
- if ([bpath isEqualToString:exe] || [bpath isEqualToString:exe_dir]) bundle = nil;
- }
-
- if ( !bundle )
- {
- // Earlier versions of this code tried to use weak linking, however it
- // appears that this does not work on 10.2. Since 10.3 and higher provide
- // both TransformProcessType and CPSEnableForegroundOperation, the following
- // conditional code compiled on 10.2 will still work on newer releases...
- OSErr err;
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_3
- if (TransformProcessType != NULL) {
- err = TransformProcessType(&cur_psn, kProcessTransformToForegroundApplication);
- } else
-#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_3
- err = CPSEnableForegroundOperation(&cur_psn, 0x03, 0x3C, 0x2C, 0x1103);
- if (err == noErr) {
- SetFrontProcess( &cur_psn );
- }
+ NSString *exe = [[bundle executablePath] stringByStandardizingPath];
+ NSString *bpath = [bundle bundlePath];
+ NSString *exe_dir = [exe stringByDeletingLastPathComponent];
+ if ([bpath isEqualToString:exe] || [bpath isEqualToString:exe_dir]) bundle = nil;
+ }
+
+ if ( !bundle ) {
+ TransformProcessType(&cur_psn, kProcessTransformToForegroundApplication); // needs Mac OS 10.3
+ /* support of Mac OS 10.2 or earlier used this undocumented call instead
+ err = CPSEnableForegroundOperation(&cur_psn, 0x03, 0x3C, 0x2C, 0x1103);
+ */
+ [NSApp activateIgnoringOtherApps:YES];
}
}
if (![NSApp servicesMenu]) createAppleMenu();
- fl_system_menu = [NSApp mainMenu];
main_screen_height = [[[NSScreen screens] objectAtIndex:0] frame].size.height;
- [[NSNotificationCenter defaultCenter] addObserver:[NSApp delegate]
+ [[NSNotificationCenter defaultCenter] addObserver:[FLWindowDelegate createOnce]
selector:@selector(anyWindowWillClose:)
name:NSWindowWillCloseNotification
object:nil];
@@ -1387,6 +1545,66 @@ void fl_open_display() {
void fl_close_display() {
}
+// Force a "Roman" or "ASCII" keyboard, which both the Mozilla and
+// Safari people seem to think implies turning off advanced IME stuff
+// (see nsTSMManager::SyncKeyScript in Mozilla and enableSecureTextInput
+// in Safari/Webcore). Should be good enough for us then...
+
+static void im_update(void) {
+ if (fl_mac_os_version >= 100500) {
+ TSMDocumentID doc;
+
+ if ((TSMGetActiveDocument == NULL) ||
+ (TSMSetDocumentProperty == NULL) ||
+ (TSMRemoveDocumentProperty == NULL) ||
+ (TISCreateASCIICapableInputSourceList == NULL))
+ return;
+
+ doc = TSMGetActiveDocument();
+
+ if (im_enabled)
+ TSMRemoveDocumentProperty(doc, kTSMDocumentEnabledInputSourcesPropertyTag);
+ else {
+ CFArrayRef inputSources;
+
+ inputSources = TISCreateASCIICapableInputSourceList();
+ TSMSetDocumentProperty(doc, kTSMDocumentEnabledInputSourcesPropertyTag,
+ sizeof(CFArrayRef), &inputSources);
+ CFRelease(inputSources);
+ }
+ } else {
+ if (KeyScript == NULL)
+ return;
+
+ if (im_enabled)
+ KeyScript(smKeyEnableKybds);
+ else
+ KeyScript(smEnableRomanKybdsOnly);
+ }
+}
+
+void Fl::enable_im() {
+ fl_open_display();
+
+ im_enabled = 1;
+
+ if (fl_mac_os_version >= 100500)
+ [NSApp updateWindows];
+ else
+ im_update();
+}
+
+void Fl::disable_im() {
+ fl_open_display();
+
+ im_enabled = 0;
+
+ if (fl_mac_os_version >= 100500)
+ [NSApp updateWindows];
+ else
+ im_update();
+}
+
// Gets the border sizes and the titlebar size
static void get_window_frame_sizes(int &bx, int &by, int &bt) {
@@ -1463,15 +1681,6 @@ void Fl::get_mouse(int &x, int &y)
/*
- * Initialize the given port for redraw and call the window's flush() to actually draw the content
- */
-void Fl_X::flush()
-{
- w->flush();
- if (fl_gc) CGContextFlush(fl_gc);
-}
-
-/*
* Gets called when a window is created, resized, or deminiaturized
*/
static void handleUpdateEvent( Fl_Window *window )
@@ -1491,7 +1700,10 @@ static void handleUpdateEvent( Fl_Window *window )
cx->region = 0;
}
cx->w->clear_damage(FL_DAMAGE_ALL);
+ CGContextRef gc = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
+ CGContextSaveGState(gc); // save original context
cx->flush();
+ CGContextRestoreGState(gc); // restore original context
cx->w->clear_damage();
}
window->clear_damage(FL_DAMAGE_ALL);
@@ -1629,16 +1841,143 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
}
}
+/** How FLTK handles Mac OS text input
+
+ Let myview be the instance of the FLView class that has the keyboard focus. FLView is an FLTK-defined NSView subclass
+ that implements the NSTextInputClient protocol to properly handle text input. It also implements the old NSTextInput
+ protocol to run with OS <= 10.4. The few NSTextInput protocol methods that differ in signature from the NSTextInputClient
+ protocol transmit the received message to the corresponding NSTextInputClient method.
+
+ Keyboard input sends keyDown: and performKeyEquivalent: messages to myview. The latter occurs for keys such as
+ ForwardDelete, arrows and F1, and when the Ctrl or Cmd modifiers are used. Other key presses send keyDown: messages.
+ The keyDown: method calls [[myview inputContext] handleEvent:theEvent] that triggers system
+ processing of keyboard events. The performKeyEquivalent: method directly calls Fl::handle(FL_KEYBOARD, focus-window)
+ when the Ctrl or Cmd modifiers are used. If not, it also calls [[myview inputContext] handleEvent:theEvent].
+ The performKeyEquivalent: method returns YES when the keystroke has been handled and NO otherwise, which allows
+ shortcuts of the system menu to be processed. Three sorts of messages are then sent back by the system to myview:
+ doCommandBySelector:, setMarkedText: and insertText:. All 3 messages eventually produce Fl::handle(FL_KEYBOARD, win) calls.
+ The doCommandBySelector: message allows to process events such as new-line, forward and backward delete, arrows,
+ escape, tab, F1. The message setMarkedText: is sent when marked text, that is, temporary text that gets replaced later
+ by some other text, is inserted. This happens when a dead key is pressed, and also
+ when entering complex scripts (e.g., Chinese). Fl_X::next_marked_length gives the byte
+ length of marked text before the FL_KEYBOARD event is processed. Fl::compose_state gives this length after this processing.
+ Message insertText: is sent to enter text in the focused widget. If there's marked text, Fl::compose_state is > 0, and this
+ marked text gets replaced by the inserted text. If there's no marked text, the new text is inserted at the insertion point.
+ When the character palette is used to enter text, the system sends an insertText: message to myview.
+ The in_key_event field of the FLView class allows to differentiate keyboard from palette inputs.
+
+ During processing of the handleEvent message, inserted and marked strings are concatenated in a single string
+ inserted in a single FL_KEYBOARD event after return from handleEvent. The need_handle member variable of FLView allows
+ to determine when setMarkedText or insertText strings have been sent during handleEvent processing and must trigger
+ an FL_KEYBOARD event. Concatenating two insertText operations or an insertText followed by a setMarkedText is possible.
+ In contrast, setMarkedText followed by insertText or by another setMarkedText isn't correct if concatenated in a single
+ string. Thus, in such case, the setMarkedText and the next operation produce each an FL_KEYBOARD event.
+
+ OS >= 10.7 contains a feature where pressing and holding certain keys opens a menu window that shows a list
+ of possible accented variants of this key. The selectedRange field of the FLView class and the selectedRange, insertText:
+ and setMarkedText: methods of the NSTextInputClient protocol are used to support this feature.
+ The notion of selected text (!= marked text) is monitored by the selectedRange field.
+ The -(NSRange)[FLView selectedRange] method is used to control whether an FLTK widget opens accented character windows
+ by returning .location = NSNotFound to disable that, or returning the value of the selectedRange field to enable the feature.
+ When selectedRange.location >= 0, the value of selectedRange.length is meaningful. 0 means no text is currently selected,
+ > 0 means this number of characters before the insertion point are selected. The insertText: method does
+ selectedRange = NSMakeRange(100, 0); to indicate no text is selected. The setMarkedText: method does
+ selectedRange = NSMakeRange(100, newSelection.length); to indicate that this length of text is selected.
+
+ With OS <= 10.5, the crucial call [[myview inputContext] handleEvent:theEvent] is not possible because neither the
+ inputContext nor the handleEvent: methods are implemented. This call is re-written:
+ static SEL inputContextSEL = (fl_mac_os_version >= 100600 ? @selector(inputContext) : @selector(FLinputContext));
+ [[myview performSelector:inputContextSEL] handleEvent:theEvent];
+ that replaces the 10.6 inputContext message by the FLinputContext message. This message and two FLTK-defined classes,
+ FLTextInputContext and FLTextView, are used to emulate with OS <= 10.5 what's possible with OS >= 10.6.
+ Method -(FLTextInputContext*)[FLView FLinputContext] returns an instance of class FLTextInputContext that possesses
+ a handleEvent: method. FLView's FLinputContext method also calls [[self window] fieldEditor:YES forObject:nil] which
+ returns the so-called view's "field editor". This editor is an instance of the FLTextView class allocated by the
+ -(id)[FLWindowDelegate windowWillReturnFieldEditor: toObject:] method.
+ The -(BOOL)[FLTextInputContext handleEvent:] method emulates the missing 10.6 -(BOOL)[NSTextInputContext handleEvent:]
+ by sending the interpretKeyEvents: message to the FLTextView object. The system sends back doCommandBySelector: and
+ insertText: messages to the FLTextView object that are transmitted unchanged to myview to be processed as with OS >= 10.6.
+ The system also sends setMarkedText: messages directly to myview.
+
+ There is furthermore an oddity of dead key processing with OS <= 10.5. It occurs when a dead key followed by a non-accented
+ key are pressed. Say, for example, that keys '^' followed by 'p' are pressed on a French or German keyboard. Resulting
+ messages are: [myview setMarkedText:@"^"], [myview insertText:@"^"], [myview insertText:@"p"], [FLTextView insertText:@"^p"].
+ The 2nd '^' replaces the marked 1st one, followed by p^p. The resulting text in the widget is "^p^p" instead of the
+ desired "^p". To avoid that, the FLTextView object is deactivated by the insertText: message and reactivated after
+ the handleEvent: message has been processed.
+
+ NSEvent's during a character composition sequence:
+ - keyDown with deadkey -> [[theEvent characters] length] is 0
+ - keyUp -> [theEvent characters] contains the deadkey
+ - keyDown with next key -> [theEvent characters] contains the composed character
+ - keyUp -> [theEvent characters] contains the standard character
+ */
-@interface FLView : NSView <NSTextInput> {
- int next_compose_length;
- bool in_key_event;
+static void cocoaKeyboardHandler(NSEvent *theEvent)
+{
+ NSUInteger mods;
+ // get the modifiers
+ mods = [theEvent modifierFlags];
+ // get the key code
+ UInt32 keyCode = 0, maskedKeyCode = 0;
+ unsigned short sym = 0;
+ keyCode = [theEvent keyCode];
+ // extended keyboards can also send sequences on key-up to generate Kanji etc. codes.
+ // Some observed prefixes are 0x81 to 0x83, followed by an 8 bit keycode.
+ // In this mode, there seem to be no key-down codes
+ // printf("%08x %08x %08x\n", keyCode, mods, key);
+ maskedKeyCode = keyCode & 0x7f;
+ mods_to_e_state( mods ); // process modifier keys
+ sym = macKeyLookUp[maskedKeyCode];
+ if (sym < 0xff00) { // a "simple" key
+ // find the result of this key without modifier
+ NSString *sim = [theEvent charactersIgnoringModifiers];
+ UniChar one;
+ CFStringGetCharacters((CFStringRef)sim, CFRangeMake(0, 1), &one);
+ // charactersIgnoringModifiers doesn't ignore shift, remove it when it's on
+ if(one >= 'A' && one <= 'Z') one += 32;
+ if (one > 0 && one <= 0x7f && (sym<'0' || sym>'9') ) sym = one;
+ }
+ Fl::e_keysym = Fl::e_original_keysym = sym;
+ /*NSLog(@"cocoaKeyboardHandler: keycode=%08x keysym=%08x mods=%08x symbol=%@ (%@)",
+ keyCode, sym, mods, [theEvent characters], [theEvent charactersIgnoringModifiers]);*/
+ // If there is text associated with this key, it will be filled in later.
+ Fl::e_length = 0;
+ Fl::e_text = (char*)"";
+}
+
+@interface FLTextInputContext : NSObject { // "emulates" NSTextInputContext before OS 10.6
+@public
+ FLTextView *edit;
+}
+-(BOOL)handleEvent:(NSEvent*)theEvent;
+@end
+@implementation FLTextInputContext
+-(BOOL)handleEvent:(NSEvent*)theEvent {
+ [self->edit setActive:YES];
+ [self->edit interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
+ [self->edit setActive:YES];
+ return YES;
+}
+@end
+
+@interface FLView : NSView <NSTextInput
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
+, NSTextInputClient
+#endif
+> {
+ BOOL in_key_event; // YES means keypress is being processed by handleEvent
+ BOOL need_handle; // YES means Fl::handle(FL_KEYBOARD,) is needed after handleEvent processing
+ NSInteger identifier;
+ NSRange selectedRange;
}
+ (void)prepareEtext:(NSString*)aString;
++ (void)concatEtext:(NSString*)aString;
- (id)init;
- (void)drawRect:(NSRect)rect;
- (BOOL)acceptsFirstResponder;
- (BOOL)acceptsFirstMouse:(NSEvent*)theEvent;
+- (void)resetCursorRects;
- (BOOL)performKeyEquivalent:(NSEvent*)theEvent;
- (void)mouseUp:(NSEvent *)theEvent;
- (void)rightMouseUp:(NSEvent *)theEvent;
@@ -1651,7 +1990,6 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
- (void)rightMouseDragged:(NSEvent *)theEvent;
- (void)otherMouseDragged:(NSEvent *)theEvent;
- (void)scrollWheel:(NSEvent *)theEvent;
-- (BOOL)handleKeyDown:(NSEvent *)theEvent;
- (void)keyDown:(NSEvent *)theEvent;
- (void)keyUp:(NSEvent *)theEvent;
- (void)flagsChanged:(NSEvent *)theEvent;
@@ -1660,24 +1998,36 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender;
- (void)draggingExited:(id < NSDraggingInfo >)sender;
- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal;
+- (FLTextInputContext*)FLinputContext;
+#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5
+- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange;
+- (void)setMarkedText:(id)aString selectedRange:(NSRange)newSelection replacementRange:(NSRange)replacementRange;
+- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange;
+- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange;
+- (NSInteger)windowLevel;
+#endif
@end
@implementation FLView
- (id)init
{
+ static NSInteger counter = 0;
self = [super init];
if (self) {
- next_compose_length = -1;
- in_key_event = false;
+ in_key_event = NO;
+ identifier = ++counter;
}
return self;
}
- (void)drawRect:(NSRect)rect
{
fl_lock_function();
+ through_drawRect = YES;
FLWindow *cw = (FLWindow*)[self window];
Fl_Window *w = [cw getFl_Window];
- handleUpdateEvent(w);
+ if (fl_x_to_redraw) fl_x_to_redraw->flush();
+ else handleUpdateEvent(w);
+ through_drawRect = NO;
fl_unlock_function();
}
@@ -1688,7 +2038,28 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
- (BOOL)performKeyEquivalent:(NSEvent*)theEvent
{
//NSLog(@"performKeyEquivalent:");
- return [self handleKeyDown:theEvent];
+ fl_lock_function();
+ cocoaKeyboardHandler(theEvent);
+ BOOL handled;
+ NSUInteger mods = [theEvent modifierFlags];
+ if ( (mods & NSControlKeyMask) || (mods & NSCommandKeyMask) ) {
+ NSString *s = [theEvent characters];
+ if ( (mods & NSShiftKeyMask) && (mods & NSCommandKeyMask) ) {
+ s = [s uppercaseString]; // US keyboards return lowercase letter in s if cmd-shift-key is hit
+ }
+ [FLView prepareEtext:s];
+ Fl::compose_state = 0;
+ handled = Fl::handle(FL_KEYBOARD, [(FLWindow*)[theEvent window] getFl_Window]);
+ }
+ else {
+ in_key_event = YES;
+ need_handle = NO;
+ handled = [[self performSelector:inputContextSEL] handleEvent:theEvent];
+ if (need_handle) handled = Fl::handle(FL_KEYBOARD, [(FLWindow*)[theEvent window] getFl_Window]);
+ in_key_event = NO;
+ }
+ fl_unlock_function();
+ return handled;
}
- (BOOL)acceptsFirstMouse:(NSEvent*)theEvent
{
@@ -1696,6 +2067,17 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
Fl_Window *first = Fl::first_window();
return (first == w || !first->modal());
}
+- (void)resetCursorRects {
+ Fl_Window *w = [(FLWindow*)[self window] getFl_Window];
+ Fl_X *i = Fl_X::i(w);
+ if (!i) return; // fix for STR #3128
+ // We have to have at least one cursor rect for invalidateCursorRectsForView
+ // to work, hence the "else" clause.
+ if (i->cursor)
+ [self addCursorRect:[self visibleRect] cursor:(NSCursor*)i->cursor];
+ else
+ [self addCursorRect:[self visibleRect] cursor:[NSCursor arrowCursor]];
+}
- (void)mouseUp:(NSEvent *)theEvent {
cocoaMouseHandler(theEvent);
}
@@ -1729,57 +2111,21 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
- (void)scrollWheel:(NSEvent *)theEvent {
cocoaMouseWheelHandler(theEvent);
}
-- (BOOL)handleKeyDown:(NSEvent *)theEvent {
- //NSLog(@"handleKeyDown");
+- (void)keyDown:(NSEvent *)theEvent {
+ //NSLog(@"keyDown:%@",[theEvent characters]);
fl_lock_function();
-
- Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window];
+ Fl_Window *window = [(FLWindow*)[theEvent window] getFl_Window];
Fl::first_window(window);
-
- next_compose_length = -1;
- // First let's process the raw key press
cocoaKeyboardHandler(theEvent);
-
- int no_text_key = false;
- static const int notext[] = { // keys that don't emit text
- FL_BackSpace, FL_Print, FL_Scroll_Lock, FL_Pause,
- FL_Insert, FL_Home, FL_Page_Up, FL_Delete, FL_End, FL_Page_Down,
- FL_Left, FL_Up, FL_Right, FL_Down,
- FL_Menu, FL_Num_Lock, FL_Help
- };
- static const int count = sizeof(notext)/sizeof(int);
- if (Fl::e_keysym > FL_F && Fl::e_keysym <= FL_F_Last) no_text_key = true;
- else for (int i=0; i < count; i++) {
- if (notext[i] == Fl::e_keysym) {
- no_text_key = true;
- break;
- }
- }
- if (!no_text_key && !(Fl::e_state & FL_META) ) {
- // Don't send cmd-<key> to interpretKeyEvents because it beeps.
- // Then we can let the OS have a stab at it and see if it thinks it
- // should result in some text
- NSText *edit = [[theEvent window] fieldEditor:YES forObject:nil];
- in_key_event = true;
- [edit interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
- in_key_event = false;
- }
- //NSLog(@"to text=%@ l=%d", [NSString stringWithUTF8String:Fl::e_text], Fl::e_length);
- int handled = Fl::handle(FL_KEYDOWN, window);
- // We have to update this after Fl::handle as it says what to do on the
- // _next_ input
- if (next_compose_length != -1)
- Fl::compose_state = next_compose_length;
-
+ in_key_event = YES;
+ need_handle = NO;
+ [[self performSelector:inputContextSEL] handleEvent:theEvent];
+ if (need_handle) Fl::handle(FL_KEYBOARD, window);
+ in_key_event = NO;
fl_unlock_function();
- return (handled ? YES : NO);
-}
-- (void)keyDown:(NSEvent *)theEvent {
- //NSLog(@"keyDown: ");
- [self handleKeyDown:theEvent];
}
- (void)keyUp:(NSEvent *)theEvent {
- //NSLog(@"keyUp: ");
+ //NSLog(@"keyUp:%@",[theEvent characters]);
fl_lock_function();
Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window];
Fl::first_window(window);
@@ -1863,8 +2209,8 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
CFStringGetCString(all, DragData, l + 1, kCFStringEncodingUTF8);
CFRelease(all);
}
- else if ( [[pboard types] containsObject:NSStringPboardType] ) {
- NSData *data = [pboard dataForType:NSStringPboardType];
+ else if ( [[pboard types] containsObject:utf8_format] ) {
+ NSData *data = [pboard dataForType:utf8_format];
DragData = (char *)malloc([data length] + 1);
[data getBytes:DragData];
DragData[[data length]] = 0;
@@ -1902,6 +2248,15 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
return NSDragOperationGeneric;
}
+- (FLTextInputContext*)FLinputContext { // used only if OS < 10.6 to replace [NSView inputContext]
+ static FLTextInputContext *context = NULL;
+ if (!context) {
+ context = [[FLTextInputContext alloc] init];
+ }
+ context->edit = (FLTextView*)[[self window] fieldEditor:YES forObject:nil];
+ return context;
+}
+
+ (void)prepareEtext:(NSString*)aString {
// fills Fl::e_text with UTF-8 encoded aString using an adequate memory allocation
static char *received_utf8 = NULL;
@@ -1923,69 +2278,124 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
Fl::e_length = l;
}
-// These functions implement text input.
-// Only two-stroke character composition works at this point.
-// Needs much elaboration to fully support CJK text input,
-// but this is the way to go.
++ (void)concatEtext:(NSString*)aString {
+ // extends Fl::e_text with aString
+ NSString *newstring = [[NSString stringWithUTF8String:Fl::e_text] stringByAppendingString:aString];
+ [FLView prepareEtext:newstring];
+}
+
- (void)doCommandBySelector:(SEL)aSelector {
+ NSString *s = [[NSApp currentEvent] characters];
+ //NSLog(@"doCommandBySelector:%s text='%@'",sel_getName(aSelector), s);
+ s = [s substringFromIndex:[s length] - 1];
+ [FLView prepareEtext:s]; // use the last character of the event; necessary for deadkey + Tab
+ Fl_Window *target = [(FLWindow*)[self window] getFl_Window];
+ Fl::handle(FL_KEYBOARD, target);
}
- (void)insertText:(id)aString {
+ [self insertText:aString replacementRange:NSMakeRange(NSNotFound, 0)];
+}
+
+- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange {
NSString *received;
if ([aString isKindOfClass:[NSAttributedString class]]) {
received = [(NSAttributedString*)aString string];
} else {
received = (NSString*)aString;
}
- //NSLog(@"insertText: received=%@",received);
-
- if (!in_key_event) fl_lock_function();
- [FLView prepareEtext:received];
- // We can get called outside of key events (e.g. from the character
- // palette). Transform such actions to FL_PASTE events.
- if (!in_key_event) {
- Fl_Window *target = [(FLWindow*)[self window] getFl_Window];
- Fl::handle(FL_PASTE, target);
- // for some reason, the window does not redraw until the next mouse move or button push
- // sending a 'redraw()' or 'awake()' does not solve the issue!
- Fl::flush();
- }
- if (!in_key_event) fl_unlock_function();
+ /*NSLog(@"insertText='%@' l=%d Fl::compose_state=%d range=%d,%d",
+ received,strlen([received UTF8String]),Fl::compose_state,replacementRange.location,replacementRange.length);*/
+ fl_lock_function();
+ Fl_Window *target = [(FLWindow*)[self window] getFl_Window];
+ while (replacementRange.length--) { // delete replacementRange.length characters before insertion point
+ int saved_keysym = Fl::e_keysym;
+ Fl::e_keysym = FL_BackSpace;
+ Fl::handle(FL_KEYBOARD, target);
+ Fl::e_keysym = saved_keysym;
+ }
+ if (in_key_event && Fl_X::next_marked_length && Fl::e_length) {
+ // if setMarkedText + insertText is sent during handleEvent, text cannot be concatenated in single FL_KEYBOARD event
+ Fl::handle(FL_KEYBOARD, target);
+ Fl::e_length = 0;
+ }
+ if (in_key_event && Fl::e_length) [FLView concatEtext:received];
+ else [FLView prepareEtext:received];
+ Fl_X::next_marked_length = 0;
+ // We can get called outside of key events (e.g., from the character palette, from CJK text input).
+ BOOL palette = !(in_key_event || Fl::compose_state);
+ if (palette) Fl::e_keysym = 0;
+ // YES if key has text attached
+ BOOL has_text_key = Fl::e_keysym <= '~' || Fl::e_keysym == FL_Iso_Key ||
+ (Fl::e_keysym >= FL_KP && Fl::e_keysym <= FL_KP_Last && Fl::e_keysym != FL_KP_Enter);
+ // insertText sent during handleEvent of a key without text cannot be processed in a single FL_KEYBOARD event.
+ // Occurs with deadkey followed by non-text key
+ if (!in_key_event || !has_text_key) {
+ Fl::handle(FL_KEYBOARD, target);
+ Fl::e_length = 0;
+ }
+ else need_handle = YES;
+ selectedRange = NSMakeRange(100, 0); // 100 is an arbitrary value
+ // for some reason, with the palette, the window does not redraw until the next mouse move or button push
+ // sending a 'redraw()' or 'awake()' does not solve the issue!
+ if (palette) Fl::flush();
+ if (fl_mac_os_version < 100600) [(FLTextView*)[[self window] fieldEditor:YES forObject:nil] setActive:NO];
+ fl_unlock_function();
}
- (void)setMarkedText:(id)aString selectedRange:(NSRange)newSelection {
+ [self setMarkedText:aString selectedRange:newSelection replacementRange:NSMakeRange(NSNotFound, 0)];
+}
+
+- (void)setMarkedText:(id)aString selectedRange:(NSRange)newSelection replacementRange:(NSRange)replacementRange {
NSString *received;
- if (newSelection.location == 0) {
- [self unmarkText];
- return;
- }
if ([aString isKindOfClass:[NSAttributedString class]]) {
received = [(NSAttributedString*)aString string];
} else {
received = (NSString*)aString;
}
- //NSLog(@"setMarkedText: %@ %d %d",received,newSelection.location,newSelection.length);
- // This code creates the OS X behaviour of seeing dead keys as things
- // are being composed.
- next_compose_length = newSelection.location;
- [FLView prepareEtext:received];
- //NSLog(@"Fl::e_text=%@ Fl::e_length=%d next_compose_length=%d", received, Fl::e_length, next_compose_length);
+ fl_lock_function();
+ /*NSLog(@"setMarkedText:%@ l=%d newSelection=%d,%d Fl::compose_state=%d replacement=%d,%d",
+ received, strlen([received UTF8String]), newSelection.location, newSelection.length, Fl::compose_state,
+ replacementRange.location, replacementRange.length);*/
+ Fl_Window *target = [(FLWindow*)[self window] getFl_Window];
+ while (replacementRange.length--) { // delete replacementRange.length characters before insertion point
+ Fl::e_keysym = FL_BackSpace;
+ Fl::compose_state = 0;
+ Fl_X::next_marked_length = 0;
+ Fl::handle(FL_KEYBOARD, target);
+ Fl::e_keysym = 'a'; // pretend a letter key was hit
+ }
+ if (in_key_event && Fl_X::next_marked_length && Fl::e_length) {
+ // if setMarkedText + setMarkedText is sent during handleEvent, text cannot be concatenated in single FL_KEYBOARD event
+ Fl::handle(FL_KEYBOARD, target);
+ Fl::e_length = 0;
+ }
+ if (in_key_event && Fl::e_length) [FLView concatEtext:received];
+ else [FLView prepareEtext:received];
+ Fl_X::next_marked_length = strlen([received UTF8String]);
+ if (!in_key_event) Fl::handle( FL_KEYBOARD, target);
+ else need_handle = YES;
+ selectedRange = NSMakeRange(100, newSelection.length);
+ fl_unlock_function();
}
- (void)unmarkText {
fl_lock_function();
- Fl::compose_state = 0;
+ Fl::reset_marked_text();
fl_unlock_function();
//NSLog(@"unmarkText");
}
- (NSRange)selectedRange {
+ Fl_Widget *w = Fl::focus();
+ if (w && w->use_accents_menu()) return selectedRange;
return NSMakeRange(NSNotFound, 0);
}
- (NSRange)markedRange {
- //NSLog(@"markedRange ?");
- return NSMakeRange(NSNotFound, Fl::compose_state);
+ //NSLog(@"markedRange=%d %d", Fl::compose_state > 0?0:NSNotFound, Fl::compose_state);
+ return NSMakeRange(Fl::compose_state > 0?0:NSNotFound, Fl::compose_state);
}
- (BOOL)hasMarkedText {
@@ -1994,6 +2404,9 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
}
- (NSAttributedString *)attributedSubstringFromRange:(NSRange)aRange {
+ return [self attributedSubstringForProposedRange:aRange actualRange:NULL];
+}
+- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange {
//NSLog(@"attributedSubstringFromRange: %d %d",aRange.location,aRange.length);
return nil;
}
@@ -2003,28 +2416,44 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
}
- (NSRect)firstRectForCharacterRange:(NSRange)aRange {
+ return [self firstRectForCharacterRange:aRange actualRange:NULL];
+}
+- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange {
+ //NSLog(@"firstRectForCharacterRange %d %d actualRange=%p",aRange.location, aRange.length,actualRange);
NSRect glyphRect;
fl_lock_function();
Fl_Widget *focus = Fl::focus();
- Fl_Window *wfocus = focus->window();
- while (wfocus->window()) wfocus = wfocus->window();
+ Fl_Window *wfocus = [(FLWindow*)[self window] getFl_Window];
+ if (!focus) focus = wfocus;
glyphRect.size.width = 0;
- if (dynamic_cast<Fl_Text_Display*>(focus) != NULL) {
- int x, y;
- Fl_Text_Display *current = (Fl_Text_Display*)focus;
- current->position_to_xy( current->insert_position(), &x, &y );
+ int x, y, height;
+ if (Fl_X::insertion_point_location(&x, &y, &height)) {
glyphRect.origin.x = (CGFloat)x;
- glyphRect.origin.y = (CGFloat)y + current->textsize();
- glyphRect.size.height = current->textsize();
+ glyphRect.origin.y = (CGFloat)y;
} else {
- glyphRect.origin.x = focus->x();
- glyphRect.origin.y = focus->y() + focus->h();
- glyphRect.size.height = 12;
+ if (focus->as_window()) {
+ glyphRect.origin.x = 0;
+ glyphRect.origin.y = focus->h();
+ }
+ else {
+ glyphRect.origin.x = focus->x();
+ glyphRect.origin.y = focus->y() + focus->h();
+ }
+ height = 12;
+ }
+ glyphRect.size.height = height;
+ Fl_Window *win = focus->as_window();
+ if (!win) win = focus->window();
+ while (win != NULL && win != wfocus) {
+ glyphRect.origin.x += win->x();
+ glyphRect.origin.y += win->y();
+ win = win->window();
}
// Convert the rect to screen coordinates
glyphRect.origin.y = wfocus->h() - glyphRect.origin.y;
- glyphRect.origin = [[self window] convertBaseToScreen:glyphRect.origin];
+ glyphRect.origin = [(FLWindow*)[self window] convertBaseToScreen:glyphRect.origin];
+ if (actualRange) *actualRange = aRange;
fl_unlock_function();
return glyphRect;
}
@@ -2033,8 +2462,12 @@ static void q_set_window_title(NSWindow *nsw, const char * name, const char *mi
return 0;
}
+- (NSInteger)windowLevel {
+ return [[self window] level];
+}
+
- (NSInteger)conversationIdentifier {
- return (NSInteger)self;
+ return identifier;
}
@end
@@ -2057,6 +2490,29 @@ void Fl_Window::fullscreen_off_x(int X, int Y, int W, int H) {
}
/*
+ * Initialize the given port for redraw and call the window's flush() to actually draw the content
+ */
+void Fl_X::flush()
+{
+ if (through_drawRect || w->as_gl_window()) {
+ make_current_counts = 1;
+ w->flush();
+ make_current_counts = 0;
+ Fl_X::q_release_context();
+ return;
+ }
+ // have Cocoa immediately redraw the window's view
+ FLView *view = (FLView*)[fl_xid(w) contentView];
+ fl_x_to_redraw = this;
+ [view setNeedsDisplay:YES];
+ // will send the drawRect: message to the window's view after having prepared the adequate NSGraphicsContext
+ [view displayIfNeededIgnoringOpacity];
+ fl_x_to_redraw = NULL;
+}
+
+//bool Fl_X::make_shaped = false;
+
+/*
* go ahead, create that (sub)window
*/
void Fl_X::make(Fl_Window* w)
@@ -2069,8 +2525,9 @@ void Fl_X::make(Fl_Window* w)
x->other_xid = 0;
x->region = 0;
x->subRegion = 0;
- x->cursor = fl_default_cursor;
+ x->cursor = NULL;
x->gc = 0; // stay 0 for Quickdraw; fill with CGContext for Quartz
+ w->set_visible();
Fl_Window *win = w->window();
Fl_X *xo = Fl_X::i(win);
if (xo) {
@@ -2092,7 +2549,7 @@ void Fl_X::make(Fl_Window* w)
}
if (w->as_gl_window()) { // if creating a sub-GL-window
while (win->window()) win = win->window();
- [Fl_X::i(win)->xid setContainsGLsubwindow:YES];
+ [Fl_X::i(win)->xid containsGLsubwindow:YES];
}
fl_show_iconic = 0;
}
@@ -2109,7 +2566,7 @@ void Fl_X::make(Fl_Window* w)
int hp = w->h();
if (w->size_range_set) {
if ( w->minh != w->maxh || w->minw != w->maxw) {
- winstyle |= NSResizableWindowMask;
+ if (w->border()) winstyle |= NSResizableWindowMask;
}
} else {
if (w->resizable()) {
@@ -2117,7 +2574,7 @@ void Fl_X::make(Fl_Window* w)
int minw = o->w(); if (minw > 100) minw = 100;
int minh = o->h(); if (minh > 100) minh = 100;
w->size_range(w->w() - o->w() + minw, w->h() - o->h() + minh, 0, 0);
- winstyle |= NSResizableWindowMask;
+ if (w->border()) winstyle |= NSResizableWindowMask;
} else {
w->size_range(w->w(), w->h(), w->w(), w->h());
}
@@ -2127,18 +2584,17 @@ void Fl_X::make(Fl_Window* w)
if (!fake_X_wm(w, xwm, ywm, bt, bx, by)) {
// menu windows and tooltips
if (w->modal()||w->tooltip_window()) {
- winstyle = NSBorderlessWindowMask;
- winlevel = NSModalPanelWindowLevel;
- } else {
- winstyle = NSBorderlessWindowMask;
+ winlevel = modal_window_level();
}
- } else if (w->modal()) {
+ //winstyle = NSBorderlessWindowMask;
+ }
+ if (w->modal()) {
winstyle &= ~NSMiniaturizableWindowMask;
// winstyle &= ~(NSResizableWindowMask | NSMiniaturizableWindowMask);
- winlevel = NSModalPanelWindowLevel;
+ winlevel = modal_window_level();
}
else if (w->non_modal()) {
- winlevel = NSFloatingWindowLevel;
+ winlevel = non_modal_window_level();
}
if (by+bt) {
@@ -2165,16 +2621,39 @@ void Fl_X::make(Fl_Window* w)
x->other_xid = 0; // room for doublebuffering image map. On OS X this is only used by overlay windows
x->region = 0;
x->subRegion = 0;
- x->cursor = fl_default_cursor;
+ x->cursor = NULL;
x->xidChildren = 0;
x->xidNext = 0;
x->gc = 0;
NSRect crect;
if (w->fullscreen_active()) {
- int sx, sy, sw, sh;
- Fl::screen_xywh(sx, sy, sw, sh, w->x(), w->y(), w->w(), w->h());
- w->resize(sx, sy, sw, sh);
+ int top, bottom, left, right;
+ int sx, sy, sw, sh, X, Y, W, H;
+
+ top = w->fullscreen_screen_top;
+ bottom = w->fullscreen_screen_bottom;
+ left = w->fullscreen_screen_left;
+ right = w->fullscreen_screen_right;
+
+ if ((top < 0) || (bottom < 0) || (left < 0) || (right < 0)) {
+ top = Fl::screen_num(w->x(), w->y(), w->w(), w->h());
+ bottom = top;
+ left = top;
+ right = top;
+ }
+
+ Fl::screen_xywh(sx, sy, sw, sh, top);
+ Y = sy;
+ Fl::screen_xywh(sx, sy, sw, sh, bottom);
+ H = sy + sh - Y;
+ Fl::screen_xywh(sx, sy, sw, sh, left);
+ X = sx;
+ Fl::screen_xywh(sx, sy, sw, sh, right);
+ W = sx + sw - X;
+
+ w->resize(X, Y, W, H);
+
winstyle = NSBorderlessWindowMask;
winlevel = NSStatusWindowLevel;
}
@@ -2188,6 +2667,10 @@ void Fl_X::make(Fl_Window* w)
[cw setFrameOrigin:crect.origin];
[cw setHasShadow:YES];
[cw setAcceptsMouseMovedEvents:YES];
+ if (w->shape_data_) {
+ [cw setOpaque:NO]; // shaped windows must be non opaque
+ [cw setBackgroundColor:[NSColor clearColor]]; // and with transparent background color
+ }
x->xid = cw;
x->w = w; w->i = x;
x->wait_for_expose = 1;
@@ -2195,6 +2678,7 @@ void Fl_X::make(Fl_Window* w)
Fl_X::first = x;
FLView *myview = [[FLView alloc] init];
[cw setContentView:myview];
+ [myview release];
[cw setLevel:winlevel];
q_set_window_title(cw, w->label(), w->iconlabel());
@@ -2212,12 +2696,10 @@ void Fl_X::make(Fl_Window* w)
[cw setAlphaValue:0.97];
}
// Install DnD handlers
- [myview registerForDraggedTypes:[NSArray arrayWithObjects:
- NSStringPboardType, NSFilenamesPboardType, nil]];
+ [myview registerForDraggedTypes:[NSArray arrayWithObjects:utf8_format, NSFilenamesPboardType, nil]];
if ( ! Fl_X::first->next ) {
// if this is the first window, we need to bring the application to the front
- ProcessSerialNumber psn = { 0, kCurrentProcess };
- SetFrontProcess( &psn );
+ [NSApp activateIgnoringOtherApps:YES];
}
if (w->size_range_set) w->size_range_();
@@ -2231,7 +2713,7 @@ void Fl_X::make(Fl_Window* w)
w->set_visible();
if ( w->border() || (!w->modal() && !w->tooltip_window()) ) Fl::handle(FL_FOCUS, w);
Fl::first_window(w);
- [cw setDelegate:[NSApp delegate]];
+ [cw setDelegate:[FLWindowDelegate createOnce]];
if (fl_show_iconic) {
fl_show_iconic = 0;
[cw miniaturize:nil];
@@ -2316,7 +2798,7 @@ void Fl_Window::show() {
labeltype(FL_NO_LABEL);
}
Fl_Tooltip::exit(this);
- if (!shown() || !i) {
+ if (!shown()) {
Fl_X::make(this);
} else {
if ( !parent() ) {
@@ -2388,9 +2870,34 @@ void Fl_Window::resize(int X,int Y,int W,int H) {
/*
* make all drawing go into this window (called by subclass flush() impl.)
+
+ This can be called in 3 different instances:
+
+ 1) When a window is created, resized, or deminiaturized.
+ The system sends the drawRect: message to the window's view after having prepared the current graphics context
+ to draw to this view. Variable through_drawRect is YES, and fl_x_to_redraw is NULL. Processing of drawRect: calls
+ handleUpdateEvent() that calls Fl_X::flush() for the window and its subwindows. Fl_X::flush() calls
+ Fl_Window::flush() that calls Fl_Window::make_current() that only needs to identify the graphics port of the
+ current graphics context. The window's draw() function is then executed.
+
+ 2) At each round of the FLTK event loop.
+ Fl::flush() is called, that calls Fl_X::flush() on each window that needs drawing. Fl_X::flush() sets
+ fl_x_to_redraw to this and sends the displayIfNeededIgnoringOpacity message to the window's view.
+ This message makes the system prepare the current graphics context adequately for drawing to this view, and
+ send it the drawRect: message which sets through_drawRect to YES. Processing of the drawRect: message calls
+ Fl_X::flush() for the window which proceeds as in 1) above.
+
+ 3) An FLTK application can call Fl_Window::make_current() at any time before it draws to a window.
+ This occurs for instance in the idle callback function of the mandelbrot test program. Variable through_drawRect is NO,
+ so Fl_Window::make_current() creates a new graphics context adequate for the window.
+ Subsequent drawing requests go to this window. CAUTION: it's not possible to call Fl::wait(), Fl::check()
+ nor Fl::ready() while in the draw() function of a widget. Use an idle callback instead.
+
*/
void Fl_Window::make_current()
{
+ if (make_current_counts > 1) return;
+ if (make_current_counts) make_current_counts++;
Fl_X::q_release_context();
fl_window = i->xid;
current_ = this;
@@ -2404,12 +2911,9 @@ void Fl_Window::make_current()
yp += win->y();
win = (Fl_Window*)win->window();
}
-
- NSView *current_focus = [NSView focusView];
- // sometimes current_focus is set to a non-FLTK view: don't touch that
- if ( [current_focus isKindOfClass:[FLView class]] ) [current_focus unlockFocus];
- [[i->xid contentView] lockFocus];
- i->gc = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
+ NSGraphicsContext *nsgc = through_drawRect ? [NSGraphicsContext currentContext] :
+ [NSGraphicsContext graphicsContextWithWindow:fl_window];
+ i->gc = (CGContextRef)[nsgc graphicsPort];
fl_gc = i->gc;
Fl_Region fl_window_region = XRectangleRegion(0,0,w(),h());
if ( ! this->window() ) {
@@ -2477,7 +2981,8 @@ void Fl_X::q_clear_clipping() {
void Fl_X::q_release_context(Fl_X *x) {
if (x && x->gc!=fl_gc) return;
if (!fl_gc) return;
- CGContextRestoreGState(fl_gc); // matches the CGContextSaveGState of make_current
+ CGContextRestoreGState(fl_gc); // KEEP IT: matches the CGContextSaveGState of make_current
+ CGContextFlush(fl_gc);
fl_gc = 0;
#if defined(FLTK_USE_CAIRO)
if (Fl::cairo_autolink_context()) Fl::cairo_make_current((Fl_Window*) 0); // capture gc changes automatically to update the cairo context adequately
@@ -2514,35 +3019,55 @@ static void convert_crlf(char * s, size_t len)
}
// fltk 1.3 clipboard support constant definitions:
-const CFStringRef flavorNames[] = {
- CFSTR("public.utf16-plain-text"),
- CFSTR("public.utf8-plain-text"),
- CFSTR("com.apple.traditional-mac-plain-text") };
-const CFStringEncoding encodings[] = {
- kCFStringEncodingUnicode,
- kCFStringEncodingUTF8,
- kCFStringEncodingMacRoman};
-const size_t handledFlavorsCount = sizeof(encodings)/sizeof(CFStringEncoding);
+static NSString *calc_utf8_format(void)
+{
+#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6
+#define NSPasteboardTypeString @"public.utf8-plain-text"
+#endif
+ if (fl_mac_os_version >= 100600) return NSPasteboardTypeString;
+ return NSStringPboardType;
+}
// clipboard variables definitions :
-char *fl_selection_buffer[2];
-int fl_selection_length[2];
+char *fl_selection_buffer[2] = {NULL, NULL};
+int fl_selection_length[2] = {0, 0};
static int fl_selection_buffer_length[2];
-static PasteboardRef myPasteboard = 0;
-static void allocatePasteboard() {
- if (!myPasteboard)
- PasteboardCreate(kPasteboardClipboard, &myPasteboard);
+static PasteboardRef allocatePasteboard(void)
+{
+ PasteboardRef clip;
+ PasteboardCreate(kPasteboardClipboard, &clip); // requires Mac OS 10.3
+ return clip;
}
+static PasteboardRef myPasteboard = allocatePasteboard();
+extern void fl_trigger_clipboard_notify(int source);
+
+void fl_clipboard_notify_change() {
+ // No need to do anything here...
+}
+
+static void clipboard_check(void)
+{
+ PasteboardSyncFlags flags;
+
+ flags = PasteboardSynchronize(myPasteboard); // requires Mac OS 10.3
+
+ if (!(flags & kPasteboardModified))
+ return;
+ if (flags & kPasteboardClientIsOwner)
+ return;
+
+ fl_trigger_clipboard_notify(1);
+}
/*
* create a selection
- * owner: widget that created the selection
* stuff: pointer to selected data
- * size of selected data
+ * len: size of selected data
+ * type: always "plain/text" for now
*/
-void Fl::copy(const char *stuff, int len, int clipboard) {
+void Fl::copy(const char *stuff, int len, int clipboard, const char *type) {
if (!stuff || len<0) return;
if (len+1 > fl_selection_buffer_length[clipboard]) {
delete[] fl_selection_buffer[clipboard];
@@ -2553,84 +3078,180 @@ void Fl::copy(const char *stuff, int len, int clipboard) {
fl_selection_buffer[clipboard][len] = 0; // needed for direct paste
fl_selection_length[clipboard] = len;
if (clipboard) {
- allocatePasteboard();
- OSStatus err = PasteboardClear(myPasteboard);
- if (err!=noErr) return; // clear did not work, maybe not owner of clipboard.
- PasteboardSynchronize(myPasteboard);
CFDataRef text = CFDataCreate(kCFAllocatorDefault, (UInt8*)fl_selection_buffer[1], len);
if (text==NULL) return; // there was a pb creating the object, abort.
- err=PasteboardPutItemFlavor(myPasteboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), text, 0);
+ NSPasteboard *clip = [NSPasteboard generalPasteboard];
+ [clip declareTypes:[NSArray arrayWithObject:utf8_format] owner:nil];
+ [clip setData:(NSData*)text forType:utf8_format];
CFRelease(text);
}
}
+static int get_plain_text_from_clipboard(char **buffer, int previous_length)
+{
+ NSInteger length = 0;
+ NSPasteboard *clip = [NSPasteboard generalPasteboard];
+ NSString *found = [clip availableTypeFromArray:[NSArray arrayWithObjects:utf8_format, @"public.utf16-plain-text", @"com.apple.traditional-mac-plain-text", nil]];
+ if (found) {
+ NSData *data = [clip dataForType:found];
+ if (data) {
+ NSInteger len;
+ char *aux_c = NULL;
+ if (![found isEqualToString:utf8_format]) {
+ NSString *auxstring;
+ auxstring = (NSString *)CFStringCreateWithBytes(NULL,
+ (const UInt8*)[data bytes],
+ [data length],
+ [found isEqualToString:@"public.utf16-plain-text"] ? kCFStringEncodingUnicode : kCFStringEncodingMacRoman,
+ false);
+ aux_c = strdup([auxstring UTF8String]);
+ [auxstring release];
+ len = strlen(aux_c) + 1;
+ }
+ else len = [data length] + 1;
+ if ( len >= previous_length ) {
+ length = len;
+ delete[] *buffer;
+ *buffer = new char[len];
+ }
+ if (![found isEqualToString:utf8_format]) {
+ strcpy(*buffer, aux_c);
+ free(aux_c);
+ }
+ else {
+ [data getBytes:*buffer];
+ }
+ (*buffer)[len - 1] = 0;
+ length = len - 1;
+ convert_crlf(*buffer, len - 1); // turn all \r characters into \n:
+ Fl::e_clipboard_type = Fl::clipboard_plain_text;
+ }
+ }
+ return length;
+}
+
+static Fl_Image* get_image_from_clipboard()
+{
+ Fl_RGB_Image *image = NULL;
+ uchar *imagedata;
+ NSBitmapImageRep *bitmap;
+ NSPasteboard *clip = [NSPasteboard generalPasteboard];
+ NSArray *present = [clip types]; // types in pasteboard in order of decreasing preference
+ NSArray *possible = [NSArray arrayWithObjects:@"com.adobe.pdf", @"public.tiff", @"com.apple.pict", nil];
+ NSString *found = nil;
+ NSUInteger rank;
+ for (rank = 0; rank < [present count]; rank++) { // find first of possible types present in pasteboard
+ for (NSUInteger i = 0; i < [possible count]; i++) {
+ if ([[present objectAtIndex:rank] isEqualToString:[possible objectAtIndex:i]]) {
+ found = [present objectAtIndex:rank];
+ goto after_loop;
+ }
+ }
+ }
+after_loop:
+ if (found) {
+ NSData *data = [clip dataForType:found];
+ if (data) {
+ if ([found isEqualToString:@"public.tiff"]) {
+ bitmap = [NSBitmapImageRep imageRepWithData:data];
+ int bpp = [bitmap bytesPerPlane];
+ int bpr = [bitmap bytesPerRow];
+ int depth = [bitmap samplesPerPixel], w = bpr/depth, h = bpp/bpr;
+ imagedata = new uchar[w * h * depth];
+ memcpy(imagedata, [bitmap bitmapData], w * h * depth);
+ image = new Fl_RGB_Image(imagedata, w, h, depth);
+ image->alloc_array = 1;
+ }
+ else if ([found isEqualToString:@"com.adobe.pdf"] || [found isEqualToString:@"com.apple.pict"]) {
+ NSRect rect;
+ NSImageRep *vectorial;
+ NSAffineTransform *dilate = [NSAffineTransform transform];
+ if ([found isEqualToString:@"com.adobe.pdf"] ) {
+ vectorial = [NSPDFImageRep imageRepWithData:data];
+ rect = [(NSPDFImageRep*)vectorial bounds]; // in points = 1/72 inch
+ Fl_Window *win = Fl::first_window();
+ int screen_num = win ? Fl::screen_num(win->x(), win->y(), win->w(), win->h()) : 0;
+ float hr, vr;
+ Fl::screen_dpi(hr, vr, screen_num); // 1 inch = hr pixels = 72 points -> hr/72 pixel/point
+ CGFloat scale = hr/72;
+ [dilate scaleBy:scale];
+ rect.size.width *= scale;
+ rect.size.height *= scale;
+ rect = NSIntegralRect(rect);
+ }
+ else {
+ vectorial = [NSPICTImageRep imageRepWithData:data];
+ rect = [(NSPICTImageRep*)vectorial boundingBox]; // in pixel, no scaling required
+ }
+ imagedata = new uchar[(int)(rect.size.width * rect.size.height) * 4];
+ memset(imagedata, -1, (int)(rect.size.width * rect.size.height) * 4);
+ bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&imagedata
+ pixelsWide:rect.size.width
+ pixelsHigh:rect.size.height
+ bitsPerSample:8
+ samplesPerPixel:3
+ hasAlpha:NO
+ isPlanar:NO
+ colorSpaceName:NSDeviceRGBColorSpace
+ bytesPerRow:rect.size.width*4
+ bitsPerPixel:32];
+ NSDictionary *dict = [NSDictionary dictionaryWithObject:bitmap
+ forKey:NSGraphicsContextDestinationAttributeName];
+ NSGraphicsContext *oldgc = [NSGraphicsContext currentContext];
+ [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithAttributes:dict]];
+ [dilate concat];
+ [vectorial draw];
+ [NSGraphicsContext setCurrentContext:oldgc];
+ [bitmap release];
+ image = new Fl_RGB_Image(imagedata, rect.size.width, rect.size.height, 4);
+ image->alloc_array = 1;
+ }
+ Fl::e_clipboard_type = Fl::clipboard_image;
+ }
+ }
+ return image;
+}
+
// Call this when a "paste" operation happens:
-void Fl::paste(Fl_Widget &receiver, int clipboard) {
+void Fl::paste(Fl_Widget &receiver, int clipboard, const char *type) {
+ if (type[0] == 0) type = Fl::clipboard_plain_text;
if (clipboard) {
- // see if we own the selection, if not go get it:
- fl_selection_length[1] = 0;
- OSStatus err = noErr;
- Boolean found = false;
- CFDataRef flavorData = NULL;
- CFStringEncoding encoding = 0;
-
- allocatePasteboard();
- PasteboardSynchronize(myPasteboard);
- ItemCount nFlavor = 0, i, j;
- err = PasteboardGetItemCount(myPasteboard, &nFlavor);
- if (err==noErr) {
- for (i=1; i<=nFlavor; i++) {
- PasteboardItemID itemID = 0;
- CFArrayRef flavorTypeArray = NULL;
- found = false;
- err = PasteboardGetItemIdentifier(myPasteboard, i, &itemID);
- if (err!=noErr) continue;
- err = PasteboardCopyItemFlavors(myPasteboard, itemID, &flavorTypeArray);
- if (err!=noErr) {
- if (flavorTypeArray) {CFRelease(flavorTypeArray); flavorTypeArray = NULL;}
- continue;
- }
- CFIndex flavorCount = CFArrayGetCount(flavorTypeArray);
- for (j = 0; j < handledFlavorsCount; j++) {
- for (CFIndex flavorIndex=0; flavorIndex<flavorCount; flavorIndex++) {
- CFStringRef flavorType = (CFStringRef)CFArrayGetValueAtIndex(flavorTypeArray, flavorIndex);
- if (UTTypeConformsTo(flavorType, flavorNames[j])) {
- err = PasteboardCopyItemFlavorData( myPasteboard, itemID, flavorNames[j], &flavorData );
- if (err != noErr) continue;
- encoding = encodings[j];
- found = true;
- break;
- }
- }
- if (found) break;
- }
- if (flavorTypeArray) {CFRelease(flavorTypeArray); flavorTypeArray = NULL;}
- if (found) break;
+ Fl::e_clipboard_type = "";
+ if (strcmp(type, Fl::clipboard_plain_text) == 0) {
+ fl_selection_length[1] = get_plain_text_from_clipboard( &fl_selection_buffer[1], fl_selection_length[1]);
}
- if (found) {
- CFIndex len = CFDataGetLength(flavorData);
- CFStringRef mycfs = CFStringCreateWithBytes(NULL, CFDataGetBytePtr(flavorData), len, encoding, false);
- CFRelease(flavorData);
- len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(mycfs), kCFStringEncodingUTF8) + 1;
- if ( len >= fl_selection_buffer_length[1] ) {
- fl_selection_buffer_length[1] = len;
- delete[] fl_selection_buffer[1];
- fl_selection_buffer[1] = new char[len];
- }
- CFStringGetCString(mycfs, fl_selection_buffer[1], len, kCFStringEncodingUTF8);
- CFRelease(mycfs);
- len = strlen(fl_selection_buffer[1]);
- fl_selection_length[1] = len;
- convert_crlf(fl_selection_buffer[1],len); // turn all \r characters into \n:
+ else if (strcmp(type, Fl::clipboard_image) == 0) {
+ Fl::e_clipboard_data = get_image_from_clipboard( );
+ if (Fl::e_clipboard_data) {
+ int done = receiver.handle(FL_PASTE);
+ Fl::e_clipboard_type = "";
+ if (done == 0) {
+ delete (Fl_Image*)Fl::e_clipboard_data;
+ Fl::e_clipboard_data = NULL;
+ }
}
- }
+ return;
+ }
+ else
+ fl_selection_length[1] = 0;
}
Fl::e_text = fl_selection_buffer[clipboard];
Fl::e_length = fl_selection_length[clipboard];
- if (!Fl::e_text) Fl::e_text = (char *)"";
+ if (!Fl::e_length) Fl::e_text = (char *)"";
receiver.handle(FL_PASTE);
}
+int Fl::clipboard_contains(const char *type) {
+ NSString *found = nil;
+ if (strcmp(type, Fl::clipboard_plain_text) == 0) {
+ found = [[NSPasteboard generalPasteboard] availableTypeFromArray:[NSArray arrayWithObjects:utf8_format, @"public.utf16-plain-text", @"com.apple.traditional-mac-plain-text", nil]];
+ }
+ else if (strcmp(type, Fl::clipboard_image) == 0) {
+ found = [[NSPasteboard generalPasteboard] availableTypeFromArray:[NSArray arrayWithObjects:@"public.tiff", @"com.adobe.pdf", @"com.apple.pict", nil]];
+ }
+ return found != nil;
+}
+
int Fl_X::unlink(Fl_X *start) {
if (start) {
Fl_X *pc = start;
@@ -2674,11 +3295,6 @@ void Fl_X::relink(Fl_Window *w, Fl_Window *wp) {
void Fl_X::destroy() {
// subwindows share their xid with their parent window, so should not close it
if (!subwindow && w && !w->parent() && xid) {
- NSView *topview = [xid contentView];
- if ( [NSView focusView] == topview ) {
- [topview unlockFocus];
- }
- [topview release];
[xid close];
}
}
@@ -2692,6 +3308,10 @@ void Fl_X::map() {
Fl_X::relink(w, w->window() );
w->redraw();
}
+ if (cursor) {
+ [(NSCursor*)cursor release];
+ cursor = NULL;
+ }
}
void Fl_X::unmap() {
@@ -2780,84 +3400,152 @@ void Fl_X::collapse() {
static NSImage *CGBitmapContextToNSImage(CGContextRef c)
// the returned NSImage is autoreleased
{
+ NSImage* image;
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
+ if (fl_mac_os_version >= 100600) {
+ CGImageRef cgimg = CGBitmapContextCreateImage(c); // requires 10.4
+ image = [[NSImage alloc] initWithCGImage:cgimg size:NSZeroSize]; // requires 10.6
+ CFRelease(cgimg);
+ }
+ else
+#endif
+ {
+ unsigned char *pdata = (unsigned char *)CGBitmapContextGetData(c);
+ NSBitmapImageRep *imagerep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&pdata
+ pixelsWide:CGBitmapContextGetWidth(c)
+ pixelsHigh:CGBitmapContextGetHeight(c)
+ bitsPerSample:8
+ samplesPerPixel:4
+ hasAlpha:YES
+ isPlanar:NO
+ colorSpaceName:NSDeviceRGBColorSpace
+ bytesPerRow:CGBitmapContextGetBytesPerRow(c)
+ bitsPerPixel:CGBitmapContextGetBitsPerPixel(c)];
+ image = [[NSImage alloc] initWithData: [imagerep TIFFRepresentation]];
+ [imagerep release];
+ }
+ return [image autorelease];
+}
+
+
+CFDataRef Fl_X::CGBitmapContextToTIFF(CGContextRef c)
+{ // the returned value is autoreleased
unsigned char *pdata = (unsigned char *)CGBitmapContextGetData(c);
NSBitmapImageRep *imagerep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&pdata
- pixelsWide:CGBitmapContextGetWidth(c)
- pixelsHigh:CGBitmapContextGetHeight(c)
- bitsPerSample:8
- samplesPerPixel:4
- hasAlpha:YES
- isPlanar:NO
- colorSpaceName:NSDeviceRGBColorSpace
- bytesPerRow:CGBitmapContextGetBytesPerRow(c)
- bitsPerPixel:CGBitmapContextGetBitsPerPixel(c)];
- NSImage* image = [[NSImage alloc] initWithData: [imagerep TIFFRepresentation]];
+ pixelsWide:CGBitmapContextGetWidth(c)
+ pixelsHigh:CGBitmapContextGetHeight(c)
+ bitsPerSample:8
+ samplesPerPixel:3
+ hasAlpha:NO
+ isPlanar:NO
+ colorSpaceName:NSDeviceRGBColorSpace
+ bytesPerRow:CGBitmapContextGetBytesPerRow(c)
+ bitsPerPixel:CGBitmapContextGetBitsPerPixel(c)];
+ NSData* tiff = [imagerep TIFFRepresentation];
[imagerep release];
- return [image autorelease];
+ return (CFDataRef)tiff;
}
-static NSCursor *PrepareCursor(NSCursor *cursor, CGContextRef (*f)() )
+int Fl_X::set_cursor(Fl_Cursor c)
{
- if (cursor == nil) {
- CGContextRef c = f();
- NSImage *image = CGBitmapContextToNSImage(c);
- fl_delete_offscreen( (Fl_Offscreen)c );
- NSPoint pt = {[image size].width/2, [image size].height/2};
- cursor = [[NSCursor alloc] initWithImage:image hotSpot:pt];
+ if (cursor) {
+ [(NSCursor*)cursor release];
+ cursor = NULL;
}
- return cursor;
-}
-void Fl_X::set_cursor(Fl_Cursor c)
-{
- NSCursor *icrsr;
switch (c) {
- case FL_CURSOR_CROSS: icrsr = [NSCursor crosshairCursor]; break;
- case FL_CURSOR_WAIT:
- static NSCursor *watch = nil;
- watch = PrepareCursor(watch, &Fl_X::watch_cursor_image);
- icrsr = watch;
- break;
- case FL_CURSOR_INSERT: icrsr = [NSCursor IBeamCursor]; break;
- case FL_CURSOR_N: icrsr = [NSCursor resizeUpCursor]; break;
- case FL_CURSOR_S: icrsr = [NSCursor resizeDownCursor]; break;
- case FL_CURSOR_NS: icrsr = [NSCursor resizeUpDownCursor]; break;
- case FL_CURSOR_HELP:
- static NSCursor *help = nil;
- help = PrepareCursor(help, &Fl_X::help_cursor_image);
- icrsr = help;
- break;
- case FL_CURSOR_HAND: icrsr = [NSCursor pointingHandCursor]; break;
- case FL_CURSOR_MOVE: icrsr = [NSCursor openHandCursor]; break;
- case FL_CURSOR_NE:
- case FL_CURSOR_SW:
- case FL_CURSOR_NESW:
- static NSCursor *nesw = nil;
- nesw = PrepareCursor(nesw, &Fl_X::nesw_cursor_image);
- icrsr = nesw;
- break;
- case FL_CURSOR_E: icrsr = [NSCursor resizeRightCursor]; break;
- case FL_CURSOR_W: icrsr = [NSCursor resizeLeftCursor]; break;
- case FL_CURSOR_WE: icrsr = [NSCursor resizeLeftRightCursor]; break;
- case FL_CURSOR_SE:
- case FL_CURSOR_NW:
- case FL_CURSOR_NWSE:
- static NSCursor *nwse = nil;
- nwse = PrepareCursor(nwse, &Fl_X::nwse_cursor_image);
- icrsr = nwse;
- break;
- case FL_CURSOR_NONE:
- static NSCursor *none = nil;
- none = PrepareCursor(none, &Fl_X::none_cursor_image);
- icrsr = none;
- break;
- case FL_CURSOR_ARROW:
- case FL_CURSOR_DEFAULT:
- default: icrsr = [NSCursor arrowCursor];
- break;
+ case FL_CURSOR_ARROW: cursor = [NSCursor arrowCursor]; break;
+ case FL_CURSOR_CROSS: cursor = [NSCursor crosshairCursor]; break;
+ case FL_CURSOR_INSERT: cursor = [NSCursor IBeamCursor]; break;
+ case FL_CURSOR_HAND: cursor = [NSCursor pointingHandCursor]; break;
+ case FL_CURSOR_MOVE: cursor = [NSCursor openHandCursor]; break;
+ case FL_CURSOR_NS: cursor = [NSCursor resizeUpDownCursor]; break;
+ case FL_CURSOR_WE: cursor = [NSCursor resizeLeftRightCursor]; break;
+ case FL_CURSOR_N: cursor = [NSCursor resizeUpCursor]; break;
+ case FL_CURSOR_E: cursor = [NSCursor resizeRightCursor]; break;
+ case FL_CURSOR_W: cursor = [NSCursor resizeLeftCursor]; break;
+ case FL_CURSOR_S: cursor = [NSCursor resizeDownCursor]; break;
+ default:
+ return 0;
}
- [icrsr set];
- cursor = icrsr;
+
+ [(NSCursor*)cursor retain];
+
+ [(NSWindow*)xid invalidateCursorRectsForView:[(NSWindow*)xid contentView]];
+
+ return 1;
+}
+
+int Fl_X::set_cursor(const Fl_RGB_Image *image, int hotx, int hoty) {
+ if (cursor) {
+ [(NSCursor*)cursor release];
+ cursor = NULL;
+ }
+
+ if ((hotx < 0) || (hotx >= image->w()))
+ return 0;
+ if ((hoty < 0) || (hoty >= image->h()))
+ return 0;
+
+ // OS X >= 10.6 can create a NSImage from a CGImage, but we need to
+ // support older versions, hence this pesky handling.
+
+ NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc]
+ initWithBitmapDataPlanes:NULL
+ pixelsWide:image->w()
+ pixelsHigh:image->h()
+ bitsPerSample:8
+ samplesPerPixel:image->d()
+ hasAlpha:!(image->d() & 1)
+ isPlanar:NO
+ colorSpaceName:(image->d()<=2) ? NSDeviceWhiteColorSpace : NSDeviceRGBColorSpace
+ bytesPerRow:(image->w() * image->d())
+ bitsPerPixel:(image->d()*8)];
+
+ // Alpha needs to be premultiplied for this format
+
+ const uchar *i = (const uchar*)*image->data();
+ unsigned char *o = [bitmap bitmapData];
+ for (int y = 0;y < image->h();y++) {
+ if (!(image->d() & 1)) {
+ for (int x = 0;x < image->w();x++) {
+ unsigned int alpha;
+ if (image->d() == 4) {
+ alpha = i[3];
+ *o++ = (unsigned char)((unsigned int)*i++ * alpha / 255);
+ *o++ = (unsigned char)((unsigned int)*i++ * alpha / 255);
+ }
+
+ alpha = i[1];
+ *o++ = (unsigned char)((unsigned int)*i++ * alpha / 255);
+ *o++ = alpha;
+ i++;
+ }
+ } else {
+ // No alpha, so we can just copy everything directly.
+ int len = image->w() * image->d();
+ memcpy(o, i, len);
+ o += len;
+ i += len;
+ }
+ i += image->ld();
+ }
+
+ NSImage *nsimage = [[NSImage alloc]
+ initWithSize:NSMakeSize(image->w(), image->h())];
+
+ [nsimage addRepresentation:bitmap];
+
+ cursor = [[NSCursor alloc]
+ initWithImage:nsimage
+ hotSpot:NSMakePoint(hotx, hoty)];
+
+ [(NSWindow*)xid invalidateCursorRectsForView:[(NSWindow*)xid contentView]];
+
+ [bitmap release];
+ [nsimage release];
+
+ return 1;
}
@interface FLaboutItemTarget : NSObject
@@ -2879,7 +3567,7 @@ void Fl_X::set_cursor(Fl_Cursor c)
}
//#include <FL/Fl_PostScript.H>
- (void)printPanel
-{
+{
Fl_Printer printer;
//Fl_PostScript_File_Device printer;
int w, h, ww, wh;
@@ -2920,24 +3608,26 @@ static void createAppleMenu(void)
static BOOL donethat = NO;
if (donethat) return;
donethat = YES;
- NSMenu *mainmenu, *services, *appleMenu;
+ NSMenu *mainmenu, *services = nil, *appleMenu;
NSMenuItem *menuItem;
NSString *title;
-
- NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
+
+ SEL infodictSEL = (fl_mac_os_version >= 100200 ? @selector(localizedInfoDictionary) : @selector(infoDictionary));
+ NSString *nsappname = [[[NSBundle mainBundle] performSelector:infodictSEL] objectForKey:@"CFBundleName"];
if (nsappname == nil)
nsappname = [[NSProcessInfo processInfo] processName];
appleMenu = [[NSMenu alloc] initWithTitle:@""];
/* Add menu items */
- title = [[NSString stringWithUTF8String:Fl_Mac_App_Menu::about] stringByAppendingString:nsappname];
+ title = [NSString stringWithFormat:NSLocalizedString([NSString stringWithUTF8String:Fl_Mac_App_Menu::about],nil), nsappname];
menuItem = [appleMenu addItemWithTitle:title action:@selector(showPanel) keyEquivalent:@""];
FLaboutItemTarget *about = [[FLaboutItemTarget alloc] init];
[menuItem setTarget:about];
[appleMenu addItem:[NSMenuItem separatorItem]];
// Print front window
- if (strlen(Fl_Mac_App_Menu::print) > 0) {
+ title = NSLocalizedString([NSString stringWithUTF8String:Fl_Mac_App_Menu::print], nil);
+ if ([title length] > 0) {
menuItem = [appleMenu
- addItemWithTitle:[NSString stringWithUTF8String:Fl_Mac_App_Menu::print]
+ addItemWithTitle:title
action:@selector(printPanel)
keyEquivalent:@""];
[menuItem setTarget:about];
@@ -2945,35 +3635,37 @@ static void createAppleMenu(void)
[menuItem setEnabled:YES];
[appleMenu addItem:[NSMenuItem separatorItem]];
}
- // Services Menu
- services = [[NSMenu alloc] init];
- menuItem = [appleMenu
- addItemWithTitle:[NSString stringWithUTF8String:Fl_Mac_App_Menu::services]
- action:nil
- keyEquivalent:@""];
- [appleMenu setSubmenu:services forItem:menuItem];
- [appleMenu addItem:[NSMenuItem separatorItem]];
- // Hide AppName
- title = [[NSString stringWithUTF8String:Fl_Mac_App_Menu::hide] stringByAppendingString:nsappname];
- [appleMenu addItemWithTitle:title
- action:@selector(hide:)
+ if (fl_mac_os_version >= 100400) { // services+hide+quit already in menu in OS 10.3
+ // Services Menu
+ services = [[NSMenu alloc] init];
+ menuItem = [appleMenu
+ addItemWithTitle:NSLocalizedString([NSString stringWithUTF8String:Fl_Mac_App_Menu::services], nil)
+ action:nil
+ keyEquivalent:@""];
+ [appleMenu setSubmenu:services forItem:menuItem];
+ [appleMenu addItem:[NSMenuItem separatorItem]];
+ // Hide AppName
+ title = [NSString stringWithFormat:NSLocalizedString([NSString stringWithUTF8String:Fl_Mac_App_Menu::hide],nil), nsappname];
+ [appleMenu addItemWithTitle:title
+ action:@selector(hide:)
+ keyEquivalent:@"h"];
+ // Hide Others
+ menuItem = [appleMenu
+ addItemWithTitle:NSLocalizedString([NSString stringWithUTF8String:Fl_Mac_App_Menu::hide_others] , nil)
+ action:@selector(hideOtherApplications:)
keyEquivalent:@"h"];
- // Hide Others
- menuItem = [appleMenu
- addItemWithTitle:[NSString stringWithUTF8String:Fl_Mac_App_Menu::hide_others]
- action:@selector(hideOtherApplications:)
- keyEquivalent:@"h"];
- [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
- // Show All
- [appleMenu addItemWithTitle:[NSString stringWithUTF8String:Fl_Mac_App_Menu::show]
- action:@selector(unhideAllApplications:) keyEquivalent:@""];
- [appleMenu addItem:[NSMenuItem separatorItem]];
- // Quit AppName
- title = [[NSString stringWithUTF8String:Fl_Mac_App_Menu::quit]
- stringByAppendingString:nsappname];
- [appleMenu addItemWithTitle:title
- action:@selector(terminate:)
- keyEquivalent:@"q"];
+ [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
+ // Show All
+ [appleMenu addItemWithTitle:NSLocalizedString([NSString stringWithUTF8String:Fl_Mac_App_Menu::show] , nil)
+ action:@selector(unhideAllApplications:) keyEquivalent:@""];
+ [appleMenu addItem:[NSMenuItem separatorItem]];
+ // Quit AppName
+ title = [NSString stringWithFormat:NSLocalizedString([NSString stringWithUTF8String:Fl_Mac_App_Menu::quit] , nil),
+ nsappname];
+ [appleMenu addItemWithTitle:title
+ action:@selector(terminate:)
+ keyEquivalent:@"q"];
+ }
/* Put menu into the menubar */
menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
[menuItem setSubmenu:appleMenu];
@@ -2984,224 +3676,16 @@ static void createAppleMenu(void)
// to avoid compiler warning raised by use of undocumented setAppleMenu :
[NSApp performSelector:@selector(setAppleMenu:) withObject:appleMenu];
}
- [NSApp setServicesMenu:services];
[NSApp setMainMenu:mainmenu];
- [services release];
+ if (services) {
+ [NSApp setServicesMenu:services];
+ [services release];
+ }
[mainmenu release];
[appleMenu release];
[menuItem release];
}
-@interface FLMenuItem : NSMenuItem {
-}
-- (void) doCallback:(id)unused;
-- (void) directCallback:(id)unused;
-- (const Fl_Menu_Item*) getFlItem;
-@end
-@implementation FLMenuItem
-- (const Fl_Menu_Item*) getFlItem
-{
- return *(const Fl_Menu_Item **)[(NSData*)[self representedObject] bytes];
-}
-- (void) doCallback:(id)unused
-{
- fl_lock_function();
- const Fl_Menu_Item *item = [self getFlItem];
- fl_sys_menu_bar->picked(item);
- if ( item->flags & FL_MENU_TOGGLE ) { // update the menu toggle symbol
- [self setState:(item->value() ? NSOnState : NSOffState)];
- }
- else if ( item->flags & FL_MENU_RADIO ) { // update the menu radio symbols
- NSMenu* menu = [self menu];
- NSInteger flRank = [menu indexOfItem:self];
- NSInteger last = [menu numberOfItems] - 1;
- int from = flRank;
- while(from > 0) {
- if ([[menu itemAtIndex:from-1] isSeparatorItem]) break;
- item = [(FLMenuItem*)[menu itemAtIndex:from-1] getFlItem];
- if ( !(item->flags & FL_MENU_RADIO) ) break;
- from--;
- }
- int to = flRank;
- while (to < last) {
- if ([[menu itemAtIndex:to+1] isSeparatorItem]) break;
- item = [(FLMenuItem*)[menu itemAtIndex:to+1] getFlItem];
- if (!(item->flags & FL_MENU_RADIO)) break;
- to++;
- }
- for(int i = from; i <= to; i++) {
- NSMenuItem *nsitem = [menu itemAtIndex:i];
- [nsitem setState:(nsitem != self ? NSOffState : NSOnState)];
- }
- }
- fl_unlock_function();
-}
-- (void) directCallback:(id)unused
-{
- fl_lock_function();
- Fl_Menu_Item *item = (Fl_Menu_Item *)[(NSData*)[self representedObject] bytes];
- if ( item && item->callback() ) item->do_callback(NULL);
- fl_unlock_function();
-}
-@end
-
-void fl_mac_set_about( Fl_Callback *cb, void *user_data, int shortcut)
-{
- fl_open_display();
- Fl_Menu_Item aboutItem;
- memset(&aboutItem, 0, sizeof(Fl_Menu_Item));
- aboutItem.callback(cb);
- aboutItem.user_data(user_data);
- aboutItem.shortcut(shortcut);
- NSMenu *appleMenu = [[[NSApp mainMenu] itemAtIndex:0] submenu];
- CFStringRef cfname = CFStringCreateCopy(NULL, (CFStringRef)[[appleMenu itemAtIndex:0] title]);
- [appleMenu removeItemAtIndex:0];
- FLMenuItem *item = [[[FLMenuItem alloc] initWithTitle:(NSString*)cfname
- action:@selector(directCallback:)
- keyEquivalent:@""] autorelease];
- if (aboutItem.shortcut()) {
- Fl_Sys_Menu_Bar::doMenuOrItemOperation(Fl_Sys_Menu_Bar::setKeyEquivalent, item, aboutItem.shortcut() & 0xff);
- Fl_Sys_Menu_Bar::doMenuOrItemOperation(Fl_Sys_Menu_Bar::setKeyEquivalentModifierMask, item, aboutItem.shortcut() );
- }
- NSData *pointer = [NSData dataWithBytes:&aboutItem length:sizeof(Fl_Menu_Item)];
- [item setRepresentedObject:pointer];
- [appleMenu insertItem:item atIndex:0];
- CFRelease(cfname);
- [item setTarget:item];
-}
-
-static char *remove_ampersand(const char *s)
-{
- char *ret = strdup(s);
- const char *p = s;
- char *q = ret;
- while(*p != 0) {
- if (p[0]=='&') {
- if (p[1]=='&') {
- *q++ = '&'; p+=2;
- } else {
- p++;
- }
- } else {
- *q++ = *p++;
- }
- }
- *q = 0;
- return ret;
-}
-
-void *Fl_Sys_Menu_Bar::doMenuOrItemOperation(Fl_Sys_Menu_Bar::menuOrItemOperation operation, ...)
-/* these operations apply to menus, submenus, or menu items
- */
-{
- NSAutoreleasePool *localPool;
- localPool = [[NSAutoreleasePool alloc] init];
- NSMenu *menu;
- NSMenuItem *item;
- int value;
- void *pter;
- void *retval = NULL;
- va_list ap;
- va_start(ap, operation);
-
- if (operation == Fl_Sys_Menu_Bar::itemAtIndex) { // arguments: NSMenu*, int. Returns the item
- menu = va_arg(ap, NSMenu*);
- value = va_arg(ap, int);
- retval = (void *)[menu itemAtIndex:value];
- }
- else if (operation == Fl_Sys_Menu_Bar::setKeyEquivalent) { // arguments: NSMenuItem*, int
- item = va_arg(ap, NSMenuItem*);
- value = va_arg(ap, int);
- char key = value;
- NSString *equiv = [[NSString alloc] initWithBytes:&key length:1 encoding:NSASCIIStringEncoding];
- [item setKeyEquivalent:equiv];
- [equiv release];
- }
- else if (operation == Fl_Sys_Menu_Bar::setKeyEquivalentModifierMask) { // arguments: NSMenuItem*, int
- item = va_arg(ap, NSMenuItem*);
- value = va_arg(ap, int);
- NSUInteger macMod = 0;
- if ( value & FL_META ) macMod = NSCommandKeyMask;
- if ( value & FL_SHIFT || isupper(value) ) macMod |= NSShiftKeyMask;
- if ( value & FL_ALT ) macMod |= NSAlternateKeyMask;
- if ( value & FL_CTRL ) macMod |= NSControlKeyMask;
- [item setKeyEquivalentModifierMask:macMod];
- }
- else if (operation == Fl_Sys_Menu_Bar::setState) { // arguments: NSMenuItem*, int
- item = va_arg(ap, NSMenuItem*);
- value = va_arg(ap, int);
- [item setState:(value ? NSOnState : NSOffState)];
- }
- else if (operation == Fl_Sys_Menu_Bar::initWithTitle) { // arguments: const char*title. Returns the newly created menu
- // creates a new (sub)menu
- char *ts = remove_ampersand(va_arg(ap, char *));
- CFStringRef title = CFStringCreateWithCString(NULL, ts, kCFStringEncodingUTF8);
- free(ts);
- NSMenu *menu = [[NSMenu alloc] initWithTitle:(NSString*)title];
- CFRelease(title);
- [menu setAutoenablesItems:NO];
- retval = (void *)menu;
- }
- else if (operation == Fl_Sys_Menu_Bar::numberOfItems) { // arguments: NSMenu *menu, int *pcount
- // upon return, *pcount is set to menu's item count
- menu = va_arg(ap, NSMenu*);
- pter = va_arg(ap, void *);
- *(int*)pter = [menu numberOfItems];
- }
- else if (operation == Fl_Sys_Menu_Bar::setSubmenu) { // arguments: NSMenuItem *item, NSMenu *menu
- // sets 'menu' as submenu attached to 'item'
- item = va_arg(ap, NSMenuItem*);
- menu = va_arg(ap, NSMenu*);
- [item setSubmenu:menu];
- [menu release];
- }
- else if (operation == Fl_Sys_Menu_Bar::setEnabled) { // arguments: NSMenuItem*, int
- item = va_arg(ap, NSMenuItem*);
- value = va_arg(ap, int);
- [item setEnabled:(value ? YES : NO)];
- }
- else if (operation == Fl_Sys_Menu_Bar::addSeparatorItem) { // arguments: NSMenu*
- menu = va_arg(ap, NSMenu*);
- [menu addItem:[NSMenuItem separatorItem]];
- }
- else if (operation == Fl_Sys_Menu_Bar::setTitle) { // arguments: NSMenuItem*, const char *
- item = va_arg(ap, NSMenuItem*);
- char *ts = remove_ampersand(va_arg(ap, char *));
- CFStringRef title = CFStringCreateWithCString(NULL, ts, kCFStringEncodingUTF8);
- free(ts);
- [item setTitle:(NSString*)title];
- CFRelease(title);
- }
- else if (operation == Fl_Sys_Menu_Bar::removeItem) { // arguments: NSMenu*, int
- menu = va_arg(ap, NSMenu*);
- value = va_arg(ap, int);
- [menu removeItem:[menu itemAtIndex:value]];
- }
- else if (operation == Fl_Sys_Menu_Bar::addNewItem) { // arguments: NSMenu *menu, Fl_Menu_Item* mitem, int *prank
- // creates a new menu item at the end of 'menu'
- // attaches the item of fl_sys_menu_bar to it
- // upon return, puts the rank (counted in NSMenu) of the new item in *prank unless prank is NULL
- menu = va_arg(ap, NSMenu*);
- Fl_Menu_Item *mitem = va_arg(ap, Fl_Menu_Item *);
- int *prank = va_arg(ap, int*);
- char *name = remove_ampersand(mitem->label());
- CFStringRef cfname = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8);
- free(name);
- FLMenuItem *item = [[FLMenuItem alloc] initWithTitle:(NSString*)cfname
- action:@selector(doCallback:)
- keyEquivalent:@""];
- NSData *pointer = [NSData dataWithBytes:&mitem length:sizeof(Fl_Menu_Item*)];
- [item setRepresentedObject:pointer];
- [menu addItem:item];
- CFRelease(cfname);
- [item setTarget:item];
- if (prank != NULL) *prank = [menu indexOfItem:item];
- [item release];
- }
- va_end(ap);
- [localPool release];
- return retval;
-}
void Fl_X::set_key_window()
{
@@ -3215,7 +3699,7 @@ static NSImage *imageFromText(const char *text, int *pwidth, int *pheight)
fl_font(FL_HELVETICA, 10);
p = text;
int nl = 0;
- while((q=strchr(p, '\n')) != NULL) {
+ while(nl < 100 && (q=strchr(p, '\n')) != NULL) {
nl++;
w2 = int(fl_width(p, q - p));
if (w2 > width) width = w2;
@@ -3256,14 +3740,30 @@ static NSImage *imageFromText(const char *text, int *pwidth, int *pheight)
static NSImage *defaultDragImage(int *pwidth, int *pheight)
{
- const int width = 16, height = 16;
+ const int version_threshold = 100700;
+ int width, height;
+ if (fl_mac_os_version >= version_threshold) {
+ width = 50; height = 40;
+ }
+ else {
+ width = 16; height = 16;
+ }
Fl_Offscreen off = Fl_Quartz_Graphics_Driver::create_offscreen_with_alpha(width, height);
fl_begin_offscreen(off);
- CGContextSetRGBFillColor( (CGContextRef)off, 0,0,0,0);
- fl_rectf(0,0,width,height);
- CGContextSetRGBStrokeColor( (CGContextRef)off, 0,0,0,0.6);
- fl_rect(0,0,width,height);
- fl_rect(2,2,width-4,height-4);
+ if (fl_mac_os_version >= version_threshold) {
+ fl_font(FL_HELVETICA, 20);
+ fl_color(FL_BLACK);
+ char str[4];
+ int l = fl_utf8encode(0x1F69A, str); // the "Delivery truck" Unicode character from "Apple Color Emoji" font
+ fl_draw(str, l, 1, 16);
+ }
+ else { // draw two squares
+ CGContextSetRGBFillColor( (CGContextRef)off, 0,0,0,0);
+ fl_rectf(0,0,width,height);
+ CGContextSetRGBStrokeColor( (CGContextRef)off, 0,0,0,0.6);
+ fl_rect(0,0,width,height);
+ fl_rect(2,2,width-4,height-4);
+ }
fl_end_offscreen();
NSImage* image = CGBitmapContextToNSImage( (CGContextRef)off );
fl_delete_offscreen( off );
@@ -3279,16 +3779,11 @@ int Fl::dnd(void)
NSAutoreleasePool *localPool;
localPool = [[NSAutoreleasePool alloc] init];
NSPasteboard *mypasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
- [mypasteboard declareTypes:[NSArray arrayWithObjects:@"public.utf8-plain-text", nil] owner:nil];
- [mypasteboard setData:(NSData*)text forType:@"public.utf8-plain-text"];
+ [mypasteboard declareTypes:[NSArray arrayWithObject:utf8_format] owner:nil];
+ [mypasteboard setData:(NSData*)text forType:utf8_format];
CFRelease(text);
Fl_Widget *w = Fl::pushed();
- Fl_Window *win = w->window();
- if (win == NULL) {
- win = (Fl_Window*)w;
- } else {
- while(win->window()) win = win->window();
- }
+ Fl_Window *win = w->top_window();
NSView *myview = [Fl_X::i(win)->xid contentView];
NSEvent *theEvent = [NSApp currentEvent];
@@ -3319,20 +3814,29 @@ int Fl::dnd(void)
}
static NSBitmapImageRep* rect_to_NSBitmapImageRep(Fl_Window *win, int x, int y, int w, int h)
-// release the returned value after use
+// the returned value is autoreleased
{
+ NSRect rect;
+ NSView *winview = nil;
while (win->window()) {
x += win->x();
y += win->y();
win = win->window();
}
- CGFloat epsilon = 0;
- if (fl_mac_os_version >= 100600) epsilon = 0.5; // STR #2887
- // The epsilon offset is absolutely necessary under 10.6. Without it, the top pixel row and
- // left pixel column are not read, and bitmap is read shifted by one pixel in both directions.
- // Under 10.5, we want no offset.
- NSRect rect = NSMakeRect(x - epsilon, y - epsilon, w, h);
- return [[NSBitmapImageRep alloc] initWithFocusedViewRect:rect];
+ if ( through_drawRect ) {
+ CGFloat epsilon = 0;
+ if (fl_mac_os_version >= 100600) epsilon = 0.5; // STR #2887
+ rect = NSMakeRect(x - epsilon, y - epsilon, w, h);
+ }
+ else {
+ rect = NSMakeRect(x, win->h()-(y+h), w, h);
+ // lock focus to win's view
+ winview = [fl_xid(win) contentView];
+ [winview lockFocus];
+ }
+ NSBitmapImageRep *bitmap = [[[NSBitmapImageRep alloc] initWithFocusedViewRect:rect] autorelease];
+ if ( !through_drawRect ) [winview unlockFocus];
+ return bitmap;
}
unsigned char *Fl_X::bitmap_from_window_rect(Fl_Window *win, int x, int y, int w, int h, int *bytesPerPixel)
@@ -3344,24 +3848,38 @@ unsigned char *Fl_X::bitmap_from_window_rect(Fl_Window *win, int x, int y, int w
*/
{
NSBitmapImageRep *bitmap = rect_to_NSBitmapImageRep(win, x, y, w, h);
+ if (bitmap == nil) return NULL;
*bytesPerPixel = [bitmap bitsPerPixel]/8;
int bpp = (int)[bitmap bytesPerPlane];
int bpr = (int)[bitmap bytesPerRow];
- int hh = bpp/bpr; // sometimes hh = h-1 for unclear reason
- int ww = bpr/(*bytesPerPixel); // sometimes ww = w-1
- unsigned char *data = new unsigned char[w * h * *bytesPerPixel];
- if (w == ww) {
- memcpy(data, [bitmap bitmapData], w * hh * *bytesPerPixel);
- } else {
- unsigned char *p = [bitmap bitmapData];
- unsigned char *q = data;
- for(int i = 0;i < hh; i++) {
- memcpy(q, p, *bytesPerPixel * ww);
- p += bpr;
- q += w * *bytesPerPixel;
+ int hh = bpp/bpr; // sometimes hh = h-1 for unclear reason, and hh = 2*h with retina
+ int ww = bpr/(*bytesPerPixel); // sometimes ww = w-1, and ww = 2*w with retina
+ unsigned char *data;
+ if (ww > w) { // with a retina display
+ Fl_RGB_Image *rgb = new Fl_RGB_Image([bitmap bitmapData], ww, hh, 4);
+ Fl_RGB_Scaling save_scaling = Fl_Image::RGB_scaling();
+ Fl_Image::RGB_scaling(FL_RGB_SCALING_BILINEAR);
+ Fl_RGB_Image *rgb2 = (Fl_RGB_Image*)rgb->copy(w, h);
+ Fl_Image::RGB_scaling(save_scaling);
+ delete rgb;
+ rgb2->alloc_array = 0;
+ data = (uchar*)rgb2->array;
+ delete rgb2;
+ }
+ else {
+ data = new unsigned char[w * h * *bytesPerPixel];
+ if (w == ww) {
+ memcpy(data, [bitmap bitmapData], w * hh * *bytesPerPixel);
+ } else {
+ unsigned char *p = [bitmap bitmapData];
+ unsigned char *q = data;
+ for(int i = 0;i < hh; i++) {
+ memcpy(q, p, *bytesPerPixel * ww);
+ p += bpr;
+ q += w * *bytesPerPixel;
}
+ }
}
- [bitmap release];
return data;
}
@@ -3376,13 +3894,13 @@ CGImageRef Fl_X::CGImage_from_window_rect(Fl_Window *win, int x, int y, int w, i
CGImageRef img;
if (fl_mac_os_version >= 100500) {
NSBitmapImageRep *bitmap = rect_to_NSBitmapImageRep(win, x, y, w, h);
- img = [bitmap CGImage]; // requires Mac OS 10.5
+ img = (CGImageRef)[bitmap performSelector:@selector(CGImage)]; // requires Mac OS 10.5
CGImageRetain(img);
- [bitmap release];
}
else {
int bpp;
unsigned char *bitmap = bitmap_from_window_rect(win, x, y, w, h, &bpp);
+ if (!bitmap) return NULL;
CGColorSpaceRef lut = CGColorSpaceCreateDeviceRGB();
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, bitmap, w*h*bpp, imgProviderReleaseData);
img = CGImageCreate(w, h, 8, 8*bpp, w*bpp, lut,
@@ -3401,7 +3919,7 @@ WindowRef Fl_X::window_ref()
// so a CGRect matches exactly what is denoted x,y,w,h for clipping purposes
CGRect fl_cgrectmake_cocoa(int x, int y, int w, int h) {
- return CGRectMake(x, y, w > 0 ? w - 0.9 : 0, h > 0 ? h - 0.9 : 0);
+ return CGRectMake(x - 0.5, y - 0.5, w, h);
}
Window fl_xid(const Fl_Window* w)
@@ -3428,30 +3946,110 @@ int Fl_Window::decorated_h()
void Fl_Paged_Device::print_window(Fl_Window *win, int x_offset, int y_offset)
{
+ NSButton *close = nil, *miniaturize = nil, *zoom = nil;
if (!win->shown() || win->parent() || !win->border() || !win->visible()) {
this->print_widget(win, x_offset, y_offset);
return;
}
- int bx, by, bt;
+ int bx, by, bt, bpp;
get_window_frame_sizes(bx, by, bt);
Fl_Display_Device::display_device()->set_current(); // send win to front and make it current
+ const char *title = win->label();
+ win->label(""); // temporarily set a void window title
win->show();
+ if (fl_mac_os_version >= 101000) {
+ // if linked for OS 10.10, capture of title bar does not capture the title bar buttons
+ // so we draw them in FLTK
+ NSWindow *xid = fl_xid(win);
+ close = [xid standardWindowButton:NSWindowCloseButton]; // 10.2
+ miniaturize = [xid standardWindowButton:NSWindowMiniaturizeButton];
+ zoom = [xid standardWindowButton:NSWindowZoomButton];
+ [close setHidden:YES]; // 10.3
+ [miniaturize setHidden:YES];
+ [zoom setHidden:YES];
+ }
fl_gc = NULL;
Fl::check();
- win->make_current();
+ BOOL to_quartz = dynamic_cast<Fl_Printer*>(this) != NULL;
+ // capture the window title bar with no title
+ CGImageRef img = NULL;
+ unsigned char *bitmap = NULL;
+ if (to_quartz)
+ img = Fl_X::CGImage_from_window_rect(win, 0, -bt, win->w(), bt);
+ else
+ bitmap = Fl_X::bitmap_from_window_rect(win, 0, -bt, win->w(), bt, &bpp);
+ win->label(title); // put back the window title
this->set_current(); // back to the Fl_Paged_Device
- // capture the window title bar
- CGImageRef img = Fl_X::CGImage_from_window_rect(win, 0, -bt, win->w(), bt);
- // and print it
- CGRect rect = { { x_offset, y_offset }, { win->w(), bt } };
- Fl_X::q_begin_image(rect, 0, 0, win->w(), bt);
- CGContextDrawImage(fl_gc, rect, img);
- Fl_X::q_end_image();
- CFRelease(img);
+ if (img && to_quartz) { // print the title bar
+ CGRect rect = { { x_offset, y_offset }, { win->w(), bt } };
+ Fl_X::q_begin_image(rect, 0, 0, win->w(), bt);
+ CGContextDrawImage(fl_gc, rect, img);
+ Fl_X::q_end_image();
+ CFRelease(img);
+ }
+ else if(!to_quartz) {
+ Fl_RGB_Image *rgb = new Fl_RGB_Image(bitmap, win->w(), bt, bpp);
+ rgb->draw(x_offset, y_offset);
+ delete rgb;
+ delete[] bitmap;
+ }
+ if (fl_mac_os_version >= 101000) { // print the title bar buttons
+ Fl_Color inactive = fl_rgb_color((uchar)0xCE, (uchar)0xCE, (uchar)0xCE); // inactive button color
+ Fl_Color redish, yellowish, greenish;
+ if ([[NSUserDefaults standardUserDefaults] integerForKey:@"AppleAquaColorVariant"] == 6) { // graphite appearance
+ redish = yellowish = greenish = fl_rgb_color((uchar)0x8C, (uchar)0x8C, (uchar)0x8C);
+ }
+ else {
+ redish = fl_rgb_color((uchar)0xFF, (uchar)0x63, (uchar)0x5A);
+ yellowish = fl_rgb_color((uchar)0xFF, (uchar)0xC6, (uchar)0x42);
+ greenish = fl_rgb_color((uchar)0x29, (uchar)0xD6, (uchar)0x52);
+ }
+
+ if (![close isEnabled]) fl_color(inactive); else fl_color(redish);
+ fl_pie(x_offset+8, y_offset+5, 12, 12, 0, 360);
+ if (![miniaturize isEnabled]) fl_color(inactive); else fl_color(yellowish);
+ fl_pie(x_offset+28, y_offset+5, 12, 12, 0, 360);
+ if (![zoom isEnabled]) fl_color(inactive); else fl_color(greenish);
+ fl_pie(x_offset+48, y_offset+5, 12, 12, 0, 360);
+
+ [close setHidden:NO]; // 10.3
+ [miniaturize setHidden:NO];
+ [zoom setHidden:NO];
+ }
+ if (title) { // print the window title
+ const int skip = 65; // approx width of the zone of the 3 window control buttons
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
+ if (fl_mac_os_version >= 100400 && to_quartz) { // use Cocoa string drawing with exact title bar font
+ // the exact font is LucidaGrande 13 pts (and HelveticaNeueDeskInterface-Regular with 10.10)
+ NSGraphicsContext *current = [NSGraphicsContext currentContext];
+ [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:fl_gc flipped:YES]];//10.4
+ NSDictionary *attr = [NSDictionary dictionaryWithObject:[NSFont titleBarFontOfSize:0]
+ forKey:NSFontAttributeName];
+ NSString *title_s = [fl_xid(win) title];
+ NSSize size = [title_s sizeWithAttributes:attr];
+ int x = x_offset + win->w()/2 - size.width/2;
+ if (x < x_offset+skip) x = x_offset+skip;
+ NSRect r = {{x, y_offset+bt/2+4}, {win->w() - skip, bt}};
+ [[NSGraphicsContext currentContext] setShouldAntialias:YES];
+ [title_s drawWithRect:r options:(NSStringDrawingOptions)0 attributes:attr]; // 10.4
+ [[NSGraphicsContext currentContext] setShouldAntialias:NO];
+ [NSGraphicsContext setCurrentContext:current];
+ }
+ else
+#endif
+ {
+ fl_font(FL_HELVETICA, 14);
+ fl_color(FL_BLACK);
+ int x = x_offset + win->w()/2 - fl_width(title)/2;
+ if (x < x_offset+skip) x = x_offset+skip;
+ fl_push_clip(x_offset, y_offset, win->w(), bt);
+ fl_draw(title, x, y_offset+bt/2+4);
+ fl_pop_clip();
+ }
+ }
this->print_widget(win, x_offset, y_offset + bt); // print the window inner part
}
-#include <dlfcn.h>
/* Returns the address of a Carbon function after dynamically loading the Carbon library if needed.
Supports old Mac OS X versions that may use a couple of Carbon calls:
@@ -3476,9 +4074,20 @@ void *Fl_X::get_carbon_function(const char *function_name) {
static int calc_mac_os_version() {
int M, m, b = 0;
NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init];
- NSDictionary * sv = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"];
- const char *s = [[sv objectForKey:@"ProductVersion"] UTF8String];
- sscanf(s, "%d.%d.%d", &M, &m, &b);
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10
+ if ([NSProcessInfo instancesRespondToSelector:@selector(operatingSystemVersion)]) {
+ NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion];
+ M = version.majorVersion;
+ m = version.minorVersion;
+ b = version.patchVersion;
+ }
+ else
+#endif
+ {
+ NSDictionary * sv = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"];
+ const char *s = [[sv objectForKey:@"ProductVersion"] UTF8String];
+ sscanf(s, "%d.%d.%d", &M, &m, &b);
+ }
[localPool release];
return M*10000 + m*100 + b;
}
@@ -3486,5 +4095,5 @@ static int calc_mac_os_version() {
#endif // __APPLE__
//
-// End of "$Id: Fl_cocoa.mm 9734 2012-11-30 18:20:36Z manolo $".
+// End of "$Id: Fl_cocoa.mm 10427 2014-11-02 21:06:07Z manolo $".
//