From 733ce7af7977ed42f43a51fd9275598cac557da3 Mon Sep 17 00:00:00 2001 From: relikd Date: Sun, 19 Aug 2018 04:04:36 +0200 Subject: [PATCH] Menu - Preferences Sync. There is still a bug with unread count ... --- baRSS/AppHook.h | 4 +- baRSS/AppHook.m | 35 +----------- baRSS/Preferences/Feeds Tab/ModalFeedEdit.h | 7 +++ baRSS/Preferences/Feeds Tab/ModalFeedEdit.m | 63 +++++++++++---------- baRSS/Preferences/Feeds Tab/SettingsFeeds.m | 54 ++++++++++++++---- baRSS/Status Bar Menu/BarMenu.h | 2 +- baRSS/Status Bar Menu/BarMenu.m | 28 ++++++++- baRSS/StoreCoordinator.h | 4 +- baRSS/StoreCoordinator.m | 16 ++++-- 9 files changed, 126 insertions(+), 87 deletions(-) diff --git a/baRSS/AppHook.h b/baRSS/AppHook.h index 3703247..d3bb5d0 100644 --- a/baRSS/AppHook.h +++ b/baRSS/AppHook.h @@ -23,7 +23,9 @@ #import #import +@class BarMenu; + @interface AppHook : NSApplication +@property (readonly, strong) BarMenu *barMenu; @property (readonly, strong) NSPersistentContainer *persistentContainer; -- (IBAction)saveAction:(id)sender; @end diff --git a/baRSS/AppHook.m b/baRSS/AppHook.m index 8187980..9835aab 100644 --- a/baRSS/AppHook.m +++ b/baRSS/AppHook.m @@ -24,10 +24,6 @@ #import "PyHandler.h" #import "BarMenu.h" -@interface AppHook() -@property (strong) BarMenu *barMenu; -@end - @implementation AppHook - (instancetype)init { @@ -37,7 +33,7 @@ } - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { - self.barMenu = [BarMenu new]; + _barMenu = [BarMenu new]; [PyHandler prepare]; printf("up and running\n"); } @@ -46,10 +42,6 @@ [PyHandler shutdown]; } -- (IBAction)closePreferences:(id)sender { - NSLog(@"closing in %@", sender); -} - #pragma mark - Core Data stack @@ -67,13 +59,8 @@ abort(); } }]; - NSUndoManager *um = [[NSUndoManager alloc] init]; - um.groupsByEvent = NO; - um.levelsOfUndo = 30; - _persistentContainer.viewContext.undoManager = um; } } - return _persistentContainer; } @@ -81,26 +68,6 @@ #pragma mark - Core Data Saving and Undo support -- (IBAction)saveAction:(id)sender { - // Performs the save action for the application, which is to send the save: message to the application's managed object context. Any encountered errors are presented to the user. - NSManagedObjectContext *context = self.persistentContainer.viewContext; - - if (![context commitEditing]) { - NSLog(@"%@:%@ unable to commit editing before saving", [self class], NSStringFromSelector(_cmd)); - } - - NSError *error = nil; - if (context.hasChanges && ![context save:&error]) { - // Customize this code block to include application-specific recovery steps. - [[NSApplication sharedApplication] presentError:error]; - } -} - -- (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window { - // Returns the NSUndoManager for the application. In this case, the manager returned is that of the managed object context for the application. - return self.persistentContainer.viewContext.undoManager; -} - - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { // Save changes in the application's managed object context before the application terminates. NSManagedObjectContext *context = self.persistentContainer.viewContext; diff --git a/baRSS/Preferences/Feeds Tab/ModalFeedEdit.h b/baRSS/Preferences/Feeds Tab/ModalFeedEdit.h index c25f69b..ea8bb3a 100644 --- a/baRSS/Preferences/Feeds Tab/ModalFeedEdit.h +++ b/baRSS/Preferences/Feeds Tab/ModalFeedEdit.h @@ -22,7 +22,14 @@ #import +@class FeedConfig; + +@protocol ModalEditDelegate +- (void)modalDidUpdateFeedConfig:(FeedConfig*)config; +@end + @protocol ModalFeedConfigEdit +@property (weak) id delegate; - (void)updateRepresentedObject; // must call [item.managedObjectContext refreshObject:item mergeChanges:YES]; @end diff --git a/baRSS/Preferences/Feeds Tab/ModalFeedEdit.m b/baRSS/Preferences/Feeds Tab/ModalFeedEdit.m index 03f928a..99add36 100644 --- a/baRSS/Preferences/Feeds Tab/ModalFeedEdit.m +++ b/baRSS/Preferences/Feeds Tab/ModalFeedEdit.m @@ -44,6 +44,7 @@ @end @implementation ModalFeedEdit +@synthesize delegate; - (void)viewDidLoad { [super viewDidLoad]; @@ -71,48 +72,47 @@ if (self.shouldSaveObject) { if (self.objectNeedsSaving) [self updateRepresentedObject]; - NSUndoManager *um = [self feedConfigOrNil].managedObjectContext.undoManager; + FeedConfig *item = [self feedConfigOrNil]; + NSUndoManager *um = item.managedObjectContext.undoManager; [um endUndoGrouping]; if (!self.objectIsModified) { [um disableUndoRegistration]; [um undoNestedGroup]; [um enableUndoRegistration]; } else { - [StoreCoordinator save]; + [self.delegate modalDidUpdateFeedConfig:item]; } } } - (void)updateRepresentedObject { FeedConfig *item = [self feedConfigOrNil]; - if (item) { - if (!self.shouldSaveObject) { // first call to this method - [item.managedObjectContext.undoManager beginUndoGrouping]; - } - self.shouldSaveObject = YES; - self.objectNeedsSaving = NO; // after this method it is saved - - // if's to prevent unnecessary undo groups if nothing has changed - if (![item.name isEqualToString: self.name.stringValue]) - item.name = self.name.stringValue; - if (![item.url isEqualToString:self.url.stringValue]) - item.url = self.url.stringValue; - if (item.refreshNum != self.refreshNum.intValue) - item.refreshNum = self.refreshNum.intValue; - if (item.refreshUnit != self.refreshUnit.indexOfSelectedItem) - item.refreshUnit = (int16_t)self.refreshUnit.indexOfSelectedItem; - - - if (self.feedResult) { - Feed *rss = [StoreCoordinator createFeedFromDictionary:self.feedResult]; - if (item.feed) - [item.managedObjectContext deleteObject:(NSManagedObject*)item.feed]; - item.feed = rss; - } - if ([item.managedObjectContext hasChanges]) { - self.objectIsModified = YES; - [item.managedObjectContext refreshObject:item mergeChanges:YES]; - } + if (!item) + return; + if (!self.shouldSaveObject) // first call to this method + [item.managedObjectContext.undoManager beginUndoGrouping]; + self.shouldSaveObject = YES; + self.objectNeedsSaving = NO; // after this method it is saved + + // if's to prevent unnecessary undo groups if nothing has changed + if (![item.name isEqualToString: self.name.stringValue]) + item.name = self.name.stringValue; + if (![item.url isEqualToString:self.url.stringValue]) + item.url = self.url.stringValue; + if (item.refreshNum != self.refreshNum.intValue) + item.refreshNum = self.refreshNum.intValue; + if (item.refreshUnit != self.refreshUnit.indexOfSelectedItem) + item.refreshUnit = (int16_t)self.refreshUnit.indexOfSelectedItem; + + if (self.feedResult) { + Feed *rss = [StoreCoordinator createFeedFromDictionary:self.feedResult inContext:item.managedObjectContext]; + if (item.feed) + [item.managedObjectContext deleteObject:(NSManagedObject*)item.feed]; + item.feed = rss; + } + if ([item.managedObjectContext hasChanges]) { + self.objectIsModified = YES; + [item.managedObjectContext refreshObject:item mergeChanges:YES]; } } @@ -151,7 +151,6 @@ }); }]; } - // http://feeds.feedburner.com/simpledesktops } - (void)setTitleFromFeed { @@ -183,6 +182,7 @@ #pragma mark - ModalGroupEdit @implementation ModalGroupEdit +@synthesize delegate; - (void)viewDidLoad { [super viewDidLoad]; if ([self.representedObject isKindOfClass:[FeedConfig class]]) { @@ -203,6 +203,7 @@ if (![item.name isEqualToString: name]) { item.name = name; [item.managedObjectContext refreshObject:item mergeChanges:YES]; + [self.delegate modalDidUpdateFeedConfig:item]; } } } diff --git a/baRSS/Preferences/Feeds Tab/SettingsFeeds.m b/baRSS/Preferences/Feeds Tab/SettingsFeeds.m index 5043ccc..34138c4 100644 --- a/baRSS/Preferences/Feeds Tab/SettingsFeeds.m +++ b/baRSS/Preferences/Feeds Tab/SettingsFeeds.m @@ -22,12 +22,13 @@ #import "SettingsFeeds.h" #import "AppHook.h" -#import "FeedConfig+Ext.h" +#import "BarMenu.h" #import "ModalSheet.h" #import "ModalFeedEdit.h" #import "DrawImage.h" +#import "StoreCoordinator.h" -@interface SettingsFeeds () +@interface SettingsFeeds () @property (weak) IBOutlet NSOutlineView *outlineView; @property (weak) IBOutlet NSTreeController *dataStore; @@ -43,10 +44,29 @@ static NSString *dragNodeType = @"baRSS-feed-drag"; - (void)viewDidLoad { [super viewDidLoad]; - self.dataStore.managedObjectContext = [(AppHook*)NSApp persistentContainer].viewContext; - self.undoManager = self.dataStore.managedObjectContext.undoManager; [self.outlineView registerForDraggedTypes:[NSArray arrayWithObject:dragNodeType]]; [self.dataStore setSortDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"sortIndex" ascending:YES]]]; + + NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; + [childContext setParentContext:[(AppHook*)NSApp persistentContainer].viewContext]; +// childContext.automaticallyMergesChangesFromParent = YES; + NSUndoManager *um = [[NSUndoManager alloc] init]; + um.groupsByEvent = NO; + um.levelsOfUndo = 30; + childContext.undoManager = um; + + self.dataStore.managedObjectContext = childContext; + self.undoManager = self.dataStore.managedObjectContext.undoManager; +} + +- (void)dealloc { + [self saveAndRebuildMenu]; +} + +- (void)saveAndRebuildMenu { + [StoreCoordinator saveContext:self.dataStore.managedObjectContext]; + [StoreCoordinator saveContext:self.dataStore.managedObjectContext.parentContext]; + [[(AppHook*)NSApp barMenu] rebuildMenu]; // updating individual items was way to complicated ... } - (IBAction)addFeed:(id)sender { @@ -63,6 +83,7 @@ static NSString *dragNodeType = @"baRSS-feed-drag"; sp.name = @"---"; sp.typ = SEPARATOR; [self.undoManager endUndoGrouping]; + [self saveAndRebuildMenu]; } - (IBAction)remove:(id)sender { @@ -71,6 +92,7 @@ static NSString *dragNodeType = @"baRSS-feed-drag"; [self incrementIndicesBy:-1 forSubsequentNodes:path]; [self.dataStore remove:sender]; [self.undoManager endUndoGrouping]; + [self saveAndRebuildMenu]; } - (IBAction)doubleClickOutlineView:(NSOutlineView*)sender { @@ -97,22 +119,28 @@ static NSString *dragNodeType = @"baRSS-feed-drag"; } self.modalController = (group ? [ModalGroupEdit new] : [ModalFeedEdit new]); self.modalController.representedObject = obj; + self.modalController.delegate = self; [self.view.window beginSheet:[ModalSheet modalWithView:self.modalController.view] completionHandler:^(NSModalResponse returnCode) { if (returnCode == NSModalResponseOK) { - [self.undoManager beginUndoGrouping]; if (!existingItem) { // create new item + [self.undoManager beginUndoGrouping]; FeedConfig *item = [self insertSortedItemAtSelection]; item.typ = (group ? GROUP : FEED); self.modalController.representedObject = item; } [self.modalController updateRepresentedObject]; - [self.undoManager endUndoGrouping]; + if (!existingItem) + [self.undoManager endUndoGrouping]; } self.modalController = nil; }]; } +- (void)modalDidUpdateFeedConfig:(FeedConfig*)config { + [self saveAndRebuildMenu]; +} + - (FeedConfig*)insertSortedItemAtSelection { NSIndexPath *selectedIndex = [self.dataStore selectionIndexPath]; NSIndexPath *insertIndex = selectedIndex; @@ -164,13 +192,15 @@ static NSString *dragNodeType = @"baRSS-feed-drag"; } - (void)outlineView:(NSOutlineView *)outlineView draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation { - self.currentlyDraggedNodes = nil; [self.undoManager endUndoGrouping]; - if ([self.dataStore.managedObjectContext hasChanges]) { - NSError *err; - [self.dataStore.managedObjectContext save:&err]; - if (err) NSLog(@"Error: %@", err); + if (self.dataStore.managedObjectContext.hasChanges) { + [self saveAndRebuildMenu]; + } else { + [self.undoManager disableUndoRegistration]; + [self.undoManager undoNestedGroup]; + [self.undoManager enableUndoRegistration]; } + self.currentlyDraggedNodes = nil; } - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id )info item:(id)item childIndex:(NSInteger)index { @@ -290,11 +320,13 @@ static NSString *dragNodeType = @"baRSS-feed-drag"; - (void)undo:(id)sender { [self.undoManager undo]; [self.dataStore rearrangeObjects]; // update ordering + [self saveAndRebuildMenu]; } - (void)redo:(id)sender { [self.undoManager redo]; [self.dataStore rearrangeObjects]; // update ordering + [self saveAndRebuildMenu]; } - (void)enterPressed:(id)sender { diff --git a/baRSS/Status Bar Menu/BarMenu.h b/baRSS/Status Bar Menu/BarMenu.h index e48e519..5ecb3d1 100644 --- a/baRSS/Status Bar Menu/BarMenu.h +++ b/baRSS/Status Bar Menu/BarMenu.h @@ -23,5 +23,5 @@ #import @interface BarMenu : NSObject -//+ (instancetype)start; +- (void)rebuildMenu; @end diff --git a/baRSS/Status Bar Menu/BarMenu.m b/baRSS/Status Bar Menu/BarMenu.m index f86bd22..3c3ed7f 100644 --- a/baRSS/Status Bar Menu/BarMenu.m +++ b/baRSS/Status Bar Menu/BarMenu.m @@ -59,13 +59,16 @@ typedef NS_OPTIONS(NSInteger, MenuItemTag) { - (instancetype)init { self = [super init]; self.barItem = [NSStatusBar.systemStatusBar statusItemWithLength:NSVariableStatusItemLength]; - self.barItem.menu = self.mainMenu; self.barItem.highlightMode = YES; - [self updateBarIcon]; + self.barItem.menu = [self generateMainMenu]; // [self donothing]; return self; } +- (void)rebuildMenu { + self.barItem.menu = [self generateMainMenu]; +} + - (void)donothing { dispatch_async(dispatch_get_main_queue(), ^{ [self.mm itemAtIndex:4].title = [NSString stringWithFormat:@"%@", [NSDate date]]; @@ -74,6 +77,21 @@ typedef NS_OPTIONS(NSInteger, MenuItemTag) { [self performSelectorInBackground:@selector(donothing) withObject:nil]; } +- (void)printUnreadRecurisve:(NSMenu*)menu str:(NSString*)prefix { + for (NSMenuItem *item in menu.itemArray) { + if (!item.hasUnread) continue; + MenuItemInfo *info = item.representedObject; + id obj = [StoreCoordinator objectWithID:info.objID]; + if ([obj isKindOfClass:[FeedItem class]]) + NSLog(@"%@ %@ (%d == %d)", prefix, item.title, item.unreadCount, [obj unread]); + else + NSLog(@"%@ %@ (%d)", prefix, item.title, item.unreadCount); + if (item.hasSubmenu) { + [self printUnreadRecurisve:item.submenu str:[NSString stringWithFormat:@" %@", prefix]]; + } + } +} + /** Update menu bar icon and text according to unread count and user preferences. */ @@ -87,6 +105,8 @@ typedef NS_OPTIONS(NSInteger, MenuItemTag) { self.barItem.image = [[RSSIcon templateIcon:16 tint:nil] image]; self.barItem.image.template = YES; } + NSLog(@"==> %d", self.unreadCountTotal); + [self printUnreadRecurisve:self.barItem.menu str:@""]; } @@ -96,7 +116,7 @@ typedef NS_OPTIONS(NSInteger, MenuItemTag) { /** Builds main menu with items on the very first menu level. Including Preferences, Quit, etc. */ -- (NSMenu*)mainMenu { +- (NSMenu*)generateMainMenu { NSMenu *menu = [NSMenu new]; menu.autoenablesItems = NO; [self addTitle:NSLocalizedString(@"Pause Updates", nil) selector:@selector(pauseUpdates:) toMenu:menu tag:TagPauseUpdates]; @@ -104,9 +124,11 @@ typedef NS_OPTIONS(NSInteger, MenuItemTag) { [menu addItem:[NSMenuItem separatorItem]]; [self defaultHeaderForMenu:menu scope:ScopeGlobal]; + self.unreadCountTotal = 0; for (FeedConfig *fc in [StoreCoordinator sortedFeedConfigItems]) { [menu addItem:[self menuItemForFeedConfig:fc unread:&_unreadCountTotal]]; } + [self updateBarIcon]; [menu addItem:[NSMenuItem separatorItem]]; [self addTitle:NSLocalizedString(@"Preferences", nil) selector:@selector(openPreferences) toMenu:menu tag:TagPreferences]; diff --git a/baRSS/StoreCoordinator.h b/baRSS/StoreCoordinator.h index ee78ec1..a383ce5 100644 --- a/baRSS/StoreCoordinator.h +++ b/baRSS/StoreCoordinator.h @@ -26,9 +26,9 @@ @interface StoreCoordinator : NSObject -+ (void)save; ++ (void)saveContext:(NSManagedObjectContext*)context; + (void)deleteUnreferencedFeeds; + (NSArray*)sortedFeedConfigItems; + (id)objectWithID:(NSManagedObjectID*)objID; -+ (Feed*)createFeedFromDictionary:(NSDictionary*)obj; ++ (Feed*)createFeedFromDictionary:(NSDictionary*)obj inContext:(NSManagedObjectContext*)context; @end diff --git a/baRSS/StoreCoordinator.m b/baRSS/StoreCoordinator.m index 7fa4984..21a8f7e 100644 --- a/baRSS/StoreCoordinator.m +++ b/baRSS/StoreCoordinator.m @@ -29,8 +29,16 @@ return [(AppHook*)NSApp persistentContainer].viewContext; } -+ (void)save { - [(AppHook*)NSApp saveAction:nil]; ++ (void)saveContext:(NSManagedObjectContext*)context { + // Performs the save action for the application, which is to send the save: message to the application's managed object context. Any encountered errors are presented to the user. + if (![context commitEditing]) { + NSLog(@"%@:%@ unable to commit editing before saving", [self class], NSStringFromSelector(_cmd)); + } + NSError *error = nil; + if (context.hasChanges && ![context save:&error]) { + // Customize this code block to include application-specific recovery steps. + [[NSApplication sharedApplication] presentError:error]; + } } + (void)deleteUnreferencedFeeds { @@ -58,8 +66,8 @@ return [[self getContext] objectWithID:objID]; } -+ (Feed*)createFeedFromDictionary:(NSDictionary*)obj { - NSManagedObjectContext *moc = [self getContext]; ++ (Feed*)createFeedFromDictionary:(NSDictionary*)obj inContext:(NSManagedObjectContext*)moc { +// NSManagedObjectContext *moc = [self getContext]; Feed *a = [[Feed alloc] initWithEntity:Feed.entity insertIntoManagedObjectContext:moc]; a.title = obj[@"feed"][@"title"]; a.subtitle = obj[@"feed"][@"subtitle"];