From e4d4f400a4b42f4713f2d2dc5e29b0c73a7717f1 Mon Sep 17 00:00:00 2001 From: Manolo Gouy Date: Tue, 11 Nov 2014 16:08:09 +0000 Subject: [PATCH] Added support of true subwindows to the Mac OS X code. With this, a window inside another window is just another window with Mac OS as it was already with MSWindows and X11. This requires Mac OS X 10.2. Window nesting to any depth is possible. git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@10449 ea41ed52-d2ee-0310-a9c1-e6b18d33e121 --- FL/mac.H | 7 +- src/Fl.cxx | 23 +- src/Fl_Gl_Window.cxx | 12 +- src/Fl_cocoa.mm | 511 +++++++++++++++++++------------------------ 4 files changed, 235 insertions(+), 318 deletions(-) diff --git a/FL/mac.H b/FL/mac.H index de8270549..811a71541 100644 --- a/FL/mac.H +++ b/FL/mac.H @@ -125,9 +125,8 @@ public: Fl_Offscreen other_xid; // pointer for offscreen bitmaps (overlay window) Fl_Window *w; // FLTK window for Fl_Region region; - Fl_Region subRegion; // region for this specific subwindow + CGRect* subRect; // make sure subwindow remains inside its parent window Fl_X *next; // linked tree to support subwindows - Fl_X *xidChildren, *xidNext; // more subwindow tree int wait_for_expose; NSCursor *cursor; static Fl_X* first; @@ -146,7 +145,6 @@ public: void destroy(void); void map(void); void unmap(void); - int unlink(Fl_X* start = NULL); void collapse(void); WindowRef window_ref(void); void set_key_window(void); @@ -169,9 +167,6 @@ public: static int insertion_point_location(int *px, int *py, int *pheight); // computes window coordinates & height of insertion point static const int CoreText_threshold; // Mac OS version from which the Core Text API is used to display text static Fl_Fontdesc* calc_fl_fonts(void); // computes the fl_fonts global variable -private: - static void relink(Fl_Window*, Fl_Window*); - bool subwindow; }; extern Window fl_window; diff --git a/src/Fl.cxx b/src/Fl.cxx index e800d79e7..a08b095ec 100644 --- a/src/Fl.cxx +++ b/src/Fl.cxx @@ -681,20 +681,13 @@ Fl_X* Fl_X::first; Fl_Window* fl_find(Window xid) { Fl_X *window; for (Fl_X **pp = &Fl_X::first; (window = *pp); pp = &window->next) -#if defined(WIN32) || defined(USE_X11) - if (window->xid == xid) -#elif defined(__APPLE_QUARTZ__) - if (window->xid == xid && !window->w->window()) -#else -# error unsupported platform -#endif // __APPLE__ - { + if (window->xid == xid) { if (window != Fl_X::first && !Fl::modal()) { - // make this window be first to speed up searches - // this is not done if modal is true to avoid messing up modal stack - *pp = window->next; - window->next = Fl_X::first; - Fl_X::first = window; + // make this window be first to speed up searches + // this is not done if modal is true to avoid messing up modal stack + *pp = window->next; + window->next = Fl_X::first; + Fl_X::first = window; } return window->w; } @@ -1505,7 +1498,6 @@ void Fl_Window::hide() { for (; *pp != ip; pp = &(*pp)->next) if (!*pp) return; *pp = ip->next; #ifdef __APPLE__ - ip->unlink(); // MacOS X manages a single pointer per application. Make sure that hiding // a toplevel window will not leave us with some random pointer shape, or // worst case, an invisible pointer @@ -1556,7 +1548,7 @@ void Fl_Window::hide() { } #elif defined(__APPLE_QUARTZ__) Fl_X::q_release_context(ip); - if ( ip->xid == fl_window && !parent() ) + if ( ip->xid == fl_window ) fl_window = 0; #endif @@ -1584,6 +1576,7 @@ void Fl_Window::hide() { if (count) delete[] doit; #elif defined(__APPLE_QUARTZ__) ip->destroy(); + delete ip->subRect; #else # error unsupported platform #endif diff --git a/src/Fl_Gl_Window.cxx b/src/Fl_Gl_Window.cxx index 7e97e3297..f23b8e2ab 100644 --- a/src/Fl_Gl_Window.cxx +++ b/src/Fl_Gl_Window.cxx @@ -180,16 +180,8 @@ void Fl_Gl_Window::make_current() { // correct parent window size to work with... GLint xywh[4]; - if (window()) { - int xoff,yoff; - const Fl_Window *win = top_window_offset(xoff, yoff); // STR #2944 [2] - xywh[0] = xoff; - xywh[1] = win->h() - yoff - h(); - } else { - xywh[0] = 0; - xywh[1] = 0; - } - + xywh[0] = 0; + xywh[1] = 0; xywh[2] = w(); xywh[3] = h(); diff --git a/src/Fl_cocoa.mm b/src/Fl_cocoa.mm index ff693c57e..2674899d4 100644 --- a/src/Fl_cocoa.mm +++ b/src/Fl_cocoa.mm @@ -88,7 +88,6 @@ extern int fl_send_system_handlers(void *e); // converting cr lf converter function static void convert_crlf(char * string, size_t len); 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); @@ -96,6 +95,7 @@ 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 NSBitmapImageRep* rect_to_NSBitmapImageRep(Fl_Window *win, int x, int y, int w, int h); Fl_Display_Device *Fl_Display_Device::_display = new Fl_Display_Device(new Fl_Quartz_Graphics_Driver); // the platform display @@ -649,19 +649,11 @@ void Fl::remove_timeout(Fl_Timeout_Handler cb, void* data) @interface FLWindow : NSWindow { Fl_Window *w; - BOOL containsGLsubwindow; } - (FLWindow*)initWithFl_W:(Fl_Window *)flw 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)containsGLsubwindow:(BOOL)contains; #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 - (NSPoint)convertBaseToScreen:(NSPoint)aPoint; #endif @@ -691,7 +683,6 @@ void Fl::remove_timeout(Fl_Timeout_Handler cb, void* data) self = [super initWithContentRect:rect styleMask:windowStyle backing:NSBackingStoreBuffered defer:NO]; 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); @@ -705,22 +696,13 @@ void Fl::remove_timeout(Fl_Timeout_Handler cb, void* data) { return w; } -- (BOOL)containsGLsubwindow -{ - return containsGLsubwindow; -} -- (void)containsGLsubwindow:(BOOL)contains -{ - containsGLsubwindow = contains; -} - (BOOL)canBecomeKeyWindow { if (Fl::modal_ && (Fl::modal_ != w)) return NO; // prevent the caption to be redrawn as active on click // when another modal window is currently the key win - - return !(w->tooltip_window() || w->menu_window()); + return !(w->tooltip_window() || w->menu_window() || w->parent()); } - (BOOL)canBecomeMainWindow @@ -729,7 +711,7 @@ void Fl::remove_timeout(Fl_Timeout_Handler cb, void* data) return NO; // prevent the caption to be redrawn as active on click // when another modal window is currently the key win - return !(w->tooltip_window() || w->menu_window()); + return !(w->tooltip_window() || w->menu_window() || w->parent()); } @end @@ -1107,12 +1089,52 @@ static FLTextView *fltextview_instance = nil; - (void)windowDidResignKey:(NSNotification *)notif; - (void)windowDidBecomeKey:(NSNotification *)notif; - (void)windowDidBecomeMain:(NSNotification *)notif; +- (void)windowWillMiniaturize:(NSNotification *)notif; - (void)windowDidDeminiaturize:(NSNotification *)notif; - (void)windowDidMiniaturize:(NSNotification *)notif; - (BOOL)windowShouldClose:(id)fl; - (void)anyWindowWillClose:(NSNotification *)notif; @end +/* recursively set Cocoa's position and size of subwindows from their FLTK coords + */ +static void position_subwindows(Fl_Window *parent, BOOL is_a_move) +{ + FLWindow *xid = fl_xid(parent); + NSArray *children = [xid childWindows]; // 10.2 + NSEnumerator *enumerator = [children objectEnumerator]; + id child; + NSRect pframe = [xid frame]; + while ((child = [enumerator nextObject]) != nil) { + NSRect rchild; + Fl_Window *sub = [child getFl_Window]; + rchild.origin = NSMakePoint(pframe.origin.x + sub->x(), pframe.origin.y + parent->h() - (sub->h() + sub->y())); + rchild.size = NSMakeSize(sub->w(), sub->h()); + [child setFrame:rchild display:YES]; + position_subwindows(sub, is_a_move); + if (is_a_move && fl_mac_os_version < 100700) { // after move, redraw parent and children of GL windows + if (parent->as_gl_window()) [child display]; + if (sub->as_gl_window()) [xid display]; + } + } +} + +/* make subwindows re-appear after appl unhide or window deminiaturize + (not necessary with 10.5 and above) + */ +static void orderfront_subwindows(FLWindow *xid) +{ + NSArray *children = [xid childWindows]; // 10.2 + NSEnumerator *enumerator = [children objectEnumerator]; + id child; + while ((child = [enumerator nextObject]) != nil) { // this undo-redo seems necessary under 10.3 + [xid removeChildWindow:child]; + [xid addChildWindow:child ordered:NSWindowAbove]; + [child orderWindow:NSWindowAbove relativeTo:[xid windowNumber]]; + orderfront_subwindows(child); + } +} + @interface FLWindowDelegateBefore10_6 : FLWindowDelegate - (id)windowWillReturnFieldEditor:(NSWindow *)sender toObject:(id)client; @end @@ -1123,12 +1145,35 @@ static FLTextView *fltextview_instance = nil; } @end +@interface FLWindowDelegateBefore10_5 : FLWindowDelegateBefore10_6 +-(void)windowDidDeminiaturize:(NSNotification *)notif; +-(void)windowWillMiniaturize:(NSNotification *)notif; +@end +@implementation FLWindowDelegateBefore10_5 +-(void)windowDidDeminiaturize:(NSNotification *)notif +{ + [super windowDidDeminiaturize:notif]; + fl_lock_function(); + orderfront_subwindows([notif object]); + fl_unlock_function(); +} +-(void)windowWillMiniaturize:(NSNotification *)notif +{ + [super windowWillMiniaturize:notif]; + NSArray *children = [(NSWindow*)[notif object] childWindows]; // 10.2 + NSEnumerator *enumerator = [children objectEnumerator]; + id child; + while ((child = [enumerator nextObject]) != nil) [child orderOut:self]; +} +@end + static FLWindowDelegate *flwindowdelegate_instance = nil; @implementation FLWindowDelegate + (void)initialize { if (self == [FLWindowDelegate self]) { - if (fl_mac_os_version < 100600) flwindowdelegate_instance = [FLWindowDelegateBefore10_6 alloc]; + if (fl_mac_os_version < 100500) flwindowdelegate_instance = [FLWindowDelegateBefore10_5 alloc]; + else if (fl_mac_os_version < 100600) flwindowdelegate_instance = [FLWindowDelegateBefore10_6 alloc]; else flwindowdelegate_instance = [FLWindowDelegate alloc]; flwindowdelegate_instance = [flwindowdelegate_instance init]; } @@ -1141,16 +1186,14 @@ static FLWindowDelegate *flwindowdelegate_instance = nil; fl_lock_function(); FLWindow *nsw = (FLWindow*)[notif object]; Fl_Window *window = [nsw getFl_Window]; - NSPoint pt, pt2; - pt.x = 0; - pt.y = [[nsw contentView] frame].size.height; - pt2 = [nsw convertBaseToScreen:pt]; - 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] && fl_mac_os_version < 100700) { - [nsw display];// with OS < 10.7, redraw window after moving if it contains OpenGL subwindows - } + if (!window->parent()) { + NSPoint pt2; + pt2 = [nsw convertBaseToScreen:NSMakePoint(0, [[nsw contentView] frame].size.height)]; + update_e_xy_and_e_xy_root(nsw); + resize_from_system = NULL; + window->position((int)pt2.x, (int)(main_screen_height - pt2.y)); + position_subwindows(window, YES); + } fl_unlock_function(); } - (void)windowDidResize:(NSNotification *)notif @@ -1158,17 +1201,16 @@ static FLWindowDelegate *flwindowdelegate_instance = nil; fl_lock_function(); FLWindow *nsw = (FLWindow*)[notif object]; Fl_Window *window = [nsw getFl_Window]; - NSRect r = [[nsw contentView] frame]; - NSPoint pt, pt2; - pt.x = 0; - pt.y = [[nsw contentView] frame].size.height; - pt2 = [nsw convertBaseToScreen:pt]; - resize_from_system = window; - update_e_xy_and_e_xy_root(nsw); - window->resize((int)pt2.x, - (int)(main_screen_height - pt2.y), - (int)r.size.width, - (int)r.size.height); + NSRect r; NSPoint pt2; + if (!window->parent()) { + r = [[nsw contentView] frame]; + pt2 = [nsw convertBaseToScreen:NSMakePoint(0, [[nsw contentView] frame].size.height)]; + pt2.y = main_screen_height - pt2.y; + resize_from_system = window; + update_e_xy_and_e_xy_root(nsw); + window->resize((int)pt2.x, (int)pt2.y, (int)r.size.width, (int)r.size.height); + position_subwindows(window, NO); + } fl_unlock_function(); } - (void)windowDidResignKey:(NSNotification *)notif @@ -1195,7 +1237,7 @@ static FLWindowDelegate *flwindowdelegate_instance = nil; [nsw setLevel:NSStatusWindowLevel]; fixup_window_levels(); } - if ( w->border() || (!w->modal() && !w->tooltip_window()) ) Fl::handle( FL_FOCUS, w); + Fl::handle( FL_FOCUS, w); fl_unlock_function(); } - (void)windowDidBecomeMain:(NSNotification *)notif @@ -1216,6 +1258,22 @@ static FLWindowDelegate *flwindowdelegate_instance = nil; update_e_xy_and_e_xy_root(nsw); fl_unlock_function(); } +- (void)windowWillMiniaturize:(NSNotification *)notif +{ + // subwindows are not captured in system-built miniature window image + fl_lock_function(); + FLWindow *nsw = (FLWindow*)[notif object]; + if ([nsw childWindows]) { + // capture the window and its subwindows and use as miniature window image + Fl_Window *window = [nsw getFl_Window]; + NSBitmapImageRep *bitmap = rect_to_NSBitmapImageRep(window, 0, 0, window->w(), window->h()); + NSImage *img = [[[NSImage alloc] initWithSize:NSMakeSize([bitmap pixelsWide], [bitmap pixelsHigh])] autorelease]; + [img addRepresentation:bitmap]; + [bitmap release]; + [nsw setMiniwindowImage:img]; + } + fl_unlock_function(); +} - (void)windowDidMiniaturize:(NSNotification *)notif { fl_lock_function(); @@ -1417,6 +1475,22 @@ static FLWindowDelegate *flwindowdelegate_instance = nil; } @end +@interface FLAppDelegateBefore10_5 : FLAppDelegate +- (void)applicationDidUnhide:(NSNotification *)notify; +@end +@implementation FLAppDelegateBefore10_5 +- (void)applicationDidUnhide:(NSNotification *)notify +{ // before 10.5, subwindows are lost when application is unhidden + fl_lock_function(); + for (Fl_X *x = Fl_X::first; x; x = x->next) { + if (![x->xid parentWindow]) { + orderfront_subwindows(x->xid); + } + } + fl_unlock_function(); +} +@end + /* * Install an open documents event handler... */ @@ -1484,7 +1558,8 @@ void fl_open_display() { if (need_new_nsapp) [NSApplication sharedApplication]; NSAutoreleasePool *localPool; localPool = [[NSAutoreleasePool alloc] init]; // never released - [(NSApplication*)NSApp setDelegate:[[FLAppDelegate alloc] init]]; + FLAppDelegate *delegate = (fl_mac_os_version < 100500 ? [FLAppDelegateBefore10_5 alloc] : [FLAppDelegate alloc]); + [(NSApplication*)NSApp setDelegate:[delegate init]]; if (need_new_nsapp) [NSApp finishLaunching]; // empty the event queue but keep system events for drag&drop of files at launch @@ -1531,8 +1606,8 @@ void fl_open_display() { /* support of Mac OS 10.2 or earlier used this undocumented call instead err = CPSEnableForegroundOperation(&cur_psn, 0x03, 0x3C, 0x2C, 0x1103); */ - [NSApp activateIgnoringOtherApps:YES]; } + [NSApp activateIgnoringOtherApps:YES]; } if (![NSApp servicesMenu]) createAppleMenu(); main_screen_height = [[[NSScreen screens] objectAtIndex:0] frame].size.height; @@ -1702,18 +1777,6 @@ static void handleUpdateEvent( Fl_Window *window ) i->region = 0; } - for ( Fl_X *cx = i->xidChildren; cx; cx = cx->xidNext ) { - if ( cx->region ) { - XDestroyRegion(cx->region); - 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); i->flush(); window->clear_damage(); @@ -1741,6 +1804,8 @@ int Fl_X::fake_X_wm(const Fl_Window* w,int &X,int &Y, int &bt,int &bx, int &by) W = w->w()+dx; H = w->h()+dy; + if (w->parent()) return 0; + // Proceed to positioning the window fully inside the screen, if possible // let's get a little elaborate here. Mac OS X puts a lot of stuff on the desk @@ -2167,7 +2232,6 @@ static FLTextInputContext* fltextinputcontext_instance = nil; prevMods = mods; } mods_to_e_state( mods ); - while (window->parent()) window = window->window(); if (sendEvent) Fl::handle(sendEvent,window); fl_unlock_function(); } @@ -2535,54 +2599,34 @@ void Fl_X::flush() fl_x_to_redraw = NULL; } -//bool Fl_X::make_shaped = false; + +static void set_subwindow_frame(Fl_Window *w) { // maps a subwindow at its correct position/size + FLWindow *xid = fl_xid(w); + Fl_Window *parent = w->window(); + FLWindow *pxid = fl_xid(parent); + if (!pxid) return; + NSRect rp = [pxid frame]; + // subwindow coordinates are in screen units from bottom just like all windows + rp.origin = NSMakePoint(rp.origin.x + w->x(), rp.origin.y + parent->h() - w->y() - w->h()); + rp.size = NSMakeSize(w->w(), w->h()); + [xid setFrame:rp display:YES]; + [pxid addChildWindow:xid ordered:NSWindowAbove]; // needs OS X 10.2 + [xid orderWindow:NSWindowAbove relativeTo:[pxid windowNumber]]; // necessary under 10.3 +} /* * go ahead, create that (sub)window */ void Fl_X::make(Fl_Window* w) { - if ( w->parent() ) { // create a subwindow - Fl_Group::current(0); - // our subwindow needs this structure to know about its clipping. - Fl_X* x = new Fl_X; - x->subwindow = true; - x->other_xid = 0; - x->region = 0; - x->subRegion = 0; - 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) { - x->xidNext = xo->xidChildren; - x->xidChildren = 0L; - xo->xidChildren = x; - x->xid = win->i->xid; - x->w = w; w->i = x; - x->wait_for_expose = 0; - { - Fl_X *z = xo->next; // we don't want a subwindow in Fl_X::first - xo->next = x; - x->next = z; - } - int old_event = Fl::e_number; - w->handle(Fl::e_number = FL_SHOW); - Fl::e_number = old_event; - w->redraw(); // force draw to happen - } - if (w->as_gl_window()) { // if creating a sub-GL-window - while (win->window()) win = win->window(); - [Fl_X::i(win)->xid containsGLsubwindow:YES]; - } - fl_show_iconic = 0; - } - else { // create a desktop window Fl_Group::current(0); fl_open_display(); NSInteger winlevel = NSNormalWindowLevel; NSUInteger winstyle; + if (w->parent()) { + w->border(0); + fl_show_iconic = 0; + } if (w->border()) winstyle = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask; else winstyle = NSBorderlessWindowMask; int xp = w->x(); @@ -2599,7 +2643,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); - if (w->border()) winstyle |= NSResizableWindowMask; + if (w->border()) winstyle |= NSResizableWindowMask; } else { w->size_range(w->w(), w->h(), w->w(), w->h()); } @@ -2611,11 +2655,9 @@ void Fl_X::make(Fl_Window* w) if (w->modal()||w->tooltip_window()) { winlevel = modal_window_level(); } - //winstyle = NSBorderlessWindowMask; - } + } if (w->modal()) { winstyle &= ~NSMiniaturizableWindowMask; - // winstyle &= ~(NSResizableWindowMask | NSMiniaturizableWindowMask); winlevel = modal_window_level(); } else if (w->non_modal()) { @@ -2634,21 +2676,12 @@ void Fl_X::make(Fl_Window* w) xp -= bx; yp -= by+bt; } - - if (w->non_modal() && Fl_X::first /*&& !fl_disable_transient_for*/) { - // find some other window to be "transient for": - Fl_Window* w = Fl_X::first->w; - while (w->parent()) w = w->window(); // todo: this code does not make any sense! (w!=w??) - } - + Fl_X* x = new Fl_X; - x->subwindow = false; 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->subRect = 0; x->cursor = NULL; - x->xidChildren = 0; - x->xidNext = 0; x->gc = 0; NSRect crect; @@ -2682,7 +2715,7 @@ void Fl_X::make(Fl_Window* w) winstyle = NSBorderlessWindowMask; winlevel = NSStatusWindowLevel; } - crect.origin.x = w->x(); + crect.origin.x = w->x(); // correct origin set later for subwindows crect.origin.y = main_screen_height - (w->y() + w->h()); crect.size.width=w->w(); crect.size.height=w->h(); @@ -2690,7 +2723,7 @@ void Fl_X::make(Fl_Window* w) contentRect:crect styleMask:winstyle]; [cw setFrameOrigin:crect.origin]; - [cw setHasShadow:YES]; + if (!w->parent()) [cw setHasShadow:YES]; [cw setAcceptsMouseMovedEvents:YES]; if (w->shape_data_) { [cw setOpaque:NO]; // shaped windows must be non opaque @@ -2699,8 +2732,17 @@ void Fl_X::make(Fl_Window* w) x->xid = cw; x->w = w; w->i = x; x->wait_for_expose = 1; - x->next = Fl_X::first; - Fl_X::first = x; + if (!w->parent()) { + x->next = Fl_X::first; + Fl_X::first = x; + } else if (Fl_X::first) { + x->next = Fl_X::first->next; + Fl_X::first->next = x; + } + else { + x->next = NULL; + Fl_X::first = x; + } FLView *myview = [[FLViewThisOS alloc] init]; [cw setContentView:myview]; [myview release]; @@ -2722,11 +2764,7 @@ void Fl_X::make(Fl_Window* w) } // Install DnD handlers [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 - [NSApp activateIgnoringOtherApps:YES]; - } - + if (w->size_range_set) w->size_range_(); if ( w->border() || (!w->modal() && !w->tooltip_window()) ) { @@ -2737,28 +2775,36 @@ 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); + if (!w->parent()) Fl::first_window(w); [cw setDelegate:[FLWindowDelegate singleInstance]]; if (fl_show_iconic) { fl_show_iconic = 0; [cw miniaturize:nil]; + } else if (w->parent()) { + set_subwindow_frame(w); } else { [cw makeKeyAndOrderFront:nil]; } - crect = [[cw contentView] frame]; - w->w(int(crect.size.width)); - w->h(int(crect.size.height)); - crect = [cw frame]; - w->x(int(crect.origin.x)); - w->y(int(main_screen_height - (crect.origin.y + w->h()))); + if (w->parent()) { + [cw setIgnoresMouseEvents:YES]; // needs OS X 10.2 + // next 2 statements so a subwindow doesn't leak out of its parent window + [cw setOpaque:NO]; + [cw setBackgroundColor:[NSColor clearColor]]; // transparent background color + } else { + crect = [[cw contentView] frame]; + w->w(int(crect.size.width)); + w->h(int(crect.size.height)); + crect = [cw frame]; + w->x(int(crect.origin.x)); + w->y(int(main_screen_height - (crect.origin.y + w->h()))); + } int old_event = Fl::e_number; w->handle(Fl::e_number = FL_SHOW); Fl::e_number = old_event; // if (w->modal()) { Fl::modal_ = w; fl_fix_focus(); } - } } @@ -2853,7 +2899,7 @@ void Fl_Window::resize(int X,int Y,int W,int H) { resize_from_system = 0; return; } - if ( (resize_from_system!=this) && (!parent()) && shown()) { + if ( (resize_from_system!=this) && shown()) { if (is_a_resize) { if (resizable()) { if (Wborder() ) bt = 0; - else get_window_frame_sizes(bx, by, bt); - NSRect dim; - dim.origin.x = X; - dim.origin.y = main_screen_height - (Y + H); - dim.size.width = W; - dim.size.height = H + bt; - [i->xid setFrame:dim display:YES]; // calls windowDidResize + Fl_Group::resize(X,Y,W,H); + // make sure that subwindows of this window don't leak out of their parent window + NSArray *children = [fl_xid(this) childWindows]; // 10.2 + NSEnumerator *enumerator = [children objectEnumerator]; + id child; + while ((child = [enumerator nextObject]) != nil) { + [[(FLWindow*)child contentView] setNeedsDisplay:YES]; + Fl_Window *childw = [child getFl_Window]; + CGRect prect = CGRectMake(0, 0, w(), h()); + CGRect srect = CGRectMake(childw->x(), childw->y(), childw->w(), childw->h()); + CGRect **active = &(Fl_X::i(childw)->subRect); + delete *active; + *active = NULL; + if (!CGRectContainsRect(prect, srect)) { // if subwindow extends outside its parent window + CGRect clip = CGRectIntersection(prect, srect); + clip = CGRectOffset(clip, -childw->x(), -childw->y()); + clip = fl_cgrectmake_cocoa(clip.origin.x, clip.origin.y, clip.size.width, clip.size.height); + *active = new CGRect(clip); + } + } + } else { - NSPoint pt; - pt.x = X; - pt.y = main_screen_height - (Y + h()); - [i->xid setFrameOrigin:pt]; // calls windowDidMove + x(X); y(Y); } return; } @@ -2927,28 +2982,10 @@ void Fl_Window::make_current() fl_window = i->xid; current_ = this; - int xp = 0, yp = 0; - Fl_Window *win = this; - while ( win ) { - if ( !win->window() ) - break; - xp += win->x(); - yp += win->y(); - win = (Fl_Window*)win->window(); - } - NSGraphicsContext *nsgc = through_drawRect ? [NSGraphicsContext currentContext] : + 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() ) { - for ( Fl_X *cx = i->xidChildren; cx; cx = cx->xidNext ) { // clip-out all sub-windows - Fl_Window *cw = cx->w; - Fl_Region from = fl_window_region; - fl_window_region = MacRegionMinusRect(from, cw->x(), cw->y(), cw->w(), cw->h() ); - XDestroyRegion(from); - } - } // antialiasing must be deactivated because it applies to rectangles too // and escapes even clipping!!! @@ -2957,16 +2994,11 @@ void Fl_Window::make_current() CGFloat hgt = [[fl_window contentView] frame].size.height; CGContextTranslateCTM(fl_gc, 0.5, hgt-0.5f); CGContextScaleCTM(fl_gc, 1.0f, -1.0f); // now 0,0 is top-left point of the window - win = this; - while(win && win->window()) { // translate to subwindow origin if this is a subwindow context - CGContextTranslateCTM(fl_gc, win->x(), win->y()); - win = win->window(); - } - //apply window's clip - CGContextClipToRects(fl_gc, fl_window_region->rects, fl_window_region->count ); - XDestroyRegion(fl_window_region); -// this is the context with origin at top left of (sub)window clipped out of its subwindows if any - CGContextSaveGState(fl_gc); + // for subwindows, limit drawing to inside of parent window + if (i->subRect) CGContextClipToRect(fl_gc, *(i->subRect)); + +// this is the context with origin at top left of (sub)window + CGContextSaveGState(fl_gc); #if defined(FLTK_USE_CAIRO) if (Fl::cairo_autolink_context()) Fl::cairo_make_current(this); // capture gc changes automatically to update the cairo context adequately #endif @@ -3296,60 +3328,19 @@ int Fl::clipboard_contains(const char *type) { return found != nil; } -int Fl_X::unlink(Fl_X *start) { - if (start) { - Fl_X *pc = start; - while (pc) { - if (pc->xidNext == this) { - pc->xidNext = xidNext; - return 1; - } - if (pc->xidChildren) { - if (pc->xidChildren == this) { - pc->xidChildren = xidNext; - return 1; - } - if (unlink(pc->xidChildren)) - return 1; - } - pc = pc->xidNext; - } - } else { - for ( Fl_X *pc = Fl_X::first; pc; pc = pc->next ) { - if (unlink(pc)) - return 1; - } - } - return 0; -} - -void Fl_X::relink(Fl_Window *w, Fl_Window *wp) { - Fl_X *x = Fl_X::i(w); - Fl_X *p = Fl_X::i(wp); - if (!x || !p) return; - // first, check if 'x' is already registered as a child of 'p' - for (Fl_X *i = p->xidChildren; i; i=i->xidNext) { - if (i == x) return; - } - // now add 'x' as the first child of 'p' - x->xidNext = p->xidChildren; - p->xidChildren = x; -} - void Fl_X::destroy() { - // subwindows share their xid with their parent window, so should not close it - if (!subwindow && w && !w->parent() && xid) { + if (xid) { [xid close]; } } void Fl_X::map() { - if (w && xid) { - [xid orderFront:nil]; + if (w && xid && ![xid parentWindow]) { // 10.2 + // after a subwindow has been unmapped, it has lost its parent window and its frame may be wrong + set_subwindow_frame(w); } //+ link to window list if (w && w->parent()) { - Fl_X::relink(w, w->window() ); w->redraw(); } if (cursor) { @@ -3359,58 +3350,10 @@ void Fl_X::map() { } void Fl_X::unmap() { - if (w && !w->parent() && xid) { + if (w && xid) { + if (w->parent()) [[xid parentWindow] removeChildWindow:xid]; // necessary with at least 10.5 [xid orderOut:nil]; } - if (w && Fl_X::i(w)) - Fl_X::i(w)->unlink(); -} - - -// removes x,y,w,h rectangle from region r and returns result as a new Fl_Region -static Fl_Region MacRegionMinusRect(Fl_Region r, int x,int y,int w,int h) -{ - Fl_Region outr = (Fl_Region)malloc(sizeof(*outr)); - outr->rects = (CGRect*)malloc(4 * r->count * sizeof(CGRect)); - outr->count = 0; - CGRect rect = fl_cgrectmake_cocoa(x, y, w, h); - for( int i = 0; i < r->count; i++) { - CGRect A = r->rects[i]; - CGRect test = CGRectIntersection(A, rect); - if (CGRectIsEmpty(test)) { - outr->rects[(outr->count)++] = A; - } - else { - const CGFloat verylarge = 100000.; - CGRect side = CGRectMake(0,0,rect.origin.x,verylarge);// W side - test = CGRectIntersection(A, side); - if ( ! CGRectIsEmpty(test)) { - outr->rects[(outr->count)++] = test; - } - side = CGRectMake(0,rect.origin.y + rect.size.height,verylarge,verylarge);// N side - test = CGRectIntersection(A, side); - if ( ! CGRectIsEmpty(test)) { - outr->rects[(outr->count)++] = test; - } - side = CGRectMake(rect.origin.x + rect.size.width, 0, verylarge, verylarge);// E side - test = CGRectIntersection(A, side); - if ( ! CGRectIsEmpty(test)) { - outr->rects[(outr->count)++] = test; - } - side = CGRectMake(0, 0, verylarge, rect.origin.y);// S side - test = CGRectIntersection(A, side); - if ( ! CGRectIsEmpty(test)) { - outr->rects[(outr->count)++] = test; - } - } - } - if (outr->count == 0) { - free(outr->rects); - free(outr); - outr = XRectangleRegion(0,0,0,0); - } - else outr->rects = (CGRect*)realloc(outr->rects, outr->count * sizeof(CGRect)); - return outr; } // intersects current and x,y,w,h rectangle and returns result as a new Fl_Region @@ -3454,8 +3397,7 @@ static NSImage *CGBitmapContextToNSImage(CGContextRef c) else #endif { - unsigned char *pdata = (unsigned char *)CGBitmapContextGetData(c); - NSBitmapImageRep *imagerep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&pdata + NSBitmapImageRep *imagerep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:CGBitmapContextGetWidth(c) pixelsHigh:CGBitmapContextGetHeight(c) bitsPerSample:8 @@ -3465,7 +3407,9 @@ static NSImage *CGBitmapContextToNSImage(CGContextRef c) colorSpaceName:NSDeviceRGBColorSpace bytesPerRow:CGBitmapContextGetBytesPerRow(c) bitsPerPixel:CGBitmapContextGetBitsPerPixel(c)]; - image = [[NSImage alloc] initWithData: [imagerep TIFFRepresentation]]; + memcpy([imagerep bitmapData], CGBitmapContextGetData(c), [imagerep bytesPerRow] * [imagerep pixelsHigh]); + image = [[NSImage alloc] initWithSize:NSMakeSize([imagerep pixelsWide], [imagerep pixelsHigh])]; + [image addRepresentation:imagerep]; [imagerep release]; } return [image autorelease]; @@ -3598,6 +3542,7 @@ int Fl_X::set_cursor(const Fl_RGB_Image *image, int hotx, int hoty) { int w, h, ww, wh; Fl_Window *win = Fl::first_window(); if(!win) return; + if (win->parent()) win = win->top_window(); if( printer.start_job(1) ) return; if( printer.start_page() ) return; fl_lock_function(); @@ -3878,11 +3823,6 @@ static CGImageRef GL_rect_to_CGImage(Fl_Window *win, int x, int y, int w, int h) // win is really a Fl_Gl_Window* { CGRect rect; - while (win->window()) { - x += win->x(); - y += win->y(); - win = win->window(); - } NSRect r = [fl_xid(win) frame]; rect = CGRectMake(r.origin.x + x, r.origin.y + win->h() - (y + h), w, h); // rect is target rect in window coordinates // convert r to global display coordinates @@ -3904,7 +3844,7 @@ static void imgProviderReleaseData (void *info, const void *data, size_t size) } -CGImageRef GL_rect_to_CGImage_10_5(Fl_Window *win, int x, int y, int w, int h) +static CGImageRef GL_rect_to_CGImage_10_5(Fl_Window *win, int x, int y, int w, int h) // captures a rectangle from a GL window and returns it as a CGImageRef // used with Mac OS X 10.5 and before // win is really a Fl_Gl_Window* @@ -3971,11 +3911,6 @@ static NSBitmapImageRep* rect_to_NSBitmapImageRep(Fl_Window *win, int x, int y, if (win->as_gl_window() && y >= 0) { bitmap = GL_rect_to_nsbitmap(win, x, y, w, h); } else { - while (win->window()) { - x += win->x(); - y += win->y(); - win = win->window(); - } NSView *winview = nil; if ( through_drawRect && Fl_Window::current() == win ) { CGFloat epsilon = 0; @@ -3994,10 +3929,12 @@ static NSBitmapImageRep* rect_to_NSBitmapImageRep(Fl_Window *win, int x, int y, if (!bitmap) return nil; } - // it's necessary to capture also GL subwindows - for (Fl_X *flx = Fl_X::i(win)->xidChildren; flx; flx = flx->xidNext) { - Fl_Window *sub = flx->w; - if (!sub->as_gl_window() ) continue; + // capture also subwindows + NSArray *children = [fl_xid(win) childWindows]; // 10.2 + NSEnumerator *enumerator = [children objectEnumerator]; + id child; + while ((child = [enumerator nextObject]) != nil) { + Fl_Window *sub = [(FLWindow*)child getFl_Window]; CGRect rsub = CGRectMake(sub->x(), win->h() -(sub->y()+sub->h()), sub->w(), sub->h()); CGRect clip = CGRectMake(x, win->h()-(y+h), w, h); clip = CGRectIntersection(rsub, clip); @@ -4014,8 +3951,8 @@ static NSBitmapImageRep* rect_to_NSBitmapImageRep(Fl_Window *win, int x, int y, unsigned char *Fl_X::bitmap_from_window_rect(Fl_Window *win, int x, int y, int w, int h, int *bytesPerPixel) /* Returns a capture of a rectangle of a mapped window as a pre-multiplied RGBA array of bytes. Alpha values are always 1 (except for the angles of a window title bar) - so pre-multiplication can be ignored. - *bytesPerPixel is always set to the value 4 upon return. + so pre-multiplication can be ignored. + *bytesPerPixel is set to the value 3 or 4 upon return. delete[] the returned pointer after use */ {