forked from gnachman/iTerm2
/
PreferencePanel.m
4364 lines (3907 loc) · 170 KB
/
PreferencePanel.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
// $Id: PreferencePanel.m,v 1.162 2008-10-02 03:48:36 yfabian Exp $
/*
** PreferencePanel.m
**
** Copyright (c) 2002, 2003
**
** Author: Fabian, Ujwal S. Setlur
**
** Project: iTerm
**
** Description: Implements the model and controller for the preference panel.
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdlib.h>
#import "PreferencePanel.h"
#import "NSStringITerm.h"
#import "iTermController.h"
#import "ITAddressBookMgr.h"
#import "iTermKeyBindingMgr.h"
#import "PTYSession.h"
#import "PseudoTerminal.h"
#import "ProfileModel.h"
#import "PasteboardHistory.h"
#import "SessionView.h"
#import "WindowArrangements.h"
#import "TriggerController.h"
#import "SmartSelectionController.h"
#import "TrouterPrefsController.h"
#import "PointerPrefsController.h"
#import "iTermFontPanel.h"
#define CUSTOM_COLOR_PRESETS @"Custom Color Presets"
#define HOTKEY_WINDOW_GENERATED_PROFILE_NAME @"Hotkey Window"
NSString* kDeleteKeyString = @"0x7f-0x0";
static float versionNumber;
@interface NSFileManager (TemporaryDirectory)
- (NSString *)temporaryDirectory;
@end
@implementation NSFileManager (TemporaryDirectory)
- (NSString *)temporaryDirectory
{
// Create a unique directory in the system temporary directory
NSString *guid = [[NSProcessInfo processInfo] globallyUniqueString];
NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:guid];
if (![self createDirectoryAtPath:path withIntermediateDirectories:NO attributes:nil error:nil]) {
return nil;
}
return path;
}
@end
@implementation PreferencePanel
+ (PreferencePanel*)sharedInstance;
{
static PreferencePanel* shared = nil;
if (!shared) {
shared = [[self alloc] initWithDataSource:[ProfileModel sharedInstance]
userDefaults:[NSUserDefaults standardUserDefaults]];
shared->oneBookmarkMode = NO;
}
return shared;
}
+ (PreferencePanel*)sessionsInstance;
{
static PreferencePanel* shared = nil;
if (!shared) {
shared = [[self alloc] initWithDataSource:[ProfileModel sessionsInstance]
userDefaults:nil];
shared->oneBookmarkMode = YES;
}
return shared;
}
/*
Static method to copy old preferences file, iTerm.plist or net.sourceforge.iTerm.plist, to new
preferences file, com.googlecode.iterm2.plist
*/
+ (BOOL)migratePreferences
{
NSString *prefDir = [[NSHomeDirectory()
stringByAppendingPathComponent:@"Library"]
stringByAppendingPathComponent:@"Preferences"];
NSString *reallyOldPrefs = [prefDir stringByAppendingPathComponent:@"iTerm.plist"];
NSString *somewhatOldPrefs = [prefDir stringByAppendingPathComponent:@"net.sourceforge.iTerm.plist"];
NSString *newPrefs = [prefDir stringByAppendingPathComponent:@"com.googlecode.iterm2.plist"];
NSFileManager *mgr = [NSFileManager defaultManager];
if ([mgr fileExistsAtPath:newPrefs]) {
return NO;
}
NSString* source;
if ([mgr fileExistsAtPath:somewhatOldPrefs]) {
source = somewhatOldPrefs;
} else if ([mgr fileExistsAtPath:reallyOldPrefs]) {
source = reallyOldPrefs;
} else {
return NO;
}
NSLog(@"Preference file migrated");
[mgr copyItemAtPath:source toPath:newPrefs error:nil];
[NSUserDefaults resetStandardUserDefaults];
return (YES);
}
- (id)initWithDataSource:(ProfileModel*)model userDefaults:(NSUserDefaults*)userDefaults
{
unsigned int storedMajorVersion = 0, storedMinorVersion = 0, storedMicroVersion = 0;
self = [super init];
dataSource = model;
prefs = userDefaults;
oneBookmarkOnly = NO;
if (userDefaults) {
[self loadPrefs];
}
[self readPreferences];
if (defaultEnableBonjour == YES) {
[[ITAddressBookMgr sharedInstance] locateBonjourServices];
}
// get the version
NSDictionary *myDict = [[NSBundle bundleForClass:[self class]] infoDictionary];
versionNumber = [(NSNumber *)[myDict objectForKey:@"CFBundleVersion"] floatValue];
if (prefs && [prefs objectForKey: @"iTerm Version"]) {
sscanf([[prefs objectForKey: @"iTerm Version"] cString], "%d.%d.%d", &storedMajorVersion, &storedMinorVersion, &storedMicroVersion);
// briefly, version 0.7.0 was stored as 0.70
if(storedMajorVersion == 0 && storedMinorVersion == 70)
storedMinorVersion = 7;
}
//NSLog(@"Stored version = %d.%d.%d", storedMajorVersion, storedMinorVersion, storedMicroVersion);
// sync the version number
if (prefs) {
[prefs setObject: [myDict objectForKey:@"CFBundleVersion"] forKey: @"iTerm Version"];
}
[toolbar setSelectedItemIdentifier:globalToolbarId];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(_reloadURLHandlers:)
name:@"iTermReloadAddressBook"
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(_savedArrangementChanged:)
name:@"iTermSavedArrangementChanged"
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyBindingsChanged)
name:@"iTermKeyBindingsChanged"
object:nil];
return (self);
}
- (void)_savedArrangementChanged:(id)sender
{
[openArrangementAtStartup setState:defaultOpenArrangementAtStartup ? NSOnState : NSOffState];
[openArrangementAtStartup setEnabled:[WindowArrangements count] > 0];
if ([WindowArrangements count] == 0) {
[openArrangementAtStartup setState:NO];
}
}
- (void)setOneBookmarkOnly
{
oneBookmarkOnly = YES;
[self showBookmarks];
[toolbar setVisible:NO];
[editAdvancedConfigButton setHidden:YES];
[bookmarksTableView setHidden:YES];
[addBookmarkButton setHidden:YES];
[removeBookmarkButton setHidden:YES];
[bookmarksPopup setHidden:YES];
[bookmarkDirectory setHidden:YES];
[bookmarkShortcutKeyLabel setHidden:YES];
[bookmarkShortcutKeyModifiersLabel setHidden:YES];
[bookmarkTagsLabel setHidden:YES];
[bookmarkCommandLabel setHidden:YES];
[initialTextLabel setHidden:YES];
[bookmarkDirectoryLabel setHidden:YES];
[bookmarkShortcutKey setHidden:YES];
[tags setHidden:YES];
[bookmarkCommandType setHidden:YES];
[bookmarkCommand setHidden:YES];
[initialText setHidden:YES];
[bookmarkDirectoryType setHidden:YES];
[bookmarkDirectory setHidden:YES];
[bookmarkUrlSchemes setHidden:YES];
[bookmarkUrlSchemesHeaderLabel setHidden:YES];
[bookmarkUrlSchemesLabel setHidden:YES];
[copyToProfileButton setHidden:NO];
[setProfileLabel setHidden:NO];
[setProfileBookmarkListView setHidden:NO];
[changeProfileButton setHidden:NO];
[columnsLabel setTextColor:[NSColor disabledControlTextColor]];
[rowsLabel setTextColor:[NSColor disabledControlTextColor]];
[columnsField setEnabled:NO];
[rowsField setEnabled:NO];
[windowTypeButton setEnabled:NO];
[screenLabel setTextColor:[NSColor disabledControlTextColor]];
[screenButton setEnabled:NO];
[spaceButton setEnabled:NO];
[spaceLabel setTextColor:[NSColor disabledControlTextColor]];
[windowTypeLabel setTextColor:[NSColor disabledControlTextColor]];
[newWindowttributesHeader setTextColor:[NSColor disabledControlTextColor]];
NSRect newFrame = [bookmarksSettingsTabViewParent frame];
newFrame.origin.x = 0;
[bookmarksSettingsTabViewParent setFrame:newFrame];
newFrame = [[self window] frame];
newFrame.size.width = [bookmarksSettingsTabViewParent frame].size.width + 26;
[[self window] setFrame:newFrame display:YES];
}
- (void)_addColorPresetsInDict:(NSDictionary*)presetsDict toMenu:(NSMenu*)theMenu
{
for (NSString* key in [[presetsDict allKeys] sortedArrayUsingSelector:@selector(compare:)]) {
NSMenuItem* presetItem = [[NSMenuItem alloc] initWithTitle:key action:@selector(loadColorPreset:) keyEquivalent:@""];
[theMenu addItem:presetItem];
[presetItem release];
}
}
- (void)_rebuildColorPresetsMenu
{
while ([presetsMenu numberOfItems] > 1) {
[presetsMenu removeItemAtIndex:1];
}
NSString* plistFile = [[NSBundle bundleForClass: [self class]] pathForResource:@"ColorPresets"
ofType:@"plist"];
NSDictionary* presetsDict = [NSDictionary dictionaryWithContentsOfFile:plistFile];
[self _addColorPresetsInDict:presetsDict toMenu:presetsMenu];
NSDictionary* customPresets = [[NSUserDefaults standardUserDefaults] objectForKey:CUSTOM_COLOR_PRESETS];
if (customPresets && [customPresets count] > 0) {
[presetsMenu addItem:[NSMenuItem separatorItem]];
[self _addColorPresetsInDict:customPresets toMenu:presetsMenu];
}
[presetsMenu addItem:[NSMenuItem separatorItem]];
[presetsMenu addItem:[[[NSMenuItem alloc] initWithTitle:@"Import..."
action:@selector(importColorPreset:)
keyEquivalent:@""] autorelease]];
[presetsMenu addItem:[[[NSMenuItem alloc] initWithTitle:@"Export..."
action:@selector(exportColorPreset:)
keyEquivalent:@""] autorelease]];
[presetsMenu addItem:[[[NSMenuItem alloc] initWithTitle:@"Delete Preset..."
action:@selector(deleteColorPreset:)
keyEquivalent:@""] autorelease]];
[presetsMenu addItem:[[[NSMenuItem alloc] initWithTitle:@"Visit Online Gallery"
action:@selector(visitGallery:)
keyEquivalent:@""] autorelease]];
}
- (void)_addColorPreset:(NSString*)presetName withColors:(NSDictionary*)theDict
{
NSMutableDictionary* customPresets = [NSMutableDictionary dictionaryWithDictionary:[[NSUserDefaults standardUserDefaults] objectForKey:CUSTOM_COLOR_PRESETS]];
if (!customPresets) {
customPresets = [NSMutableDictionary dictionaryWithCapacity:1];
}
int i = 1;
NSString* temp = presetName;
while ([customPresets objectForKey:temp]) {
++i;
temp = [NSString stringWithFormat:@"%@ (%d)", presetName, i];
}
[customPresets setObject:theDict forKey:temp];
[[NSUserDefaults standardUserDefaults] setObject:customPresets forKey:CUSTOM_COLOR_PRESETS];
[self _rebuildColorPresetsMenu];
}
- (NSString*)_presetNameFromFilename:(NSString*)filename
{
return [[filename stringByDeletingPathExtension] lastPathComponent];
}
- (BOOL)importColorPresetFromFile:(NSString*)filename
{
NSDictionary* aDict = [NSDictionary dictionaryWithContentsOfFile:filename];
if (!aDict) {
NSRunAlertPanel(@"Import Failed.",
@"The selected file could not be read or did not contain a valid color scheme.",
@"OK",
nil,
nil,
nil);
return NO;
} else {
[self _addColorPreset:[self _presetNameFromFilename:filename]
withColors:aDict];
return YES;
}
}
- (void)importColorPreset:(id)sender
{
// Create the File Open Dialog class.
NSOpenPanel* openDlg = [NSOpenPanel openPanel];
// Set options.
[openDlg setCanChooseFiles:YES];
[openDlg setCanChooseDirectories:NO];
[openDlg setAllowsMultipleSelection:YES];
[openDlg setAllowedFileTypes:[NSArray arrayWithObject:@"itermcolors"]];
// Display the dialog. If the OK button was pressed,
// process the files.
if ([openDlg runModalForDirectory:nil file:nil] == NSOKButton) {
// Get an array containing the full filenames of all
// files and directories selected.
for (NSString* filename in [openDlg filenames]) {
[self importColorPresetFromFile:filename];
}
}
}
- (void)_exportColorPresetToFile:(NSString*)filename
{
NSArray* colorKeys = [NSArray arrayWithObjects:
@"Ansi 0 Color",
@"Ansi 1 Color",
@"Ansi 2 Color",
@"Ansi 3 Color",
@"Ansi 4 Color",
@"Ansi 5 Color",
@"Ansi 6 Color",
@"Ansi 7 Color",
@"Ansi 8 Color",
@"Ansi 9 Color",
@"Ansi 10 Color",
@"Ansi 11 Color",
@"Ansi 12 Color",
@"Ansi 13 Color",
@"Ansi 14 Color",
@"Ansi 15 Color",
@"Foreground Color",
@"Background Color",
@"Bold Color",
@"Selection Color",
@"Selected Text Color",
@"Cursor Color",
@"Cursor Text Color",
nil];
NSColorWell* wells[] = {
ansi0Color,
ansi1Color,
ansi2Color,
ansi3Color,
ansi4Color,
ansi5Color,
ansi6Color,
ansi7Color,
ansi8Color,
ansi9Color,
ansi10Color,
ansi11Color,
ansi12Color,
ansi13Color,
ansi14Color,
ansi15Color,
foregroundColor,
backgroundColor,
boldColor,
selectionColor,
selectedTextColor,
cursorColor,
cursorTextColor
};
NSMutableDictionary* theDict = [NSMutableDictionary dictionaryWithCapacity:24];
int i = 0;
for (NSString* colorKey in colorKeys) {
NSColor* theColor = [[wells[i++] color] colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
double r = [theColor redComponent];
double g = [theColor greenComponent];
double b = [theColor blueComponent];
NSDictionary* colorDict = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithDouble:r], @"Red Component",
[NSNumber numberWithDouble:g], @"Green Component",
[NSNumber numberWithDouble:b], @"Blue Component",
nil];
[theDict setObject:colorDict forKey:colorKey];
}
if (![theDict writeToFile:filename atomically:NO]) {
NSRunAlertPanel(@"Save Failed.",
[NSString stringWithFormat:@"Could not save to %@", filename],
@"OK",
nil,
nil,
nil);
}
}
- (void)exportColorPreset:(id)sender
{
// Create the File Open Dialog class.
NSSavePanel* saveDlg = [NSSavePanel savePanel];
// Set options.
[saveDlg setAllowedFileTypes:[NSArray arrayWithObject:@"itermcolors"]];
if ([saveDlg runModalForDirectory:nil file:nil] == NSOKButton) {
[self _exportColorPresetToFile:[saveDlg filename]];
}
}
- (void)deleteColorPreset:(id)sender
{
NSDictionary* customPresets = [[NSUserDefaults standardUserDefaults] objectForKey:CUSTOM_COLOR_PRESETS];
if (!customPresets || [customPresets count] == 0) {
NSRunAlertPanel(@"No deletable color presets.",
@"You cannot erase the built-in presets and no custom presets have been imported.",
@"OK",
nil,
nil);
return;
}
NSAlert *alert = [NSAlert alertWithMessageText:@"Select a preset to delete:"
defaultButton:@"OK"
alternateButton:@"Cancel"
otherButton:nil
informativeTextWithFormat:@""];
NSPopUpButton* pub = [[[NSPopUpButton alloc] init] autorelease];
for (NSString* key in [[customPresets allKeys] sortedArrayUsingSelector:@selector(compare:)]) {
[pub addItemWithTitle:key];
}
[pub sizeToFit];
[alert setAccessoryView:pub];
NSInteger button = [alert runModal];
if (button == NSAlertDefaultReturn) {
NSMutableDictionary* newCustom = [NSMutableDictionary dictionaryWithDictionary:customPresets];
[newCustom removeObjectForKey:[[pub selectedItem] title]];
[[NSUserDefaults standardUserDefaults] setObject:newCustom
forKey:CUSTOM_COLOR_PRESETS];
[self _rebuildColorPresetsMenu];
}
}
- (void)visitGallery:(id)sender
{
NSString* COLOR_GALLERY_URL = @"http://www.iterm2.com/colorgallery";
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:COLOR_GALLERY_URL]];
}
- (BOOL)_dirIsWritable:(NSString *)dir
{
if ([[dir stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] length] == 0) {
return NO;
}
NSString *filename = [NSString stringWithFormat:@"%@/.testwritable.%d", dir, (int)getpid()];
NSError *error = nil;
[@"test" writeToFile:filename
atomically:YES
encoding:NSUTF8StringEncoding
error:&error];
if (error) {
return NO;
}
unlink([filename UTF8String]);
return YES;
}
- (BOOL)_logDirIsWritable
{
return [self _dirIsWritable:[logDir stringValue]];
}
- (void)_updateLogDirWarning
{
[logDirWarning setHidden:[autoLog state] == NSOffState || [self _logDirIsWritable]];
}
- (BOOL)_prefsDirIsWritable
{
return [self _dirIsWritable:[defaultPrefsCustomFolder stringByExpandingTildeInPath]];
}
- (void)_updatePrefsDirWarning
{
if (([defaultPrefsCustomFolder hasPrefix:@"http://"] ||
[defaultPrefsCustomFolder hasPrefix:@"https://"]) &&
[NSURL URLWithString:defaultPrefsCustomFolder]) {
// Don't warn about URLs, too expensive to check
[prefsDirWarning setHidden:YES];
return;
}
[prefsDirWarning setHidden:!defaultLoadPrefsFromCustomFolder || [self _prefsDirIsWritable]];
}
- (void)setScreens
{
int selectedTag = [screenButton selectedTag];
[screenButton removeAllItems];
int i = 0;
[screenButton addItemWithTitle:@"No Preference"];
[[screenButton lastItem] setTag:-1];
for (NSScreen* screen in [NSScreen screens]) {
if (i == 0) {
[screenButton addItemWithTitle:[NSString stringWithFormat:@"Main Screen", i]];
} else {
[screenButton addItemWithTitle:[NSString stringWithFormat:@"Screen %d", i+1]];
}
[[screenButton lastItem] setTag:i];
i++;
}
if (selectedTag >= 0 && selectedTag < i) {
[screenButton selectItemWithTag:selectedTag];
} else {
[screenButton selectItemWithTag:-1];
}
if ([windowTypeButton selectedTag] == WINDOW_TYPE_NORMAL) {
[screenButton setEnabled:NO];
[screenLabel setTextColor:[NSColor disabledControlTextColor]];
[screenButton selectItemWithTag:-1];
} else if (!oneBookmarkOnly) {
[screenButton setEnabled:YES];
[screenLabel setTextColor:[NSColor blackColor]];
}
}
- (void)awakeFromNib
{
[self window];
[[self window] setCollectionBehavior:NSWindowCollectionBehaviorMoveToActiveSpace];
NSAssert(bookmarksTableView, @"Null table view");
[bookmarksTableView setUnderlyingDatasource:dataSource];
bookmarksToolbarId = [bookmarksToolbarItem itemIdentifier];
globalToolbarId = [globalToolbarItem itemIdentifier];
appearanceToolbarId = [appearanceToolbarItem itemIdentifier];
keyboardToolbarId = [keyboardToolbarItem itemIdentifier];
arrangementsToolbarId = [arrangementsToolbarItem itemIdentifier];
mouseToolbarId = [mouseToolbarItem itemIdentifier];
[globalToolbarItem setEnabled:YES];
[toolbar setSelectedItemIdentifier:globalToolbarId];
// add list of encodings
NSEnumerator *anEnumerator;
NSNumber *anEncoding;
[characterEncoding removeAllItems];
anEnumerator = [[[iTermController sharedInstance] sortedEncodingList] objectEnumerator];
while ((anEncoding = [anEnumerator nextObject]) != NULL) {
[characterEncoding addItemWithTitle:[NSString localizedNameOfStringEncoding:[anEncoding unsignedIntValue]]];
[[characterEncoding lastItem] setTag:[anEncoding unsignedIntValue]];
}
[self setScreens];
[keyMappings setDoubleAction:@selector(editKeyMapping:)];
[globalKeyMappings setDoubleAction:@selector(editKeyMapping:)];
keyString = nil;
[copyTo allowMultipleSelections];
// Add presets to preset color selection.
[self _rebuildColorPresetsMenu];
// Add preset keybindings to button-popup-list.
NSArray* presetArray = [iTermKeyBindingMgr presetKeyMappingsNames];
if (presetArray != nil) {
[presetsPopupButton addItemsWithTitles:presetArray];
} else {
[presetsPopupButton setEnabled:NO];
[presetsErrorLabel setFont:[NSFont boldSystemFontOfSize:12]];
[presetsErrorLabel setStringValue:@"PresetKeyMappings.plist failed to load"];
}
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleWindowWillCloseNotification:)
name:NSWindowWillCloseNotification object: [self window]];
if (oneBookmarkMode) {
[self setOneBookmarkOnly];
}
[[tags cell] setDelegate:self];
[tags setDelegate:self];
if (IsLionOrLater()) {
[lionStyleFullscreen setHidden:NO];
} else {
[lionStyleFullscreen setHidden:YES];
}
[initialText setContinuous:YES];
[blurRadius setContinuous:YES];
[transparency setContinuous:YES];
[blend setContinuous:YES];
[dimmingAmount setContinuous:YES];
[minimumContrast setContinuous:YES];
[prefsCustomFolder setEnabled:defaultLoadPrefsFromCustomFolder];
[browseCustomFolder setEnabled:defaultLoadPrefsFromCustomFolder];
[pushToCustomFolder setEnabled:defaultLoadPrefsFromCustomFolder];
[self _updatePrefsDirWarning];
}
- (void)handleWindowWillCloseNotification:(NSNotification *)notification
{
// This is so tags get saved because Cocoa doesn't notify you that the
// field changed unless the user presses enter twice in it (!).
[self bookmarkSettingChanged:nil];
}
- (void)genericCloseSheet:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
{
[action setTitle:@"Ignore"];
[sheet close];
}
- (BOOL)_originatorIsBookmark:(id)originator
{
return originator == addNewMapping || originator == keyMappings;
}
- (void)setAdvancedBookmarkMatrix:(NSMatrix *)matrix withValue:(NSString *)value
{
if ([value isEqualToString:@"Yes"]) {
[matrix selectCellWithTag:0];
} else if ([value isEqualToString:@"Recycle"]) {
[matrix selectCellWithTag:2];
} else {
[matrix selectCellWithTag:1];
}
}
- (void)safelySetStringValue:(NSString *)value in:(NSTextField *)field
{
if (value) {
[field setStringValue:value];
} else {
[field setStringValue:@""];
}
}
- (IBAction)showAdvancedWorkingDirConfigPanel:(id)sender
{
// Populate initial values
Profile* bookmark = [dataSource bookmarkWithGuid:[bookmarksTableView selectedGuid]];
[self setAdvancedBookmarkMatrix:awdsWindowDirectoryType
withValue:[bookmark objectForKey:KEY_AWDS_WIN_OPTION]];
[self safelySetStringValue:[bookmark objectForKey:KEY_AWDS_WIN_DIRECTORY]
in:awdsWindowDirectory];
[self setAdvancedBookmarkMatrix:awdsTabDirectoryType
withValue:[bookmark objectForKey:KEY_AWDS_TAB_OPTION]];
[self safelySetStringValue:[bookmark objectForKey:KEY_AWDS_TAB_DIRECTORY]
in:awdsTabDirectory];
[self setAdvancedBookmarkMatrix:awdsPaneDirectoryType
withValue:[bookmark objectForKey:KEY_AWDS_PANE_OPTION]];
[self safelySetStringValue:[bookmark objectForKey:KEY_AWDS_PANE_DIRECTORY]
in:awdsPaneDirectory];
[NSApp beginSheet:advancedWorkingDirSheet_
modalForWindow:[self window]
modalDelegate:self
didEndSelector:@selector(advancedWorkingDirSheetClosed:returnCode:contextInfo:)
contextInfo:nil];
}
- (void)setValueInBookmark:(NSMutableDictionary *)dict
forAdvancedWorkingDirMatrix:(NSMatrix *)matrix
key:(NSString *)key
{
NSString *value;
NSString *values[] = { @"Yes", @"No", @"Recycle" };
value = values[matrix.selectedTag];
[dict setObject:value forKey:key];
}
- (IBAction)closeAdvancedWorkingDirSheet:(id)sender
{
Profile* bookmark = [dataSource bookmarkWithGuid:[bookmarksTableView selectedGuid]];
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:bookmark];
[self setValueInBookmark:dict
forAdvancedWorkingDirMatrix:awdsWindowDirectoryType
key:KEY_AWDS_WIN_OPTION];
[dict setObject:[awdsWindowDirectory stringValue] forKey:KEY_AWDS_WIN_DIRECTORY];
[self setValueInBookmark:dict
forAdvancedWorkingDirMatrix:awdsTabDirectoryType
key:KEY_AWDS_TAB_OPTION];
[dict setObject:[awdsTabDirectory stringValue] forKey:KEY_AWDS_TAB_DIRECTORY];
[self setValueInBookmark:dict
forAdvancedWorkingDirMatrix:awdsPaneDirectoryType
key:KEY_AWDS_PANE_OPTION];
[dict setObject:[awdsPaneDirectory stringValue] forKey:KEY_AWDS_PANE_DIRECTORY];
[dataSource setBookmark:dict withGuid:[bookmark objectForKey:KEY_GUID]];
[self bookmarkSettingChanged:nil];
[NSApp endSheet:advancedWorkingDirSheet_];
}
- (void)advancedWorkingDirSheetClosed:(NSWindow *)sheet
returnCode:(int)returnCode
contextInfo:(void *)contextInfo
{
[sheet close];
}
- (IBAction)editSmartSelection:(id)sender
{
[smartSelectionWindowController_ window];
[smartSelectionWindowController_ windowWillOpen];
[NSApp beginSheet:[smartSelectionWindowController_ window]
modalForWindow:[self window]
modalDelegate:self
didEndSelector:@selector(advancedTabCloseSheet:returnCode:contextInfo:)
contextInfo:nil];
}
- (IBAction)closeSmartSelectionSheet:(id)sender
{
[NSApp endSheet:[smartSelectionWindowController_ window]];
}
- (void)advancedTabCloseSheet:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
{
[sheet close];
}
- (IBAction)editTriggers:(id)sender
{
[NSApp beginSheet:[triggerWindowController_ window]
modalForWindow:[self window]
modalDelegate:self
didEndSelector:@selector(advancedTabCloseSheet:returnCode:contextInfo:)
contextInfo:nil];
}
- (IBAction)closeTriggersSheet:(id)sender
{
[NSApp endSheet:[triggerWindowController_ window]];
}
- (IBAction)closeCurrentSession:(id)sender
{
if ([[self window] isKeyWindow]) {
[self closeWindow:self];
}
}
+ (void)populatePopUpButtonWithBookmarks:(NSPopUpButton*)button selectedGuid:(NSString*)selectedGuid
{
int selectedIndex = 0;
int i = 0;
[button removeAllItems];
NSArray* bookmarks = [[ProfileModel sharedInstance] bookmarks];
for (Profile* bookmark in bookmarks) {
int j = 0;
NSString* temp;
do {
if (j == 0) {
temp = [bookmark objectForKey:KEY_NAME];
} else {
temp = [NSString stringWithFormat:@"%@ (%d)", [bookmark objectForKey:KEY_NAME], j];
}
j++;
} while ([button indexOfItemWithTitle:temp] != -1);
[button addItemWithTitle:temp];
NSMenuItem* item = [button lastItem];
[item setRepresentedObject:[bookmark objectForKey:KEY_GUID]];
if ([[item representedObject] isEqualToString:selectedGuid]) {
selectedIndex = i;
}
i++;
}
[button selectItemAtIndex:selectedIndex];
}
+ (void)recursiveAddMenu:(NSMenu *)menu
toButtonMenu:(NSMenu *)buttonMenu
depth:(int)depth{
for (NSMenuItem* item in [menu itemArray]) {
if ([item isSeparatorItem]) {
continue;
}
if ([[item title] isEqualToString:@"Services"] || // exclude services menu
isnumber([[item title] characterAtIndex:0])) { // exclude windows in window menu
continue;
}
NSMenuItem *theItem = [[[NSMenuItem alloc] init] autorelease];
[theItem setTitle:[item title]];
[theItem setIndentationLevel:depth];
if ([item hasSubmenu]) {
if (depth == 0 && [[buttonMenu itemArray] count]) {
[buttonMenu addItem:[NSMenuItem separatorItem]];
}
[theItem setEnabled:NO];
[buttonMenu addItem:theItem];
[PreferencePanel recursiveAddMenu:[item submenu]
toButtonMenu:buttonMenu
depth:depth + 1];
} else {
[buttonMenu addItem:theItem];
}
}
}
+ (void)populatePopUpButtonWithMenuItems:(NSPopUpButton *)button
selectedValue:(NSString *)selectedValue {
[PreferencePanel recursiveAddMenu:[NSApp mainMenu]
toButtonMenu:[button menu]
depth:0];
if (selectedValue) {
NSMenuItem *theItem = [[button menu] itemWithTitle:selectedValue];
if (theItem) {
[button setTitle:selectedValue];
[theItem setState:NSOnState];
}
}
}
- (void)editKeyMapping:(id)sender
{
int rowIndex;
modifyMappingOriginator = sender;
if ([self _originatorIsBookmark:sender]) {
rowIndex = [keyMappings selectedRow];
} else {
rowIndex = [globalKeyMappings selectedRow];
}
if (rowIndex < 0) {
[self addNewMapping:sender];
return;
}
[keyPress setStringValue:[self formattedKeyCombinationForRow:rowIndex originator:sender]];
if (keyString) {
[keyString release];
}
// For some reason, the first item is checked by default. Make sure every
// item is unchecked before making a selection.
for (NSMenuItem* item in [action itemArray]) {
[item setState:NSOffState];
}
keyString = [[self keyComboAtIndex:rowIndex originator:sender] copy];
int theTag = [[[self keyInfoAtIndex:rowIndex originator:sender] objectForKey:@"Action"] intValue];
[action selectItemWithTag:theTag];
// Can't search for an item with tag 0 using the API, so search manually.
for (NSMenuItem* anItem in [[action menu] itemArray]) {
if (![anItem isSeparatorItem] && [anItem tag] == theTag) {
[action setTitle:[anItem title]];
break;
}
}
NSString* text = [[self keyInfoAtIndex:rowIndex originator:sender] objectForKey:@"Text"];
[valueToSend setStringValue:text ? text : @""];
[PreferencePanel populatePopUpButtonWithBookmarks:bookmarkPopupButton
selectedGuid:text];
[PreferencePanel populatePopUpButtonWithMenuItems:menuToSelect
selectedValue:text];
[self updateValueToSend];
newMapping = NO;
[NSApp beginSheet:editKeyMappingWindow
modalForWindow:[self window]
modalDelegate:self
didEndSelector:@selector(genericCloseSheet:returnCode:contextInfo:)
contextInfo:nil];
}
- (IBAction)changeProfile:(id)sender
{
NSString* origGuid = [bookmarksTableView selectedGuid];
Profile* origBookmark = [dataSource bookmarkWithGuid:origGuid];
NSString *theName = [[[origBookmark objectForKey:KEY_NAME] copy] autorelease];
NSString *guid = [setProfileBookmarkListView selectedGuid];
Profile *bookmark = [[ProfileModel sharedInstance] bookmarkWithGuid:guid];
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:bookmark];
[dict setObject:theName forKey:KEY_NAME];
[dict setObject:origGuid forKey:KEY_GUID];
// Change the dict in the sessions bookmarks so that if you copy it back, it gets copied to
// the new profile.
[dict setObject:guid forKey:KEY_ORIGINAL_GUID];
[dataSource setBookmark:dict withGuid:origGuid];
[self updateBookmarkFields:dict];
[self bookmarkSettingChanged:nil];
}
- (BOOL)_warnAboutDeleteOverride
{
if ([[NSUserDefaults standardUserDefaults] objectForKey:@"NeverWarnAboutDeleteOverride"] != nil) {
return YES;
}
switch (NSRunAlertPanel(@"Some Profile Overrides the Delete Key",
@"Careful! You have at least one profile that has a key mapping for the Delete key. It will take precedence over this setting. Check your profiles' keyboard settings if Delete does not work as expected.",
@"OK",
@"Never warn me again",
@"Cancel",
nil)) {
case NSAlertDefaultReturn:
return YES;
case NSAlertAlternateReturn:
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:YES] forKey:@"NeverWarnAboutDeleteOverride"];
return YES;
case NSAlertOtherReturn:
return NO;
}
return YES;
}
- (BOOL)_warnAboutOverride
{
if ([[NSUserDefaults standardUserDefaults] objectForKey:@"NeverWarnAboutOverrides"] != nil) {
return YES;
}
switch (NSRunAlertPanel(@"Overriding Global Shortcut",
@"The keyboard shortcut you have set for this profile will take precedence over an existing shortcut for the same key combination in a global shortcut.",
@"OK",
@"Never warn me again",
@"Cancel",
nil)) {
case NSAlertDefaultReturn:
return YES;
case NSAlertAlternateReturn:
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:YES] forKey:@"NeverWarnAboutOverrides"];
return YES;
case NSAlertOtherReturn:
return NO;
}
return YES;
}
- (BOOL)_warnAboutPossibleOverride
{
if ([[NSUserDefaults standardUserDefaults] objectForKey:@"NeverWarnAboutPossibleOverrides"] != nil) {
return YES;
}
switch (NSRunAlertPanel(@"Some Profile Overrides this Shortcut",
@"The global keyboard shortcut you have set is overridden by at least one profile. Check your profiles' keyboard settings if it does not work as expected.",
@"OK",
@"Never warn me again",
@"Cancel",
nil)) {
case NSAlertDefaultReturn:
return YES;
case NSAlertAlternateReturn:
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:YES] forKey:@"NeverWarnAboutPossibleOverrides"];
return YES;
case NSAlertOtherReturn:
return NO;
}
return YES;
}
- (BOOL)_anyBookmarkHasKeyMapping:(NSString*)theString
{
for (Profile* bookmark in [[ProfileModel sharedInstance] bookmarks]) {
if ([iTermKeyBindingMgr haveKeyMappingForKeyString:theString inBookmark:bookmark]) {
return YES;
}
}
return NO;
}
- (IBAction)saveKeyMapping:(id)sender
{