Core/Source/GrowlPositionController.m
author Peter Hosey
Fri Jun 05 21:49:20 2009 -0700 (2009-06-05)
changeset 4208 10d1c49d4df1
parent 3583 66400f11c7c2
child 4227 1d6cde4c4fc3
permissions -rw-r--r--
Fix MusicVideo not showing non-first notifications on displays that don't have the menu bar on them.

The problem was that GrowlDisplayWindowController and GrowlPositionController were both trying to get the screen of a window that wasn't on a screen (yet|anymore), both before and after displaying the notification. The solution is to get the screen from the display window controller, not from the window.
ofri@2651
     1
//
ofri@2651
     2
//  GrowlPositionController.m
ofri@2651
     3
//  Growl
ofri@2651
     4
//
ofri@2651
     5
//  Created by Ofri Wolfus on 31/08/05.
ingmarstein@3040
     6
//  Copyright 2004-2006 The Growl Project. All rights reserved.
ofri@2654
     7
//
ofri@2654
     8
// This file is under the BSD License, refer to License.txt for details
ofri@2651
     9
//
ofri@2651
    10
ofri@2651
    11
#import "GrowlPositionController.h"
rudy@2947
    12
#import "GrowlDisplayWindowController.h"
bgannin@3267
    13
#import "GrowlPreferencesController.h"
boredzo@3091
    14
#import "NSMutableStringAdditions.h"
bgannin@3317
    15
#import "GrowlDefines.h"
bgannin@3317
    16
#import "GrowlTicketController.h"
ofri@2651
    17
boredzo@3093
    18
#import "GrowlLog.h"
boredzo@3093
    19
bgannin@3450
    20
@interface GrowlPositionController (PRIVATE)
ofri@2666
    21
- (NSMutableSet *)reservedRectsForScreen:(NSScreen *)inScreen;
evands@3576
    22
- (NSRectArray)copyRectsInSet:(NSSet *)rectSet count:(int *)outCount padding:(float)padding excludingDisplayController:(GrowlDisplayWindowController *)displayController;
ofri@2666
    23
@end
ofri@2666
    24
ofri@2651
    25
@implementation GrowlPositionController
ofri@2651
    26
ofri@2651
    27
//Initialize
ofri@2651
    28
- (id) initSingleton {
ingmarstein@2655
    29
	if ((self = [super initSingleton])) {
ingmarstein@2655
    30
		reservedRects = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
evands@3516
    31
		reservedRectsByController = [[NSMutableDictionary alloc] init];
ingmarstein@2655
    32
	}
ingmarstein@2655
    33
ofri@2651
    34
	return self;
ofri@2651
    35
}
ofri@2651
    36
ofri@2651
    37
//Deallocate
ofri@2651
    38
- (void) destroy {
ingmarstein@2655
    39
	CFRelease(reservedRects);
ofri@2651
    40
}
ofri@2651
    41
bgannin@3267
    42
//Read in the stored selection from picker and translate to a properly returned GrowlPosition.
bgannin@3267
    43
+ (enum GrowlPosition)selectedOriginPosition
bgannin@3267
    44
{
bgannin@3317
    45
	enum GrowlPositionOrigin globalSelectedPosition = (enum GrowlPositionOrigin)[[GrowlPreferencesController sharedController] integerForKey:GROWL_POSITION_PREFERENCE_KEY];
bgannin@3267
    46
	enum GrowlPosition translatedPosition;
bgannin@3267
    47
		
bgannin@3317
    48
	switch(globalSelectedPosition){
ingmarstein@3401
    49
		default:
bgannin@3267
    50
		case GrowlNoOrigin:
bgannin@3267
    51
			//Default to middle of the screen if no origin is set, though this case shouldn't be hit.
bgannin@3267
    52
			translatedPosition = GrowlMiddleColumnPosition;
bgannin@3267
    53
			break;
bgannin@3267
    54
		case GrowlTopLeftCorner:
bgannin@3267
    55
			translatedPosition = GrowlTopLeftPosition;
bgannin@3267
    56
			break;
bgannin@3267
    57
		case GrowlBottomRightCorner:
bgannin@3267
    58
			translatedPosition = GrowlBottomRightPosition;
bgannin@3267
    59
			break;
bgannin@3267
    60
		case GrowlTopRightCorner:
bgannin@3267
    61
			translatedPosition = GrowlTopRightPosition;
bgannin@3267
    62
			break;
bgannin@3267
    63
		case GrowlBottomLeftCorner:
bgannin@3267
    64
			translatedPosition = GrowlBottomLeftPosition;
bgannin@3267
    65
			break;
bgannin@3267
    66
	}
bgannin@3267
    67
	
bgannin@3267
    68
	return translatedPosition;
bgannin@3267
    69
}
bgannin@3267
    70
ofri@2651
    71
//Return a rect suitable for the position and screen.
boredzo@3085
    72
