From 9507f1d412d44c957819d45df7792aeecf658f98 Mon Sep 17 00:00:00 2001 From: relikd Date: Sun, 5 Aug 2018 01:20:05 +0200 Subject: [PATCH] Proper editing functionality + bugfix sortindex consistency --- baRSS.xcodeproj/project.pbxproj | 12 +- baRSS/AppHook.m | 9 +- baRSS/Base.lproj/Main.xib | 278 ++++++++++-------- .../DBv1.xcdatamodel/contents | 12 +- baRSS/{FeedEdit.h => ModalSheet.h} | 27 +- baRSS/{FeedEdit.m => ModalSheet.m} | 69 ++++- baRSS/NewsController.h | 1 + baRSS/NewsController.m | 171 +++++++---- baRSS/Preferences.h | 7 +- baRSS/Preferences.m | 65 ++-- 10 files changed, 426 insertions(+), 225 deletions(-) rename baRSS/{FeedEdit.h => ModalSheet.h} (62%) rename baRSS/{FeedEdit.m => ModalSheet.m} (53%) diff --git a/baRSS.xcodeproj/project.pbxproj b/baRSS.xcodeproj/project.pbxproj index 95aaf52..49a7898 100644 --- a/baRSS.xcodeproj/project.pbxproj +++ b/baRSS.xcodeproj/project.pbxproj @@ -8,7 +8,7 @@ /* Begin PBXBuildFile section */ 1968E0AE14B8E8A90E194980 /* PyHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 1968EF7567E06D2A5BB3481A /* PyHandler.m */; }; - 544B011A2114B41200386E5C /* FeedEdit.m in Sources */ = {isa = PBXBuildFile; fileRef = 544B01192114B41200386E5C /* FeedEdit.m */; }; + 544B011A2114B41200386E5C /* ModalSheet.m in Sources */ = {isa = PBXBuildFile; fileRef = 544B01192114B41200386E5C /* ModalSheet.m */; }; 544B011D2114EE9100386E5C /* AppHook.m in Sources */ = {isa = PBXBuildFile; fileRef = 544B011C2114EE9100386E5C /* AppHook.m */; }; 544FBD4521064AEB008A260C /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 544FBD4421064AEB008A260C /* Python.framework */; }; 544FBD4721064B2F008A260C /* getFeed.py in Resources */ = {isa = PBXBuildFile; fileRef = 544FBD4621064B2F008A260C /* getFeed.py */; }; @@ -25,8 +25,8 @@ /* Begin PBXFileReference section */ 1968E7919BAA36F042FCB717 /* PyHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PyHandler.h; sourceTree = ""; }; 1968EF7567E06D2A5BB3481A /* PyHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PyHandler.m; sourceTree = ""; }; - 544B01182114B41200386E5C /* FeedEdit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FeedEdit.h; sourceTree = ""; }; - 544B01192114B41200386E5C /* FeedEdit.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FeedEdit.m; sourceTree = ""; }; + 544B01182114B41200386E5C /* ModalSheet.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ModalSheet.h; sourceTree = ""; }; + 544B01192114B41200386E5C /* ModalSheet.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ModalSheet.m; sourceTree = ""; }; 544B011B2114EE9100386E5C /* AppHook.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppHook.h; sourceTree = ""; }; 544B011C2114EE9100386E5C /* AppHook.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppHook.m; sourceTree = ""; }; 544FBD4421064AEB008A260C /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Python.framework; path = System/Library/Frameworks/Python.framework; sourceTree = SDKROOT; }; @@ -106,8 +106,8 @@ 54ACC29421061E270020715F /* NewsController.m */, 54ACC29621061FBA0020715F /* Preferences.h */, 54ACC29721061FBA0020715F /* Preferences.m */, - 544B01182114B41200386E5C /* FeedEdit.h */, - 544B01192114B41200386E5C /* FeedEdit.m */, + 544B01182114B41200386E5C /* ModalSheet.h */, + 544B01192114B41200386E5C /* ModalSheet.m */, 54ACC28521061B3C0020715F /* Assets.xcassets */, 54ACC28721061B3C0020715F /* Main.xib */, 54ACC28A21061B3C0020715F /* Info.plist */, @@ -202,7 +202,7 @@ 54ACC29521061E270020715F /* NewsController.m in Sources */, 54ACC28C21061B3C0020715F /* main.m in Sources */, 54ACC28121061B3B0020715F /* AppDelegate.m in Sources */, - 544B011A2114B41200386E5C /* FeedEdit.m in Sources */, + 544B011A2114B41200386E5C /* ModalSheet.m in Sources */, 54ACC29821061FBA0020715F /* Preferences.m in Sources */, 1968E0AE14B8E8A90E194980 /* PyHandler.m in Sources */, ); diff --git a/baRSS/AppHook.m b/baRSS/AppHook.m index d31b1bc..78868da 100644 --- a/baRSS/AppHook.m +++ b/baRSS/AppHook.m @@ -43,14 +43,19 @@ static NSEventModifierFlags fnKeyFlags = NSEventModifierFlagShift | NSEventModif case 'w': if ([self sendAction:@selector(performClose:) to:nil from:self]) return; break; } } else if (flags == (NSEventModifierFlagCommand | NSEventModifierFlagShift)) { - if (key == 'z') { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wundeclared-selector" + if (key == 'z') { if ([self sendAction:@selector(redo:) to:nil from:self]) return; -#pragma clang diagnostic pop + } + } else { + if (key == 13 || key == 3) { // Enter / Return key + if ([self sendAction:@selector(enterPressed:) to:nil from:self]) + return; } } +#pragma clang diagnostic pop } [super sendEvent:event]; } diff --git a/baRSS/Base.lproj/Main.xib b/baRSS/Base.lproj/Main.xib index 9db1b58..ed30530 100644 --- a/baRSS/Base.lproj/Main.xib +++ b/baRSS/Base.lproj/Main.xib @@ -18,7 +18,6 @@ - @@ -59,6 +58,8 @@ + + @@ -72,14 +73,14 @@ - + - + - + - + @@ -89,7 +90,7 @@ - + @@ -321,7 +322,7 @@ - + @@ -334,25 +335,22 @@ - + - + - + - - - @@ -361,11 +359,11 @@ - + - + @@ -373,7 +371,7 @@ - + @@ -381,7 +379,7 @@ - + @@ -393,7 +391,7 @@ - + @@ -404,9 +402,6 @@ - - - @@ -416,7 +411,7 @@ - + @@ -426,7 +421,7 @@ - + @@ -456,7 +451,7 @@ - + @@ -471,7 +466,7 @@ CA - + @@ -511,66 +506,19 @@ CA - + - + + - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/baRSS/DBv1.xcdatamodeld/DBv1.xcdatamodel/contents b/baRSS/DBv1.xcdatamodeld/DBv1.xcdatamodel/contents index fc0ab34..137cd9b 100644 --- a/baRSS/DBv1.xcdatamodeld/DBv1.xcdatamodel/contents +++ b/baRSS/DBv1.xcdatamodeld/DBv1.xcdatamodel/contents @@ -12,12 +12,16 @@ + + + - + + - + @@ -38,8 +42,8 @@ - - + + diff --git a/baRSS/FeedEdit.h b/baRSS/ModalSheet.h similarity index 62% rename from baRSS/FeedEdit.h rename to baRSS/ModalSheet.h index f23e9d7..0963232 100644 --- a/baRSS/FeedEdit.h +++ b/baRSS/ModalSheet.h @@ -22,6 +22,29 @@ #import -@interface FeedEdit : NSWindow - +@interface ModalSheet : NSWindow +- (void)setFormContent:(NSView *)subcontent; @end + + +@interface ModalFeedEdit : NSView +@property (weak) IBOutlet NSTextField *url; +@property (weak) IBOutlet NSTextField *title; +@property (weak) IBOutlet NSTextField *refreshNum; +@property (weak) IBOutlet NSPopUpButton *refreshUnit; +- (void)setDefaultValues; +- (void)setURL:(NSString*)url name:(NSString*)name refreshNum:(int32_t)num unit:(int16_t)unit; ++ (NSString*)stringForRefreshNum:(int32_t)num unit:(int16_t)unit; +@end + + +@interface ModalGroupEdit : NSView +@property (weak) IBOutlet NSTextField *title; +- (void)setDefaultValues; +- (void)setGroupName:(NSString*)name; +@end + + +@interface StrictUIntFormatter : NSFormatter +@end + diff --git a/baRSS/FeedEdit.m b/baRSS/ModalSheet.m similarity index 53% rename from baRSS/FeedEdit.m rename to baRSS/ModalSheet.m index 0b4a900..f773d95 100644 --- a/baRSS/FeedEdit.m +++ b/baRSS/ModalSheet.m @@ -20,25 +20,80 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#import "FeedEdit.h" +#import "ModalSheet.h" -@implementation FeedEdit +#define BETWEEN(x,min,max) (x < min ? min : x > max ? max : x) + +#pragma mark - ModalSheet + +@interface ModalSheet() +@property (weak) IBOutlet NSView *content; +@end + +@implementation ModalSheet +- (void)setFormContent:(NSView *)subcontent { + [self.content.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; + + CGFloat heightDiff = subcontent.frame.size.height - self.content.frame.size.height; + NSRect oldFrame = self.frame; + oldFrame.size.height += heightDiff; + [self setFrame:oldFrame display:NO]; + + [subcontent setFrameSize:NSMakeSize(self.content.frame.size.width, subcontent.frame.size.height)]; + [self.content addSubview:subcontent]; + [self recalculateKeyViewLoop]; + + self.minSize = NSMakeSize(323 + 40, self.frame.size.height); + self.maxSize = NSMakeSize(1200, self.frame.size.height); +} - (IBAction)didTapDoneButton:(id)sender { - [self.parentWindow endSheet:self returnCode:NSModalResponseOK]; + [self.sheetParent endSheet:self returnCode:NSModalResponseOK]; } - - (IBAction)didTapCancelButton:(id)sender { - [self.parentWindow endSheet:self returnCode:NSModalResponseAbort]; + [self.sheetParent endSheet:self returnCode:NSModalResponseAbort]; } - @end +#pragma mark - ModalFeedEdit -@interface StrictUIntFormatter : NSFormatter + +@implementation ModalFeedEdit +- (void)setDefaultValues { + self.url.stringValue = @""; + self.title.stringValue = @""; + self.refreshNum.intValue = 30; + [self.refreshUnit selectItemAtIndex:1]; +} +- (void)setURL:(NSString*)url name:(NSString*)name refreshNum:(int32_t)num unit:(int16_t)unit { + self.url.objectValue = url; + self.title.objectValue = name; + self.refreshNum.intValue = num; + [self.refreshUnit selectItemAtIndex:BETWEEN(unit, 0, self.refreshUnit.numberOfItems - 1)]; +} ++ (NSString*)stringForRefreshNum:(int32_t)num unit:(int16_t)unit { + return [NSString stringWithFormat:@"%d%c", num, [@"smhdw" characterAtIndex:(NSUInteger)BETWEEN(unit, 0, 4)]]; +} @end + +#pragma mark - ModalGroupEdit + + +@implementation ModalGroupEdit +- (void)setDefaultValues { + self.title.stringValue = @"New Group"; +} +- (void)setGroupName:(NSString*)name { + self.title.objectValue = name; +} +@end + + +#pragma mark - StrictUIntFormatter + + @implementation StrictUIntFormatter - (NSString *)stringForObjectValue:(id)obj { return [NSString stringWithFormat:@"%d", [[NSString stringWithFormat:@"%@", obj] intValue]]; diff --git a/baRSS/NewsController.h b/baRSS/NewsController.h index a8202b1..9067b0c 100644 --- a/baRSS/NewsController.h +++ b/baRSS/NewsController.h @@ -28,4 +28,5 @@ - (IBAction)addSeparator:(NSButton *)sender; - (NSString*)copyDescriptionOfSelectedItems; +- (void)openModalForSelection; @end diff --git a/baRSS/NewsController.m b/baRSS/NewsController.m index 28276b9..54e21f4 100644 --- a/baRSS/NewsController.m +++ b/baRSS/NewsController.m @@ -24,9 +24,12 @@ #import "PyHandler.h" #import "Preferences.h" #import "DBv1+CoreDataModel.h" +#import "ModalSheet.h" @interface NewsController () @property (weak) IBOutlet Preferences *preferencesWindow; +@property (weak) IBOutlet ModalFeedEdit *viewModalEditFeed; +@property (weak) IBOutlet ModalGroupEdit *viewModalEditGroup; @property (weak) IBOutlet NSOutlineView *outlineView; @property (strong) NSArray *currentlyDraggedNodes; @@ -35,7 +38,7 @@ @implementation NewsController // Declare a string constant for the drag type - to be used when writing and retrieving pasteboard data... -static NSString *dragNodeType = @"baRSS-feed-type"; +static NSString *dragNodeType = @"baRSS-feed-drag"; - (void)awakeFromNib { [super awakeFromNib]; @@ -45,17 +48,24 @@ static NSString *dragNodeType = @"baRSS-feed-type"; } - (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item { - // owner is nil to prohibit repeated awakeFromNib calls - NSTableCellView *cellView = [self.outlineView makeViewWithIdentifier:tableColumn.identifier owner:nil]; - if (cellView) - return cellView; // is a refresh cell - FeedConfig *f = [(NSTreeNode*)item representedObject]; - if (f.type == 2) { // Seperator - return [self.outlineView makeViewWithIdentifier:@"cellFeedConfigSeperator" owner:nil]; + bool isSeperator = (f.type == 2); + bool isRefreshColumn = [tableColumn.identifier isEqualToString:@"RefreshColumn"]; + + NSString *cellIdent = (isRefreshColumn ? @"cellRefresh" : (isSeperator ? @"cellSeparator" : @"cellFeed")); + // owner is nil to prohibit repeated awakeFromNib calls + NSTableCellView *cellView = [self.outlineView makeViewWithIdentifier:cellIdent owner:nil]; + + if (isRefreshColumn) { + cellView.textField.stringValue = (f.type != 1 ? @"" : [ModalFeedEdit stringForRefreshNum:f.refreshNum unit:f.refreshUnit]); + } else if (isSeperator) { + return cellView; // the refresh cell is already skipped with the above if condition + } else { + cellView.textField.objectValue = f.name; + cellView.imageView.image = (f.type == 0 ? [NSImage imageNamed:NSImageNameFolder] : nil); + // TODO: load icon or show default rss icon } - cellView = [self.outlineView makeViewWithIdentifier:@"cellFeedConfigName" owner:nil]; - cellView.imageView.image = [NSImage imageNamed:NSImageNameFolder]; + cellView.textField.textColor = (f.refreshNum == 0 ? [NSColor disabledControlTextColor] : [NSColor controlTextColor]); return cellView; } @@ -99,39 +109,17 @@ static NSString *dragNodeType = @"baRSS-feed-type"; NSLog(@"all unread"); } -- (IBAction)presentModalFeedProperties:(id)sender { - self.preferencesWindow.feedDetailSheet.parentWindow = self.preferencesWindow; - [self.preferencesWindow beginSheet:self.preferencesWindow.feedDetailSheet completionHandler:^(NSModalResponse returnCode) { - NSLog(@"%ld", (long)returnCode); - }]; -// if ([sender isKindOfClass:[NSOutlineView class]]) { -// NSOutlineView *ov = sender; -// if (ov.clickedRow == -1) -// return; // ignore clicks on column headers and where no row was selected -// -// id vop = [ov itemAtRow:ov.clickedRow]; -// NSLog(@"%@", vop); -// } +#pragma mark - Insert & Edit Feed Items + +- (IBAction)addFeed:(id)sender { + [self showModalForFeedConfig:nil isGroupEdit:NO]; } -- (IBAction)addFeed:(NSButton *)sender { - [self.managedObjectContext.undoManager beginUndoGrouping]; - FeedConfig *nf = [self insertSortedItemAtSelection]; - nf.type = 1; - nf.name = [NSString stringWithFormat:@"%@", [NSDate date]]; - nf.refresh = @"42s"; - [self.managedObjectContext.undoManager endUndoGrouping]; +- (IBAction)addGroup:(id)sender { + [self showModalForFeedConfig:nil isGroupEdit:YES]; } -- (IBAction)addGroup:(NSButton *)sender { - [self.managedObjectContext.undoManager beginUndoGrouping]; - FeedConfig *g = [self insertSortedItemAtSelection]; - g.name = @"Group"; - g.type = 0; - [self.managedObjectContext.undoManager endUndoGrouping]; -} - -- (IBAction)addSeparator:(NSButton *)sender { +- (IBAction)addSeparator:(id)sender { [self.managedObjectContext.undoManager beginUndoGrouping]; FeedConfig *sp = [self insertSortedItemAtSelection]; sp.name = @"---"; @@ -139,6 +127,86 @@ static NSString *dragNodeType = @"baRSS-feed-type"; [self.managedObjectContext.undoManager endUndoGrouping]; } +- (void)remove:(id)sender { + [self.managedObjectContext.undoManager beginUndoGrouping]; + for (NSIndexPath *path in self.selectionIndexPaths) + [self incrementIndicesBy:-1 forSubsequentNodes:path]; + [super remove:sender]; + [self.managedObjectContext.undoManager endUndoGrouping]; +} + +- (IBAction)doubleClickOutlineView:(NSOutlineView*)sender { + if (sender.clickedRow == -1) + return; // ignore clicks on column headers and where no row was selected + + FeedConfig *fc = [(NSTreeNode*)[sender itemAtRow:sender.clickedRow] representedObject]; + [self showModalForFeedConfig:fc isGroupEdit:YES]; // yes will be overwritten anyway +} + +- (void)openModalForSelection { + [self showModalForFeedConfig:self.selectedObjects.firstObject isGroupEdit:YES]; // yes will be overwritten anyway +} + +- (void)showModalForFeedConfig:(FeedConfig*)obj isGroupEdit:(bool)group { + bool existingItem = [obj isKindOfClass:[FeedConfig class]]; + if (existingItem) { + if (obj.type == 2) return; // Separator + group = (obj.type == 0); + if (group) [self.viewModalEditGroup setGroupName:obj.name]; + else [self.viewModalEditFeed setURL:obj.url name:obj.name refreshNum:obj.refreshNum unit:obj.refreshUnit]; + } else { + if (group) [self.viewModalEditGroup setDefaultValues]; + else [self.viewModalEditFeed setDefaultValues]; + } + NSView *content = (group ? self.viewModalEditGroup : self.viewModalEditFeed); + [self.preferencesWindow presentModal:content completion:^(NSModalResponse returnCode) { + if (returnCode == NSModalResponseOK) { + [self.managedObjectContext.undoManager beginUndoGrouping]; + FeedConfig *item = obj; + if (!existingItem) { // create new item + item = [self insertSortedItemAtSelection]; + item.type = (group ? 0 : 1); + } + + if (group) { + item.name = self.viewModalEditGroup.title.stringValue; + } else { + item.name = self.viewModalEditFeed.title.stringValue; + item.url = self.viewModalEditFeed.url.stringValue; + item.refreshNum = self.viewModalEditFeed.refreshNum.intValue; + item.refreshUnit = (int16_t)self.viewModalEditFeed.refreshUnit.indexOfSelectedItem; + } + [self.managedObjectContext.undoManager endUndoGrouping]; + [self rearrangeObjects]; + } + }]; +} + +- (FeedConfig*)insertSortedItemAtSelection { + NSIndexPath *selectedIndex = [self selectionIndexPath]; + NSIndexPath *insertIndex = selectedIndex; + + FeedConfig *selected = [[[self arrangedObjects] descendantNodeAtIndexPath:selectedIndex] representedObject]; + NSUInteger lastIndex = selected.children.count; + bool groupSelected = (selected.type == 0); + + if (!groupSelected) { + lastIndex = (NSUInteger)selected.sortIndex + 1; // insert after selection + insertIndex = [insertIndex indexPathByRemovingLastIndex]; + [self incrementIndicesBy:+1 forSubsequentNodes:selectedIndex]; + --selected.sortIndex; // insert after selection + } + + FeedConfig *newItem = [[FeedConfig alloc] initWithEntity:FeedConfig.entity insertIntoManagedObjectContext:self.managedObjectContext]; + [self insertObject:newItem atArrangedObjectIndexPath:[insertIndex indexPathByAddingIndex:lastIndex]]; + // First insert, then parent, else troubles + newItem.sortIndex = (int32_t)lastIndex; + newItem.parent = (groupSelected ? selected : selected.parent); + return newItem; +} + +#pragma mark - Import & Export of Data + - (NSString*)copyDescriptionOfSelectedItems { NSMutableString *str = [[NSMutableString alloc] init]; for (FeedConfig *item in self.selectedObjects) { @@ -157,30 +225,13 @@ static NSString *dragNodeType = @"baRSS-feed-type"; switch (obj.type) { case 0: [str appendFormat:@"%@:\n", obj.name]; break; // Group case 2: [str appendString:@"-------------\n"]; break; // Separator - default: [str appendFormat:@"%@ (%@) - %@\n", obj.name, obj.url, obj.refresh]; + default: [str appendFormat:@"%@ (%@) - %@\n", obj.name, obj.url, [ModalFeedEdit stringForRefreshNum:obj.refreshNum unit:obj.refreshUnit]]; } for (FeedConfig *child in obj.children) { [self traverseChildren:child appendString:str indentation:indent + 1]; } } -- (FeedConfig*)insertSortedItemAtSelection { - FeedConfig *selected = [[[self arrangedObjects] descendantNodeAtIndexPath:[self selectionIndexPath]] representedObject]; - if (selected.type != 0) { // other than group - [self incrementIndicesBy:+1 forSubsequentNodes:[self selectionIndexPath]]; - } - FeedConfig *newItem = [[FeedConfig alloc] initWithEntity:FeedConfig.entity insertIntoManagedObjectContext:self.managedObjectContext]; - if (selected.type == 0) { // a group - newItem.sortIndex = (int32_t)selected.children.count; - newItem.parent = selected; - } else { - newItem.sortIndex = selected.sortIndex; - newItem.parent = selected.parent; - --selected.sortIndex; // was increased before the new item is inserted - } - return newItem; -} - - (void)incrementIndicesBy:(int)val forSubsequentNodes:(NSIndexPath*)path { NSIndexPath *parentPath = [path indexPathByRemovingLastIndex]; NSTreeNode *root = [self arrangedObjects]; @@ -272,11 +323,11 @@ static NSString *dragNodeType = @"baRSS-feed-type"; @end +#pragma mark - Drawings on Screen -@interface Separator : NSView -@end +@interface DrawSeparator : NSView @end -@implementation Separator +@implementation DrawSeparator - (void)drawRect:(NSRect)dirtyRect { [super drawRect:dirtyRect]; NSGradient *grdnt = [[NSGradient alloc] initWithStartingColor:[NSColor darkGrayColor] endingColor:[[NSColor darkGrayColor] colorWithAlphaComponent:0.0]]; diff --git a/baRSS/Preferences.h b/baRSS/Preferences.h index c11e8d9..22e1869 100644 --- a/baRSS/Preferences.h +++ b/baRSS/Preferences.h @@ -22,7 +22,10 @@ #import -@interface Preferences : NSWindow -@property (weak) IBOutlet NSWindow *feedDetailSheet; +@class ModalSheet; +@interface Preferences : NSWindow +@property (weak) IBOutlet ModalSheet *modalSheet; + +- (void)presentModal:(NSView*)view completion:(void (^ __nullable)(NSModalResponse returnCode))handler; @end diff --git a/baRSS/Preferences.m b/baRSS/Preferences.m index c1f66b9..1f437eb 100644 --- a/baRSS/Preferences.m +++ b/baRSS/Preferences.m @@ -22,6 +22,7 @@ #import "Preferences.h" #import "NewsController.h" +#import "ModalSheet.h" @interface Preferences () @property (weak) IBOutlet NSView *viewGeneral; @@ -33,36 +34,62 @@ - (void)awakeFromNib { [super awakeFromNib]; - if (self.contentView.subviews.count == 0) { + NSUInteger idx = (NSUInteger)[[NSUserDefaults standardUserDefaults] integerForKey:@"preferencesTab"]; + if (idx >= self.toolbar.items.count) + idx = 0; + [self tabClicked:self.toolbar.items[idx]]; +} + +- (IBAction)tabClicked:(NSToolbarItem *)sender { + self.contentView = nil; + if ([sender.itemIdentifier isEqualToString:@"tabGeneral"]) self.contentView = self.viewGeneral; - self.toolbar.selectedItemIdentifier = self.toolbar.items.firstObject.itemIdentifier; - } -} - -- (IBAction)tabGeneralClicked:(NSToolbarItem *)sender { - self.contentView = self.viewGeneral; -} - -- (IBAction)tabFeedsClicked:(NSToolbarItem *)sender { - self.contentView = self.viewFeeds; + else if ([sender.itemIdentifier isEqualToString:@"tabFeeds"]) + self.contentView = self.viewFeeds; + + self.toolbar.selectedItemIdentifier = sender.itemIdentifier; + [self recalculateKeyViewLoop]; + [self setInitialFirstResponder:self.contentView]; + + [[NSUserDefaults standardUserDefaults] setInteger:(NSInteger)[self.toolbar.items indexOfObject:sender] + forKey:@"preferencesTab"]; } - (void)undo:(id)sender { - if (self.contentView == self.viewFeeds) { - [self.newsController.managedObjectContext.undoManager undo]; - [self.newsController rearrangeObjects]; // update the ordering - } + [self.newsController.managedObjectContext.undoManager undo]; + [self.newsController rearrangeObjects]; // update ordering } - (void)redo:(id)sender { - if (self.contentView == self.viewFeeds) { - [self.newsController.managedObjectContext.undoManager redo]; - [self.newsController rearrangeObjects]; // update the ordering - } + [self.newsController.managedObjectContext.undoManager redo]; + [self.newsController rearrangeObjects]; // update ordering } - (void)copy:(id)sender { [self.newsController copyDescriptionOfSelectedItems]; } +- (void)enterPressed:(id)sender { + [self.newsController openModalForSelection]; +} + +- (BOOL)respondsToSelector:(SEL)aSelector { + bool isFeedView = (self.contentView == self.viewFeeds) && !self.attachedSheet; + // Only if 'Feeds' Tab is selected & no open modal sheet & NSOutlineView has focus + if (aSelector == @selector(enterPressed:) || aSelector == @selector(copy:)) { + bool outlineHasFocus = [[self firstResponder] isKindOfClass:[NSOutlineView class]]; + return isFeedView && outlineHasFocus && (self.newsController.selectedNodes.count > 0); + } else if (aSelector == @selector(undo:)) { + return isFeedView && [self.newsController.managedObjectContext.undoManager canUndo]; + } else if (aSelector == @selector(redo:)) { + return isFeedView && [self.newsController.managedObjectContext.undoManager canRedo]; + } + return [super respondsToSelector:aSelector]; +} + +- (void)presentModal:(NSView*)view completion:(void (^ __nullable)(NSModalResponse returnCode))handler { + [self.modalSheet setFormContent:view]; + [self beginSheet:self.modalSheet completionHandler:handler]; +} + @end