5 // Created by Peter Hosey on 2006-06-21.
6 // Copyright 2006 Peter Hosey. All rights reserved.
9 #import "CPUUsageMonitor.h"
11 #import "CPUUsageView.h"
12 #import "NSString+Percentage.h"
14 #import "BZGridEnumerator.h"
16 #include <sys/types.h>
19 //sysctl and its parameters
20 #include <sys/sysctl.h>
22 #include <sys/errno.h>
25 #define DOCK_ICON_SIZE 128.0f
27 @interface CPUUsageMonitor (PRIVATE)
29 - (void)layOutCellsInWindow:(NSWindow *)thisWindow;
33 @implementation CPUUsageMonitor
36 [self exposeBinding:@"backgroundColor"];
37 [self exposeBinding:@"cellWidth"];
38 [self exposeBinding:@"cellHeight"];
39 [self exposeBinding:@"shouldDrawToDockIcon"];
40 [self exposeBinding:@"shouldDrawToWindow"];
44 if((self = [super init])) {
45 NSNumber *thirtyTwo = [NSNumber numberWithFloat:32.0f];
46 NSDictionary *defaultPrefs = [NSDictionary dictionaryWithObjectsAndKeys:
47 [NSArchiver archivedDataWithRootObject:[NSColor whiteColor]], @"Floater text color",
48 [NSNumber numberWithFloat:0.0f], @"Floater text opacity",
49 [NSArchiver archivedDataWithRootObject:[NSColor whiteColor]], @"Dock icon text color",
50 [NSNumber numberWithFloat:1.0f], @"Dock icon text opacity",
51 [NSArchiver archivedDataWithRootObject:[NSColor redColor]], @"Background color",
52 thirtyTwo, @"Cell width",
53 thirtyTwo, @"Cell height",
54 [NSNumber numberWithBool:YES], @"Draw CPU usage to window",
55 [NSNumber numberWithInt:CPUUsageOrientationVertical], @"Window orientation",
56 [NSNumber numberWithBool:NO], @"Draw CPU usage to Dock icon",
59 [[NSUserDefaults standardUserDefaults] registerDefaults:defaultPrefs];
64 #pragma mark NSApplication delegate conformance
66 - (void)applicationDidFinishLaunching:(NSNotification *)notification {
67 //Set up us the properties of new cells.
68 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
69 NSData *data = [defaults dataForKey:@"Background color"];
70 NSColor *color = data ? [NSUnarchiver unarchiveObjectWithData:data] : [NSColor redColor];
71 [self setBackgroundColor:color];
73 [self setCellWidth:[defaults floatForKey:@"Cell width"]];
74 [self setCellHeight:[defaults floatForKey:@"Cell height"]];
76 //Sign up for notifications of pref changes.
77 NSUserDefaultsController *udc = [NSUserDefaultsController sharedUserDefaultsController];
78 NSDictionary *colorBindingOptions = [NSDictionary dictionaryWithObject:@"NSUnarchiveFromData" forKey:NSValueTransformerNameBindingOption];
79 [self bind:@"backgroundColor" toObject:udc withKeyPath:@"values.Background color" options:colorBindingOptions];
80 [self bind:@"cellWidth" toObject:udc withKeyPath:@"values.Cell width" options:nil];
81 [self bind:@"cellHeight" toObject:udc withKeyPath:@"values.Cell height" options:nil];
82 [self bind:@"shouldDrawToDockIcon" toObject:udc withKeyPath:@"values.Draw CPU usage to Dock icon" options:nil];
83 [self bind:@"shouldDrawToWindow" toObject:udc withKeyPath:@"values.Draw CPU usage to window" options:nil];
85 //Fetch the orientation from prefs.
86 orientation = [defaults integerForKey:@"Window orientation"];
88 //Set up the orientation buttons.
89 int buttonStates[2] = { NSOffState, NSOnState };
90 [horizontalButton setState:buttonStates[(orientation == CPUUsageOrientationHorizontal )]];
91 [verticalButton setState:buttonStates[(orientation == CPUUsageOrientationVertical )]];
92 [rectangularButton setState:buttonStates[(orientation == CPUUsageOrientationRectangular)]];
94 //We could get the number of processors the same way that we get the CPU usage info, but that allocates memory.
96 int mib[miblen] = { CTL_HW, HW_NCPU };
97 size_t sizeOfNumCPUs = sizeof(numCPUs);
98 int status = sysctl(mib, miblen,
99 &numCPUs, &sizeOfNumCPUs,
100 /*newp*/ NULL, /*newlen*/ 0U);
102 numCPUs = 1; //XXX Should probably error out instead…
104 //Set up our display destinations.
105 //First, the Dock icon window. We'll copy the content view into an image, which we set as the application icon.
106 NSRect dockIconRect = { NSZeroPoint, { DOCK_ICON_SIZE, DOCK_ICON_SIZE } };
107 dockIconWindow = [[NSPanel alloc] initWithContentRect:dockIconRect
108 styleMask:NSBorderlessWindowMask
109 backing:NSBackingStoreBuffered
111 [dockIconWindow setTitle:@"CPU Usage hidden Dock-icon window"];
112 [dockIconWindow setOpaque:NO];
113 [dockIconWindow setAcceptsMouseMovedEvents:NO];
114 [dockIconWindow setMovableByWindowBackground:YES];
115 [dockIconWindow setHasShadow:NO];
116 [dockIconWindow setIgnoresMouseEvents:YES];
117 [dockIconWindow setReleasedWhenClosed:YES];
118 [dockIconWindow setOneShot:NO];
119 [dockIconWindow setBackgroundColor:[NSColor clearColor]];
120 //We need to order the window in so that the views can draw. But we only want the user to see the Dock icon, not the window. So we set the alpha to 0.
121 [dockIconWindow setAlphaValue:0.0f];
122 [dockIconWindow orderBack:nil];
124 dockIconUsageViewsContainingView = [dockIconWindow contentView];
126 dockIconUsageViews = [[NSMutableArray alloc] initWithCapacity:numCPUs];
127 [self layOutCellsInWindow:dockIconWindow];
129 //Second, create the floater.
130 float numCPUsFloat = numCPUs;
131 float numViewsX, numViewsY;
132 if(orientation == CPUUsageOrientationHorizontal) {
133 numViewsX = numCPUsFloat;
135 } else if(orientation == CPUUsageOrientationVertical) {
137 numViewsY = numCPUsFloat;
139 numViewsX = ceilf(sqrtf(numCPUsFloat));
140 numViewsY = ceilf(numCPUsFloat / numViewsX);
142 float numCellsMissing = ((numViewsX * numViewsY) - numCPUsFloat);
144 //Here, these enumerators are used only to compute the frame of the window. No actual enumeration is done.
145 BZGridEnumerator *floaterOddCellsEnum = nil;
146 BZGridEnumerator *floaterCellsEnum = [[BZGridEnumerator alloc] initWithCellSize:(NSSize){ cellWidth, cellHeight }
147 numberOfColumns:numViewsX
148 numberOfRows:numViewsY];
149 NSRect frame = [floaterCellsEnum overallRect];
150 if(numCellsMissing) {
151 float numOddCells = numCellsMissing ? numViewsX - numCellsMissing : 0.0f;
152 /*oddCellsWidth is the fraction of the total frame width used by odd cells.
154 *numEmptySpaces = total - numCPUs
155 *numOddCPUs = X - numEmptySpaces
156 *oddCellsWidth = numOddCPUs / X
158 float oddCellsWidth = numOddCells / numViewsX;
159 float oddCellWidth = oddCellsWidth / numOddCells;
161 floaterOddCellsEnum = [[BZGridEnumerator alloc] initWithCellSize:(NSSize){ frame.size.width * oddCellWidth, cellHeight }
162 offset:(NSPoint){ 0.0f, cellHeight * numViewsY }
163 numberOfColumns:numOddCells
165 frame.size.height += [floaterOddCellsEnum overallRect].size.height;
168 window = [[NSWindow alloc] initWithContentRect:frame
169 styleMask:NSBorderlessWindowMask
170 backing:NSBackingStoreBuffered
172 [window setTitle:@"CPU Usage"];
173 [window setOpaque:NO];
174 [window setAcceptsMouseMovedEvents:NO];
175 [window setMovableByWindowBackground:YES];
176 [window setLevel:kCGDesktopWindowLevel + 2];
177 [window setHasShadow:NO];
178 [window setIgnoresMouseEvents:YES];
179 [window setReleasedWhenClosed:YES];
180 if(orientation != CPUUsageOrientationRectangular) {
181 BOOL isHorizontal = (orientation == CPUUsageOrientationHorizontal);
182 [window setResizeIncrements:(NSSize){ numCPUs * isHorizontal, numCPUs * !isHorizontal }];
184 [window setBackgroundColor:[NSColor clearColor]];
186 usageViews = [[NSMutableArray alloc] initWithCapacity:numCPUs];
187 [self layOutCellsInWindow:window];
189 [window setFrameAutosaveName:@"CPU usage window"];
190 if(![window setFrameUsingName:@"CPU usage window"])
193 frame.origin = [window frame].origin;
194 [window setFrame:frame display:NO];
196 [preferencesPanel center];
198 CPUUsageLock = [[NSLock alloc] init];
199 CPUUsage = NSZoneMalloc([self zone], numCPUs * sizeof(float));
201 updateTimer = [[NSTimer scheduledTimerWithTimeInterval:0.5
203 selector:@selector(updateCPUUsageButNotViews:)
205 repeats:YES] retain];
208 //Launch the threaded timers that sweep our CPU usage array looking for views.
209 for(unsigned i = 0U; i < numCPUs; ++i) {
210 [NSThread detachNewThreadSelector:@selector(threadedLaunchTimer:)
212 withObject:[NSNumber numberWithUnsignedInt:i]];
215 if(shouldDrawToWindow)
216 [window orderFront:nil];
219 - (void)applicationWillTerminate:(NSNotification *)notification {
220 //First, fade out the window.
221 NSDictionary *animDict = [NSDictionary dictionaryWithObjectsAndKeys:
222 window, NSViewAnimationTargetKey,
223 NSViewAnimationFadeOutEffect, NSViewAnimationEffectKey,
225 NSViewAnimation *anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:animDict]];
226 [anim setDuration:0.5];
227 [anim setAnimationBlockingMode:NSAnimationNonblockingThreaded];
229 //Tell the threads to stop, then wait for them to comply.
230 // threadsShouldExit = YES;
231 threadsRemainingToDie = [usageViews count];
232 [anim startAnimation];
234 unsigned sleepCount = 100U; //Wait no more than this many seconds.
235 while(threadsRemainingToDie && sleepCount--)
236 [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
238 //Wait for the animation to run out.
239 while([anim isAnimating])
240 [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
242 [updateTimer invalidate];
243 [updateTimer release];
245 [usageViews release];
247 [backgroundColor release];
251 NSZoneFree([self zone], CPUUsage);
253 [NSApp setApplicationIconImage:[NSImage imageNamed:@"CPUUsageIcon"]];
256 - (void)applicationDidBecomeActive:(NSNotification *)notification {
257 NSEnumerator *usageViewsEnum = [usageViews objectEnumerator];
258 CPUUsageView *usageView;
259 while((usageView = [usageViewsEnum nextObject])) {
260 [usageView setDrawsFrame:YES];
261 [usageView setNeedsDisplay:YES];
264 [window setIgnoresMouseEvents:NO];
266 - (void)applicationWillResignActive:(NSNotification *)notification {
267 NSEnumerator *usageViewsEnum = [usageViews objectEnumerator];
268 CPUUsageView *usageView;
269 while((usageView = [usageViewsEnum nextObject])) {
270 [usageView setDrawsFrame:NO];
271 [usageView setNeedsDisplay:YES];
274 [window setIgnoresMouseEvents:YES];
279 - (void)drawToDockIcon {
280 NSRect dockIconRect = { NSZeroPoint, { DOCK_ICON_SIZE, DOCK_ICON_SIZE } };
282 [dockIconUsageViewsContainingView lockFocus];
283 NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithFocusedViewRect:dockIconRect];
284 [dockIconUsageViewsContainingView unlockFocus];
286 NSImage *image = [[NSImage alloc] initWithSize:dockIconRect.size];
287 [image addRepresentation:rep];
292 [backgroundColor set];
293 NSFrameRectWithWidth([dockIconUsageViewsContainingView frame], 2.0f);
296 [NSApp setApplicationIconImage:image];
302 - (void)threadedUpdateCPUUsageView:(NSTimer *)timer {
303 unsigned i = [[timer userInfo] unsignedIntValue];
305 CPUUsageView *view = [usageViews objectAtIndex:i];
306 CPUUsageView *viewInDockIcon = [dockIconUsageViews objectAtIndex:i];
309 [view setCPUUsage:CPUUsage[i]];
310 [viewInDockIcon setCPUUsage:CPUUsage[i]];
311 [CPUUsageLock unlock];
313 if(shouldDrawToWindow)
315 if(shouldDrawToDockIcon)
316 [viewInDockIcon display];
318 if(threadsRemainingToDie) {
322 --threadsRemainingToDie;
326 - (void)threadedLaunchTimer:(NSNumber *)CPUIndexNum {
327 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
329 NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
331 NSTimer *timer = [NSTimer timerWithTimeInterval:0.5
333 selector:@selector(threadedUpdateCPUUsageView:)
334 userInfo:CPUIndexNum //userInfo
336 [timer setFireDate:[NSDate dateWithTimeIntervalSinceNow:0.25]];
338 [runLoop addTimer:timer forMode:NSDefaultRunLoopMode];
345 - (void)updateCPUUsageButNotViews:(NSTimer *)timer {
346 if(shouldDrawToDockIcon)
347 [self drawToDockIcon];
349 natural_t numProcessors_nobodyCares = 0U;
350 processor_info_array_t processorInfo;
351 mach_msg_type_number_t numProcessorInfo;
353 kern_return_t err = host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numProcessors_nobodyCares, &processorInfo, &numProcessorInfo);
354 if(err == KERN_SUCCESS) {
355 vm_map_t target_task = mach_task_self();
359 for(unsigned i = 0U; i < numCPUs; ++i) {
360 //We only want the last $REFRESH_TIME seconds' worth of data, not all time.
362 if(lastProcessorInfo) {
364 (processorInfo[(CPU_STATE_MAX * i) + CPU_STATE_USER] - lastProcessorInfo[(CPU_STATE_MAX * i) + CPU_STATE_USER])
365 + (processorInfo[(CPU_STATE_MAX * i) + CPU_STATE_SYSTEM] - lastProcessorInfo[(CPU_STATE_MAX * i) + CPU_STATE_SYSTEM])
366 + (processorInfo[(CPU_STATE_MAX * i) + CPU_STATE_NICE] - lastProcessorInfo[(CPU_STATE_MAX * i) + CPU_STATE_NICE])
368 total = inUse + (processorInfo[(CPU_STATE_MAX * i) + CPU_STATE_IDLE] - lastProcessorInfo[(CPU_STATE_MAX * i) + CPU_STATE_IDLE]);
370 inUse = processorInfo[(CPU_STATE_MAX * i) + CPU_STATE_USER] + processorInfo[(CPU_STATE_MAX * i) + CPU_STATE_SYSTEM] + processorInfo[(CPU_STATE_MAX * i) + CPU_STATE_NICE];
371 total = inUse + processorInfo[(CPU_STATE_MAX * i) + CPU_STATE_IDLE];
374 CPUUsage[i] = inUse / total;
377 [CPUUsageLock unlock];
379 if(lastProcessorInfo) {
380 size_t lastProcessorInfoSize = sizeof(integer_t) * numLastProcessorInfo;
381 vm_deallocate(target_task, (vm_address_t)lastProcessorInfo, lastProcessorInfoSize);
384 lastProcessorInfo = processorInfo;
385 numLastProcessorInfo = numProcessorInfo;
387 NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
388 NSLocalizedString(@"Could not get CPU load information", /*comment*/ nil), NSLocalizedDescriptionKey,
389 NSLocalizedString(@"Do you have a CPU in your computer? (Just kidding. I have no idea how to get a useful string from a kernel error code. --The Author)", /*comment*/ nil), NSLocalizedRecoverySuggestionErrorKey,
391 [NSApp presentError:[NSError errorWithDomain:NSMachErrorDomain code:err userInfo:dict]];
392 [NSApp terminate:nil];
396 #pragma mark Accessors
398 - (NSColor *)backgroundColor {
399 return backgroundColor;
401 - (void)setBackgroundColor:(NSColor *)newBackgroundColor {
402 if(backgroundColor != newBackgroundColor) {
403 [backgroundColor release];
404 backgroundColor = [newBackgroundColor retain];
411 - (void)setCellWidth:(float)newCellWidth {
412 cellWidth = newCellWidth;
415 register float numCPUsFloat = numCPUs;
416 float numViewsX, numViewsY;
417 if(orientation == CPUUsageOrientationHorizontal) {
420 } else if(orientation == CPUUsageOrientationVertical) {
424 numViewsX = ceilf(sqrtf(numCPUsFloat));
425 numViewsY = ceilf(numCPUsFloat / numViewsX);
428 NSRect oldFrame = [window frame];
429 NSRect newFrame = oldFrame;
430 newFrame.size.width = cellWidth * numViewsX;
431 newFrame.size.height = cellHeight * numViewsY;
432 // [window setFrame:newFrame display:YES animate:YES]; //For some reason, this blocks dragging the window around. Lucky me — the NSViewAnimation is prettier.
433 NSDictionary *animDict = [NSDictionary dictionaryWithObjectsAndKeys:
434 window, NSViewAnimationTargetKey,
435 [NSValue valueWithRect:oldFrame], NSViewAnimationStartFrameKey,
436 [NSValue valueWithRect:newFrame], NSViewAnimationEndFrameKey,
438 NSViewAnimation *anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:animDict]];
439 [anim startAnimation];
444 - (float)cellHeight {
447 - (void)setCellHeight:(float)newCellHeight {
448 cellHeight = newCellHeight;
451 register float numCPUsFloat = numCPUs;
452 float numViewsX, numViewsY;
453 if(orientation == CPUUsageOrientationHorizontal) {
456 } else if(orientation == CPUUsageOrientationVertical) {
460 numViewsX = ceilf(sqrtf(numCPUsFloat));
461 numViewsY = ceilf(numCPUsFloat / numViewsX);
464 NSRect oldFrame = [window frame];
465 NSRect newFrame = oldFrame;
466 newFrame.size.width = cellWidth * numViewsX;
467 newFrame.size.height = cellHeight * numViewsY;
468 // [window setFrame:frame display:YES animate:YES]; //For some reason, this blocks dragging the window around. Lucky me — the NSViewAnimation is prettier.
469 NSDictionary *animDict = [NSDictionary dictionaryWithObjectsAndKeys:
470 window, NSViewAnimationTargetKey,
471 [NSValue valueWithRect:oldFrame], NSViewAnimationStartFrameKey,
472 [NSValue valueWithRect:newFrame], NSViewAnimationEndFrameKey,
474 NSViewAnimation *anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:animDict]];
475 [anim startAnimation];
480 - (BOOL)shouldDrawToDockIcon {
481 return shouldDrawToDockIcon;
483 - (void)setShouldDrawToDockIcon:(BOOL)flag {
484 shouldDrawToDockIcon = flag;
486 if(!shouldDrawToDockIcon)
487 [NSApp setApplicationIconImage:[NSImage imageNamed:@"CPUUsageIcon"]];
490 - (BOOL)shouldDrawToWindow {
491 return shouldDrawToWindow;
493 - (void)setShouldDrawToWindow:(BOOL)flag {
494 shouldDrawToWindow = flag;
496 if(shouldDrawToWindow)
497 [window orderFront:nil];
499 [window orderOut:nil];
504 - (IBAction)setOrientationToVertical:sender {
505 orientation = CPUUsageOrientationVertical;
506 [[[NSUserDefaultsController sharedUserDefaultsController] values] setValue:[NSNumber numberWithInt:orientation] forKey:@"Window orientation"];
508 [self layOutCellsInWindow:window];
510 [verticalButton setState:NSOnState];
511 [horizontalButton setState:NSOffState];
512 [rectangularButton setState:NSOffState];
514 - (IBAction)setOrientationToHorizontal:sender {
515 orientation = CPUUsageOrientationHorizontal;
516 [[[NSUserDefaultsController sharedUserDefaultsController] values] setValue:[NSNumber numberWithInt:orientation] forKey:@"Window orientation"];
518 [self layOutCellsInWindow:window];
520 [verticalButton setState:NSOffState];
521 [horizontalButton setState:NSOnState];
522 [rectangularButton setState:NSOffState];
524 - (IBAction)setOrientationToRectangular:sender {
525 orientation = CPUUsageOrientationRectangular;
526 [[[NSUserDefaultsController sharedUserDefaultsController] values] setValue:[NSNumber numberWithInt:orientation] forKey:@"Window orientation"];
528 [self layOutCellsInWindow:window];
530 [verticalButton setState:NSOffState];
531 [horizontalButton setState:NSOffState];
532 [rectangularButton setState:NSOnState];
537 @implementation CPUUsageMonitor (PRIVATE)
539 - (void)layOutCellsInWindow:(NSWindow *)thisWindow {
540 [thisWindow disableScreenUpdatesUntilFlush];
542 //Each cell in the Dock icon is square, so this rectangle may not be square. Consider a dual-proc machine.
543 //And of course, the floater is non-square in most configurations.
553 register float numCPUsFloat = numCPUs;
554 float numViewsX, numViewsY;
555 if((thisWindow == window) && (orientation == CPUUsageOrientationHorizontal)) {
556 numViewsX = numCPUsFloat;
558 } else if((thisWindow == window) && (orientation == CPUUsageOrientationVertical)) {
560 numViewsY = numCPUsFloat;
562 numViewsX = ceilf(sqrtf(numCPUsFloat));
563 numViewsY = ceilf(numCPUsFloat / numViewsX);
565 BOOL drawingToDock = (thisWindow == dockIconWindow);
569 cellSize.width = DOCK_ICON_SIZE / numViewsX;
570 cellSize.height = DOCK_ICON_SIZE / numViewsY;
572 cellSize.width = cellWidth;
573 cellSize.height = cellHeight;
576 //Handle odd cells (1 and 2 above).
577 //The number of cells missing from the row with the odd cells (in the example above, this = 1).
578 float numCellsMissing = ((numViewsX * numViewsY) - numCPUsFloat);
579 float numOddCellsFloat = numCellsMissing ? numViewsX - numCellsMissing : 0.0f;
580 unsigned numOddCells = numOddCellsFloat;
581 unsigned numNotOddCells = numCPUs - numOddCells;
583 BZGridEnumerator *floaterOddCellsEnum = nil;
584 BZGridEnumerator *floaterCellsEnum = [[BZGridEnumerator alloc] initWithCellSize:cellSize
585 numberOfColumns:numViewsX
586 numberOfRows:numViewsY];
587 NSRect frame = [floaterCellsEnum overallRect];
588 if(numCellsMissing) {
589 /*oddCellsWidth is the fraction of the total frame width used by odd cells.
591 *numEmptySpaces = total - numCPUs
592 *numOddCPUs = X - numEmptySpaces
593 *oddCellsWidth = numOddCPUs / X
595 float oddCellsWidth = numOddCellsFloat / numViewsX;
596 float oddCellWidth = oddCellsWidth / numOddCellsFloat;
597 floaterOddCellsEnum = [[BZGridEnumerator alloc] initWithCellSize:(NSSize){ frame.size.width * oddCellWidth, cellSize.height }
598 offset:(NSPoint){ 0.0f, cellSize.height * numViewsY }
599 numberOfColumns:numOddCells
601 frame.size.height += [floaterOddCellsEnum overallRect].size.height;
603 frame.origin = [thisWindow frame].origin;
604 [thisWindow setFrame:frame display:NO];
605 float offsetY = drawingToDock ? ((DOCK_ICON_SIZE - frame.size.height) * 0.5f) : 0.0f;
607 //Sign up for notifications of pref changes.
608 NSUserDefaultsController *udc = [NSUserDefaultsController sharedUserDefaultsController];
609 NSDictionary *colorBindingOptions = [NSDictionary dictionaryWithObject:@"NSUnarchiveFromData" forKey:NSValueTransformerNameBindingOption];
611 NSMutableArray *usageViewsArray = (thisWindow == window) ? usageViews : dockIconUsageViews;
612 unsigned usageViewsArrayCount = [usageViewsArray count];
614 //We need this information to set drawsFrame of every view to the correct value.
615 //We don't need to retrieve it every time, so we get it once here and cache it in a variable.
616 //The Dock icon window never has per-cell frames.
617 BOOL shouldDrawFrame = [NSApp isActive] && (thisWindow == window);
619 //Add CPU usage views.
620 unsigned numCols = numViewsX, numRows = numViewsY;
621 NSView *contentView = [thisWindow contentView];
622 unsigned CPUNum = 0U;
623 if(numCellsMissing) {
624 while(numOddCells--) {
625 NSRect oddCellRect = [floaterOddCellsEnum nextRect];
626 oddCellRect.origin.y += offsetY;
628 CPUUsageView *usageView;
629 if(CPUNum >= usageViewsArrayCount) {
630 usageView = [[[CPUUsageView alloc] initWithFrame:oddCellRect] autorelease];
631 [contentView addSubview:usageView];
632 [usageViewsArray addObject:usageView];
633 ++usageViewsArrayCount;
635 usageView = [usageViewsArray objectAtIndex:CPUNum];
636 [usageView setFrame:oddCellRect];
639 [usageView setDrawsFrame:shouldDrawFrame];
640 [usageView setCPUUsage:8.88f]; //XXX TEMP - Should just not update the Dock icon until after all samples anyway
642 [usageView setCPUNumber:++CPUNum];
644 [usageView bind:@"textColor"
646 withKeyPath:@"values.Dock icon text color"
647 options:colorBindingOptions];
648 [usageView bind:@"textOpacity"
650 withKeyPath:@"values.Dock icon text opacity"
652 [usageView bind:@"backgroundColor"
654 withKeyPath:@"backgroundColor"
657 oddCellRect.origin.x += oddCellRect.size.width;
660 while(numNotOddCells--) {
661 frame = [floaterCellsEnum nextRect];
662 frame.origin.y += offsetY;
663 unsigned col = CPUNum % numCols, row = CPUNum / numRows;
665 CPUUsageView *usageView;
666 if(CPUNum >= usageViewsArrayCount) {
667 usageView = [[[CPUUsageView alloc] initWithFrame:frame] autorelease];
668 [contentView addSubview:usageView];
669 [usageViewsArray addObject:usageView];
670 ++usageViewsArrayCount;
672 usageView = [usageViewsArray objectAtIndex:CPUNum];
673 [usageView setFrame:frame];
675 unsigned mask = NSViewWidthSizable | NSViewHeightSizable
676 | (NSViewMinXMargin * (col > 0U) )
677 | (NSViewMaxXMargin * (col < (numCols - 1U)))
678 | (NSViewMaxYMargin * (row > 0U) )
679 | (NSViewMinYMargin * (row < (numRows - 1U)))
681 [usageView setAutoresizingMask:mask];
682 [usageView setMenu:contextualMenu];
684 [usageView setDrawsFrame:shouldDrawFrame];
686 [usageView setCPUNumber:++CPUNum];
688 [usageView bind:@"textColor"
690 withKeyPath:@"values.Floater text color"
691 options:colorBindingOptions];
692 [usageView bind:@"textOpacity"
694 withKeyPath:@"values.Floater text opacity"
696 [usageView bind:@"backgroundColor"
698 withKeyPath:@"backgroundColor"
702 [floaterOddCellsEnum release];
703 [floaterCellsEnum release];