Menu - Preferences Sync. There is still a bug with unread count ...
This commit is contained in:
@@ -23,7 +23,9 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
|
||||
@class BarMenu;
|
||||
|
||||
@interface AppHook : NSApplication <NSApplicationDelegate>
|
||||
@property (readonly, strong) BarMenu *barMenu;
|
||||
@property (readonly, strong) NSPersistentContainer *persistentContainer;
|
||||
- (IBAction)saveAction:(id)sender;
|
||||
@end
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -22,7 +22,14 @@
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@class FeedConfig;
|
||||
|
||||
@protocol ModalEditDelegate <NSObject>
|
||||
- (void)modalDidUpdateFeedConfig:(FeedConfig*)config;
|
||||
@end
|
||||
|
||||
@protocol ModalFeedConfigEdit <NSObject>
|
||||
@property (weak) id<ModalEditDelegate> delegate;
|
||||
- (void)updateRepresentedObject; // must call [item.managedObjectContext refreshObject:item mergeChanges:YES];
|
||||
@end
|
||||
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 () <ModalEditDelegate>
|
||||
@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 <NSDraggingInfo>)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 {
|
||||
|
||||
@@ -23,5 +23,5 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface BarMenu : NSObject <NSMenuDelegate>
|
||||
//+ (instancetype)start;
|
||||
- (void)rebuildMenu;
|
||||
@end
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
|
||||
|
||||
@interface StoreCoordinator : NSObject
|
||||
+ (void)save;
|
||||
+ (void)saveContext:(NSManagedObjectContext*)context;
|
||||
+ (void)deleteUnreferencedFeeds;
|
||||
+ (NSArray<FeedConfig*>*)sortedFeedConfigItems;
|
||||
+ (id)objectWithID:(NSManagedObjectID*)objID;
|
||||
+ (Feed*)createFeedFromDictionary:(NSDictionary*)obj;
|
||||
+ (Feed*)createFeedFromDictionary:(NSDictionary*)obj inContext:(NSManagedObjectContext*)context;
|
||||
@end
|
||||
|
||||
@@ -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"];
|
||||
|
||||
Reference in New Issue
Block a user