+ (NSRect) rectForPosition:(enum GrowlPosition)position inScreen:(NSScreen *)screen {
ofri@2660
    73
	NSRect screenFrame;
ofri@2660
    74
	NSSize areaSize;
ingmarstein@2655
    75
	NSRect result = NSZeroRect;
ingmarstein@2721
    76
ofri@2660
    77
	//Treat nil as the main screen
ofri@2660
    78
	if (!screen)
ofri@2660
    79
		screen = [NSScreen mainScreen];
ingmarstein@2721
    80
ofri@2660
    81
	screenFrame = [screen visibleFrame];
ofri@2660
    82
	areaSize = NSMakeSize(screenFrame.size.width / 3.0f, screenFrame.size.height / 3.0f);	//We have 9 identical areas on each screen
ingmarstein@2655
    83
ofri@2651
    84
	switch (position) {
ofri@2651
    85
			//Top left
ofri@2651
    86
		case GrowlTopLeftPosition:
ofri@2651
    87
			result = NSMakeRect(screenFrame.origin.x,
ingmarstein@2655
    88
								screenFrame.origin.y + areaSize.height + areaSize.height,
ingmarstein@2655
    89
								areaSize.width,
ingmarstein@2655
    90
								areaSize.height);
ingmarstein@2655
    91
			break;
ingmarstein@2655
    92
ofri@2651
    93
			//Top middle
ofri@2651
    94
		case GrowlTopMiddlePosition:
ofri@2651
    95
			result = NSMakeRect(screenFrame.origin.x + areaSize.width,
ingmarstein@2655
    96
								screenFrame.origin.y + areaSize.height + areaSize.height,
ingmarstein@2655
    97
								areaSize.width,
ingmarstein@2655
    98
								areaSize.height);
ingmarstein@2655
    99
			break;
ingmarstein@2655
   100
ofri@2651
   101
			//Top right
ofri@2651
   102
		case GrowlTopRightPosition:
ingmarstein@2655
   103
			result = NSMakeRect(screenFrame.origin.x + areaSize.width + areaSize.width,
ingmarstein@2655
   104
								screenFrame.origin.y + areaSize.height + areaSize.height,
ingmarstein@2655
   105
								areaSize.width,
ingmarstein@2655
   106
								areaSize.height);
ingmarstein@2655
   107
			break;
ingmarstein@2655
   108
ofri@2651
   109
			//Center left
ofri@2651
   110
		case GrowlCenterLeftPosition:
ofri@2651
   111
			result = NSMakeRect(screenFrame.origin.x,
ofri@2651
   112
								screenFrame.origin.y + areaSize.height,
ofri@2651
   113
								areaSize.width,
ofri@2651
   114
								areaSize.height);
ofri@2651
   115
			break;
ingmarstein@2655
   116
ofri@2651
   117
			//Center middle
ofri@2651
   118
		case GrowlCenterMiddlePosition:
ofri@2651
   119
			result = NSMakeRect(screenFrame.origin.x + areaSize.width,
ofri@2651
   120
								screenFrame.origin.y + areaSize.height,
ofri@2651
   121
								areaSize.width,
ofri@2651
   122
								areaSize.height);
ofri@2651
   123
			break;
ingmarstein@2655
   124
ofri@2651
   125
			//Center right
ofri@2651
   126
		case GrowlCenterRightPosition:
ingmarstein@2655
   127
			result = NSMakeRect(screenFrame.origin.x + areaSize.width + areaSize.width,
ingmarstein@2655
   128
								screenFrame.origin.y + areaSize.height,
ingmarstein@2655
   129
								areaSize.width,
ingmarstein@2655
   130
								areaSize.height);
ingmarstein@2655
   131
			break;
ingmarstein@2655
   132
ofri@2651
   133
			//Bottom left
ofri@2651
   134
		case GrowlBottomLeftPosition:
ofri@2651
   135
			result = NSMakeRect(screenFrame.origin.x,
ofri@2651
   136
								screenFrame.origin.y,
ofri@2651
   137
								areaSize.width,
ofri@2651
   138
								areaSize.height);
ofri@2651
   139
			break;
ingmarstein@2655
   140
ofri@2651
   141
			//Bottom middle
ofri@2651
   142
		case GrowlBottomMiddlePosition:
ofri@2651
   143
			result = NSMakeRect(screenFrame.origin.x + areaSize.width,
ofri@2651
   144
								screenFrame.origin.y,
ofri@2651
   145
								areaSize.width,
ofri@2651
   146
								areaSize.height);
ofri@2651
   147
			break;
ingmarstein@2655
   148
ofri@2651
   149
			//Bottom right
ofri@2651
   150
		case GrowlBottomRightPosition:
ingmarstein@2655
   151
			result = NSMakeRect(screenFrame.origin.x + areaSize.width + areaSize.width,
ingmarstein@2655
   152
								screenFrame.origin.y,
ingmarstein@2655
   153
								areaSize.width,
ingmarstein@2655
   154
								areaSize.height);
ingmarstein@2655
   155
			break;
ingmarstein@2655
   156
ofri@2651
   157
			//Top row
ofri@2651
   158
		case GrowlTopRowPosition:
ofri@2651
   159
			result = NSMakeRect(screenFrame.origin.x,
ingmarstein@2655
   160
								screenFrame.origin.y + areaSize.height + areaSize.height,
ofri@2651
   161
								screenFrame.size.width,
ofri@2651
   162
								areaSize.height);
ofri@2651
   163
			break;
ingmarstein@2655
   164
ofri@2651
   165
			//Center row
ofri@2651
   166
		case GrowlCenterRowPosition:
ofri@2651
   167
			result = NSMakeRect(screenFrame.origin.x,
ofri@2651
   168
								screenFrame.origin.y + areaSize.height,
ofri@2651
   169
								screenFrame.size.width,
ofri@2651
   170
								areaSize.height);
ofri@2651
   171
			break;
ingmarstein@2655
   172
ofri@2651
   173
			//Bottom row
ofri@2651
   174
		case GrowlBottomRowPosition:
ofri@2651
   175
			result = NSMakeRect(screenFrame.origin.x,
ofri@2651
   176
								screenFrame.origin.y,
ofri@2651
   177
								screenFrame.size.width,
ofri@2651
   178
								areaSize.height);
ofri@2651
   179
			break;
ingmarstein@2655
   180
ofri@2651
   181
			//Left column
ofri@2651
   182
		case GrowlLeftColumnPosition:
ofri@2651
   183
			result = NSMakeRect(screenFrame.origin.x,
ofri@2651
   184
								screenFrame.origin.y,
ofri@2651
   185
								areaSize.width,
ofri@2651
   186
								screenFrame.size.height);
ofri@2651
   187
			break;
ingmarstein@2655
   188
ofri@2651
   189
			//Middle column
ofri@2651
   190
		case GrowlMiddleColumnPosition:
ofri@2651
   191
			result = NSMakeRect(screenFrame.origin.x + areaSize.width,
ofri@2651
   192
								screenFrame.origin.y,
ofri@2651
   193
								areaSize.width,
ofri@2651
   194
								screenFrame.size.height);
ofri@2651
   195
			break;
ingmarstein@2655
   196
ofri@2651
   197
			//Right column
ofri@2651
   198
		case GrowlRightColumnPosition:
ingmarstein@2655
   199
			result = NSMakeRect(screenFrame.origin.x + areaSize.width + areaSize.width,
ofri@2651
   200
								screenFrame.origin.y,
ofri@2651
   201
								areaSize.width,
ofri@2651
   202
								screenFrame.size.height);
ofri@2651
   203
			break;
ofri@2651
   204
	}
ingmarstein@2655
   205
ofri@2651
   206
	return result;
ofri@2651
   207
}
ingmarstein@2655
   208
