diff --git a/ChangeLog b/ChangeLog index d2582a073..9d23f1f8f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,12 +1,84 @@ +2006-10-06 Richard Frith-Macdonald + + * Source/GSToolTips.h: Remove some ivars + * Source/GSToolTips.m: Move ivars to be static variables for common + use by all tool tips. Cancel an in-progress tooltip when a new one + starts. + +2006-10-04 Richard Frith-Macdonald + + * Source/GSToolTips.h: create tool tip wondow fully on screen + * Source/GSToolTips.m: ditto + * Source/NSView.m: very minor tooltip efficiency tweak. + * Version: Bump to 0.12 for next + +2006-10-04 Enrico Sersale + + * Source/NSView.m: In -setToolTip: sets the has_tooltips flag. + +2006-10-04 Richard Frith-Macdonald + + * Source/NSWindow.m: Add help key support for context help. + * NSToolbarItem.m: Implement tool tips. + * GSToolTips.m: Use correct font. + +2006-10-02 20:41-EDT Gregory John Casamento + + * Resources/Esperanto.lproj/Localizable.strings + * Resources/German.lproj/Localizable.strings + * Resources/Italian.lproj/Localizable.strings + * Resources/Lojban.lproj/Localizable.strings: Correct grammar + in localization bundles as well. + +2006-10-02 Richard Frith-Macdonald + + * Source/GNUmakefile: Add GSToolTips.[hm] + * Source/NSWindow.m: Add private tooltip functionality. + * Source/GSToolTips.h: Interface to let views and windows handle tips. + * Source/GSToolTips.m: ToolTip implementation ... main part. + * Source/NSView.m: Use GSToolTips to implement tool tips. + * Headers/AppKit/NSResponder.h: Add a flag for views with tool tips. + Basic implementation of tool tip help. + +2006-10-02 Nicola Pero + + * configure.ac: Check the new variable GNUSTEP_IS_FLATTENED, + and default to yes. + * configure: Regenerated. + + * Source/GSTheme.m: Worked arond a compiler bug that would abort + compilation on GCC 3.3.3 20040412 (Red Hat Linux 3.3.3-7). Avoid + [NSMutableSet set] as the compiler is confusing +(id)set with + -(void)set. + +2006-10-02 08:19-EDT Gregory John Casamento + + * Source/NSDocumentController.m: Correct grammar in + reviewUnsavedDocumentsWithAlertTitle:... "Quit Anyways" should + be "Quit Anyway". + +2006-10-01 Richard Frith-Macdonald + + * Source/GSTrackingRect.m: + * Source/NSView.m: + * Headers/AppKit/NSView.h: + Fix tracking rect bug ... the owner of the rectangle should not + be retained by a tracking rect (but should be retained by a cursor + rect). Updated documentation to clearly state what mis retained. + 2006-10-01 Richard Frith-Macdonald * Source/GSTheme.m: Add rudimentary inspector. Make themes in panel be in alphabetical order except for the default theme (always first in the list). Allow setting of default theme for application. - * Source/NSHelpManager.m: + * Source/NSHelpManager.m: Implement context help cursor. * Source/NSHelpPanel.m: Implement simple fallback help panel display for systems where no rtf/rtfd viewing application is installed. + * Source/NSResponder.m: Tweak context help + * Source/NSCursor.m: tidied a little + * Images/GNUmakefile: Install context help cursor + * Images/common_HelpCursor.tiff: add context help cursor 2006-09-30 Richard Frith-Macdonald diff --git a/Headers/AppKit/NSResponder.h b/Headers/AppKit/NSResponder.h index af0117288..d5c4f823f 100644 --- a/Headers/AppKit/NSResponder.h +++ b/Headers/AppKit/NSResponder.h @@ -77,6 +77,7 @@ /* change to keep track of it. */ unsigned valid_rects:1; /* Some cursor rects may be ok. */ unsigned needs_display:1; /* Window/view needs display. */ + unsigned has_tooltips:1; /* The view has tooltips set. */ } _rFlags; } diff --git a/Headers/AppKit/NSView.h b/Headers/AppKit/NSView.h index 295c4b149..f8515c72e 100644 --- a/Headers/AppKit/NSView.h +++ b/Headers/AppKit/NSView.h @@ -328,14 +328,41 @@ typedef enum _NSFocusRingType { event: (NSEvent *)theEvent; #endif -/* - * Managing the Cursor +/** + * Adds a cursor rectangle. This provides for automatic update of the + * cursor to be anObject while the mouse is in aRect.
+ * The cursor (anObject) is retained until the cursor rectangle is + * removed or discarded.
+ * Generally your subviews should call this in their implementation of + * the -resetCursorRects method.
+ * It is your responsibility to ensure that aRect lies within your veiw's + * visible rectangle. */ - (void) addCursorRect: (NSRect)aRect cursor: (NSCursor*)anObject; + +/** + * Removes all the cursor rectancles which have been set up for the + * receiver. This is equivalent to calling -removeCursorRect:cursor: + * for all cursor rectangles which have been set up.
+ * This is called automatically before the system calls -resetCursorRects + * so you never need to call it. + */ - (void) discardCursorRects; + +/** + * Removes the cursor rectangle which was set up for the specified + * rectangle and cursor. + */ - (void) removeCursorRect: (NSRect)aRect cursor: (NSCursor*)anObject; + +/** + * This is called to establish a new set of cursor rectangles whenever + * the receiver needs to do so (eg the view has been resized). The default + * implementation does nothing, but subclasses should use it to make + * calls to -addCursorRect:cursor: to establish their new cursor rectangles. + */ - (void) resetCursorRects; #ifndef STRICT_OPENSTEP @@ -351,10 +378,26 @@ typedef enum _NSFocusRingType { - (NSString *) toolTip; #endif -/* - * Tracking rectangles +/** + * Removes a tracking rectangle which was previously established using the + * -addTrackingRect:owner:userData:assumeInside: method.
+ * The value of tag must be the value returned by the method used to add + * the rectangle. */ - (void) removeTrackingRect: (NSTrackingRectTag)tag; + +/** + * Adds a tracking rectangle to monitor mouse movement and generate + * mouse-entered and mouse-exited events.
+ * The event messages are sent to anObject, which is not + * retained and must therefore be sure to remove any tracking rectangles + * using it before it is deallocated.
+ * The value of data is supplied as the user data in the event objects + * generated.
+ * If flag is YES then the mouse is assumed to be inside the tracking + * rectangle and the first event generated will therefore be a mouse exit, + * if it is NO then the first event generated will be a mouse entry. + */ - (NSTrackingRectTag) addTrackingRect: (NSRect)aRect owner: (id)anObject userData: (void*)data diff --git a/Images/GNUmakefile b/Images/GNUmakefile index 4d5f919bf..ab9cde640 100644 --- a/Images/GNUmakefile +++ b/Images/GNUmakefile @@ -19,7 +19,8 @@ # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free -# Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA. +# Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02111 USA. GNUSTEP_INSTALLATION_DIR = $(GNUSTEP_SYSTEM_ROOT) @@ -50,6 +51,7 @@ common_CloseBroken.tiff \ common_CloseBrokenH.tiff \ common_ColorSwatch.tiff \ common_2DDash.tiff \ +common_HelpCursor.tiff \ common_Home.tiff \ common_HomeDirectory.tiff \ common_Mount.tiff \ diff --git a/Images/common_HelpCursor.tiff b/Images/common_HelpCursor.tiff new file mode 100644 index 000000000..857b9aa7c Binary files /dev/null and b/Images/common_HelpCursor.tiff differ diff --git a/Resources/Esperanto.lproj/Localizable.strings b/Resources/Esperanto.lproj/Localizable.strings index 7b4437faf..dc2bf6b8e 100644 --- a/Resources/Esperanto.lproj/Localizable.strings +++ b/Resources/Esperanto.lproj/Localizable.strings @@ -174,7 +174,7 @@ add comments above this one "Review Unsaved" = "Rigardu ree"; /* File: ../Source/NSDocumentController.m:502 */ -"Quit Anyways" = "Tamen finu"; +"Quit Anyway" = "Tamen finu"; /* File: ../Source/NSDocumentController.m:565 */ "Power Off" = "Malŝaltu"; diff --git a/Resources/German.lproj/Localizable.strings b/Resources/German.lproj/Localizable.strings index cd3f1f517..49ad0ccf5 100644 --- a/Resources/German.lproj/Localizable.strings +++ b/Resources/German.lproj/Localizable.strings @@ -149,7 +149,7 @@ add comments above this one /* File: ../Source/NSDocumentController.m:565 */ "Power Off" = "Aus"; /* File: ../Source/NSDocumentController.m:502 */ -"Quit Anyways" = "Dennoch beenden"; +"Quit Anyway" = "Dennoch beenden"; /* File: ../Source/NSDocumentController.m:500 */ "Review Unsaved" = "Ansehen"; /* File: ../Source/NSDocumentController.m:499 */ diff --git a/Resources/Italian.lproj/Localizable.strings b/Resources/Italian.lproj/Localizable.strings index 6bace4723..680fb306d 100644 --- a/Resources/Italian.lproj/Localizable.strings +++ b/Resources/Italian.lproj/Localizable.strings @@ -125,7 +125,7 @@ add comments above this one /* File: ../Source/NSDocumentController.m:502 */ /* Flag: untranslated */ -"Quit Anyways" = "Esci comunque"; +"Quit Anyway" = "Esci comunque"; /* File: ../Source/NSDocumentController.m:565 */ /* Flag: untranslated */ diff --git a/Resources/Lojban.lproj/Localizable.strings b/Resources/Lojban.lproj/Localizable.strings index d6c8b6858..9d9f191fc 100644 --- a/Resources/Lojban.lproj/Localizable.strings +++ b/Resources/Lojban.lproj/Localizable.strings @@ -232,7 +232,7 @@ add comments above this one /* File: ../Source/NSDocumentController.m:502 */ /* Flag: untranslated */ -"Quit Anyways" = "birti fe le fanmo"; +"Quit Anyway" = "birti fe le fanmo"; /* File: ../Source/NSDocumentController.m:565 */ /* Flag: untranslated */ diff --git a/Source/GNUmakefile b/Source/GNUmakefile index 5e917ef73..f5adb9f39 100644 --- a/Source/GNUmakefile +++ b/Source/GNUmakefile @@ -193,6 +193,7 @@ GSHorizontalTypesetter.m \ GSNibTemplates.m \ GSNibCompatibility.m \ GSTitleView.m \ +GSToolTips.m \ GSToolbar.m \ GSToolbarView.m \ GSStandardWindowDecorationView.m \ diff --git a/Source/GSTheme.m b/Source/GSTheme.m index 6f0a11471..428261f59 100644 --- a/Source/GSTheme.m +++ b/Source/GSTheme.m @@ -31,6 +31,7 @@ #include "Foundation/NSNotification.h" #include "Foundation/NSNull.h" #include "Foundation/NSPathUtilities.h" +#include "Foundation/NSSet.h" #include "Foundation/NSUserDefaults.h" #include "GNUstepGUI/GSTheme.h" #include "AppKit/NSApplication.h" @@ -1561,7 +1562,14 @@ static GSThemePanel *sharedPanel = nil; - (void) update: (id)sender { NSArray *array; - NSMutableSet *set = [NSMutableSet set]; + + /* Avoid [NSMutableSet set] that confuses GCC 3.3.3. It seems to confuse + * this static +(id)set method with the instance -(void)set, so it would + * refuse to compile saying + * GSTheme.m:1565: error: void value not ignored as it ought to be + */ + NSMutableSet *set = AUTORELEASE([NSMutableSet new]); + NSString *selected = RETAIN([[matrix selectedCell] title]); unsigned existing = [[matrix cells] count]; NSFileManager *mgr = [NSFileManager defaultManager]; diff --git a/Source/GSToolTips.h b/Source/GSToolTips.h new file mode 100644 index 000000000..09b3ad353 --- /dev/null +++ b/Source/GSToolTips.h @@ -0,0 +1,100 @@ +/** + Interface of the GSToolTips class + + Copyright (C) 2006 Free Software Foundation, Inc. + + Author: Richard Frith-Macdonald + Date: 2006 + + This file is part of the GNUstep GUI Library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; see the file COPYING.LIB. + If not, write to the Free Software Foundation, + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include "GNUstepGUI/GSTrackingRect.h" + + +@class NSTimer; +@class NSView; +@class NSWindow; + +@interface GSToolTips : NSObject +{ + NSView *view; + NSTrackingRectTag toolTipTag; +} + +/** Destroy object handling tips for aView. + */ ++ (void) removeTipsForView: (NSView*)aView; + +/** Return object to handle tips for aView. + */ ++ (GSToolTips*) tipsForView: (NSView*)aView; + +/** Initialiser for internal use only. + */ +- (id) initForView: (NSView*)aView; + +/** Support [NSView-addToolTipRect:owner:userData:]. + */ +- (NSToolTipTag) addToolTipRect: (NSRect)aRect + owner: (id)anObject + userData: (void *)data; + +/** Return the number of tooltip rectangles active. + */ +- (unsigned) count; + +/** Handle mouse entry to tracking rect + */ +- (void) mouseEntered: (NSEvent *)theEvent; + +/** Handle mouse exit from tracking rect + */ +- (void) mouseExited: (NSEvent *)theEvent; + +/** Cancel tooltip because user clicked mouse. + */ +- (void) mouseDown: (NSEvent *)theEvent; + +/** Move tooltip window with user's mouse movement. + */ +- (void) mouseMoved: (NSEvent *)theEvent; + +/** Rebuild rectangles. Called by NSView whenever it has to rebuild its + * coordinate system because it has been resized, moved, or reparented. + */ +- (void) rebuild; + +/** Support [NSView-removeAllToolTips] + */ +- (void) removeAllToolTips; + +/** Support [NSView-removeToolTip:]. + */ +- (void) removeToolTip: (NSToolTipTag)tag; + +/** Support [NSView-setToolTip:]. + */ +- (void) setToolTip: (NSString *)string; + +/** Support [NSView-toolTip]. + */ +- (NSString *) toolTip; +@end + diff --git a/Source/GSToolTips.m b/Source/GSToolTips.m new file mode 100644 index 000000000..5cce62575 --- /dev/null +++ b/Source/GSToolTips.m @@ -0,0 +1,550 @@ +/** + Implementation of the GSToolTips class + + Copyright (C) 2006 Free Software Foundation, Inc. + + Author: Richard Frith-Macdonald + Date: 2006 + + This file is part of the GNUstep GUI Library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; see the file COPYING.LIB. + If not, write to the Free Software Foundation, + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include "AppKit/NSAttributedString.h" +#include "AppKit/NSBezierPath.h" +#include "AppKit/NSView.h" +#include "AppKit/NSWindow.h" +#include "AppKit/NSScreen.h" +#include "GNUstepGUI/GSTrackingRect.h" +#include "GSToolTips.h" + +@interface NSWindow (GNUstepPrivate) + ++ (void) _setToolTipVisible: (GSToolTips*)t; ++ (GSToolTips*) _toolTipVisible; + +@end + + +@interface NSObject (ToolTips) +- (NSString*) view: (NSView*)v stringForToolTip: (NSToolTipTag)t + point: (NSPoint)p userData: (void*)d; +@end + +/* A trivial class to hold information about the provider of the tooltip + * string. Instance allocation/deallocation is managed by GSToolTip and + * are instances are stored in the user data field of tracking rectangles. + */ +@interface GSTTProvider : NSObject +{ + id object; + void *data; +} +- (void*) data; +- (id) initWithObject: (id)o userData: (void*)d; +- (id) object; +- (void) setObject: (id)o; +@end + +@implementation GSTTProvider +- (void*) data +{ + return data; +} +- (id) initWithObject: (id)o userData: (void*)d +{ + data = d; + object = o; + return self; +} +- (id) object +{ + return object; +} +- (void) setObject: (id)o +{ + object = o; +} +@end + +@interface GSToolTips (Private) +- (void) _drawText: (NSAttributedString *)text; +- (void) _endDisplay; +- (void) _timedOut: (NSTimer *)timer; +@end + +typedef struct NSView_struct +{ + @defs(NSView) +} *NSViewPtr; + +@implementation GSToolTips + +static NSMapTable *viewsMap = 0; +static NSTimer *timer; +static NSWindow *window; +static NSSize offset; +static BOOL restoreMouseMoved; + ++ (void) initialize +{ + viewsMap = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks, + NSObjectMapValueCallBacks, 8); +} + ++ (void) removeTipsForView: (NSView*)aView +{ + GSToolTips *tt = (GSToolTips*)NSMapGet(viewsMap, (void*)aView); + + if (tt != nil) + { + [tt removeAllToolTips]; + NSMapRemove(viewsMap, (void*)aView); + } +} + ++ (GSToolTips*) tipsForView: (NSView*)aView +{ + GSToolTips *tt = (GSToolTips*)NSMapGet(viewsMap, (void*)aView); + + if (tt == nil) + { + tt = [[GSToolTips alloc] initForView: aView]; + NSMapInsert(viewsMap, (void*)aView, (void*)tt); + RELEASE(tt); + } + return tt; +} + + + +- (NSToolTipTag) addToolTipRect: (NSRect)aRect + owner: (id)anObject + userData: (void *)data +{ + NSTrackingRectTag tag; + GSTTProvider *provider; + + if (timer != nil) + { + return -1; // A tip is already in progress. + } + if (NSEqualRects(aRect, NSZeroRect)) + { + return -1; // No rectangle. + } + if (anObject == nil) + { + return -1; // No provider object. + } + + provider = [[GSTTProvider alloc] initWithObject: anObject userData: data]; + tag = [view addTrackingRect: aRect + owner: self + userData: provider + assumeInside: NO]; + return tag; +} + +- (unsigned) count +{ + NSEnumerator *enumerator; + GSTrackingRect *rect; + unsigned count = 0; + + enumerator = [((NSViewPtr)view)->_tracking_rects objectEnumerator]; + while ((rect = [enumerator nextObject]) != nil) + { + if (rect->owner == self) + { + count++; + } + } + return count; +} + +- (void) dealloc +{ + [self _endDisplay]; + [self removeAllToolTips]; + [super dealloc]; +} + +- (id) initForView: (NSView*)aView +{ + view = aView; + timer = nil; + window = nil; + toolTipTag = -1; + return self; +} + +- (void) mouseEntered: (NSEvent *)theEvent +{ + GSTTProvider *provider; + NSString *toolTipString; + + if (timer != nil) + { + /* Moved from one tooltip view to another, so reset the timer. + */ + [timer invalidate]; + timer = nil; + } + + provider = (GSTTProvider*)[theEvent userData]; + if ([[provider object] respondsToSelector: + @selector(view:stringForToolTip:point:userData:)] == YES) + { + toolTipString = [[provider object] view: view + stringForToolTip: [theEvent trackingNumber] + point: [theEvent locationInWindow] + userData: [provider data]]; + } + else + { + toolTipString = [[provider object] description]; + } + + timer = [NSTimer scheduledTimerWithTimeInterval: 0.5 + target: self + selector: @selector(_timedOut:) + userInfo: toolTipString + repeats: YES]; + if ([[view window] acceptsMouseMovedEvents] == YES) + { + restoreMouseMoved = NO; + } + else + { + restoreMouseMoved = YES; + [[view window] setAcceptsMouseMovedEvents: YES]; + } + [NSWindow _setToolTipVisible: self]; +} + +- (void) mouseExited: (NSEvent *)theEvent +{ + [self _endDisplay]; +} + +- (void) mouseDown: (NSEvent *)theEvent +{ + [self _endDisplay]; +} + +- (void) mouseMoved: (NSEvent *)theEvent +{ + NSPoint mouseLocation; + NSPoint origin; + + if (window == nil) + { + return; + } + + mouseLocation = [NSEvent mouseLocation]; + + origin = NSMakePoint(mouseLocation.x + offset.width, + mouseLocation.y + offset.height); + + [window setFrameOrigin: origin]; +} + +- (void) rebuild +{ + NSEnumerator *enumerator; + GSTrackingRect *rect; + + enumerator = [((NSViewPtr)view)->_tracking_rects objectEnumerator]; + while ((rect = [enumerator nextObject]) != nil) + { + if (rect->owner == self) + { + GSTTProvider *provider = (GSTTProvider *)rect->user_data; + + // FIXME can we do anything with tooltips other than the main one? + if (rect->tag == toolTipTag) + { + NSTrackingRectTag tag; + NSRect frame; + + [view removeTrackingRect: rect->tag]; + frame = [view frame]; + frame.origin.x = 0; + frame.origin.y = 0; + tag = [view addTrackingRect: frame + owner: self + userData: provider + assumeInside: NO]; + toolTipTag = tag; + } + } + } +} + +- (void) removeAllToolTips +{ + NSEnumerator *enumerator; + GSTrackingRect *rect; + + [self _endDisplay]; + + enumerator = [((NSViewPtr)view)->_tracking_rects objectEnumerator]; + while ((rect = [enumerator nextObject]) != nil) + { + if (rect->owner == self) + { + RELEASE((GSTTProvider*)rect->user_data); + [view removeTrackingRect: rect->tag]; + } + } + toolTipTag = -1; +} + +- (void) removeToolTip: (NSToolTipTag)tag +{ + NSEnumerator *enumerator; + GSTrackingRect *rect; + + enumerator = [((NSViewPtr)view)->_tracking_rects objectEnumerator]; + while ((rect = [enumerator nextObject]) != nil) + { + if (rect->tag == tag && rect->owner == self) + { + RELEASE((GSTTProvider*)rect->user_data); + [view removeTrackingRect: tag]; + } + } +} + +- (void) setToolTip: (NSString *)string +{ + if ([string length] == 0) + { + if (toolTipTag != -1) + { + [self _endDisplay]; + [self removeToolTip: toolTipTag]; + toolTipTag = -1; + } + } + else + { + GSTTProvider *provider; + + if (toolTipTag == -1) + { + NSRect rect; + + rect = [view frame]; + rect.origin.x = 0; + rect.origin.y = 0; + + provider = [[GSTTProvider alloc] initWithObject: string + userData: nil]; + toolTipTag = [view addTrackingRect: rect + owner: self + userData: provider + assumeInside: NO]; + } + else + { + NSEnumerator *enumerator; + GSTrackingRect *rect; + + enumerator = [((NSViewPtr)view)->_tracking_rects objectEnumerator]; + while ((rect = [enumerator nextObject]) != nil) + { + if (rect->tag == toolTipTag && rect->owner == self) + { + [((GSTTProvider*)rect->user_data) setObject: string]; + } + } + } + } +} + +- (NSString *) toolTip +{ + NSEnumerator *enumerator; + GSTrackingRect *rect; + + enumerator = [((NSViewPtr)view)->_tracking_rects objectEnumerator]; + while ((rect = [enumerator nextObject]) != nil) + { + if (rect->tag == toolTipTag) + { + return [((GSTTProvider*)rect->user_data) object]; + } + } + return nil; +} + +@end + +@implementation GSToolTips (Private) + +- (void) _drawText: (NSAttributedString *)text +{ + NSRectEdge sides[] = {NSMinXEdge, NSMaxYEdge, NSMaxXEdge, NSMinYEdge}; + NSColor *black = [NSColor blackColor]; + NSColor *colors[] = {black, black, black, black}; + NSRect bounds = [[window contentView] bounds]; + NSRect textRect; + + textRect = [window frame]; + textRect.origin.x = 2; + textRect.origin.y = -2; + + [[window contentView] lockFocus]; + + [text drawInRect: textRect]; + NSDrawColorTiledRects(bounds, bounds, sides, colors, 4); + + [[window contentView] unlockFocus]; +} + +- (void) _endDisplay +{ + if ([NSWindow _toolTipVisible] == self) + { + [NSWindow _setToolTipVisible: nil]; + } + if (timer != nil) + { + if ([timer isValid]) + { + [timer invalidate]; + } + timer = nil; + } + if (window != nil) + { + [window close]; + window = nil; + } + if (restoreMouseMoved == YES) + { + restoreMouseMoved = NO; + [[view window] setAcceptsMouseMovedEvents: NO]; + } +} + +- (void) _timedOut: (NSTimer *)aTimer +{ + NSString *toolTipString = [aTimer userInfo]; + NSAttributedString *toolTipText = nil; + NSSize textSize; + NSPoint mouseLocation = [NSEvent mouseLocation]; + NSRect visible; + NSRect rect; + NSColor *color; + NSMutableDictionary *attributes; + + if (timer != nil) + { + if ([timer isValid]) + { + [timer invalidate]; + } + timer = nil; + } + + if (window != nil) + { + /* Moved from one tooltip view to another ... so stop displaying + * the old tool tip before we start the new one. + * This is similar to the case in -mouseEntered: where we cancel + * the timer for one tooltip view because we have entered another + * one. + * To think about ... if we entered a tooltip rectangle without + * having left the previous one, then when we leave this rectangle + * we are probably back in the other one and should really restart + * the timer for the original view. However, this is a rare case + * so it's probably better to ignore it than add a lot of code to + * keep track of all entry and exit. + */ + [self _endDisplay]; + } + + attributes = [NSMutableDictionary dictionary]; + [attributes setObject: [NSFont toolTipsFontOfSize: 10.0] + forKey: NSFontAttributeName]; + toolTipText = + [[NSAttributedString alloc] initWithString: toolTipString + attributes: attributes]; + textSize = [toolTipText size]; + + /* Create window just off the current mouse position + * Constrain it to be on screen, shrinking if necessary. + */ + rect = NSMakeRect(mouseLocation.x + 8, + mouseLocation.y - 16 - (textSize.height+3), + textSize.width + 4, textSize.height + 4); + visible = [[NSScreen mainScreen] visibleFrame]; + if (NSMaxY(rect) > NSMaxY(visible)) + { + rect.origin.y -= (NSMaxY(rect) - NSMaxY(visible)); + } + if (NSMinY(rect) < NSMinY(visible)) + { + rect.origin.y += (NSMinY(visible) - NSMinY(rect)); + } + if (NSMaxY(rect) > NSMaxY(visible)) + { + rect.origin.y = visible.origin.y; + rect.size.height = visible.size.height; + } + + if (NSMaxX(rect) > NSMaxX(visible)) + { + rect.origin.x -= (NSMaxX(rect) - NSMaxX(visible)); + } + if (NSMinX(rect) < NSMinX(visible)) + { + rect.origin.x += (NSMinX(visible) - NSMinX(rect)); + } + if (NSMaxX(rect) > NSMaxX(visible)) + { + rect.origin.x = visible.origin.x; + rect.size.width = visible.size.width; + } + offset.height = rect.origin.y - mouseLocation.y; + offset.width = rect.origin.x - mouseLocation.x; + + window = [[NSWindow alloc] initWithContentRect: rect + styleMask: NSBorderlessWindowMask + backing: NSBackingStoreRetained + defer: YES]; + + color + = [NSColor colorWithDeviceRed: 1.0 green: 1.0 blue: 0.90 alpha: 1.0]; + [window setBackgroundColor: color]; + [window setReleasedWhenClosed: YES]; + [window setExcludedFromWindowsMenu: YES]; + [window setLevel: NSStatusWindowLevel]; + + [window orderFront: nil]; + + [self _drawText: toolTipText]; + RELEASE(toolTipText); +} + +@end + diff --git a/Source/GSTrackingRect.m b/Source/GSTrackingRect.m index 6a65d45e2..2ee8b6ac0 100644 --- a/Source/GSTrackingRect.m +++ b/Source/GSTrackingRect.m @@ -51,10 +51,6 @@ rectangle = aRect; tag = aTag; owner = anObject; - if (owner != nil) - { - RETAIN(owner); - } user_data = theData; flags.inside = flag; flags.isValid = YES; @@ -63,7 +59,6 @@ - (void) dealloc { - TEST_RELEASE(owner); [super dealloc]; } @@ -103,10 +98,6 @@ { flags.isValid = NO; flags.checked = NO; - if (owner != nil) - { - DESTROY(owner); - } } } @@ -134,7 +125,7 @@ rectangle = [aDecoder decodeRect]; [aDecoder decodeValueOfObjCType: @encode(NSTrackingRectTag) at: &tag]; - [aDecoder decodeValueOfObjCType: @encode(id) at: &owner]; + owner = [aDecoder decodeObject]; [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &inside]; flags.inside = inside; } diff --git a/Source/NSCursor.m b/Source/NSCursor.m index 945a8cffc..6fb7ff14d 100644 --- a/Source/NSCursor.m +++ b/Source/NSCursor.m @@ -353,7 +353,7 @@ backgroundColorHint:(NSColor *)bg } return cursor; } -- (void)dealloc +- (void) dealloc { RELEASE (_cursor_image); [super dealloc]; @@ -550,8 +550,10 @@ backgroundColorHint:(NSColor *)bg } else { - [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_is_set_on_mouse_entered]; - [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_is_set_on_mouse_exited]; + [aDecoder decodeValueOfObjCType: @encode(BOOL) + at: &_is_set_on_mouse_entered]; + [aDecoder decodeValueOfObjCType: @encode(BOOL) + at: &_is_set_on_mouse_exited]; _cursor_image = [aDecoder decodeObject]; _hot_spot = [aDecoder decodePoint]; [self _computeCid]; diff --git a/Source/NSDocumentController.m b/Source/NSDocumentController.m index 0b3e099ee..718d311e0 100644 --- a/Source/NSDocumentController.m +++ b/Source/NSDocumentController.m @@ -520,7 +520,7 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ result = NSRunAlertPanel(title, _(@"You have unsaved documents"), _(@"Review Unsaved"), cancelString, - _(@"Quit Anyways")); + _(@"Quit Anyway")); #define ReviewUnsaved NSAlertDefaultReturn #define Cancel NSAlertAlternateReturn diff --git a/Source/NSHelpManager.m b/Source/NSHelpManager.m index e54a63b23..193d59082 100644 --- a/Source/NSHelpManager.m +++ b/Source/NSHelpManager.m @@ -38,7 +38,12 @@ #include "AppKit/NSWorkspace.h" #include "AppKit/NSHelpManager.h" #include "AppKit/NSHelpPanel.h" +#include "AppKit/NSHelpPanel.h" +#include "AppKit/NSCursor.h" +#include "AppKit/NSImage.h" #include "AppKit/NSGraphics.h" +#include "AppKit/NSScrollView.h" +#include "AppKit/NSTextView.h" #include "GNUstepGUI/GSHelpManagerPanel.h" @@ -259,6 +264,7 @@ static NSHelpManager *_gnu_sharedHelpManager = nil; static BOOL _gnu_contextHelpActive = NO; +static NSCursor *helpCursor = nil; // @@ -281,18 +287,31 @@ static BOOL _gnu_contextHelpActive = NO; + (void) setContextHelpModeActive: (BOOL) flag { - _gnu_contextHelpActive = flag; - if (flag) + if (flag != _gnu_contextHelpActive) { - [[NSNotificationCenter defaultCenter] - postNotificationName: NSContextHelpModeDidActivateNotification - object: [self sharedHelpManager]]; - } - else - { - [[NSNotificationCenter defaultCenter] - postNotificationName: NSContextHelpModeDidDeactivateNotification - object: [self sharedHelpManager]]; + _gnu_contextHelpActive = flag; + if (flag) + { + if (helpCursor == nil) + { + helpCursor = [[NSCursor alloc] + initWithImage: [NSImage imageNamed: @"common_HelpCursor"] + hotSpot: NSMakePoint(8, 2)]; + [helpCursor setOnMouseEntered: NO]; + [helpCursor setOnMouseExited: NO]; + } + [helpCursor push]; + [[NSNotificationCenter defaultCenter] + postNotificationName: NSContextHelpModeDidActivateNotification + object: [self sharedHelpManager]]; + } + else + { + [helpCursor pop]; + [[NSNotificationCenter defaultCenter] + postNotificationName: NSContextHelpModeDidDeactivateNotification + object: [self sharedHelpManager]]; + } } } diff --git a/Source/NSResponder.m b/Source/NSResponder.m index 6c89329f7..eb198c0ef 100644 --- a/Source/NSResponder.m +++ b/Source/NSResponder.m @@ -180,14 +180,16 @@ - (void) helpRequested: (NSEvent*)theEvent { - if (![[NSHelpManager sharedHelpManager] - showContextHelpForObject: self - locationHint: [theEvent locationInWindow]]) - if (_next_responder) - { - [_next_responder helpRequested: theEvent]; - return; - } + if ([[NSHelpManager sharedHelpManager] + showContextHelpForObject: self + locationHint: [theEvent locationInWindow]] == NO) + { + if (_next_responder) + { + [_next_responder helpRequested: theEvent]; + return; + } + } [NSHelpManager setContextHelpModeActive: NO]; } diff --git a/Source/NSToolbarItem.m b/Source/NSToolbarItem.m index 1e081867c..2da505baf 100644 --- a/Source/NSToolbarItem.m +++ b/Source/NSToolbarItem.m @@ -1311,6 +1311,7 @@ NSString *GSMovableToolbarItemPboardType = @"GSMovableToolbarItemPboardType"; - (void) setToolTip: (NSString *)toolTip { ASSIGN(_toolTip, toolTip); + [_view setToolTip: _toolTip]; } - (void) setView: (NSView *)view @@ -1346,6 +1347,7 @@ NSString *GSMovableToolbarItemPboardType = @"GSMovableToolbarItemPboardType"; _flags._setAction = [_view respondsToSelector: @selector(setAction:)]; _flags._setTarget = [_view respondsToSelector: @selector(setTarget:)]; _flags._setImage = [_backView respondsToSelector: @selector(setImage:)]; + [_view setToolTip: _toolTip]; } [_backView release]; diff --git a/Source/NSView.m b/Source/NSView.m index 11a57196f..04f5120c5 100644 --- a/Source/NSView.m +++ b/Source/NSView.m @@ -69,6 +69,7 @@ #include "GNUstepGUI/GSDisplayServer.h" #include "GNUstepGUI/GSTrackingRect.h" #include "GNUstepGUI/GSVersion.h" +#include "GSToolTips.h" /* * We need a fast array that can store objects without retain/release ... @@ -325,7 +326,12 @@ GSSetDragTypes(NSView* obj, NSArray *types) fromView: _super_view]; _visibleRect = NSIntersectionRect(superviewsVisibleRect, _bounds); + } + if (_rFlags.has_tooltips != 0) + { + GSToolTips *tt = [GSToolTips tipsForView: self]; + [tt rebuild]; } } } @@ -521,7 +527,7 @@ GSSetDragTypes(NSView* obj, NSArray *types) /* * Now we clean up all views which have us as their previous view. - * We also relase the memory we used. + * We also release the memory we used. */ if (nKV(self) != 0) { @@ -556,8 +562,16 @@ GSSetDragTypes(NSView* obj, NSArray *types) RELEASE(_frameMatrix); RELEASE(_boundsMatrix); TEST_RELEASE(_sub_views); - TEST_RELEASE(_tracking_rects); + if (_rFlags.has_tooltips != 0) + { + [GSToolTips removeTipsForView: self]; + } + if (_rFlags.has_currects != 0) + { + [self discardCursorRects]; // Handle release of cursors + } TEST_RELEASE(_cursor_rects); + TEST_RELEASE(_tracking_rects); [self unregisterDraggedTypes]; [self releaseGState]; @@ -2610,7 +2624,7 @@ Returns YES iff any scrolling was done. m = [rectClass allocWithZone: NSDefaultMallocZone()]; m = [m initWithRect: aRect tag: 0 - owner: anObject + owner: RETAIN(anObject) userData: NULL inside: YES]; [_cursor_rects addObject: m]; @@ -2624,17 +2638,18 @@ Returns YES iff any scrolling was done. { if (_rFlags.has_currects != 0) { - if (_rFlags.valid_rects != 0) - { - unsigned count = [_cursor_rects count]; - if (count > 0) + unsigned count = [_cursor_rects count]; + + if (count > 0) + { + GSTrackingRect *rects[count]; + + [_cursor_rects getObjects: rects]; + if (_rFlags.valid_rects != 0) { - GSTrackingRect *rects[count]; NSPoint loc = ((struct NSWindow_struct *)_window)->_lastPoint; unsigned i; - [_cursor_rects getObjects: rects]; - for (i = 0; i < count; ++i) { GSTrackingRect *r = rects[i]; @@ -2644,10 +2659,14 @@ Returns YES iff any scrolling was done. } [r invalidate]; } + _rFlags.valid_rects = 0; } - _rFlags.valid_rects = 0; + while (count-- > 0) + { + RELEASE([rects[count] owner]); + } + [_cursor_rects removeAllObjects]; } - [_cursor_rects removeAllObjects]; _rFlags.has_currects = 0; } } @@ -2677,6 +2696,7 @@ Returns YES iff any scrolling was done. _rFlags.has_currects = 0; _rFlags.valid_rects = 0; } + RELEASE(c); break; } else @@ -4091,13 +4111,15 @@ static NSView* findByTag(NSView *view, int aTag, unsigned *level) _rFlags.flipped_view = [self isFlipped]; - [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_is_rotated_from_base]; [aDecoder decodeValueOfObjCType: @encode(BOOL) - at: &_is_rotated_or_scaled_from_base]; + at: &_is_rotated_from_base]; + [aDecoder decodeValueOfObjCType: @encode(BOOL) + at: &_is_rotated_or_scaled_from_base]; [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_post_frame_changes]; - [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_autoresizes_subviews]; + [aDecoder decodeValueOfObjCType: @encode(BOOL) + at: &_autoresizes_subviews]; [aDecoder decodeValueOfObjCType: @encode(unsigned int) - at: &_autoresizingMask]; + at: &_autoresizingMask]; [self setNextKeyView: [aDecoder decodeObject]]; [[aDecoder decodeObject] setNextKeyView: self]; @@ -4109,9 +4131,9 @@ static NSView* findByTag(NSView *view, int aTag, unsigned *level) while ((sub = [e nextObject]) != nil) { NSAssert([sub window] == nil, - NSInternalInconsistencyException); + NSInternalInconsistencyException); NSAssert([sub superview] == nil, - NSInternalInconsistencyException); + NSInternalInconsistencyException); [sub viewWillMoveToWindow: _window]; [sub viewWillMoveToSuperview: self]; [sub setNextResponder: self]; @@ -4301,23 +4323,51 @@ static NSView* findByTag(NSView *view, int aTag, unsigned *level) owner: (id)anObject userData: (void *)data { - return 0; + GSToolTips *tt = [GSToolTips tipsForView: self]; + + _rFlags.has_tooltips = 1; + return [tt addToolTipRect: aRect owner: anObject userData: data]; } - (void) removeAllToolTips { + if (_rFlags.has_tooltips == 1) + { + GSToolTips *tt = [GSToolTips tipsForView: self]; + + [tt removeAllToolTips]; + } } - (void) removeToolTip: (NSToolTipTag)tag { + if (_rFlags.has_tooltips == 1) + { + GSToolTips *tt = [GSToolTips tipsForView: self]; + + [tt removeToolTip: tag]; + } } - (void) setToolTip: (NSString *)string { + if (_rFlags.has_tooltips == 1 || [string length] > 0) + { + GSToolTips *tt = [GSToolTips tipsForView: self]; + + _rFlags.has_tooltips = 1; + [tt setToolTip: string]; + } } - (NSString *) toolTip { + if (_rFlags.has_tooltips == 1) + { + GSToolTips *tt = [GSToolTips tipsForView: self]; + + return [tt toolTip]; + } return nil; } diff --git a/Source/NSWindow.m b/Source/NSWindow.m index 7a80b34e6..942ba58ae 100644 --- a/Source/NSWindow.m +++ b/Source/NSWindow.m @@ -75,6 +75,9 @@ #include "GNUstepGUI/GSTrackingRect.h" #include "GNUstepGUI/GSDisplayServer.h" #include "GNUstepGUI/GSToolbarView.h" +#include "GSToolTips.h" + +static GSToolTips *toolTipVisible = nil; #include "GSWindowDecorationView.h" @@ -96,11 +99,14 @@ BOOL GSViewAcceptsDrag(NSView *v, id dragInfo); /* * Category for internal methods (for use only within the NSWindow class itself - * or with other AppKit classes in the case of the _windowView method) + * or with other AppKit classes. */ @interface NSWindow (GNUstepPrivate) -+(void) _addAutodisplayedWindow: (NSWindow *)w; -+(void) _removeAutodisplayedWindow: (NSWindow *)w; + ++ (void) _addAutodisplayedWindow: (NSWindow *)w; ++ (void) _removeAutodisplayedWindow: (NSWindow *)w; ++ (void) _setToolTipVisible: (GSToolTips*)t; ++ (GSToolTips*) _toolTipVisible; - (void) _lossOfKeyOrMainWindow; - (NSView *) _windowView; @@ -109,6 +115,15 @@ BOOL GSViewAcceptsDrag(NSView *v, id dragInfo); @implementation NSWindow (GNUstepPrivate) ++ (void) _setToolTipVisible: (GSToolTips*)t +{ + toolTipVisible = t; +} + ++ (GSToolTips*) _toolTipVisible +{ + return toolTipVisible; +} /* Window autodisplay machinery. */ - (void) _handleAutodisplay @@ -2792,6 +2807,12 @@ resetCursorRectsForView(NSView *theView) character = [characters characterAtIndex: 0]; } + if (character == NSHelpFunctionKey) + { + [NSHelpManager setContextHelpModeActive: YES]; + return; + } + // If this is a TAB or TAB+SHIFT event, move to the next key view if (character == NSTabCharacter) { @@ -2851,6 +2872,27 @@ resetCursorRectsForView(NSView *theView) [super keyDown: theEvent]; } +- (void) keyUp: (NSEvent*)theEvent +{ + if ([NSHelpManager isContextHelpModeActive]) + { + NSString *characters = [theEvent characters]; + unichar character = 0; + + if ([characters length] > 0) + { + character = [characters characterAtIndex: 0]; + } + if (character == NSHelpFunctionKey) + { + [NSHelpManager setContextHelpModeActive: NO]; + return; + } + } + + [super keyUp: theEvent]; +} + /* Return mouse location in reciever's base coord system, ignores event * loop status */ - (NSPoint) mouseLocationOutsideOfEventStream @@ -3183,6 +3225,13 @@ resetCursorRectsForView(NSView *theView) else { ASSIGN(_lastView, v); + if (toolTipVisible != nil) + { + /* Inform the tooltips system that we have had + * a mouse down so it should stop displaying. + */ + [toolTipVisible mouseDown: theEvent]; + } [v mouseDown: theEvent]; } } @@ -3253,7 +3302,19 @@ resetCursorRectsForView(NSView *theView) * forward the mouse movement to the correct view. */ v = [_wv hitTest: [theEvent locationInWindow]]; - [v mouseMoved: theEvent]; + + /* If the view is displaying a tooltip, we should + * send mouse movements to the tooltip system so + * that the window can track the mouse. + */ + if (toolTipVisible != nil) + { + [toolTipVisible mouseMoved: theEvent]; + } + else + { + [v mouseMoved: theEvent]; + } } break; } diff --git a/Version b/Version index c489631b4..c66ec9bf9 100644 --- a/Version +++ b/Version @@ -5,15 +5,15 @@ GNUSTEP_GUI_GCC=2.9.5 # Versions for libraries that gnustep-gui is dependent upon -GNUSTEP_GUI_BASE=1.13.0 +GNUSTEP_GUI_BASE=1.14.0 GNUSTEP_GUI_LIBTIFF=3.4 # The version number of this release. GNUSTEP_GUI_MAJOR_VERSION=0 -GNUSTEP_GUI_MINOR_VERSION=11 +GNUSTEP_GUI_MINOR_VERSION=12 GNUSTEP_GUI_SUBMINOR_VERSION=0 # numeric value should match above -VERSION_NUMBER=011.0 +VERSION_NUMBER=012.0 GNUSTEP_GUI_VERSION=${GNUSTEP_GUI_MAJOR_VERSION}.${GNUSTEP_GUI_MINOR_VERSION}.${GNUSTEP_GUI_SUBMINOR_VERSION} VERSION=${GNUSTEP_GUI_VERSION} diff --git a/configure b/configure index ae26d1d69..08ef53a2f 100755 --- a/configure +++ b/configure @@ -2863,13 +2863,13 @@ fi #-------------------------------------------------------------------- # Set location of GNUstep dirs for later use GNUSTEP_HDIR=$GNUSTEP_SYSTEM_ROOT/Library/Headers -if test "$GNUSTEP_FLATTENED" = yes; then - GNUSTEP_LDIR=$GNUSTEP_SYSTEM_ROOT/Library/Libraries -else +if test "$GNUSTEP_IS_FLATTENED" = no; then clean_target_os=`$GNUSTEP_MAKEFILES/clean_os.sh $target_os` clean_target_cpu=`$GNUSTEP_MAKEFILES/clean_cpu.sh $target_cpu` obj_dir=$clean_target_cpu/$clean_target_os GNUSTEP_LDIR=$GNUSTEP_SYSTEM_ROOT/Library/Libraries/$obj_dir +else + GNUSTEP_LDIR=$GNUSTEP_SYSTEM_ROOT/Library/Libraries fi CPPFLAGS="$CPPFLAGS -I$GNUSTEP_HDIR" LDFLAGS="$LDFLAGS -L$GNUSTEP_LDIR/$LIBRARY_COMBO -L$GNUSTEP_LDIR" diff --git a/configure.ac b/configure.ac index 58a6f1897..585abebff 100644 --- a/configure.ac +++ b/configure.ac @@ -64,13 +64,13 @@ AC_PATH_X # Added for checking the existence of ungif. Note that #-------------------------------------------------------------------- # Set location of GNUstep dirs for later use GNUSTEP_HDIR=$GNUSTEP_SYSTEM_ROOT/Library/Headers -if test "$GNUSTEP_FLATTENED" = yes; then - GNUSTEP_LDIR=$GNUSTEP_SYSTEM_ROOT/Library/Libraries -else +if test "$GNUSTEP_IS_FLATTENED" = no; then clean_target_os=`$GNUSTEP_MAKEFILES/clean_os.sh $target_os` clean_target_cpu=`$GNUSTEP_MAKEFILES/clean_cpu.sh $target_cpu` obj_dir=$clean_target_cpu/$clean_target_os GNUSTEP_LDIR=$GNUSTEP_SYSTEM_ROOT/Library/Libraries/$obj_dir +else + GNUSTEP_LDIR=$GNUSTEP_SYSTEM_ROOT/Library/Libraries fi CPPFLAGS="$CPPFLAGS -I$GNUSTEP_HDIR" LDFLAGS="$LDFLAGS -L$GNUSTEP_LDIR/$LIBRARY_COMBO -L$GNUSTEP_LDIR"