forked from mccraigmccraig/colloquy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
JVDirectChatPanel.m
2031 lines (1604 loc) · 84.6 KB
/
JVDirectChatPanel.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#import "JVDirectChatPanel.h"
#import "JVBuddy.h"
#import "JVChatController.h"
#import "JVChatEvent.h"
#import "JVChatMessage.h"
#import "JVChatRoomMember.h"
#import "JVChatRoomPanel.h"
#import "JVChatTranscript.h"
#import "JVChatUserInspector.h"
#import "JVEmoticonSet.h"
#import "JVMarkedScroller.h"
#import "JVNotificationController.h"
//#import "JVSQLChatTranscript.h"
#import "JVSpeechController.h"
#import "JVSplitView.h"
#import "JVStyle.h"
#import "JVStyleView.h"
#import "JVTabbedChatWindowController.h"
#import "KAIgnoreRule.h"
#import "MVBuddyListController.h"
#import "MVChatUserAdditions.h"
#import "MVConnectionsController.h"
#import "MVFileTransferController.h"
#import "MVMenuButton.h"
#import "MVTextView.h"
#import "NSAttributedStringMoreAdditions.h"
#import "NSBundleAdditions.h"
#import "NSURLAdditions.h"
static NSSet *actionVerbs = nil;
const NSStringEncoding JVAllowedTextEncodings[] = {
/* Universal */
NSUTF8StringEncoding,
NSNonLossyASCIIStringEncoding,
/* Western */
(NSStringEncoding) -1, // Divider
NSASCIIStringEncoding,
NSISOLatin1StringEncoding, // ISO Latin 1
(NSStringEncoding) 0x80000203, // ISO Latin 3
(NSStringEncoding) 0x8000020F, // ISO Latin 9
NSMacOSRomanStringEncoding, // Mac
NSWindowsCP1252StringEncoding, // Windows
/* Baltic */
(NSStringEncoding) -1,
(NSStringEncoding) 0x8000020D, // ISO Latin 7
(NSStringEncoding) 0x80000507, // Windows
/* Central European */
(NSStringEncoding) -1,
NSISOLatin2StringEncoding, // ISO Latin 2
(NSStringEncoding) 0x80000204, // ISO Latin 4
(NSStringEncoding) 0x8000001D, // Mac
NSWindowsCP1250StringEncoding, // Windows
/* Cyrillic */
(NSStringEncoding) -1,
(NSStringEncoding) 0x80000A02, // KOI8-R
(NSStringEncoding) 0x80000205, // ISO Latin 5
(NSStringEncoding) 0x80000007, // Mac
NSWindowsCP1251StringEncoding, // Windows
/* Japanese */
(NSStringEncoding) -1, // Divider
(NSStringEncoding) 0x80000A01, // ShiftJIS
NSISO2022JPStringEncoding, // ISO-2022-JP
NSJapaneseEUCStringEncoding, // EUC
(NSStringEncoding) 0x80000001, // Mac
NSShiftJISStringEncoding, // Windows
/* Simplified Chinese */
(NSStringEncoding) -1, // Divider
(NSStringEncoding) 0x80000632, // GB 18030
(NSStringEncoding) 0x80000631, // GBK
(NSStringEncoding) 0x80000930, // EUC
(NSStringEncoding) 0x80000019, // Mac
(NSStringEncoding) 0x80000421, // Windows
/* Traditional Chinese */
(NSStringEncoding) -1, // Divider
(NSStringEncoding) 0x80000A03, // Big5
(NSStringEncoding) 0x80000A06, // Big5 HKSCS
(NSStringEncoding) 0x80000931, // EUC
(NSStringEncoding) 0x80000002, // Mac
(NSStringEncoding) 0x80000423, // Windows
/* Korean */
(NSStringEncoding) -1, // Divider
(NSStringEncoding) 0x80000940, // EUC
(NSStringEncoding) 0x80000003, // Mac
(NSStringEncoding) 0x80000422, // Windows
/* Hebrew */
(NSStringEncoding) -1, // Divider
(NSStringEncoding) 0x80000208, // ISO-8859-8
(NSStringEncoding) 0x80000005, // Mac
(NSStringEncoding) 0x80000505, // Windows
/* End */ 0 };
NSString *JVToolbarTextEncodingItemIdentifier = @"JVToolbarTextEncodingItem";
NSString *JVToolbarClearScrollbackItemIdentifier = @"JVToolbarClearScrollbackItem";
NSString *JVToolbarSendFileItemIdentifier = @"JVToolbarSendFileItem";
NSString *JVToolbarMarkItemIdentifier = @"JVToolbarMarkItem";
NSString *JVChatMessageWasProcessedNotification = @"JVChatMessageWasProcessedNotification";
NSString *JVChatEventMessageWasProcessedNotification = @"JVChatEventMessageWasProcessedNotification";
@interface JVDirectChatPanel (JVDirectChatPrivate) <ABImageClient>
- (NSString *) _selfCompositeName;
- (NSString *) _selfStoredNickname;
- (void) _hyperlinkRoomNames:(NSMutableAttributedString *) message;
- (NSMutableAttributedString *) _convertRawMessage:(NSData *) message;
- (NSMutableAttributedString *) _convertRawMessage:(NSData *) message withBaseFont:(NSFont *) baseFont;
- (void) _saveSelfIcon;
- (void) _saveBuddyIcon:(JVBuddy *) buddy;
- (void) _setCurrentMessage:(JVMutableChatMessage *) message;
@end
#pragma mark -
@interface JVChatTranscriptPanel (JVChatTranscriptPrivate)
- (void) _refreshWindowFileProxy;
- (void) _changeEmoticonsMenuSelection;
- (void) _didSwitchStyles:(NSNotification *) notification;
@end
#pragma mark -
@interface JVStyleView (JVStyleViewPrivate)
- (unsigned long) _visibleMessageCount;
- (long) _locationOfElementAtIndex:(unsigned long) index;
@end
#pragma mark -
@implementation JVDirectChatPanel
- (id) init {
if( ( self = [super init] ) ) {
send = nil;
_target = nil;
_firstMessage = YES;
_newMessageCount = 0;
_newHighlightMessageCount = 0;
_cantSendMessages = NO;
_isActive = NO;
_forceSplitViewPosition = YES;
_historyIndex = 0;
_sendHeight = 25.;
_encoding = NSASCIIStringEncoding;
_encodingMenu = nil;
_spillEncodingMenu = nil;
_sendHistory = [[NSMutableArray arrayWithCapacity:30] retain];
[_sendHistory insertObject:[[[NSAttributedString alloc] initWithString:@""] autorelease] atIndex:0];
_waitingAlerts = [[NSMutableArray arrayWithCapacity:5] retain];
_waitingAlertNames = [[NSMutableDictionary dictionaryWithCapacity:5] retain];
}
return self;
}
- (id) initWithTarget:(id) target {
if( ( self = [self init] ) ) {
_target = [target retain];
if( [self connection] ) {
NSURL *connURL = [[self connection] url];
NSString *targetName = nil;
if( [target respondsToSelector:@selector( name )] )
targetName = [(MVChatRoom *)target name];
else if( [target respondsToSelector:@selector( nickname )] )
targetName = [(MVChatUser *)target nickname];
else targetName = [target description];
NSURL *source = [[NSURL alloc] initWithScheme:[connURL scheme] host:[connURL host] path:[[connURL path] stringByAppendingString:[NSString stringWithFormat:@"/%@", targetName]]];
if( ( [self isMemberOfClass:[JVDirectChatPanel class]] && [[NSUserDefaults standardUserDefaults] boolForKey:@"JVLogPrivateChats"] ) ||
( [self isMemberOfClass:[JVChatRoomPanel class]] && [[NSUserDefaults standardUserDefaults] boolForKey:@"JVLogChatRooms"] ) ) {
NSString *logs = [[[NSUserDefaults standardUserDefaults] stringForKey:@"JVChatTranscriptFolder"] stringByStandardizingPath];
NSFileManager *fileManager = [NSFileManager defaultManager];
if( ! [fileManager fileExistsAtPath:logs] ) [fileManager createDirectoryAtPath:logs attributes:nil];
int org = [[NSUserDefaults standardUserDefaults] integerForKey:@"JVChatTranscriptFolderOrganization"];
if( org == 1 ) {
logs = [logs stringByAppendingPathComponent:[[self user] serverAddress]];
if( ! [fileManager fileExistsAtPath:logs] ) [fileManager createDirectoryAtPath:logs attributes:nil];
} else if( org == 2 ) {
logs = [logs stringByAppendingPathComponent:[NSString stringWithFormat:@"%@ (%@)", [self target], [[self user] serverAddress]]];
if( ! [fileManager fileExistsAtPath:logs] ) [fileManager createDirectoryAtPath:logs attributes:nil];
} else if( org == 3 ) {
logs = [logs stringByAppendingPathComponent:[[self user] serverAddress]];
if( ! [fileManager fileExistsAtPath:logs] ) [fileManager createDirectoryAtPath:logs attributes:nil];
logs = [logs stringByAppendingPathComponent:[self title]];
if( ! [fileManager fileExistsAtPath:logs] ) [fileManager createDirectoryAtPath:logs attributes:nil];
}
NSString *logName = nil;
NSString *dateString = [[NSCalendarDate date] descriptionWithCalendarFormat:[[NSUserDefaults standardUserDefaults] stringForKey:NSShortDateFormatString]];
int session = [[NSUserDefaults standardUserDefaults] integerForKey:@"JVChatTranscriptSessionHandling"];
if( ! session ) {
BOOL nameFound = NO;
unsigned int i = 1;
if( org ) logName = [NSString stringWithFormat:@"%@.colloquyTranscript", [self target]];
else logName = [NSString stringWithFormat:@"%@ (%@).colloquyTranscript", [self target], [[self user] serverAddress]];
nameFound = ! [fileManager fileExistsAtPath:[logs stringByAppendingPathComponent:logName]];
while( ! nameFound ) {
if( org ) logName = [NSString stringWithFormat:@"%@ %d.colloquyTranscript", [self target], i++];
else logName = [NSString stringWithFormat:@"%@ (%@) %d.colloquyTranscript", [self target], [[self user] serverAddress], i++];
nameFound = ! [fileManager fileExistsAtPath:[logs stringByAppendingPathComponent:logName]];
}
} else if( session == 1 ) {
if( org ) logName = [NSString stringWithFormat:@"%@.colloquyTranscript", [self target]];
else logName = [NSString stringWithFormat:@"%@ (%@).colloquyTranscript", [self target], [[self user] serverAddress]];
} else if( session == 2 ) {
if( org ) logName = [NSMutableString stringWithFormat:@"%@ %@.colloquyTranscript", [self target], dateString];
else logName = [NSMutableString stringWithFormat:@"%@ (%@) %@.colloquyTranscript", [self target], [[self user] serverAddress], dateString];
[(NSMutableString *)logName replaceOccurrencesOfString:@"/" withString:@"-" options:NSLiteralSearch range:NSMakeRange( 0, [logName length] )];
[(NSMutableString *)logName replaceOccurrencesOfString:@":" withString:@"-" options:NSLiteralSearch range:NSMakeRange( 0, [logName length] )];
}
logs = [logs stringByAppendingPathComponent:logName];
// _sqlTestTranscript = [[JVSQLChatTranscript alloc] initWithContentsOfFile:[[logs stringByDeletingPathExtension] stringByAppendingPathExtension:@"colloquySQLTranscript"]];
if( [fileManager fileExistsAtPath:logs] )
[[self transcript] startNewSession];
[[self transcript] setFilePath:logs];
[[self transcript] setSource:source];
[[self transcript] setAutomaticallyWritesChangesToFile:YES];
}
[source release];
[[self transcript] setElementLimit:0]; // start with zero limit
if( ! [target isKindOfClass:[MVDirectChatConnection class]] ) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _didConnect: ) name:MVChatConnectionDidConnectNotification object:[self connection]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _didDisconnect: ) name:MVChatConnectionDidDisconnectNotification object:[self connection]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _awayStatusChanged: ) name:MVChatConnectionSelfAwayStatusChangedNotification object:[self connection]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _errorOccurred: ) name:MVChatConnectionErrorNotification object:[self connection]];
}
}
_settings = [[NSMutableDictionary dictionaryWithDictionary:[[NSUserDefaults standardUserDefaults] dictionaryForKey:[[self identifier] stringByAppendingString:@" Settings"]]] retain];
}
return self;
}
- (void) awakeFromNib {
JVStyle *style = nil;
NSString *variant = nil;
JVEmoticonSet *emoticon = nil;
if( [[self target] isKindOfClass:[MVDirectChatConnection class]] ) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _refreshIcon: ) name:MVDirectChatConnectionDidConnectNotification object:[self target]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _refreshIcon: ) name:MVDirectChatConnectionDidDisconnectNotification object:[self target]];
} else {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _refreshIcon: ) name:MVChatConnectionDidConnectNotification object:[self connection]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _refreshIcon: ) name:MVChatConnectionDidDisconnectNotification object:[self connection]];
}
if( [self preferenceForKey:@"style"] ) {
style = [JVStyle styleWithIdentifier:[self preferenceForKey:@"style"]];
variant = [self preferenceForKey:@"style variant"];
if( style ) [self setStyle:style withVariant:variant];
}
if( [(NSString *)[self preferenceForKey:@"emoticon"] length] ) {
emoticon = [JVEmoticonSet emoticonSetWithIdentifier:[self preferenceForKey:@"emoticon"]];
if( emoticon ) [self setEmoticons:emoticon];
}
[super awakeFromNib];
[display setBodyTemplate:@"directChat"];
[self changeEncoding:nil];
/* if( [self isMemberOfClass:[JVDirectChatPanel class]] ) {
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@/%@", [[self connection] urlScheme], [[self user] serverAddress], [[[self target] description] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
NSString *path = [[NSString stringWithFormat:@"~/Library/Application Support/Colloquy/Recent Acquaintances/%@ (%@).inetloc", [self target], [[self user] serverAddress]] stringByExpandingTildeInPath];
[url writeToInternetLocationFile:path];
[[NSFileManager defaultManager] changeFileAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSFileExtensionHidden, nil] atPath:path];
} */
[send setHorizontallyResizable:YES];
[send setVerticallyResizable:YES];
[send setAutoresizingMask:NSViewWidthSizable];
[send setSelectable:YES];
[send setEditable:YES];
[send setRichText:YES];
[send setImportsGraphics:NO];
[send setUsesFontPanel:YES];
[send setUsesFindPanel:NO];
[send setAllowsUndo:YES];
[send setUsesRuler:NO];
[send setDelegate:self];
[send setContinuousSpellCheckingEnabled:[[NSUserDefaults standardUserDefaults] boolForKey:@"JVChatSpellChecking"]];
[send setUsesSystemCompleteOnTab:[[NSUserDefaults standardUserDefaults] boolForKey:@"JVUsePantherTextCompleteOnTab"]];
[send reset:nil];
if( [[NSUserDefaults standardUserDefaults] boolForKey:@"JVChatInputAutoResizes"] )
[(JVSplitView *)[[[send superview] superview] superview] setIsPaneSplitter:YES];
}
- (void) dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[_target release];
[_sendHistory release];
[_waitingAlertNames release];
[_settings release];
[_encodingMenu release];
[_spillEncodingMenu release];
NSEnumerator *enumerator = [_waitingAlerts objectEnumerator];
id alert = nil;
while( ( alert = [enumerator nextObject] ) )
NSReleaseAlertPanel( alert );
[_waitingAlerts release];
_target = nil;
_sendHistory = nil;
_waitingAlerts = nil;
_waitingAlertNames = nil;
_settings = nil;
_encodingMenu = nil;
_spillEncodingMenu = nil;
[super dealloc];
}
#pragma mark -
- (id) target {
return _target;
}
- (MVChatUser *) user {
if( [[self target] isKindOfClass:[MVChatUser class]] )
return [self target];
if( [[self target] isKindOfClass:[MVDirectChatConnection class]] )
return [(MVDirectChatConnection *)[self target] user];
return nil;
}
- (NSURL *) url {
NSString *server = [[[self connection] url] absoluteString];
return [NSURL URLWithString:[NSString stringWithFormat:@"%@/%@", server, [[[self user] nickname] stringByEncodingIllegalURLCharacters]]];
}
- (MVChatConnection *) connection {
if( [[self target] respondsToSelector:@selector( connection )] )
return (MVChatConnection *)[[self target] connection];
if( [[self target] isKindOfClass:[MVDirectChatConnection class]] )
return [[(MVDirectChatConnection *)[self target] user] connection];
return nil;
}
#pragma mark -
- (NSView *) view {
if( ! _nibLoaded ) _nibLoaded = [NSBundle loadNibNamed:@"JVDirectChat" owner:self];
return contents;
}
- (NSResponder *) firstResponder {
return send;
}
#pragma mark -
- (BOOL) isEnabled {
if( [[self target] isKindOfClass:[MVDirectChatConnection class]] )
return ( [(MVDirectChatConnection *)[self target] status] == MVDirectChatConnectionConnectedStatus );
return [[self connection] isConnected];
}
- (NSString *) title {
/* if( _buddy && [_buddy preferredNameWillReturn] != JVBuddyActiveNickname )
return [_buddy preferredName]; */
return [[self user] displayName];
}
- (NSString *) windowTitle {
/* if( _buddy && [_buddy preferredNameWillReturn] != JVBuddyActiveNickname )
return [NSString stringWithFormat:@"%@ (%@)", [_buddy preferredName], [[self user] serverAddress]]; */
if( [[self target] isKindOfClass:[MVDirectChatConnection class]] ) {
NSString *host = [(MVDirectChatConnection *)[self target] connectedHost];
if( ! [host length] ) host = [[self user] address];
if( [host length] ) return [NSString stringWithFormat:@"%@ (%@)", [self title], host];
return [self title];
}
return [NSString stringWithFormat:@"%@ (%@)", [self title], [[self user] serverAddress]];
}
- (NSString *) information {
/* if( _buddy && [_buddy preferredNameWillReturn] != JVBuddyActiveNickname && ! [[self target] isEqualToString:[_buddy preferredName]] )
return [NSString stringWithFormat:@"%@ (%@)", [self target], [[self user] serverAddress]]; */
if( [[self target] isKindOfClass:[MVDirectChatConnection class]] ) {
if( [(MVDirectChatConnection *)[self target] status] == MVDirectChatConnectionWaitingStatus )
return NSLocalizedString( @"waiting for connection", "waiting for connection information" );
if( [(MVDirectChatConnection *)[self target] status] == MVDirectChatConnectionDisconnectedStatus )
return NSLocalizedString( @"disconnected", "disconnected information" );
NSString *host = [(MVDirectChatConnection *)[self target] connectedHost];
if( ! [host length] ) host = [[self user] address];
return host;
}
return [[self user] serverAddress];
}
- (NSString *) toolTip {
NSString *messageCount = @"";
if( [self newMessagesWaiting] == 0 ) messageCount = NSLocalizedString( @"no messages waiting", "no messages waiting room tooltip" );
else if( [self newMessagesWaiting] == 1 ) messageCount = NSLocalizedString( @"1 message waiting", "one message waiting room tooltip" );
else messageCount = [NSString stringWithFormat:NSLocalizedString( @"%d messages waiting", "messages waiting room tooltip" ), [self newMessagesWaiting]];
/* if( _buddy && [_buddy preferredNameWillReturn] != JVBuddyActiveNickname )
return [NSString stringWithFormat:@"%@\n%@ (%@)\n%@", [_buddy preferredName], [self target], [[self user] serverAddress], messageCount]; */
if( [[self target] isKindOfClass:[MVDirectChatConnection class]] ) {
NSString *info = nil;
if( [(MVDirectChatConnection *)[self target] status] == MVDirectChatConnectionWaitingStatus )
info = NSLocalizedString( @"waiting for connection", "waiting for connection tooltip" );
else if( [(MVDirectChatConnection *)[self target] status] == MVDirectChatConnectionDisconnectedStatus )
info = NSLocalizedString( @"disconnected", "disconnected tooltip" );
else {
info = [(MVDirectChatConnection *)[self target] connectedHost];
if( ! [info length] ) info = [[self user] address];
}
if( [info length] ) return [NSString stringWithFormat:@"%@ (%@)\n%@", [self title], info, messageCount];
return [NSString stringWithFormat:@"%@\n%@", [self title], messageCount];
}
return [NSString stringWithFormat:@"%@ (%@)\n%@", [self title], [[self user] serverAddress], messageCount];
}
#pragma mark -
- (NSMenu *) menu {
NSMenu *menu = [[NSMenu alloc] initWithTitle:@""];
NSMenuItem *item = nil;
NSArray *standardItems = [[self user] standardMenuItems];
NSEnumerator *enumerator = [standardItems objectEnumerator];
while( ( item = [enumerator nextObject] ) )
if( [item action] != @selector( startDirectChat: ) )
[menu addItem:item];
[menu addItem:[NSMenuItem separatorItem]];
item = [[NSMenuItem alloc] initWithTitle:NSLocalizedString( @"Ignore Notifications", "lists whether or not notifications are enabled for this conversation") action:@selector( toggleNotifications: ) keyEquivalent:@""];
[item setTarget:self];
[menu addItem:item];
[item release];
[menu addItem:[NSMenuItem separatorItem]];
if( [[[self windowController] allChatViewControllers] count] > 1 ) {
item = [[NSMenuItem alloc] initWithTitle:NSLocalizedString( @"Detach From Window", "detach from window contextual menu item title" ) action:@selector( detachView: ) keyEquivalent:@""];
[item setRepresentedObject:self];
[item setTarget:[JVChatController defaultController]];
[menu addItem:item];
[item release];
}
if( [[self target] isKindOfClass:[MVDirectChatConnection class]] ) {
item = [[NSMenuItem alloc] initWithTitle:NSLocalizedString( @"Disconnect", "disconnect contextual menu item title" ) action:@selector( disconnect ) keyEquivalent:@""];
[item setTarget:[self target]];
[menu addItem:item];
[item release];
}
item = [[NSMenuItem alloc] initWithTitle:NSLocalizedString( @"Close", "close contextual menu item title" ) action:@selector( close: ) keyEquivalent:@""];
[item setTarget:self];
[menu addItem:item];
[item release];
return [menu autorelease];
}
- (BOOL) validateMenuItem:(NSMenuItem *) menuItem {
if( [menuItem action] == @selector( toggleNotifications: ) ) {
if( [[self preferenceForKey:@"muted"] boolValue] )
[menuItem setState:NSOnState];
else [menuItem setState:NSOffState];
}
return YES;
}
- (NSImage *) icon {
BOOL smallIcons = [[[self windowController] preferenceForKey:@"small drawer icons"] boolValue];
if( smallIcons || [_windowController isMemberOfClass:[JVTabbedChatWindowController class]] )
return [NSImage imageNamed:@"privateChatTab"];
return [NSImage imageNamed:@"messageUser"];
}
- (NSImage *) statusImage {
if( _isActive && [[[self view] window] isKeyWindow] ) {
_newMessageCount = 0;
_newHighlightMessageCount = 0;
return nil;
}
if( [_windowController isMemberOfClass:[JVTabbedChatWindowController class]] )
return ( [_waitingAlerts count] ? [NSImage imageNamed:@"AlertCautionIcon"] : ( _newMessageCount ? ( _newHighlightMessageCount ? [NSImage imageNamed:@"privateChatTabNewMessage"] : [NSImage imageNamed:@"privateChatTabNewMessage"] ) : nil ) );
return ( [_waitingAlerts count] ? [NSImage imageNamed:@"viewAlert"] : nil );
}
#pragma mark -
- (NSString *) identifier {
if( [[self target] isKindOfClass:[MVDirectChatConnection class]] )
return [NSString stringWithFormat:@"Direct Chat %@", [self user]];
return [NSString stringWithFormat:@"Direct Chat %@ (%@)", [self user], [[self user] serverAddress]];
}
#pragma mark -
- (void) didUnselect {
_newMessageCount = 0;
_newHighlightMessageCount = 0;
_isActive = NO;
[super didUnselect];
}
- (void) willSelect {
_newMessageCount = 0;
_newHighlightMessageCount = 0;
}
- (void) didSelect {
if( ! [[NSUserDefaults standardUserDefaults] boolForKey:@"JVChatInputAutoResizes"] ) {
[(JVSplitView *)[[[send superview] superview] superview] setPositionUsingName:@"JVChatSplitViewPosition"];
} else [self textDidChange:nil];
_newMessageCount = 0;
_newHighlightMessageCount = 0;
_isActive = YES;
[super didSelect];
[_windowController reloadListItem:self andChildren:NO];
[[[self view] window] makeFirstResponder:send];
if( [_waitingAlerts count] )
[[NSApplication sharedApplication] beginSheet:[_waitingAlerts objectAtIndex:0] modalForWindow:[_windowController window] modalDelegate:self didEndSelector:@selector( _alertSheetDidEnd:returnCode:contextInfo: ) contextInfo:NULL];
}
#pragma mark -
#pragma mark Drag & Drop Support
- (BOOL) acceptsDraggedFileOfType:(NSString *) type {
return YES;
}
- (void) handleDraggedFile:(NSString *) path {
BOOL passive = [[NSUserDefaults standardUserDefaults] boolForKey:@"JVSendFilesPassively"];
[[MVFileTransferController defaultController] addFileTransfer:[[self user] sendFile:path passively:passive]];
}
#pragma mark -
#pragma mark GUI Actions
- (IBAction) getInfo:(id) sender {
[[JVInspectorController inspectorOfObject:self] show:sender];
}
#pragma mark -
- (void) showAlert:(NSPanel *) alert withName:(NSString *) name {
if( _isActive && ! [[_windowController window] attachedSheet] ) {
if( alert ) [[NSApplication sharedApplication] beginSheet:alert modalForWindow:[_windowController window] modalDelegate:self didEndSelector:@selector( _alertSheetDidEnd:returnCode:contextInfo: ) contextInfo:NULL];
} else {
if( name && [_waitingAlertNames objectForKey:name] ) {
NSPanel *sheet = [[_waitingAlertNames objectForKey:name] retain];
if( alert ) {
[_waitingAlerts replaceObjectAtIndex:[_waitingAlerts indexOfObjectIdenticalTo:[_waitingAlertNames objectForKey:name]] withObject:alert];
[_waitingAlertNames setObject:alert forKey:name];
} else {
[_waitingAlerts removeObjectAtIndex:[_waitingAlerts indexOfObjectIdenticalTo:[_waitingAlertNames objectForKey:name]]];
[_waitingAlertNames removeObjectForKey:name];
}
NSReleaseAlertPanel( sheet );
[sheet release];
} else {
if( name && alert ) [_waitingAlertNames setObject:alert forKey:name];
if( alert ) [_waitingAlerts addObject:alert];
}
}
[_windowController reloadListItem:self andChildren:NO];
}
#pragma mark -
#pragma mark Prefences/User Defaults
- (void) setPreference:(id) value forKey:(NSString *) key {
NSParameterAssert( key != nil );
NSParameterAssert( [key length] );
if( value ) [_settings setObject:value forKey:key];
else [_settings removeObjectForKey:key];
if( [_settings count] ) [[NSUserDefaults standardUserDefaults] setObject:_settings forKey:[[self identifier] stringByAppendingString:@" Settings"]];
else [[NSUserDefaults standardUserDefaults] removeObjectForKey:[[self identifier] stringByAppendingString:@" Settings"]];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (id) preferenceForKey:(NSString *) key {
NSParameterAssert( key != nil );
NSParameterAssert( [key length] );
return [_settings objectForKey:key];
}
#pragma mark -
#pragma mark Styles
- (IBAction) changeStyle:(id) sender {
JVStyle *style = [sender representedObject];
[self setPreference:[style identifier] forKey:@"style"];
[self setPreference:nil forKey:@"style variant"];
[super changeStyle:sender];
}
- (IBAction) changeStyleVariant:(id) sender {
JVStyle *style = [[sender representedObject] objectForKey:@"style"];
NSString *variant = [[sender representedObject] objectForKey:@"variant"];
[self setPreference:[style identifier] forKey:@"style"];
[self setPreference:variant forKey:@"style variant"];
[super changeStyleVariant:sender];
}
- (IBAction) changeEmoticons:(id) sender {
JVEmoticonSet *emoticon = [sender representedObject];
[self setPreference:[emoticon identifier] forKey:@"emoticon"];
[super changeEmoticons:sender];
}
#pragma mark -
#pragma mark Encoding Support
- (NSStringEncoding) encoding {
return _encoding;
}
- (IBAction) changeEncoding:(id) sender {
if( ! [[self connection] supportedStringEncodings] ) return;
NSMenuItem *menuItem = nil;
unsigned i = 0, count = 0;
BOOL new = NO;
if( ! [sender tag] ) {
_encoding = (NSStringEncoding) [[self preferenceForKey:@"encoding"] intValue];
if( ! _encoding ) _encoding = [[self connection] encoding];
} else _encoding = (NSStringEncoding) [sender tag];
if( ! _encodingMenu ) {
_encodingMenu = [[NSMenu alloc] initWithTitle:@""];
menuItem = [[[NSMenuItem alloc] initWithTitle:@"" action:NULL keyEquivalent:@""] autorelease];
[menuItem setImage:[NSImage imageNamed:@"encoding"]];
[_encodingMenu addItem:menuItem];
new = YES;
}
const NSStringEncoding *supportedEncodings = [[self connection] supportedStringEncodings];
for( i = 0; supportedEncodings[i]; i++ ) {
/* if( supportedEncodings[i] == (NSStringEncoding) -1 ) {
if( new ) [_encodingMenu addItem:[NSMenuItem separatorItem]];
continue;
} */
if( new ) menuItem = [[[NSMenuItem alloc] initWithTitle:[NSString localizedNameOfStringEncoding:supportedEncodings[i]] action:@selector( changeEncoding: ) keyEquivalent:@""] autorelease];
else menuItem = (NSMenuItem *)[_encodingMenu itemAtIndex:i + 1];
if( _encoding == supportedEncodings[i] ) {
[menuItem setState:NSOnState];
} else [menuItem setState:NSOffState];
if( new ) {
[menuItem setTag:supportedEncodings[i]];
[_encodingMenu addItem:menuItem];
}
}
if( ! _spillEncodingMenu ) _spillEncodingMenu = [[NSMenu alloc] initWithTitle:NSLocalizedString( @"Encoding", "encoding menu toolbar item" )];
count = [_spillEncodingMenu numberOfItems];
for( i = 0; i < count; i++ ) [_spillEncodingMenu removeItemAtIndex:0];
count = [_encodingMenu numberOfItems];
for( i = 1; i < count; i++ ) [_spillEncodingMenu addItem:[[(NSMenuItem *)[_encodingMenu itemAtIndex:i] copy] autorelease]];
if( _encoding != [[self connection] encoding] ) {
[self setPreference:[NSNumber numberWithInt:_encoding] forKey:@"encoding"];
} else [self setPreference:nil forKey:@"encoding"];
}
#pragma mark -
#pragma mark Messages & Events
- (void) addEventMessageToDisplay:(NSString *) message withName:(NSString *) name andAttributes:(NSDictionary *) attributes {
if( ! _nibLoaded ) [self view];
NSParameterAssert( name != nil );
NSParameterAssert( [name length] );
JVMutableChatEvent *event = [JVMutableChatEvent chatEventWithName:name andMessage:message];
[event setAttributes:attributes];
[display setScrollbackLimit:[[NSUserDefaults standardUserDefaults] integerForKey:@"JVChatScrollbackLimit"]];
if( [[self transcript] automaticallyWritesChangesToFile] )
[[self transcript] setElementLimit:( [display scrollbackLimit] * 2 )];
JVChatEvent *newEvent = [[self transcript] appendEvent:event];
// [_sqlTestTranscript appendEvent:event];
[display appendChatTranscriptElement:newEvent];
[[NSNotificationCenter defaultCenter] postNotificationName:JVChatEventMessageWasProcessedNotification object:self userInfo:[NSDictionary dictionaryWithObject:newEvent forKey:@"event"]];
if( ! [[[_windowController window] representedFilename] length] )
[self _refreshWindowFileProxy];
}
- (void) addMessageToDisplay:(NSData *) message fromUser:(MVChatUser *) user asAction:(BOOL) action withIdentifier:(NSString *) identifier andType:(JVChatMessageType) type;
{
[self addMessageToDisplay:message fromUser:user withAttributes:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:action] forKey:@"action"] withIdentifier:identifier andType:type];
}
- (void) addMessageToDisplay:(NSData *) message fromUser:(MVChatUser *) user withAttributes:(NSDictionary *) msgAttributes withIdentifier:(NSString *) identifier andType:(JVChatMessageType) type {
if( ! _nibLoaded ) [self view];
NSParameterAssert( message != nil );
NSParameterAssert( user != nil );
NSFont *baseFont = [[NSFontManager sharedFontManager] fontWithFamily:[[display preferences] standardFontFamily] traits:( NSUnboldFontMask | NSUnitalicFontMask ) weight:5 size:[[display preferences] defaultFontSize]];
if( ! baseFont ) baseFont = [NSFont userFontOfSize:12.];
NSMutableDictionary *options = [NSMutableDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:_encoding], @"StringEncoding", [NSNumber numberWithBool:[[NSUserDefaults standardUserDefaults] boolForKey:@"JVChatStripMessageColors"]], @"IgnoreFontColors", [NSNumber numberWithBool:[[NSUserDefaults standardUserDefaults] boolForKey:@"JVChatStripMessageFormatting"]], @"IgnoreFontTraits", baseFont, @"BaseFont", nil];
NSTextStorage *messageString = [NSTextStorage attributedStringWithChatFormat:message options:options];
if( ! messageString ) {
[options setObject:[NSNumber numberWithUnsignedInt:NSISOLatin1StringEncoding] forKey:@"StringEncoding"];
messageString = [NSMutableAttributedString attributedStringWithChatFormat:message options:options];
NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithObjectsAndKeys:baseFont, NSFontAttributeName, nil];
NSMutableAttributedString *error = [[[NSMutableAttributedString alloc] initWithString:[@" " stringByAppendingString:NSLocalizedString( @"incompatible encoding", "encoding of the message different than your current encoding" )] attributes:attributes] autorelease];
[error addAttribute:@"CSSClasses" value:[NSSet setWithObjects:@"error", @"encoding", nil] range:NSMakeRange( 1, ( [error length] - 1 ) )];
[messageString appendAttributedString:error];
}
if( ! [messageString length] ) {
NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithObjectsAndKeys:baseFont, NSFontAttributeName, [NSSet setWithObjects:@"error", @"encoding", nil], @"CSSClasses", nil];
messageString = [[[NSTextStorage alloc] initWithString:NSLocalizedString( @"incompatible encoding", "encoding of the message different than your current encoding" ) attributes:attributes] autorelease];
}
JVMutableChatMessage *cmessage = [[JVMutableChatMessage alloc] initWithText:messageString sender:user];
[cmessage setMessageIdentifier:identifier];
[cmessage setAttributes:msgAttributes];
[cmessage setAction:[[msgAttributes objectForKey:@"action"] boolValue]];
[cmessage setType:type];
messageString = [cmessage body]; // just incase
[self _setCurrentMessage:cmessage];
if( ! [user isLocalUser] )
[cmessage setIgnoreStatus:[[JVChatController defaultController] shouldIgnoreUser:user withMessage:messageString inView:self]];
if( ! [user isLocalUser] && [cmessage ignoreStatus] == JVNotIgnored )
_newMessageCount++;
if( ! [[NSUserDefaults standardUserDefaults] boolForKey:@"MVChatDisableLinkHighlighting"] ) {
[messageString makeLinkAttributesAutomatically];
[self _hyperlinkRoomNames:messageString];
}
[[self emoticons] performEmoticonSubstitution:messageString];
if( ! [user isLocalUser] ) {
NSCharacterSet *escapeSet = [NSCharacterSet characterSetWithCharactersInString:@"^[]{}()\\.$*+?|"];
NSMutableArray *names = [[[NSUserDefaults standardUserDefaults] stringArrayForKey:@"MVChatHighlightNames"] mutableCopy];
if( [self connection] )
[names addObject:[[self connection] nickname]];
NSEnumerator *enumerator = [names objectEnumerator];
AGRegex *regex = nil;
NSString *name = nil;
while( ( name = [enumerator nextObject] ) ) {
if( ! [name length] ) continue;
if( [name hasPrefix:@"/"] && [name hasSuffix:@"/"] && [name length] > 1 ) {
regex = [AGRegex regexWithPattern:[name substringWithRange:NSMakeRange( 1, [name length] - 2 )] options:AGRegexCaseInsensitive];
} else {
name = [name stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"\"'"]];
NSString *pattern = [NSString stringWithFormat:@"(?<=^|\\s|[^\\w])%@(?=$|\\s|[^\\w])", [name stringByEscapingCharactersInSet:escapeSet]];
regex = [AGRegex regexWithPattern:pattern options:AGRegexCaseInsensitive];
}
NSArray *matches = [regex findAllInString:[messageString string]];
NSEnumerator *enumerator = [matches objectEnumerator];
AGRegexMatch *match = nil;
while( ( match = [enumerator nextObject] ) ) {
NSRange foundRange = [match range];
NSMutableSet *classes = [NSMutableSet setWithSet:[messageString attribute:@"CSSClasses" atIndex:foundRange.location effectiveRange:NULL]];
[classes addObject:@"highlight"];
[messageString addAttribute:@"CSSClasses" value:[NSSet setWithSet:classes] range:foundRange];
[cmessage setHighlighted:YES];
}
}
[names release];
}
[self processIncomingMessage:cmessage];
if( [[cmessage sender] isKindOfClass:[JVChatRoomMember class]] )
user = [(JVChatRoomMember *)[cmessage sender] user]; // if this is a chat room, JVChatRoomPanel makes the sender a member object
else user = [cmessage sender]; // if plugins changed the sending user for some reason, allow it
if( ! [messageString length] && [cmessage ignoreStatus] == JVNotIgnored ) { // plugins decided to excluded this message, decrease the new message counts
_newMessageCount--;
[cmessage release];
return;
}
if( [cmessage isHighlighted] && [cmessage ignoreStatus] == JVNotIgnored ) {
_newHighlightMessageCount++;
NSMutableDictionary *context = [NSMutableDictionary dictionary];
[context setObject:[NSString stringWithFormat:NSLocalizedString( @"%@ Mentioned a Highlight Word", "mention bubble title" ), [user displayName]] forKey:@"title"];
[context setObject:[messageString string] forKey:@"description"];
[context setObject:[NSImage imageNamed:@"activityNewImportant"] forKey:@"image"];
[context setObject:self forKey:@"target"];
[context setObject:NSStringFromSelector( @selector( activate: ) ) forKey:@"action"];
[self performNotification:@"JVChatMentioned" withContextInfo:context];
}
if( [cmessage ignoreStatus] != JVNotIgnored ) {
NSMutableDictionary *context = [NSMutableDictionary dictionary];
[context setObject:( ( [cmessage ignoreStatus] == JVUserIgnored ) ? NSLocalizedString( @"User Ignored", "user ignored bubble title" ) : NSLocalizedString( @"Message Ignored", "message ignored bubble title" ) ) forKey:@"title"];
if( [self isMemberOfClass:[JVChatRoomPanel class]] ) [context setObject:[NSString stringWithFormat:NSLocalizedString( @"%@'s message was ignored in %@.", "chat room user ignored bubble text" ), user, [self title]] forKey:@"description"];
else [context setObject:[NSString stringWithFormat:NSLocalizedString( @"%@'s message was ignored.", "direct chat user ignored bubble text" ), user] forKey:@"description"];
[context setObject:[NSImage imageNamed:@"activity"] forKey:@"image"];
[self performNotification:( ( [cmessage ignoreStatus] == JVUserIgnored ) ? @"JVUserIgnored" : @"JVMessageIgnored" ) withContextInfo:context];
}
[display setScrollbackLimit:[[NSUserDefaults standardUserDefaults] integerForKey:@"JVChatScrollbackLimit"]];
if( [[self transcript] automaticallyWritesChangesToFile] )
[[self transcript] setElementLimit:( [display scrollbackLimit] * 2 )];
JVChatMessage *newMessage = [[self transcript] appendMessage:cmessage];
// [_sqlTestTranscript appendMessage:cmessage];
if( [display appendChatMessage:newMessage] ) {
if( [cmessage isHighlighted] ) [display markScrollbarForMessage:newMessage];
[self quickSearchMatchMessage:newMessage];
_firstMessage = NO; // not the first message anymore
} else if( [cmessage ignoreStatus] == JVNotIgnored ) {
// the style decided to excluded this message, decrease the new message counts
if( [cmessage isHighlighted] ) _newHighlightMessageCount--;
_newMessageCount--;
}
if( [cmessage ignoreStatus] == JVNotIgnored ) {
NSString *voiceIdentifier = [[[MVBuddyListController sharedBuddyList] buddyForUser:user] speechVoice];
if( [voiceIdentifier length] ) {
NSString *text = [cmessage bodyAsPlainText];
if( [cmessage isAction] ) text = [NSString stringWithFormat:@"%@ %@", [[cmessage sender] displayName], text];
[[JVSpeechController sharedSpeechController] startSpeakingString:[cmessage bodyAsPlainText] usingVoice:voiceIdentifier];
}
}
[[NSNotificationCenter defaultCenter] postNotificationName:JVChatMessageWasProcessedNotification object:self userInfo:[NSDictionary dictionaryWithObject:newMessage forKey:@"message"]];
[self _setCurrentMessage:nil];
[cmessage release];
[_windowController reloadListItem:self andChildren:NO];
if( ! [[[_windowController window] representedFilename] length] )
[self _refreshWindowFileProxy];
}
- (void) processIncomingMessage:(JVMutableChatMessage *) message {
if( [[message sender] respondsToSelector:@selector( isLocalUser )] && ! [[message sender] isLocalUser] ) {
if( [message ignoreStatus] == JVNotIgnored && _firstMessage ) {
NSMutableDictionary *context = [NSMutableDictionary dictionary];
[context setObject:NSLocalizedString( @"New Private Message", "first message bubble title" ) forKey:@"title"];
[context setObject:[NSString stringWithFormat:NSLocalizedString( @"%@ wrote you a private message.", "first message bubble text" ), [self title]] forKey:@"description"];
[context setObject:[NSImage imageNamed:@"messageUser"] forKey:@"image"];
[context setObject:[[self windowTitle] stringByAppendingString:@"JVChatPrivateMessage"] forKey:@"coalesceKey"];
[context setObject:self forKey:@"target"];
[context setObject:NSStringFromSelector( @selector( activate: ) ) forKey:@"action"];
[self performNotification:@"JVChatFirstMessage" withContextInfo:context];
} else if( [message ignoreStatus] == JVNotIgnored ) {
NSMutableDictionary *context = [NSMutableDictionary dictionary];
[context setObject:NSLocalizedString( @"Private Message", "new message bubble title" ) forKey:@"title"];
if( [self newMessagesWaiting] == 1 ) [context setObject:[NSString stringWithFormat:NSLocalizedString( @"You have 1 message waiting from %@.", "new single message bubble text" ), [self title]] forKey:@"description"];
[context setObject:[NSString stringWithFormat:NSLocalizedString( @"You have %d messages waiting from %@.", "new messages bubble text" ), [self newMessagesWaiting], [self title]] forKey:@"description"];
[context setObject:[NSImage imageNamed:@"messageUser"] forKey:@"image"];
[context setObject:[[self windowTitle] stringByAppendingString:@"JVChatPrivateMessage"] forKey:@"coalesceKey"];
[context setObject:self forKey:@"target"];
[context setObject:NSStringFromSelector( @selector( activate: ) ) forKey:@"action"];
[self performNotification:@"JVChatAdditionalMessages" withContextInfo:context];
}
}
NSMethodSignature *signature = [NSMethodSignature methodSignatureWithReturnAndArgumentTypes:@encode( void ), @encode( JVMutableChatMessage * ), @encode( id ), nil];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setSelector:@selector( processIncomingMessage:inView: )];
[invocation setArgument:&message atIndex:2];
[invocation setArgument:&self atIndex:3];
[[MVChatPluginManager defaultManager] makePluginsPerformInvocation:invocation stoppingOnFirstSuccessfulReturn:NO];
}
- (void) echoSentMessageToDisplay:(JVMutableChatMessage *) message {
NSString *cformat = nil;
switch( [[self connection] outgoingChatFormat] ) {
case MVChatConnectionDefaultMessageFormat:
case MVChatWindowsIRCMessageFormat:
cformat = NSChatWindowsIRCFormatType;
break;
case MVChatCTCPTwoMessageFormat:
cformat = NSChatCTCPTwoFormatType;
break;
default:
case MVChatNoMessageFormat:
cformat = nil;
}
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:[self encoding]], @"StringEncoding", cformat, @"FormatType", nil];
NSData *msgData = [[message body] chatFormatWithOptions:options]; // we could save this back to the message object before sending
[self addMessageToDisplay:msgData fromUser:[message sender] withAttributes:[message attributes] withIdentifier:[message messageIdentifier] andType:[message type]];
}
- (unsigned int) newMessagesWaiting {
return _newMessageCount;
}
- (unsigned int) newHighlightMessagesWaiting {
return _newHighlightMessageCount;
}
- (JVMutableChatMessage *) currentMessage {
return _currentMessage;
}
#pragma mark -
#pragma mark Notifications Handling
/**
* This method should be used to handle all notification processing. It will check to see if
* notifications have been muted for the given chat room, and if they have, it will not pass
* the notification request on to the JVNotificationController.
*/
- (void) performNotification:(NSString *) identifier withContextInfo:(NSDictionary *) context {
if( ![[self preferenceForKey:@"muted"] boolValue] )
[[JVNotificationController defaultController] performNotification:identifier withContextInfo:context];
}
/**
* Toggles notifications (this is for private, direct user-user chats)
*/
- (IBAction) toggleNotifications:(id) sender {
if( [self preferenceForKey:@"muted"] == [NSNumber numberWithBool:YES] )
[self setPreference:[NSNumber numberWithBool:NO] forKey:@"muted"];
else [self setPreference:[NSNumber numberWithBool:YES] forKey:@"muted"];
}
#pragma mark -
#pragma mark Input Handling
- (IBAction) send:(id) sender {
NSTextStorage *subMsg = nil;
BOOL action = NO;
NSRange range;