Menu - Preferences Sync. There is still a bug with unread count ...

This commit is contained in:
relikd
2018-08-19 04:04:36 +02:00
parent f2281b91c4
commit 733ce7af79
9 changed files with 126 additions and 87 deletions

View File

@@ -23,7 +23,9 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import <CoreData/CoreData.h> #import <CoreData/CoreData.h>
@class BarMenu;
@interface AppHook : NSApplication <NSApplicationDelegate> @interface AppHook : NSApplication <NSApplicationDelegate>
@property (readonly, strong) BarMenu *barMenu;
@property (readonly, strong) NSPersistentContainer *persistentContainer; @property (readonly, strong) NSPersistentContainer *persistentContainer;
- (IBAction)saveAction:(id)sender;
@end @end

View File

@@ -24,10 +24,6 @@
#import "PyHandler.h" #import "PyHandler.h"
#import "BarMenu.h" #import "BarMenu.h"
@interface AppHook()
@property (strong) BarMenu *barMenu;
@end
@implementation AppHook @implementation AppHook
- (instancetype)init { - (instancetype)init {
@@ -37,7 +33,7 @@
} }
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
self.barMenu = [BarMenu new]; _barMenu = [BarMenu new];
[PyHandler prepare]; [PyHandler prepare];
printf("up and running\n"); printf("up and running\n");
} }
@@ -46,10 +42,6 @@
[PyHandler shutdown]; [PyHandler shutdown];
} }
- (IBAction)closePreferences:(id)sender {
NSLog(@"closing in %@", sender);
}
#pragma mark - Core Data stack #pragma mark - Core Data stack
@@ -67,13 +59,8 @@
abort(); abort();
} }
}]; }];
NSUndoManager *um = [[NSUndoManager alloc] init];
um.groupsByEvent = NO;
um.levelsOfUndo = 30;
_persistentContainer.viewContext.undoManager = um;
} }
} }
return _persistentContainer; return _persistentContainer;
} }
@@ -81,26 +68,6 @@
#pragma mark - Core Data Saving and Undo support #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 { - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
// Save changes in the application's managed object context before the application terminates. // Save changes in the application's managed object context before the application terminates.
NSManagedObjectContext *context = self.persistentContainer.viewContext; NSManagedObjectContext *context = self.persistentContainer.viewContext;

View File

@@ -22,7 +22,14 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
@class FeedConfig;
@protocol ModalEditDelegate <NSObject>
- (void)modalDidUpdateFeedConfig:(FeedConfig*)config;
@end
@protocol ModalFeedConfigEdit <NSObject> @protocol ModalFeedConfigEdit <NSObject>
@property (weak) id<ModalEditDelegate> delegate;
- (void)updateRepresentedObject; // must call [item.managedObjectContext refreshObject:item mergeChanges:YES]; - (void)updateRepresentedObject; // must call [item.managedObjectContext refreshObject:item mergeChanges:YES];
@end @end

View File