jkp@2841
   209
- (BOOL) positionDisplay:(GrowlDisplayWindowController *)displayController {
ingmarstein@3117
   210
	GrowlLog *growlLog = [GrowlLog sharedController];
bgannin@3317
   211
	
bgannin@3317
   212
	GrowlApplicationTicket *displayTicket = [[GrowlTicketController sharedController] ticketForApplicationName:[[displayController notification] applicationName]];
bgannin@3317
   213
	selectedPositionType = [displayTicket positionType];
bgannin@3317
   214
	selectedCustomPosition = (enum GrowlPositionOrigin)[displayTicket selectedPosition];
boredzo@3101
   215
jkp@2841
   216
	NSScreen *preferredScreen = [displayController screen];
jkp@2841
   217
	NSRect screenFrame = [preferredScreen visibleFrame];
jkp@2841
   218
	NSSize displaySize = [[displayController window] frame].size;
jkp@2841
   219
	float padding = [displayController requiredDistanceFromExistingDisplays];
ingmarstein@2943
   220
jkp@2841
   221
	// Ask the display where it wants to be displayed in the first instance....
evands@3576
   222
	NSPoint idealOrigin;
evands@3576
   223
	NSRect idealFrame;
evands@3576
   224
evands@3583
   225
	enum GrowlExpansionDirection primaryDirection = [displayController primaryExpansionDirection];
evands@3583
   226
	enum GrowlExpansionDirection secondaryDirection = [displayController secondaryExpansionDirection];
evands@3583
   227
	
evands@3576
   228
	if ([reservedRectsByController objectForKey:[NSValue valueWithPointer:displayController]]) {
evands@3583
   229
		NSRect currentlyReservedRect = [[reservedRectsByController objectForKey:[NSValue valueWithPointer:displayController]] rectValue];
evands@3583
   230
		idealOrigin = currentlyReservedRect.origin;
evands@3583
   231
		
evands@3583
   232
		//The expansion direction determines which origins should be kept constant
evands@3583
   233
		switch (primaryDirection) {
evands@3583
   234
			case GrowlDownExpansionDirection:
evands@3583
   235
				idealOrigin.y += (currentlyReservedRect.size.height - displaySize.height);
evands@3583
   236
				break;
evands@3583
   237
			case GrowlUpExpansionDirection:
evands@3583
   238
				break;
evands@3583
   239
			case GrowlLeftExpansionDirection:
evands@3583
   240
				idealOrigin.x += (currentlyReservedRect.size.width - displaySize.width);
evands@3583
   241
				break;
evands@3583
   242
			case GrowlRightExpansionDirection:
evands@3583
   243
				break;
evands@3583
   244
			case GrowlNoExpansionDirection:
evands@3583
   245
				break;
evands@3583
   246
		}
evands@3583
   247
evands@3583
   248
		idealFrame = NSMakeRect(idealOrigin.x, idealOrigin.y,
evands@3583
   249
								displaySize.width, displaySize.height);
evands@3583
   250
evands@3576
   251
		if (!NSContainsRect(screenFrame,idealFrame)) {
evands@3576
   252
			idealOrigin = [displayController idealOriginInRect:screenFrame];
evands@3576
   253
			idealFrame = NSMakeRect(idealOrigin.x,idealOrigin.y,displaySize.width,displaySize.height);
evands@3576
   254
		}
evands@3576
   255
	} else {
evands@3576
   256
		idealOrigin = [displayController idealOriginInRect:screenFrame];
evands@3576
   257
		idealFrame = NSMakeRect(idealOrigin.x,idealOrigin.y,displaySize.width,displaySize.height);
evands@3576
   258
	}
evands@3576
   259
	
jkp@2841
   260
	// Try and reserve the rect
jkp@2841
   261
	NSRect displayFrame = idealFrame;
evands@3576
   262
	if ([self reserveRect:displayFrame inScreen:preferredScreen forDisplayController:displayController]) {
evands@3583
   263
		[[displayController window] setFrame:displayFrame display:YES animate:YES];		
jkp@2841
   264
		return YES;
jkp@2841
   265
	}
ingmarstein@2943
   266
evands@3528
   267
	// Something was blocking the display...try to find the next position for the display.
evands@3528
   268
evands@3526
   269
	[growlLog writeToLog:@"---"];
evands@3526
   270
	[growlLog writeToLog:@"positionDisplay: could not reserve initial rect; looking for another one"];
evands@3526
   271
	[growlLog writeToLog:@"primaryDirection: %@", NSStringFromGrowlExpansionDirection(primaryDirection)];
evands@3526
   272
	[growlLog writeToLog:@"secondaryDirection: %@", NSStringFromGrowlExpansionDirection(secondaryDirection)];
evands@3526
   273
	
evands@3528
   274
	int			numberOfRects;
evands@3576
   275
	NSRectArray usedRects = [self copyRectsInSet:[self reservedRectsForScreen:preferredScreen] count:&numberOfRects padding:padding excludingDisplayController:displayController];
evands@3528
   276
evands@3528
   277
	/* This will loop until the display is placed or we run off the screen entirely
evands@3528
   278
	 * A more 'efficient' implementation might sort all of the usedRects, then look at them iteratively.  I (evands) found it to be
evands@3528
   279
	 * thoroughly nontrivial to do such a sort in a robust fashion. While the below code does loop more than an 'efficient' search,
evands@3528
   280
	 * it ends up being a whole bunch of simple float comparisons in the worst case, which any modern computer can handle with ease. Let's
evands@3528
   281
	 * not over-optimize unless this is an actual bottleneck. :)
evands@3528
   282
	 */
evands@3528
   283
	while (1) {
evands@3528
   284
		BOOL haveBestSecondaryOrigin = NO;
evands@3528
   285
		float bestSecondaryOrigin = 0;
evands@3528
   286
evands@3528
   287
		while (NSContainsRect(screenFrame,displayFrame)) {
evands@3528
   288
			//Adjust in our primary direction
evands@3528
   289
			switch (primaryDirection) {
evands@3528
   290
				case GrowlDownExpansionDirection:
evands@3528
   291
					displayFrame.origin.y -= 1;
evands@3528
   292
					break;
evands@3528
   293
				case GrowlUpExpansionDirection:
evands@3528
   294
					displayFrame.origin.y += 1;
evands@3528
   295
					break;
evands@3528
   296
				case GrowlLeftExpansionDirection:
evands@3528
   297
					displayFrame.origin.x -= 1;
evands@3528
   298
					break;
evands@3528
   299
				case GrowlRightExpansionDirection:
evands@3528
   300
					displayFrame.origin.x += 1;
evands@3528
   301
					break;
evands@3528
   302
				case GrowlNoExpansionDirection:
evands@3528
   303
					NSLog(@"This should never happen");
evands@3528
   304
					free(usedRects);
evands@3528
   305
					return NO;
evands@3528
   306
					break;
evands@3528
   307
			}
evands@3528
   308
			
evands@3528
   309
			BOOL intersects = NO;
evands@3528
   310
			//Check to see if the proposed displayFrame intersects with any used rect
evands@3528
   311
			for (int i = 0; i < numberOfRects; i++) {
evands@3528
   312
				if (NSIntersectsRect(displayFrame, usedRects[i])) {
evands@3528
   313
					//We intersected. Sadness.
evands@3528
   314
					intersects = YES;
evands@3528
   315
					
evands@3528
   316
					/* Determine, based on this intersection, how far we should shift if we end up moving in
evands@3528
   317
					 * our secondary direction.
evands@3528
   318
					 */
evands@3528
   319
					switch (secondaryDirection) {
evands@3528
   320
						case GrowlDownExpansionDirection:
evands@3528
   321
						{
evands@3528
   322
							if (!haveBestSecondaryOrigin ||
evands@3528
   323
								NSMinY(usedRects[i]) > bestSecondaryOrigin) {
evands@3528
   324
								haveBestSecondaryOrigin = YES;
evands@3528
   325
								bestSecondaryOrigin = NSMinY(usedRects[i]) - NSHeight(displayFrame);
evands@3528
   326
							}
evands@3528
   327
							break;
evands@3528
   328
						}
evands@3528
   329
						case GrowlUpExpansionDirection:
evands@3528
   330
						{
evands@3528
   331
							if (!haveBestSecondaryOrigin ||
evands@3528
   332
								NSMaxY(usedRects[i]) < bestSecondaryOrigin) {
evands@3528
   333
								haveBestSecondaryOrigin = YES;
evands@3528
   334
								bestSecondaryOrigin = NSMaxY(usedRects[i]);
evands@3528
   335
							}
evands@3528
   336
							break;
evands@3528
   337
						}
evands@3528
   338
						case GrowlLeftExpansionDirection:
evands@3528
   339
						{
evands@3528
   340
							if (!haveBestSecondaryOrigin ||
evands@3528
   341
								NSMinX(usedRects[i]) < bestSecondaryOrigin) {
evands@3528
   342
								haveBestSecondaryOrigin = YES;
evands@3528
   343
								bestSecondaryOrigin = NSMinX(usedRects[i]) - NSWidth(displayFrame);
evands@3528
   344
							}
evands@3528
   345
							break;
evands@3528
   346
						}
evands@3528
   347
						case GrowlRightExpansionDirection:
evands@3528
   348
						{
evands@3528
   349
							if (!haveBestSecondaryOrigin ||
evands@3528
   350
								NSMaxX(usedRects[i]) < bestSecondaryOrigin) {
evands@3528
   351
								haveBestSecondaryOrigin = YES;
evands@3528
   352
								bestSecondaryOrigin = NSMaxX(usedRects[i]);
evands@3528
   353
							}
evands@3528
   354
							break;
evands@3528
   355
						}
evands@3528
   356
						case GrowlNoExpansionDirection:
evands@3528
   357
							NSLog(@"This should never happen");
evands@3528
   358
							free(usedRects);
evands@3528
   359
							return NO;
evands@3528
   360
							break;
ingmarstein@3018
   361
					}
ingmarstein@3018
   362
				}
evands@3528
   363
			}
evands@3528
   364
			
evands@3528
   365
			if (!intersects) break;
jkp@2841
   366
		}
ingmarstein@2943
   367
evands@3530
   368
		if (NSContainsRect(screenFrame,displayFrame)) {
evands@3530
   369
			//The rect is on the screen! Try to reserve it.
evands@3576
   370
			if ([self reserveRect:displayFrame inScreen:preferredScreen forDisplayController:displayController]) {
evands@3583
   371
				[[displayController window] setFrame:displayFrame display:YES animate:YES];		
evands@3530
   372
				free(usedRects);
evands@3525
   373
				return YES;
evands@3525
   374
			}
evands@3530
   375
		}
evands@3530
   376
		// If we've run offscreen or couldn't reserve that rect, use the secondary direction after resetting from our previous efforts
evands@3530
   377
		switch (primaryDirection) {
evands@3530
   378
			case GrowlDownExpansionDirection:
evands@3530
   379
			case GrowlUpExpansionDirection:
evands@3530
   380
				displayFrame.origin.y = idealFrame.origin.y;
evands@3530
   381
				break;
evands@3530
   382
			case GrowlLeftExpansionDirection:
evands@3530
   383
			case GrowlRightExpansionDirection:
evands@3530
   384
				displayFrame.origin.x = idealFrame.origin.x;
evands@3530
   385
				break;
evands@3530
   386
			case GrowlNoExpansionDirection:
evands@3530
   387
				NSLog(@"This should never happen");
evands@3530
   388
				free(usedRects);
evands@3530
   389
				return NO;
evands@3530
   390
				break;
evands@3530
   391
		}
evands@3530
   392
		
evands@3530
   393
		switch (secondaryDirection) {
evands@3530
   394
			case GrowlDownExpansionDirection:
evands@3530
   395
			case GrowlUpExpansionDirection:
evands@3530
   396
				displayFrame.origin.y = bestSecondaryOrigin;
evands@3530
   397
				break;
evands@3530
   398
			case GrowlLeftExpansionDirection:
evands@3530
   399
			case GrowlRightExpansionDirection:
evands@3530
   400
				displayFrame.origin.x = bestSecondaryOrigin;
evands@3530
   401
				break;
evands@3530
   402
			case GrowlNoExpansionDirection:
evands@3530
   403
				NSLog(@"This should never happen");
evands@3530
   404
				free(usedRects);
evands@3530
   405
				return NO;
evands@3530
   406
				break;
evands@3530
   407
		}
evands@3530
   408
		
evands@3530
   409
		if (!NSContainsRect(screenFrame,displayFrame)) {
evands@3530
   410
			NSLog(@"Could not display Growl notification; no screen space available.");
evands@3530
   411
			break;
jkp@2841
   412
		}
jkp@2841
   413
	}
evands@3528
   414
	
evands@3528
   415
	free(usedRects);
evands@3528
   416
jkp@2841
   417
	return NO;
jkp@2841
   418
}
ofri@2651
   419
