-
Notifications
You must be signed in to change notification settings - Fork 1
/
StatusWindow.m
executable file
·517 lines (435 loc) · 19.9 KB
/
StatusWindow.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
#import "StatusWindow.h"
#define SW_PAD 24.00
#define SW_SPACE 24.00
#define SW_MINW 211.00
#define SW_BORDER 32.00
#define SW_METER_PAD 4.00
#define SW_BUTTON_PAD_R 30.00
#define SW_BUTTON_PAD_B 24.00
#define SW_BUTTON_DIV 12.00
#define SW_BUTTON_EXTRA_W 8.00
#define SW_SHADOW_SAT 1.25
@interface StatusWindow (Private)
- (NSRect)setupWindowWithDataSize:(NSSize)dataSize;
@end
@implementation StatusWindow
/*************************************************************************/
#pragma mark -
#pragma mark INITIALIZATION / DEALLOCATION METHODS
/*************************************************************************/
- (id)initWithContentView:(NSView *)contentView
exitMode:(ITTransientStatusWindowExitMode)exitMode
backgroundType:(ITTransientStatusWindowBackgroundType)backgroundType
{
if ( ( self = [super initWithContentView:contentView
exitMode:exitMode
backgroundType:backgroundType] ) ) {
// Set default values.
_image = [[NSImage imageNamed:@"NSApplicationIcon"] retain];
_locked = NO;
_sizing = ITTransientStatusWindowRegular;
}
return self;
}
- (void)dealloc
{
[_image release];
[super dealloc];
}
/*************************************************************************/
#pragma mark -
#pragma mark ACCESSOR METHODS
/*************************************************************************/
- (void)setImage:(NSImage *)newImage
{
[_image autorelease];
_image = [newImage copy];
}
- (void)setLocked:(BOOL)flag
{
_locked = flag;
[self setExitMode:(flag ? ITTransientStatusWindowExitOnCommand : ITTransientStatusWindowExitAfterDelay)];
}
- (void)setSizing:(ITTransientStatusWindowSizing)newSizing
{
_sizing = newSizing;
}
/*************************************************************************/
#pragma mark -
#pragma mark INSTANCE METHODS
/*************************************************************************/
- (void)appear:(id)sender
{
if ( ! _locked ) {
[super appear:sender];
}
}
- (void)vanish:(id)sender
{
if ( ! _locked ) {
[super vanish:sender];
}
}
- (NSRect)setupWindowWithDataSize:(NSSize)dataSize
{
float divisor = 1.0;
NSRect imageRect;
float imageWidth = 0.0;
float imageHeight = 0.0;
float dataWidth = dataSize.width;
float dataHeight = dataSize.height;
float contentHeight = 0.0;
float windowWidth = 0.0;
float windowHeight = 0.0;
NSRect visibleFrame = [[self screen] visibleFrame];
NSPoint screenOrigin = visibleFrame.origin;
float screenWidth = visibleFrame.size.width;
float screenHeight = visibleFrame.size.height;
float maxWidth = ( screenWidth - (SW_BORDER * 2) );
float maxHeight = ( screenHeight - (SW_BORDER * 2) );
float excessWidth = 0.0;
float excessHeight = 0.0;
NSPoint windowOrigin = NSZeroPoint;
ITImageView *imageView;
BOOL shouldAnimate = ( ! (([self visibilityState] == ITWindowAppearingState) ||
([self visibilityState] == ITWindowVanishingState)) );
if ( _sizing == ITTransientStatusWindowSmall ) {
divisor = SMALL_DIVISOR;
} else if ( _sizing == ITTransientStatusWindowMini ) {
divisor = MINI_DIVISOR;
}
// Get image width and height.
imageWidth = ( [_image size].width / divisor );
imageHeight = ( [_image size].height / divisor );
// Set the content height to the greater of the text and image heights.
contentHeight = ( ( imageHeight > dataHeight ) ? imageHeight : dataHeight );
// Setup the Window, and remove all its contentview's subviews.
windowWidth = ( (SW_PAD / divisor) + imageWidth + ((dataWidth > 0) ? (SW_SPACE / divisor) + dataWidth : 0) + (SW_PAD / divisor) );
windowHeight = ( (SW_PAD / divisor) + contentHeight + (SW_PAD / divisor) );
// Constrain size to max limits. Adjust data sizes accordingly.
excessWidth = (windowWidth - maxWidth );
excessHeight = (windowHeight - maxHeight);
if ( excessWidth > 0.0 ) {
windowWidth = maxWidth;
dataWidth -= excessWidth;
}
if ( excessHeight > 0.0 ) {
windowHeight = maxHeight;
dataHeight -= excessHeight;
}
if ( [self horizontalPosition] == ITWindowPositionLeft ) {
windowOrigin.x = ( SW_BORDER + screenOrigin.x );
} else if ( [self horizontalPosition] == ITWindowPositionCenter ) {
windowOrigin.x = ( screenOrigin.x + (screenWidth / 2) - (windowWidth / 2) );
} else if ( [self horizontalPosition] == ITWindowPositionRight ) {
windowOrigin.x = ( screenOrigin.x + screenWidth - (windowWidth + SW_BORDER) );
}
if ( [self verticalPosition] == ITWindowPositionTop ) {
windowOrigin.y = ( screenOrigin.y + screenHeight - (windowHeight + SW_BORDER) );
} else if ( [self verticalPosition] == ITWindowPositionMiddle ) {
// Middle-oriented windows should be slightly proud of the screen's middle.
windowOrigin.y = ( (screenOrigin.y + (screenHeight / 2) - (windowHeight / 2)) + (screenHeight / 8) );
} else if ( [self verticalPosition] == ITWindowPositionBottom ) {
windowOrigin.y = ( SW_BORDER + screenOrigin.y );
}
[self setFrame:NSMakeRect( windowOrigin.x,
windowOrigin.y,
windowWidth,
windowHeight) display:YES animate:shouldAnimate];
[[[self contentView] subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];
// Setup, position, fill, and add the image view to the content view.
imageRect = NSMakeRect( (SW_PAD / divisor) + ((dataWidth > 0) ? 4 : 0),
((SW_PAD / divisor) + ((contentHeight - imageHeight) / 2)),
imageWidth,
imageHeight );
imageView = [[[ITImageView alloc] initWithFrame:imageRect] autorelease];
[imageView setAutoresizingMask:(NSViewMinYMargin | NSViewMaxYMargin)];
[imageView setImage:_image];
[imageView setCastsShadow:YES];
[[self contentView] addSubview:imageView];
return NSMakeRect( ((SW_PAD / divisor) + imageWidth + (SW_SPACE / divisor)),
((SW_PAD / divisor) + ((contentHeight - dataHeight) / 2)),
dataWidth,
dataHeight);
}
- (void)buildImageWindowWithImage:(NSImage *)image
{
if (!_locked) {
float divisor = 1.0;
NSRect dataRect;
if (_sizing == ITTransientStatusWindowSmall) {
divisor = SMALL_DIVISOR;
} else if (_sizing == ITTransientStatusWindowMini) {
divisor = MINI_DIVISOR;
}
[self setImage:image];
dataRect = [self setupWindowWithDataSize:NSMakeSize(0, 0)]; //We have no text, so there is no data
[[self contentView] setNeedsDisplay:YES];
}
}
- (void)buildTextWindowWithString:(id)text
{
if ( ! _locked ) {
float divisor = 1.0;
float dataWidth = 0.0;
float dataHeight = 0.0;
NSRect dataRect;
NSArray *lines = [(([text isKindOfClass:[NSString class]]) ? text : [text mutableString]) componentsSeparatedByString:@"\n"];
id oneLine = nil;
NSEnumerator *lineEnum = [lines objectEnumerator];
float baseFontSize = 18.0;
ITTextField *textField;
NSFont *font;
NSDictionary *attr;
if ( _sizing == ITTransientStatusWindowSmall ) {
divisor = SMALL_DIVISOR;
} else if ( _sizing == ITTransientStatusWindowMini ) {
divisor = MINI_DIVISOR;
}
font = [NSFont fontWithName:@"LucidaGrande-Bold" size:(baseFontSize / divisor)];
attr = [NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName];
// Iterate over each line to get text width and height
while ( (oneLine = [lineEnum nextObject]) ) {
// Get the width of one line, adding 8.0 because Apple sucks donkey rectum.
float oneLineWidth = ( [oneLine sizeWithAttributes:attr].width + 8.0 );
// Add the height of this line to the total text height
dataHeight += [oneLine sizeWithAttributes:attr].height;
// If this line wider than the last one, set it as the text width.
dataWidth = ( ( dataWidth > oneLineWidth ) ? dataWidth : oneLineWidth );
}
// Add 4.0 to the final dataHeight to accomodate the shadow.
dataHeight += 4.0;
dataRect = [self setupWindowWithDataSize:NSMakeSize(dataWidth, dataHeight)];
// Create, position, setup, fill, and add the text view to the content view.
textField = [[[ITTextField alloc] initWithFrame:dataRect] autorelease];
[textField setAutoresizingMask:(NSViewHeightSizable | NSViewWidthSizable)];
[textField setEditable:NO];
[textField setSelectable:NO];
[textField setBordered:NO];
[textField setDrawsBackground:NO];
[textField setFont:font];
[textField setTextColor:[NSColor whiteColor]];
[textField setCastsShadow:YES];
[[textField cell] setWraps:NO];
if ([text isKindOfClass:[NSString class]]) {
[textField setStringValue:text];
} else {
[textField setAttributedStringValue:text];
}
[textField setShadowSaturation:SW_SHADOW_SAT];
[[self contentView] addSubview:textField];
// Display the window.
[[self contentView] setNeedsDisplay:YES];
_textField = textField;
}
}
- (void)buildMeterWindowWithCharacter:(NSString *)character
size:(float)size
count:(int)count
active:(int)active
{
if ( ! _locked ) {
float divisor = 1.0;
NSFont *font;
NSDictionary *attr;
NSSize charSize;
float cellHeight;
float cellWidth;
float dataWidth;
NSRect dataRect;
NSEnumerator *cellEnum = nil;
id aCell = nil;
int activeCount = 0;
NSColor *onColor = [NSColor whiteColor];
NSColor *offColor = [NSColor colorWithCalibratedWhite:0.15 alpha:0.50];
NSMatrix *volMatrix;
if ( _sizing == ITTransientStatusWindowSmall ) {
divisor = SMALL_DIVISOR;
} else if ( _sizing == ITTransientStatusWindowMini ) {
divisor = MINI_DIVISOR;
}
font = [NSFont fontWithName:@"AppleGothic" size:( size / divisor )];
attr = [NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName];
charSize = [character sizeWithAttributes:attr];
cellHeight = ( charSize.height + 4.0 ); // Add 4.0 for shadow
cellWidth = ( (charSize.width) + (SW_METER_PAD / divisor) );
dataWidth = ( cellWidth * count );
dataRect = [self setupWindowWithDataSize:NSMakeSize(dataWidth, cellHeight)];
volMatrix = [[[NSMatrix alloc] initWithFrame:dataRect
mode:NSHighlightModeMatrix
cellClass:NSClassFromString(@"ITTextFieldCell")
numberOfRows:1
numberOfColumns:count] autorelease];
[volMatrix setCellSize:NSMakeSize(cellWidth, cellHeight)];
[volMatrix setIntercellSpacing:NSMakeSize(0, 0)];
[volMatrix setAutoresizingMask:(NSViewHeightSizable | NSViewWidthSizable)];
cellEnum = [[volMatrix cells] objectEnumerator];
while ( (aCell = [cellEnum nextObject]) ) {
[aCell setEditable:NO];
[aCell setSelectable:NO];
[aCell setBordered:NO];
[aCell setDrawsBackground:NO];
[aCell setAlignment:NSCenterTextAlignment];
[aCell setFont:font];
[aCell setStringValue:character];
[aCell setShadowSaturation:SW_SHADOW_SAT];
activeCount ++;
if ( active >= activeCount ) {
[aCell setCastsShadow:YES];
[aCell setTextColor:onColor];
} else {
[aCell setCastsShadow:NO];
[aCell setTextColor:offColor];
}
}
[[self contentView] addSubview:volMatrix];
[[self contentView] setNeedsDisplay:YES];
}
}
- (void)buildDialogWindowWithMessage:(NSString *)message
defaultButton:(NSString *)defaultTitle
alternateButton:(NSString *)alternateTitle
target:(id)target
defaultAction:(SEL)okAction
alternateAction:(SEL)alternateAction
{
if ( ! _locked ) {
float divisor = 1.0;
float textWidth = 0.0;
float textHeight = 0.0;
float okWidth = 0.0;
float cancelWidth = 0.0;
float wideButtonW = 0.0;
float buttonWidth = 0.0;
float dataHeight = 0.0;
float dataWidth = 0.0;
NSRect dataRect;
float textY = 0.0;
NSRect textRect;
float textAddBelow = 32.0;
float dataMinH = 92.0;
float textMinH = 48.0;
NSArray *lines = [message componentsSeparatedByString:@"\n"];
id oneLine = nil;
NSEnumerator *lineEnum = [lines objectEnumerator];
float baseFontSize = 18.0;
ITTextField *textField;
ITButton *okButton;
ITButton *cancelButton;
NSColor *textColor = [NSColor whiteColor];
NSFont *font;
NSDictionary *attr;
NSFont *buttonFont;
NSDictionary *buttonAttr;
if ( _sizing == ITTransientStatusWindowSmall ) {
divisor = SMALL_DIVISOR;
} else if ( _sizing == ITTransientStatusWindowMini ) {
divisor = MINI_DIVISOR;
}
font = [NSFont fontWithName:@"LucidaGrande-Bold" size:(baseFontSize / divisor)];
attr = [NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName];
buttonFont = [NSFont fontWithName:@"LucidaGrande-Bold" size:(14 / divisor)];
buttonAttr = [NSDictionary dictionaryWithObjectsAndKeys:
buttonFont , NSFontAttributeName,
textColor , NSForegroundColorAttributeName,
nil];
// Iterate over each line to get text width and height
while ( (oneLine = [lineEnum nextObject]) ) {
// Get the width of one line, adding 8.0 because Apple sucks donkey rectum.
float oneLineWidth = ( [oneLine sizeWithAttributes:attr].width + 8.0 );
// Add the height of this line to the total text height
textHeight += [oneLine sizeWithAttributes:attr].height;
// If this line wider than the last one, set it as the text width.
textWidth = ( ( textWidth > oneLineWidth ) ? textWidth : oneLineWidth );
}
// Add 4.0 to the final dataHeight to accomodate the shadow.
textHeight += 4.0;
// Add extra padding below the text
dataHeight = (textHeight + textAddBelow);
// Test to see if data height is tall enough
if ( dataHeight < dataMinH ) {
dataHeight = dataMinH;
}
// Make the buttons, set the titles, and size them to fit their titles
okButton = [[[ITButton alloc] initWithFrame:NSMakeRect(0, 0, 300, 24)] autorelease];
cancelButton = [[[ITButton alloc] initWithFrame:NSMakeRect(0, 0, 300, 24)] autorelease];
[okButton setTarget:target];
[cancelButton setTarget:target];
[okButton setAction:okAction];
[cancelButton setAction:alternateAction];
[okButton setBezelStyle:ITGrayRoundedBezelStyle];
[cancelButton setBezelStyle:ITGrayRoundedBezelStyle];
[okButton setAlignment:NSRightTextAlignment];
[cancelButton setAlignment:NSCenterTextAlignment];
[okButton setImagePosition:NSNoImage];
[cancelButton setImagePosition:NSNoImage];
[okButton setAttributedTitle:[[[NSAttributedString alloc] initWithString:defaultTitle
attributes:buttonAttr] autorelease]];
[cancelButton setAttributedTitle:[[[NSAttributedString alloc] initWithString:alternateTitle
attributes:buttonAttr] autorelease]];
[okButton sizeToFit];
[cancelButton sizeToFit];
// Get the button widths. Add any extra width here.
okWidth = ([okButton frame].size.width + SW_BUTTON_EXTRA_W);
cancelWidth = ([cancelButton frame].size.width + SW_BUTTON_EXTRA_W);
// Figure out which button is wider.
wideButtonW = ( (okWidth > cancelWidth) ? okWidth : cancelWidth );
// Get the total width of the buttons. Add the divider space.
buttonWidth = ( (wideButtonW * 2) + SW_BUTTON_DIV );
// Set the dataWidth to whichever is greater: text width or button width.
dataWidth = ( (textWidth > buttonWidth) ? textWidth : buttonWidth);
// Setup the window
dataRect = [self setupWindowWithDataSize:NSMakeSize(dataWidth, dataHeight)];
// Set an initial vertical point for the textRect's origin.
textY = dataRect.origin.y + textAddBelow;
// Move that point up if the minimimum height of the text area is not occupied.
if ( textHeight < textMinH ) {
textY += ( (textMinH - textHeight) / 2 );
}
// Build the text rect.
textRect = NSMakeRect(dataRect.origin.x,
textY,
textWidth,
textHeight);
// Create, position, setup, fill, and add the text view to the content view.
textField = [[[ITTextField alloc] initWithFrame:textRect] autorelease];
[textField setEditable:NO];
[textField setSelectable:NO];
[textField setBordered:NO];
[textField setDrawsBackground:NO];
[textField setFont:font];
[textField setTextColor:textColor];
[textField setCastsShadow:YES];
[textField setStringValue:message];
[textField setShadowSaturation:SW_SHADOW_SAT];
[[self contentView] addSubview:textField];
// Set the button frames, and add them to the content view.
[okButton setFrame:NSMakeRect( ([[self contentView] frame].size.width - (wideButtonW + SW_BUTTON_PAD_R) ),
SW_BUTTON_PAD_B,
wideButtonW,
24.0)];
[cancelButton setFrame:NSMakeRect( ([[self contentView] frame].size.width - ((wideButtonW * 2) + SW_BUTTON_DIV + SW_BUTTON_PAD_R) ),
SW_BUTTON_PAD_B,
wideButtonW,
24.0)];
[[self contentView] addSubview:okButton];
if (alternateTitle) {
[[self contentView] addSubview:cancelButton];
}
[self setIgnoresMouseEvents:NO];
// Display the window.
[[self contentView] setNeedsDisplay:YES];
}
}
- (void)updateTime:(NSString *)time range:(NSRange)range
{
NSMutableAttributedString *string = [[_textField attributedStringValue] mutableCopy];
[string replaceCharactersInRange:range withString:time];
[_textField setAttributedStringValue:[string autorelease]];
[[self contentView] setNeedsDisplay:YES];
}
- (NSTimeInterval)animationResizeTime:(NSRect)newFrame
{
return (NSTimeInterval)0.25;
}
@end