aboutsummaryrefslogtreecommitdiffstats
path: root/Source/SPTooltip.m
diff options
context:
space:
mode:
authorBibiko <bibiko@eva.mpg.de>2009-08-11 15:59:35 +0000
committerBibiko <bibiko@eva.mpg.de>2009-08-11 15:59:35 +0000
commitf6afd1e9699b891f1deee9058a1737e8c5744bd7 (patch)
treed7764e93b9d093578e998eb69785956641d9c941 /Source/SPTooltip.m
parente0b78dfa72f5c24ba7e592d4b381fd97bfb2d407 (diff)
downloadsequelpro-f6afd1e9699b891f1deee9058a1737e8c5744bd7.tar.gz
sequelpro-f6afd1e9699b891f1deee9058a1737e8c5744bd7.tar.bz2
sequelpro-f6afd1e9699b891f1deee9058a1737e8c5744bd7.zip
• implemented the first working copy of SPTooltip
- not yet finished • added svn property "Id"
Diffstat (limited to 'Source/SPTooltip.m')
-rw-r--r--Source/SPTooltip.m280
1 files changed, 280 insertions, 0 deletions
diff --git a/Source/SPTooltip.m b/Source/SPTooltip.m
index c0cfd10f..0fce5f2a 100644
--- a/Source/SPTooltip.m
+++ b/Source/SPTooltip.m
@@ -29,6 +29,286 @@
#import "SPTooltip.h"
+
+static float slow_in_out (float t)
+{
+ if(t < 1.0f)
+ t = 1.0f / (1.0f + exp((-t*12.0f)+6.0f));
+ if(t>1.0f) return 1.0f;
+ return t;
+}
+
+
+@interface SPTooltip (Private)
+- (void)setContent:(NSString *)content transparent:(BOOL)transparent;
+- (void)runUntilUserActivity;
+- (void)stopAnimation:(id)sender;
+@end
+
+@interface WebView (LeopardOnly)
+- (void)setDrawsBackground:(BOOL)drawsBackground;
+@end
+
@implementation SPTooltip
+// ==================
+// = Setup/teardown =
+// ==================
++ (void)showWithObject:(id)content ofType:(NSString *)type transparent:(BOOL)transparent
+{
+ SPTooltip* tip = [SPTooltip new];
+ [tip setFrameTopLeftPoint:[NSEvent mouseLocation]];
+ // The tooltip will show itself automatically when the HTML is loaded
+ if([type isEqualToString:@"text"]) {
+ NSString* html = nil;
+ NSMutableString* text = [[(NSString*)content mutableCopy] autorelease];
+ if(text)
+ {
+ [text replaceOccurrencesOfString:@"&" withString:@"&amp;" options:0 range:NSMakeRange(0, [text length])];
+ [text replaceOccurrencesOfString:@"<" withString:@"&lt;" options:0 range:NSMakeRange(0, [text length])];
+ [text insertString:@"<pre>" atIndex:0];
+ [text appendString:@"</pre>"];
+ html = text;
+ }
+ else
+ {
+ html = @"Error";
+ }
+ [tip setContent:html transparent:transparent];
+ }
+ else if([type isEqualToString:@"html"])
+ [tip setContent:(NSString*)content transparent:transparent];
+
+}
+
++ (void)showWithObject:(id)content atLocation:(NSPoint)point ofType:(NSString *)type transparent:(BOOL)transparent
+{
+ SPTooltip* tip = [SPTooltip new];
+ [tip setFrameTopLeftPoint:point];
+ // The tooltip will show itself automatically when the HTML is loaded
+ if([type isEqualToString:@"text"]) {
+ NSString* html = nil;
+ NSMutableString* text = [[(NSString*)content mutableCopy] autorelease];
+ if(text)
+ {
+ [text replaceOccurrencesOfString:@"&" withString:@"&amp;" options:0 range:NSMakeRange(0, [text length])];
+ [text replaceOccurrencesOfString:@"<" withString:@"&lt;" options:0 range:NSMakeRange(0, [text length])];
+ [text insertString:@"<pre>" atIndex:0];
+ [text appendString:@"</pre>"];
+ html = text;
+ }
+ else
+ {
+ html = @"Error";
+ }
+ [tip setContent:html transparent:transparent];
+ }
+ else if([type isEqualToString:@"html"])
+ [tip setContent:(NSString*)content transparent:transparent];
+}
+
+- (id)init;
+{
+ if(self = [self initWithContentRect:NSMakeRect(1,1,1,1) styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO])
+ {
+ [self setReleasedWhenClosed:YES];
+ [self setAlphaValue:0.97f];
+ [self setOpaque:NO];
+ [self setBackgroundColor:[NSColor colorWithDeviceRed:1.0f green:0.96f blue:0.76f alpha:1.0f]];
+ [self setBackgroundColor:[NSColor clearColor]];
+ [self setHasShadow:YES];
+ [self setLevel:NSStatusWindowLevel];
+ [self setHidesOnDeactivate:YES];
+ [self setIgnoresMouseEvents:YES];
+
+ SPTooltipPreferencesIdentifier = @"SequelPro Tooltip";
+
+ webPreferences = [[WebPreferences alloc] initWithIdentifier:SPTooltipPreferencesIdentifier];
+ [webPreferences setJavaScriptEnabled:YES];
+ NSString *fontName = @"Monaco";
+ int fontSize = 12;
+ NSFont* font = [NSFont fontWithName:fontName size:fontSize];
+ [webPreferences setStandardFontFamily:[font familyName]];
+ [webPreferences setDefaultFontSize:fontSize];
+ [webPreferences setDefaultFixedFontSize:fontSize];
+
+ webView = [[WebView alloc] initWithFrame:NSZeroRect];
+ [webView setPreferencesIdentifier:SPTooltipPreferencesIdentifier];
+ [webView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
+ [webView setFrameLoadDelegate:self];
+ if ([webView respondsToSelector:@selector(setDrawsBackground:)])
+ [webView setDrawsBackground:NO];
+
+ [self setContentView:webView];
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ [didOpenAtDate release];
+ [webView release];
+ [webPreferences release];
+ [super dealloc];
+}
+
+// ===========
+// = Webview =
+// ===========
+- (void)setContent:(NSString *)content transparent:(BOOL)transparent
+{
+ NSString *fullContent = @"<html>"
+ @"<head>"
+ @" <style type='text/css' media='screen'>"
+ @" body {"
+ @" background: %@;"
+ @" margin: 0;"
+ @" padding: 2px;"
+ @" overflow: hidden;"
+ @" display: table-cell;"
+ @" max-width: 800px;"
+ @" }"
+ @" pre { white-space: pre-wrap; }"
+ @" </style>"
+ @"</head>"
+ @"<body>%@</body>"
+ @"</html>";
+
+ fullContent = [NSString stringWithFormat:fullContent, transparent ? @"transparent" : @"#F6EDC3", content];
+ [[webView mainFrame] loadHTMLString:fullContent baseURL:nil];
+}
+
+- (void)sizeToContent
+{
+ // Current tooltip position
+ NSPoint pos = NSMakePoint([self frame].origin.x, [self frame].origin.y + [self frame].size.height);
+
+ // Find the screen which we are displaying on
+ NSRect screenFrame = [[NSScreen mainScreen] frame];
+ NSScreen* candidate;
+ for(candidate in [NSScreen screens])
+ {
+ if(NSMinX([candidate frame]) < pos.x && NSMinX([candidate frame]) > NSMinX(screenFrame))
+ screenFrame = [candidate frame];
+ }
+
+ // The webview is set to a large initial size and then sized down to fit the content
+ [self setContentSize:NSMakeSize(screenFrame.size.width - screenFrame.size.width / 3.0f , screenFrame.size.height)];
+
+ int height = [[[webView windowScriptObject] evaluateWebScript:@"document.body.offsetHeight + document.body.offsetTop;"] intValue];
+ int width = [[[webView windowScriptObject] evaluateWebScript:@"document.body.offsetWidth + document.body.offsetLeft;"] intValue];
+
+ [webView setFrameSize:NSMakeSize(width, height)];
+
+ NSRect frame = [self frameRectForContentRect:[webView frame]];
+ frame.size.width = MIN(NSWidth(frame), NSWidth(screenFrame));
+ frame.size.height = MIN(NSHeight(frame), NSHeight(screenFrame));
+
+
+ [self setFrame:frame display:NO];
+
+ pos.x = MAX(NSMinX(screenFrame), MIN(pos.x, NSMaxX(screenFrame)-NSWidth(frame)));
+ pos.y = MIN(MAX(NSMinY(screenFrame)+NSHeight(frame), pos.y), NSMaxY(screenFrame));
+
+ [self setFrameTopLeftPoint:pos];
+}
+
+- (void)webView:(WebView*)sender didFinishLoadForFrame:(WebFrame*)frame;
+{
+ [self sizeToContent];
+ [self orderFront:self];
+ [self performSelector:@selector(runUntilUserActivity) withObject:nil afterDelay:0];
+}
+
+// ==================
+// = Event handling =
+// ==================
+- (BOOL)shouldCloseForMousePosition:(NSPoint)aPoint
+{
+ float ignorePeriod = [[NSUserDefaults standardUserDefaults] floatForKey:@"OakToolTipMouseMoveIgnorePeriod"];
+ if(-[didOpenAtDate timeIntervalSinceNow] < ignorePeriod)
+ return NO;
+
+ if(NSEqualPoints(mousePositionWhenOpened, NSZeroPoint))
+ {
+ mousePositionWhenOpened = aPoint;
+ return NO;
+ }
+
+ NSPoint p = mousePositionWhenOpened;
+ float deltaX = p.x - aPoint.x;
+ float deltaY = p.y - aPoint.y;
+ float dist = sqrtf(deltaX * deltaX + deltaY * deltaY);
+
+ float moveThreshold = 2;
+ return dist > moveThreshold;
+}
+
+- (void)runUntilUserActivity
+{
+ [self setValue:[NSDate date] forKey:@"didOpenAtDate"];
+ mousePositionWhenOpened = NSZeroPoint;
+
+ NSWindow* keyWindow = [[NSApp keyWindow] retain];
+ BOOL didAcceptMouseMovedEvents = [keyWindow acceptsMouseMovedEvents];
+ [keyWindow setAcceptsMouseMovedEvents:YES];
+ NSEvent* event;
+ while(event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantFuture] inMode:NSDefaultRunLoopMode dequeue:YES])
+ {
+ [NSApp sendEvent:event];
+
+ if([event type] == NSLeftMouseDown || [event type] == NSRightMouseDown || [event type] == NSOtherMouseDown || [event type] == NSKeyDown || [event type] == NSScrollWheel)
+ break;
+
+ if([event type] == NSMouseMoved && [self shouldCloseForMousePosition:[NSEvent mouseLocation]])
+ break;
+
+ if(keyWindow != [NSApp keyWindow] || ![NSApp isActive])
+ break;
+ }
+
+ [keyWindow setAcceptsMouseMovedEvents:didAcceptMouseMovedEvents];
+ [keyWindow release];
+
+ [self orderOut:self];
+}
+
+// =============
+// = Animation =
+// =============
+- (void)orderOut:(id)sender
+{
+ if(![self isVisible] || animationTimer)
+ return;
+
+ [self stopAnimation:self];
+ [self setValue:[NSDate date] forKey:@"animationStart"];
+ [self setValue:[NSTimer scheduledTimerWithTimeInterval:0.02f target:self selector:@selector(animationTick:) userInfo:nil repeats:YES] forKey:@"animationTimer"];
+}
+
+- (void)animationTick:(id)sender
+{
+ float alpha = 0.97f * (1.0f - slow_in_out(-1.5 * [animationStart timeIntervalSinceNow]));
+ if(alpha > 0.0f)
+ {
+ [self setAlphaValue:alpha];
+ }
+ else
+ {
+ [super orderOut:self];
+ [self stopAnimation:self];
+ [self close];
+ }
+}
+- (void)stopAnimation:(id)sender;
+{
+ if(animationTimer)
+ {
+ [[self retain] autorelease];
+ [animationTimer invalidate];
+ [self setValue:nil forKey:@"animationTimer"];
+ [self setValue:nil forKey:@"animationStart"];
+ [self setAlphaValue:0.97f];
+ }
+}
@end