ofri@2651
   420
//Reserve a rect in a specific screen.
evands@3576
   421
- (BOOL) reserveRect:(NSRect)inRect inScreen:(NSScreen *)inScreen forDisplayController:(GrowlDisplayWindowController *)displayController {
ingmarstein@3018
   422
	BOOL result = YES;
evands@3576
   423
	NSValue *displayControllerValue = (displayController ? [NSValue valueWithPointer:displayController] : nil);
ingmarstein@2721
   424
evands@3580
   425
	//Treat nil as the main screen
evands@3580
   426
	if (!inScreen) inScreen = [NSScreen mainScreen];
evands@3580
   427
evands@3580
   428
	NSMutableSet	*reservedRectsOfScreen = [self reservedRectsForScreen:inScreen];
evands@3580
   429
	NSValue			*newRectValue = [NSValue valueWithRect:inRect];
evands@3580
   430
	NSEnumerator	*rectValuesEnumerator;
evands@3580
   431
	NSValue			*value;
evands@3580
   432
	
evands@3580
   433
	//Make sure the rect is not already reserved. However, if it is reserved by displayController, that's fine (it is just rerequesting its current space).
evands@3580
   434
	if ([reservedRectsOfScreen member:newRectValue] &&
evands@3580
   435
		(!displayController || (![[reservedRectsByController objectForKey:displayControllerValue] isEqual:newRectValue]))) {
evands@3580
   436
		result = NO;
evands@3580
   437
	} else {
evands@3580
   438
		rectValuesEnumerator = [reservedRectsOfScreen objectEnumerator];
evands@3580
   439
		
evands@3580
   440
		// Loop through all the values in reservedRects and make sure
evands@3580
   441
		// that the new rect does not intersect with any of the already
evands@3580
   442
		// reserved rects, excepting if the displayController itself is reserving the rect.
evands@3580
   443
		while ((value = [rectValuesEnumerator nextObject])) {	
evands@3580
   444
			if ((NSIntersectsRect(inRect, [value rectValue])) && 
evands@3580
   445
				(!displayController || (![[reservedRectsByController objectForKey:displayControllerValue] isEqual:value]))) {
evands@3580
   446
				result = NO;
evands@3580
   447
				break;
ofri@2651
   448
			}
ofri@2651
   449
		}
evands@3580
   450
	}
evands@3580
   451
	
evands@3580
   452
	// Add the new rect if it passed the intersection test
evands@3580
   453
	if (result) {
evands@3580
   454
		[self clearReservedRectForDisplayController:displayController];
evands@3580
   455
		[reservedRectsByController setObject:[NSValue valueWithRect:inRect]
evands@3580
   456
									  forKey:displayControllerValue];
evands@3580
   457
		[reservedRectsOfScreen addObject:newRectValue];
ofri@2651
   458
	}
evands@3526
   459
evands@3516
   460
	return result;
evands@3516
   461
}
evands@3516
   462
