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