root/trunk/lilypad/Sources/LPGroupChatController.m @ 194

Revision 194, 42.6 KB (checked in by jppavao, 5 years ago)

Fixed the color of our own messages on a group-chat. They're now always displayed using a distinctive color. Fixes #156.

Line 
1//
2//  LPGroupChatController.m
3//  Lilypad
4//
5//      Copyright (C) 2006-2007 PT.COM,  All rights reserved.
6//      Author: Joao Pavao <jppavao@criticalsoftware.com>
7//
8//      For more information on licensing, read the README file.
9//      Para mais informações sobre o licenciamento, leia o ficheiro README.
10//
11
12#import "LPGroupChatController.h"
13#import "LPGroupChat.h"
14#import "LPGroupChatContact.h"
15#import "LPGroupChatConfigController.h"
16#import "LPAccount.h"
17#import "LPRoster.h"
18#import "LPGroup.h"
19#import "LPContact.h"
20#import "LPContactEntry.h"
21#import "LPChatViewsController.h"
22#import "LPColorBackgroundView.h"
23#import "LPGrowingTextField.h"
24#import "LPRosterDragAndDrop.h"
25#import "LPCapabilitiesPredicates.h"
26
27#import "NSString+HTMLAdditions.h"
28#import "NSxString+EmoticonAdditions.h"
29
30
31#define INPUT_LINE_HISTORY_ITEMS_MAX    20
32
33
34// KVO Contexts
35static NSString *LPGroupChatContext                                     = @"GroupChatContext";
36static NSString *LPGroupChatParticipantsContext         = @"ParticipantsContext";
37static NSString *LPParticipantsAttribsContext           = @"PartAttributesContext";
38
39
40// Toolbar item identifiers
41static NSString *ToolbarSetTopicIdentifier              = @"SetTopic";
42static NSString *ToolbarSetNicknameIdentifier   = @"SetNickname";
43static NSString *ToolbarInviteIdentifier                = @"Invite";
44static NSString *ToolbarPrivateChatIdentifier   = @"PrivateChat";
45static NSString *ToolbarConfigRoomIdentifier    = @"ConfigRoom";
46
47
48@implementation LPGroupChatParticipantsTableView
49
50- (NSMenu *)menuForEvent:(NSEvent *)theEvent
51{
52        NSPoint mouseLocInView = [self convertPoint:[theEvent locationInWindow] fromView:nil];
53        int hitRow = [self rowAtPoint:mouseLocInView];
54       
55        if (hitRow < 0) {
56                return nil;
57        }
58        else {
59                [[self window] makeFirstResponder:self];
60               
61                if (![[self selectedRowIndexes] containsIndex:hitRow])
62                        [self selectRowIndexes:[NSIndexSet indexSetWithIndex:hitRow] byExtendingSelection:NO];
63               
64                return [self menu];
65        }
66}
67
68@end
69
70
71@interface LPGroupChatController (Private)
72- (void)p_startObservingGroupChatParticipants;
73- (void)p_stopObservingGroupChatParticipants;
74- (void)p_startObservingGroupChatParticipant:(LPGroupChatContact *)participant;
75- (void)p_stopObservingGroupChatParticipant:(LPGroupChatContact *)participant;
76- (void)p_setupChatDocumentTitle;
77- (void)p_setupToolbar;
78@end
79
80
81@implementation LPGroupChatController
82
83- initWithGroupChat:(LPGroupChat *)groupChat delegate:(id)delegate
84{
85        if (self = [self initWithWindowNibName:@"GroupChat"]) {
86                m_delegate = delegate;
87               
88                m_groupChat = [groupChat retain];
89                [m_groupChat setDelegate:self];
90               
91                NSUserDefaultsController *prefsCtrl = [NSUserDefaultsController sharedUserDefaultsController];
92               
93                [m_groupChat addObserver:self forKeyPath:@"active" options:0 context:LPGroupChatContext];
94                [m_groupChat addObserver:self forKeyPath:@"nickname" options:0 context:LPGroupChatContext];
95                [m_groupChat addObserver:self forKeyPath:@"myGroupChatContact" options:0 context:LPGroupChatContext];
96                [m_groupChat addObserver:self forKeyPath:@"myGroupChatContact.affiliation" options:0 context:LPGroupChatContext];
97                [prefsCtrl addObserver:self forKeyPath:@"values.DisplayEmoticonImages" options:0 context:NULL];
98               
99                m_gaggedContacts = [[NSMutableSet alloc] init];
100               
101                // Input line history
102                m_inputLineHistory = [[NSMutableArray alloc] init];
103               
104                // Observe group chat participants on attributes that should trigger a re-sorting of the participants list
105                [self p_startObservingGroupChatParticipants];
106        }
107        return self;
108}
109
110- (void)dealloc
111{
112        [self p_stopObservingGroupChatParticipants];
113       
114        NSUserDefaultsController *prefsCtrl = [NSUserDefaultsController sharedUserDefaultsController];
115       
116        [prefsCtrl removeObserver:self forKeyPath:@"values.DisplayEmoticonImages"];
117        [m_groupChat removeObserver:self forKeyPath:@"myGroupChatContact.affiliation"];
118        [m_groupChat removeObserver:self forKeyPath:@"myGroupChatContact"];
119        [m_groupChat removeObserver:self forKeyPath:@"nickname"];
120        [m_groupChat removeObserver:self forKeyPath:@"active"];
121       
122        [m_groupChat release];
123        [m_gaggedContacts release];
124        [m_inputLineHistory release];
125        [m_configController release];
126        [super dealloc];
127}
128
129- (void)windowDidLoad
130{
131        [self p_setupToolbar];
132       
133        [m_chatViewsController setOwnerName:[[m_groupChat myGroupChatContact] userPresentableNickname]];
134       
135        // Workaround for centering the icons.
136        [m_segmentedButton setLabel:nil forSegment:0];
137        [[m_segmentedButton cell] setToolTip:NSLocalizedString(@"Choose Emoticon", @"") forSegment:0];
138        // IB displays a round segmented button that apparently needs less space than the on that ends up
139        // showing in the app (the flat segmented button used in metal windows).
140        [m_segmentedButton sizeToFit];
141       
142        [m_topControlsBar setBackgroundColor:[NSColor colorWithPatternImage:[NSImage imageNamed:@"chatIDBackground"]]];
143        [m_topControlsBar setBorderColor:[NSColor colorWithCalibratedWhite:0.60 alpha:1.0]];
144       
145        [m_inputControlsBar setShadedBackgroundWithOrientation:LPVerticalBackgroundShading
146                                                                                          minEdgeColor:[NSColor colorWithCalibratedWhite:0.79 alpha:1.0]
147                                                                                          maxEdgeColor:[NSColor colorWithCalibratedWhite:0.99 alpha:1.0]];
148        [m_inputControlsBar setBorderColor:[NSColor colorWithCalibratedWhite:0.80 alpha:1.0]];
149       
150       
151        [m_participantsTableView setIntercellSpacing:NSMakeSize(15.0, 6.0)];
152       
153        NSSortDescriptor *sortByGagged = [[NSSortDescriptor alloc] initWithKey:@"gagged"
154                                                                                                                                 ascending:YES
155                                                                                                                                  selector:@selector(compare:)];
156        NSSortDescriptor *sortByRole = [[NSSortDescriptor alloc] initWithKey:@"role"
157                                                                                                                           ascending:NO
158                                                                                                                                selector:@selector(roleCompare:)];
159        NSSortDescriptor *sortByNick = [[NSSortDescriptor alloc] initWithKey:@"nickname"
160                                                                                                                           ascending:YES
161                                                                                                                                selector:@selector(caseInsensitiveCompare:)];
162        [m_participantsController setSortDescriptors:[NSArray arrayWithObjects:sortByGagged, sortByRole, sortByNick, nil]];
163        [sortByGagged release];
164        [sortByRole release];
165        [sortByNick release];
166       
167        // Make the participants list accept drops of contacts or contact entries
168        [m_participantsTableView registerForDraggedTypes:
169                [NSArray arrayWithObjects:LPRosterContactPboardType, LPRosterContactEntryPboardType, nil]];
170       
171        [m_participantsTableView setToolTip:NSLocalizedString(@"Drag a contact into this list to invite it to join this chat-room.", @"Group Chat participants list")];
172}
173
174
175- (void)p_startObservingGroupChatParticipants
176{
177        [[self groupChat] addObserver:self
178                                           forKeyPath:@"participants"
179                                                  options:( NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld )
180                                                  context:LPGroupChatParticipantsContext];
181       
182        NSEnumerator *participantEnum = [[[self groupChat] participants] objectEnumerator];
183        LPGroupChatContact *participant;
184        while (participant = [participantEnum nextObject])
185                [self p_startObservingGroupChatParticipant:participant];
186}
187
188- (void)p_stopObservingGroupChatParticipants
189{
190        NSEnumerator *participantEnum = [[[self groupChat] participants] objectEnumerator];
191        LPGroupChatContact *participant;
192        while (participant = [participantEnum nextObject])
193                [self p_stopObservingGroupChatParticipant:participant];
194       
195        [[self groupChat] removeObserver:self forKeyPath:@"participants"];
196}
197
198- (void)p_startObservingGroupChatParticipant:(LPGroupChatContact *)participant
199{
200        [participant addObserver:self forKeyPath:@"gagged" options:0 context:LPParticipantsAttribsContext];
201        [participant addObserver:self forKeyPath:@"role" options:0 context:LPParticipantsAttribsContext];
202        [participant addObserver:self forKeyPath:@"nickname" options:0 context:LPParticipantsAttribsContext];
203}
204
205- (void)p_stopObservingGroupChatParticipant:(LPGroupChatContact *)participant
206{
207        [participant removeObserver:self forKeyPath:@"gagged"];
208        [participant removeObserver:self forKeyPath:@"role"];
209        [participant removeObserver:self forKeyPath:@"nickname"];
210}
211
212
213- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
214{
215        if (context == LPGroupChatContext) {
216                if ([keyPath isEqualToString:@"active"] || [keyPath isEqualToString:@"myGroupChatContact.affiliation"]) {
217                        // Update menus and the toolbar as action validation results may have changed
218                        [NSApp setWindowsNeedUpdate:YES];
219                }
220                else if ([keyPath isEqualToString:@"myGroupChatContact"] || [keyPath isEqualToString:@"nickname"]) {
221                        // Our nickname in the group chat has just changed
222                        LPGroupChatContact *myGroupChatContact = [m_groupChat myGroupChatContact];
223                        if (myGroupChatContact != nil) {
224                                [m_chatViewsController setOwnerName:[myGroupChatContact userPresentableNickname]];
225                        }
226                }
227        }
228        else if (context == LPGroupChatParticipantsContext) {
229                NSKeyValueChange keyValueChange = [[change valueForKey:NSKeyValueChangeKindKey] intValue];
230               
231                if (keyValueChange == NSKeyValueChangeInsertion) {
232                        NSEnumerator *participantEnum = [[change valueForKey:NSKeyValueChangeNewKey] objectEnumerator];
233                        LPGroupChatContact *participant;
234                        while (participant = [participantEnum nextObject])
235                                [self p_startObservingGroupChatParticipant:participant];                       
236                }
237                else if (keyValueChange == NSKeyValueChangeRemoval) {
238                        NSEnumerator *participantEnum = [[change valueForKey:NSKeyValueChangeOldKey] objectEnumerator];
239                        LPGroupChatContact *participant;
240                        while (participant = [participantEnum nextObject])
241                                [self p_stopObservingGroupChatParticipant:participant];                 
242                }
243        }
244        else if (context == LPParticipantsAttribsContext) {
245                [m_participantsController rearrangeObjects];
246        }
247        else if ([keyPath isEqualToString:@"values.DisplayEmoticonImages"]) {
248                BOOL displayImages = [[object valueForKeyPath:keyPath] boolValue];
249                [m_chatViewsController showEmoticonsAsImages:displayImages];
250        }
251        else {
252                [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
253        }
254}
255
256- (LPGroupChat *)groupChat
257{
258        return [[m_groupChat retain] autorelease];
259}
260
261- (NSString *)roomJID
262{
263        return [m_groupChat roomJID];
264}
265
266- (void)p_appendSystemMessage:(NSString *)msg
267{
268        [m_chatViewsController appendDIVBlockToWebViewWithInnerHTML:[msg stringByEscapingHTMLEntities]
269                                                                                                           divClass:@"systemMessage"
270                                                                                        scrollToVisibleMode:LPScrollWithAnimationIfConvenient];
271}
272
273
274- (void)p_setupChatDocumentTitle
275{
276        NSString *timeFormat = NSLocalizedString(@"%Y-%m-%d %Hh%Mm%Ss",
277                                                                                         @"time format for chat transcripts titles and filenames");
278        NSMutableString *mutableTimeFormat = [timeFormat mutableCopy];
279       
280        // Make the timeFormat safe for filenames
281        [mutableTimeFormat replaceOccurrencesOfString:@":" withString:@"." options:0
282                                                                                        range:NSMakeRange(0, [mutableTimeFormat length])];
283        [mutableTimeFormat replaceOccurrencesOfString:@"/" withString:@"-" options:0
284                                                                                        range:NSMakeRange(0, [mutableTimeFormat length])];
285       
286        NSString *newTitle = [NSString stringWithFormat:
287                NSLocalizedString(@"Group-chat on room \"%@\" on %@", @"filename and title for saved chat transcripts"),
288                [[self groupChat] roomName],
289                [[NSDate date] descriptionWithCalendarFormat:mutableTimeFormat timeZone:nil locale:nil]];
290       
291        [m_chatViewsController setChatDocumentTitle:newTitle];
292        [mutableTimeFormat release];
293}
294
295
296#pragma mark -
297#pragma mark Actions
298
299
300- (IBAction)segmentClicked:(id)sender
301{
302        int clickedSegment = [sender selectedSegment];
303    int clickedSegmentTag = [[sender cell] tagForSegment:clickedSegment];
304       
305        if (clickedSegmentTag == 0) {    // emoticons
306                NSWindow *win = [self window];
307                NSRect  buttonFrame = [sender frame];
308                NSPoint topRight = [win convertBaseToScreen:[[sender superview] convertPoint:buttonFrame.origin
309                                                                                                                                                          toView:nil]];
310               
311                [sender setImage:[NSImage imageNamed:@"emoticonIconPressed"] forSegment:clickedSegment];
312                [(NSView *)sender display];
313                [m_chatViewsController pickEmoticonWithMenuTopRightAt:NSMakePoint(topRight.x + [sender widthForSegment:clickedSegment], topRight.y)
314                                                                                                 parentWindow:[self window]];
315                [sender setImage:[NSImage imageNamed:@"emoticonIconUnpressed"] forSegment:clickedSegment];
316                [(NSView *)sender display];
317        }
318}
319
320
321- (IBAction)sendMessage:(id)sender
322{
323        NSAttributedString *attributedMessage = [m_inputTextField attributedStringValue];
324        NSString *message = [attributedMessage stringByFlatteningAttachedEmoticons];
325       
326        // Check if the text is all made of whitespace.
327        static NSCharacterSet *requiredCharacters = nil;
328        if (requiredCharacters == nil) {
329                requiredCharacters = [[[NSCharacterSet whitespaceAndNewlineCharacterSet] invertedSet] retain];
330        }
331       
332        if ([message rangeOfCharacterFromSet:requiredCharacters].location != NSNotFound) {
333                [m_groupChat sendPlainTextMessage:message];
334        }
335       
336        // Store it in the input line history
337        if ([m_inputLineHistory count] > 0)
338                [m_inputLineHistory replaceObjectAtIndex:0 withObject:attributedMessage];
339        else
340                [m_inputLineHistory addObject:attributedMessage];
341       
342        if ([m_inputLineHistory count] > INPUT_LINE_HISTORY_ITEMS_MAX)
343                [m_inputLineHistory removeObjectsInRange:NSMakeRange(INPUT_LINE_HISTORY_ITEMS_MAX, [m_inputLineHistory count] - INPUT_LINE_HISTORY_ITEMS_MAX)];
344        [m_inputLineHistory insertObject:@"" atIndex:0];
345        m_currentInputLineHistoryEntryIndex = 0;
346       
347        // Prepare the window to take another message from the user
348        [[self window] makeFirstResponder:m_inputTextField];
349        [m_inputTextField setStringValue:@""];
350        [m_inputTextField performSelector:@selector(calcContentSize) withObject:nil afterDelay:0.0];
351}
352
353
354- (IBAction)changeTopic:(id)sender
355{
356        NSString *currentTopic = [[self groupChat] topic];
357        [m_changeTopicTextField setStringValue:(currentTopic ? currentTopic : @"")];
358       
359        [NSApp beginSheet:m_changeTopicWindow
360           modalForWindow:[self window]
361                modalDelegate:self didEndSelector:NULL contextInfo:NULL];
362}
363
364
365- (IBAction)changeTopicOKClicked:(id)sender
366{
367        [NSApp endSheet:m_changeTopicWindow];
368        [m_changeTopicWindow orderOut:nil];
369       
370        [[self groupChat] setTopic:[m_changeTopicTextField stringValue]];
371}
372
373
374- (IBAction)changeNickname:(id)sender
375{
376        NSString *currentNick = [[self groupChat] nickname];
377        [m_changeNicknameTextField setStringValue:(currentNick ? currentNick : @"")];
378        [m_changeNicknameTextField selectText:nil];
379       
380        [NSApp beginSheet:m_changeNicknameWindow
381           modalForWindow:[self window]
382                modalDelegate:self didEndSelector:NULL contextInfo:NULL];
383}
384
385
386- (IBAction)changeNicknameOKClicked:(id)sender
387{
388        [NSApp endSheet:m_changeNicknameWindow];
389        [m_changeNicknameWindow orderOut:nil];
390       
391        [[self groupChat] setNickname:[m_changeNicknameTextField stringValue]];
392}
393
394
395- (IBAction)inviteContact:(id)sender
396{
397        [m_inviteContactTextField setStringValue:@""];
398        [m_inviteContactTextField selectText:nil];
399        [m_inviteContactReasonTextField setStringValue:@""];
400       
401        [NSApp beginSheet:m_inviteContactWindow
402           modalForWindow:[self window]
403                modalDelegate:self didEndSelector:NULL contextInfo:NULL];
404}
405
406
407- (IBAction)inviteContactOKClicked:(id)sender
408{
409        [NSApp endSheet:m_inviteContactWindow];
410        [m_inviteContactWindow orderOut:nil];
411       
412        [[self groupChat] inviteJID:[m_inviteContactTextField stringValue]
413                                         withReason:[m_inviteContactReasonTextField stringValue]];
414}
415
416
417- (IBAction)startPrivateChat:(id)sender
418{
419        NSEnumerator *participantsEnum = [[m_participantsController selectedObjects] objectEnumerator];
420        LPGroupChatContact *participant;
421       
422        while (participant = [participantsEnum nextObject]) {
423                NSString                *participantJID = [participant JIDInGroupChat];
424               
425                LPAccount               *account = [[self groupChat] account];
426                LPRoster                *roster = [account roster];
427                LPContactEntry  *entry = [roster contactEntryForAddress:participantJID
428                                                                                                                account:account
429                                                          createNewHiddenWithNameIfNotFound:[participant nickname]];
430               
431                // Start a chat with this contact entry
432                if ([m_delegate respondsToSelector:@selector(groupChatController:openChatWithContactEntry:)]) {
433                        [m_delegate groupChatController:self openChatWithContactEntry:entry];
434                }
435        }
436}
437
438
439- (LPGroupChatConfigController *)p_groupChatConfigController
440{
441        if (m_configController == nil) {
442                m_configController = [[LPGroupChatConfigController alloc] initWithGroupChatController:self];
443        }
444        return m_configController;
445}
446
447
448- (IBAction)configureChatRoom:(id)sender
449{
450        NSString *myAffiliation = [[[self groupChat] myGroupChatContact] affiliation];
451       
452        if (![myAffiliation isEqualToString:@"owner"]) {
453                NSBeginAlertSheet(NSLocalizedString(@"You are not allowed to change the configuration for this room.", @""),
454                                                  NSLocalizedString(@"OK", @""), nil, nil,
455                                                  [self window], self, NULL, NULL, NULL,
456                                                  NSLocalizedString(@"The configuration can only be changed by the owner of the room.", @""));
457        }
458        else {
459                [[self p_groupChatConfigController] reloadConfigurationForm];
460               
461                [NSApp beginSheet:[[self p_groupChatConfigController] window]
462                   modalForWindow:[self window]
463                        modalDelegate:self didEndSelector:NULL contextInfo:NULL];
464        }
465}
466
467
468- (IBAction)actionSheetCancelClicked:(id)sender
469{
470        NSWindow *sheet = [[self window] attachedSheet];
471       
472        [NSApp endSheet:sheet];
473        [sheet orderOut:nil];
474}
475
476
477- (IBAction)gagContact:(id)sender
478{
479        NSArray *contacts = [m_participantsController selectedObjects];
480       
481        [m_gaggedContacts addObjectsFromArray:contacts];
482       
483        NSEnumerator            *contactEnum = [contacts objectEnumerator];
484        LPGroupChatContact      *contact;
485        while (contact = [contactEnum nextObject]) {
486                [contact setGagged:YES];
487        }
488}
489
490
491- (IBAction)ungagContact:(id)sender
492{
493        NSArray *contacts = [m_participantsController selectedObjects];
494       
495        [m_gaggedContacts minusSet:[NSSet setWithArray:contacts]];
496       
497        NSEnumerator            *contactEnum = [contacts objectEnumerator];
498        LPGroupChatContact      *contact;
499        while (contact = [contactEnum nextObject]) {
500                [contact setGagged:NO];
501        }
502}
503
504
505- (IBAction)toggleGagContact:(id)sender
506{
507        NSSet *targets = [NSSet setWithArray:[m_participantsController selectedObjects]];
508        if ([targets isSubsetOfSet:m_gaggedContacts])
509                [self ungagContact:sender];
510        else
511                [self gagContact:sender];
512}
513
514
515- (IBAction)saveDocumentTo:(id)sender
516{
517        NSSavePanel *sp = [NSSavePanel savePanel];
518       
519        [sp setCanSelectHiddenExtension:YES];
520        [sp setRequiredFileType:@"webarchive"];
521       
522        [sp beginSheetForDirectory:nil
523                                                  file:[m_chatViewsController chatDocumentTitle]
524                                modalForWindow:[self window]
525                                 modalDelegate:self
526                                didEndSelector:@selector(p_savePanelDidEnd:returnCode:contextInfo:)
527                                   contextInfo:NULL];
528}
529
530
531- (void)p_savePanelDidEnd:(NSSavePanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
532{
533        if (returnCode == NSOKButton) {
534                NSError *error;
535                if (![m_chatViewsController saveDocumentToFile:[sheet filename] hideExtension:[sheet isExtensionHidden] error:&error]) {
536                        [self presentError:error];
537                }
538        }
539}
540
541
542- (IBAction)printDocument:(id)sender
543{
544        NSPrintOperation *op = [NSPrintOperation printOperationWithView:[[[m_chatWebView mainFrame] frameView] documentView]];
545        [op runOperationModalForWindow:[self window]
546                                                  delegate:nil
547                                        didRunSelector:NULL
548                                           contextInfo:NULL];
549}
550
551
552#pragma mark Searching
553
554- (IBAction)showFindPanel:(id)sender
555{
556        [[LPChatFindPanelController sharedFindPanel] showWindow:sender];
557}
558
559- (IBAction)findNext:(id)sender
560{
561        LPChatFindPanelController *findPanel = [LPChatFindPanelController sharedFindPanel];
562        NSString *searchStr = [findPanel searchString];
563        BOOL found = NO;
564       
565        if ([searchStr length] > 0)
566                found = [m_chatWebView searchFor:searchStr direction:YES caseSensitive:NO wrap:YES];
567       
568        [findPanel searchStringWasFound:found];
569}
570
571- (IBAction)findPrevious:(id)sender
572{
573        LPChatFindPanelController *findPanel = [LPChatFindPanelController sharedFindPanel];
574        NSString *searchStr = [findPanel searchString];
575        BOOL found = NO;
576       
577        if ([searchStr length] > 0)
578                found = [m_chatWebView searchFor:searchStr direction:NO caseSensitive:NO wrap:YES];
579       
580        [findPanel searchStringWasFound:found];
581}
582
583- (IBAction)useSelectionForFind:(id)sender
584{
585        NSString *selectedString = nil;
586        id firstResponder = [[self window] firstResponder];
587       
588        if ([firstResponder isKindOfClass:[NSText class]])
589                selectedString = [[firstResponder string] substringWithRange:[firstResponder selectedRange]];
590        else if ([firstResponder isDescendantOf:m_chatWebView])
591                selectedString = [[m_chatWebView selectedDOMRange] toString];
592       
593        if ([selectedString length] > 0)
594                [[LPChatFindPanelController sharedFindPanel] setSearchString:selectedString];
595}
596
597
598#pragma mark Action Validation
599
600- (BOOL)p_validateActionWithSelector:(SEL)action
601{
602        if (action == @selector(configureChatRoom:)) {
603                return [[[[self groupChat] myGroupChatContact] affiliation] isEqualToString:@"owner"];
604        }
605        else if (action == @selector(startPrivateChat:)) {
606                return ([[m_participantsController selectedObjects] count] > 0);
607        }
608        else if (action == @selector(gagContact:)) {
609                NSSet *targets = [NSSet setWithArray:[m_participantsController selectedObjects]];
610                return ([targets count] > 0 && ![targets isSubsetOfSet:m_gaggedContacts]);
611        }
612        else if (action == @selector(ungagContact:)) {
613                NSSet *targets = [NSSet setWithArray:[m_participantsController selectedObjects]];
614                return ([targets count] > 0 && [targets intersectsSet:m_gaggedContacts]);
615        }
616        else if (action == @selector(changeTopic:) ||
617                         action == @selector(changeNickname:) ||
618                         action == @selector(inviteContact:)) {
619                return [[self groupChat] isActive];
620        }
621        else if (action == @selector(useSelectionForFind:)) {
622                id firstResponder = [[self window] firstResponder];
623               
624                if ([firstResponder isKindOfClass:[NSText class]])
625                        return ([firstResponder selectedRange].length > 0);
626                else if ([firstResponder isDescendantOf:m_chatWebView])
627                        return ([[[m_chatWebView selectedDOMRange] toString] length] > 0);
628                else
629                        return NO;
630        }
631        else {
632                return YES;
633        }
634}
635
636
637- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
638{
639        SEL action = [menuItem action];
640       
641        if (action == @selector(toggleGagContact:)) {
642                NSSet *targets = [NSSet setWithArray:[m_participantsController selectedObjects]];
643               
644                if ([targets isSubsetOfSet:m_gaggedContacts])
645                        [menuItem setState:NSOnState];
646                else if (![targets intersectsSet:m_gaggedContacts])
647                        [menuItem setState:NSOffState];
648                else
649                        [menuItem setState:NSMixedState];
650        }
651       
652        return [self p_validateActionWithSelector:action];
653}
654
655
656#pragma mark -
657#pragma mark LPGroupChat Delegate Methods
658
659
660- (void)groupChat:(LPGroupChat *)chat didReceiveMessage:(NSString *)msg fromContact:(LPGroupChatContact *)contact
661{
662        if (![contact isGagged]) {
663                NSString *messageHTML = [m_chatViewsController HTMLifyRawMessageString:msg];
664                NSString *authorName = (contact ? [contact userPresentableNickname] : @"");
665                NSString *htmlString = [m_chatViewsController HTMLStringForStandardBlockWithInnerHTML:messageHTML
666                                                                                                                                                                        timestamp:[NSDate date]
667                                                                                                                                                                   authorName:authorName];
668               
669                // if it's an outbound message, also scroll down so that the user can see what he has just written
670                [m_chatViewsController appendDIVBlockToWebViewWithInnerHTML:htmlString
671                                                                                                                   divClass:@"messageBlock"
672                                                                                                scrollToVisibleMode:LPScrollWithAnimationIfConvenient];
673        }
674}
675
676- (void)groupChat:(LPGroupChat *)chat didReceiveSystemMessage:(NSString *)msg
677{
678        [self p_appendSystemMessage:msg];
679}
680
681- (void)groupChat:(LPGroupChat *)chat unableToProceedDueToWrongPasswordWithErrorMessage:(NSString *)msg
682{
683        [self p_appendSystemMessage:msg];
684       
685        [m_passwordPromptTextField selectText:nil];
686       
687        [NSApp beginSheet:m_passwordPromptWindow
688           modalForWindow:[self window]
689                modalDelegate:self didEndSelector:NULL contextInfo:NULL];
690}
691
692- (IBAction)passwordPromptOKClicked:(id)sender
693{
694        [NSApp endSheet:m_passwordPromptWindow];
695        [m_passwordPromptWindow orderOut:nil];
696       
697        [[self groupChat] retryJoinWithNickname:[[self groupChat] nickname]
698                                                                   password:[m_passwordPromptTextField stringValue]];
699}
700
701- (void)groupChat:(LPGroupChat *)chat unableToProceedDueToNicknameAlreadyInUseWithErrorMessage:(NSString *)msg
702{
703        [self p_appendSystemMessage:msg];
704       
705        NSString *currentNick = [[self groupChat] nickname];
706        if (currentNick == nil) currentNick = @"";
707        NSString *lastSetNick = [[self groupChat] lastSetNickname];
708        if (lastSetNick == nil) lastSetNick = @"";
709       
710        NSString *labelFormatString = NSLocalizedString(@"The nickname \"%@\" is already in use on this server."
711                                                                                                        @" Please choose an alternate nickname to proceed.",
712                                                                                                        @"Chat room duplicate nickname error");
713       
714        [m_alternateNicknamePromptLabel setStringValue:[NSString stringWithFormat:labelFormatString, lastSetNick]];
715        [m_alternateNicknamePromptTextField setStringValue:currentNick];
716        [m_alternateNicknamePromptTextField selectText:nil];
717       
718        [NSApp beginSheet:m_alternateNicknamePromptWindow
719           modalForWindow:[self window]
720                modalDelegate:self didEndSelector:NULL contextInfo:NULL];
721}
722
723- (IBAction)alternateNicknameOKClicked:(id)sender
724{
725        [NSApp endSheet:m_alternateNicknamePromptWindow];
726        [m_alternateNicknamePromptWindow orderOut:nil];
727       
728        LPGroupChat *gc = [self groupChat];
729       
730        if ([gc isActive]) {
731                [gc setNickname:[m_alternateNicknamePromptTextField stringValue]];
732        }
733        else {
734                [gc retryJoinWithNickname:[m_alternateNicknamePromptTextField stringValue]
735                                                 password:[[self groupChat] lastUsedPassword]];
736        }
737}
738
739- (void)groupChat:(LPGroupChat *)chat didReceiveRoomConfigurationForm:(NSString *)configFormXML errorMessage:(NSString *)errorMsg
740{
741        [m_configController takeReceivedRoomConfigurationForm:configFormXML errorMessage:errorMsg];
742}
743
744- (void)groupChat:(LPGroupChat *)chat didReceiveResultOfRoomConfigurationModification:(BOOL)succeeded errorMessage:(NSString *)errorMsg
745{
746        [m_configController takeResultOfRoomConfigurationModification:succeeded errorMessage:errorMsg];
747}
748
749- (void)groupChat:(LPGroupChat *)chat didInviteJID:(NSString *)jid withReason:(NSString *)reason
750{
751        // Append a "system message" to the chat transcript
752        NSString *msgFormat = NSLocalizedString(@"An invitation to join this chat has been sent to <%@>%@.",
753                                                                                        @"System message: invitation for group chat was sent");
754        NSString *msg = [NSString stringWithFormat:msgFormat, jid,
755                ([reason length] > 0 ?
756                 [NSString stringWithFormat:@" with reason \"%@\"", reason] :
757                 @"")];
758       
759        [self p_appendSystemMessage:msg];
760}
761
762#pragma mark -
763#pragma mark NSResponder Methods
764
765
766- (void)keyDown:(NSEvent *)theEvent
767{
768        /* If a keyDown event reaches this low in the responder chain then it means that no text field is
769        active to process the event. Activate the input text field and reroute the event that was received
770        back to it. */
771        if ([m_inputTextField canBecomeKeyView]) {
772                NSWindow *window = [self window];
773                [window makeFirstResponder:m_inputTextField];
774                [[window firstResponder] keyDown:theEvent];
775        } else {
776                [super keyDown:theEvent];
777        }
778}
779
780
781#pragma mark -
782#pragma mark Private Methods
783
784
785- (void)p_resizeInputFieldToContentsSize:(NSSize)newSize
786{
787        // Determine the new window frame
788        float   heightDifference = newSize.height - NSHeight([m_inputTextField bounds]);
789       
790        if ((heightDifference > 0.5) || (heightDifference < -0.5)) {
791                NSRect  newWindowFrame = [[self window] frame];
792               
793                newWindowFrame.size.height += heightDifference;
794                newWindowFrame.origin.y -= heightDifference;
795               
796                // Make sure the window is completely enclosed within the screen rect
797                NSRect screenRect = [[[self window] screen] visibleFrame];
798               
799                if (NSContainsRect(screenRect, newWindowFrame) == NO) {
800                        float dX = 0.0, dY = 0.0;
801                       
802                        if (NSMinX(screenRect) > NSMinX(newWindowFrame)) {
803                                dX = NSMinX(screenRect) - NSMinX(newWindowFrame);
804                        }
805                        else if (NSMaxX(screenRect) < NSMaxX(newWindowFrame)) {
806                                dX = NSMaxX(screenRect) - NSMaxX(newWindowFrame);
807                        }
808                       
809                        if (NSMinY(screenRect) > NSMinY(newWindowFrame)) {
810                                dY = NSMinY(screenRect) - NSMinY(newWindowFrame);
811                        }
812                        else if (NSMaxY(screenRect) < NSMaxY(newWindowFrame)) {
813                                dY = NSMaxY(screenRect) - NSMaxY(newWindowFrame);
814                        }
815                       
816                        newWindowFrame = NSOffsetRect(newWindowFrame, dX, dY);
817                }
818               
819                // Do the actual resizing
820                unsigned int splitViewResizeMask = [m_chatTranscriptSplitView autoresizingMask];
821                unsigned int inputBoxResizeMask = [m_inputControlsBar autoresizingMask];
822               
823                [m_chatTranscriptSplitView setAutoresizingMask:NSViewMinYMargin];
824                [m_inputControlsBar setAutoresizingMask:NSViewHeightSizable];
825               
826                [[self window] setFrame:newWindowFrame display:YES animate:YES];
827               
828                [m_inputControlsBar setAutoresizingMask:inputBoxResizeMask];
829                [m_chatTranscriptSplitView setAutoresizingMask:splitViewResizeMask];
830        }
831}
832
833
834#pragma mark -
835#pragma mark NSWindow Delegate Methods
836
837
838- (BOOL)windowShouldClose:(id)sender
839{
840        // Prevent accidental closing of the window
841        NSBeginCriticalAlertSheet(NSLocalizedString(@"Are you sure you want to close this window?", @""),
842                                                          NSLocalizedString(@"Close", @""), NSLocalizedString(@"Cancel", @""), nil,
843                                                          [self window], self, @selector(p_windowCloseConfirmationSheetDidEnd:returnCode:contextInfo:), NULL, NULL,
844                                                          NSLocalizedString(@"Closing the window will result in leaving the chat room \"%@\".", @""),
845                                                          [[self groupChat] roomName]);
846        return NO;
847}
848
849- (void)p_windowCloseConfirmationSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
850{
851        if (returnCode == NSAlertDefaultReturn) {
852                [[self window] close];
853        }
854}
855
856- (void)windowWillClose:(NSNotification *)aNotification
857{
858        // If the WebView hasn't finished loading when the window is closed (extremely rare, but could happen), then we don't
859        // want to do any of the setup that is about to happen in our frame load delegate methods, since the window is going away
860        // anyway. If we allowed that setup to happen when the window is already closed it could originate some crashes, since
861        // most of the stuff was already released by the time the delegate methods get called.
862        [m_chatWebView setFrameLoadDelegate:nil];
863        [m_chatWebView setUIDelegate:nil];
864       
865        [m_groupChat endGroupChat];
866       
867        if ([m_delegate respondsToSelector:@selector(groupChatControllerWindowWillClose:)]) {
868                [m_delegate groupChatControllerWindowWillClose:self];
869        }
870}
871
872
873#pragma mark -
874#pragma mark WebView Frame Load Delegate Methods
875
876
877- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame
878{
879        [self p_setupChatDocumentTitle];
880       
881        [m_chatViewsController dumpQueuedMessagesToWebView];
882        [m_chatViewsController showEmoticonsAsImages:[[NSUserDefaults standardUserDefaults] boolForKey:@"DisplayEmoticonImages"]];
883}
884
885
886#pragma mark WebView UI Delegate Methods
887
888
889- (NSArray *)webView:(WebView *)sender contextMenuItemsForElement:(NSDictionary *)element defaultMenuItems:(NSArray *)defaultMenuItems
890{
891        if (sender == m_chatWebView) {
892                NSMutableArray  *itemsToReturn = [NSMutableArray array];
893                NSEnumerator    *enumerator = [defaultMenuItems objectEnumerator];
894                id                              menuItem;
895               
896                while ((menuItem = [enumerator nextObject]) != nil) {
897                        switch ([menuItem tag]) {
898                                case WebMenuItemTagCopyLinkToClipboard:
899                                case WebMenuItemTagCopy:
900                                case WebMenuItemTagSpellingGuess:
901                                case WebMenuItemTagNoGuessesFound:
902                                case WebMenuItemTagIgnoreSpelling:
903                                case WebMenuItemTagLearnSpelling:
904                                case WebMenuItemTagOther:
905                                        [itemsToReturn addObject:menuItem];
906                                        break;
907                        }
908                }
909               
910                return itemsToReturn;
911        }
912        else {
913                return [NSArray array];
914        }
915}
916
917
918- (unsigned)webView:(WebView *)sender dragDestinationActionMaskForDraggingInfo:(id <NSDraggingInfo>)draggingInfo
919{
920        // We don't want the WebView to process anything dropped on it
921        return WebDragDestinationActionNone;
922}
923
924
925- (unsigned)webView:(WebView *)sender dragSourceActionMaskForPoint:(NSPoint)point
926{
927        return WebDragSourceActionAny;
928}
929
930
931#pragma mark -
932
933#pragma mark NSControl Delegate Methods
934
935- (void)controlTextDidChange:(NSNotification *)aNotification
936{
937        m_currentInputLineHistoryEntryIndex = 0;
938}
939
940
941- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)command
942{
943        if (command == @selector(pageDown:)                                             || command == @selector(pageUp:)                                ||
944                command == @selector(scrollPageDown:)                           || command == @selector(scrollPageUp:)                  ||
945                command == @selector(moveToBeginningOfDocument:)        || command == @selector(moveToEndOfDocument:)   ||
946                /* The following two selectors are undocumented. They're used by Cocoa to represent a Home or End key press. */
947                command == @selector(scrollToBeginningOfDocument:)      || command == @selector(scrollToEndOfDocument:)  )
948        {
949                [[[m_chatWebView mainFrame] frameView] doCommandBySelector:command];
950                return YES;
951        }
952        else if (command == @selector(moveUp:) || command == @selector(moveDown:)) {
953               
954                if (m_currentInputLineHistoryEntryIndex == 0) {
955                        if ([m_inputLineHistory count] > 0) {
956                                [m_inputLineHistory replaceObjectAtIndex:0 withObject:[m_inputTextField attributedStringValue]];
957                        }
958                        else {
959                                [m_inputLineHistory addObject:[m_inputTextField attributedStringValue]];
960                        }
961                }
962               
963                if (command == @selector(moveUp:))
964                        m_currentInputLineHistoryEntryIndex = (m_currentInputLineHistoryEntryIndex + 1) % [m_inputLineHistory count];
965                else
966                        m_currentInputLineHistoryEntryIndex = (m_currentInputLineHistoryEntryIndex > 0 ?
967                                                                                                   m_currentInputLineHistoryEntryIndex :
968                                                                                                   [m_inputLineHistory count]) - 1;
969               
970                [m_inputTextField setAttributedStringValue:[m_inputLineHistory objectAtIndex:m_currentInputLineHistoryEntryIndex]];
971                [m_inputTextField performSelector:@selector(calcContentSize) withObject:nil afterDelay:0.0];
972               
973                return YES;
974        }
975        else {
976                return NO;
977        }
978}
979
980
981#pragma mark LPGrowingTextField Delegate Methods
982
983- (void)growingTextField:(LPGrowingTextField *)textField contentSizeDidChange:(NSSize)neededSize
984{
985        [self p_resizeInputFieldToContentsSize:neededSize];
986}
987
988
989#pragma mark -
990#pragma mark NSTableView Delegate Methods
991
992
993- (int)numberOfRowsInTableView:(NSTableView *)aTableView
994{
995        return 0;
996}
997
998
999- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
1000{
1001        return nil;
1002}
1003
1004
1005- (BOOL)tableView:(NSTableView *)tableView shouldShowCellExpansionForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
1006{
1007        return NO;
1008}
1009
1010
1011- (NSString *)tableView:(NSTableView *)aTableView toolTipForCell:(NSCell *)aCell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)aTableColumn row:(int)row mouseLocation:(NSPoint)mouseLocation
1012{
1013        LPGroupChatContact *contact = [[m_participantsController arrangedObjects] objectAtIndex:row];
1014        NSString *realJID = [contact realJID];
1015        NSString *statusMessage = [contact statusMessage];
1016       
1017        return [NSString stringWithFormat:@"%@\n%@%@, %@\n\n%@%@%@",
1018                [contact nickname],
1019                ([contact isGagged] ? [NSString stringWithFormat:@"%@\n", NSLocalizedString(@"(gagged)", @"")] : @""),
1020                [contact role], [contact affiliation],
1021                ([realJID length] > 0 ? [NSString stringWithFormat:@"%@\n\n", realJID] : @""),
1022                NSLocalizedStringFromTable(LPStatusStringFromStatus([contact status]), @"Status", @""),
1023                ([statusMessage length] > 0 ? [NSString stringWithFormat:@" : \"%@\"", statusMessage] : @"")];
1024}
1025
1026
1027- (void)tableView:(NSTableView *)aTableView willDisplayCell:(id)aCell forTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
1028{
1029        LPGroupChatContact *contact = [[m_participantsController arrangedObjects] objectAtIndex:rowIndex];
1030        NSString *role = [contact role];
1031       
1032        NSColor *textColor = nil;
1033       
1034        if ([[aTableView selectedRowIndexes] containsIndex:rowIndex]) {
1035                textColor = [NSColor whiteColor];
1036        }
1037        else {
1038                if ([role isEqualToString:@"moderator"]) {
1039                        textColor = [NSColor redColor];
1040                }
1041                else if ([role isEqualToString:@"visitor"]) {
1042                        textColor = [NSColor grayColor];
1043                }
1044                else {  // @"participant"
1045                        textColor = [NSColor blackColor];
1046                }
1047               
1048                if ([contact isGagged]) {
1049                        textColor = [textColor blendedColorWithFraction:0.5 ofColor:[NSColor whiteColor]];
1050                }
1051        }
1052       
1053        [aCell setTextColor:textColor];
1054}
1055
1056
1057//- (BOOL)tableView:(NSTableView *)aTableView writeRows:(NSArray *)rows toPasteboard:(NSPasteboard *)pboard
1058//{
1059//      // This method is deprecated in 10.4, but the alternative doesn't exist on 10.3, so we have to use this one.
1060//     
1061//}
1062
1063
1064- (NSDragOperation)tableView:(NSTableView *)aTableView validateDrop:(id <NSDraggingInfo>)info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)operation
1065{
1066        NSDragOperation         resultOp = NSDragOperationNone;
1067        NSArray                         *draggedTypes = [[info draggingPasteboard] types];
1068        NSArray                         *itemsBeingDragged = nil;
1069       
1070        if ([draggedTypes containsObject:LPRosterContactEntryPboardType]) {
1071                itemsBeingDragged = LPRosterContactEntriesBeingDragged([info draggingPasteboard]);
1072        }
1073        else if ([draggedTypes containsObject:LPRosterContactPboardType]) {
1074                itemsBeingDragged = LPRosterContactsBeingDragged([info draggingPasteboard]);
1075        }
1076       
1077        if ([itemsBeingDragged someOnlineItemInArrayPassesCapabilitiesPredicate:@selector(canDoMUC)]) {
1078                resultOp = NSDragOperationGeneric;
1079                // Highlight the whole table
1080                [aTableView setDropRow:-1 dropOperation:NSTableViewDropOn];
1081        }
1082       
1083        return resultOp;
1084}
1085
1086
1087- (BOOL)tableView:(NSTableView *)aTableView acceptDrop:(id <NSDraggingInfo>)info row:(int)row dropOperation:(NSTableViewDropOperation)operation
1088{
1089        NSPasteboard            *pboard = [info draggingPasteboard];
1090        NSArray                         *draggedTypes = [pboard types];
1091        NSDragOperation         dragOpMask = [info draggingSourceOperationMask];
1092       
1093        if (dragOpMask & NSDragOperationGeneric) {
1094                if ([draggedTypes containsObject:LPRosterContactEntryPboardType]) {
1095                        NSArray                 *entriesBeingDragged = LPRosterContactEntriesBeingDragged([info draggingPasteboard]);
1096                       
1097                        NSEnumerator    *entriesEnum = [entriesBeingDragged objectEnumerator];
1098                        LPContactEntry  *entry;
1099                       
1100                        while (entry = [entriesEnum nextObject])
1101                                if ([entry canDoMUC])
1102                                        [[self groupChat] inviteJID:[entry address] withReason:@""];
1103                }
1104                else if ([draggedTypes containsObject:LPRosterContactPboardType]) {
1105                        NSArray                 *contactsBeingDragged = LPRosterContactsBeingDragged([info draggingPasteboard]);
1106                        NSEnumerator    *contactsEnum = [contactsBeingDragged objectEnumerator];
1107                        LPContact               *contact;
1108                       
1109                        while (contact = [contactsEnum nextObject]) {
1110                                LPContactEntry *entry = [[contact contactEntries] firstOnlineItemInArrayPassingCapabilitiesPredicate:@selector(canDoMUC)];
1111                                if (entry)
1112                                        [[self groupChat] inviteJID:[entry address] withReason:@""];
1113                        }
1114                }
1115        }
1116               
1117        return YES;
1118}
1119
1120
1121#pragma mark -
1122#pragma mark NSSplitView Delegate Methods
1123
1124
1125- (void)splitView:(NSSplitView *)sender resizeSubviewsWithOldSize:(NSSize)oldSize
1126{
1127        NSAssert(([[sender subviews] count] == 2), @"We were expecting exactly 2 views inside the NSSplitView!");
1128       
1129        NSView *leftPane = [[sender subviews] objectAtIndex:0];
1130        NSView *rightPane = [[sender subviews] objectAtIndex:1];
1131        NSRect newLeftPaneFrame = [leftPane frame];
1132        NSRect newRightPaneFrame = [rightPane frame];
1133        NSRect newSplitViewFrame = [sender frame];
1134       
1135        float widthDelta = NSWidth(newSplitViewFrame) - oldSize.width;
1136       
1137        newLeftPaneFrame.size.height = newRightPaneFrame.size.height = newSplitViewFrame.size.height;
1138        newRightPaneFrame.origin.x += widthDelta;
1139        newLeftPaneFrame.size.width += widthDelta;
1140       
1141        [leftPane setFrame:newLeftPaneFrame];
1142        [rightPane setFrame:newRightPaneFrame];
1143}
1144
1145
1146#pragma mark -
1147#pragma mark NSToolbar Methods
1148
1149
1150- (void)p_setupToolbar 
1151{
1152        // Create a new toolbar instance
1153        NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier:@"LPGroupChatToolbar"];
1154       
1155        //[toolbar setDisplayMode:NSToolbarDisplayModeIconOnly];
1156        [toolbar setSizeMode:NSToolbarSizeModeSmall];
1157       
1158        // Set up toolbar properties: Allow customization, give a default display mode, and remember state in user defaults
1159        [toolbar setAllowsUserCustomization:YES];
1160        [toolbar setAutosavesConfiguration:YES];
1161       
1162        // We are the delegate.
1163        [toolbar setDelegate:self];
1164       
1165        // Attach the toolbar to the window.
1166        [[self window] setToolbar:toolbar];
1167        [toolbar release];
1168}
1169
1170
1171- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)identifier willBeInsertedIntoToolbar:(BOOL)willBeInserted 
1172{
1173        // Create our toolbar items.
1174    NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:identifier];
1175       
1176        if ([identifier isEqualToString:ToolbarSetTopicIdentifier])
1177        {
1178                [item setLabel:NSLocalizedString(@"Set Topic", @"toolbar button label")];
1179                [item setPaletteLabel:NSLocalizedString(@"Set Topic", @"toolbar button label")];
1180                [item setImage:[NSImage imageNamed:@"iconMUCtopic"]];
1181                [item setToolTip:NSLocalizedString(@"Change the topic of this chat-room.", @"toolbar button")];
1182                [item setAction:@selector(changeTopic:)];
1183                [item setTarget:self];
1184        }
1185        else if ([identifier isEqualToString:ToolbarSetNicknameIdentifier])
1186        {
1187                [item setLabel:NSLocalizedString(@"Set Nickname", @"toolbar button label")];
1188                [item setPaletteLabel:NSLocalizedString(@"Set Nickname", @"toolbar button label")];
1189                [item setImage:[NSImage imageNamed:@"NSApplicationIcon"]];
1190                [item setToolTip:NSLocalizedString(@"Change your nickname on this chat-room.", @"toolbar button")];
1191                [item setAction:@selector(changeNickname:)];
1192                [item setTarget:self];
1193        }
1194        else if ([identifier isEqualToString:ToolbarInviteIdentifier])
1195        {
1196                [item setLabel:NSLocalizedString(@"Invite", @"toolbar button label")];
1197                [item setPaletteLabel:NSLocalizedString(@"Invite Contact", @"toolbar button label")];
1198                [item setImage:[NSImage imageNamed:@"iconMUCinvite"]];
1199                [item setToolTip:NSLocalizedString(@"Invite another contact to this chat-room.", @"toolbar button")];
1200                [item setAction:@selector(inviteContact:)];
1201                [item setTarget:self];
1202        }
1203        else if ([identifier isEqualToString:ToolbarPrivateChatIdentifier])
1204        {
1205                [item setLabel:NSLocalizedString(@"Private Chat", @"toolbar button label")];
1206                [item setPaletteLabel:NSLocalizedString(@"Start Private Chat", @"toolbar button label")];
1207                [item setImage:[NSImage imageNamed:@"iconMUCpvt"]];
1208                [item setToolTip:NSLocalizedString(@"Start a private chat with another participant of this chat-room.", @"toolbar button")];
1209                [item setAction:@selector(startPrivateChat:)];
1210                [item setTarget:self];
1211        }
1212        else if ([identifier isEqualToString:ToolbarConfigRoomIdentifier])
1213        {
1214                [item setLabel:NSLocalizedString(@"Configure Room", @"toolbar button label")];
1215                [item setPaletteLabel:NSLocalizedString(@"Configure Room", @"toolbar button label")];
1216                [item setImage:[NSImage imageNamed:@"NSApplicationIcon"]];
1217                [item setToolTip:NSLocalizedString(@"Configure this chat-room.", @"toolbar button")];
1218                [item setAction:@selector(configureChatRoom:)];
1219                [item setTarget:self];
1220        }
1221        else
1222        {
1223                // Invalid identifier!
1224                NSLog(@"WARNING: Invalid toolbar item identifier: %@", identifier);
1225                item = nil;
1226        }
1227       
1228        return [item autorelease];
1229}
1230
1231
1232- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar *)toolbar 
1233{
1234    return [NSArray arrayWithObjects:
1235                ToolbarSetTopicIdentifier,
1236                ToolbarSetNicknameIdentifier,
1237                ToolbarInviteIdentifier,
1238                ToolbarPrivateChatIdentifier,
1239                NSToolbarSeparatorItemIdentifier,
1240                ToolbarConfigRoomIdentifier,
1241                NSToolbarSeparatorItemIdentifier,
1242                NSToolbarPrintItemIdentifier,
1243                NSToolbarFlexibleSpaceItemIdentifier,
1244                NSToolbarSeparatorItemIdentifier,
1245                NSToolbarCustomizeToolbarItemIdentifier,
1246                nil];
1247}
1248
1249
1250- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar 
1251{       
1252    return [NSArray arrayWithObjects:
1253                ToolbarSetTopicIdentifier,
1254                ToolbarSetNicknameIdentifier,
1255                ToolbarInviteIdentifier,
1256                ToolbarPrivateChatIdentifier,
1257                ToolbarConfigRoomIdentifier,
1258                NSToolbarCustomizeToolbarItemIdentifier,
1259                NSToolbarFlexibleSpaceItemIdentifier,
1260                NSToolbarSeparatorItemIdentifier,
1261                NSToolbarSpaceItemIdentifier,
1262                NSToolbarPrintItemIdentifier,
1263                nil];
1264}
1265
1266
1267- (BOOL)validateToolbarItem:(NSToolbarItem *)theItem
1268{
1269        SEL action = [theItem action];
1270       
1271        if (action == @selector(configureChatRoom:)) {
1272                BOOL isOwner = [[[[self groupChat] myGroupChatContact] affiliation] isEqualToString:@"owner"];
1273                [theItem setToolTip:( isOwner ?
1274                                                          NSLocalizedString(@"Configure this chat-room.", @"toolbar button") :
1275                                                          NSLocalizedString(@"You must be the room owner in order to be allowed to configure it.", @"toolbar button") )];
1276        }
1277       
1278        return [self p_validateActionWithSelector:action];
1279}
1280
1281
1282@end
Note: See TracBrowser for help on using the browser.