Peter@4208
   463
- (BOOL) reserveRect:(NSRect)inRect forDisplayController:(GrowlDisplayWindowController *)displayController {
Peter@4208
   464
	return [self reserveRect:inRect inScreen:[displayController screen] forDisplayController:displayController];
Peter@4208
   465
}
ofri@2651
   466
evands@3516
   467
- (void) clearReservedRectForDisplayController:(GrowlDisplayWindowController *)displayController
evands@3516
   468
{
evands@3528
   469
	NSValue *controllerKey = [NSValue valueWithPointer:displayController];
Peter@4208
   470
	NSMutableSet *reservedRectsOfScreen = [self reservedRectsForScreen:[displayController screen]];
evands@3528
   471
	NSValue *value = [reservedRectsByController objectForKey:controllerKey];
evands@3528
   472
evands@3528
   473
	if (value) {
evands@3528
   474
		[reservedRectsOfScreen removeObject:value];
evands@3528
   475
		[reservedRectsByController removeObjectForKey:controllerKey];
evands@3528
   476
	}
evands@3528
   477
}
evands@3528
   478
evands@3528
   479
/*!
evands@3528
   480
 * @method copyRectsInSet:count:padding
evands@3528
   481
 * @brief Returns a malloc'd array of NSRect structs which were contained as values in rectSet
evands@3528
   482
 *
evands@3528
   483
 * @param rectSet An NSSet which must contain only NSValues representing rects via -[NSValue rectValue]
evands@3528
   484
 * @param outCount If non-NULL, on return will have the number of rects in the returned array
evands@3528
   485
 * @param padding Padding to add to each returned rect in the rect array
evands@3576
   486
 * @param displayController A display controller whose rect(s) should not be included. Pass nil to include all rects.
evands@3528
   487
 * @result A malloc'd NSRectArray. This value should be freed after use.
evands@3528
   488
 */
