Positioning Fixes. The window might still do some weirdness if you change its positi...
[ITKit.git] / ITTransientStatusWindow.m
1 #import "ITTransientStatusWindow.h"
2 #import "ITWindowEffect.h"
3 #import <ApplicationServices/ApplicationServices.h>
4 #import "ITCoreGraphicsHacks.h"
5 #import "ITTextField.h"
6 #import "ITTSWBackgroundView.h"
7
8 #define EFFECT_FPS 30.0
9
10
11 /*************************************************************************/
12 #pragma mark -
13 #pragma mark PRIVATE METHOD DECLARATIONS
14 /*************************************************************************/
15
16 @interface ITTransientStatusWindow (Private)
17 - (id)initWithContentView:(NSView *)contentView
18                  exitMode:(ITTransientStatusWindowExitMode)exitMode
19            backgroundType:(ITTransientStatusWindowBackgroundType)backgroundType;
20 - (void)rebuildWindow;
21 - (void)startVanishTimer;
22 - (void)stopVanishTimer;
23 @end
24
25
26 /*************************************************************************/
27 #pragma mark -
28 #pragma mark IMPLEMENTATION
29 /*************************************************************************/
30
31 @implementation ITTransientStatusWindow
32
33
34 /*************************************************************************/
35 #pragma mark -
36 #pragma mark SHARED STATIC OBJECTS
37 /*************************************************************************/
38
39 static ITTransientStatusWindow *staticWindow = nil;
40
41
42 /*************************************************************************/
43 #pragma mark -
44 #pragma mark INITIALIZATION METHODS
45 /*************************************************************************/
46
47 + (ITTransientStatusWindow *)sharedWindow
48 {
49     if ( ! (staticWindow) ) {
50         staticWindow = [[self alloc] initWithContentView:nil
51                                                 exitMode:ITTransientStatusWindowExitAfterDelay
52                                           backgroundType:ITTransientStatusWindowRounded];
53     }
54     return staticWindow;
55 }
56
57 - (id)initWithContentView:(NSView *)contentView
58                  exitMode:(ITTransientStatusWindowExitMode)exitMode
59            backgroundType:(ITTransientStatusWindowBackgroundType)backgroundType
60 {
61     NSRect contentRect;
62     
63     // If no Content View was provided, use a generic view.
64     if ( ! (contentView) ) {
65         contentView = [[[NSView alloc] initWithFrame:
66             NSMakeRect(100.0, 100.0, 200.0, 200.0)] autorelease];
67     }
68     
69     // Set the content rect to the frame of the content view, now guaranteed to exist.
70     contentRect = [contentView frame];
71     
72     if ( ( self = [super initWithContentRect:contentRect
73                                    styleMask:NSBorderlessWindowMask
74                                      backing:NSBackingStoreBuffered
75                                        defer:NO] ) ) {
76                                     
77         _visibilityState     = ITWindowHiddenState;
78         _exitMode            = exitMode;
79         _exitDelay           = DEFAULT_EXIT_DELAY;
80         _backgroundType      = backgroundType;
81         _verticalPosition    = ITWindowPositionBottom;
82         _horizontalPosition  = ITWindowPositionLeft;
83         _screenPadding       = 32.0;
84         //_screenNumber        = 0;
85         _entryEffect         = nil;
86         _exitEffect          = nil;
87         _reallyIgnoresEvents = YES;
88         _exitTimer           = nil;
89
90 //      if ( _backgroundType == ITTransientStatusWindowRounded ) {
91 //          _contentSubView = contentView;
92 //      } else {
93 //          [self setContentView:contentView];
94 //      }
95
96         [self setIgnoresMouseEvents:YES];
97         [self setLevel:NSScreenSaverWindowLevel];
98         [self setContentView:contentView];
99         [self rebuildWindow];
100     }
101     return self;
102 }
103
104
105 /*************************************************************************/
106 #pragma mark -
107 #pragma mark INSTANCE METHODS
108 /*************************************************************************/
109
110 - (BOOL)ignoresMouseEvents
111 {
112     return _reallyIgnoresEvents;
113 }
114
115 - (void)setIgnoresMouseEvents:(BOOL)flag
116 {
117     CGSValueObj          key;
118     CGSValueObj          ignore;
119
120     key = CGSCreateCString("IgnoreForEvents");
121     ignore = CGSCreateBoolean( (flag ? kCGSTrue : kCGSFalse) );
122     CGSSetWindowProperty([NSApp contextID], (CGSWindowID)[self windowNumber], key, ignore);
123     CGSReleaseObj(key);
124     CGSReleaseObj(ignore);
125
126     _reallyIgnoresEvents = flag;
127 }
128
129 /*
130
131 - (id)contentView
132 {
133     if ( _backgroundType == ITTransientStatusWindowRounded ) {
134         return _contentSubView;
135     } else {
136         return [super contentView];
137     }
138 }
139
140 - (void)setContentView:(NSView *)aView
141 {
142     if ( _backgroundType == ITTransientStatusWindowRounded ) {
143        [_contentSubView removeFromSuperview];
144         _contentSubView = aView;
145         [_contentSubView setFrame:[[super contentView] frame]];
146         [[super contentView] addSubview:_contentSubView];
147         [_contentSubView setNextResponder:self];
148     } else {
149         [super setContentView:aView];
150     }
151 }
152
153 */
154
155 - (IBAction)appear:(id)sender
156 {
157     if ( _visibilityState == ITWindowHiddenState ) {
158          // Window is hidden.  Appear as normal, and start the timer.
159         [_entryEffect performAppear];
160     } else if ( _visibilityState == ITWindowVisibleState ) {
161          // Window is completely visible.  Simply reset the timer.
162         [self startVanishTimer];
163     } else if ( _visibilityState == ITWindowAppearingState ) {
164          // Window is on its way in.  Do nothing.
165     } else if ( _visibilityState == ITWindowVanishingState ) {
166         // Window is on its way out.  Cancel the vanish.
167         [_exitEffect cancelVanish];
168     }
169 }
170
171 - (IBAction)vanish:(id)sender
172 {
173     if ( _visibilityState == ITWindowVisibleState ) {
174         // Window is totally visible.  Perform exit effect.
175         [_exitEffect performVanish];
176     } else if ( _visibilityState == ITWindowHiddenState ) {
177         // Window is hidden.  Do nothing.
178     } else if ( _visibilityState == ITWindowAppearingState ) {
179         // Window is on its way in.  Cancel appear.
180         [_entryEffect cancelAppear];
181     } else if ( _visibilityState == ITWindowVanishingState ) {
182         // Window is on its way out.  Do nothing.
183     }
184 }
185
186 - (ITWindowVisibilityState)visibilityState
187 {
188     return _visibilityState;
189 }
190
191 - (void)setVisibilityState:(ITWindowVisibilityState)newState
192 {
193     _visibilityState = newState;
194     
195     if ( _visibilityState == ITWindowVisibleState ) {
196         [self startVanishTimer];
197     } else if ( (_visibilityState == ITWindowVanishingState) || (_visibilityState == ITWindowHiddenState) ) {
198         [self stopVanishTimer];
199     }
200 }
201
202 - (ITTransientStatusWindowExitMode)exitMode
203 {
204     return _exitMode;
205 }
206
207 - (void)setExitMode:(ITTransientStatusWindowExitMode)newMode
208 {
209     _exitMode = newMode;
210     
211     if ( _visibilityState == ITWindowVisibleState ) {
212         if ( _exitMode == ITTransientStatusWindowExitOnCommand ) {
213             [self stopVanishTimer];
214         } else if ( _exitMode == ITTransientStatusWindowExitAfterDelay ) {
215             [self startVanishTimer];
216         }
217     }
218 }
219
220 - (float)exitDelay
221 {
222     return _exitDelay;
223 }
224
225 - (void)setExitDelay:(float)seconds
226 {
227     _exitDelay = seconds;
228 }
229
230 - (ITTransientStatusWindowBackgroundType)backgroundType
231 {
232 //  return _backgroundType;
233     return ITTransientStatusWindowRounded;
234 }
235
236 - (void)setBackgroundType:(ITTransientStatusWindowBackgroundType)newType
237 {
238 //  setBackgroundType: is currently ignored.  Defaults to ITTransientStatusWindowRounded.
239 //  _backgroundType = newType;
240     _backgroundType = ITTransientStatusWindowRounded;
241 }
242
243 - (ITVerticalWindowPosition)verticalPosition;
244 {
245     return _verticalPosition;
246 }
247
248 - (void)setVerticalPosition:(ITVerticalWindowPosition)newPosition;
249 {
250     _verticalPosition = newPosition;
251 }
252
253 - (ITHorizontalWindowPosition)horizontalPosition;
254 {
255     return _horizontalPosition;
256 }
257
258 - (void)setHorizontalPosition:(ITHorizontalWindowPosition)newPosition;
259 {
260     _horizontalPosition = newPosition;
261 }
262
263 - (float)effectProgress
264 {
265     return _effectProgress;
266 }
267
268 - (void)setEffectProgress:(float)newProgress
269 {
270     _effectProgress = newProgress;
271 }
272
273 - (float)screenPadding
274 {
275     return _screenPadding;
276 }
277
278 - (void)setScreenPadding:(float)newPadding
279 {
280     _screenPadding = newPadding;
281 }
282
283 /*- (int)screenNumber
284 {
285     return _screenNumber;
286 }
287
288 - (void)setScreenNumber:(int)newNumber
289 {
290     _screenNumber = newNumber;
291 }*/
292
293 - (ITWindowEffect *)entryEffect
294 {
295     return _entryEffect;
296 }
297
298 - (void)setEntryEffect:(ITWindowEffect *)newEffect
299 {
300     [_entryEffect releaseWhenIdle];
301     _entryEffect = [newEffect retain];
302 }
303
304 - (ITWindowEffect *)exitEffect
305 {
306     return _exitEffect;
307 }
308
309 - (void)setExitEffect:(ITWindowEffect *)newEffect
310 {
311     [_exitEffect releaseWhenIdle];
312     _exitEffect = [newEffect retain];
313 }
314
315
316 /*************************************************************************/
317 #pragma mark -
318 #pragma mark PRIVATE METHODS
319 /*************************************************************************/
320
321 - (void)rebuildWindow;
322 {
323     if ( _backgroundType == ITTransientStatusWindowRounded ) {
324         ITTSWBackgroundView *roundedView = [[[ITTSWBackgroundView alloc] initWithFrame:[self frame]] autorelease];
325
326         [self setBackgroundColor:[NSColor clearColor]];
327         [self setHasShadow:NO];
328         [self setOpaque:NO];
329         
330         [self setContentView:roundedView];
331 //      [super setContentView:roundedView];
332 //      [_contentSubView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
333 //      [self setContentView:_contentSubView];
334     } else {
335         // YUO == CLWONBAOT
336     }
337 }
338
339 - (void)startVanishTimer
340 {
341     if ( _exitMode == ITTransientStatusWindowExitAfterDelay) {
342         [self stopVanishTimer];
343         _exitTimer = [NSTimer scheduledTimerWithTimeInterval:_exitDelay
344                                                       target:self
345                                                     selector:@selector(doDelayedExit)
346                                                     userInfo:nil
347                                                      repeats:NO];
348     }
349 }
350
351 - (void)doDelayedExit
352 {
353     [self vanish:self];
354     _exitTimer = nil;
355 }
356
357 - (void)stopVanishTimer
358 {
359     if ( _exitTimer ) {
360         [_exitTimer invalidate];
361         _exitTimer = nil;
362     }
363 }
364
365 @end