From 60664fa7cb35a7c6806e7883c8b3e759e7a0c5a1 Mon Sep 17 00:00:00 2001 From: Brett Terpstra Date: Fri, 20 May 2011 00:21:52 -0500 Subject: [PATCH] Squashed commit of the following: commit 772c5030ea058861186787d85361bbad17699358 Author: Brett Terpstra Date: Fri May 20 00:20:44 2011 -0500 Random license update. commit 3c82201c7a8878938ddc5c2e0ecd579b5447f117 Author: Brett Terpstra Date: Thu May 19 23:30:09 2011 -0500 Cleaning up some loose ends; removing some now-unnecessary functions. Signed-off-by: Brett Terpstra commit ba6047889fa808d77f052a4f0adb6534967647c0 Author: Brett Terpstra Date: Thu May 19 22:48:33 2011 -0500 Pretty close to implementing the ODBEditor method for external editing. Signed-off-by: Brett Terpstra --- AppController.h | 1 + AppController.m | 176 +++---- English.lproj/MainMenu.xib | 90 ++-- English.lproj/Preferences.xib | 382 +++++++-------- ExternalEditorListController.h | 77 +++ ExternalEditorListController.m | 442 ++++++++++++++++++ GlobalPrefs.h | 28 +- GlobalPrefs.m | 105 ++--- LabelColumnCell.m | 21 +- LabelsListController.h | 27 +- LabelsListController.m | 87 +++- Notation.xcodeproj/project.pbxproj | 38 ++ NotationController.m | 46 +- NoteObject.h | 37 +- NoteObject.m | 217 ++++----- NotesTableView.h | 31 +- NotesTableView.m | 135 +++--- ODBEditor/LICENSE.txt | 2 + ODBEditor/NSAppleEventDescriptor-Extensions.h | 8 + ODBEditor/NSAppleEventDescriptor-Extensions.m | 16 + ODBEditor/ODBEditor.h | 58 +++ ODBEditor/ODBEditor.m | 398 ++++++++++++++++ ODBEditor/ODBEditorSuite.h | 33 ++ PrefsWindowController.h | 5 +- PrefsWindowController.m | 65 +-- PreviewController.m | 2 + TemporaryFileCachePreparer.h | 60 +++ TemporaryFileCachePreparer.m | 278 +++++++++++ UnifiedCell.h | 25 +- UnifiedCell.m | 84 ++-- de.lproj/MainMenu.xib | 135 +++++- de.lproj/Preferences.xib | 350 +++++++------- fr.lproj/MainMenu.xib | 136 +++++- fr.lproj/Preferences.xib | 350 +++++++------- pt.lproj/MainMenu.xib | 136 +++++- pt.lproj/Preferences.xib | 350 +++++++------- 36 files changed, 3117 insertions(+), 1314 deletions(-) create mode 100644 ExternalEditorListController.h create mode 100644 ExternalEditorListController.m create mode 100644 ODBEditor/LICENSE.txt create mode 100644 ODBEditor/NSAppleEventDescriptor-Extensions.h create mode 100644 ODBEditor/NSAppleEventDescriptor-Extensions.m create mode 100644 ODBEditor/ODBEditor.h create mode 100644 ODBEditor/ODBEditor.m create mode 100755 ODBEditor/ODBEditorSuite.h create mode 100755 TemporaryFileCachePreparer.h create mode 100755 TemporaryFileCachePreparer.m diff --git a/AppController.h b/AppController.h index 202917f1..3672ed79 100755 --- a/AppController.h +++ b/AppController.h @@ -136,6 +136,7 @@ void outletObjectAwoke(id sender); - (IBAction)copyNoteLink:(id)sender; - (IBAction)exportNote:(id)sender; - (IBAction)revealNote:(id)sender; +- (IBAction)editNoteExternally:(id)sender; - (IBAction)printNote:(id)sender; - (IBAction)tagNote:(id)sender; - (IBAction)importNotes:(id)sender; diff --git a/AppController.m b/AppController.m index aebdb09b..152499b0 100755 --- a/AppController.m +++ b/AppController.m @@ -28,6 +28,7 @@ #import "NSFileManager_NV.h" #import "EncodingsManager.h" #import "ExporterManager.h" +#import "ExternalEditorListController.h" #import "NSData_transformations.h" #import "BufferUtils.h" #import "LinkingEditor.h" @@ -342,16 +343,11 @@ - (void)runDelayedUIActionsAfterLaunch { } } // add elasticthreads' menuitems - NSMenuItem *theMenuItem = [[[NSMenuItem alloc] initWithTitle:@"Open Note in TextEdit" - action:@selector(openFileInEditor:) keyEquivalent:@"O"] autorelease]; - if ([prefsController textEditor]) { - [theMenuItem setTitle:[@"Open Note in " stringByAppendingString:[prefsController textEditor]]]; - } + NSMenuItem *theMenuItem = [[[NSMenuItem alloc] init] autorelease]; [theMenuItem setTarget:self]; NSMenu *notesMenu = [[[NSApp mainMenu] itemWithTag:NOTES_MENU_ID] submenu]; - [notesMenu insertItem:theMenuItem atIndex:9]; theMenuItem = [theMenuItem copy]; - [statBarMenu insertItem:theMenuItem atIndex:4]; +// [statBarMenu insertItem:theMenuItem atIndex:4]; [theMenuItem release]; //theMenuItem = [[viewMenu itemWithTag:801] copy]; //[statBarMenu insertItem:theMenuItem atIndex:11]; @@ -637,19 +633,12 @@ - (BOOL)validateMenuItem:(NSMenuItem*)menuItem { } else if (selector == @selector(fixFileEncoding:)) { return (currentNote != nil && storageFormatOfNote(currentNote) == PlainTextFormat && ![currentNote contentsWere7Bit]); + } else if (selector == @selector(editNoteExternally:)) { + return (numberSelected > 0) && [[menuItem representedObject] canEditAllNotes:[notationController notesAtIndexes:[notesTableView selectedRowIndexes]]]; } - return YES; } -/* - - (void)menuNeedsUpdate:(NSMenu *)menu { - NSLog(@"mama needs update: %@", [menu title]); - - NSArray *selectedNotes = [notationController notesAtIndexes:[notesTableView selectedRowIndexes]]; - [selectedNotes setURLsInNotesForMenu:menu]; - }*/ - - (void)updateNoteMenus { NSMenu *notesMenu = [[[NSApp mainMenu] itemWithTag:NOTES_MENU_ID] submenu]; @@ -661,6 +650,7 @@ - (void)updateNoteMenus { NSLocalizedString(@"Delete", nil), trailingQualifier]]; } + [notesMenu setSubmenu:[[ExternalEditorListController sharedInstance] addEditNotesMenu] forItem:[notesMenu itemWithTag:88]]; NSMenu *viewMenu = [[[NSApp mainMenu] itemWithTag:VIEW_MENU_ID] submenu]; menuIndex = [viewMenu indexOfItemWithTarget:notesTableView andAction:@selector(toggleNoteBodyPreviews:)]; @@ -861,6 +851,22 @@ - (IBAction)revealNote:(id)sender { [[NSWorkspace sharedWorkspace] selectFile:path inFileViewerRootedAtPath:@""]; } +- (IBAction)editNoteExternally:(id)sender { + ExternalEditor *ed = [sender representedObject]; + if ([ed isKindOfClass:[ExternalEditor class]]) { + NSIndexSet *indexes = [notesTableView selectedRowIndexes]; + if (kCGEventFlagMaskAlternate == (CGEventSourceFlagsState(kCGEventSourceStateCombinedSessionState) & NSDeviceIndependentModifierFlagsMask)) { + //allow changing the default editor directly from Notes menu + [[ExternalEditorListController sharedInstance] setDefaultEditor:ed]; + } + //force-write any queued changes to disk in case notes are being stored as separate files which might be opened directly by the method below + [notationController synchronizeNoteChanges:nil]; + [[notationController notesAtIndexes:indexes] makeObjectsPerformSelector:@selector(editExternallyUsingEditor:) withObject:ed]; + } else { + NSBeep(); + } +} + - (IBAction)printNote:(id)sender { NSIndexSet *indexes = [notesTableView selectedRowIndexes]; @@ -2955,75 +2961,75 @@ - (IBAction)toggleWordCount:(id)sender{ } - (void)flagsChanged:(NSEvent *)theEvent{ - if (ModFlagger>=0) { - if (([theEvent keyCode]==58)||([theEvent keyCode]==61)) { - if (([theEvent modifierFlags]==524576)||([theEvent modifierFlags]==524608)) { //option down - modifierTimer = [[NSTimer scheduledTimerWithTimeInterval:0.65 - target:self - selector:@selector(updateModifier:) - userInfo:@"option" - repeats:NO] retain]; - }else if ([theEvent modifierFlags]==256) { //option up - - if (modifierTimer) { - if ([modifierTimer isValid]) { - [modifierTimer invalidate]; - }else { - [self performSelector:@selector(popWordCount:) withObject:NO afterDelay:0.35]; - } - modifierTimer = nil; - [modifierTimer release]; - } - ModFlagger = 0; - - } - }else if (([theEvent keyCode]==59)||([theEvent keyCode]==62)) { - if (([theEvent modifierFlags]==262401)||([theEvent modifierFlags]==270592)) { //control down - modifierTimer = [[NSTimer scheduledTimerWithTimeInterval:0.70 - target:self - selector:@selector(updateModifier:) - userInfo:@"control" - repeats:NO] retain]; - - }else if ([theEvent modifierFlags]==256) { //control up - - if (modifierTimer) { - if ([modifierTimer isValid]) { - [modifierTimer invalidate]; - }else { - [self performSelector:@selector(popPreview:) withObject:NO afterDelay:0.46]; - } - modifierTimer = nil; - [modifierTimer release]; - } - ModFlagger = 0; - } - }else if ([theEvent modifierFlags]==256) { - ModFlagger = 0; - if (modifierTimer) { - if ([modifierTimer isValid]) { - [modifierTimer invalidate]; - } - modifierTimer = nil; - [modifierTimer release]; - } - - }else { - ModFlagger = -1; - if (modifierTimer) { - if ([modifierTimer isValid]) { - [modifierTimer invalidate]; - } - modifierTimer = nil; - [modifierTimer release]; - } - NSTimer *disTimer = [NSTimer scheduledTimerWithTimeInterval:0.2f - target:self - selector:@selector(disableKeyMasks:) - userInfo:@"commandorshift" - repeats:NO]; - } - } +// if (ModFlagger>=0) { +// if (([theEvent keyCode]==58)||([theEvent keyCode]==61)) { +// if (([theEvent modifierFlags]==524576)||([theEvent modifierFlags]==524608)) { //option down +// modifierTimer = [[NSTimer scheduledTimerWithTimeInterval:0.65 +// target:self +// selector:@selector(updateModifier:) +// userInfo:@"option" +// repeats:NO] retain]; +// }else if ([theEvent modifierFlags]==256) { //option up +// +// if (modifierTimer) { +// if ([modifierTimer isValid]) { +// [modifierTimer invalidate]; +// }else { +// [self performSelector:@selector(popWordCount:) withObject:NO afterDelay:0.35]; +// } +// modifierTimer = nil; +// [modifierTimer release]; +// } +// ModFlagger = 0; +// +// } +// }else if (([theEvent keyCode]==59)||([theEvent keyCode]==62)) { +// if (([theEvent modifierFlags]==262401)||([theEvent modifierFlags]==270592)) { //control down +// modifierTimer = [[NSTimer scheduledTimerWithTimeInterval:0.70 +// target:self +// selector:@selector(updateModifier:) +// userInfo:@"control" +// repeats:NO] retain]; +// +// }else if ([theEvent modifierFlags]==256) { //control up +// +// if (modifierTimer) { +// if ([modifierTimer isValid]) { +// [modifierTimer invalidate]; +// }else { +// [self performSelector:@selector(popPreview:) withObject:NO afterDelay:0.46]; +// } +// modifierTimer = nil; +// [modifierTimer release]; +// } +// ModFlagger = 0; +// } +// }else if ([theEvent modifierFlags]==256) { +// ModFlagger = 0; +// if (modifierTimer) { +// if ([modifierTimer isValid]) { +// [modifierTimer invalidate]; +// } +// modifierTimer = nil; +// [modifierTimer release]; +// } +// +// }else { +// ModFlagger = -1; +// if (modifierTimer) { +// if ([modifierTimer isValid]) { +// [modifierTimer invalidate]; +// } +// modifierTimer = nil; +// [modifierTimer release]; +// } +// NSTimer *disTimer = [NSTimer scheduledTimerWithTimeInterval:0.2f +// target:self +// selector:@selector(disableKeyMasks:) +// userInfo:@"commandorshift" +// repeats:NO]; +// } +// } } - (void)updateModifier:(NSTimer*)theTimer{ diff --git a/English.lproj/MainMenu.xib b/English.lproj/MainMenu.xib index 24a73bbb..4062966e 100644 --- a/English.lproj/MainMenu.xib +++ b/English.lproj/MainMenu.xib @@ -342,6 +342,15 @@ + + + Edit With + + 2147483647 + + + 88 + YES @@ -1516,7 +1525,7 @@ {213, 107} - + 256 YES @@ -1525,6 +1534,7 @@ 256 {{240, 12}, {99, 32}} + YES 67239424 @@ -1570,6 +1580,7 @@ {{7, 11}, {353, 106}} + {{0, 0}, {1920, 1058}} @@ -1897,16 +1908,18 @@ - + 268 {163, 96} + + StatusItemView YES - + 274 YES @@ -1918,32 +1931,9 @@ 2322 - - YES - - YES - Apple HTML pasteboard type - Apple PDF pasteboard type - Apple PICT pasteboard type - Apple PNG pasteboard type - Apple URL pasteboard type - CorePasteboardFlavorType 0x6D6F6F76 - NSColor pasteboard type - NSFilenamesPboardType - NSStringPboardType - NeXT Encapsulated PostScript v1.2 pasteboard type - NeXT RTFD pasteboard type - NeXT Rich Text Format v1.0 pasteboard type - NeXT TIFF v4.0 pasteboard type - NeXT font pasteboard type - NeXT ruler pasteboard type - WebURLsWithTitlesPboardType - public.url - - {399, 14} - + @@ -2034,6 +2024,7 @@ -2147479296 {{384, 0}, {15, 304}} + 2 _doScroller: @@ -2054,14 +2045,15 @@ {399, 304} - + + 528 - + 274 YES @@ -2075,7 +2067,7 @@ 4352 {399, 136} - + YES @@ -2134,6 +2126,7 @@ -2147483392 {{383, 17}, {15, 135}} + 2 _doScroller: @@ -2168,7 +2161,8 @@ {399, 153} - + + 528 @@ -3454,6 +3448,7 @@ + @@ -4542,6 +4537,14 @@ + + 1298 + + + YES + + + @@ -4728,6 +4731,7 @@ 129.ImportedFromIB2 1292.IBPluginDependency 1293.IBPluginDependency + 1298.IBPluginDependency 130.IBPluginDependency 130.ImportedFromIB2 131.IBPluginDependency @@ -5115,6 +5119,7 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -5325,7 +5330,7 @@ - 1297 + 1300 @@ -5340,9 +5345,11 @@ bringFocusToControlField: copyNoteLink: deleteNote: + editNoteExternally: exportNote: fieldAction: importNotes: + lockPreview: multiTag: openFileInEditor: printNote: @@ -5398,6 +5405,8 @@ id id id + id + id @@ -5407,9 +5416,11 @@ bringFocusToControlField: copyNoteLink: deleteNote: + editNoteExternally: exportNote: fieldAction: importNotes: + lockPreview: multiTag: openFileInEditor: printNote: @@ -5448,6 +5459,10 @@ deleteNote: id + + editNoteExternally: + id + exportNote: id @@ -5460,6 +5475,10 @@ importNotes: id + + lockPreview: + id + multiTag: id @@ -5560,6 +5579,7 @@ YES editorStatusView field + lockNoteItem mainView multiMarkdownPreview notesScrollView @@ -5581,6 +5601,7 @@ YES EmptyView DualField + NSMenuItem ETContentView NSMenuItem AugmentedScrollView @@ -5605,6 +5626,7 @@ YES editorStatusView field + lockNoteItem mainView multiMarkdownPreview notesScrollView @@ -5632,6 +5654,10 @@ field DualField + + lockNoteItem + NSMenuItem + mainView ETContentView diff --git a/English.lproj/Preferences.xib b/English.lproj/Preferences.xib index bf314393..621a41c4 100644 --- a/English.lproj/Preferences.xib +++ b/English.lproj/Preferences.xib @@ -12,26 +12,24 @@ YES - NSColorWell - NSMenu - NSSliderCell + NSUserDefaultsController + NSPopUpButton NSButton - NSCustomObject - NSSlider - NSCustomView - NSComboBox - NSComboBoxCell - NSTextField - NSWindowTemplate + NSMenu NSTextFieldCell NSButtonCell + NSMenuItem NSBox + NSColorWell + NSMatrix + NSSlider + NSSliderCell + NSCustomView + NSCustomObject NSView + NSWindowTemplate + NSTextField NSPopUpButtonCell - NSUserDefaultsController - NSPopUpButton - NSMenuItem - NSMatrix YES @@ -78,7 +76,7 @@ {1e+13, 1e+13} - + 256 YES @@ -87,7 +85,6 @@ 12 {{0, 39}, {368, 5}} - {0, 0} @@ -123,8 +120,6 @@ 268 {{160, 15}, {192, 14}} - - YES 68288064 @@ -140,7 +135,7 @@ 6 System controlColor - + 3 MC42NjY2NjY2NjY3AA @@ -161,7 +156,6 @@ 264 {{34, 9}, {121, 25}} - YES @@ -186,7 +180,6 @@ 264 {{32, 48}, {269, 18}} - YES @@ -215,7 +208,6 @@ 264 {{68, 239}, {101, 17}} - YES @@ -233,7 +225,6 @@ 264 {{171, 233}, {95, 26}} - YES @@ -326,7 +317,6 @@ 264 {{32, 97}, {242, 18}} - YES @@ -350,7 +340,6 @@ 264 {{32, 72}, {223, 18}} - YES @@ -374,7 +363,6 @@ 264 {{32, 160}, {286, 18}} - YES @@ -398,7 +386,6 @@ -2147483384 {{271, 237}, {61, 22}} - YES @@ -423,7 +410,6 @@ 264 {{8, 195}, {161, 20}} - YES @@ -441,7 +427,6 @@ 264 {{174, 196}, {89, 22}} - YES @@ -461,7 +446,6 @@ 264 {{265, 188}, {73, 32}} - YES @@ -486,7 +470,6 @@ 264 {{49, 121}, {286, 33}} - YES @@ -505,14 +488,12 @@ {368, 277} - - NSView NSResponder - + 256 YES @@ -521,7 +502,6 @@ 256 {{17, 368}, {154, 17}} - YES @@ -539,7 +519,6 @@ 256 {{173, 362}, {178, 26}} - YES @@ -609,8 +588,6 @@ 256 {368, 346} - - NSView @@ -618,8 +595,6 @@ {368, 406} - - NSView @@ -631,6 +606,74 @@ 256 YES + + + 268 + {{153, 15}, {194, 26}} + + + + YES + + -2076049856 + 2048 + + + 109199615 + 129 + + + 400 + 75 + + + Item 1 + + 1048576 + 2147483647 + 1 + + + _popUpItemAction: + + + YES + + OtherViews + + YES + + + + Item 2 + + 1048576 + 2147483647 + + + _popUpItemAction: + + + + + Item 3 + + 1048576 + 2147483647 + + + _popUpItemAction: + + + + + + 1 + YES + YES + 2 + + 264 @@ -655,7 +698,7 @@ {{17, 22}, {134, 17}} - + YES 67239424 @@ -671,100 +714,6 @@ - - - 268 - {{166, 16}, {178, 26}} - - - - YES - - 74579521 - 272630784 - - - - YES - - - 5 - YES - - - - 274 - {15, 0} - - - YES - - YES - - - 12 - 10 - 1000 - - 75628032 - 0 - - - LucidaGrande - 12 - 16 - - - 3 - MC4zMzMzMzI5ODU2AA - - - - - 338820672 - 1024 - - - YES - - 6 - System - controlBackgroundColor - - - - - 3 - YES - - - - 3 - 2 - - - 6 - System - gridColor - - 3 - MC41AA - - - 19 - tableViewAction: - -767524864 - - - - 1 - 15 - 0 - YES - 0 - - - 264 @@ -1259,7 +1208,7 @@ SW5jLiwgMjAwNQAAAAA NSResponder - + 256 YES @@ -1268,7 +1217,6 @@ SW5jLiwgMjAwNQAAAAA 268 {{92, 78}, {172, 18}} - YES @@ -1292,7 +1240,6 @@ SW5jLiwgMjAwNQAAAAA 268 {{95, 98}, {169, 18}} - YES @@ -1316,8 +1263,6 @@ SW5jLiwgMjAwNQAAAAA 264 {{346, 22}, {38, 13}} - - YES 67239424 @@ -1334,7 +1279,6 @@ SW5jLiwgMjAwNQAAAAA 264 {{317, 22}, {36, 13}} - YES @@ -1352,7 +1296,6 @@ SW5jLiwgMjAwNQAAAAA 264 {{50, 20}, {149, 17}} - YES @@ -1370,7 +1313,6 @@ SW5jLiwgMjAwNQAAAAA 268 {{200, 20}, {113, 15}} - YES @@ -1393,7 +1335,6 @@ SW5jLiwgMjAwNQAAAAA 268 {{50, 45}, {212, 17}} - YES @@ -1411,7 +1352,6 @@ SW5jLiwgMjAwNQAAAAA 264 {{259, 43}, {22, 18}} - YES @@ -1434,7 +1374,6 @@ SW5jLiwgMjAwNQAAAAA 264 {{35, 130}, {326, 14}} - YES @@ -1452,7 +1391,6 @@ SW5jLiwgMjAwNQAAAAA 268 {{115, 229}, {130, 18}} - YES @@ -1476,7 +1414,6 @@ SW5jLiwgMjAwNQAAAAA 264 {{35, 275}, {76, 20}} - YES @@ -1494,7 +1431,6 @@ SW5jLiwgMjAwNQAAAAA 264 {{315, 268}, {72, 32}} - YES @@ -1519,7 +1455,6 @@ SW5jLiwgMjAwNQAAAAA 266 {{116, 275}, {197, 23}} - YES @@ -1544,7 +1479,6 @@ SW5jLiwgMjAwNQAAAAA 264 {{151, 156}, {94, 17}} - YES @@ -1571,7 +1505,6 @@ SW5jLiwgMjAwNQAAAAA {{251, 152}, {52, 24}} - YES YES @@ -1582,7 +1515,6 @@ SW5jLiwgMjAwNQAAAAA 264 {{122, 188}, {123, 17}} - YES @@ -1609,7 +1541,6 @@ SW5jLiwgMjAwNQAAAAA {{251, 184}, {52, 24}} - YES YES @@ -1629,7 +1560,6 @@ SW5jLiwgMjAwNQAAAAA {{251, 226}, {52, 24}} - YES YES @@ -1640,8 +1570,6 @@ SW5jLiwgMjAwNQAAAAA {420, 319} - - NSView NSResponder @@ -2029,22 +1957,6 @@ SW5jLiwgMjAwNQAAAAA 451 - - - appList - - - - 462 - - - - delegate - - - - 464 - toggleKeepsTextWidthInWindow: @@ -2069,26 +1981,6 @@ SW5jLiwgMjAwNQAAAAA 480 - - - hidden: values.TextEditor - - - - - - hidden: values.TextEditor - hidden - values.TextEditor - - NSValueTransformerName - NSIsNil - - 2 - - - 500 - hidden: values.TextEditor @@ -2317,6 +2209,22 @@ SW5jLiwgMjAwNQAAAAA 560 + + + externalEditorMenuButton + + + + 567 + + + + changedExternalEditorsMenu: + + + + 568 + @@ -2530,10 +2438,10 @@ SW5jLiwgMjAwNQAAAAA - + editing view @@ -2951,20 +2859,6 @@ SW5jLiwgMjAwNQAAAAA - - 452 - - - YES - - - - - - 453 - - - 454 @@ -3246,6 +3140,50 @@ SW5jLiwgMjAwNQAAAAA + + 561 + + + YES + + + + + + 562 + + + YES + + + + + + 563 + + + YES + + + + + + + + 564 + + + + + 565 + + + + + 566 + + + @@ -3440,9 +3378,6 @@ SW5jLiwgMjAwNQAAAAA 441.IBViewBoundsToFrameTransform 442.IBPluginDependency 443.IBPluginDependency - 452.IBPluginDependency - 452.IBViewBoundsToFrameTransform - 453.IBPluginDependency 454.IBPluginDependency 454.IBViewBoundsToFrameTransform 455.IBPluginDependency @@ -3499,6 +3434,8 @@ SW5jLiwgMjAwNQAAAAA 552.IBPluginDependency 552.IBViewBoundsToFrameTransform 553.IBPluginDependency + 561.IBPluginDependency + 562.IBPluginDependency 6.IBPluginDependency 6.ImportedFromIB2 @@ -3739,11 +3676,6 @@ SW5jLiwgMjAwNQAAAAA com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - - P4AAAL+AAABDKQAAwhQAAA - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin P4AAAL+AAABBoAAAwggAAA @@ -3824,6 +3756,8 @@ SW5jLiwgMjAwNQAAAAA com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin @@ -3839,7 +3773,7 @@ SW5jLiwgMjAwNQAAAAA - 560 + 568 @@ -4218,6 +4152,7 @@ SW5jLiwgMjAwNQAAAAA changedAltRows: changedAutoSuggestLinks: changedBackgroundTextColorWell: + changedExternalEditorsMenu: changedForegroundTextColorWell: changedHighlightSearchTerms: changedMakeURLsClickable: @@ -4267,6 +4202,7 @@ SW5jLiwgMjAwNQAAAAA id id id + id @@ -4277,6 +4213,7 @@ SW5jLiwgMjAwNQAAAAA changedAltRows: changedAutoSuggestLinks: changedBackgroundTextColorWell: + changedExternalEditorsMenu: changedForegroundTextColorWell: changedHighlightSearchTerms: changedMakeURLsClickable: @@ -4317,6 +4254,10 @@ SW5jLiwgMjAwNQAAAAA changedBackgroundTextColorWell: id + + changedExternalEditorsMenu: + id + changedForegroundTextColorWell: id @@ -4418,6 +4359,7 @@ SW5jLiwgMjAwNQAAAAA confirmDeletionButton databaseView editingView + externalEditorMenuButton folderLocationsMenuButton fontsColorsView foregroundColorWell @@ -4456,6 +4398,7 @@ SW5jLiwgMjAwNQAAAAA NSView NSView NSPopUpButton + NSPopUpButton NSView NSColorWell NSView @@ -4495,6 +4438,7 @@ SW5jLiwgMjAwNQAAAAA confirmDeletionButton databaseView editingView + externalEditorMenuButton folderLocationsMenuButton fontsColorsView foregroundColorWell @@ -4565,6 +4509,10 @@ SW5jLiwgMjAwNQAAAAA editingView NSView + + externalEditorMenuButton + NSPopUpButton + folderLocationsMenuButton NSPopUpButton diff --git a/ExternalEditorListController.h b/ExternalEditorListController.h new file mode 100644 index 00000000..aa34e6bc --- /dev/null +++ b/ExternalEditorListController.h @@ -0,0 +1,77 @@ +// +// ExternalEditorListController.h +// Notation +// +// Created by Zachary Schneirov on 3/14/11. + +/*Copyright (c) 2010, Zachary Schneirov. All rights reserved. + This file is part of Notational Velocity. + + Notational Velocity 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 3 of the License, or + (at your option) any later version. + + Notational Velocity 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 Notational Velocity. If not, see . */ + + +#import + +extern NSString *ExternalEditorsChangedNotification; + +@class NoteObject; + +@interface ExternalEditor : NSObject { + + BOOL installCheckFailed; + NSImage *iconImg; + NSString *bundleIdentifier; + NSURL *resolvedURL; + NSString *displayName; + NSMutableDictionary *knownPathExtensions; +} + +- (id)initWithBundleID:(NSString*)aBundleIdentifier resolvedURL:(NSURL*)aURL; +- (BOOL)canEditNoteDirectly:(NoteObject*)aNote; +- (BOOL)canEditAllNotes:(NSArray*)notes; +- (NSImage*)iconImage; +- (NSURL*)resolvedURL; +- (NSString*)displayName; +- (BOOL)isInstalled; +- (BOOL)isODBEditor; +- (NSString*)bundleIdentifier; + +@end + +@interface ExternalEditorListController : NSObject { + + NSMutableArray *userEditorList; + NSArray *ODBEditorList; + ExternalEditor *defaultEditor; + + NSMutableSet *editNotesMenus, *editorPrefsMenus; + + NSMutableArray *_installedODBEditors; +} +- (id)initWithUserDefaults; ++ (ExternalEditorListController*)sharedInstance; +- (void)addUserEditorFromDialog:(id)sender; +- (void)resetUserEditors:(id)sender; +- (void)_initDefaults; +- (NSArray*)_installedODBEditors; +- (BOOL)editorIsMember:(ExternalEditor*)anEditor; ++ (NSSet*)ODBAppIdentifiers; +- (NSArray*)userEditorIdentifiers; +- (NSMenu*)addEditorPrefsMenu; +- (NSMenu*)addEditNotesMenu; +- (void)menusChanged; +- (void)_updateMenu:(NSMenu*)theMenu; +- (ExternalEditor*)defaultExternalEditor; +- (void)setDefaultEditor:(id)anEditor; +@end diff --git a/ExternalEditorListController.m b/ExternalEditorListController.m new file mode 100644 index 00000000..8093d17e --- /dev/null +++ b/ExternalEditorListController.m @@ -0,0 +1,442 @@ +// +// ExternalEditorListController.m +// Notation +// +// Created by Zachary Schneirov on 3/14/11. + +/*Copyright (c) 2010, Zachary Schneirov. All rights reserved. + This file is part of Notational Velocity. + + Notational Velocity 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 3 of the License, or + (at your option) any later version. + + Notational Velocity 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 Notational Velocity. If not, see . */ + + +#import "ExternalEditorListController.h" +#import "NoteObject.h" +#import "NotationController.h" +#import "NotationPrefs.h" +#import "NSBezierPath_NV.h" + +static NSString *UserEEIdentifiersKey = @"UserEEIdentifiers"; +static NSString *DefaultEEIdentifierKey = @"DefaultEEIdentifier"; +NSString *ExternalEditorsChangedNotification = @"ExternalEditorsChanged"; + +@implementation ExternalEditor + +- (id)initWithBundleID:(NSString*)aBundleIdentifier resolvedURL:(NSURL*)aURL { + if ([self init]) { + bundleIdentifier = [aBundleIdentifier retain]; + resolvedURL = [aURL retain]; + + NSAssert(resolvedURL || bundleIdentifier, @"the bundle identifier and URL cannot both be nil"); + if (!bundleIdentifier) { + if (!(bundleIdentifier = [[[NSBundle bundleWithPath:[aURL path]] bundleIdentifier] copy])) { + NSLog(@"initWithBundleID:resolvedURL: URL does not seem to point to a valid bundle"); + return nil; + } + } + } + return self; +} + +- (BOOL)canEditNoteDirectly:(NoteObject*)aNote { + NSAssert(aNote != nil, @"aNote is nil"); + + //for determining whether this potentially non-ODB-editor can open a non-plain-text file + //process: does pathExtension key exist in knownPathExtensions dict? + //if not, check this path extension w/ launch services + //then add a corresponding YES/NO NSNumber value to the knownPathExtensions dict + + //but first, this editor can't handle any path if it's not actually installed + if (![self isInstalled]) return NO; + + //and if this note isn't actually stored in a separate file, then obviously it can't be opened directly + if ([[aNote delegate] currentNoteStorageFormat] == SingleDatabaseFormat) return NO; + + //and if aNote is in plaintext format and this editor is ODB-capable, then it should also be a general-purpose texteditor + //conversely ODB editors should never be allowed to open non-plain-text documents; for some reason LSCanURLAcceptURL claims they can do that + //one exception known: writeroom can edit rich-text documents + if ([self isODBEditor] && ![bundleIdentifier hasPrefix:@"com.hogbaysoftware.WriteRoom"]) { + return storageFormatOfNote(aNote) == PlainTextFormat; + } + + if (!knownPathExtensions) knownPathExtensions = [NSMutableDictionary new]; + NSString *extension = [[filenameOfNote(aNote) pathExtension] lowercaseString]; + NSNumber *canHandleNumber = [knownPathExtensions objectForKey:extension]; + + if (!canHandleNumber) { + NSString *path = [aNote noteFilePath]; + + Boolean canAccept = false; + OSStatus err = LSCanURLAcceptURL((CFURLRef)[NSURL fileURLWithPath:path], (CFURLRef)[self resolvedURL], kLSRolesEditor, kLSAcceptAllowLoginUI, &canAccept); + if (noErr != err) { + NSLog(@"LSCanURLAcceptURL '%@' err: %d", path, err); + } + [knownPathExtensions setObject:[NSNumber numberWithBool:(BOOL)canAccept] forKey:extension]; + + return (BOOL)canAccept; + } + + return [canHandleNumber boolValue]; +} + +- (BOOL)canEditAllNotes:(NSArray*)notes { + NSUInteger i = 0; + for (i=0; i<[notes count]; i++) { + if (![self isODBEditor] && ![self canEditNoteDirectly:[notes objectAtIndex:i]]) + return NO; + } + return YES; +} + +- (NSImage*)iconImage { + if (!iconImg) { + FSRef appRef; + if (CFURLGetFSRef((CFURLRef)[self resolvedURL], &appRef)) + iconImg = [[NSImage smallIconForFSRef:&appRef] retain]; + } + return iconImg; +} + +- (NSString*)displayName { + if (!displayName) { + LSCopyDisplayNameForURL((CFURLRef)[self resolvedURL], (CFStringRef*)&displayName); + } + return displayName; +} + +- (NSURL*)resolvedURL { + if (!resolvedURL && !installCheckFailed) { + + OSStatus err = LSFindApplicationForInfo(kLSUnknownCreator, (CFStringRef)bundleIdentifier, NULL, NULL, (CFURLRef*)&resolvedURL); + + if (kLSApplicationNotFoundErr == err) { + installCheckFailed = YES; + } else if (noErr != err) { + NSLog(@"LSFindApplicationForInfo error for bundle identifier '%@': %d", bundleIdentifier, err); + } + } + return resolvedURL; +} + +- (BOOL)isInstalled { + return [self resolvedURL] != nil; +} + +- (BOOL)isODBEditor { + return [[ExternalEditorListController ODBAppIdentifiers] containsObject:bundleIdentifier]; +} + +- (NSString*)bundleIdentifier { + return bundleIdentifier; +} + +- (NSString*)description { + return [bundleIdentifier stringByAppendingFormat:@" (URL: %@)", resolvedURL]; +} + +- (NSUInteger)hash { + return [bundleIdentifier hash]; +} +- (BOOL)isEqual:(id)otherEntry { + return [[otherEntry bundleIdentifier] isEqualToString:bundleIdentifier]; +} +- (NSComparisonResult)compareDisplayName:(ExternalEditor *)otherEd { + return [[self displayName] caseInsensitiveCompare:[otherEd displayName]]; +} + + +- (void)dealloc { + [knownPathExtensions release]; + [bundleIdentifier release]; + [displayName release]; + [resolvedURL release]; + [iconImg release]; + [super dealloc]; +} + + +@end + +@implementation ExternalEditorListController + +static ExternalEditorListController* sharedInstance = nil; + ++ (ExternalEditorListController*)sharedInstance { + if (sharedInstance == nil) + sharedInstance = [[ExternalEditorListController alloc] initWithUserDefaults]; + return sharedInstance; +} + ++ (id)allocWithZone:(NSZone *)zone { + if (sharedInstance == nil) { + sharedInstance = [super allocWithZone:zone]; + return sharedInstance; // assignment and return on first allocation + } + return nil; // on subsequent allocation attempts return nil +} + +- (id)initWithUserDefaults { + if ([self init]) { + //TextEdit is not an ODB editor, but can be used to open files directly + [[NSUserDefaults standardUserDefaults] registerDefaults: + [NSDictionary dictionaryWithObject:[NSArray arrayWithObject:@"com.apple.TextEdit"] forKey:UserEEIdentifiersKey]]; + + [self _initDefaults]; + } + return self; +} + +- (id)init { + if ([super init]) { + + userEditorList = [[NSMutableArray alloc] init]; + } + return self; +} + +- (void)_initDefaults { + NSArray *userIdentifiers = [[NSUserDefaults standardUserDefaults] arrayForKey:UserEEIdentifiersKey]; + + NSUInteger i = 0; + for (i=0; i<[userIdentifiers count]; i++) { + ExternalEditor *ed = [[ExternalEditor alloc] initWithBundleID:[userIdentifiers objectAtIndex:i] resolvedURL:nil]; + [userEditorList addObject:ed]; + [ed release]; + } + + //initialize the default editor if one has not already been set or if the identifier was somehow lost from the list + if (![self editorIsMember:[self defaultExternalEditor]] || ![[self defaultExternalEditor] isInstalled]) { + if ([[self _installedODBEditors] count]) { + [self setDefaultEditor:[[self _installedODBEditors] lastObject]]; + } + } +} + +- (NSArray*)_installedODBEditors { + if (!_installedODBEditors) { + _installedODBEditors = [[NSMutableArray alloc] initWithCapacity:5]; + + NSArray *ODBApps = [[[self class] ODBAppIdentifiers] allObjects]; + NSUInteger i = 0; + for (i=0; i<[ODBApps count]; i++) { + ExternalEditor *ed = [[ExternalEditor alloc] initWithBundleID:[ODBApps objectAtIndex:i] resolvedURL:nil]; + if ([ed isInstalled]) { + [_installedODBEditors addObject:ed]; + } + [ed release]; + } + [_installedODBEditors sortUsingSelector:@selector(compareDisplayName:)]; + } + return _installedODBEditors; +} + ++ (NSSet*)ODBAppIdentifiers { + static NSSet *_ODBAppIdentifiers = nil; + if (!_ODBAppIdentifiers) + _ODBAppIdentifiers = [[NSSet alloc] initWithObjects: + @"de.codingmonkeys.SubEthaEdit", @"com.barebones.bbedit", @"com.barebones.textwrangler", + @"com.macromates.textmate", @"com.transtex.texeditplus", @"jp.co.artman21.JeditX", @"org.gnu.Aquamacs", + @"org.smultron.Smultron", @"com.peterborgapps.Smultron", @"org.fraise.Fraise", @"com.aynimac.CotEditor", @"com.macrabbit.cssedit", + @"com.talacia.Tag", @"org.skti.skEdit", @"com.cgerdes.ji", @"com.optima.PageSpinner", @"com.hogbaysoftware.WriteRoom", + @"com.hogbaysoftware.WriteRoom.mac", @"org.vim.MacVim", @"com.forgedit.ForgEdit", @"com.tacosw.TacoHTMLEdit", @"com.macrabbit.espresso", nil]; + return _ODBAppIdentifiers; +} + +- (void)addUserEditorFromDialog:(id)sender { + + //always send menuChanged notification because this class is the target of its menus, + //so the notification is the only way to maintain a consistent selected item in PrefsWindowController + [self performSelector:@selector(menusChanged) withObject:nil afterDelay:0.0]; + + NSOpenPanel *openPanel = [NSOpenPanel openPanel]; + [openPanel setResolvesAliases:YES]; + [openPanel setAllowsMultipleSelection:NO]; + + if ([openPanel runModalForDirectory:@"/Applications" file:nil types:[NSArray arrayWithObject:@"app"]] == NSOKButton) { + if (![openPanel filename]) goto errorReturn; + NSURL *appURL = [NSURL fileURLWithPath:[openPanel filename]]; + if (!appURL) goto errorReturn; + + ExternalEditor *ed = [[ExternalEditor alloc] initWithBundleID:nil resolvedURL:appURL]; + if (!ed) goto errorReturn; + + //check against lists of all known editors, installed or not + if (![self editorIsMember:ed]) { + [userEditorList addObject:ed]; + [[NSUserDefaults standardUserDefaults] setObject:[self userEditorIdentifiers] forKey:UserEEIdentifiersKey]; + } + + [self setDefaultEditor:ed]; + } + return; +errorReturn: + NSBeep(); + NSLog(@"Unable to add external editor"); +} + +- (void)resetUserEditors:(id)sender { + [userEditorList removeAllObjects]; + + [[NSUserDefaults standardUserDefaults] removeObjectForKey:UserEEIdentifiersKey]; + + [self _initDefaults]; + + [self menusChanged]; +} + +- (NSArray*)userEditorIdentifiers { + //for storing in nsuserdefaults + //extract bundle identifiers + + NSMutableArray *array = [NSMutableArray arrayWithCapacity:[userEditorList count]]; + NSUInteger i = 0; + for (i=0; i<[userEditorList count]; i++) { + [array addObject:[[userEditorList objectAtIndex:i] bundleIdentifier]]; + } + + return array; +} + + +- (BOOL)editorIsMember:(ExternalEditor*)anEditor { + //does the editor exist in any of the lists? + return [userEditorList containsObject:anEditor] || [[ExternalEditorListController ODBAppIdentifiers] containsObject:[anEditor bundleIdentifier]]; +} + +- (NSMenu*)addEditorPrefsMenu { + if (!editorPrefsMenus) editorPrefsMenus = [NSMutableSet new]; + NSMenu *aMenu = [[NSMenu alloc] initWithTitle:@"External Editors Menu"]; + [aMenu setAutoenablesItems:NO]; + [aMenu setDelegate:self]; + [editorPrefsMenus addObject:[aMenu autorelease]]; + [self _updateMenu:aMenu]; + return aMenu; +} + +- (NSMenu*)addEditNotesMenu { + if (!editNotesMenus) editNotesMenus = [NSMutableSet new]; + NSMenu *aMenu = [[NSMenu alloc] initWithTitle:@"Edit Note Menu"]; + [aMenu setAutoenablesItems:YES]; + [aMenu setDelegate:self]; + [editNotesMenus addObject:[aMenu autorelease]]; + [self _updateMenu:aMenu]; + return aMenu; +} + +- (void)menusChanged { + + [editNotesMenus makeObjectsPerformSelector:@selector(_updateMenuForEEListController:) withObject:self]; + [editorPrefsMenus makeObjectsPerformSelector:@selector(_updateMenuForEEListController:) withObject:self]; + [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:ExternalEditorsChangedNotification object:self]]; +} + +- (void)_updateMenu:(NSMenu*)theMenu { + //for allowing the user to configure external editors in the preferences window + + if (IsSnowLeopardOrLater) { + [theMenu performSelector:@selector(removeAllItems)]; + } else { + while ([theMenu numberOfItems]) + [theMenu removeItemAtIndex:0]; + } + + BOOL isPrefsMenu = [editorPrefsMenus containsObject:theMenu]; + BOOL didAddItem = NO; + NSMutableArray *editors = [NSMutableArray arrayWithArray:[self _installedODBEditors]]; + [editors addObjectsFromArray:[userEditorList filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"isInstalled == YES"]]]; + [editors sortUsingSelector:@selector(compareDisplayName:)]; + + NSUInteger i = 0; + for (i=0; i<[editors count]; i++) { + ExternalEditor *ed = [editors objectAtIndex:i]; + + //change action SEL based on whether this is coming from Notes menu or preferences window + NSMenuItem *theMenuItem = isPrefsMenu ? + [[[NSMenuItem alloc] initWithTitle:[ed displayName] action:@selector(setDefaultEditor:) keyEquivalent:@""] autorelease] : + [[[NSMenuItem alloc] initWithTitle:[ed displayName] action:@selector(editNoteExternally:) keyEquivalent:@""] autorelease]; + + if (!isPrefsMenu && [[self defaultExternalEditor] isEqual:ed]) { + [theMenuItem setKeyEquivalent:@"E"]; + [theMenuItem setKeyEquivalentModifierMask: NSCommandKeyMask | NSShiftKeyMask]; + } + //PrefsWindowController maintains default-editor selection by updating on ExternalEditorsChangedNotification + + [theMenuItem setTarget: isPrefsMenu ? self : [NSApp delegate]]; + + [theMenuItem setRepresentedObject:ed]; +// +// if ([ed iconImage]) +// [theMenuItem setImage:[ed iconImage]]; +// + [theMenu addItem:theMenuItem]; + didAddItem = YES; + } + + if (!didAddItem) { + //disabled placeholder menu item; will probably not be displayed, but would be necessary for preferences list + NSMenuItem *theMenuItem = [[[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"(None)", @"description for no key combination") action:NULL keyEquivalent:@""] autorelease]; + [theMenuItem setEnabled:NO]; + [theMenu addItem:theMenuItem]; + } + if ([userEditorList count] > 1 && isPrefsMenu) { + //if the user added at least one editor (in addition to the default TextEdit item), then allow items to be reset to their default + [theMenu addItem:[NSMenuItem separatorItem]]; + + NSMenuItem *theMenuItem = [[[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Reset", @"menu command to clear out custom external editors") + action:@selector(resetUserEditors:) keyEquivalent:@""] autorelease]; + [theMenuItem setTarget:self]; + [theMenu addItem:theMenuItem]; + } + [theMenu addItem:[NSMenuItem separatorItem]]; + + NSMenuItem *theMenuItem = [[[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Other...", @"title of menu item for selecting a different notes folder") + action:@selector(addUserEditorFromDialog:) keyEquivalent:@""] autorelease]; + [theMenuItem setTarget:self]; + [theMenu addItem:theMenuItem]; +} + +- (ExternalEditor*)defaultExternalEditor { + if (!defaultEditor) { + NSString *defaultIdentifier = [[NSUserDefaults standardUserDefaults] stringForKey:DefaultEEIdentifierKey]; + if (defaultIdentifier) + defaultEditor = [[ExternalEditor alloc] initWithBundleID:defaultIdentifier resolvedURL:nil]; + } + return defaultEditor; +} + +- (void)setDefaultEditor:(id)anEditor { + if ((anEditor = ([anEditor isKindOfClass:[NSMenuItem class]] ? [anEditor representedObject] : anEditor))) { + [defaultEditor release]; + defaultEditor = [anEditor retain]; + + [[NSUserDefaults standardUserDefaults] setObject:[defaultEditor bundleIdentifier] forKey:DefaultEEIdentifierKey]; + + [self menusChanged]; + } +} + +@end + + +//this category exists because I want to use -makeObjectsPerformSelector: in -menusChanged + +@interface NSMenu (ExternalEditorListMenu) +- (void)_updateMenuForEEListController:(ExternalEditorListController*)controller; +@end + +@implementation NSMenu (ExternalEditorListMenu) +- (void)_updateMenuForEEListController:(ExternalEditorListController*)controller { + [controller _updateMenu:self]; +} +@end diff --git a/GlobalPrefs.h b/GlobalPrefs.h index 71836577..6c192118 100755 --- a/GlobalPrefs.h +++ b/GlobalPrefs.h @@ -5,15 +5,20 @@ // Created by Zachary Schneirov on 1/31/06. /*Copyright (c) 2010, Zachary Schneirov. All rights reserved. - Redistribution and use in source and binary forms, with or without modification, are permitted - provided that the following conditions are met: - - Redistributions of source code must retain the above copyright notice, this list of conditions - and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, this list of - conditions and the following disclaimer in the documentation and/or other materials provided with - the distribution. - - Neither the name of Notational Velocity nor the names of its contributors may be used to endorse - or promote products derived from this software without specific prior written permission. */ + This file is part of Notational Velocity. + + Notational Velocity 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 3 of the License, or + (at your option) any later version. + + Notational Velocity 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 Notational Velocity. If not, see . */ #import @@ -167,7 +172,6 @@ BOOL ColorsEqualWith8BitChannels(NSColor *c1, NSColor *c2); - (void)setAliasDataForDefaultDirectory:(NSData*)alias sender:(id)sender; - (NSData*)aliasDataForDefaultDirectory; -- (NSImage*)iconForDefaultDirectoryWithFSRef:(FSRef*)fsRef; - (NSString*)displayNameForDefaultDirectoryWithFSRef:(FSRef*)fsRef; - (NSString*)humanViewablePathForDefaultDirectory; @@ -175,7 +179,7 @@ BOOL ColorsEqualWith8BitChannels(NSColor *c1, NSColor *c2); - (BOOL)triedToImportBlor; - (void)synchronize; - +- (NSImage*)iconForDefaultDirectoryWithFSRef:(FSRef*)fsRef; // - (NSString *)textEditor; - (void)setTextEditor:(NSString *)inApp; @@ -199,3 +203,5 @@ BOOL ColorsEqualWith8BitChannels(NSColor *c1, NSColor *c2); @interface NSObject (GlobalPrefsDelegate) - (void)settingChangedForSelectorString:(NSString*)selectorString; @end + + diff --git a/GlobalPrefs.m b/GlobalPrefs.m index eb389db4..3af52c8a 100755 --- a/GlobalPrefs.m +++ b/GlobalPrefs.m @@ -5,20 +5,22 @@ // Created by Zachary Schneirov on 1/31/06. /*Copyright (c) 2010, Zachary Schneirov. All rights reserved. - Redistribution and use in source and binary forms, with or without modification, are permitted - provided that the following conditions are met: - - Redistributions of source code must retain the above copyright notice, this list of conditions - and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, this list of - conditions and the following disclaimer in the documentation and/or other materials provided with - the distribution. - - Neither the name of Notational Velocity nor the names of its contributors may be used to endorse - or promote products derived from this software without specific prior written permission. */ - -/*[NSArchiver archivedDataWithRootObject:[NSColor colorWithCalibratedRed:0.74f green:0.74f blue:0.74f alpha:1.0f]], ForegroundTextColorKey, - [NSArchiver archivedDataWithRootObject:[NSColor colorWithCalibratedRed:0.082f green:0.082f blue:0.082f alpha:1.0f]], BackgroundTextColorKey,*/ + This file is part of Notational Velocity. + + Notational Velocity 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 3 of the License, or + (at your option) any later version. + + Notational Velocity 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 Notational Velocity. If not, see . */ + -#import "AppController.h" #import "GlobalPrefs.h" #import "NSData_transformations.h" #import "NotationPrefs.h" @@ -30,6 +32,7 @@ #import "PTKeyCombo.h" #import "PTHotKeyCenter.h" #import "NSString_NV.h" +#import "AppController.h" #define SEND_CALLBACKS() sendCallbacksForGlobalPrefs(self, _cmd, sender) @@ -161,7 +164,7 @@ - (id)init { [NSColor colorWithCalibratedRed:0.945 green:0.702 blue:0.702 alpha:1.0f]], SearchTermHighlightColorKey, [NSNumber numberWithFloat:[NSFont smallSystemFontSize]], TableFontSizeKey, - [NSArray arrayWithObjects:NoteTitleColumnString, NoteLabelsColumnString, NoteDateModifiedColumnString, nil], NoteAttributesVisibleKey, + [NSArray arrayWithObjects:NoteTitleColumnString, NoteDateModifiedColumnString, nil], NoteAttributesVisibleKey, NoteDateModifiedColumnString, TableSortColumnKey, [NSNumber numberWithBool:YES], TableIsReverseSortedKey, nil]]; @@ -890,35 +893,6 @@ - (NSData*)aliasDataForDefaultDirectory { return [defaults dataForKey:DirectoryAliasKey]; } -- (NSImage*)iconForDefaultDirectoryWithFSRef:(FSRef*)fsRef { - OSStatus err = noErr; - - if (!fsRef) - return nil; - - if (IsZeros(fsRef, sizeof(FSRef))) { - if (![[self aliasDataForDefaultDirectory] fsRefAsAlias:fsRef]) - return nil; - } - IconRef iconRef; - if ((err = GetIconRefFromFileInfo(fsRef, 0, NULL, 0, NULL, kIconServicesNormalUsageFlag, &iconRef, NULL)) == noErr) { - - NSImage *image = [[[NSImage alloc] initWithSize:NSMakeSize(16.0f, 16.0f)] autorelease]; - NSRect frame = NSMakeRect(0.0f,0.0f,16.0f,16.0f); - - [image lockFocus]; - err = PlotIconRefInContext([[NSGraphicsContext currentContext] graphicsPort], (CGRect *)&frame, 0, 0, nil, 0, iconRef); - [image unlockFocus]; - - if (err == noErr) - return image; - } - - NSLog(@"iconForDefaultDirectory error: %d", err); - - return nil; -} - - (NSString*)displayNameForDefaultDirectoryWithFSRef:(FSRef*)fsRef { if (!fsRef) @@ -982,25 +956,36 @@ - (void)synchronize { [defaults synchronize]; } -//elasticthreads' work - -- (NSString *)textEditor{ - NSString *theData = [defaults stringForKey:TextEditor]; - if (theData){ - return theData; - } else { - //[self setTextEditor:@"Default"]; - //return @"Default"; - return nil; - } +- (NSImage*)iconForDefaultDirectoryWithFSRef:(FSRef*)fsRef { + OSStatus err = noErr; + + if (!fsRef) + return nil; + + if (IsZeros(fsRef, sizeof(FSRef))) { + if (![[self aliasDataForDefaultDirectory] fsRefAsAlias:fsRef]) + return nil; + } + IconRef iconRef; + if ((err = GetIconRefFromFileInfo(fsRef, 0, NULL, 0, NULL, kIconServicesNormalUsageFlag, &iconRef, NULL)) == noErr) { + + NSImage *image = [[[NSImage alloc] initWithSize:NSMakeSize(16.0f, 16.0f)] autorelease]; + NSRect frame = NSMakeRect(0.0f,0.0f,16.0f,16.0f); + + [image lockFocus]; + err = PlotIconRefInContext([[NSGraphicsContext currentContext] graphicsPort], (CGRect *)&frame, 0, 0, nil, 0, iconRef); + [image unlockFocus]; + + if (err == noErr) + return image; + } + + NSLog(@"iconForDefaultDirectory error: %d", err); + + return nil; } -- (void)setTextEditor:(NSString *)inApp{ - //if (inApp) { - [defaults setObject:inApp forKey:TextEditor]; - [defaults synchronize]; - //} -} +//elasticthreads' work - (BOOL)managesTextWidthInWindow{ return [defaults boolForKey:KeepsMaxTextWidth]; diff --git a/LabelColumnCell.m b/LabelColumnCell.m index 292a587b..555d644d 100644 --- a/LabelColumnCell.m +++ b/LabelColumnCell.m @@ -56,17 +56,16 @@ - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView { [super drawWithFrame:cellFrame inView:controlView]; } - if (!isEditing) { - NSImage *img = ([self isHighlighted] && [tv isActiveStyle]) ? [noteObject highlightedLabelsPreviewImage] : [noteObject labelsPreviewImage]; - if (img) { - [[NSGraphicsContext currentContext] saveGraphicsState]; - - - NSRectClip(cellFrame); - NSPoint imgSpot = NSMakePoint(NSMinX(cellFrame), NSMaxY(cellFrame) - ceilf(((cellFrame.size.height + 1.0) - [img size].height)/2.0)); - [img compositeToPoint:imgSpot operation:NSCompositeSourceOver]; - [[NSGraphicsContext currentContext] restoreGraphicsState]; - } + if (!isEditing && [labelsOfNote(noteObject) length]) { + + [[NSGraphicsContext currentContext] saveGraphicsState]; + NSRectClip(cellFrame); + NSRect blocksRect = cellFrame; + blocksRect.origin = NSMakePoint(NSMinX(cellFrame), NSMaxY(cellFrame) - ceilf(((cellFrame.size.height + 1.0) - + ([[GlobalPrefs defaultPrefs] tableFontSize] * 1.3 + 1.5))/2.0)); + [noteObject drawLabelBlocksInRect:blocksRect rightAlign:NO highlighted:([self isHighlighted] && [tv isActiveStyle])]; + + [[NSGraphicsContext currentContext] restoreGraphicsState]; } } diff --git a/LabelsListController.h b/LabelsListController.h index 8d2d908c..f744785a 100755 --- a/LabelsListController.h +++ b/LabelsListController.h @@ -5,15 +5,20 @@ // Created by Zachary Schneirov on 1/10/06. /*Copyright (c) 2010, Zachary Schneirov. All rights reserved. - Redistribution and use in source and binary forms, with or without modification, are permitted - provided that the following conditions are met: - - Redistributions of source code must retain the above copyright notice, this list of conditions - and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, this list of - conditions and the following disclaimer in the documentation and/or other materials provided with - the distribution. - - Neither the name of Notational Velocity nor the names of its contributors may be used to endorse - or promote products derived from this software without specific prior written permission. */ + This file is part of Notational Velocity. + + Notational Velocity 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 3 of the License, or + (at your option) any later version. + + Notational Velocity 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 Notational Velocity. If not, see . */ #import @@ -24,6 +29,7 @@ @interface LabelsListController : FastListDataSource { NSCountedSet *allLabels, *filteredLabels; + NSMutableDictionary *labelImages; unsigned *removeIndicies; } @@ -33,6 +39,9 @@ - (NSArray*)labelTitlesPrefixedByString:(NSString*)prefixString indexOfSelectedItem:(NSInteger *)anIndex minusWordSet:(NSSet*)antiSet; +- (void)invalidateCachedLabelImages; +- (NSImage*)cachedLabelImageForWord:(NSString*)aWord highlighted:(BOOL)isHighlighted; + - (NSSet*)notesAtFilteredIndex:(int)labelIndex; - (NSSet*)notesAtFilteredIndexes:(NSIndexSet*)anIndexSet; diff --git a/LabelsListController.m b/LabelsListController.m index b8daa984..19a9351c 100755 --- a/LabelsListController.m +++ b/LabelsListController.m @@ -5,20 +5,27 @@ // Created by Zachary Schneirov on 1/10/06. /*Copyright (c) 2010, Zachary Schneirov. All rights reserved. - Redistribution and use in source and binary forms, with or without modification, are permitted - provided that the following conditions are met: - - Redistributions of source code must retain the above copyright notice, this list of conditions - and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, this list of - conditions and the following disclaimer in the documentation and/or other materials provided with - the distribution. - - Neither the name of Notational Velocity nor the names of its contributors may be used to endorse - or promote products derived from this software without specific prior written permission. */ + This file is part of Notational Velocity. + + Notational Velocity 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 3 of the License, or + (at your option) any later version. + + Notational Velocity 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 Notational Velocity. If not, see . */ #import "LabelsListController.h" #import "LabelObject.h" #import "NoteObject.h" +#import "GlobalPrefs.h" +#import "NSBezierPath_NV.h" #import "NSCollection_utils.h" @@ -37,6 +44,14 @@ - (id)init { return self; } +- (void)dealloc { + + [labelImages release]; + [allLabels release]; + [filteredLabels release]; + [super dealloc]; +} + - (void)unfilterLabels { [filteredLabels setSet:allLabels]; @@ -89,6 +104,56 @@ - (NSArray*)labelTitlesPrefixedByString:(NSString*)prefixString indexOfSelectedI return titles; } +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 +static CGRect NSRectToCGRect(NSRect nsrect) { + union _ {NSRect ns; CGRect cg;}; + return ((union _ *)&nsrect)->cg; +} +#endif + +- (void)invalidateCachedLabelImages { + //used when the list font size changes + [labelImages removeAllObjects]; +} +- (NSImage*)cachedLabelImageForWord:(NSString*)aWord highlighted:(BOOL)isHighlighted { + if (!labelImages) labelImages = [[NSMutableDictionary alloc] init]; + + NSString *imgKey = [[aWord lowercaseString] stringByAppendingFormat:@", %d", isHighlighted]; + NSImage *img = [labelImages objectForKey:imgKey]; + if (!img) { + //generate the image and add it to labelImages under imgKey + float tableFontSize = [[GlobalPrefs defaultPrefs] tableFontSize] - 1.0; + NSDictionary *attrs = [NSDictionary dictionaryWithObject:[NSFont systemFontOfSize:tableFontSize] forKey:NSFontAttributeName]; + NSSize wordSize = [aWord sizeWithAttributes:attrs]; + NSRect wordRect = NSMakeRect(0, 0, roundf(wordSize.width + 4.0), roundf(tableFontSize * 1.3)); + + //peter hosey's suggestion, rather than doing setWindingRule: and appendBezierPath: as before: + //http://stackoverflow.com/questions/4742773/why-wont-helvetica-neue-bold-glyphs-draw-as-a-normal-subpath-in-nsbezierpath + + img = [[NSImage alloc] initWithSize:wordRect.size]; + [img lockFocus]; + + CGContextRef context = (CGContextRef)([[NSGraphicsContext currentContext] graphicsPort]); + CGContextBeginTransparencyLayer(context, NULL); + + CGContextClipToRect(context, NSRectToCGRect(wordRect)); + + NSBezierPath *backgroundPath = [NSBezierPath bezierPathWithRoundRectInRect:wordRect radius:2.0f]; + [(isHighlighted ? [NSColor whiteColor] : [NSColor colorWithCalibratedWhite:0.55 alpha:1.0]) setFill]; + [backgroundPath fill]; + + [[NSGraphicsContext currentContext] setCompositingOperation:NSCompositeSourceOut]; + [aWord drawWithRect:(NSRect){{2.0, 3.0}, wordRect.size} options:NSStringDrawingUsesFontLeading attributes:attrs]; + + CGContextEndTransparencyLayer(context); + + [img unlockFocus]; + + [labelImages setObject:[img autorelease] forKey:imgKey]; + } + return img; +} + //NotationController will probably want to filter these further if there is already a search in progress - (NSSet*)notesAtFilteredIndex:(int)labelIndex { @@ -125,6 +190,8 @@ - (void)removeLabelSet:(NSSet*)labelSet fromNote:(NoteObject*)note { [allLabels minusSet:labelSet]; + //could use this as an opportunity to remove counterparts in labelImages + //we narrow down the set to make sure that we operate on the actual objects within it, and note the objects used as prototypes //these will be any labels that were shared by notes other than this one NSMutableSet *existingLabels = [allLabels setIntersectedWithSet:labelSet]; @@ -146,6 +213,8 @@ - (void)addLabelSet:(NSSet*)labelSet toNote:(NoteObject*)note { - (void)removeLabelSet:(NSSet*)labelSet fromNoteSet:(NSSet*)notes { [allLabels minusSet:labelSet]; + //could use this as an opportunity to remove counterparts in labelImages + NSMutableSet *existingLabels = [allLabels setIntersectedWithSet:labelSet]; [existingLabels makeObjectsPerformSelector:@selector(removeNoteSet:) withObject:notes]; } diff --git a/Notation.xcodeproj/project.pbxproj b/Notation.xcodeproj/project.pbxproj index aa140217..89005165 100755 --- a/Notation.xcodeproj/project.pbxproj +++ b/Notation.xcodeproj/project.pbxproj @@ -266,6 +266,11 @@ 93B348291371547800658F98 /* HUDIconPrint.png in Resources */ = {isa = PBXBuildFile; fileRef = 93B348251371547800658F98 /* HUDIconPrint.png */; }; 93B3482A1371547800658F98 /* HUDIconSave.png in Resources */ = {isa = PBXBuildFile; fileRef = 93B348261371547800658F98 /* HUDIconSave.png */; }; 93B3482B1371547800658F98 /* HUDIconShare.png in Resources */ = {isa = PBXBuildFile; fileRef = 93B348271371547800658F98 /* HUDIconShare.png */; }; + 93B90E7A1386018200BEC5FB /* ExternalEditorListController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93B90E781386018200BEC5FB /* ExternalEditorListController.m */; }; + 93B90E84138601C100BEC5FB /* LICENSE.txt in Resources */ = {isa = PBXBuildFile; fileRef = 93B90E7E138601C100BEC5FB /* LICENSE.txt */; }; + 93B90E85138601C100BEC5FB /* NSAppleEventDescriptor-Extensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 93B90E80138601C100BEC5FB /* NSAppleEventDescriptor-Extensions.m */; }; + 93B90E86138601C100BEC5FB /* ODBEditor.m in Sources */ = {isa = PBXBuildFile; fileRef = 93B90E82138601C100BEC5FB /* ODBEditor.m */; }; + 93B90E921386072E00BEC5FB /* TemporaryFileCachePreparer.m in Sources */ = {isa = PBXBuildFile; fileRef = 93B90E911386072E00BEC5FB /* TemporaryFileCachePreparer.m */; }; D3EECA00106F66C70023D7F8 /* NVPasswordGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = D3EEC9FE106F66C70023D7F8 /* NVPasswordGenerator.m */; }; /* End PBXBuildFile section */ @@ -717,6 +722,16 @@ 93B348251371547800658F98 /* HUDIconPrint.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = HUDIconPrint.png; path = Images/HUDIconPrint.png; sourceTree = ""; }; 93B348261371547800658F98 /* HUDIconSave.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = HUDIconSave.png; path = Images/HUDIconSave.png; sourceTree = ""; }; 93B348271371547800658F98 /* HUDIconShare.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = HUDIconShare.png; path = Images/HUDIconShare.png; sourceTree = ""; }; + 93B90E771386018200BEC5FB /* ExternalEditorListController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExternalEditorListController.h; sourceTree = ""; }; + 93B90E781386018200BEC5FB /* ExternalEditorListController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExternalEditorListController.m; sourceTree = ""; }; + 93B90E7E138601C100BEC5FB /* LICENSE.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE.txt; sourceTree = ""; }; + 93B90E7F138601C100BEC5FB /* NSAppleEventDescriptor-Extensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSAppleEventDescriptor-Extensions.h"; sourceTree = ""; }; + 93B90E80138601C100BEC5FB /* NSAppleEventDescriptor-Extensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSAppleEventDescriptor-Extensions.m"; sourceTree = ""; }; + 93B90E81138601C100BEC5FB /* ODBEditor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ODBEditor.h; sourceTree = ""; }; + 93B90E82138601C100BEC5FB /* ODBEditor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ODBEditor.m; sourceTree = ""; }; + 93B90E83138601C100BEC5FB /* ODBEditorSuite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ODBEditorSuite.h; sourceTree = ""; }; + 93B90E901386072E00BEC5FB /* TemporaryFileCachePreparer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TemporaryFileCachePreparer.h; sourceTree = ""; }; + 93B90E911386072E00BEC5FB /* TemporaryFileCachePreparer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TemporaryFileCachePreparer.m; sourceTree = ""; }; D3EEC9FE106F66C70023D7F8 /* NVPasswordGenerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NVPasswordGenerator.m; sourceTree = ""; }; D3EEC9FF106F66C70023D7F8 /* NVPasswordGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NVPasswordGenerator.h; sourceTree = ""; }; F0E01EE812DB95F40000B192 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/Preferences.xib; sourceTree = ""; }; @@ -747,6 +762,8 @@ 080E96DDFE201D6D7F000001 /* Classes */ = { isa = PBXGroup; children = ( + 93B90E901386072E00BEC5FB /* TemporaryFileCachePreparer.h */, + 93B90E911386072E00BEC5FB /* TemporaryFileCachePreparer.m */, 5C96F713131C4A1B00A2E4AC /* UIElement Subclasses */, 5CA10F30131C29E900DE1F91 /* AppController_Preview.h */, 5CA10F31131C29E900DE1F91 /* AppController_Preview.m */, @@ -759,6 +776,8 @@ 21D60126098FDB46007440DF /* GlobalPrefs.h */, 21D60127098FDB46007440DF /* GlobalPrefs.m */, 2114C11409575BD400614E74 /* AppController.h */, + 93B90E771386018200BEC5FB /* ExternalEditorListController.h */, + 93B90E781386018200BEC5FB /* ExternalEditorListController.m */, 2114C11509575BD400614E74 /* AppController.m */, 2191982009D607C600586B15 /* PrefsWindowController.h */, 2191982109D607C600586B15 /* PrefsWindowController.m */, @@ -1144,6 +1163,7 @@ 29B97315FDCFA39411CA2CEA /* Other Sources */ = { isa = PBXGroup; children = ( + 93B90E7D138601C100BEC5FB /* ODBEditor */, 5C96F5A8131C379200A2E4AC /* Markup */, 5CA10FFA131C2A7600DE1F91 /* readability */, 21F07C7010EDED87004FE56F /* BSJSON */, @@ -1273,6 +1293,19 @@ path = readability; sourceTree = ""; }; + 93B90E7D138601C100BEC5FB /* ODBEditor */ = { + isa = PBXGroup; + children = ( + 93B90E7E138601C100BEC5FB /* LICENSE.txt */, + 93B90E7F138601C100BEC5FB /* NSAppleEventDescriptor-Extensions.h */, + 93B90E80138601C100BEC5FB /* NSAppleEventDescriptor-Extensions.m */, + 93B90E81138601C100BEC5FB /* ODBEditor.h */, + 93B90E82138601C100BEC5FB /* ODBEditor.m */, + 93B90E83138601C100BEC5FB /* ODBEditorSuite.h */, + ); + path = ODBEditor; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -1467,6 +1500,7 @@ 93B348291371547800658F98 /* HUDIconPrint.png in Resources */, 93B3482A1371547800658F98 /* HUDIconSave.png in Resources */, 93B3482B1371547800658F98 /* HUDIconShare.png in Resources */, + 93B90E84138601C100BEC5FB /* LICENSE.txt in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1577,6 +1611,10 @@ 5C9C7734131DA04300FB2652 /* WordCountToken.m in Sources */, 5CA08C2013301D5E004E3250 /* ETContentView.m in Sources */, 5C2FAD2B1333970200FBFD34 /* ETScrollView.m in Sources */, + 93B90E7A1386018200BEC5FB /* ExternalEditorListController.m in Sources */, + 93B90E85138601C100BEC5FB /* NSAppleEventDescriptor-Extensions.m in Sources */, + 93B90E86138601C100BEC5FB /* ODBEditor.m in Sources */, + 93B90E921386072E00BEC5FB /* TemporaryFileCachePreparer.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/NotationController.m b/NotationController.m index ac4bfc25..70721153 100755 --- a/NotationController.m +++ b/NotationController.m @@ -5,15 +5,21 @@ // Created by Zachary Schneirov on 12/19/05. /*Copyright (c) 2010, Zachary Schneirov. All rights reserved. - Redistribution and use in source and binary forms, with or without modification, are permitted - provided that the following conditions are met: - - Redistributions of source code must retain the above copyright notice, this list of conditions - and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, this list of - conditions and the following disclaimer in the documentation and/or other materials provided with - the distribution. - - Neither the name of Notational Velocity nor the names of its contributors may be used to endorse - or promote products derived from this software without specific prior written permission. */ + This file is part of Notational Velocity. + + Notational Velocity 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 3 of the License, or + (at your option) any later version. + + Notational Velocity 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 Notational Velocity. If not, see . */ + #import "AppController.h" #import "NotationController.h" @@ -28,6 +34,7 @@ #import "NoteAttributeColumn.h" #import "FrozenNotation.h" #import "AlienNoteImporter.h" +#import "ODBEditor.h" #import "NotationFileManager.h" #import "NotationSyncServiceManager.h" #import "NotationDirectoryManager.h" @@ -572,6 +579,9 @@ - (void)handleJournalError { - (void)databaseEncryptionSettingsChanged { //we _must_ re-init the journal (if fmt is single-db and jrnl exists) in addition to flushing DB [self flushEverything]; + + //called whenever note-storage format or encryption-activation changes + [[ODBEditor sharedODBEditor] initializeDatabase:notationPrefs]; } //notation prefs delegate method @@ -609,6 +619,8 @@ - (void)databaseSettingsChangedFromOldFormat:(int)oldFormat { [self synchronizeNotesFromDirectory]; } + //perform after delay because this could trigger the mounting of a RAM disk in a background NSTask + [[ODBEditor sharedODBEditor] performSelector:@selector(initializeDatabase:) withObject:notationPrefs afterDelay:0.0]; } - (int)currentNoteStorageFormat { @@ -697,8 +709,15 @@ - (BOOL)aliasNeedsUpdating { return aliasNeedsUpdating; } -- (void)endDeletionManagerIfNecessary { - return [deletionManager cancelPanelReturningCode:NSRunStoppedResponse]; +- (void)closeAllResources { + [allNotes makeObjectsPerformSelector:@selector(abortEditingInExternalEditor)]; + + [deletionManager cancelPanelReturningCode:NSRunStoppedResponse]; + [self stopSyncServices]; + [self stopFileNotifications]; + if ([self flushAllNoteChanges]) + [self closeJournal]; + [allNotes makeObjectsPerformSelector:@selector(disconnectLabels)]; } - (void)checkIfNotationIsTrashed { @@ -1027,6 +1046,7 @@ - (void)removeNote:(NoteObject*)aNoteObject { [aNoteObject retain]; [aNoteObject disconnectLabels]; + [aNoteObject abortEditingInExternalEditor]; [allNotes removeObjectIdenticalTo:aNoteObject]; DeletedNoteObject *deletedNote = [self _addDeletedNote:aNoteObject]; @@ -1501,10 +1521,6 @@ - (void)regeneratePreviewsForColumn:(NSTableColumn*)col visibleFilteredRows:(NSR } } -- (void)invalidateAllLabelPreviewImages { - [allNotes makeObjectsPerformSelector:@selector(invalidateLabelsPreviewImage)]; -} - - (void)regenerateAllPreviews { [allNotes makeObjectsPerformSelector:@selector(updateTablePreviewString)]; } diff --git a/NoteObject.h b/NoteObject.h index aa2b9337..3f84a4cc 100755 --- a/NoteObject.h +++ b/NoteObject.h @@ -5,15 +5,20 @@ // Created by Zachary Schneirov on 12/19/05. /*Copyright (c) 2010, Zachary Schneirov. All rights reserved. - Redistribution and use in source and binary forms, with or without modification, are permitted - provided that the following conditions are met: - - Redistributions of source code must retain the above copyright notice, this list of conditions - and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, this list of - conditions and the following disclaimer in the documentation and/or other materials provided with - the distribution. - - Neither the name of Notational Velocity nor the names of its contributors may be used to endorse - or promote products derived from this software without specific prior written permission. */ + This file is part of Notational Velocity. + + Notational Velocity 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 3 of the License, or + (at your option) any later version. + + Notational Velocity 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 Notational Velocity. If not, see . */ #import @@ -24,6 +29,7 @@ @class LabelObject; @class WALStorageController; @class NotesTableView; +@class ExternalEditor; typedef struct _NoteFilterContext { char* needle; @@ -35,8 +41,6 @@ typedef struct _NoteFilterContext { NSString *titleString, *labelString; NSMutableAttributedString *contentString; - NSImage *labelsPreviewImage, *highlightedLabelsPreviewImage; - //caching/searching purposes only -- created at runtime char *cTitle, *cContents, *cLabels, *cTitleFoundPtr, *cContentsFoundPtr, *cLabelsFoundPtr; NSMutableSet *labelSet; @@ -156,10 +160,9 @@ NSInteger compareFileSize(id *a, id *b); - (void)setLabelString:(NSString*)newLabels; - (NSMutableSet*)labelSetFromCurrentString; - (NSArray*)orderedLabelTitles; -- (void)invalidateLabelsPreviewImage; -- (NSImage*)highlightedLabelsPreviewImage; -- (NSImage*)labelsPreviewImage; -- (NSImage*)_labelsPreviewImageOfColor:(NSColor*)aColor; +- (NSSize)sizeOfLabelBlocks; +- (void)_drawLabelBlocksInRect:(NSRect)aRect rightAlign:(BOOL)onRight highlighted:(BOOL)isHighlighted getSizeOnly:(NSSize*)reqSize; +- (void)drawLabelBlocksInRect:(NSRect)aRect rightAlign:(BOOL)onRight highlighted:(BOOL)isHighlighted; - (void)setSyncObjectAndKeyMD:(NSDictionary*)aDict forService:(NSString*)serviceName; - (void)removeAllSyncMDForService:(NSString*)serviceName; @@ -174,7 +177,7 @@ NSInteger compareFileSize(id *a, id *b); - (BOOL)upgradeEncodingToUTF8; - (BOOL)updateFromFile; - (BOOL)updateFromCatalogEntry:(NoteCatalogEntry*)catEntry; -- (BOOL)updateFromData:(NSMutableData*)data; +- (BOOL)updateFromData:(NSMutableData*)data inFormat:(int)fmt; - (OSStatus)writeFileDatesAndUpdateTrackingInfo; @@ -195,6 +198,8 @@ NSInteger compareFileSize(id *a, id *b); - (OSStatus)exportToDirectoryRef:(FSRef*)directoryRef withFilename:(NSString*)userFilename usingFormat:(int)storageFormat overwrite:(BOOL)overwrite; - (NSRange)nextRangeForWords:(NSArray*)words options:(unsigned)opts range:(NSRange)inRange; +- (void)editExternallyUsingEditor:(ExternalEditor*)ed; +- (void)abortEditingInExternalEditor; - (void)setFilenameFromTitle; - (void)setFilename:(NSString*)aString withExternalTrigger:(BOOL)externalTrigger; diff --git a/NoteObject.m b/NoteObject.m index 0b25527a..a7f702bd 100755 --- a/NoteObject.m +++ b/NoteObject.m @@ -5,15 +5,20 @@ // Created by Zachary Schneirov on 12/19/05. /*Copyright (c) 2010, Zachary Schneirov. All rights reserved. - Redistribution and use in source and binary forms, with or without modification, are permitted - provided that the following conditions are met: - - Redistributions of source code must retain the above copyright notice, this list of conditions - and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, this list of - conditions and the following disclaimer in the documentation and/or other materials provided with - the distribution. - - Neither the name of Notational Velocity nor the names of its contributors may be used to endorse - or promote products derived from this software without specific prior written permission. */ + This file is part of Notational Velocity. + + Notational Velocity 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 3 of the License, or + (at your option) any later version. + + Notational Velocity 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 Notational Velocity. If not, see . */ #import "NoteObject.h" @@ -30,12 +35,13 @@ #import "NotationSyncServiceManager.h" #import "SyncServiceSessionProtocol.h" #import "SyncSessionController.h" +#import "ExternalEditorListController.h" #import "NSData_transformations.h" #import "NSCollection_utils.h" #import "NotesTableView.h" #import "UnifiedCell.h" #import "LabelColumnCell.h" -#import "NSBezierPath_NV.h" +#import "ODBEditor.h" #if __LP64__ // Needed for compatability with data created by 32bit app @@ -187,6 +193,7 @@ NSInteger compareLabelString(id *a, id *b) { (CFStringRef)(labelsOfNote(*(NoteObject **)b)), kCFCompareCaseInsensitive); } NSInteger compareTitleString(id *a, id *b) { + //add kCFCompareNumerically to options for natural order sort CFComparisonResult stringResult = CFStringCompare((CFStringRef)(titleOfNote(*(NoteObject**)a)), (CFStringRef)(titleOfNote(*(NoteObject**)b)), kCFCompareCaseInsensitive); @@ -709,12 +716,11 @@ - (void)updateTablePreviewString { if ([prefs tableColumnsShowPreview]) { if ([prefs horizontalLayout]) { - //labelsPreviewImage does work only when the image is explicitly invalidated, and because updateTablePreviewString //is called for visible notes at launch and resize only, generation of images for invisible notes is delayed until after launch - NSImage *img = ColumnIsSet(NoteLabelsColumn, [prefs tableColumnsBitmap]) ? [self labelsPreviewImage] : nil; + NSSize labelBlockSize = ColumnIsSet(NoteLabelsColumn, [prefs tableColumnsBitmap]) ? [self sizeOfLabelBlocks] : NSZeroSize; tableTitleString = [[titleString attributedMultiLinePreviewFromBodyText:contentString upToWidth:[delegate titleColumnWidth] - intrusionWidth:img ? [img size].width : 0.0] retain]; + intrusionWidth:labelBlockSize.width] retain]; } else { tableTitleString = [[titleString attributedSingleLinePreviewFromBodyText:contentString upToWidth:[delegate titleColumnWidth]] retain]; } @@ -962,7 +968,6 @@ - (BOOL)_setLabelString:(NSString*)newLabelString { cLabelsFoundPtr = cLabels = replaceString(cLabels, [labelString lowercaseUTF8String]); [self updateLabelConnections]; - [self invalidateLabelsPreviewImage]; return YES; } return NO; @@ -1009,99 +1014,64 @@ - (NSArray*)orderedLabelTitles { return [labelString labelCompatibleWords]; } -- (void)invalidateLabelsPreviewImage { - [highlightedLabelsPreviewImage release]; - highlightedLabelsPreviewImage = nil; - [labelsPreviewImage release]; - labelsPreviewImage = nil; -} - -- (NSImage*)highlightedLabelsPreviewImage { - if (!highlightedLabelsPreviewImage && [labelString length]) { - highlightedLabelsPreviewImage = [[self _labelsPreviewImageOfColor:[NSColor whiteColor]] retain]; - } - return highlightedLabelsPreviewImage; +- (NSSize)sizeOfLabelBlocks { + NSSize size = NSZeroSize; + [self _drawLabelBlocksInRect:NSZeroRect rightAlign:NO highlighted:NO getSizeOnly:&size]; + return size; } -- (NSImage*)labelsPreviewImage { - if (!labelsPreviewImage && [labelString length]) { - labelsPreviewImage = [[self _labelsPreviewImageOfColor:[NSColor colorWithCalibratedWhite:0.55 alpha:1.0]] retain]; - } - return labelsPreviewImage; +- (void)drawLabelBlocksInRect:(NSRect)aRect rightAlign:(BOOL)onRight highlighted:(BOOL)isHighlighted { + return [self _drawLabelBlocksInRect:aRect rightAlign:onRight highlighted:isHighlighted getSizeOnly:NULL]; } -- (NSImage*)_labelsPreviewImageOfColor:(NSColor*)aColor { - if ([labelString length]) { - float tableFontSize = [[GlobalPrefs defaultPrefs] tableFontSize] - 1.0; - NSFont *font = [NSFont systemFontOfSize:tableFontSize]; - NSDictionary *attrs = [NSDictionary dictionaryWithObject:font forKey:NSFontNameAttribute]; - - //compute dimensions of each word first using nslayoutmanager; -sizeWithAttributes: likes to ignore the font and size here for some reason - - static NSTextStorage *textStorage = nil; - static NSTextContainer *textContainer = nil; - static NSLayoutManager *layoutManager = nil; - - if (!layoutManager) { - textStorage = [[NSTextStorage alloc] initWithString:@"" attributes:attrs]; - textContainer = [[NSTextContainer alloc] initWithContainerSize:NSMakeSize(1e7, 1e7)]; - layoutManager = [[NSLayoutManager alloc] init]; +- (void)_drawLabelBlocksInRect:(NSRect)aRect rightAlign:(BOOL)onRight highlighted:(BOOL)isHighlighted getSizeOnly:(NSSize*)reqSize { + //used primarily by UnifiedCell, but also by LabelColumnCell, as well as to determine the width of all label-block-images for this note + //iterate over words in orderedLabelTitles, retrieving images via -[LabelsListController cachedLabelImageForWord:highlighted:] + //if right-align is enabled, then the label-images are queued on the first pass and drawn in reverse on the second + + float totalWidth = 0.0, height = 0.0; + + if (![labelString length]) goto returnSizeIfNecessary; + + NSArray *words = [self orderedLabelTitles]; + if (![words count]) goto returnSizeIfNecessary; + + NSPoint nextBoxPoint = onRight ? NSMakePoint(NSMaxX(aRect), aRect.origin.y) : aRect.origin; + NSMutableArray *images = reqSize || !onRight ? nil : [NSMutableArray arrayWithCapacity:[words count]]; + NSInteger i; + + for (i=0; i<(NSInteger)[words count]; i++) { + NSString *word = [words objectAtIndex:i]; + if ([word length]) { + NSImage *img = [[delegate labelsListDataSource] cachedLabelImageForWord:word highlighted:isHighlighted]; - [textContainer setLineFragmentPadding:0.0]; - [layoutManager addTextContainer:textContainer]; - [textStorage addLayoutManager:layoutManager]; + if (!reqSize) { + if (onRight) { + [images addObject:img]; + } else { + [img compositeToPoint:nextBoxPoint operation:NSCompositeSourceOver]; + nextBoxPoint.x += [img size].width + 4.0; + } + } else { + totalWidth += [img size].width + 4.0; + height = MAX(height, [img size].height); + } } - - NSArray *words = [self orderedLabelTitles]; - if (![words count]) - return nil; - - NSBezierPath *blocksPath = [NSBezierPath bezierPath]; - NSPoint nextBoxPoint = NSZeroPoint; - NSUInteger i; - float imageWidth = 0.0; - - for (i=0; i<[words count]; i++) { - NSString *word = [words objectAtIndex:i]; - if ([word length]) { - - //Force the layout manager to layout its text - [[textStorage mutableString] setString:word]; - [textStorage setFont:font]; //will infuriatingly revert to measuring Lucida Grande 11 otherwise, despite what it actually says - - (void)[layoutManager glyphRangeForTextContainer:textContainer]; - NSSize wordSize = [layoutManager usedRectForTextContainer:textContainer].size; - - NSRect wordRect = NSMakeRect(nextBoxPoint.x, nextBoxPoint.y, roundf(wordSize.width + 4.0), roundf(tableFontSize * 1.3)); - imageWidth += wordRect.size.width + 4.0; - - NSBezierPath *stringPath = [NSBezierPath bezierPathWithLayoutManager:layoutManager characterRange:NSMakeRange(0,[word length]) - atPoint:NSMakePoint(nextBoxPoint.x + 2.0, 3.0)]; - wordRect.origin = nextBoxPoint; - - NSBezierPath *backgroundPath = [NSBezierPath bezierPathWithRoundRectInRect:wordRect radius:2.0f]; - - [backgroundPath setWindingRule:NSEvenOddWindingRule]; - [backgroundPath appendBezierPath:stringPath]; - - [blocksPath appendBezierPath:backgroundPath]; - - nextBoxPoint = NSMakePoint(roundf(nextBoxPoint.x + wordRect.size.width + 4.0), 0.0); + } + + if (!reqSize) { + if (onRight) { + //draw images in reverse instead + for (i = [images count] - 1; i>=0; i--) { + NSImage *img = [images objectAtIndex:i]; + nextBoxPoint.x -= [img size].width + 4.0; + [img compositeToPoint:nextBoxPoint operation:NSCompositeSourceOver]; } } - - - NSImage *img = [[NSImage alloc] initWithSize:NSMakeSize(imageWidth - 4.0, tableFontSize * 1.3 + 1.5)]; - [img lockFocus]; - - [aColor setFill]; - [blocksPath fill]; - - [img unlockFocus]; - - return [img autorelease]; + } else { + returnSizeIfNecessary: + if (reqSize) *reqSize = NSMakeSize(totalWidth, height); } - return nil; } @@ -1420,7 +1390,7 @@ - (BOOL)updateFromFile { return NO; } - if ([self updateFromData:data]) { + if ([self updateFromData:data inFormat:currentFormatID]) { FSCatalogInfo info; if ([delegate fileInNotesDirectory:noteFileRefInit(self) isOwnedByUs:NULL hasCatalogInfo:&info] == noErr) { fileModifiedDate = info.contentModDate; @@ -1443,7 +1413,7 @@ - (BOOL)updateFromCatalogEntry:(NoteCatalogEntry*)catEntry { return NO; } - if (![self updateFromData:data]) + if (![self updateFromData:data inFormat:currentFormatID]) return NO; [self setFilename:(NSString*)catEntry->filename withExternalTrigger:YES]; @@ -1496,7 +1466,7 @@ - (BOOL)updateFromCatalogEntry:(NoteCatalogEntry*)catEntry { return YES; } -- (BOOL)updateFromData:(NSMutableData*)data { +- (BOOL)updateFromData:(NSMutableData*)data inFormat:(int)fmt { if (!data) { NSLog(@"%@: Data is nil!", NSStringFromSelector(_cmd)); @@ -1506,7 +1476,7 @@ - (BOOL)updateFromData:(NSMutableData*)data { NSMutableString *stringFromData = nil; NSMutableAttributedString *attributedStringFromData = nil; //interpret based on format; text, rtf, html, etc... - switch (currentFormatID) { + switch (fmt) { case SingleDatabaseFormat: //hmmmmm NSAssert(NO, @"Warning! Tried to update data from a note in single-db format!"); @@ -1534,11 +1504,11 @@ - (BOOL)updateFromData:(NSMutableData*)data { break; default: - NSLog(@"%@: Unknown format: %d", NSStringFromSelector(_cmd), currentFormatID); + NSLog(@"%@: Unknown format: %d", NSStringFromSelector(_cmd), fmt); } if (!attributedStringFromData) { - NSLog(@"Couldn't make string out of data for note %@ with format %d", titleString, currentFormatID); + NSLog(@"Couldn't make string out of data for note %@ with format %d", titleString, fmt); return NO; } @@ -1734,6 +1704,40 @@ - (OSStatus)exportToDirectoryRef:(FSRef*)directoryRef withFilename:(NSString*)us return noErr; } +- (void)editExternallyUsingEditor:(ExternalEditor*)ed { + [[ODBEditor sharedODBEditor] editNote:self inEditor:ed context:nil]; +} + +- (void)abortEditingInExternalEditor { + [[ODBEditor sharedODBEditor] abortAllEditingSessionsForClient:self]; +} + +-(void)odbEditor:(ODBEditor *)editor didModifyFile:(NSString *)path newFileLocation:(NSString *)newPath context:(NSDictionary *)context { + + //read path/newPath into NSData and update note contents + + //can't use updateFromCatalogEntry because it would assign ownership via various metadata + + if ([self updateFromData:[NSMutableData dataWithContentsOfFile:path options:NSUncachedRead error:NULL] inFormat:PlainTextFormat]) { + //reflect the temp file's changes directly back to the backing-store-file, database, and sync services + [self makeNoteDirtyUpdateTime:YES updateFile:YES]; + + [delegate note:self attributeChanged:NotePreviewString]; + [[delegate delegate] contentsUpdatedForNote:self]; + } else { + NSBeep(); + NSLog(@"odbEditor:didModifyFile: unable to get data from %@", path); + } +} +-(void)odbEditor:(ODBEditor *)editor didClosefile:(NSString *)path context:(NSDictionary *)context { + //remove the temp file +#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 + [[NSFileManager defaultManager] removeItemAtPath:path error:NULL]; +#else + [[NSFileManager defaultManager] removeFileAtPath:path handler:nil]; +#endif +} + - (NSRange)nextRangeForWords:(NSArray*)words options:(unsigned)opts range:(NSRange)inRange { //opts indicate forwards or backwards, inRange allows us to continue from where we left off //return location of NSNotFound and length 0 if none of the words could be found inRange @@ -1792,9 +1796,10 @@ BOOL noteTitleIsAPrefixOfOtherNoteTitle(NoteObject *longerNote, NoteObject *shor - (void)addPrefixParentNote:(NoteObject*)aNote { if (!prefixParentNotes) { - prefixParentNotes = [[NSMutableArray alloc] init]; + prefixParentNotes = [[NSMutableArray alloc] initWithObjects:&aNote count:1]; + } else { + [prefixParentNotes addObject:aNote]; } - [prefixParentNotes addObject:aNote]; } - (void)removeAllPrefixParentNotes { [prefixParentNotes removeAllObjects]; diff --git a/NotesTableView.h b/NotesTableView.h index 531963fc..2e52c79e 100755 --- a/NotesTableView.h +++ b/NotesTableView.h @@ -1,14 +1,19 @@ /* NotesTableView */ /*Copyright (c) 2010, Zachary Schneirov. All rights reserved. - Redistribution and use in source and binary forms, with or without modification, are permitted - provided that the following conditions are met: - - Redistributions of source code must retain the above copyright notice, this list of conditions - and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, this list of - conditions and the following disclaimer in the documentation and/or other materials provided with - the distribution. - - Neither the name of Notational Velocity nor the names of its contributors may be used to endorse - or promote products derived from this software without specific prior written permission. */ + This file is part of Notational Velocity. + + Notational Velocity 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 3 of the License, or + (at your option) any later version. + + Notational Velocity 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 Notational Velocity. If not, see . */ #import @@ -16,8 +21,6 @@ @class HeaderViewWithMenu; @class NoteAttributeColumn; @class GlobalPrefs; -//@class NotesTableCornerView; -//@class NVTransparentScroller; typedef struct _ViewLocationContext { BOOL pivotRowWasEdge; @@ -44,11 +47,9 @@ typedef struct _ViewLocationContext { GlobalPrefs *globalPrefs; NSMenuItem *dummyItem; HeaderViewWithMenu *headerView; - //NotesTableCornerView *cornerView; NSView *cornerView; NSTextFieldCell *cachedCell; - //NVTransparentScroller *nvNotesScroller; NSDictionary *loadStatusAttributes; float loadStatusStringWidth; NSString *loadStatusString; @@ -56,6 +57,8 @@ typedef struct _ViewLocationContext { float tableFontHeight; int affinity; + + NSUserDefaults *userDefaults; } - (void)noteFirstVisibleRow; @@ -93,10 +96,10 @@ typedef struct _ViewLocationContext { - (NoteAttributeColumn*)noteAttributeColumnForIdentifier:(NSString*)identifier; - (void)incrementNoteSelection:(id)sender; +- (void)_incrementNoteSelectionByTag:(NSInteger)tag; - (id)labelsListSource; - (void)setLabelsListSource:(id)labelsSource; -- (NSArray *)labelCompletionsForString:(NSString *)fieldString index:(int)index; @end diff --git a/NotesTableView.m b/NotesTableView.m index c81d52c8..bcf20a4c 100755 --- a/NotesTableView.m +++ b/NotesTableView.m @@ -1,19 +1,25 @@ /*Copyright (c) 2010, Zachary Schneirov. All rights reserved. - Redistribution and use in source and binary forms, with or without modification, are permitted - provided that the following conditions are met: - - Redistributions of source code must retain the above copyright notice, this list of conditions - and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, this list of - conditions and the following disclaimer in the documentation and/or other materials provided with - the distribution. - - Neither the name of Notational Velocity nor the names of its contributors may be used to endorse - or promote products derived from this software without specific prior written permission. */ + This file is part of Notational Velocity. + + Notational Velocity 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 3 of the License, or + (at your option) any later version. + + Notational Velocity 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 Notational Velocity. If not, see . */ #import "NotesTableView.h" #import "AppController_Importing.h" #import "FastListDataSource.h" #import "NoteAttributeColumn.h" +#import "ExternalEditorListController.h" #import "GlobalPrefs.h" #import "NotationPrefs.h" #import "NoteObject.h" @@ -23,6 +29,7 @@ #import "HeaderViewWithMenu.h" #import "NSString_NV.h" #import "NotesTableHeaderCell.h" +#import "LinkingEditor.h" //#import "NotesTableCornerView.h" #define STATUS_STRING_FONT_SIZE 16.0f @@ -30,7 +37,7 @@ #define SYNTHETIC_TAGS_COLUMN_INDEX 200 -static void _CopyItemWithSelectorFromMenu(NSMenu *destMenu, NSMenu *sourceMenu, SEL aSel, id target); +static void _CopyItemWithSelectorFromMenu(NSMenu *destMenu, NSMenu *sourceMenu, SEL aSel, id target, NSInteger tag); @implementation NotesTableView @@ -39,7 +46,10 @@ - (id)initWithCoder:(NSCoder *)decoder { if ((self = [super initWithCoder:decoder])) { globalPrefs = [GlobalPrefs defaultPrefs]; - + + userDefaults = [NSUserDefaults standardUserDefaults]; + [userDefaults registerDefaults: [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool: NO], @"UseCtrlForSwitchingNotes", nil]]; + loadStatusString = NSLocalizedString(@"Loading Notes...",nil); loadStatusAttributes = [[NSDictionary dictionaryWithObjectsAndKeys: [NSFont fontWithName:@"Helvetica" size:STATUS_STRING_FONT_SIZE], NSFontAttributeName, @@ -306,13 +316,15 @@ - (void)_configureAttributesForCurrentLayout { for (i=0; i<[allColumns count]; i++) { [[[allColumns objectAtIndex:i] dataCell] setFont:font]; } + BOOL isOneRow = !horiz || (![globalPrefs tableColumnsShowPreview] && !ColumnIsSet(NoteLabelsColumn, [globalPrefs tableColumnsBitmap])); if (IsLeopardOrLater) - [self setSelectionHighlightStyle:horiz ? NSTableViewSelectionHighlightStyleSourceList : NSTableViewSelectionHighlightStyleRegular]; + [self setSelectionHighlightStyle:isOneRow ? NSTableViewSelectionHighlightStyleRegular : NSTableViewSelectionHighlightStyleSourceList]; + [self setBackgroundColor: horiz ? [NSColor colorWithCalibratedWhite:0.98 alpha:1.0] : [NSColor whiteColor]]; NSLayoutManager *lm = [[NSLayoutManager alloc] init]; tableFontHeight = [lm defaultLineHeightForFont:font]; - float h[4] = {(tableFontHeight * 3.0 + 5.0f), (tableFontHeight * 2.0 + 6.0f), (tableFontHeight + 4.0f), tableFontHeight + 2.0f}; + float h[4] = {(tableFontHeight * 3.0 + 5.0f), (tableFontHeight * 2.0 + 6.0f), (tableFontHeight + 2.0f), tableFontHeight + 2.0f}; [self setRowHeight: horiz ? ([globalPrefs tableColumnsShowPreview] ? h[0] : (ColumnIsSet(NoteLabelsColumn,[globalPrefs tableColumnsBitmap]) ? h[1] : h[2])) : h[3]]; [lm release]; @@ -443,10 +455,8 @@ - (void)editRowAtColumnWithIdentifier:(id)identifier { } if (colIndex > -1) { - [self editColumn:colIndex row:selected withEvent:[[self window] currentEvent] select:YES]; } else { - NSBeep(); } } @@ -658,18 +668,20 @@ - (NSMenu *)menuForEvent:(NSEvent *)theEvent { return [self defaultNoteCommandsMenuWithTarget:[NSApp delegate]]; } -static void _CopyItemWithSelectorFromMenu(NSMenu *destMenu, NSMenu *sourceMenu, SEL aSel, id target) { - int menuIndex = [sourceMenu indexOfItemWithTarget:target andAction:aSel]; - if (menuIndex > -1) [destMenu addItem:[[(NSMenuItem*)[sourceMenu itemAtIndex:menuIndex] copy] autorelease]]; +static void _CopyItemWithSelectorFromMenu(NSMenu *destMenu, NSMenu *sourceMenu, SEL aSel, id target, NSInteger tag) { + NSInteger idx = [sourceMenu indexOfItemWithTag:tag]; + if (idx > -1 || (idx = [sourceMenu indexOfItemWithTarget:target andAction:aSel]) > -1) { + [destMenu addItem:[[(NSMenuItem*)[sourceMenu itemAtIndex:idx] copy] autorelease]]; + } } - (NSMenu *)defaultNoteCommandsMenuWithTarget:(id)target { NSMenu *theMenu = [[[NSMenu alloc] initWithTitle:@"Contextual Note Commands Menu"] autorelease]; NSMenu *notesMenu = [[[NSApp mainMenu] itemWithTag:NOTES_MENU_ID] submenu]; - _CopyItemWithSelectorFromMenu(theMenu, notesMenu, @selector(renameNote:), target); - _CopyItemWithSelectorFromMenu(theMenu, notesMenu, @selector(tagNote:), target); - _CopyItemWithSelectorFromMenu(theMenu, notesMenu, @selector(deleteNote:), target); + _CopyItemWithSelectorFromMenu(theMenu, notesMenu, @selector(renameNote:), target, -1); + _CopyItemWithSelectorFromMenu(theMenu, notesMenu, @selector(tagNote:), target, -1); + _CopyItemWithSelectorFromMenu(theMenu, notesMenu, @selector(deleteNote:), target, -1); [theMenu addItem:[NSMenuItem separatorItem]]; @@ -679,13 +691,15 @@ - (NSMenu *)defaultNoteCommandsMenuWithTarget:(id)target { [noteLinkItem setTarget:target]; [theMenu addItem:[noteLinkItem autorelease]]; - _CopyItemWithSelectorFromMenu(theMenu, notesMenu, @selector(exportNote:), target); - _CopyItemWithSelectorFromMenu(theMenu, notesMenu, @selector(revealNote:), target); - _CopyItemWithSelectorFromMenu(theMenu, notesMenu, @selector(openFileInEditor:), target); + _CopyItemWithSelectorFromMenu(theMenu, notesMenu, @selector(exportNote:), target, -1); + _CopyItemWithSelectorFromMenu(theMenu, notesMenu, @selector(revealNote:), target, -1); + _CopyItemWithSelectorFromMenu(theMenu, notesMenu, NULL, target, 88); + + [theMenu setSubmenu:[[ExternalEditorListController sharedInstance] addEditNotesMenu] forItem:[theMenu itemAtIndex:[theMenu numberOfItems] - 1]]; [theMenu addItem:[NSMenuItem separatorItem]]; - _CopyItemWithSelectorFromMenu(theMenu, notesMenu, @selector(printNote:), target); + _CopyItemWithSelectorFromMenu(theMenu, notesMenu, @selector(printNote:), target, -1); NSArray *notes = [(FastListDataSource*)[self dataSource] objectsAtFilteredIndexes:[self selectedRowIndexes]]; [notes addMenuItemsForURLsInNotes:theMenu]; @@ -730,7 +744,6 @@ - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal { } - (void)mouseDown:(NSEvent*)event { - [[NSApp delegate] setIsEditing:NO]; //this seems like it should happen automatically, but it does not. if (![NSApp isActive]) { @@ -788,7 +801,9 @@ - (void)mouseDown:(NSEvent*)event { #define UPCHAR(x) ((x) == NSUpArrowFunctionKey || (x) == NSUpTextMovement) - (void)keyDown:(NSEvent*)theEvent { + unichar keyChar = [theEvent firstCharacter]; + if (keyChar == NSNewlineCharacter || keyChar == NSCarriageReturnCharacter || keyChar == NSEnterCharacter) { unsigned int sel = [self selectedRow]; if (sel < (unsigned)[self numberOfRows] && [self numberOfSelectedRows] == 1) { @@ -868,6 +883,7 @@ - (void)keyDown:(NSEvent*)theEvent { } + enum { kNext_Tag = 'j', kPrev_Tag = 'k' }; //use this method to catch next note/prev note before View menu does @@ -876,38 +892,50 @@ - (BOOL)performKeyEquivalent:(NSEvent *)theEvent { unsigned mods = [theEvent modifierFlags]; + BOOL isControlKeyPressed = (mods & NSControlKeyMask) != 0 && [userDefaults boolForKey: @"UseCtrlForSwitchingNotes"]; + BOOL isCommandKeyPressed = (mods & NSCommandKeyMask) != 0; + // Also catch Ctrl-J/-K to match the shortcuts of other apps - if (((mods & NSCommandKeyMask) || (mods & NSControlKeyMask)) && ((mods & NSShiftKeyMask) == 0)) { + if ((isControlKeyPressed || isCommandKeyPressed) && ((mods & NSShiftKeyMask) == 0)) { unichar keyChar = ' '; - if (mods & NSCommandKeyMask) { + if (isCommandKeyPressed) { keyChar = [theEvent firstCharacter]; /*cannot use ignoringModifiers here as it subverts the Dvorak-Qwerty-CMD keyboard layout */ } - if (mods & NSControlKeyMask) { + if (isControlKeyPressed) { keyChar = [theEvent firstCharacterIgnoringModifiers]; /* first gets '\n' when control key is set, so fall back to ignoringModifiers */ } - if (keyChar == kNext_Tag || keyChar == kPrev_Tag) { - + // Handle J and K for both Control and Command + if ( keyChar == kNext_Tag || keyChar == kPrev_Tag ) { if (mods & NSAlternateKeyMask) { - [self selectRowAndScroll:(keyChar == kNext_Tag ? [self numberOfRows] - 1 : 0)]; + [self selectRowAndScroll:((keyChar == kNext_Tag) ? [self numberOfRows] - 1 : 0)]; } else { - if (!dummyItem) dummyItem = [[NSMenuItem alloc] init]; - [dummyItem setTag:keyChar]; - - [self incrementNoteSelection:dummyItem]; + [self _incrementNoteSelectionByTag:keyChar]; } return YES; } + + // Handle N and P, but only when Control is pressed + if ( (keyChar == 'n' || keyChar == 'p') && (!isCommandKeyPressed)) { + // Determine if the note editing pane is selected: + if (![[[self window] firstResponder] isKindOfClass:[LinkingEditor class]]) { + [self _incrementNoteSelectionByTag:(keyChar == 'n') ? kNext_Tag : kPrev_Tag]; + return YES; + } + } + + // Make Control-[ equivalent to Escape + if ( (keyChar == '[' ) && (!isCommandKeyPressed)) { + [self cancelOperation:nil]; + return YES; + } } return [super performKeyEquivalent:theEvent]; } - -- (void)incrementNoteSelection:(id)sender { - - int tag = [sender tag]; +- (void)_incrementNoteSelectionByTag:(NSInteger)tag { int rowNumber = [self selectedRow]; int totalNotes = [self numberOfRows]; @@ -922,6 +950,10 @@ - (void)incrementNoteSelection:(id)sender { [self selectRowAndScroll:rowNumber]; } +- (void)incrementNoteSelection:(id)sender { + [self _incrementNoteSelectionByTag:[sender tag]]; +} + - (void)deselectAll:(id)sender { [super deselectAll:sender]; @@ -938,11 +970,7 @@ - (void)selectRowAndScroll:(NSInteger)row { } - (BOOL)textView:(NSTextView *)aTextView doCommandBySelector:(SEL)command { - if ((command == @selector(insertTab:))||(command == @selector(insertNewline:))||(command == @selector(insertBacktab:))||(command == @selector(cancelOperation:))) { - [[NSApp delegate] setIsEditing:NO]; - }//else { - // NSLog(@"ntv got %@",NSStringFromSelector(command)); - //} + if (command == @selector(moveToEndOfLine:) || command == @selector(moveToRightEndOfLine:)) { NSEvent *event = [[self window] currentEvent]; @@ -959,7 +987,7 @@ - (BOOL)textView:(NSTextView *)aTextView doCommandBySelector:(SEL)command { } } else if (command == @selector(insertTab:)) { - if ([globalPrefs horizontalLayout] && !lastEventActivatedTagEdit) { + if ([globalPrefs horizontalLayout] && !lastEventActivatedTagEdit && ColumnIsSet(NoteLabelsColumn, [globalPrefs tableColumnsBitmap])) { //if we're currently renaming a note in horizontal mode, then tab should move focus to tags area [self editRowAtColumnWithIdentifier:NoteLabelsColumnString]; @@ -992,6 +1020,7 @@ - (void)setLabelsListSource:(id)labelsSource { } - (NSArray *)textView:(NSTextView *)aTextView completions:(NSArray *)words forPartialWordRange:(NSRange)charRange indexOfSelectedItem:(NSInteger *)anIndex { + if (charRange.location != NSNotFound) { if (!IsLeopardOrLater) goto getCompletions; @@ -1011,10 +1040,10 @@ - (NSArray *)textView:(NSTextView *)aTextView completions:(NSArray *)words forP getCompletions: { - NSSet *existingWordSet = [NSSet setWithArray:[[aTextView string] labelCompatibleWords]]; - NSArray *tags = [labelsListSource labelTitlesPrefixedByString:[[aTextView string] substringWithRange:charRange] - indexOfSelectedItem:anIndex minusWordSet:existingWordSet]; - return tags; + NSSet *existingWordSet = [NSSet setWithArray:[[aTextView string] labelCompatibleWords]]; + NSArray *tags = [labelsListSource labelTitlesPrefixedByString:[[aTextView string] substringWithRange:charRange] + indexOfSelectedItem:anIndex minusWordSet:existingWordSet]; + return tags; } } } @@ -1036,7 +1065,7 @@ - (BOOL)eventIsTagEdit:(NSEvent*)event forColumn:(NSInteger)columnIndex row:(NSI //mouse is inside this column's row's cell's tags frame UnifiedCell *cell = [[[self tableColumns] objectAtIndex:columnIndex] dataCellForRow:rowIndex]; - NSRect tagCellRect = [cell nv_tagsRectForFrame:[self frameOfCellAtColumn:columnIndex row:rowIndex] andImage:nil]; + NSRect tagCellRect = [cell nv_tagsRectForFrame:[self frameOfCellAtColumn:columnIndex row:rowIndex]]; return [self mouse:p inRect:tagCellRect]; @@ -1064,7 +1093,7 @@ - (BOOL)lastEventActivatedTagEdit { } - (void)editColumn:(NSInteger)columnIndex row:(NSInteger)rowIndex withEvent:(NSEvent *)event select:(BOOL)flag { - [[NSApp delegate] setIsEditing:YES]; + BOOL isTitleCol = [self columnWithIdentifier:NoteTitleColumnString] == columnIndex; //if event's mouselocation is inside rowIndex cell's tag rect and this edit is in horizontal mode in the title column @@ -1128,12 +1157,12 @@ - (void)cancelOperation:(id)sender { } - (void)textDidChange:(NSNotification *)aNotification { - NSInteger col = [self editedColumn]; if (col > -1 && [self attributeSetterForColumn:[[self tableColumns] objectAtIndex:col]] == @selector(setLabelString:)) { //text changed while editing tags; autocomplete! NSTextView *editor = [aNotification object]; + //NSLog(@"isAutocompleting: %d, wasDeleting: %d", isAutocompleting, wasDeleting); if (!isAutocompleting && !wasDeleting) { isAutocompleting = YES; diff --git a/ODBEditor/LICENSE.txt b/ODBEditor/LICENSE.txt new file mode 100644 index 00000000..f03d6248 --- /dev/null +++ b/ODBEditor/LICENSE.txt @@ -0,0 +1,2 @@ +This work is hereby released into the Public Domain. To view a copy of the public domain dedication, visit http://creativecommons.org/licenses/publicdomain/ or send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + diff --git a/ODBEditor/NSAppleEventDescriptor-Extensions.h b/ODBEditor/NSAppleEventDescriptor-Extensions.h new file mode 100644 index 00000000..9c1ea0ee --- /dev/null +++ b/ODBEditor/NSAppleEventDescriptor-Extensions.h @@ -0,0 +1,8 @@ +#import + +@interface NSAppleEventDescriptor(Extensions) + ++ (NSAppleEventDescriptor *)descriptorWithFilePath:(NSString *)fileName; ++ (NSAppleEventDescriptor *)descriptorWithFileURL:(NSURL *)fileURL; + +@end diff --git a/ODBEditor/NSAppleEventDescriptor-Extensions.m b/ODBEditor/NSAppleEventDescriptor-Extensions.m new file mode 100644 index 00000000..22c3e049 --- /dev/null +++ b/ODBEditor/NSAppleEventDescriptor-Extensions.m @@ -0,0 +1,16 @@ +#import "NSAppleEventDescriptor-Extensions.h" + +@implementation NSAppleEventDescriptor(Extensions) + ++ (NSAppleEventDescriptor *)descriptorWithFilePath:(NSString *)fileName { + NSURL *url = [NSURL fileURLWithPath: fileName]; + return [self descriptorWithFileURL: url]; +} + ++ (NSAppleEventDescriptor *)descriptorWithFileURL:(NSURL *)fileURL { + NSString *string = [fileURL absoluteString]; + NSData *data = [string dataUsingEncoding: NSUTF8StringEncoding]; + return [self descriptorWithDescriptorType: typeFileURL data: data]; +} + +@end diff --git a/ODBEditor/ODBEditor.h b/ODBEditor/ODBEditor.h new file mode 100644 index 00000000..d8075343 --- /dev/null +++ b/ODBEditor/ODBEditor.h @@ -0,0 +1,58 @@ + +#import + +// http://gusmueller.com/odb/ + +extern NSString * const ODBEditorCustomPathKey; + +@class TemporaryFileCachePreparer; +@class ExternalEditor; +@class NotationPrefs; +@class NoteObject; + +@interface ODBEditor : NSObject +{ + UInt32 _signature; + NSMutableDictionary *_filePathsBeingEdited; + + TemporaryFileCachePreparer *editingSpacePreparer; +} ++ (id)sharedODBEditor; + +- (void)abortEditingFile:(NSString *)path; +- (void)abortAllEditingSessionsForClient:(id)client; + +- (void)initializeDatabase:(NotationPrefs*)prefs; + +// NOTE that client is never retained - it is your reponsibility to +// make sure the client sticks around and abort editing for that client +// before it is dealloc'd +// +// Also note that while it is possible to start several editString +// sessions for a single client it is the client's responsibility to +// distinguish between the sessions (possibly using the original +// context that you supplied.) It is also the clients responsibility to +// do the same for file editing sessions, but this should be easier +// since the file path will remain static (except in the save as case) +// whereas the string returned is obviously going to change as the user +// edits it. + +- (BOOL)editFile:(NSString *)path inEditor:(ExternalEditor*)ed options:(NSDictionary *)options forClient:(id)client context:(NSDictionary *)context; +- (BOOL)editNote:(NoteObject*)aNote inEditor:(ExternalEditor*)ed context:(NSDictionary *)context; + +- (BOOL)editString:(NSString *)string inEditor:(ExternalEditor*)ed options:(NSDictionary *)options forClient:(id)client context:(NSDictionary *)context; + +@end + +@interface NSObject(ODBEditorClient) + +// see the ODB Editor documentation for when newFileLocation is sent +// if the file wasn't subject to a save as newpath will be nil + +-(void)odbEditor:(ODBEditor *)editor didModifyFile:(NSString *)path newFileLocation:(NSString *)newPath context:(NSDictionary *)context; +-(void)odbEditor:(ODBEditor *)editor didClosefile:(NSString *)path context:(NSDictionary *)context; + +-(void)odbEditor:(ODBEditor *)editor didModifyFileForString:(NSString *)newString context:(NSDictionary *)context; +-(void)odbEditor:(ODBEditor *)editor didCloseFileForString:(NSString *)newString context:(NSDictionary *)context; + +@end diff --git a/ODBEditor/ODBEditor.m b/ODBEditor/ODBEditor.m new file mode 100644 index 00000000..0feb97f2 --- /dev/null +++ b/ODBEditor/ODBEditor.m @@ -0,0 +1,398 @@ +// +// ODBEditor.m +// B-Quartic + +// http://gusmueller.com/odb/ + +/** + + Nov 30- Updates from Eric Blair: + removed entries from the _filesBeingEdited dictionary when the odb connection is closed. + added support for handling Save As messages and differentiate between editing a file and editing a string. + + Nov 30- Updates from Gus Mueller: + Added stringByResolvingSymlinksInPath around the file paths passed around, because it seems if you write to + /tmp/, sometimes you'll get back /private/tmp as a param + +*/ + + +#import "NSAppleEventDescriptor-Extensions.h" +#import "ODBEditor.h" +#import "ODBEditorSuite.h" +#import "NotationPrefs.h" +#import "TemporaryFileCachePreparer.h" +#import "ExternalEditorListController.h" +#import "NoteObject.h" +#import + +NSString * const ODBEditorCustomPathKey = @"ODBEditorCustomPath"; +NSString * const ODBEditorNonRetainedClient = @"ODBEditorNonRetainedClient"; +NSString * const ODBEditorClientContext = @"ODBEditorClientContext"; +NSString * const ODBEditorFileName = @"ODBEditorFileName"; +NSString * const ODBEditorIsEditingString = @"ODBEditorIsEditingString"; + +@interface ODBEditor(Private) + +- (BOOL)_launchExternalEditor:(ExternalEditor*)ed; +- (NSString*)_nonexistingTemporaryPathForFilename:(NSString*)filename; +- (NSString *)_tempFilePathForEditingString:(NSString *)string; +- (BOOL)_editFile:(NSString *)path inEditor:(ExternalEditor*)ed isEditingString:(BOOL)editingStringFlag options:(NSDictionary *)options forClient:(id)client context:(NSDictionary *)context; +- (void)handleModifiedFileEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent; +- (void)handleClosedFileEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent; + +@end + +@implementation ODBEditor + +static ODBEditor *_sharedODBEditor; + ++ (id)sharedODBEditor { + if (_sharedODBEditor == nil) { + _sharedODBEditor = [[ODBEditor alloc] init]; + } + return _sharedODBEditor; +} + +- (id)init { + self = [super init]; + if (self != nil) { + UInt32 packageType = 0; + UInt32 packageCreator = 0; + + if (_sharedODBEditor != nil) { + [self autorelease]; + [NSException raise: NSInternalInconsistencyException format: @"ODBEditor is a singleton - use [ODBEditor sharedODBEditor]"]; + return nil; + } + // our initialization + + CFBundleGetPackageInfo(CFBundleGetMainBundle(), &packageType, &packageCreator); + _signature = packageCreator; + + _filePathsBeingEdited = [[NSMutableDictionary alloc] init]; + + // setup our event handlers + + NSAppleEventManager *appleEventManager = [NSAppleEventManager sharedAppleEventManager]; + [appleEventManager setEventHandler: self andSelector: @selector(handleModifiedFileEvent:withReplyEvent:) forEventClass: kODBEditorSuite andEventID: kAEModifiedFile]; + [appleEventManager setEventHandler: self andSelector: @selector(handleClosedFileEvent:withReplyEvent:) forEventClass: kODBEditorSuite andEventID: kAEClosedFile]; + + } + + return self; +} + +- (void)dealloc { + NSAppleEventManager *appleEventManager = [NSAppleEventManager sharedAppleEventManager]; + [appleEventManager removeEventHandlerForEventClass: kODBEditorSuite andEventID: kAEModifiedFile]; + [appleEventManager removeEventHandlerForEventClass: kODBEditorSuite andEventID: kAEClosedFile]; + [_filePathsBeingEdited release]; + [editingSpacePreparer release]; + [super dealloc]; +} + +- (void)initializeDatabase:(NotationPrefs*)prefs { + if (editingSpacePreparer) { + [editingSpacePreparer setDelegate:nil]; + [editingSpacePreparer release]; + } + [(editingSpacePreparer = [[TemporaryFileCachePreparer alloc] init]) setDelegate:self]; + [editingSpacePreparer prepEditingSpaceIfNecessaryForNotationPrefs:prefs]; +} + +- (void)temporaryFileCachePreparerDidNotFinish:(TemporaryFileCachePreparer*)preparer { + NSLog(@"preparer failed"); +} +- (void)temporaryFileCachePreparerFinished:(TemporaryFileCachePreparer*)preparer { + NSLog(@"finished: '%@'", [preparer preparedCachePath]); +} + +- (void)abortEditingFile:(NSString *)path { + //#warning REVIEW if we created a temporary file for this session should we try to delete it and/or close it in the editor? + + if (path) { + if (nil == [_filePathsBeingEdited objectForKey: path]) + NSLog(@"ODBEditor: No active editing session for \"%@\"", path); + + [_filePathsBeingEdited removeObjectForKey: path]; + } else { + NSLog(@"abortEditingFile: path is nil"); + } +} + +- (void)abortAllEditingSessionsForClient:(id)client { + //#warning REVIEW if we created a temporary file for this session should we try to delete it and/or close it in the editor? + + if (![_filePathsBeingEdited count]) return; + + BOOL found = NO; + NSEnumerator *enumerator = [_filePathsBeingEdited objectEnumerator]; + NSMutableArray *keysToRemove = [NSMutableArray array]; + NSDictionary *dictionary = nil; + + while (nil != (dictionary = [enumerator nextObject])) { + id iterClient = [[dictionary objectForKey: ODBEditorNonRetainedClient] nonretainedObjectValue]; + + if (iterClient == client) { + found = YES; + [keysToRemove addObject:[dictionary objectForKey: ODBEditorFileName]]; + } + } + + [_filePathsBeingEdited removeObjectsForKeys: keysToRemove]; + + if (! found) { + //NSLog(@"ODBEditor: No active editing session for \"%@\" in '%@'", client, _filePathsBeingEdited); + } +} + +- (BOOL)editNote:(NoteObject*)aNote inEditor:(ExternalEditor*)ed context:(NSDictionary *)context { + if (!aNote) goto beepReturn; + + //see comments in -[TemporaryFileCachePreprer prepEditingSpaceIfNecessaryForNotationPrefs:] + + //let's first see if we can avoid this whole ODB protocol rigmarole altogether, and ideally even allow non-plain-text editors to be used + if ([ed canEditNoteDirectly:aNote]) { + NSString *path = [aNote noteFilePath]; + + [[NSWorkspace sharedWorkspace] openURLs:[NSArray arrayWithObject:[NSURL fileURLWithPath:path]] withAppBundleIdentifier:[ed bundleIdentifier] options:NSWorkspaceLaunchDefault additionalEventParamDescriptor:nil launchIdentifiers:NULL]; + return YES; + } + + //weren't able to edit the note-file directly, so fall back to opening a copy of it using an ODB editor + //what if this editor is not an ODB editor? what if the path doesn't exist? + + if (![editingSpacePreparer preparedCachePath]) { + NSLog(@"not editing '%@' because temporary cache path was not initialized", aNote); + goto beepReturn; + } + if (![ed isODBEditor]) { + NSLog(@"not editing '%@' with '%@' because it is not an ODB editor and the note-file cannot be saved directly", aNote, ed); + goto beepReturn; + } + + //now write aNote as text to path? + NSString *path = [self _nonexistingTemporaryPathForFilename:filenameOfNote(aNote)]; + NSError *error = nil; + if (![[[aNote contentString] string] writeToFile:path atomically:NO encoding:NSUTF8StringEncoding error:&error]) { + NSLog(@"not editing '%@' because it could not be written to '%@'", aNote, path); + goto beepReturn; + } + + return [self editFile:path inEditor:ed options:[NSDictionary dictionaryWithObject:titleOfNote(aNote) forKey:ODBEditorCustomPathKey] forClient:aNote context:context]; +beepReturn: + NSBeep(); + return NO; +} + +- (BOOL)editFile:(NSString *)path inEditor:(ExternalEditor*)ed options:(NSDictionary *)options forClient:(id)client context:(NSDictionary *)context { + return [self _editFile:path inEditor:ed isEditingString:NO options:options forClient:client context:context]; +} + +- (BOOL)editString:(NSString *)string inEditor:(ExternalEditor*)ed options:(NSDictionary *)options forClient:(id)client context:(NSDictionary *)context { + NSString *path = [self _tempFilePathForEditingString:string]; + + if (path != nil) { + return [self _editFile:path inEditor:ed isEditingString:YES options:options forClient:client context:context]; + } + + return NO; +} + + +@end + +@implementation ODBEditor(Private) + +- (BOOL)_launchExternalEditor:(ExternalEditor*)ed { + BOOL success = NO; + BOOL running = NO; + NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; + NSArray *runningApplications = [workspace launchedApplications]; + NSEnumerator *enumerator = [runningApplications objectEnumerator]; + NSDictionary *applicationInfo; + + NSString *editorBundleIdentifier = [ed bundleIdentifier]; + + while (nil != (applicationInfo = [enumerator nextObject])) { + NSString *bundleIdentifier = [applicationInfo objectForKey: @"NSApplicationBundleIdentifier"]; + + if ([bundleIdentifier isEqualToString: editorBundleIdentifier]) { + running = YES; + // bring the app forward + success = [workspace launchApplication: [applicationInfo objectForKey: @"NSApplicationPath"]]; + break; + } + } + + if (running == NO) { + success = [workspace launchAppWithBundleIdentifier: editorBundleIdentifier options:NSWorkspaceLaunchDefault additionalEventParamDescriptor: nil launchIdentifier:NULL]; + } + + return success; +} + +- (NSString*)_nonexistingTemporaryPathForFilename:(NSString*)filename { + unsigned int sTempFileSequence = 0; + NSString *path = nil; + NSString *basename = [filename stringByDeletingPathExtension]; + NSFileManager *fileManager = [NSFileManager defaultManager]; + + NSAssert([editingSpacePreparer preparedCachePath] != nil, @"cache path does not exist!"); + + do { + path = sTempFileSequence++ ? [NSString stringWithFormat: @"%@ %03d.txt", basename, sTempFileSequence] : [basename stringByAppendingPathExtension:@"txt"]; + path = [[editingSpacePreparer preparedCachePath] stringByAppendingPathComponent: path]; + } while ([fileManager fileExistsAtPath:path]); + + return path; +} + + +- (NSString *)_tempFilePathForEditingString:(NSString *)string { + NSString *path = [self _nonexistingTemporaryPathForFilename:@"Untitled Text"]; + + NSError *error = nil; + if (NO == [string writeToFile:path atomically:NO encoding:NSUTF8StringEncoding error:&error]) { + NSLog([error description], nil); + path = nil; + } + + return path; +} + +- (BOOL)_editFile:(NSString *)path inEditor:(ExternalEditor*)ed isEditingString:(BOOL)editingStringFlag options:(NSDictionary *)options forClient:(id)client context:(NSDictionary *)context { + // 10.2 fix- akm Nov 30 2004 + path = [path stringByResolvingSymlinksInPath]; + + BOOL success = NO; + OSStatus status = noErr; + if (!ed) ed = [[ExternalEditorListController sharedInstance] defaultExternalEditor]; + NSData *targetBundleID = [[ed bundleIdentifier] dataUsingEncoding: NSUTF8StringEncoding]; + NSAppleEventDescriptor *targetDescriptor = [NSAppleEventDescriptor descriptorWithDescriptorType: typeApplicationBundleID data: targetBundleID]; + NSAppleEventDescriptor *appleEvent = [NSAppleEventDescriptor appleEventWithEventClass: kCoreEventClass + eventID: kAEOpenDocuments + targetDescriptor: targetDescriptor + returnID: kAutoGenerateReturnID + transactionID: kAnyTransactionID]; + NSAppleEventDescriptor *replyDescriptor = nil; + NSAppleEventDescriptor *errorDescriptor = nil; + AEDesc reply = {typeNull, NULL}; + NSString *customPath = [options objectForKey: ODBEditorCustomPathKey]; + + [self _launchExternalEditor:ed]; + + [appleEvent setParamDescriptor: [NSAppleEventDescriptor descriptorWithFilePath: path] forKeyword: keyDirectObject]; + [appleEvent setParamDescriptor: [NSAppleEventDescriptor descriptorWithTypeCode: _signature] forKeyword: keyFileSender]; + if (customPath != nil) + [appleEvent setParamDescriptor: [NSAppleEventDescriptor descriptorWithString: customPath] forKeyword: keyFileCustomPath]; + + AESendMessage([appleEvent aeDesc], &reply, kAEWaitReply, kAEDefaultTimeout); + + if (status == noErr) { + replyDescriptor = [[[NSAppleEventDescriptor alloc] initWithAEDescNoCopy: &reply] autorelease]; + errorDescriptor = [replyDescriptor paramDescriptorForKeyword: keyErrorNumber]; + + if (errorDescriptor != nil) { + status = [errorDescriptor int32Value]; + } + + if (status == noErr) { + // save off some information that we'll need when we get called back + + NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; + + [dictionary setObject: [NSValue valueWithNonretainedObject: client] forKey: ODBEditorNonRetainedClient]; + if (context != NULL) + [dictionary setObject: context forKey: ODBEditorClientContext]; + [dictionary setObject: path forKey: ODBEditorFileName]; + [dictionary setObject: [NSNumber numberWithBool: editingStringFlag] forKey: ODBEditorIsEditingString]; + + [_filePathsBeingEdited setObject: dictionary forKey: path]; + } + } + + success = (status == noErr); + + return success; +} + +- (void)handleModifiedFileEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent { + NSAppleEventDescriptor *fpDescriptor = [[event paramDescriptorForKeyword: keyDirectObject] coerceToDescriptorType: typeFileURL]; + NSString *urlString = [[[NSString alloc] initWithData: [fpDescriptor data] encoding: NSUTF8StringEncoding] autorelease]; + NSString *path = [[[NSURL URLWithString: urlString] path] stringByResolvingSymlinksInPath]; + NSAppleEventDescriptor *nfpDescription = [[event paramDescriptorForKeyword: keyNewLocation] coerceToDescriptorType: typeFileURL]; + NSString *newUrlString = [[[NSString alloc] initWithData: [nfpDescription data] encoding: NSUTF8StringEncoding] autorelease]; + NSString *newPath = [[NSURL URLWithString: newUrlString] path]; + NSDictionary *dictionary = nil; + NSError *error = nil; + + dictionary = [_filePathsBeingEdited objectForKey: path]; + + if (dictionary != nil) + { + id client = [[dictionary objectForKey: ODBEditorNonRetainedClient] nonretainedObjectValue]; + id isString = [dictionary objectForKey: ODBEditorIsEditingString]; + NSDictionary *context = [dictionary objectForKey: ODBEditorClientContext]; + + if([isString boolValue]) { + NSString *stringContents = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error]; + if (stringContents) { + [client odbEditor: self didModifyFileForString: stringContents context: context]; + } else { + NSLog([error description], nil); + } + } else { + [client odbEditor:self didModifyFile:path newFileLocation:newPath context:context]; + } + + // if we've received a Save As message, remove the file from the list of edited files + // This may be break compatibility with BBEdit versioner < 6.0, since these versions + // continue to send notifications after after doing a Save As... + if(newPath) { + [_filePathsBeingEdited removeObjectForKey: newPath]; + } + + } + else + { + NSLog(@"Got ODB editor event for unknown file '%@'", path); + } +} + +- (void)handleClosedFileEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent { + NSAppleEventDescriptor *descriptor = [[event paramDescriptorForKeyword: keyDirectObject] coerceToDescriptorType: typeFileURL]; + NSString *urlString = [[[NSString alloc] initWithData: [descriptor data] encoding: NSUTF8StringEncoding] autorelease]; + NSString *fileName = [[[NSURL URLWithString: urlString] path] stringByResolvingSymlinksInPath]; + NSDictionary *dictionary = nil; + NSError *error = nil; + + dictionary = [_filePathsBeingEdited objectForKey: fileName]; + + if (dictionary != nil) { + id client = [[dictionary objectForKey: ODBEditorNonRetainedClient] nonretainedObjectValue]; + id isString = [dictionary objectForKey: ODBEditorIsEditingString]; + NSDictionary *context = [dictionary objectForKey: ODBEditorClientContext]; + + if([isString boolValue]) { + NSString *stringContents = [NSString stringWithContentsOfURL:[NSURL fileURLWithPath:fileName] encoding:NSUTF8StringEncoding error:&error]; + if (stringContents) { + [client odbEditor: self didCloseFileForString: stringContents context: context]; + } else { + NSLog([error description], nil); + } + } else { + [client odbEditor:self didClosefile:fileName context:context]; + } + } + else + { + NSLog(@"Got ODB editor event for unknown file '%@'", fileName); + } + if (fileName) + [_filePathsBeingEdited removeObjectForKey: fileName]; +} + +@end + diff --git a/ODBEditor/ODBEditorSuite.h b/ODBEditor/ODBEditorSuite.h new file mode 100755 index 00000000..13c04d4a --- /dev/null +++ b/ODBEditor/ODBEditorSuite.h @@ -0,0 +1,33 @@ +// +// ODB Editor Suite constants +// +// +// Copyright �2000, Bare Bones Software, Inc. +// + +// For full information and documentation, see +// + +// optional paramters to 'aevt'/'odoc' +#define keyFileSender (UTGetOSTypeFromString(CFSTR("FSnd"))) +#define keyFileSenderToken (UTGetOSTypeFromString(CFSTR("FTok"))) +#define keyFileCustomPath (UTGetOSTypeFromString(CFSTR("Burl"))) + +// suite code for ODB editor suite events +// +// WARNING: although the suite code is coincidentally the same +// as BBEdit's application signature, you must not change this, +// or else you'll break the suite. If you do that, ninjas will +// come to your house and kick your ass. +// + +#define kODBEditorSuite (UTGetOSTypeFromString(CFSTR("R*ch"))) + +// ODB editor suite events, sent by the editor to the server. + +#define kAEModifiedFile (UTGetOSTypeFromString(CFSTR("FMod"))) +#define keyNewLocation (UTGetOSTypeFromString(CFSTR("New?"))) +#define kAEClosedFile (UTGetOSTypeFromString(CFSTR("FCls"))) + +// optional paramter to kAEModifiedFile/kAEClosedFile +#define keySenderToken (UTGetOSTypeFromString(CFSTR("Tokn"))) diff --git a/PrefsWindowController.h b/PrefsWindowController.h index 00a7b3c0..ab057ea5 100755 --- a/PrefsWindowController.h +++ b/PrefsWindowController.h @@ -26,6 +26,7 @@ IBOutlet NSTextField *bodyTextFontField; IBOutlet NSMatrix *tabKeyRadioMatrix; IBOutlet NSPopUpButton *tableTextMenuButton; + IBOutlet NSPopUpButton *externalEditorMenuButton; IBOutlet NSTextField *tableTextSizeField; IBOutlet NSTextField *appShortcutField; IBOutlet NSButton *completeNoteTitlesButton; @@ -77,6 +78,7 @@ - (IBAction)changedSpellChecking:(id)sender; - (IBAction)changedTabBehavior:(id)sender; - (IBAction)changedTableText:(id)sender; +- (IBAction)changedExternalEditorsMenu:(id)sender; - (IBAction)changedTitleCompletion:(id)sender; - (IBAction)changedSoftTabs:(id)sender; - (IBAction)changedUseMarkdownImport:(id)sender; @@ -84,6 +86,8 @@ - (IBAction)changedShowGrid:(id)sender; - (IBAction)changedAltRows:(id)sender; +- (void)_selectDefaultExternalEditor; + - (NSMenu*)directorySelectionMenu; - (void)changeDefaultDirectory; - (BOOL)getNewNotesRefFromOpenPanel:(FSRef*)notesDirectoryRef returnedPath:(NSString**)path; @@ -98,6 +102,5 @@ - (IBAction)setMaxWidth:(id)sender; - (void)relaunchNV:(id)sender; - (void)reActivate:(id)sender; -- (void)updateAppList:(id)sender; @end diff --git a/PrefsWindowController.m b/PrefsWindowController.m index 60a41b9a..e2c45044 100755 --- a/PrefsWindowController.m +++ b/PrefsWindowController.m @@ -14,6 +14,7 @@ #import "PTKeyComboPanel.h" #import "PTKeyCombo.h" #import "NotationPrefsViewController.h" +#import "ExternalEditorListController.h" #import "NSData_transformations.h" #import "NSString_NV.h" #import "NSFileManager_NV.h" @@ -49,26 +50,6 @@ - (void)showWindow:(id)sender { if (![window isVisible]) [window center]; - NSArray *appArray = [[NSApp delegate] getTxtAppList]; - // NSArray *appArray = nil; - if (appArray) { - if ((![appList numberOfItems]>0)||(![[appList objectValues] isEqualToArray:appArray])) { - [appList removeAllItems]; - [appList addItemsWithObjectValues:appArray]; - NSString *defApp = [prefsController textEditor]; - if ((![appArray containsObject:defApp])&&(![defApp isEqualToString:@"Default"])) { - defApp = @"Default"; - [prefsController setTextEditor:@"Default"]; - } - if ([defApp isEqualToString:@"Default"]) { - [appList selectItemAtIndex:0]; - }else{ - [appList selectItemWithObjectValue:defApp]; - } - } - } - - [window makeKeyAndOrderFront:self]; } @@ -207,6 +188,19 @@ - (IBAction)changedTabBehavior:(id)sender { [prefsController setTabIndenting:[[tabKeyRadioMatrix cellAtRow:0 column:0] state] sender:self]; } +- (IBAction)changedExternalEditorsMenu:(id)sender { + //not currently called as an action in practice + [self _selectDefaultExternalEditor]; +} + +- (void)_selectDefaultExternalEditor { + ExternalEditor *ed = [[ExternalEditorListController sharedInstance] defaultExternalEditor]; + NSInteger idx = ed ? [externalEditorMenuButton indexOfItemWithRepresentedObject:ed] : 0; + if (idx > -1) { + [externalEditorMenuButton selectItemAtIndex:idx]; + } +} + - (IBAction)changedTableText:(id)sender { if (sender == tableTextMenuButton) { if ([tableTextSizeField selectedTag] != 3) [tableTextSizeField setFloatValue:[prefsController tableFontSize]]; @@ -405,6 +399,11 @@ - (void)awakeFromNib { [tableTextSizeField setFloatValue:fontSize]; [tableTextSizeField setHidden:(fontButtonIndex != 3)]; + [externalEditorMenuButton setMenu:[[ExternalEditorListController sharedInstance] addEditorPrefsMenu]]; + [self _selectDefaultExternalEditor]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changedExternalEditorsMenu:) + name:ExternalEditorsChangedNotification object:nil]; + [completeNoteTitlesButton setState:[prefsController autoCompleteSearches]]; [checkSpellingButton setState:[prefsController checkSpellingAsYouType]]; [confirmDeletionButton setState:[prefsController confirmNoteDeletion]]; @@ -601,17 +600,6 @@ - (IBAction)toggleKeepsTextWidthInWindow:(id)sender{ [[NSApp delegate] setMaxNoteBodyWidth]; } -- (void)updateAppList:(id)sender{ - NSArray *appArr = [[NSApp delegate] getTxtAppList]; - NSString *defApp = [appList objectValueOfSelectedItem]; - if ((![[appList objectValues] isEqualToArray:appArr])||(![appArr containsObject:defApp])) { - [appList removeAllItems]; - [appList addItemsWithObjectValues:appArr]; - [prefsController setTextEditor:@"Default"]; - [appList selectItemAtIndex:0]; - } -} - - (IBAction)setMaxWidth:(id)sender{ double dbWidth = [maxWidthSlider doubleValue]; dbWidth = dbWidth - fmod(dbWidth,2.0); @@ -639,19 +627,4 @@ - (IBAction)changedShowGrid:(id)sender { [[NSApp delegate] refreshNotesList]; } -//delegate methods for text editor application combobox -- (void)comboBoxSelectionDidChange:(NSNotification *)notification{ - NSString *defApp = [appList objectValueOfSelectedItem]; - if (defApp) { - if ([defApp hasPrefix:@"Default"]) { - defApp = @"Default"; - } - [prefsController setTextEditor:defApp]; - } -} - -- (void)comboBoxWillPopUp:(NSNotification *)notification{ - [self updateAppList:self]; -} - @end diff --git a/PreviewController.m b/PreviewController.m index 85e1cf91..29cd8bee 100755 --- a/PreviewController.m +++ b/PreviewController.m @@ -400,6 +400,7 @@ -(IBAction)makePreviewSticky:(id)sender { self.isPreviewSticky = YES; [[preview window] setTitle:@"Locked"]; + [stickyPreviewButton setState:YES]; [stickyPreviewButton setToolTip:@"Return the preview to normal functionality."]; [stickyPreviewButton setAction:@selector(makePreviewNotSticky:)]; [shareButton setEnabled:NO]; @@ -411,6 +412,7 @@ -(IBAction)makePreviewNotSticky:(id)sender { self.isPreviewSticky = NO; [[preview window] setTitle:@"Preview"]; + [stickyPreviewButton setState:NO]; [stickyPreviewButton setToolTip:@"Maintain current note in Preview, even if you switch to other notes."]; [stickyPreviewButton setAction:@selector(makePreviewSticky:)]; [shareButton setEnabled:YES]; diff --git a/TemporaryFileCachePreparer.h b/TemporaryFileCachePreparer.h new file mode 100755 index 00000000..423b241e --- /dev/null +++ b/TemporaryFileCachePreparer.h @@ -0,0 +1,60 @@ +// +// TemporaryFileCachePreparer.h +// Notation +// + +/*Copyright (c) 2010, Zachary Schneirov. All rights reserved. + This file is part of Notational Velocity. + + Notational Velocity 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 3 of the License, or + (at your option) any later version. + + Notational Velocity 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 Notational Velocity. If not, see . */ + + +#import + +@class NotationPrefs; + +@interface TemporaryFileCachePreparer : NSObject { + NSString *cachePath; + + id delegate; + + NotationPrefs *notationPrefs; + BOOL startedPreparing; + NSTask *mountTask, *newfsTask, *attachTask; + NSString *deviceName, *preparedCachePath; +} + +- (void)prepEditingSpaceIfNecessaryForNotationPrefs:(NotationPrefs*)prefs; +- (void)_attachRAMDiskOfCapacity:(NSUInteger)numberOfMegabytes; +- (void)_buildHFSFileSystemOnDevice:(NSString*)aDeviceName; +- (void)_mountHFSFileSystemOnDevice:(NSString*)aDeviceName; + +- (BOOL)_createFolderAtPath:(NSString*)path; + +- (BOOL)isPreparing; +- (void)_finishPreparationWithPath:(NSString*)aPath; +- (void)_stopPreparation; +- (NSString*)preparedCachePath; +- (void)setDelegate:(id)aDelegate; +- (id)delegate; + +@end + + +@interface NSObject (TemporaryFileCachePreparerDelegate) + +- (void)temporaryFileCachePreparerDidNotFinish:(TemporaryFileCachePreparer*)preparer; +- (void)temporaryFileCachePreparerFinished:(TemporaryFileCachePreparer*)preparer; + +@end diff --git a/TemporaryFileCachePreparer.m b/TemporaryFileCachePreparer.m new file mode 100755 index 00000000..3c431ebf --- /dev/null +++ b/TemporaryFileCachePreparer.m @@ -0,0 +1,278 @@ +// +// TemporaryFileCachePreparer.m +// Notation +// + +/*Copyright (c) 2010, Zachary Schneirov. All rights reserved. + This file is part of Notational Velocity. + + Notational Velocity 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 3 of the License, or + (at your option) any later version. + + Notational Velocity 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 Notational Velocity. If not, see . */ + + +#import "TemporaryFileCachePreparer.h" +#import "NotationPrefs.h" +#include + +//used to mount a RAM disk for temporary file editing +//instances of this class are probably not useful for more than one preparation +//(which probably wouldn't be necessary anyway as RAM disks can't be unmounted) + +@implementation TemporaryFileCachePreparer + +static BOOL MountPointExists(const char *expectedMountPath); +static NSString *RAMDiskMountPath(); +static NSString *TempDirectoryPathForEditing(); + +- (id)init { + if ([super init]) { + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskTerminated:) + name:NSTaskDidTerminateNotification object:nil]; + } + + return self; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [super dealloc]; +} + +static BOOL MountPointExists(const char *expectedMountPath) { + struct statfs *buf; + + int i, numMounts = getmntinfo(&buf, MNT_NOWAIT); + if (!numMounts) return NO; + + char absExpectedMountPath[PATH_MAX]; + if (!realpath(expectedMountPath, absExpectedMountPath)) { + NSLog(@"error getting realpath from path '%s': %d", expectedMountPath, errno); + return NO; + } + + for (i=0; i 0 && numberOfMegabytes < 100, @"unreasonable capacity requested"); + + [self retain]; + [(attachTask = [NSTask new]) setLaunchPath:@"/usr/bin/hdiutil"]; + [attachTask setArguments:[NSArray arrayWithObjects:@"attach", @"-nomount", @"-nobrowse", [NSString stringWithFormat:@"ram://%u", (2 * 1024 * numberOfMegabytes)], nil]]; + [attachTask setStandardOutput:[NSPipe pipe]]; + [attachTask launch]; +} + +- (void)_buildHFSFileSystemOnDevice:(NSString*)aDeviceName { + NSAssert(newfsTask == nil, @"newfsTask was already used!"); + NSAssert(aDeviceName != nil, @"no device name passed"); + + [self retain]; + [(newfsTask = [NSTask new]) setLaunchPath:@"/sbin/newfs_hfs"]; + [newfsTask setArguments:[NSArray arrayWithObjects:@"-v", [RAMDiskMountPath() lastPathComponent], aDeviceName, nil]]; + [newfsTask launch]; +} + +- (void)_mountHFSFileSystemOnDevice:(NSString*)aDeviceName { + NSAssert(mountTask == nil, @"mountTask was already used!"); + NSAssert(aDeviceName != nil, @"no device name passed"); + + [self retain]; + [(mountTask = [NSTask new]) setLaunchPath:@"/sbin/mount"]; + [mountTask setArguments:[NSArray arrayWithObjects:@"-t", @"hfs", @"-o", @"nobrowse", aDeviceName, RAMDiskMountPath(), nil]]; + [mountTask launch]; +} + +- (BOOL)isPreparing { + return startedPreparing; +} + +- (NSString*)preparedCachePath { + return preparedCachePath; +} + +- (void)_finishPreparationWithPath:(NSString*)aPath { + //funnel for the success delegate method + + NSAssert(preparedCachePath == nil, @"preparedCachePath already set?"); + preparedCachePath = [aPath retain]; + + startedPreparing = NO; + + [delegate temporaryFileCachePreparerFinished:self]; +} + +- (void)_stopPreparation { + + startedPreparing = NO; + + [delegate temporaryFileCachePreparerDidNotFinish:self]; +} + +- (void)setDelegate:(id)aDelegate { + if (aDelegate) { + NSAssert([aDelegate respondsToSelector:@selector(temporaryFileCachePreparerDidNotFinish:)], @"delegate is bad (1)"); + NSAssert([aDelegate respondsToSelector:@selector(temporaryFileCachePreparerFinished:)], @"delegate is bad (2)"); + } + delegate = aDelegate; +} +- (id)delegate { + return delegate; +} + +- (BOOL)_createFolderAtPath:(NSString*)path { + NSError *err = nil; + NSFileManager *fileMan = [NSFileManager defaultManager]; + BOOL isDirectory = NO, didCreate = ([fileMan fileExistsAtPath:path isDirectory:&isDirectory] && isDirectory) ? YES : +#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 + [fileMan createDirectoryAtPath:path withIntermediateDirectories:NO attributes: + [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:0700] forKey:NSFilePosixPermissions] error:&err]; +#else + [fileMan createDirectoryAtPath:path attributes: + [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:0700] forKey:NSFilePosixPermissions]]; +#endif + if (!didCreate) NSLog(@"couldn't create directory '%@': %@", path, (err ? [err localizedDescription] : @"(unknown error)")); + return didCreate; +} + +- (void)taskTerminated:(NSNotification *)aNotification { + + if (startedPreparing) { + NSTask *task = [aNotification object]; + + if (task == attachTask || task == newfsTask || task == mountTask) { + //each launched task retains self, so as long as each task triggers this method, then each retain should be balanced with an autorelease + [self autorelease]; + + if ([task terminationStatus]) { + //assume an exit status of 0 means success + [self _stopPreparation]; + return; + } + } + + if (task == attachTask) { + //read deviceName and store in ivar + //start newfs task + + NSData *outData = [[[attachTask standardOutput] fileHandleForReading] readDataToEndOfFile]; + if (outData) { + NSString *outString = [[[NSString alloc] initWithData:outData encoding:NSUTF8StringEncoding] autorelease]; + + [[NSScanner scannerWithString:outString] scanUpToCharactersFromSet: + [NSCharacterSet whitespaceAndNewlineCharacterSet] intoString:&deviceName]; + if (!deviceName) deviceName = [outString retain]; + } + if (![deviceName length]) { + NSLog(@"couldn't get device name from hdiutil attach"); + [self _stopPreparation]; + } else { +// NSLog(@"device name is '%@'", deviceName); + [self _buildHFSFileSystemOnDevice:deviceName]; + } + } + + if (task == newfsTask) { + //make directory and set permissions + if (![self _createFolderAtPath:RAMDiskMountPath()]) { + [self _stopPreparation]; + } else { + //start mount task + [self _mountHFSFileSystemOnDevice:deviceName]; + } + } + + if (task == mountTask) { + //return newly initialized path to delegate, after verifying + + NSString *path = RAMDiskMountPath(); + if (MountPointExists([path fileSystemRepresentation])) { + [self _finishPreparationWithPath:path]; + } else { + NSLog(@"the RAM disk somehow does not exist!"); + [self _stopPreparation]; + } + } + + } else { + //don't bother doing anything unless we're actually expecting one of this instance's tasks to complete + } +} + + + +@end diff --git a/UnifiedCell.h b/UnifiedCell.h index 4a1fa454..b5133142 100644 --- a/UnifiedCell.h +++ b/UnifiedCell.h @@ -1,13 +1,18 @@ /*Copyright (c) 2010, Zachary Schneirov. All rights reserved. - Redistribution and use in source and binary forms, with or without modification, are permitted - provided that the following conditions are met: - - Redistributions of source code must retain the above copyright notice, this list of conditions - and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, this list of - conditions and the following disclaimer in the documentation and/or other materials provided with - the distribution. - - Neither the name of Notational Velocity nor the names of its contributors may be used to endorse - or promote products derived from this software without specific prior written permission. */ + This file is part of Notational Velocity. + + Notational Velocity 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 3 of the License, or + (at your option) any later version. + + Notational Velocity 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 Notational Velocity. If not, see . */ #import @@ -22,7 +27,7 @@ NSAttributedString *AttributedStringForSelection(NSAttributedString *str, BOOL withShadow); - (NSRect)nv_titleRectForFrame:(NSRect)aFrame; -- (NSRect)nv_tagsRectForFrame:(NSRect)frame andImage:(NSImage*)img; +- (NSRect)nv_tagsRectForFrame:(NSRect)frame; - (float)tableFontFrameHeight; diff --git a/UnifiedCell.m b/UnifiedCell.m index 92295b7b..eb0364ce 100644 --- a/UnifiedCell.m +++ b/UnifiedCell.m @@ -1,13 +1,18 @@ /*Copyright (c) 2010, Zachary Schneirov. All rights reserved. - Redistribution and use in source and binary forms, with or without modification, are permitted - provided that the following conditions are met: - - Redistributions of source code must retain the above copyright notice, this list of conditions - and the following disclaimer. - - Redistributions in binary form must reproduce the above copyright notice, this list of - conditions and the following disclaimer in the documentation and/or other materials provided with - the distribution. - - Neither the name of Notational Velocity nor the names of its contributors may be used to endorse - or promote products derived from this software without specific prior written permission. */ + This file is part of Notational Velocity. + + Notational Velocity 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 3 of the License, or + (at your option) any later version. + + Notational Velocity 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 Notational Velocity. If not, see . */ #import "UnifiedCell.h" @@ -40,8 +45,8 @@ - (void)dealloc { //changes will hereafter affect all field editors for the window; do not want - (NSText *)setUpFieldEditorAttributes:(NSText *)textObj { NSTextView *tv = (NSTextView *)[super setUpFieldEditorAttributes:textObj]; - //NSLog(@"okfield1s"); - [tv setTextContainerInset:NSMakeSize(-9,-9)]; + + [tv setTextContainerInset:NSMakeSize(-2,-2)]; NSTextContainer *tc = [tv textContainer]; [tc setContainerSize:NSMakeSize(1.0e7, 1.0e7)]; @@ -60,10 +65,10 @@ - (NSText *)setUpFieldEditorAttributes:(NSText *)textObj { - (void)selectWithFrame:(NSRect)aRect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)anObject start:(NSInteger)selStart length:(NSInteger)selLength { - NSRect rect = [(NotesTableView*)controlView lastEventActivatedTagEdit] ? [self nv_tagsRectForFrame:aRect andImage:nil] : [self nv_titleRectForFrame:aRect]; + NSRect rect = [(NotesTableView*)controlView lastEventActivatedTagEdit] ? [self nv_tagsRectForFrame:aRect] : [self nv_titleRectForFrame:aRect]; [super selectWithFrame:rect inView:controlView editor:textObj delegate:anObject start:selStart length:selLength]; - [controlView setKeyboardFocusRingNeedsDisplayInRect:NSInsetRect([self nv_tagsRectForFrame:aRect andImage:nil], -3, -3)]; + [controlView setKeyboardFocusRingNeedsDisplayInRect:NSInsetRect([self nv_tagsRectForFrame:aRect], -3, -3)]; } - (NSFocusRingType)focusRingType { @@ -81,16 +86,11 @@ - (NSRect)nv_titleRectForFrame:(NSRect)aFrame { return NSMakeRect(aFrame.origin.x, aFrame.origin.y, aFrame.size.width, [self tableFontFrameHeight]); } -- (NSRect)nv_tagsRectForFrame:(NSRect)frame andImage:(NSImage*)img { +- (NSRect)nv_tagsRectForFrame:(NSRect)frame { //if no tags, return a default small frame to allow adding them float fontHeight = [self tableFontFrameHeight]; - NSSize size = img ? [img size] : NSMakeSize(NSWidth(frame), fontHeight); - - NSPoint pos = NSMakePoint((previewIsHidden ? NSMinX(frame) + 3.0 : NSMaxX(frame) - size.width), - (previewIsHidden ? NSMinY(frame) + fontHeight + size.height : NSMaxY(frame) - 3.0)); - if (!img) { - pos.y -= fontHeight; - } + NSSize size = NSMakeSize(NSWidth(frame), fontHeight); + NSPoint pos = NSMakePoint(NSMinX(frame) + 3.0, (previewIsHidden ? NSMinY(frame) + fontHeight + size.height + 2.0 : NSMaxY(frame) - 2.0) - fontHeight); return (NSRect){pos, size}; } @@ -171,12 +171,9 @@ - (NSMutableDictionary*)baseTextAttributes { } - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView { + NotesTableView *tv = (NotesTableView *)controlView; - if ([[[tv enclosingScrollView] verticalScroller] isHidden]) { - cellFrame.size.width +=1.5f; - }else{ - cellFrame.size.width-=8.25f; - } + [super drawWithFrame:cellFrame inView:controlView]; //draw note date and tags @@ -187,7 +184,7 @@ - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView { NSColor *textColor = ([self isHighlighted] && isActive) ? [NSColor whiteColor] : (![self isHighlighted] ? [[self class] dateColorForTint]/*[NSColor grayColor]*/ : nil); if (textColor) [baseAttrs setObject:textColor forKey:NSForegroundColorAttributeName]; - if (IsSnowLeopardOrLater && [self isHighlighted]) { + if (IsSnowLeopardOrLater && [self isHighlighted] && ([tv selectionHighlightStyle] == NSTableViewSelectionHighlightStyleSourceList)) { [baseAttrs setObject:ShadowForSnowLeopard() forKey:NSShadowAttributeName]; } @@ -209,21 +206,16 @@ - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView { [dateStr drawInRect:NSMakeRect(NSMaxX(cellFrame) - 70.0, NSMinY(cellFrame), 70.0, fontHeight) withAttributes:baseAttrs]; } - if (ColumnIsSet(NoteLabelsColumn, columnsBitmap)) { - NSImage *img = ([self isHighlighted] && isActive) ? [noteObject highlightedLabelsPreviewImage] : [noteObject labelsPreviewImage]; + if (ColumnIsSet(NoteLabelsColumn, columnsBitmap) && [labelsOfNote(noteObject) length]) { + NSRect rect = [self nv_tagsRectForFrame:cellFrame]; + rect.origin.y += fontHeight; + rect = [controlView centerScanRect:rect]; - if (img) { - NSRect rect = [self nv_tagsRectForFrame:cellFrame andImage:img]; - rect = [controlView centerScanRect:rect]; - - //clip the tags image within the bounds of the cell so that narrow columns look nicer - [NSGraphicsContext saveGraphicsState]; - NSRectClip(cellFrame); - - [img compositeToPoint:rect.origin operation:NSCompositeSourceOver]; - - [NSGraphicsContext restoreGraphicsState]; - } + //clip the tags image within the bounds of the cell so that narrow columns look nicer + [NSGraphicsContext saveGraphicsState]; + NSRectClip(cellFrame); + [noteObject drawLabelBlocksInRect:rect rightAlign:!previewIsHidden highlighted:([self isHighlighted] && isActive)]; + [NSGraphicsContext restoreGraphicsState]; } if ([tv currentEditor] && [self isHighlighted]) { @@ -237,16 +229,14 @@ - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView { [cloneStr release]; //draw a slightly different focus ring than what would have been drawn - NSRect rect = [tv lastEventActivatedTagEdit] ? [self nv_tagsRectForFrame:cellFrame andImage:nil] : [self nv_titleRectForFrame:cellFrame]; - rect = NSInsetRect(rect, -2, -1); - + NSRect rect = [tv lastEventActivatedTagEdit] ? [self nv_tagsRectForFrame:cellFrame] : [self nv_titleRectForFrame:cellFrame]; [NSGraphicsContext saveGraphicsState]; - NSBezierPath *path = [NSBezierPath bezierPathWithRect:rect]; + NSBezierPath *path = [NSBezierPath bezierPathWithRect:NSInsetRect(rect, -2, -1)]; //megafocusring casts a shadow both outside and inside if ([tv lastEventActivatedTagEdit]) { - NSSetFocusRingStyle(NSFocusRingBelow); - [path fill]; + NSSetFocusRingStyle(NSFocusRingBelow); + [path fill]; } NSSetFocusRingStyle(NSFocusRingOnly); [path fill]; diff --git a/de.lproj/MainMenu.xib b/de.lproj/MainMenu.xib index 6714309b..2464cb1a 100644 --- a/de.lproj/MainMenu.xib +++ b/de.lproj/MainMenu.xib @@ -340,6 +340,15 @@ + + + Edit With + + 2147483647 + + + 88 + YES @@ -1134,6 +1143,24 @@ + + + Lock Note to Preview + L + 1572864 + 2147483647 + + + + + + Print Preview / PDF + P + 1572864 + 2147483647 + + + Save Preview HTML @@ -3118,6 +3145,46 @@ 1297 + + + lockPreview: + + + + 1301 + + + + printPreview: + + + + 1302 + + + + printPreviewItem + + + + 1303 + + + + lockNoteItem + + + + 1304 + + + + savePreviewItem + + + + 1305 + @@ -3337,6 +3404,7 @@ + @@ -4357,6 +4425,8 @@ + + @@ -4430,6 +4500,21 @@ + + 1298 + + + + + 1299 + + + + + 1300 + + + @@ -4619,9 +4704,12 @@ 1292.IBPluginDependency 1293.IBPluginDependency 1294.IBPluginDependency + 1298.IBPluginDependency + 1299.IBPluginDependency 130.IBEditorWindowLastContentRect 130.IBPluginDependency 130.ImportedFromIB2 + 1300.IBPluginDependency 131.IBPluginDependency 131.ImportedFromIB2 134.IBPluginDependency @@ -5003,10 +5091,13 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin {{366, 565}, {64, 6}} com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -5209,7 +5300,7 @@ - 1297 + 1305 @@ -5224,12 +5315,15 @@ bringFocusToControlField: copyNoteLink: deleteNote: + editNoteExternally: exportNote: fieldAction: importNotes: + lockPreview: multiTag: openFileInEditor: printNote: + printPreview: renameNote: revealNote: savePreview: @@ -5280,6 +5374,9 @@ id id id + id + id + id @@ -5289,12 +5386,15 @@ bringFocusToControlField: copyNoteLink: deleteNote: + editNoteExternally: exportNote: fieldAction: importNotes: + lockPreview: multiTag: openFileInEditor: printNote: + printPreview: renameNote: revealNote: savePreview: @@ -5329,6 +5429,10 @@ deleteNote: id + + editNoteExternally: + id + exportNote: id @@ -5341,6 +5445,10 @@ importNotes: id + + lockPreview: + id + multiTag: id @@ -5353,6 +5461,10 @@ printNote: id + + printPreview: + id + renameNote: id @@ -5437,11 +5549,14 @@ YES editorStatusView field + lockNoteItem mainView multiMarkdownPreview notesScrollView notesTableView previewToggler + printPreviewItem + savePreviewItem sparkleUpdateItem statBarMenu syncWaitPanel @@ -5456,12 +5571,15 @@ YES EmptyView DualField + NSMenuItem ETContentView NSMenuItem AugmentedScrollView NotesTableView NSMenuItem NSMenuItem + NSMenuItem + NSMenuItem NSMenu NSPanel NSProgressIndicator @@ -5478,11 +5596,14 @@ YES editorStatusView field + lockNoteItem mainView multiMarkdownPreview notesScrollView notesTableView previewToggler + printPreviewItem + savePreviewItem sparkleUpdateItem statBarMenu syncWaitPanel @@ -5503,6 +5624,10 @@ field DualField + + lockNoteItem + NSMenuItem + mainView ETContentView @@ -5523,6 +5648,14 @@ previewToggler NSMenuItem + + printPreviewItem + NSMenuItem + + + savePreviewItem + NSMenuItem + sparkleUpdateItem NSMenuItem diff --git a/de.lproj/Preferences.xib b/de.lproj/Preferences.xib index 26cbaa7b..895cf2df 100644 --- a/de.lproj/Preferences.xib +++ b/de.lproj/Preferences.xib @@ -12,26 +12,24 @@ YES - NSColorWell - NSMenu - NSSliderCell + NSUserDefaultsController + NSPopUpButton NSButton - NSCustomObject - NSSlider - NSCustomView - NSComboBox - NSComboBoxCell - NSTextField - NSWindowTemplate + NSMenu NSTextFieldCell NSButtonCell + NSMenuItem NSBox + NSColorWell + NSMatrix + NSSlider + NSSliderCell + NSCustomView + NSCustomObject NSView + NSWindowTemplate + NSTextField NSPopUpButtonCell - NSUserDefaultsController - NSPopUpButton - NSMenuItem - NSMatrix YES @@ -136,7 +134,7 @@ 6 System controlColor - + 3 MC42NjY2NjY2NjY3AA @@ -604,6 +602,73 @@ 256 YES + + + 268 + {{158, 15}, {194, 26}} + + + YES + + -2076049856 + 2048 + + + 109199615 + 129 + + + 400 + 75 + + + Item 1 + + 1048576 + 2147483647 + 1 + + + _popUpItemAction: + + + YES + + OtherViews + + YES + + + + Item 2 + + 1048576 + 2147483647 + + + _popUpItemAction: + + + + + Item 3 + + 1048576 + 2147483647 + + + _popUpItemAction: + + + + + + 1 + YES + YES + 2 + + 264 @@ -747,99 +812,6 @@ - - - 268 - {{161, 16}, {178, 26}} - - - YES - - 74579521 - 272630784 - - - - YES - - - 5 - YES - - - - 274 - {15, 0} - - - YES - - YES - - - 12 - 10 - 1000 - - 75628032 - 0 - - - LucidaGrande - 12 - 16 - - - 3 - MC4zMzMzMzI5ODU2AA - - - - - 338820672 - 1024 - - - YES - - 6 - System - controlBackgroundColor - - - - - 3 - YES - - - - 3 - 2 - - - 6 - System - gridColor - - 3 - MC41AA - - - 19 - tableViewAction: - -767524864 - - - - 1 - 15 - 0 - YES - 0 - - - 264 @@ -1121,7 +1093,7 @@ NSResponder - + 256 YES @@ -1130,7 +1102,6 @@ 268 {{138, 82}, {172, 18}} - YES -2080244224 @@ -1153,7 +1124,6 @@ 268 {{141, 102}, {169, 18}} - YES @@ -1177,7 +1147,6 @@ 264 {{345, 22}, {38, 13}} - YES 67239424 @@ -1194,7 +1163,6 @@ 264 {{316, 22}, {36, 13}} - YES 67763712 @@ -1211,7 +1179,6 @@ 264 {{49, 20}, {149, 17}} - YES 67239424 @@ -1228,7 +1195,6 @@ 268 {{199, 20}, {113, 15}} - YES -2079981824 @@ -1250,7 +1216,6 @@ 268 {{49, 45}, {212, 17}} - YES 67239424 @@ -1267,7 +1232,6 @@ 264 {{258, 43}, {22, 18}} - YES 67239424 @@ -1289,7 +1253,6 @@ 264 {{32, 139}, {326, 14}} - YES 67239424 @@ -1306,7 +1269,6 @@ 268 {{143, 238}, {147, 18}} - YES -2080244224 @@ -1329,7 +1291,6 @@ 264 {{17, 284}, {140, 20}} - YES 67239424 @@ -1346,7 +1307,6 @@ 264 {{351, 279}, {99, 32}} - YES -2080244224 @@ -1370,7 +1330,6 @@ 266 {{162, 284}, {187, 23}} - YES 71433728 @@ -1394,7 +1353,6 @@ 264 {{206, 165}, {85, 17}} - YES 67239424 @@ -1420,7 +1378,6 @@ {{297, 161}, {52, 24}} - YES YES @@ -1430,7 +1387,6 @@ 264 {{149, 197}, {142, 17}} - YES 67239424 @@ -1456,7 +1412,6 @@ {{297, 193}, {52, 24}} - YES YES @@ -1475,7 +1430,6 @@ {{297, 235}, {52, 24}} - YES YES @@ -1485,8 +1439,6 @@ {464, 328} - - NSView NSResponder @@ -1881,42 +1833,6 @@ 391 - - - appList - - - - 396 - - - - delegate - - - - 397 - - - - hidden: values.TextEditor - - - - - - hidden: values.TextEditor - hidden - values.TextEditor - - NSValueTransformerName - NSIsNil - - 2 - - - 399 - hidden: values.TextEditor @@ -2153,6 +2069,22 @@ 463 + + + changedExternalEditorsMenu: + + + + 470 + + + + externalEditorMenuButton + + + + 471 + @@ -2366,10 +2298,10 @@ - + editing view @@ -2844,15 +2776,6 @@ - - 392 - - - YES - - - - 393 @@ -2867,11 +2790,6 @@ - - 395 - - - 402 @@ -3082,6 +3000,50 @@ + + 464 + + + YES + + + + + + 465 + + + YES + + + + + + 466 + + + YES + + + + + + + + 467 + + + + + 468 + + + + + 469 + + + @@ -3271,12 +3233,9 @@ 383.IBPluginDependency 384.IBPluginDependency 385.IBPluginDependency - 392.IBPluginDependency - 392.IBViewBoundsToFrameTransform 393.IBPluginDependency 393.IBViewBoundsToFrameTransform 394.IBPluginDependency - 395.IBPluginDependency 402.IBPluginDependency 402.IBPropertyAccessControl 402.IBViewBoundsToFrameTransform @@ -3332,6 +3291,8 @@ 457.IBViewBoundsToFrameTransform 458.IBPluginDependency 459.IBPluginDependency + 464.IBPluginDependency + 465.IBPluginDependency 5.IBEditorWindowLastContentRect 5.IBPluginDependency 5.IBWindowTemplateEditedContentRect @@ -3589,16 +3550,11 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - - P4AAAL+AAABDKQAAwhQAAA - - com.apple.InterfaceBuilder.CocoaPlugin P4AAAL+AAABBoAAAwggAAA com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin P4AAAL+AAABCkgAAwrAAAA @@ -3679,6 +3635,8 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin {{622, 191}, {399, 334}} com.apple.InterfaceBuilder.CocoaPlugin {{622, 191}, {399, 334}} @@ -3699,7 +3657,7 @@ - 463 + 471 @@ -4078,6 +4036,7 @@ changedAltRows: changedAutoSuggestLinks: changedBackgroundTextColorWell: + changedExternalEditorsMenu: changedForegroundTextColorWell: changedHighlightSearchTerms: changedMakeURLsClickable: @@ -4127,6 +4086,7 @@ id id id + id @@ -4137,6 +4097,7 @@ changedAltRows: changedAutoSuggestLinks: changedBackgroundTextColorWell: + changedExternalEditorsMenu: changedForegroundTextColorWell: changedHighlightSearchTerms: changedMakeURLsClickable: @@ -4177,6 +4138,10 @@ changedBackgroundTextColorWell: id + + changedExternalEditorsMenu: + id + changedForegroundTextColorWell: id @@ -4278,6 +4243,7 @@ confirmDeletionButton databaseView editingView + externalEditorMenuButton folderLocationsMenuButton fontsColorsView foregroundColorWell @@ -4316,6 +4282,7 @@ NSView NSView NSPopUpButton + NSPopUpButton NSView NSColorWell NSView @@ -4355,6 +4322,7 @@ confirmDeletionButton databaseView editingView + externalEditorMenuButton folderLocationsMenuButton fontsColorsView foregroundColorWell @@ -4425,6 +4393,10 @@ editingView NSView + + externalEditorMenuButton + NSPopUpButton + folderLocationsMenuButton NSPopUpButton diff --git a/fr.lproj/MainMenu.xib b/fr.lproj/MainMenu.xib index 4629bfca..2fc36044 100644 --- a/fr.lproj/MainMenu.xib +++ b/fr.lproj/MainMenu.xib @@ -68,7 +68,6 @@ {{7, 11}, {397, 464}} - {{0, 0}, {1920, 1058}} {213, 129} @@ -343,6 +342,15 @@ + + + Edit With + + 2147483647 + + + 88 + YES @@ -1127,6 +1135,24 @@ + + + Lock Note to Preview + L + 1572864 + 2147483647 + + + + + + Print Preview / PDF + P + 1572864 + 2147483647 + + + Save Preview HTML @@ -3042,6 +3068,46 @@ 1302 + + + lockPreview: + + + + 1316 + + + + lockNoteItem + + + + 1317 + + + + printPreview: + + + + 1318 + + + + printPreviewItem + + + + 1319 + + + + savePreviewItem + + + + 1320 + @@ -3261,6 +3327,7 @@ + @@ -4282,6 +4349,8 @@ + + @@ -4355,6 +4424,21 @@ + + 1303 + + + + + 1314 + + + + + 1315 + + + @@ -4547,8 +4631,11 @@ 130.IBEditorWindowLastContentRect 130.IBPluginDependency 130.ImportedFromIB2 + 1303.IBPluginDependency 131.IBPluginDependency 131.ImportedFromIB2 + 1314.IBPluginDependency + 1315.IBPluginDependency 134.IBPluginDependency 134.ImportedFromIB2 136.IBPluginDependency @@ -4932,8 +5019,11 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -5134,7 +5224,7 @@ - 1302 + 1320 @@ -5149,12 +5239,15 @@ bringFocusToControlField: copyNoteLink: deleteNote: + editNoteExternally: exportNote: fieldAction: importNotes: + lockPreview: multiTag: openFileInEditor: printNote: + printPreview: renameNote: revealNote: savePreview: @@ -5205,6 +5298,9 @@ id id id + id + id + id @@ -5214,12 +5310,15 @@ bringFocusToControlField: copyNoteLink: deleteNote: + editNoteExternally: exportNote: fieldAction: importNotes: + lockPreview: multiTag: openFileInEditor: printNote: + printPreview: renameNote: revealNote: savePreview: @@ -5254,6 +5353,10 @@ deleteNote: id + + editNoteExternally: + id + exportNote: id @@ -5266,6 +5369,10 @@ importNotes: id + + lockPreview: + id + multiTag: id @@ -5278,6 +5385,10 @@ printNote: id + + printPreview: + id + renameNote: id @@ -5362,11 +5473,14 @@ YES editorStatusView field + lockNoteItem mainView multiMarkdownPreview notesScrollView notesTableView previewToggler + printPreviewItem + savePreviewItem sparkleUpdateItem statBarMenu syncWaitPanel @@ -5381,12 +5495,15 @@ YES EmptyView DualField + NSMenuItem ETContentView NSMenuItem AugmentedScrollView NotesTableView NSMenuItem NSMenuItem + NSMenuItem + NSMenuItem NSMenu NSPanel NSProgressIndicator @@ -5403,11 +5520,14 @@ YES editorStatusView field + lockNoteItem mainView multiMarkdownPreview notesScrollView notesTableView previewToggler + printPreviewItem + savePreviewItem sparkleUpdateItem statBarMenu syncWaitPanel @@ -5428,6 +5548,10 @@ field DualField + + lockNoteItem + NSMenuItem + mainView ETContentView @@ -5448,6 +5572,14 @@ previewToggler NSMenuItem + + printPreviewItem + NSMenuItem + + + savePreviewItem + NSMenuItem + sparkleUpdateItem NSMenuItem diff --git a/fr.lproj/Preferences.xib b/fr.lproj/Preferences.xib index bacd9d66..8d6fc8e6 100644 --- a/fr.lproj/Preferences.xib +++ b/fr.lproj/Preferences.xib @@ -12,26 +12,24 @@ YES - NSColorWell - NSMenu - NSSliderCell + NSUserDefaultsController + NSPopUpButton NSButton - NSCustomObject - NSSlider - NSCustomView - NSComboBox - NSComboBoxCell - NSTextField - NSWindowTemplate + NSMenu NSTextFieldCell NSButtonCell + NSMenuItem NSBox + NSColorWell + NSMatrix + NSSlider + NSSliderCell + NSCustomView + NSCustomObject NSView + NSWindowTemplate + NSTextField NSPopUpButtonCell - NSUserDefaultsController - NSPopUpButton - NSMenuItem - NSMatrix YES @@ -136,7 +134,7 @@ 6 System controlColor - + 3 MC42NjY2NjY2NjY3AA @@ -604,6 +602,73 @@ 256 YES + + + 268 + {{153, 21}, {194, 26}} + + + YES + + -2076049856 + 2048 + + + 109199615 + 129 + + + 400 + 75 + + + Item 1 + + 1048576 + 2147483647 + 1 + + + _popUpItemAction: + + + YES + + OtherViews + + YES + + + + Item 2 + + 1048576 + 2147483647 + + + _popUpItemAction: + + + + + Item 3 + + 1048576 + 2147483647 + + + _popUpItemAction: + + + + + + 1 + YES + YES + 2 + + 264 @@ -747,99 +812,6 @@ - - - 268 - {{158, 21}, {178, 26}} - - - YES - - 74579521 - 272630784 - - - - YES - - - 5 - YES - - - - 274 - {15, 0} - - - YES - - YES - - - 12 - 10 - 1000 - - 75628032 - 0 - - - LucidaGrande - 12 - 16 - - - 3 - MC4zMzMzMzI5ODU2AA - - - - - 338820672 - 1024 - - - YES - - 6 - System - controlBackgroundColor - - - - - 3 - YES - - - - 3 - 2 - - - 6 - System - gridColor - - 3 - MC41AA - - - 19 - tableViewAction: - -767524864 - - - - 1 - 15 - 0 - YES - 0 - - - 264 @@ -1121,7 +1093,7 @@ NSResponder - + 256 YES @@ -1130,7 +1102,6 @@ 268 {{85, 79}, {172, 18}} - YES -2080244224 @@ -1153,7 +1124,6 @@ 268 {{88, 99}, {169, 18}} - YES @@ -1177,7 +1147,6 @@ 264 {{347, 22}, {38, 13}} - YES 67239424 @@ -1194,7 +1163,6 @@ 264 {{318, 22}, {36, 13}} - YES 67763712 @@ -1211,7 +1179,6 @@ 264 {{51, 20}, {149, 17}} - YES 67239424 @@ -1228,7 +1195,6 @@ 268 {{201, 20}, {113, 15}} - YES -2079981824 @@ -1250,7 +1216,6 @@ 268 {{51, 45}, {212, 17}} - YES 67239424 @@ -1267,7 +1232,6 @@ 264 {{261, 44}, {22, 18}} - YES 67239424 @@ -1289,7 +1253,6 @@ 264 {{35, 129}, {326, 14}} - YES 67239424 @@ -1315,7 +1278,6 @@ {{243, 225}, {52, 24}} - YES YES @@ -1328,7 +1290,6 @@ 264 {{39, 275}, {54, 19}} - YES 67239424 @@ -1345,7 +1306,6 @@ 264 {{300, 267}, {83, 32}} - YES 67239424 @@ -1369,7 +1329,6 @@ 266 {{98, 274}, {197, 23}} - YES 71433728 @@ -1393,7 +1352,6 @@ 268 {{75, 228}, {162, 18}} - YES -2080244224 @@ -1416,7 +1374,6 @@ 264 {{146, 151}, {92, 21}} - YES 67239424 @@ -1442,7 +1399,6 @@ {{243, 151}, {52, 24}} - YES YES @@ -1452,7 +1408,6 @@ 264 {{114, 183}, {124, 21}} - YES 67239424 @@ -1478,15 +1433,12 @@ {{243, 183}, {52, 24}} - YES YES {420, 318} - - NSView NSResponder @@ -1881,22 +1833,6 @@ 400 - - - delegate - - - - 405 - - - - appList - - - - 406 - hidden: values.TextEditor @@ -1917,26 +1853,6 @@ 409 - - - hidden: values.TextEditor - - - - - - hidden: values.TextEditor - hidden - values.TextEditor - - NSValueTransformerName - NSIsNil - - 2 - - - 413 - hidden: values.KeepsMaxTextWidth @@ -2153,6 +2069,22 @@ 471 + + + changedExternalEditorsMenu: + + + + 478 + + + + externalEditorMenuButton + + + + 479 + @@ -2366,10 +2298,10 @@ - + editing view @@ -2844,15 +2776,6 @@ - - 401 - - - YES - - - - 402 @@ -2867,11 +2790,6 @@ - - 404 - - - 414 @@ -3082,6 +3000,50 @@ + + 472 + + + YES + + + + + + 473 + + + YES + + + + + + 474 + + + YES + + + + + + + + 475 + + + + + 476 + + + + + 477 + + + @@ -3266,12 +3228,9 @@ 392.IBPluginDependency 393.IBPluginDependency 394.IBPluginDependency - 401.IBPluginDependency - 401.IBViewBoundsToFrameTransform 402.IBPluginDependency 402.IBViewBoundsToFrameTransform 403.IBPluginDependency - 404.IBPluginDependency 414.IBPluginDependency 414.IBPropertyAccessControl 414.IBViewBoundsToFrameTransform @@ -3327,6 +3286,8 @@ 465.IBViewBoundsToFrameTransform 466.IBPluginDependency 467.IBPluginDependency + 472.IBPluginDependency + 473.IBPluginDependency 5.IBEditorWindowLastContentRect 5.IBPluginDependency 5.IBWindowTemplateEditedContentRect @@ -3577,16 +3538,11 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - - P4AAAL+AAABDSQAAwjgAAA - - com.apple.InterfaceBuilder.CocoaPlugin P4AAAL+AAABCUAAAwiwAAA com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin P4AAAL+AAABCDAAAwrAAAA @@ -3667,6 +3623,8 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin {{278, 522}, {399, 334}} com.apple.InterfaceBuilder.CocoaPlugin {{278, 522}, {399, 334}} @@ -3687,7 +3645,7 @@ - 471 + 479 @@ -4066,6 +4024,7 @@ changedAltRows: changedAutoSuggestLinks: changedBackgroundTextColorWell: + changedExternalEditorsMenu: changedForegroundTextColorWell: changedHighlightSearchTerms: changedMakeURLsClickable: @@ -4115,6 +4074,7 @@ id id id + id @@ -4125,6 +4085,7 @@ changedAltRows: changedAutoSuggestLinks: changedBackgroundTextColorWell: + changedExternalEditorsMenu: changedForegroundTextColorWell: changedHighlightSearchTerms: changedMakeURLsClickable: @@ -4165,6 +4126,10 @@ changedBackgroundTextColorWell: id + + changedExternalEditorsMenu: + id + changedForegroundTextColorWell: id @@ -4266,6 +4231,7 @@ confirmDeletionButton databaseView editingView + externalEditorMenuButton folderLocationsMenuButton fontsColorsView foregroundColorWell @@ -4304,6 +4270,7 @@ NSView NSView NSPopUpButton + NSPopUpButton NSView NSColorWell NSView @@ -4343,6 +4310,7 @@ confirmDeletionButton databaseView editingView + externalEditorMenuButton folderLocationsMenuButton fontsColorsView foregroundColorWell @@ -4413,6 +4381,10 @@ editingView NSView + + externalEditorMenuButton + NSPopUpButton + folderLocationsMenuButton NSPopUpButton diff --git a/pt.lproj/MainMenu.xib b/pt.lproj/MainMenu.xib index 636a44b0..6cd5d980 100644 --- a/pt.lproj/MainMenu.xib +++ b/pt.lproj/MainMenu.xib @@ -68,7 +68,6 @@ {{7, 11}, {397, 464}} - {{0, 0}, {1920, 1058}} {213, 129} @@ -341,6 +340,15 @@ + + + Edit With + + 2147483647 + + + 88 + YES @@ -1133,6 +1141,24 @@ + + + Lock Note to Preview + L + 1572864 + 2147483647 + + + + + + Print Preview / PDF + P + 1572864 + 2147483647 + + + Save Preview HTML @@ -3115,6 +3141,46 @@ 1291 + + + lockPreview: + + + + 1295 + + + + printPreview: + + + + 1296 + + + + lockNoteItem + + + + 1297 + + + + printPreviewItem + + + + 1298 + + + + savePreviewItem + + + + 1299 + @@ -3334,6 +3400,7 @@ + @@ -4354,6 +4421,8 @@ + + @@ -4427,6 +4496,21 @@ + + 1292 + + + + + 1293 + + + + + 1294 + + + @@ -4621,6 +4705,9 @@ 1288.IBPluginDependency 129.IBPluginDependency 129.ImportedFromIB2 + 1292.IBPluginDependency + 1293.IBPluginDependency + 1294.IBPluginDependency 130.IBPluginDependency 130.ImportedFromIB2 131.IBPluginDependency @@ -5008,6 +5095,9 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -5210,7 +5300,7 @@ - 1291 + 1299 @@ -5225,12 +5315,15 @@ bringFocusToControlField: copyNoteLink: deleteNote: + editNoteExternally: exportNote: fieldAction: importNotes: + lockPreview: multiTag: openFileInEditor: printNote: + printPreview: renameNote: revealNote: savePreview: @@ -5281,6 +5374,9 @@ id id id + id + id + id @@ -5290,12 +5386,15 @@ bringFocusToControlField: copyNoteLink: deleteNote: + editNoteExternally: exportNote: fieldAction: importNotes: + lockPreview: multiTag: openFileInEditor: printNote: + printPreview: renameNote: revealNote: savePreview: @@ -5330,6 +5429,10 @@ deleteNote: id + + editNoteExternally: + id + exportNote: id @@ -5342,6 +5445,10 @@ importNotes: id + + lockPreview: + id + multiTag: id @@ -5354,6 +5461,10 @@ printNote: id + + printPreview: + id + renameNote: id @@ -5438,11 +5549,14 @@ YES editorStatusView field + lockNoteItem mainView multiMarkdownPreview notesScrollView notesTableView previewToggler + printPreviewItem + savePreviewItem sparkleUpdateItem statBarMenu syncWaitPanel @@ -5457,12 +5571,15 @@ YES EmptyView DualField + NSMenuItem ETContentView NSMenuItem AugmentedScrollView NotesTableView NSMenuItem NSMenuItem + NSMenuItem + NSMenuItem NSMenu NSPanel NSProgressIndicator @@ -5479,11 +5596,14 @@ YES editorStatusView field + lockNoteItem mainView multiMarkdownPreview notesScrollView notesTableView previewToggler + printPreviewItem + savePreviewItem sparkleUpdateItem statBarMenu syncWaitPanel @@ -5504,6 +5624,10 @@ field DualField + + lockNoteItem + NSMenuItem + mainView ETContentView @@ -5524,6 +5648,14 @@ previewToggler NSMenuItem + + printPreviewItem + NSMenuItem + + + savePreviewItem + NSMenuItem + sparkleUpdateItem NSMenuItem diff --git a/pt.lproj/Preferences.xib b/pt.lproj/Preferences.xib index dcc9643a..539bc15d 100644 --- a/pt.lproj/Preferences.xib +++ b/pt.lproj/Preferences.xib @@ -12,26 +12,24 @@ YES - NSColorWell - NSMenu - NSSliderCell + NSUserDefaultsController + NSPopUpButton NSButton - NSCustomObject - NSSlider - NSCustomView - NSComboBox - NSComboBoxCell - NSTextField - NSWindowTemplate + NSMenu NSTextFieldCell NSButtonCell + NSMenuItem NSBox + NSColorWell + NSMatrix + NSSlider + NSSliderCell + NSCustomView + NSCustomObject NSView + NSWindowTemplate + NSTextField NSPopUpButtonCell - NSUserDefaultsController - NSPopUpButton - NSMenuItem - NSMatrix YES @@ -136,7 +134,7 @@ 6 System controlColor - + 3 MC42NjY2NjY2NjY3AA @@ -603,6 +601,73 @@ 256 YES + + + 268 + {{157, 14}, {194, 26}} + + + YES + + -2076049856 + 2048 + + + 109199615 + 129 + + + 400 + 75 + + + Item 1 + + 1048576 + 2147483647 + 1 + + + _popUpItemAction: + + + YES + + OtherViews + + YES + + + + Item 2 + + 1048576 + 2147483647 + + + _popUpItemAction: + + + + + Item 3 + + 1048576 + 2147483647 + + + _popUpItemAction: + + + + + + 1 + YES + YES + 2 + + 264 @@ -746,99 +811,6 @@ - - - 268 - {{170, 14}, {178, 26}} - - - YES - - 74579521 - 272630784 - - - - YES - - - 5 - YES - - - - 274 - {15, 0} - - - YES - - YES - - - 12 - 10 - 1000 - - 75628032 - 0 - - - LucidaGrande - 12 - 16 - - - 3 - MC4zMzMzMzI5ODU2AA - - - - - 338820672 - 1024 - - - YES - - 6 - System - controlBackgroundColor - - - - - 3 - YES - - - - 3 - 2 - - - 6 - System - gridColor - - 3 - MC41AA - - - 19 - tableViewAction: - -767524864 - - - - 1 - 15 - 0 - YES - 0 - - - 264 @@ -1222,7 +1194,7 @@ SW5jLiwgMjAwNQAAAAA NSResponder - + 256 YES @@ -1231,7 +1203,6 @@ SW5jLiwgMjAwNQAAAAA 268 {{56, 81}, {172, 18}} - YES -2080244224 @@ -1254,7 +1225,6 @@ SW5jLiwgMjAwNQAAAAA 268 {{59, 101}, {169, 18}} - YES @@ -1278,7 +1248,6 @@ SW5jLiwgMjAwNQAAAAA 264 {{339, 22}, {38, 13}} - YES 67239424 @@ -1295,7 +1264,6 @@ SW5jLiwgMjAwNQAAAAA 264 {{310, 22}, {36, 13}} - YES 67763712 @@ -1312,7 +1280,6 @@ SW5jLiwgMjAwNQAAAAA 264 {{43, 20}, {149, 17}} - YES 67239424 @@ -1329,7 +1296,6 @@ SW5jLiwgMjAwNQAAAAA 268 {{193, 20}, {113, 15}} - YES -2079981824 @@ -1351,7 +1317,6 @@ SW5jLiwgMjAwNQAAAAA 268 {{43, 45}, {212, 17}} - YES 67239424 @@ -1368,7 +1333,6 @@ SW5jLiwgMjAwNQAAAAA 264 {{252, 43}, {22, 18}} - YES 67239424 @@ -1390,7 +1354,6 @@ SW5jLiwgMjAwNQAAAAA 264 {{43, 139}, {326, 14}} - YES 67239424 @@ -1416,7 +1379,6 @@ SW5jLiwgMjAwNQAAAAA {{217, 235}, {52, 24}} - YES YES @@ -1429,7 +1391,6 @@ SW5jLiwgMjAwNQAAAAA 268 {{90, 238}, {121, 18}} - YES -2080244224 @@ -1452,7 +1413,6 @@ SW5jLiwgMjAwNQAAAAA 264 {{35, 287}, {51, 17}} - YES 67239424 @@ -1469,7 +1429,6 @@ SW5jLiwgMjAwNQAAAAA 264 {{291, 279}, {96, 32}} - YES 67239424 @@ -1493,7 +1452,6 @@ SW5jLiwgMjAwNQAAAAA 266 {{92, 284}, {197, 23}} - YES 71433728 @@ -1517,7 +1475,6 @@ SW5jLiwgMjAwNQAAAAA 264 {{162, 165}, {49, 17}} - YES 67239424 @@ -1543,7 +1500,6 @@ SW5jLiwgMjAwNQAAAAA {{217, 161}, {52, 24}} - YES YES @@ -1553,7 +1509,6 @@ SW5jLiwgMjAwNQAAAAA 264 {{162, 197}, {49, 17}} - YES 67239424 @@ -1579,15 +1534,12 @@ SW5jLiwgMjAwNQAAAAA {{217, 193}, {52, 24}} - YES YES {420, 328} - - NSView NSResponder @@ -2002,42 +1954,6 @@ SW5jLiwgMjAwNQAAAAA 398 - - - appList - - - - 399 - - - - delegate - - - - 400 - - - - hidden: values.TextEditor - - - - - - hidden: values.TextEditor - hidden - values.TextEditor - - NSValueTransformerName - NSIsNil - - 2 - - - 402 - value: values.KeepsMaxTextWidth @@ -2262,6 +2178,22 @@ SW5jLiwgMjAwNQAAAAA 474 + + + externalEditorMenuButton + + + + 481 + + + + changedExternalEditorsMenu: + + + + 482 + @@ -2475,10 +2407,10 @@ SW5jLiwgMjAwNQAAAAA - + editing view @@ -2953,15 +2885,6 @@ SW5jLiwgMjAwNQAAAAA - - 393 - - - YES - - - - 394 @@ -2976,11 +2899,6 @@ SW5jLiwgMjAwNQAAAAA - - 396 - - - 403 @@ -3191,6 +3109,50 @@ SW5jLiwgMjAwNQAAAAA + + 475 + + + YES + + + + + + 476 + + + YES + + + + + + 477 + + + YES + + + + + + + + 478 + + + + + 479 + + + + + 480 + + + @@ -3377,12 +3339,9 @@ SW5jLiwgMjAwNQAAAAA 384.IBPluginDependency 385.IBPluginDependency 386.IBPluginDependency - 393.IBPluginDependency - 393.IBViewBoundsToFrameTransform 394.IBPluginDependency 394.IBViewBoundsToFrameTransform 395.IBPluginDependency - 396.IBPluginDependency 403.IBPluginDependency 403.IBPropertyAccessControl 403.IBViewBoundsToFrameTransform @@ -3438,6 +3397,8 @@ SW5jLiwgMjAwNQAAAAA 468.IBViewBoundsToFrameTransform 469.IBPluginDependency 470.IBPluginDependency + 475.IBPluginDependency + 476.IBPluginDependency 5.IBEditorWindowLastContentRect 5.IBPluginDependency 5.IBPropertyAccessControl @@ -3687,16 +3648,11 @@ SW5jLiwgMjAwNQAAAAA com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - - P4AAAL+AAABDKQAAwhQAAA - - com.apple.InterfaceBuilder.CocoaPlugin P4AAAL+AAABBoAAAwggAAA com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin P4AAAL+AAABCMAAAwrAAAA @@ -3777,6 +3733,8 @@ SW5jLiwgMjAwNQAAAAA com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin {{329, 268}, {399, 334}} com.apple.InterfaceBuilder.CocoaPlugin @@ -3798,7 +3756,7 @@ SW5jLiwgMjAwNQAAAAA - 474 + 482 @@ -4177,6 +4135,7 @@ SW5jLiwgMjAwNQAAAAA changedAltRows: changedAutoSuggestLinks: changedBackgroundTextColorWell: + changedExternalEditorsMenu: changedForegroundTextColorWell: changedHighlightSearchTerms: changedMakeURLsClickable: @@ -4226,6 +4185,7 @@ SW5jLiwgMjAwNQAAAAA id id id + id @@ -4236,6 +4196,7 @@ SW5jLiwgMjAwNQAAAAA changedAltRows: changedAutoSuggestLinks: changedBackgroundTextColorWell: + changedExternalEditorsMenu: changedForegroundTextColorWell: changedHighlightSearchTerms: changedMakeURLsClickable: @@ -4276,6 +4237,10 @@ SW5jLiwgMjAwNQAAAAA changedBackgroundTextColorWell: id + + changedExternalEditorsMenu: + id + changedForegroundTextColorWell: id @@ -4377,6 +4342,7 @@ SW5jLiwgMjAwNQAAAAA confirmDeletionButton databaseView editingView + externalEditorMenuButton folderLocationsMenuButton fontsColorsView foregroundColorWell @@ -4415,6 +4381,7 @@ SW5jLiwgMjAwNQAAAAA NSView NSView NSPopUpButton + NSPopUpButton NSView NSColorWell NSView @@ -4454,6 +4421,7 @@ SW5jLiwgMjAwNQAAAAA confirmDeletionButton databaseView editingView + externalEditorMenuButton folderLocationsMenuButton fontsColorsView foregroundColorWell @@ -4524,6 +4492,10 @@ SW5jLiwgMjAwNQAAAAA editingView NSView + + externalEditorMenuButton + NSPopUpButton + folderLocationsMenuButton NSPopUpButton