@@ -44,6 +44,7 @@
@end @end
@implementation ModalFeedEdit @implementation ModalFeedEdit
@synthesize delegate;
- (void)viewDidLoad { - (void)viewDidLoad {
[super viewDidLoad]; [super viewDidLoad];
@@ -71,24 +72,25 @@
if (self.shouldSaveObject) { if (self.shouldSaveObject) {
if (self.objectNeedsSaving) if (self.objectNeedsSaving)
[self updateRepresentedObject]; [self updateRepresentedObject];
NSUndoManager *um = [self feedConfigOrNil].managedObjectContext.undoManager; FeedConfig *item = [self feedConfigOrNil];
NSUndoManager *um = item.managedObjectContext.undoManager;
[um endUndoGrouping]; [um endUndoGrouping];
if (!self.objectIsModified) { if (!self.objectIsModified) {
[um disableUndoRegistration]; [um disableUndoRegistration];
[um undoNestedGroup]; [um undoNestedGroup];
[um enableUndoRegistration]; [um enableUndoRegistration];
} else { } else {
[StoreCoordinator save]; [self.delegate modalDidUpdateFeedConfig:item];
} }
} }
} }
- (void)updateRepresentedObject { - (void)updateRepresentedObject {
FeedConfig *item = [self feedConfigOrNil]; FeedConfig *item = [self feedConfigOrNil];
if (item) { if (!item)
if (!self.shouldSaveObject) { // first call to this method return;
if (!self.shouldSaveObject) // first call to this method
[item.managedObjectContext.undoManager beginUndoGrouping]; [item.managedObjectContext.undoManager beginUndoGrouping];
}
self.shouldSaveObject = YES; self.shouldSaveObject = YES;
self.objectNeedsSaving = NO; // after this method it is saved self.objectNeedsSaving = NO; // after this method it is saved
@@ -102,9 +104,8 @@
if (item.refreshUnit != self.refreshUnit.indexOfSelectedItem) if (item.refreshUnit != self.refreshUnit.indexOfSelectedItem)
item.refreshUnit = (int16_t)self.refreshUnit.indexOfSelectedItem; item.refreshUnit = (int16_t)self.refreshUnit.indexOfSelectedItem;
if (self.feedResult) { if (self.feedResult) {
Feed *rss = [StoreCoordinator createFeedFromDictionary:self.feedResult]; Feed *rss = [StoreCoordinator createFeedFromDictionary:self.feedResult inContext:item.managedObjectContext];
if (item.feed) if (item.feed)
[item.managedObjectContext deleteObject:(NSManagedObject*)item.feed]; [item.managedObjectContext deleteObject:(NSManagedObject*)item.feed];
item.feed = rss; item.feed = rss;
@@ -114,7 +115,6 @@
[item.managedObjectContext refreshObject:item mergeChanges:YES]; [item.managedObjectContext refreshObject:item mergeChanges:YES];
} }
} }
}
- (FeedConfig*)feedConfigOrNil { - (FeedConfig*)feedConfigOrNil {
if ([self.representedObject isKindOfClass:[FeedConfig class]]) if ([self.representedObject isKindOfClass:[FeedConfig class]])
@@ -151,7 +151,6 @@
}); });
}]; }];
} }
// http://feeds.feedburner.com/simpledesktops
} }
- (void)setTitleFromFeed { - (void)setTitleFromFeed {
@@ -183,6 +182,7 @@
#pragma mark - ModalGroupEdit #pragma mark - ModalGroupEdit
@implementation ModalGroupEdit @implementation ModalGroupEdit
@synthesize delegate;
- (void)viewDidLoad { - (void)viewDidLoad {
[super viewDidLoad]; [super viewDidLoad];
if ([self.representedObject isKindOfClass:[FeedConfig class]]) { if ([self.representedObject isKindOfClass:[FeedConfig class]]) {
@@ -203,6 +203,7 @@
if (![item.name isEqualToString: name]) { if (![item.name isEqualToString: name]) {
item.name = name; item.name = name;
[item.managedObjectContext refreshObject:item mergeChanges:YES]; [item.managedObjectContext refreshObject:item mergeChanges:YES];
[self.delegate modalDidUpdateFeedConfig:item];
} }
} }
} }

View File

@@ -22,12 +22,13 @@
#import "SettingsFeeds.h" #import "SettingsFeeds.h"
#import "AppHook.h" #import "AppHook.h"
#import "FeedConfig+Ext.h" #import "BarMenu.h"
#import "ModalSheet.h" #import "ModalSheet.h"
#import "ModalFeedEdit.h" #import "ModalFeedEdit.h"
#import "DrawImage.h" #import "DrawImage.h"
#import "StoreCoordinator.h"
@interface SettingsFeeds () @interface SettingsFeeds () <ModalEditDelegate>
@property (weak) IBOutlet NSOutlineView *outlineView; @property (weak) IBOutlet NSOutlineView *outlineView;
@property (weak) IBOutlet NSTreeController *dataStore; @property (weak) IBOutlet NSTreeController *dataStore;
@@ -43,10 +44,29 @@ static NSString *dragNodeType = @"baRSS-feed-drag";
- (void)viewDidLoad { - (void)viewDidLoad {
[super viewDidLoad]; [super viewDidLoad];
self.dataStore.managedObjectContext = [(AppHook*)NSApp persistentContainer].viewContext;
self.undoManager = self.dataStore.managedObjectContext.undoManager;
[self.outlineView registerForDraggedTypes:[NSArray arrayWithObject:dragNodeType]]; [self.outlineView registerForDraggedTypes:[NSArray arrayWithObject:dragNodeType]];
[self.dataStore setSortDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"sortIndex" ascending:YES]]]; [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 { - (IBAction)addFeed:(id)sender {
@@ -63,6 +83,7 @@ static NSString *dragNodeType = @"baRSS-feed-drag";
sp.name = @"---"; sp.name = @"---";
sp.typ = SEPARATOR; sp.typ = SEPARATOR;
[self.undoManager endUndoGrouping]; [self.undoManager endUndoGrouping];
[self saveAndRebuildMenu];
} }
- (IBAction)remove:(id)sender { - (IBAction)remove:(id)sender {
@@ -71,6 +92,7 @@ static NSString *dragNodeType = @"baRSS-feed-drag";
[self incrementIndicesBy:-1 forSubsequentNodes:path]; [self incrementIndicesBy:-1 forSubsequentNodes:path];
[self.dataStore remove:sender]; [self.dataStore remove:sender];
[self.undoManager endUndoGrouping]; [self.undoManager endUndoGrouping];
[self saveAndRebuildMenu];
} }
- (IBAction)doubleClickOutlineView:(NSOutlineView*)sender { - (IBAction)doubleClickOutlineView:(NSOutlineView*)sender {
@@ -97,22 +119,28 @@ static NSString *dragNodeType = @"baRSS-feed-drag";
} }
self.modalController = (group ? [ModalGroupEdit new] : [ModalFeedEdit new]); self.modalController = (group ? [ModalGroupEdit new] : [ModalFeedEdit new]);
self.modalController.representedObject = obj; self.modalController.representedObject = obj;
self.modalController.delegate = self;
[self.view.window beginSheet:[ModalSheet modalWithView:self.modalController.view] completionHandler:^(NSModalResponse returnCode) { [self.view.window beginSheet:[ModalSheet modalWithView:self.modalController.view] completionHandler:^(NSModalResponse returnCode) {
if (returnCode == NSModalResponseOK) { if (returnCode == NSModalResponseOK) {
[self.undoManager beginUndoGrouping];
if (!existingItem) { // create new item if (!existingItem) { // create new item
[self.undoManager beginUndoGrouping];
FeedConfig *item = [self insertSortedItemAtSelection]; FeedConfig *item = [self insertSortedItemAtSelection];
item.typ = (group ? GROUP : FEED); item.typ = (group ? GROUP : FEED);
self.modalController.representedObject = item; self.modalController.representedObject = item;
} }
[self.modalController updateRepresentedObject]; [self.modalController updateRepresentedObject];
if (!existingItem)
[self.undoManager endUndoGrouping]; [self.undoManager endUndoGrouping];
} }
self.modalController = nil; self.modalController = nil;
}]; }];
} }
- (void)modalDidUpdateFeedConfig:(FeedConfig*)config {
[self saveAndRebuildMenu];
}
- (FeedConfig*)insertSortedItemAtSelection { - (FeedConfig*)insertSortedItemAtSelection {
NSIndexPath *selectedIndex = [self.dataStore selectionIndexPath]; NSIndexPath *selectedIndex = [self.dataStore selectionIndexPath];
NSIndexPath *insertIndex = selectedIndex; 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 { - (void)outlineView:(NSOutlineView *)outlineView draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation {
self.currentlyDraggedNodes = nil;
[self.undoManager endUndoGrouping]; [self.undoManager endUndoGrouping];
if ([self.dataStore.managedObjectContext hasChanges]) { if (self.dataStore.managedObjectContext.hasChanges) {
NSError *err; [self saveAndRebuildMenu];
[self.dataStore.managedObjectContext save:&err]; } else {
if (err) NSLog(@"Error: %@", err); [self.undoManager disableUndoRegistration];
[self.undoManager undoNestedGroup];
[self.undoManager enableUndoRegistration];
} }
self.currentlyDraggedNodes = nil;
} }
- (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index { - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index {
@@ -290,11 +320,13 @@ static NSString *dragNodeType = @"baRSS-feed-drag";
- (void)undo:(id)sender { - (void)undo:(id)sender {
[self.undoManager undo]; [self.undoManager undo];
[self.dataStore rearrangeObjects]; // update ordering [self.dataStore rearrangeObjects]; // update ordering
[self saveAndRebuildMenu];
} }
- (void)redo:(id)sender { - (void)redo:(id)sender {
[self.undoManager redo]; [self.undoManager redo];
[self.dataStore rearrangeObjects]; // update ordering [self.dataStore rearrangeObjects]; // update ordering
[self saveAndRebuildMenu];
} }
- (void)enterPressed:(id)sender { - (void)enterPressed:(id)sender {

View File

@@ -23,5 +23,5 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
@interface BarMenu : NSObject <NSMenuDelegate> @interface BarMenu : NSObject <NSMenuDelegate>
//+ (instancetype)start; - (void)rebuildMenu;
@end @end

View File

@@ -59,13 +59,16 @@ typedef NS_OPTIONS(NSInteger, MenuItemTag) {
- (instancetype)init { - (instancetype)init {
self = [super init]; self = [super init];
self.barItem = [NSStatusBar.systemStatusBar statusItemWithLength:NSVariableStatusItemLength]; self.barItem = [NSStatusBar.systemStatusBar statusItemWithLength:NSVariableStatusItemLength];
self.barItem.menu = self.mainMenu;
self.barItem.highlightMode = YES; self.barItem.highlightMode = YES;
[self updateBarIcon]; self.barItem.menu = [self generateMainMenu];
// [self donothing]; // [self donothing];
return self; return self;
} }
- (void)rebuildMenu {
self.barItem.menu = [self generateMainMenu];
}
- (void)donothing { - (void)donothing {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[self.mm itemAtIndex:4].title = [NSString stringWithFormat:@"%@", [NSDate date]]; [self.mm itemAtIndex:4].title = [NSString stringWithFormat:@"%@", [NSDate date]];
@@ -74,6 +77,21 @@ typedef NS_OPTIONS(NSInteger, MenuItemTag) {
[self performSelectorInBackground:@selector(donothing) withObject:nil]; [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. 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 = [[RSSIcon templateIcon:16 tint:nil] image];
self.barItem.image.template = YES; 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. Builds main menu with items on the very first menu level. Including Preferences, Quit, etc.
*/ */
- (NSMenu*)mainMenu { - (NSMenu*)generateMainMenu {
NSMenu *menu = [NSMenu new]; NSMenu *menu = [NSMenu new];
menu.autoenablesItems = NO; menu.autoenablesItems = NO;
[self addTitle:NSLocalizedString(@"Pause Updates", nil) selector:@selector(pauseUpdates:) toMenu:menu tag:TagPauseUpdates]; [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]]; [menu addItem:[NSMenuItem separatorItem]];
[self defaultHeaderForMenu:menu scope:ScopeGlobal]; [self defaultHeaderForMenu:menu scope:ScopeGlobal];
self.unreadCountTotal = 0;
for (FeedConfig *fc in [StoreCoordinator sortedFeedConfigItems]) { for (FeedConfig *fc in [StoreCoordinator sortedFeedConfigItems]) {
[menu addItem:[self menuItemForFeedConfig:fc unread:&_unreadCountTotal]]; [menu addItem:[self menuItemForFeedConfig:fc unread:&_unreadCountTotal]];
} }
[self updateBarIcon];
[menu addItem:[NSMenuItem separatorItem]]; [menu addItem:[NSMenuItem separatorItem]];
[self addTitle:NSLocalizedString(@"Preferences", nil) selector:@selector(openPreferences) toMenu:menu tag:TagPreferences]; [self addTitle:NSLocalizedString(@"Preferences", nil) selector:@selector(openPreferences) toMenu:menu tag:TagPreferences];

View File

@@ -26,9 +26,9 @@
@interface StoreCoordinator : NSObject @interface StoreCoordinator : NSObject
+ (void)save; + (void)saveContext:(NSManagedObjectContext*)context;
+ (void)deleteUnreferencedFeeds; + (void)deleteUnreferencedFeeds;
+ (NSArray<FeedConfig*>*)sortedFeedConfigItems; + (NSArray<FeedConfig*>*)sortedFeedConfigItems;
+ (id)objectWithID:(NSManagedObjectID*)objID; + (id)objectWithID:(NSManagedObjectID*)objID;
+ (Feed*)createFeedFromDictionary:(NSDictionary*)obj; + (Feed*)createFeedFromDictionary:(NSDictionary*)obj inContext:(NSManagedObjectContext*)context;
@end @end

View File

@@ -29,8 +29,16 @@
return [(AppHook*)NSApp persistentContainer].viewContext; return [(AppHook*)NSApp persistentContainer].viewContext;
} }
+ (void)save { + (void)saveContext:(NSManagedObjectContext*)context {
[(AppHook*)NSApp saveAction:nil]; // 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 { + (void)deleteUnreferencedFeeds {
@@ -58,8 +66,8 @@
return [[self getContext] objectWithID:objID]; return [[self getContext] objectWithID:objID];
} }
+ (Feed*)createFeedFromDictionary:(NSDictionary*)obj { + (Feed*)createFeedFromDictionary:(NSDictionary*)obj inContext:(NSManagedObjectContext*)moc {
NSManagedObjectContext *moc = [self getContext]; // NSManagedObjectContext *moc = [self getContext];
Feed *a = [[Feed alloc] initWithEntity:Feed.entity insertIntoManagedObjectContext:moc]; Feed *a = [[Feed alloc] initWithEntity:Feed.entity insertIntoManagedObjectContext:moc];
a.title = obj[@"feed"][@"title"]; a.title = obj[@"feed"][@"title"];
a.subtitle = obj[@"feed"][@"subtitle"]; a.subtitle = obj[@"feed"][@"subtitle"];