root/trunk/Inspectors/JVChatRoomInspector.m

Revision 3763, 16.1 kB (checked in by timothy, 11 months ago)

Rip out 10.3.9 support and worarounds. Mac OS 10.4 is now required.

Line 
1 #import "JVChatRoomInspector.h"
2 #import "JVChatRoomMember.h"
3 #import "JVDirectChatPanel.h"
4 #import "JVChatTranscriptPanel.h"
5
6 @interface JVChatTranscriptPanel (JVChatTranscriptPrivate)
7 - (NSMenu *) _stylesMenu;
8 - (NSMenu *) _emoticonsMenu;
9 @end
10
11 #pragma mark -
12
13 @interface JVDirectChatPanel (JVDirectChatPrivate)
14 - (NSMenu *) _encodingMenu;
15 @end
16
17 #pragma mark -
18
19 @interface JVChatRoomInspector (JVChatRoomInspectorPrivate)
20 - (void) _topicChanged:(NSNotification *) notification;
21 - (void) _reloadTopic;
22 - (void) _refreshEditStatus:(NSNotification *) notification;
23 - (void) _roomModeChanged:(NSNotification *)notification;
24 @end
25
26 #pragma mark -
27
28 @implementation JVChatRoomPanel (JVChatRoomInspection)
29 - (id <JVInspector>) inspector {
30         return [[[JVChatRoomInspector alloc] initWithRoom:self] autorelease];
31 }
32 @end
33
34 #pragma mark -
35
36 @implementation JVChatRoomInspector
37 - (id) initWithRoom:(JVChatRoomPanel *) room {
38         if( ( self = [self init] ) )
39                 _room = [room retain];
40         return self;
41 }
42
43 - (void) dealloc {
44         [[NSNotificationCenter defaultCenter] removeObserver:self];
45
46         [banRules setDataSource:nil];
47         [banRules setDelegate:nil];
48
49         [_room release];
50         [_latestBanList release];
51
52         _room = nil;
53         _latestBanList = nil;
54
55         [super dealloc];
56 }
57
58 #pragma mark -
59
60 - (NSView *) view {
61         if( ! _nibLoaded ) _nibLoaded = [NSBundle loadNibNamed:@"JVRoomInspector" owner:self];
62         return view;
63 }
64
65 - (NSSize) minSize {
66         return NSMakeSize( 275., 340. );
67 }
68
69 - (NSString *) title {
70         return [_room title];
71 }
72
73 - (NSString *) type {
74         return NSLocalizedString( @"Room", "chat room inspector type" );
75 }
76
77 - (void) willLoad {
78         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _topicChanged: ) name:MVChatRoomTopicChangedNotification object:[_room target]];
79         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _refreshEditStatus: ) name:MVChatRoomUserModeChangedNotification object:[_room target]];
80         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _roomModeChanged: ) name:MVChatRoomModesChangedNotification object:[_room target]];
81         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _refreshEditStatus: ) name:MVChatRoomKickedNotification object:[_room target]];
82         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( refreshBanList: ) name:MVChatRoomBannedUsersSyncedNotification object:[_room target]];
83
84         [[_room connection] sendRawMessage:[NSString stringWithFormat:@"MODE %@", (MVChatRoom *)[_room target]]];
85
86         [nameField setStringValue:[_room title]];
87
88         NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];
89         [formatter setFormatterBehavior:NSDateFormatterBehavior10_4];
90         [formatter setDateStyle:NSDateFormatterShortStyle];
91         [formatter setTimeStyle:NSDateFormatterShortStyle];
92
93         if( [[_room target] isJoined] )
94                 [infoField setObjectValue:[NSString stringWithFormat:NSLocalizedString( @"Joined: %@", "chat room joined date label" ), [formatter stringFromDate:[[_room target] dateJoined]]]];
95         else [infoField setObjectValue:[NSString stringWithFormat:NSLocalizedString( @"Parted: %@", "chat room parted date label" ), [formatter stringFromDate:[[_room target] dateParted]]]];
96
97         [encodingSelection setMenu:[_room _encodingMenu]];
98         [styleSelection setMenu:[_room _stylesMenu]];
99         [emoticonSelection setMenu:[_room _emoticonsMenu]];
100
101         [self _reloadTopic];
102         [self _roomModeChanged:nil];
103         [self refreshBanList:nil];
104 }
105
106 - (BOOL) shouldUnload {
107         [[view window] makeFirstResponder:view];
108         return YES;
109 }
110
111 #pragma mark -
112
113 - (IBAction) changeChatOption:(id) sender {
114         if( sender == requiresPassword ) {
115                 [password setEnabled:(BOOL)[sender state]];
116                 if( [sender state] ) [[password window] makeFirstResponder:password];
117                 else [(MVChatRoom *)[_room target] removeMode:MVChatRoomPassphraseToJoinMode];
118         } else if( sender == limitMembers ) {
119                 [memberLimit setEnabled:(BOOL)[sender state]];
120                 if( [sender state] ) [[memberLimit window] makeFirstResponder:memberLimit];
121                 else [(MVChatRoom *)[_room target] removeMode:MVChatRoomLimitNumberOfMembersMode];
122         } else if( sender == password ) {
123                 BOOL enabled = ( [[sender stringValue] length] ? YES : NO );
124                 if( enabled ) [(MVChatRoom *)[_room target] setMode:MVChatRoomPassphraseToJoinMode withAttribute:[sender stringValue]];
125                 else {
126                         [(MVChatRoom *)[_room target] removeMode:MVChatRoomPassphraseToJoinMode];
127                         [requiresPassword setState:NSOffState];
128                         [sender setEnabled:NO];
129                 }
130         } else if( sender == memberLimit ) {
131                 BOOL enabled = ( [sender intValue] > 1 ? YES : NO );
132                 if( enabled ) [(MVChatRoom *)[_room target] setMode:MVChatRoomLimitNumberOfMembersMode withAttribute:[sender stringValue]];
133                 else {
134                         [(MVChatRoom *)[_room target] removeMode:MVChatRoomLimitNumberOfMembersMode];
135                         [limitMembers setState:NSOffState];
136                         [sender setEnabled:NO];
137                 }
138         } else if( [sender selectedCell] == privateRoom ) {
139                 if( [[sender selectedCell] state] ) [(MVChatRoom *)[_room target] setMode:MVChatRoomPrivateMode];
140                 else [(MVChatRoom *)[_room target] removeMode:MVChatRoomPrivateMode];
141         } else if( [sender selectedCell] == secretRoom ) {
142                 if( [[sender selectedCell] state] ) [(MVChatRoom *)[_room target] setMode:MVChatRoomSecretMode];
143                 else [(MVChatRoom *)[_room target] removeMode:MVChatRoomSecretMode];
144         } else if( [sender selectedCell] == inviteOnly ) {
145                 if( [[sender selectedCell] state] ) [(MVChatRoom *)[_room target] setMode:MVChatRoomInviteOnlyMode];
146                 else [(MVChatRoom *)[_room target] removeMode:MVChatRoomInviteOnlyMode];
147         } else if( [sender selectedCell] == noOutside ) {
148                 if( [[sender selectedCell] state] ) [(MVChatRoom *)[_room target] setMode:MVChatRoomNoOutsideMessagesMode];
149                 else [(MVChatRoom *)[_room target] removeMode:MVChatRoomNoOutsideMessagesMode];
150         } else if( [sender selectedCell] == topicChangeable ) {
151                 if( [[sender selectedCell] state] ) [(MVChatRoom *)[_room target] setMode:MVChatRoomOperatorsOnlySetTopicMode];
152                 else [(MVChatRoom *)[_room target] removeMode:MVChatRoomOperatorsOnlySetTopicMode];
153         } else if( [sender selectedCell] == moderated ) {
154                 if( [[sender selectedCell] state] ) [(MVChatRoom *)[_room target] setMode:MVChatRoomNormalUsersSilencedMode];
155                 else [(MVChatRoom *)[_room target] removeMode:MVChatRoomNormalUsersSilencedMode];
156         }
157 }
158
159 - (IBAction) refreshBanList:(id) sender {
160         [_latestBanList autorelease];
161         _latestBanList = [[[[_room target] bannedUsers] allObjects] mutableCopy];
162
163         SEL sortSelector = NULL;
164         int sortKey = [[NSUserDefaults standardUserDefaults] integerForKey:@"JVChatRoomInspectorBanListSort"];
165         switch( sortKey ) {
166                 default:
167                 case 1: sortSelector = @selector( compareByNickname: ); break;
168                 case 2: sortSelector = @selector( compareByUsername: ); break;
169                 case 3: sortSelector = @selector( compareByAddress: );
170         }
171
172         [_latestBanList sortUsingSelector:sortSelector];
173         [banRules reloadData]; 
174 }
175
176 #pragma mark -
177
178 - (IBAction) saveTopic:(id) sender {
179         [(MVChatRoom *)[_room target] setTopic:[topic textStorage]];
180 }
181
182 - (IBAction) resetTopic:(id) sender {
183         [self _reloadTopic];
184 }
185
186 - (BOOL) textView:(NSTextView *) textView clickedOnLink:(id) link atIndex:(unsigned) charIndex {
187         // do nothing, ignore clicked links
188         return YES;
189 }
190
191 - (BOOL) textView:(NSTextView *) textView returnKeyPressed:(NSEvent *) event {
192         // do nothing, don't insert line returns
193         return YES;
194 }
195
196 - (BOOL) textView:(NSTextView *) textView enterKeyPressed:(NSEvent *) event {
197         // do nothing, don't insert line returns
198         return YES;
199 }
200
201 #pragma mark -
202
203 - (IBAction) newBanRule:(id) sender {
204         [_latestBanList addObject:@""];
205         [banRules noteNumberOfRowsChanged];
206         [banRules selectRow:( [_latestBanList count] - 1 ) byExtendingSelection:NO];
207         [banRules editColumn:0 row:( [_latestBanList count] - 1 ) withEvent:nil select:NO];
208 }
209
210 - (IBAction) deleteBanRule:(id) sender {
211         if( ! [banRules numberOfSelectedRows] || [banRules editedRow] != -1 ) return;
212
213         NSIndexSet *selection = [banRules selectedRowIndexes];
214
215         [banRules deselectAll:nil];
216
217         unsigned int count = [selection count];
218         unsigned int buffer[count];
219         count = [selection getIndexes:buffer maxCount:count inIndexRange:NULL];
220         if( ! count ) return;
221
222         unsigned int i = count;
223
224         do {
225                 if( i >= 1 ) i--;
226                 unsigned int index = buffer[i];
227                 if( index >= [_latestBanList count] ) continue;
228                 MVChatUser *ban = [_latestBanList objectAtIndex:index];
229                 [[_room target] removeBanForUser:ban];
230                 [_latestBanList removeObjectAtIndex:index];
231         } while( i );
232
233         [banRules reloadData];
234 }
235
236 - (IBAction) editBanRule:(id) sender {
237         int row = [banRules selectedRow];
238         if( row == -1 || [banRules numberOfSelectedRows] > 1 ) return;
239         [banRules editColumn:0 row:row withEvent:nil select:YES];
240 }
241
242 #pragma mark -
243
244 - (int) numberOfRowsInTableView:(NSTableView *) tableView {
245         return [_latestBanList count];
246 }
247
248 - (id) tableView:(NSTableView *) tableView objectValueForTableColumn:(NSTableColumn *) column row:(int) row {
249         if ( [[column identifier] isEqualToString:@"author"] ) {
250                 MVChatUser *user = [_latestBanList objectAtIndex:row];
251                 if( [user respondsToSelector:@selector( attributeForKey: )] )
252                         return [user attributeForKey:MVChatUserBanServerAttribute];
253                 return nil;
254         }
255         return [[_latestBanList objectAtIndex:row] description];
256 }
257
258 - (NSString *) tableView:(NSTableView *) tableView toolTipForCell:(NSCell *) cell rect:(NSRectPointer) rect tableColumn:(NSTableColumn *) column row:(int) row mouseLocation:(NSPoint) mouseLocation {
259         MVChatUser *user = [_latestBanList objectAtIndex:row];
260         NSDate *date = [user attributeForKey:MVChatUserBanDateAttribute];
261         NSString *dateString = nil;
262
263         NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];
264         [formatter setFormatterBehavior:NSDateFormatterBehavior10_4];
265         [formatter setDateStyle:NSDateFormatterShortStyle];
266         [formatter setTimeStyle:NSDateFormatterShortStyle];
267         dateString = [formatter stringFromDate:date];
268
269         NSString *server = [user attributeForKey:MVChatUserBanServerAttribute];
270
271         return [NSString stringWithFormat:@"%@ (%@)", dateString, server];
272 }
273
274 - (void) tableView:(NSTableView *) tableView setObjectValue:(id) object forTableColumn:(NSTableColumn *) column row:(int) row {
275         MVChatUser *newBan = [MVChatUser wildcardUserFromString:object];
276         id ban = [_latestBanList objectAtIndex:row];
277         if( [ban isEqual:newBan] ) return;
278
279         if( [ban isKindOfClass:[MVChatUser class]] )
280                 [[_room target] removeBanForUser:ban];
281
282         if( newBan && [object length] ) {
283                 [[_room target] addBanForUser:newBan];
284                 [_latestBanList replaceObjectAtIndex:row withObject:newBan];
285         } else [_latestBanList removeObjectAtIndex:row];
286
287         [banRules reloadData];
288 }
289
290 - (void) tableViewSelectionDidChange:(NSNotification *) notification {
291         unsigned int localUserModes = ( [[_room connection] localUser] ? [(MVChatRoom *)[_room target] modesForMemberUser:[[_room connection] localUser]] : 0 );
292         BOOL canEdit = ( localUserModes & MVChatRoomMemberOperatorMode );
293         if( ! canEdit ) canEdit = ( localUserModes & MVChatRoomMemberHalfOperatorMode );
294         if( ! canEdit ) canEdit = ( localUserModes & MVChatRoomMemberAdministratorMode );
295         if( ! canEdit ) canEdit = ( localUserModes & MVChatRoomMemberFounderMode );
296         if( ! canEdit ) canEdit = [[[_room connection] localUser] isServerOperator];
297
298         [deleteBanButton setEnabled:( canEdit && [banRules selectedRow] != -1 )];
299         [editBanButton setEnabled:( canEdit && [banRules selectedRow] != -1 && [banRules numberOfSelectedRows] == 1 )];
300 }
301 @end
302
303 #pragma mark -
304
305 @implementation JVChatRoomInspector (JVChatRoomInspectorPrivate)
306 - (void) _topicChanged:(NSNotification *) notification {
307         if( [[topic window] firstResponder] == topic && [topic isEditable] ) return;
308         [self _reloadTopic];
309 }
310
311 - (void) _reloadTopic {
312         NSFont *baseFont = [NSFont userFontOfSize:12.];
313         NSMutableDictionary *options = [NSMutableDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:[_room encoding]], @"StringEncoding", [NSNumber numberWithBool:[[NSUserDefaults standardUserDefaults] boolForKey:@"JVChatStripMessageColors"]], @"IgnoreFontColors", [NSNumber numberWithBool:[[NSUserDefaults standardUserDefaults] boolForKey:@"JVChatStripMessageFormatting"]], @"IgnoreFontTraits", baseFont, @"BaseFont", nil];
314         NSAttributedString *messageString = [NSAttributedString attributedStringWithChatFormat:[(MVChatRoom *)[_room target] topic] options:options];
315
316         if( ! messageString ) {
317                 [options setObject:[NSNumber numberWithUnsignedInt:NSISOLatin1StringEncoding] forKey:@"StringEncoding"];
318                 messageString = [NSAttributedString attributedStringWithChatFormat:[(MVChatRoom *)[_room target] topic] options:options];
319         }
320
321         [[topic textStorage] setAttributedString:messageString];
322 }
323
324 - (void) _refreshEditStatus:(NSNotification *) notification {
325         if( notification && ! [[[notification userInfo] objectForKey:@"who"] isLocalUser] ) return;
326
327         unsigned int localUserModes = ( [[_room connection] localUser] ? [(MVChatRoom *)[_room target] modesForMemberUser:[[_room connection] localUser]] : 0 );
328         BOOL canEdit = ( localUserModes & MVChatRoomMemberOperatorMode );
329         if( ! canEdit ) canEdit = ( localUserModes & MVChatRoomMemberHalfOperatorMode );
330         if( ! canEdit ) canEdit = ( localUserModes & MVChatRoomMemberAdministratorMode );
331         if( ! canEdit ) canEdit = ( localUserModes & MVChatRoomMemberFounderMode );
332         if( ! canEdit ) canEdit = [[[_room connection] localUser] isServerOperator];
333
334         [newBanButton setEnabled:canEdit];
335         [deleteBanButton setEnabled:( canEdit && [banRules selectedRow] != -1 )];
336         [editBanButton setEnabled:( canEdit && [banRules selectedRow] != -1 && [banRules numberOfSelectedRows] == 1 )];
337
338         NSTableColumn *column = [banRules tableColumnWithIdentifier:@"rule"];
339         [column setEditable:canEdit];
340
341         [topicChangeable setEnabled:canEdit];
342         [privateRoom setEnabled:canEdit];
343         [secretRoom setEnabled:canEdit];
344         [inviteOnly setEnabled:canEdit];
345         [noOutside setEnabled:canEdit];
346         [moderated setEnabled:canEdit];
347
348         [topic setEditable:( canEdit || ! ( [(MVChatRoom *)[_room target] modes] & MVChatRoomOperatorsOnlySetTopicMode ) )];
349
350         [limitMembers setEnabled:canEdit];
351         if( [limitMembers state] == NSOnState ) [memberLimit setEnabled:canEdit];
352         else [memberLimit setEnabled:NO];
353
354         [requiresPassword setEnabled:canEdit];
355         if( [requiresPassword state] == NSOnState ) [password setEnabled:canEdit];
356         else [password setEnabled:NO];
357 }
358
359 - (void) _roomModeChanged:(NSNotification *) notification {
360         unsigned int changedModes = ( notification ? [[[notification userInfo] objectForKey:@"changedModes"] unsignedIntValue] : [(MVChatRoom *)[_room target] modes] );
361         unsigned int newModes = [(MVChatRoom *)[_room target] modes];
362         unsigned int localUserModes = ( [[_room connection] localUser] ? [(MVChatRoom *)[_room target] modesForMemberUser:[[_room connection] localUser]] : 0 );
363
364         if( changedModes & MVChatRoomPrivateMode )
365                 [privateRoom setState:( newModes & MVChatRoomPrivateMode ? NSOnState : NSOffState )];
366
367         if( changedModes & MVChatRoomSecretMode )
368                 [secretRoom setState:( newModes & MVChatRoomSecretMode ? NSOnState : NSOffState )];
369
370         if( changedModes & MVChatRoomInviteOnlyMode )
371                 [inviteOnly setState:( newModes & MVChatRoomInviteOnlyMode ? NSOnState : NSOffState )];
372
373         if( changedModes & MVChatRoomNormalUsersSilencedMode )
374                 [moderated setState:( newModes & MVChatRoomNormalUsersSilencedMode ? NSOnState : NSOffState )];
375
376         if( changedModes & MVChatRoomOperatorsOnlySetTopicMode ) {
377                 BOOL enabled = ( newModes & MVChatRoomOperatorsOnlySetTopicMode ? YES : NO );
378                 if( enabled ) [self _reloadTopic];
379                 if( localUserModes & MVChatRoomMemberOperatorMode ) [topic setEditable:YES];
380                 else [topic setEditable:( ! enabled )];
381                 [topicChangeable setState:( enabled ? NSOnState : NSOffState )];
382         }
383
384         if( changedModes & MVChatRoomNoOutsideMessagesMode )
385                 [noOutside setState:( newModes & MVChatRoomNoOutsideMessagesMode ? NSOnState : NSOffState )];
386
387         if( changedModes & MVChatRoomPassphraseToJoinMode ) {
388                 [requiresPassword setState:( newModes & MVChatRoomPassphraseToJoinMode ? NSOnState : NSOffState )];
389                 if( newModes & MVChatRoomPassphraseToJoinMode ) [password setStringValue:[(MVChatRoom *)[_room target] attributeForMode:MVChatRoomPassphraseToJoinMode]];
390                 else [password setStringValue:@""];
391         }
392
393         if( changedModes & MVChatRoomLimitNumberOfMembersMode ) {
394                 [limitMembers setState:( newModes & MVChatRoomLimitNumberOfMembersMode ? NSOnState : NSOffState )];
395                 if( newModes & MVChatRoomLimitNumberOfMembersMode ) [memberLimit setObjectValue:[(MVChatRoom *)[_room target] attributeForMode:MVChatRoomLimitNumberOfMembersMode]];
396                 else [memberLimit setStringValue:@""];
397         }
398
399         [self _refreshEditStatus:nil];
400 }
401 @end
Note: See TracBrowser for help on using the browser.