ca, fr, fr_CA, and it updates
[adiumx.git] / Source / AIMenuItemView.m
blob019aa50940f802ab3ddeb3762845688b632d6228
1 //
2 //  AIMenuItemView.m
3 //  Adium
4 //
5 //  Created by Evan Schoenberg on 12/20/05.
6 //
8 #import "AIMenuItemView.h"
10 #define CHECK_UNICODE   0x2713 /* Unicode CHECK MARK*/
12 #define MENU_ITEM_HEIGHT        17
13 #define MENU_ITEM_SPACING       2
15 @interface AIMenuItemView (PRIVATE)
17 @end
19 @implementation AIMenuItemView
20 - (void)_initMenuItemView
22         currentHoveredIndex = -1;
25 - (void)awakeFromNib
27         if ([[self superclass] instancesRespondToSelector:@selector(awakeFromNib)]) {
28         [super awakeFromNib];
29         }
31         if (delegate && [delegate respondsToSelector:@selector(menuForMenuItemView:)]) {
32                 [self setMenu:[delegate menuForMenuItemView:self]];
33         }
34         
35         [self _initMenuItemView];
38 - (id)initWithFrame:(NSRect)inFrame
40         if ((self = [super initWithFrame:inFrame])) {
41                 [self _initMenuItemView];
42         }
43         
44         return self;
47 - (void)dealloc
49         [menu release];
50         [trackingTags release];
51         [menuItemAttributes release];
52         [disabledMenuItemAttributes release];
53         [hoveredMenuItemAttributes release];
55         [super dealloc];
59 #pragma mark Menu
61 /*!
62  * @brief Set the menu we display
63  */
64 - (void)setMenu:(NSMenu *)inMenu
66 //      NSLog(@"Set menu: %@",inMenu);
67         if (menu != inMenu) {
68                 [menu release];
69                 menu = [inMenu retain];
70         }
71         
72         [self setNeedsDisplay:YES];
73         
74         if ([delegate respondsToSelector:@selector(menuItemViewDidChangeMenu:)]) {
75                 [delegate menuItemViewDidChangeMenu:self];
76         }
77 //      NSLog(@"set menu reset");
78         [self resetCursorRects];
81 - (NSMenu *)menu
83         return menu;
86 /*!
87  * @brief Set our delegate
88  */
89 - (void)setDelegate:(id)inDelegate
91         delegate = inDelegate;
94 #pragma mark Index and Point/Rect Correlation
96 /*!
97  * @brief Return the index at a point
98  *
99  * @param inPoint The point in our local coordinates
100  */
101 - (int)indexAtPoint:(NSPoint)inPoint
103         float   heightFromTop = [self frame].size.height - inPoint.y;
104         int             index = 0;
106         while ((heightFromTop - (((MENU_ITEM_HEIGHT + MENU_ITEM_SPACING) * index) + MENU_ITEM_HEIGHT)) > 0) {
107                 index++;
108         }
110         return index;
114  * @brief Return the rect (in local coordinates) for a menu item by index
115  */
116 - (NSRect)rectForIndex:(int)index
118         NSRect  myFrame = [self frame];
119         return NSMakeRect(0,
120                                           (myFrame.size.height - (((MENU_ITEM_HEIGHT + MENU_ITEM_SPACING) * index) + MENU_ITEM_HEIGHT)),
121                                           myFrame.size.width,
122                                           MENU_ITEM_HEIGHT);
123         
126 #pragma mark Drawing
127 - (void)drawRect:(NSRect)inRect
129         int             i, numberOfMenuItems;
130         BOOL    willDisplayACheckbox = NO;
132         numberOfMenuItems = [menu numberOfItems];
133         
134         //Determine if one or more menu items is in a non-off (that it, on or mixed) state.
135         for (i = 0; i < numberOfMenuItems; i++) {
136                 NSMenuItem                      *menuItem = [menu itemAtIndex:i];
137                 if ([menuItem state] != NSOffState) {
138                         willDisplayACheckbox = YES;
139                         break;
140                 }
141         }
142         
143         //Now do the actual drawing
144         for (i = 0; i < numberOfMenuItems; i++) {
145                 NSRect  menuItemRect = [self rectForIndex:i];
147                 if (NSIntersectsRect(menuItemRect,inRect)) {
148                         NSMenuItem                      *menuItem = [menu itemAtIndex:i];
150                         if ([menuItem isSeparatorItem]) {
151                                 //Draw the separatorItem line centered in the menu item rect
152                                 menuItemRect.origin.y = (menuItemRect.origin.y + (menuItemRect.size.height / 2) - 1);
153                                 menuItemRect.size.height = 1;
154                                 
155                                 [[NSColor grayColor] set];
156                                 NSRectFill(menuItemRect);
157         
158                         } else {
159                                 NSAttributedString      *title;
160                                 BOOL                            currentlyHovered = ((currentHoveredIndex == i) && [menuItem isEnabled]);
162                                 if (currentlyHovered) {
163                                         //Draw a selectedMenuItemColor box if we are hovered...
164                                         [[NSColor selectedMenuItemColor] set];
165                                         NSRectFill(menuItemRect);
166                                 }
167                                 
168                                 //Move in so the highlight drawn above has proper borders around a checkmark
169                                 menuItemRect.origin.x += 1;
170                                 menuItemRect.size.width -= 1;
171                                 
172                                 //Indent the menu item if appropriate
173                                 float indentation = [menuItem indentationLevel] * 5.0;
174                                 menuItemRect.origin.x += indentation;
175                                 menuItemRect.size.width -= indentation;
177                                 if ([menuItem state] == NSOnState) {
178                                         NSImage *onStateImage;
179                                         NSSize  size;
181                                         if (currentlyHovered) {
182                                                 //If we're currently hovered, we need to turn our checkmark white...
183                                                 NSImage *originalImage = [menuItem onStateImage];
184                                                 size = [originalImage size];
186                                                 onStateImage = [[[NSImage alloc] initWithSize:[originalImage size]] autorelease];
187                                                 
188                                                 [onStateImage lockFocus];
190                                                 //Fill the new image with white
191                                                 [[NSColor whiteColor] set];
192                                                 NSRectFill(NSMakeRect(0, 0, size.width, size.height));
194                                                 //But only keep the white where originalImage (the checkmark) exists
195                                                 [originalImage drawInRect:NSMakeRect(0, 0, size.width, size.height)
196                                                                                  fromRect:NSMakeRect(0, 0, size.width, size.height)
197                                                                                 operation:NSCompositeDestinationAtop
198                                                                                  fraction:1.0];
199                                                 [onStateImage unlockFocus];
200                                                 
201                                         } else {
202                                                 onStateImage = [menuItem onStateImage];
203                                                 size = [onStateImage size];
204                                         }
205                                         
206                                         [onStateImage drawAtPoint:NSMakePoint(menuItemRect.origin.x, 
207                                                                                                                   menuItemRect.origin.y + ((menuItemRect.size.height - size.height) / 2))
208                                                                          fromRect:NSMakeRect(0, 0, size.width, size.height)
209                                                                         operation:NSCompositeSourceOver
210                                                                          fraction:(currentlyHovered ? 1.0 : 0.85)];
211                                 }
212                                 
213                                 NSDictionary    *currentTextAttributes;
215                                 if (currentlyHovered) {
216                                         //We're displaying the hovered menu item
217                                         if (!hoveredMenuItemAttributes) {
218                                                 hoveredMenuItemAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
219                                                         [NSFont menuFontOfSize:13], NSFontAttributeName,
220                                                         [NSColor selectedMenuItemTextColor], NSForegroundColorAttributeName,
221                                                         nil];
222                                         }
223                                         
224                                         currentTextAttributes = hoveredMenuItemAttributes;
225                                         
226                                 } else if (![menuItem isEnabled]) {
227                                         //We're displaying a disabled menu item
228                                         if (!disabledMenuItemAttributes) {
229                                                 disabledMenuItemAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
230                                                 [NSFont menuFontOfSize:13], NSFontAttributeName,
231                                                 [NSColor disabledControlTextColor], NSForegroundColorAttributeName,
232                                                 nil];
233                                         }
235                                         currentTextAttributes = disabledMenuItemAttributes;
237                                 } else {
238                                         //We're displaying a non-hovered menu item
239                                         if (!menuItemAttributes) {
240                                                 menuItemAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
241                                                         [NSFont menuFontOfSize:13], NSFontAttributeName,
242                                                         nil];
243                                         }
244                                         
245                                         currentTextAttributes = menuItemAttributes;
246                                 }
248                                 title = [[NSAttributedString alloc] initWithString:[menuItem title]
249                                                                                                                 attributes:currentTextAttributes];
251                                 menuItemRect.origin.x += 2;
252                                 menuItemRect.size.width -= 2;
254                                 if (willDisplayACheckbox) {
255                                         //Shift right, and shorten our width, for indentation like a real menu, leaving space for the checkmark if it's needed
256 #define CHECKMARK_WIDTH 9
257                                         menuItemRect.origin.x += CHECKMARK_WIDTH;
258                                         menuItemRect.size.width -= CHECKMARK_WIDTH;
259                                 }
261                                 [title drawInRect:menuItemRect];
262                                 [title release];
263                         }
264                 }
265         }
268 #pragma mark Sizing
269 - (void)sizeToFit
271         NSRect frame = [self frame];
273         float change = (([[self menu] numberOfItems] * (MENU_ITEM_HEIGHT + MENU_ITEM_SPACING)) - MENU_ITEM_SPACING) - frame.size.height;
274         frame.size.height += change;
275         frame.origin.y -= change;
277         [self setFrame:frame];
280 #pragma mark Hovering tracking
283  * @brief The mouse is hovering over a point
285  * @param inPoint The point in our local coordinates, or NULL if we are no longer hovering
286  */
287 - (void)setHoveringAtPoint:(NSPoint)inPoint
289         if (!NSEqualPoints(inPoint, NSZeroPoint)) {
290                 currentHoveredIndex = [self indexAtPoint:inPoint];
291         } else {
292                 currentHoveredIndex = -1;
293         }
295         [self setNeedsDisplay:YES];
299 //Cursor entered one of our tracking rects
300 - (void)mouseEntered:(NSEvent *)theEvent
302         [self setHoveringAtPoint:[self convertPoint:[theEvent locationInWindow] fromView:nil]];
303         
304         [super mouseEntered:theEvent];
308 //Cursor left one of our tracking rects
309 - (void)mouseExited:(NSEvent *)theEvent
311         [self setHoveringAtPoint:NSZeroPoint];
313         [super mouseExited:theEvent];
316 - (void)mouseDown:(NSEvent *)theEvent
318         if (currentHoveredIndex != -1) {
319                 NSMenuItem      *menuItem = [[self menu] itemAtIndex:currentHoveredIndex];
320                 [[menuItem target] performSelector:[menuItem action] withObject:menuItem];
321         }
322         
323         [super mouseDown:theEvent];
327 #pragma mark Tracking rects
330  * @brief Remove all our tracking rects
331  */
332 - (void)removeTrackingRects
334         NSEnumerator *enumerator;
335         NSNumber         *trackingTag;
336         
337         enumerator = [trackingTags objectEnumerator];
338         while ((trackingTag = [enumerator nextObject])) {
339                 [self removeTrackingRect:[trackingTag intValue]];
340         }
341         
342         [trackingTags release]; trackingTags = nil;     
345 //Reset our cursor tracking
346 - (void)resetCursorRects
348         //Stop any existing tracking
349         if (trackingTags) {
350                 [self removeTrackingRects];
351         }
352         
353         //Add tracking rects if our superview and window are ready
354         if ([self superview] && [self window]) {                
355                 int             i, numberOfMenuItems;
357                 trackingTags = [[NSMutableSet alloc] init];
359                 numberOfMenuItems = [menu numberOfItems];
360                 for (i = 0; i < numberOfMenuItems; i++) {
361                         NSTrackingRectTag       trackingTag;
362                         NSRect                          trackRect = [self rectForIndex:i];
363                         NSPoint                         localPoint = [self convertPoint:[[self window] convertScreenToBase:[NSEvent mouseLocation]]
364                                                                                                            fromView:nil];
365                         BOOL                            mouseInside = NSPointInRect(localPoint, trackRect);
367                         trackingTag = [self addTrackingRect:trackRect owner:self userData:nil assumeInside:mouseInside];
368                         [trackingTags addObject:[NSNumber numberWithInt:trackingTag]];
369                         NSLog(@"Added tracking rect %i for %@ (%i)",trackingTag,NSStringFromRect(trackRect), mouseInside);
370                         if (mouseInside) [self mouseEntered:nil];
371                 }
372         }
375 @end