evands@3576
   489
- (NSRectArray)copyRectsInSet:(NSSet *)rectSet count:(int *)outCount padding:(float)padding excludingDisplayController:(GrowlDisplayWindowController *)displayController
evands@3528
   490
{
evands@3528
   491
	NSEnumerator *enumerator = [rectSet objectEnumerator];
evands@3528
   492
	NSValue		 *value;
evands@3576
   493
	NSValue		 *displayControllerValue = [NSValue valueWithPointer:displayController];
evands@3576
   494
	int			  count = [rectSet count];
evands@3528
   495
	
evands@3528
   496
	if (outCount) *outCount = count;
evands@3528
   497
evands@3528
   498
	NSRectArray gridRects = (NSRectArray)malloc(sizeof(NSRect) * count);
evands@3528
   499
	int i = 0;
evands@3528
   500
	while ((value = [enumerator nextObject])) {
evands@3576
   501
		if (!displayController || (![[reservedRectsByController objectForKey:displayControllerValue] isEqual:value])) {
evands@3576
   502
			gridRects[i++] = NSInsetRect([value rectValue], -padding, -padding);
evands@3576
   503
		}
evands@3528
   504
	}
evands@3528
   505
	
evands@3528
   506
	return gridRects;
evands@3528
   507
}
evands@3528
   508
