1/* 2** PlopWindowController.m 3** 4** Copyright (c) 2002 5** 6** Author: Ludovic Marcotte <ludovic@Sophos.ca> 7** 8** This program is free software; you can redistribute it and/or modify 9** it under the terms of the GNU General Public License as published by 10** the Free Software Foundation; either version 2 of the License, or 11** (at your option) any later version. 12** 13** This program is distributed in the hope that it will be useful, 14** but WITHOUT ANY WARRANTY; without even the implied warranty of 15** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16** GNU General Public License for more details. 17** 18** You should have received a copy of the GNU General Public License 19** along with this program; if not, write to the Free Software 20** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21*/ 22 23#import "PlopWindowController.h" 24 25#import "Channel.h" 26#import "Constants.h" 27#import "NSStringExtensions.h" 28#import "Plop.h" 29#import "PlopView.h" 30#import "PlopWindow.h" 31 32static id lastPlopWindowOnTop = nil; 33 34@implementation PlopWindowController 35 36- (id) initWithWindowNibName: (NSString *) theNibName 37{ 38 PlopWindow *plopWindow; 39 40 plopWindow = [[PlopWindow alloc] initWithContentRect:NSMakeRect(10,10,DEFAULT_PLOP_WIDTH,DEFAULT_PLOP_HEIGHT) 41 styleMask: NSBorderlessWindowMask 42 backing: NSBackingStoreBuffered 43 defer: NO]; 44 45 self = [super initWithWindow: plopWindow]; 46 RELEASE(plopWindow); 47 48 [plopWindow layoutWindow]; 49 [plopWindow setDelegate: self]; 50 51 // We link our outlets 52 textView = [plopWindow textView]; 53 54 55 // We set our text view color 56 [textView setBackgroundColor: [NSColor colorWithDeviceRed: 0.90 57 green: 0.90 58 blue: 0.90 59 alpha: 1.0]]; 60 61 // We set the title to an empty string 62 [[self window] setTitle: @""]; 63 64#ifdef MACOSX 65 // We link the dock menu 66 dockMenu = [[NSApp delegate] applicationDockMenu: nil]; 67#endif 68 69 return self; 70} 71 72 73// 74// 75// 76- (void) dealloc 77{ 78 NSLog(@"PlopWindowController: -dealloc"); 79 80 lastPlopWindowOnTop = nil; 81 82 [channel setPlopWindowController: nil]; 83 RELEASE(channel); 84 85#ifdef MACOSX 86 RELEASE(dockSubMenu); 87#endif 88 89 [super dealloc]; 90} 91 92 93 94// 95// delegate methods 96// 97- (BOOL) textView: (NSTextView *) textView 98 clickedOnLink: (id) link 99 atIndex: (unsigned) charIndex 100{ 101 return [self openURL: link]; 102} 103 104 105// 106// 107// 108- (void) windowDidBecomeKey: (NSNotification *) aNotification 109{ 110 lastPlopWindowOnTop = [self window]; 111 //[[PlopInfoWindowController singleInstance] setPlop: [self plop]]; 112} 113 114 115// 116// 117// 118- (void) windowDidLoad 119{ 120 lastPlopWindowOnTop = [self window]; 121 122 [super windowDidLoad]; 123} 124 125 126// 127// 128// 129- (void) windowDidMove: (NSNotification *) aNotification 130{ 131 [self _updateWindowFrame]; 132} 133 134 135// 136// 137// 138- (void) windowDidResize: (NSNotification *) aNotification 139{ 140 [self _updateWindowFrame]; 141} 142 143 144// 145// 146// 147- (void) windowWillClose: (NSNotification *) theNotification 148{ 149 AUTORELEASE(self); 150} 151 152 153// 154// access / mutation 155// 156+ (id) lastPlopWindowOnTop 157{ 158 return lastPlopWindowOnTop; 159} 160 161 162// 163// 164// 165- (Channel *) channel 166{ 167 return channel; 168} 169 170 171// 172// 173// 174- (void) setChannel: (Channel *) theChannel 175{ 176 if ( theChannel ) 177 { 178 RETAIN(theChannel); 179 RELEASE(channel); 180 channel = theChannel; 181 182 [channel setPlopWindowController: self]; 183 184 // We set our count variable and our icon 185 [[self plopView] setCount: [channel itemCount]]; 186 [[self plopView] setIcon: [channel icon]]; 187 188 // We now update the text view with the content of the channel 189 [self update]; 190 191 // We finally set the window title and frame 192 if ( [channel title] ) 193 { 194 [[self window] setTitle: [channel title]]; 195 } 196 [[self window] setFrame: [[channel plop] frame] 197 display: NO]; 198 199#ifdef MACOSX 200 // We create a submenu in the dock menu 201 [dockMenu addItemWithTitle: [[channel plop] title] action: nil keyEquivalent: @""]; 202 dockSubMenu = [[NSMenu alloc] init]; 203 [dockMenu setSubmenu: dockSubMenu forItem: [dockMenu itemWithTitle: [[channel plop] title]]]; 204#endif 205 } 206 else 207 { 208 RELEASE(channel); 209 channel = nil; 210 } 211} 212 213 214// 215// 216// 217- (NSTextView *) textView 218{ 219 return textView; 220} 221 222 223// 224// 225// 226- (PlopView *) plopView 227{ 228 return [(PlopWindow *)[self window] plopView]; 229} 230 231 232// 233// action methods 234// 235#ifdef MACOSX 236- (IBAction) dockItemSelected: (id) sender 237{ 238 if ( [sender isKindOfClass: [NSMenuItem class]] ) 239 { 240 [self openURL: (NSURL *)[(NSMenuItem *)sender representedObject]]; 241 } 242} 243 244 245- (IBAction) dockItemUpdate: (id) sender 246{ 247 248 if ( [sender isKindOfClass: [NSMenuItem class]] ) 249 { 250 [channel update: [self plopView]]; 251 } 252} 253#endif 254 255 256// 257// Other methods 258// 259- (BOOL) openURL: (NSURL *) theURL 260{ 261 NSLog(@"Opening %@...", [theURL description]); 262 263 if ( [[NSUserDefaults standardUserDefaults] integerForKey: @"BROWSER_SOURCE"] == USE_DEFAULT_BROWSER ) 264 { 265 return [[NSWorkspace sharedWorkspace] openURL: theURL]; 266 } 267 else 268 { 269 NSString *handler; 270 271 handler = [[NSUserDefaults standardUserDefaults] objectForKey: @"BROWSER_PATH"]; 272 273 if ( !handler || [[handler stringByTrimmingWhiteSpaces] length] == 0 ) 274 { 275 NSBeep(); 276 return NO; 277 } 278 279 [NSTask launchedTaskWithLaunchPath: @"/bin/sh" 280 arguments: [NSArray arrayWithObjects: @"-c", 281 [NSString stringWithFormat: handler, [theURL description]], 282 nil] ]; 283 284 } 285 286 return YES; 287} 288 289 290// 291// 292// 293- (void) update 294{ 295 NSMutableAttributedString *aMutableAttributedString; 296 int i; 297 298 if ( ![self channel] ) 299 { 300 return; 301 } 302 303#ifdef MACOSX 304 // We empty the dock sub menu 305 for (i = [dockSubMenu numberOfItems] - 1; i > -1; i--) 306 { 307 [dockSubMenu removeItem: [dockSubMenu itemAtIndex: i]]; 308 } 309#endif 310 311 aMutableAttributedString = [[NSMutableAttributedString alloc] init]; 312 313 for (i = 0; i < [[self channel] count]; i++) 314 { 315 NSDictionary *linkAttributes; 316 Element *aElement; 317 318 aElement = [[self channel] elementAtIndex: i]; 319 320 if ( [aElement type] == ITEM && [aElement link] ) 321 { 322 NSAttributedString *aAttributedString; 323 324 linkAttributes= [NSDictionary dictionaryWithObjectsAndKeys: [aElement link], NSLinkAttributeName, 325#ifdef MACOSX 326 [NSNumber numberWithInt: NSNoUnderlineStyle], 327#else 328 [NSNumber numberWithInt: GSNoUnderlineStyle], 329#endif 330 NSUnderlineStyleAttributeName, 331 [NSColor blackColor], NSForegroundColorAttributeName, 332 NULL]; 333 aAttributedString = [[NSAttributedString alloc] initWithString: [[aElement title] 334 stringByAppendingString: @"\n"] 335 attributes: linkAttributes]; 336 337 [aMutableAttributedString appendAttributedString: aAttributedString]; 338 339#ifdef MACOSX 340 // We add a menu item in the dock sub menu 341 [dockSubMenu addItem: [self _constructMenuItem: [aElement title] 342 action: @selector(dockItemSelected:) 343 url: [aElement link]]]; 344#endif 345 346 } 347 } 348 349#ifdef MACOSX 350 // We complete the dock sub menu but a Refresh menu item 351 [dockSubMenu addItem: [NSMenuItem separatorItem]]; 352 [dockSubMenu addItem: [self _constructMenuItem: @"Refresh" 353 action: @selector(dockItemUpdate:) 354 url: nil]]; 355#endif 356 357 [[textView textStorage] setAttributedString: aMutableAttributedString]; 358 RELEASE(aMutableAttributedString); 359 360 // We set the count of plopview and ask to refresh it 361 [[self plopView] setCount: [channel itemCount]]; 362 [[self plopView] setNeedsDisplay: YES]; 363} 364 365 366@end 367 368 369// 370// private methods 371// 372@implementation PlopWindowController (Private) 373 374 375#ifdef MACOSX 376- (NSMenuItem *) _constructMenuItem: (NSString *) theTitle 377 action: (SEL) theSelector 378 url: (NSURL *) theURL 379{ 380 NSMenuItem *item; 381 382 // This is the "correct" way to create the menu items - this code is used if we're 383 // running on a fixed system 384 if (NSAppKitVersionNumber >= kFixedDockMenuAppKitVersion) 385 { 386 item = [[NSMenuItem alloc] initWithTitle: theTitle 387 action: theSelector 388 keyEquivalent: @""]; 389 AUTORELEASE(item); 390 [item setTarget: self]; 391 [item setRepresentedObject: theURL]; 392 [item setEnabled: YES]; 393 } 394 else // we're running on an OS version that isn't fixed; use NSInvocation 395 { 396 // This invocation is going to be of the form aSelector 397 NSInvocation *myInv; 398 399 myInv = [NSInvocation invocationWithMethodSignature: [self methodSignatureForSelector: theSelector]]; 400 item = [[NSMenuItem alloc] initWithTitle: theTitle 401 action: @selector(invoke) 402 keyEquivalent: @""]; 403 AUTORELEASE(item); 404 405 // This invocation is going to be an invocation of aSelector 406 [myInv setSelector: theSelector]; 407 408 // This invocation is going to send its message to self 409 [myInv setTarget: self]; 410 411 // The parameter to this invocation will be item, i.e. the NSMenuItem that should 412 // be the sender to -menuSelected: Note that we pass it at index 2 - indices 0 and 1 413 // are taken by the hidden arguments self and _cmd, accessible through -setTarget: 414 // and -setSelector: 415 [myInv setArgument: &item atIndex: 2]; 416 417 // here we retain the invocation so it won't go away. Later on we'll have to 418 // release it to avoid leaking 419 [item setTarget: RETAIN(myInv)]; 420 [item setTag: 1]; 421 422 [item setRepresentedObject: theURL]; 423 [item setEnabled: YES]; 424 } 425 426 return item; 427} 428#endif 429 430 431- (void) _updateWindowFrame 432{ 433 if ( [[self channel] plop] ) 434 { 435 NSRect theFrame; 436 437 theFrame = [[self window] frame]; 438 [[[self channel] plop] setFrame: theFrame]; 439 } 440} 441 442 443@end 444