|
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";
|
|
Rudy@4227
|
635 |
break;
|
|
Rudy@4227
|
636 |
|
|
boredzo@3091
|
637 |
default:
|
|
boredzo@3091
|
638 |
second = nil;
|
|
boredzo@3091
|
639 |
};
|
|
boredzo@3091
|
640 |
|
|
boredzo@3091
|
641 |
if (first && second) {
|
|
boredzo@3091
|
642 |
unsigned firstLength = [first length];
|
|
boredzo@3091
|
643 |
unsigned secondLength = [second length];
|
|
boredzo@3091
|
644 |
|
|
boredzo@3091
|
645 |
if (firstLength && secondLength) {
|
|
boredzo@3091
|
646 |
unsigned capacity = firstLength + secondLength + 1U;
|
|
boredzo@3091
|
647 |
NSMutableString *mutable = [[NSMutableString alloc] initWithCapacity:capacity];
|
|
boredzo@3091
|
648 |
|
|
boredzo@3091
|
649 |
[mutable appendString:first];
|
|
boredzo@3091
|
650 |
[mutable appendCharacter:'-'];
|
|
boredzo@3091
|
651 |
[mutable appendString:second];
|
|
boredzo@3091
|
652 |
|
|
boredzo@3091
|
653 |
str = [mutable autorelease];
|
|
boredzo@3091
|
654 |
} else if (firstLength || secondLength) {
|
|
boredzo@3091
|
655 |
str = firstLength ? first : second;
|
|
boredzo@3091
|
656 |
}
|
|
boredzo@3091
|
657 |
}
|
|
boredzo@3091
|
658 |
|
|
boredzo@3091
|
659 |
return str;
|
|
boredzo@3091
|
660 |
}
|
|
bgannin@3266
|
661 |
|
|
boredzo@3091
|
662 |
NSString *NSStringFromGrowlExpansionDirection(enum GrowlExpansionDirection dir) {
|
|
boredzo@3091
|
663 |
switch (dir) {
|
|
boredzo@3091
|
664 |
case GrowlNoExpansionDirection:
|
|
boredzo@3091
|
665 |
return @"nowhere";
|
|
boredzo@3091
|
666 |
case GrowlDownExpansionDirection:
|
|
boredzo@3091
|
667 |
return @"down";
|
|
boredzo@3091
|
668 |
case GrowlUpExpansionDirection:
|
|
boredzo@3091
|
669 |
return @"up";
|
|
boredzo@3091
|
670 |
case GrowlLeftExpansionDirection:
|
|
boredzo@3091
|
671 |
return @"left";
|
|
boredzo@3091
|
672 |
case GrowlRightExpansionDirection:
|
|
boredzo@3091
|
673 |
return @"right";
|
|
boredzo@3091
|
674 |
default:
|
|
boredzo@3091
|
675 |
return nil;
|
|
boredzo@3091
|
676 |
};
|
|
boredzo@3091
|
677 |
}
|