evands@3528
   509
//Returns the set of reserved rect for a specific screen. The return value *is* the storage!
ofri@2666
   510
- (NSMutableSet *)reservedRectsForScreen:(NSScreen *)screen {
ofri@2666
   511
	NSMutableSet *result = nil;
ingmarstein@2721
   512
ofri@2666
   513
	//Treat nil as the main screen
ofri@2666
   514
	if (!screen)
ofri@2666
   515
		screen = [NSScreen mainScreen];
ingmarstein@2721
   516
ofri@2666
   517
	//Get the set of reserved rects for our screen
ofri@2666
   518
	result = (NSMutableSet *)CFDictionaryGetValue(reservedRects, screen);
ingmarstein@2943
   519
ofri@2666
   520
	//Make sure the set exists. If not, create it.
ofri@2666
   521
	if (!result) {
evands@3525
   522
		result = [[NSMutableSet alloc] init];
evands@3525
   523
		CFDictionarySetValue(reservedRects, screen, result);
evands@3525
   524
		[result release];
ofri@2666
   525
	}
ingmarstein@2721
   526
ofri@2666
   527
	return result;
ofri@2666
   528
}
ofri@2666
   529
bgannin@3317
   530
- (enum GrowlPosition) originPosition {
ingmarstein@3401
   531
	if (selectedPositionType == 1) {
bgannin@3317
   532
		enum GrowlPosition translatedPosition;
ingmarstein@3401
   533
		switch (selectedCustomPosition) {
ingmarstein@3401
   534
			default:
bgannin@3317
   535
			case GrowlNoOrigin:
bgannin@3317
   536
				//Default to middle of the screen if no origin is set, though this case shouldn't be hit.
bgannin@3317
   537
				translatedPosition = GrowlMiddleColumnPosition;
bgannin@3317
   538
				break;
bgannin@3317
   539
			case GrowlTopLeftCorner:
bgannin@3317
   540
				translatedPosition = GrowlTopLeftPosition;
bgannin@3317
   541
				break;
bgannin@3317
   542
			case GrowlBottomRightCorner:
bgannin@3317
   543
				translatedPosition = GrowlBottomRightPosition;
bgannin@3317
   544
				break;
bgannin@3317
   545
			case GrowlTopRightCorner:
bgannin@3317
   546
				translatedPosition = GrowlTopRightPosition;
bgannin@3317
   547
				break;
bgannin@3317
   548
			case GrowlBottomLeftCorner:
bgannin@3317
   549
				translatedPosition = GrowlBottomLeftPosition;
bgannin@3317
   550
				break;
bgannin@3317
   551
		}		
bgannin@3317
   552
		return translatedPosition;
bgannin@3317
   553
	}
bgannin@3317
   554
	return [GrowlPositionController selectedOriginPosition];
bgannin@3317
   555
}
bgannin@3317
   556
ofri@2651
   557
@end
boredzo@3091
   558
boredzo@3091
   559
NSString *NSStringFromGrowlPosition(enum GrowlPosition pos) {
boredzo@3091
   560
	NSString *str = nil;
boredzo@3091
   561
boredzo@3091
   562
	NSString *first;
boredzo@3091
   563
	switch (pos) {
boredzo@3091
   564
		case GrowlTopLeftPosition:
boredzo@3091
   565
		case GrowlTopMiddlePosition:
boredzo@3091
   566
		case GrowlTopRightPosition:
boredzo@3091
   567
		case GrowlTopRowPosition:
boredzo@3091
   568
			first = @"top";
boredzo@3091
   569
			break;
boredzo@3091
   570
boredzo@3091
   571
		case GrowlCenterLeftPosition:
boredzo@3091
   572
		case GrowlCenterMiddlePosition:
boredzo@3091
   573
		case GrowlCenterRightPosition:
boredzo@3091
   574
		case GrowlCenterRowPosition:
boredzo@3091
   575
			first = @"center";
boredzo@3091
   576
			break;
boredzo@3091
   577
boredzo@3091
   578
		case GrowlBottomLeftPosition:
boredzo@3091
   579
		case GrowlBottomMiddlePosition:
boredzo@3091
   580
		case GrowlBottomRightPosition:
boredzo@3091
   581
		case GrowlBottomRowPosition:
boredzo@3091
   582
			first = @"bottom";
boredzo@3091
   583
			break;
boredzo@3091
   584
boredzo@3091
   585
		case GrowlLeftColumnPosition:
boredzo@3091
   586
			first = @"left";
boredzo@3091
   587
			break;
boredzo@3091
   588
boredzo@3091
   589
		case GrowlMiddleColumnPosition:
boredzo@3091
   590
			first = @"middle";
boredzo@3091
   591
			break;
boredzo@3091
   592
boredzo@3091
   593
		case GrowlRightColumnPosition:
boredzo@3091
   594
			first = @"right";
boredzo@3091
   595
			break;
boredzo@3091
   596
boredzo@3091
   597
		default:
boredzo@3091
   598
			first = nil;
boredzo@3091
   599
	};
boredzo@3091
   600
boredzo@3091
   601
	NSString *second;
boredzo@3091
   602
	switch (pos) {
boredzo@3091
   603
		case GrowlTopLeftPosition:
boredzo@3091
   604
		case GrowlCenterLeftPosition:
boredzo@3091
   605
		case GrowlBottomLeftPosition:
boredzo@3091
   606
			second = @"left";
boredzo@3091
   607
			break;
boredzo@3091
   608
boredzo@3091
   609
		case GrowlTopMiddlePosition:
boredzo@3091
   610
		case GrowlBottomMiddlePosition:
boredzo@3091
   611
			second = @"center";
boredzo@3091
   612
			break;
boredzo@3091
   613
boredzo@3091
   614
		case GrowlCenterMiddlePosition:
boredzo@3091
   615
			//just say 'center'
boredzo@3091
   616
			second = @"";
boredzo@3091
   617
			break;
boredzo@3091
   618
boredzo@3091
   619
		case GrowlTopRightPosition:
boredzo@3091
   620
		case GrowlCenterRightPosition:
boredzo@3091
   621
		case GrowlBottomRightPosition:
boredzo@3091
   622
			second = @"right";
boredzo@3091
   623
			break;
boredzo@3091
   624
boredzo@3091
   625
		case GrowlTopRowPosition:
boredzo@3091
   626
		case GrowlCenterRowPosition:
boredzo@3091
   627
		case GrowlBottomRowPosition:
boredzo@3091
   628
			second = @"row";
boredzo@3091
   629
			break;
boredzo@3091
   630
boredzo@3091
   631
		case GrowlLeftColumnPosition:
boredzo@3091
   632
		case GrowlMiddleColumnPosition:
boredzo@3091
   633
		case GrowlRightColumnPosition:
boredzo@3091
   634
			second = @"column";
boredzo@3091
   635
boredzo@3091
   636
		default:
boredzo@3091
   637
			second = nil;
boredzo@3091
   638
	};
boredzo@3091
   639
boredzo@3091
   640
	if (first && second) {
boredzo@3091
   641
		unsigned  firstLength = [first  length];
boredzo@3091
   642
		unsigned secondLength = [second length];
boredzo@3091
   643
boredzo@3091
   644
		if (firstLength && secondLength) {
boredzo@3091
   645
			unsigned capacity = firstLength + secondLength + 1U;
boredzo@3091
   646
			NSMutableString *mutable = [[NSMutableString alloc] initWithCapacity:capacity];
boredzo@3091
   647
boredzo@3091
   648
			[mutable appendString:first];
boredzo@3091
   649
			[mutable appendCharacter:'-'];
boredzo@3091
   650
			[mutable appendString:second];
boredzo@3091
   651
boredzo@3091
   652
			str = [mutable autorelease];
boredzo@3091
   653
		} else if (firstLength || secondLength) {
boredzo@3091
   654
			str = firstLength ? first : second;
boredzo@3091
   655
		}
boredzo@3091
   656
	}
boredzo@3091
   657
boredzo@3091
   658
	return str;
boredzo@3091
   659
}	
bgannin@3266
   660
boredzo@3091
   661
NSString *NSStringFromGrowlExpansionDirection(enum GrowlExpansionDirection dir) {
boredzo@3091
   662
	switch (dir) {
boredzo@3091
   663
		case GrowlNoExpansionDirection:
boredzo@3091
   664
			return @"nowhere";
boredzo@3091
   665
		case GrowlDownExpansionDirection:
boredzo@3091
   666
			return @"down";
boredzo@3091
   667
		case GrowlUpExpansionDirection:
boredzo@3091
   668
			return @"up";
boredzo@3091
   669
		case GrowlLeftExpansionDirection:
boredzo@3091
   670
			return @"left";
boredzo@3091
   671
		case GrowlRightExpansionDirection:
boredzo@3091
   672
			return @"right";
boredzo@3091
   673
		default:
boredzo@3091
   674
			return nil;
boredzo@3091
   675
	};
boredzo@3091
   676
}