Refactoring UserPrefs

This commit is contained in:
relikd
2019-10-03 12:13:38 +02:00
parent b25565c74f
commit ae18e93b6a
21 changed files with 223 additions and 255 deletions

View File

@@ -123,7 +123,7 @@ defaults write de.relikd.baRSS articlesInMenuLimit -int 40
5. You can change the appearance of colors throughout the application. E.g., The tint color of the menu bar icon and the color of the blue dot of unread articles. 5. You can change the appearance of colors throughout the application. E.g., The tint color of the menu bar icon and the color of the blue dot of unread articles.
``` ```
defaults write de.relikd.baRSS colorStatusIconTint -string "#37F" defaults write de.relikd.baRSS colorStatusIconTint -string "#37F"
defaults write de.relikd.baRSS colorUnreadTickMark -string "#FBA33A" defaults write de.relikd.baRSS colorUnreadIndicator -string "#FBA33A"
``` ```

View File

@@ -407,10 +407,10 @@
54E9CF2F225913850023696F /* Helper */ = { 54E9CF2F225913850023696F /* Helper */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
54209E922117325100F3B5EF /* DrawImage.h */,
54209E932117325100F3B5EF /* DrawImage.m */,
5496B50F214D6275003ED4ED /* UserPrefs.h */, 5496B50F214D6275003ED4ED /* UserPrefs.h */,
5496B510214D6275003ED4ED /* UserPrefs.m */, 5496B510214D6275003ED4ED /* UserPrefs.m */,
54209E922117325100F3B5EF /* DrawImage.h */,
54209E932117325100F3B5EF /* DrawImage.m */,
54910065233A4D4000858AE2 /* URLScheme.h */, 54910065233A4D4000858AE2 /* URLScheme.h */,
54910066233A4D4000858AE2 /* URLScheme.m */, 54910066233A4D4000858AE2 /* URLScheme.m */,
); );
@@ -655,10 +655,7 @@
GCC_DYNAMIC_NO_PIC = NO; GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0; GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = ( GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1";
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNDECLARED_SELECTOR = YES;
@@ -750,6 +747,10 @@
"$(PROJECT_DIR)", "$(PROJECT_DIR)",
"$(PROJECT_DIR)/Carthage/Build/Mac", "$(PROJECT_DIR)/Carthage/Build/Mac",
); );
GCC_PREPROCESSOR_DEFINITIONS = (
"APP_NAME=\"\\@\\\"$(PRODUCT_NAME)\\\"\"",
"$(inherited)",
);
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
GCC_WARN_ABOUT_MISSING_NEWLINE = YES; GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
@@ -801,6 +802,10 @@
"$(PROJECT_DIR)", "$(PROJECT_DIR)",
"$(PROJECT_DIR)/Carthage/Build/Mac", "$(PROJECT_DIR)/Carthage/Build/Mac",
); );
GCC_PREPROCESSOR_DEFINITIONS = (
"APP_NAME=\"\\@\\\"$(PRODUCT_NAME)\\\"\"",
"$(inherited)",
);
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
GCC_WARN_ABOUT_MISSING_NEWLINE = YES; GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;

View File

@@ -45,6 +45,7 @@
} }
- (void)applicationWillFinishLaunching:(NSNotification *)notification { - (void)applicationWillFinishLaunching:(NSNotification *)notification {
UserPrefsInit();
RegisterImageViewNames(); RegisterImageViewNames();
_statusItem = [BarStatusItem new]; _statusItem = [BarStatusItem new];
NSAppleEventManager *appleEventManager = [NSAppleEventManager sharedAppleEventManager]; NSAppleEventManager *appleEventManager = [NSAppleEventManager sharedAppleEventManager];
@@ -70,11 +71,10 @@
[UpdateScheduler unregisterNetworkChangeNotification]; [UpdateScheduler unregisterNetworkChangeNotification];
} }
/// Called during application start. Perform any version dependent updates here /// Called during application start. Perform any version migration updates here.
- (void)migrateVersionUpdate { - (void)migrateVersionUpdate {
// Currently unused, but you'll be thankful to know the previous version number in the future // Currently unused, but you'll be thankful in the future for a previously saved version number
[UserPrefs dbUpdateFileVersion]; [StoreCoordinator setOption:@"app-version" value: UserPrefsAppVersion()];
[UserPrefs dbUpdateAppVersion];
} }

View File

@@ -64,6 +64,7 @@ static NSImageName const RSSImageMenuItemUnread = @"RSSImageMenuItemUnread";
/// Helper method calls @c (defaultCenter)postNotification: /// Helper method calls @c (defaultCenter)postNotification:
static inline void PostNotification(NSNotificationName name, id obj) { [[NSNotificationCenter defaultCenter] postNotificationName:name object:obj]; } static inline void PostNotification(NSNotificationName name, id obj) { [[NSNotificationCenter defaultCenter] postNotificationName:name object:obj]; }
/// Helper method calls @c (defaultCenter)addObserver:
static inline void RegisterNotification(NSNotificationName name, SEL action, id observer) { [[NSNotificationCenter defaultCenter] addObserver:observer selector:action name:name object:nil]; } static inline void RegisterNotification(NSNotificationName name, SEL action, id observer) { [[NSNotificationCenter defaultCenter] addObserver:observer selector:action name:name object:nil]; }
/** /**
@c notification.object is @c NSNumber of type @c NSUInteger. @c notification.object is @c NSNumber of type @c NSUInteger.

View File

@@ -63,7 +63,7 @@
+ (void)didClickOnMenuItem:(NSMenuItem*)sender { + (void)didClickOnMenuItem:(NSMenuItem*)sender {
NSString *url = [StoreCoordinator urlForFeedWithIndexPath:sender.representedObject]; NSString *url = [StoreCoordinator urlForFeedWithIndexPath:sender.representedObject];
if (url && url.length > 0) if (url && url.length > 0)
[UserPrefs openURLsWithPreferredBrowser:@[[NSURL URLWithString:url]]]; UserPrefsOpenURL(url);
} }

View File

@@ -52,20 +52,20 @@
NSString *title = self.title; NSString *title = self.title;
if (!title) return @""; if (!title) return @"";
// TODO: It should be enough to get user prefs once per menu build // TODO: It should be enough to get user prefs once per menu build
if ([UserPrefs defaultNO:@"feedShortNames"]) { if (UserPrefsBool(Pref_feedTruncateTitle)) {
NSUInteger limit = [UserPrefs shortArticleNamesLimit]; NSUInteger limit = UserPrefsUInt(Pref_shortArticleNamesLimit);
if (title.length > limit) if (title.length > limit)
title = [NSString stringWithFormat:@"%@…", [title substringToIndex:limit-1]]; title = [[title substringToIndex:limit] stringByAppendingString:@"…"];
} }
return title; return title;
} }
/// @return Fully initialized @c NSMenuItem with @c title, @c tooltip, @c tickmark, and @c action. /// @return Fully initialized @c NSMenuItem with @c title, @c tooltip, @c unread-indicator, and @c action.
- (NSMenuItem*)newMenuItem { - (NSMenuItem*)newMenuItem {
NSMenuItem *item = [NSMenuItem new]; NSMenuItem *item = [NSMenuItem new];
item.title = [self shortArticleName]; item.title = [self shortArticleName];
item.enabled = (self.link.length > 0); item.enabled = (self.link.length > 0);
item.state = (self.unread && [UserPrefs defaultYES:@"feedTickMark"] ? NSControlStateValueOn : NSControlStateValueOff); item.state = (self.unread && UserPrefsBool(Pref_feedUnreadIndicator) ? NSControlStateValueOn : NSControlStateValueOff);
item.onStateImage = [NSImage imageNamed:RSSImageMenuItemUnread]; item.onStateImage = [NSImage imageNamed:RSSImageMenuItemUnread];
item.accessibilityLabel = (self.unread ? NSLocalizedString(@"article: unread", @"accessibility label, feed menu item") : NSLocalizedString(@"article: read", @"accessibility label, feed menu item")); item.accessibilityLabel = (self.unread ? NSLocalizedString(@"article: unread", @"accessibility label, feed menu item") : NSLocalizedString(@"article: read", @"accessibility label, feed menu item"));
item.toolTip = (self.abstract ? self.abstract : self.body); // fall back to body (html) item.toolTip = (self.abstract ? self.abstract : self.body); // fall back to body (html)
@@ -83,7 +83,7 @@
NSString *url = fa.link; NSString *url = fa.link;
BOOL success = NO; BOOL success = NO;
if (url && url.length > 0 && !flipUnread) // flipUnread == change unread state if (url && url.length > 0 && !flipUnread) // flipUnread == change unread state
success = [UserPrefs openURLsWithPreferredBrowser:@[[NSURL URLWithString:url]]]; success = UserPrefsOpenURL(url);
if (flipUnread || (success && fa.unread)) { if (flipUnread || (success && fa.unread)) {
fa.unread = !fa.unread; fa.unread = !fa.unread;
[StoreCoordinator saveContext:moc andParent:YES]; [StoreCoordinator saveContext:moc andParent:YES];

View File

@@ -23,8 +23,6 @@
@import Cocoa; @import Cocoa;
#import "DBv1+CoreDataModel.h" #import "DBv1+CoreDataModel.h"
static int const dbFileVersion = 1; // update in case database structure changes
@interface StoreCoordinator : NSObject @interface StoreCoordinator : NSObject
// Managing contexts // Managing contexts
+ (NSManagedObjectContext*)getMainContext; + (NSManagedObjectContext*)getMainContext;

View File

@@ -320,8 +320,8 @@ void RegisterImageViewNames(void) {
Register(16, RSSImageSettingsGroup, NSLocalizedString(@"Group settings", nil), ^(NSRect r) { DrawGroupIcon(r, black, NO); return YES; }); Register(16, RSSImageSettingsGroup, NSLocalizedString(@"Group settings", nil), ^(NSRect r) { DrawGroupIcon(r, black, NO); return YES; });
Register(16, RSSImageSettingsFeed, NSLocalizedString(@"Feed settings", nil), ^(NSRect r) { DrawRSSIcon(r, black, NO, YES); return YES; }); Register(16, RSSImageSettingsFeed, NSLocalizedString(@"Feed settings", nil), ^(NSRect r) { DrawRSSIcon(r, black, NO, YES); return YES; });
NSColor *c1 = [UserPrefs defaultColor:orange forKey:@"colorStatusIconTint"]; NSColor *c1 = UserPrefsColor(Pref_colorStatusIconTint, orange);
NSColor *c2 = [UserPrefs defaultColor:[NSColor systemBlueColor] forKey:@"colorUnreadTickMark"]; NSColor *c2 = UserPrefsColor(Pref_colorUnreadIndicator, [NSColor systemBlueColor]);
Register(16, RSSImageMenuBarIconActive, NSLocalizedString(@"RSS menu bar icon", nil), ^(NSRect r) { DrawRSSIcon(r, c1.CGColor, YES, YES); return YES; }); Register(16, RSSImageMenuBarIconActive, NSLocalizedString(@"RSS menu bar icon", nil), ^(NSRect r) { DrawRSSIcon(r, c1.CGColor, YES, YES); return YES; });
Register(16, RSSImageMenuBarIconPaused, NSLocalizedString(@"RSS menu bar icon, paused", nil), ^(NSRect r) { DrawRSSIcon(r, c1.CGColor, YES, NO); return YES; }); Register(16, RSSImageMenuBarIconPaused, NSLocalizedString(@"RSS menu bar icon, paused", nil), ^(NSRect r) { DrawRSSIcon(r, c1.CGColor, YES, NO); return YES; });

View File

@@ -20,33 +20,91 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
#ifndef UserPrefs_h
#define UserPrefs_h
@import Cocoa; @import Cocoa;
@interface UserPrefs : NSObject // ---------------------------------------------------------------
// User Preferences Plist // | MARK: Constants
+ (BOOL)defaultYES:(NSString*)key; // ---------------------------------------------------------------
+ (BOOL)defaultNO:(NSString*)key;
+ (NSUInteger)defaultUInt:(NSUInteger)defaultInt forKey:(NSString*)key;
+ (NSString*)getHttpApplication; // ------ Preferences window ------
+ (void)setHttpApplication:(NSString*)bundleID; /** default: @c 1 */ static NSString* const Pref_prefSelectedTab = @"prefSelectedTab";
+ (BOOL)openURLsWithPreferredBrowser:(NSArray<NSURL*>*)urls; /** default: @c nil */ static NSString* const Pref_prefWindowFrame = @"prefWindowFrame";
/** default: @c nil */ static NSString* const Pref_modalSheetWidth = @"modalSheetWidth";
// ------ General settings ------ (Preferences > General Tab) ------
/** default: @c nil */ static NSString* const Pref_defaultHttpApplication = @"defaultHttpApplication";
// ------ Appearance matrix ------ (Preferences > Appearance Tab) ------
/** default: @c YES */ static NSString* const Pref_globalTintMenuIcon = @"globalTintMenuBarIcon";
/** default: @c YES */ static NSString* const Pref_globalUpdateAll = @"globalUpdateAll";
/** default: @c YES */ static NSString* const Pref_globalOpenUnread = @"globalOpenUnread";
/** default: @c YES */ static NSString* const Pref_globalMarkRead = @"globalMarkRead";
/** default: @c YES */ static NSString* const Pref_globalMarkUnread = @"globalMarkUnread";
/** default: @c YES */ static NSString* const Pref_globalUnreadCount = @"globalUnreadCount";
/** default: @c YES */ static NSString* const Pref_groupOpenUnread = @"groupOpenUnread";
/** default: @c YES */ static NSString* const Pref_groupMarkRead = @"groupMarkRead";
/** default: @c YES */ static NSString* const Pref_groupMarkUnread = @"groupMarkUnread";
/** default: @c YES */ static NSString* const Pref_groupUnreadCount = @"groupUnreadCount";
/** default: @c YES */ static NSString* const Pref_feedOpenUnread = @"feedOpenUnread";
/** default: @c YES */ static NSString* const Pref_feedMarkRead = @"feedMarkRead";
/** default: @c YES */ static NSString* const Pref_feedMarkUnread = @"feedMarkUnread";
/** default: @c YES */ static NSString* const Pref_feedUnreadCount = @"feedUnreadCount";
/** default: @c YES */ static NSString* const Pref_feedUnreadIndicator = @"feedUnreadIndicator";
/** default: @c NO */ static NSString* const Pref_feedTruncateTitle = @"feedTruncateTitle";
/** default: @c NO */ static NSString* const Pref_feedLimitArticles = @"feedLimitArticles";
// ------ Hidden preferences ------ only modifiable via `defaults write de.relikd.baRSS {KEY}` ------
/** default: @c 10 */ static NSString* const Pref_openFewLinksLimit = @"openFewLinksLimit";
/** default: @c 60 */ static NSString* const Pref_shortArticleNamesLimit = @"shortArticleNamesLimit";
/** default: @c 40 */ static NSString* const Pref_articlesInMenuLimit = @"articlesInMenuLimit";
/** default: @c nil */ static NSString* const Pref_colorStatusIconTint = @"colorStatusIconTint";
/** default: @c nil */ static NSString* const Pref_colorUnreadIndicator = @"colorUnreadIndicator";
// Hidden Plist Properties
+ (NSUInteger)openFewLinksLimit; // Change with: defaults write de.relikd.baRSS openFewLinksLimit -int 10
+ (NSUInteger)shortArticleNamesLimit; // Change with: defaults write de.relikd.baRSS shortArticleNamesLimit -int 50
+ (NSUInteger)articlesInMenuLimit; // Change with: defaults write de.relikd.baRSS articlesInMenuLimit -int 40
+ (NSColor*)defaultColor:(NSColor*)defaultColor forKey:(NSString*)key; // Change with: defaults write de.relikd.baRSS {KEY} -string "#FBA33A"
// Application Info Plist // ---------------------------------------------------------------
+ (NSString*)appName; // | MARK: - NSUserDefaults
+ (NSString*)appVersion; // ---------------------------------------------------------------
+ (NSString*)appVersionWithBuildNo;
// Core Data Properties void UserPrefsInit(void);
+ (BOOL)dbIsUnusedInitalState; NSColor* UserPrefsColor(NSString *key, NSColor *defaultColor); // Change with: defaults write de.relikd.baRSS {KEY} -string "#FBA33A"
+ (BOOL)dbIsCurrentFileVersion; // ------ Getter ------
+ (BOOL)dbIsCurrentAppVersion; /// Helper method calls @c (standardUserDefaults)boolForKey:
+ (void)dbUpdateFileVersion; static inline BOOL UserPrefsBool(NSString* const key) { return [[NSUserDefaults standardUserDefaults] boolForKey:key]; }
+ (void)dbUpdateAppVersion; /// Helper method calls @c (standardUserDefaults)integerForKey:
@end static inline NSInteger UserPrefsInt(NSString* const key) { return [[NSUserDefaults standardUserDefaults] integerForKey:key]; }
/// Helper method calls @c (standardUserDefaults)integerForKey: @return @c (NSUInteger)result
static inline NSUInteger UserPrefsUInt(NSString* const key) { return (NSUInteger)[[NSUserDefaults standardUserDefaults] integerForKey:key]; }
/// Helper method calls @c (standardUserDefaults)stringForKey:
static inline NSString* UserPrefsString(NSString* const key) { return [[NSUserDefaults standardUserDefaults] stringForKey:key]; }
// ------ Setter ------
/// Helper method calls @c (standardUserDefaults)setObject:forKey:
static inline void UserPrefsSet(NSString* const key, id value) { [[NSUserDefaults standardUserDefaults] setObject:value forKey:key]; }
/// Helper method calls @c (standardUserDefaults)setInteger:forKey:
static inline void UserPrefsSetInt(NSString* const key, NSInteger value) { [[NSUserDefaults standardUserDefaults] setInteger:value forKey:key]; }
/// Helper method calls @c (standardUserDefaults)setBool:forKey:
static inline void UserPrefsSetBool(NSString* const key, BOOL value) { [[NSUserDefaults standardUserDefaults] setBool:value forKey:key]; }
// ---------------------------------------------------------------
// | MARK: - NSBundle
// ---------------------------------------------------------------
/// Helper method calls @c (mainBundle)CFBundleShortVersionString
static inline NSString* UserPrefsAppVersion() { return [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"]; }
// ---------------------------------------------------------------
// | MARK: - Open URLs
// ---------------------------------------------------------------
/**
Open web links in default browser or a browser the user selected in the preferences.
@param urls A list of @c NSURL objects that will be opened immediatelly in bulk.
@return @c YES if @c urls are opened successfully. @c NO on error.
*/
static inline BOOL UserPrefsOpenURLs(NSArray<NSURL*> *urls) {
return [[NSWorkspace sharedWorkspace] openURLs:urls withAppBundleIdentifier:UserPrefsString(Pref_defaultHttpApplication) options:NSWorkspaceLaunchDefault additionalEventParamDescriptor:nil launchIdentifiers:nil];
}
/// Call @c UserPrefsOpenURLs() with single item array and convert string to @c NSURL
static inline BOOL UserPrefsOpenURL(NSString *url) { return UserPrefsOpenURLs(@[[NSURL URLWithString:url]]); }
#endif /* UserPrefs_h */

View File

@@ -21,72 +21,36 @@
// SOFTWARE. // SOFTWARE.
#import "UserPrefs.h" #import "UserPrefs.h"
#import "StoreCoordinator.h" #import "NSString+Ext.h" // hexColor
#import "NSString+Ext.h"
@implementation UserPrefs /// Helper method for @c UserPrefsInit()
static inline void defaultsAppend(NSMutableDictionary *defs, id value, NSArray<NSString*>* keys) {
#pragma mark - User Preferences Plist for (NSString *key in keys)
[defs setObject:value forKey:key];
/// @return @c YES if key is not set. Otherwise, return user defaults property from plist.
+ (BOOL)defaultYES:(NSString*)key {
if ([[NSUserDefaults standardUserDefaults] objectForKey:key] == NULL) {
return YES;
}
return [[NSUserDefaults standardUserDefaults] boolForKey:key];
} }
/// @return @c NO if key is not set. Otherwise, return user defaults property from plist. /// Helper method calls @c (standardUserDefaults)registerDefaults:
+ (BOOL)defaultNO:(NSString*)key { void UserPrefsInit(void) {
return [[NSUserDefaults standardUserDefaults] boolForKey:key]; NSMutableDictionary *defs = [NSMutableDictionary dictionary];
defaultsAppend(defs, @YES, @[Pref_globalTintMenuIcon,
Pref_globalUpdateAll,
Pref_globalOpenUnread, Pref_groupOpenUnread, Pref_feedOpenUnread,
Pref_globalMarkRead, Pref_groupMarkRead, Pref_feedMarkRead,
Pref_globalMarkUnread, Pref_groupMarkUnread, Pref_feedMarkUnread,
Pref_globalUnreadCount, Pref_groupUnreadCount, Pref_feedUnreadCount,
Pref_feedUnreadIndicator]);
defaultsAppend(defs, @NO, @[Pref_feedTruncateTitle,
Pref_feedLimitArticles]);
// Display limits & truncation ( defaults write de.relikd.baRSS {KEY} -int 10 )
[defs setObject:[NSNumber numberWithUnsignedInteger:10] forKey:Pref_openFewLinksLimit];
[defs setObject:[NSNumber numberWithUnsignedInteger:60] forKey:Pref_shortArticleNamesLimit];
[defs setObject:[NSNumber numberWithUnsignedInteger:40] forKey:Pref_articlesInMenuLimit];
[defs setObject:[NSNumber numberWithUnsignedInteger:1] forKey:Pref_prefSelectedTab]; // feed tab
[[NSUserDefaults standardUserDefaults] registerDefaults:defs];
} }
/// @return Return @c defaultInt if key is not set. Otherwise, return user defaults property from plist. /// @return User set value. If it wasn't modified or couldn't be parsed return @c defaultColor
+ (NSUInteger)defaultUInt:(NSUInteger)defaultInt forKey:(NSString*)key { NSColor* UserPrefsColor(NSString *key, NSColor *defaultColor) {
NSInteger ret = [[NSUserDefaults standardUserDefaults] integerForKey:key];
if (ret > 0) return (NSUInteger)ret;
return defaultInt;
}
/// @return User configured custom browser. Or @c nil if not set yet. (which will fallback to default browser)
+ (NSString*)getHttpApplication {
return [[NSUserDefaults standardUserDefaults] stringForKey:@"defaultHttpApplication"];
}
/// Store custom browser bundle id to user defaults.
+ (void)setHttpApplication:(NSString*)bundleID {
[[NSUserDefaults standardUserDefaults] setObject:bundleID forKey:@"defaultHttpApplication"];
}
/**
Open web links in default browser or a browser the user selected in the preferences.
@param urls A list of @c NSURL objects that will be opened immediatelly in bulk.
@return @c YES if @c urls are opened successfully. @c NO on error.
*/
+ (BOOL)openURLsWithPreferredBrowser:(NSArray<NSURL*>*)urls {
if (urls.count == 0) return NO;
return [[NSWorkspace sharedWorkspace] openURLs:urls withAppBundleIdentifier:[self getHttpApplication] options:NSWorkspaceLaunchDefault additionalEventParamDescriptor:nil launchIdentifiers:nil];
}
#pragma mark - Hidden Plist Properties -
/// @return The limit on how many links should be opened at the same time, if user holds the option key.
/// Default: @c 10
+ (NSUInteger)openFewLinksLimit { return [self defaultUInt:10 forKey:@"openFewLinksLimit"]; }
/// @return The limit on when to truncate article titles (Short names setting must be active).
/// Default: @c 60
+ (NSUInteger)shortArticleNamesLimit { return [self defaultUInt:60 forKey:@"shortArticleNamesLimit"]; }
/// @return The maximum number of articles displayed per feed (Limit articles setting must be active).
/// Default: @c 40
+ (NSUInteger)articlesInMenuLimit { return [self defaultUInt:40 forKey:@"articlesInMenuLimit"]; }
/// @return Returns @c defaultColor if defaults value couldn't be parsed or wasn't modified by user.
+ (NSColor*)defaultColor:(NSColor*)defaultColor forKey:(NSString*)key {
NSString *colorStr = [[NSUserDefaults standardUserDefaults] stringForKey:key]; NSString *colorStr = [[NSUserDefaults standardUserDefaults] stringForKey:key];
if (colorStr) { if (colorStr) {
NSColor *color = [colorStr hexColor]; NSColor *color = [colorStr hexColor];
@@ -96,62 +60,3 @@
} }
return defaultColor; return defaultColor;
} }
#pragma mark - Application Info Plist
/// @return The application name, e.g., 'baRSS' or 'baRSS Beta'
+ (NSString*)appName {
return [[NSBundle mainBundle] infoDictionary][(NSString*)kCFBundleNameKey];
}
/// @return The application version number, e.g., '0.9.4'
+ (NSString*)appVersion {
return [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"];
}
/// @return The application version number including build number, e.g., '0.9.4 (9906)'
+ (NSString*)appVersionWithBuildNo {
NSString *buildNo = [[NSBundle mainBundle] infoDictionary][@"CFBundleVersion"];
return [[self appVersion] stringByAppendingFormat:@" (%@)", buildNo];
}
#pragma mark - Core Data Properties -
/// Helper method that retrieves and transforms option value to int
+ (int)dbIntForKey:(NSString*)key defaultsTo:(int)otherwise {
NSString *str = [StoreCoordinator optionForKey:key];
if (!str) return otherwise;
int num = [NSDecimalNumber decimalNumberWithString:str].intValue;
return isnan(num) ? otherwise : num;
}
/// Check whether the database was just initialized (first install)
+ (BOOL)dbIsUnusedInitalState {
return [StoreCoordinator optionForKey:@"db-version"] == nil;
}
/// Check whether the stored database version is up to date
+ (BOOL)dbIsCurrentFileVersion {
return [self dbIntForKey:@"db-version" defaultsTo:-1] == dbFileVersion;
}
/// Write current database version to core data
+ (void)dbUpdateFileVersion {
[StoreCoordinator setOption:@"db-version" value:[NSString stringWithFormat:@"%d", dbFileVersion]];
}
/// Check whether the stored application version is up to date
+ (BOOL)dbIsCurrentAppVersion {
return [[StoreCoordinator optionForKey:@"app-version"] isEqualToString:[self appVersion]];
}
/// Write current application version to core data
+ (void)dbUpdateAppVersion {
[StoreCoordinator setOption:@"app-version" value:[self appVersion]];
}
@end

View File

@@ -70,7 +70,7 @@
</dict> </dict>
</array> </array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>13931</string> <string>14336</string>
<key>LSApplicationCategoryType</key> <key>LSApplicationCategoryType</key>
<string>public.app-category.news</string> <string>public.app-category.news</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>

View File

@@ -21,7 +21,6 @@
// SOFTWARE. // SOFTWARE.
#import "NSURL+Ext.h" #import "NSURL+Ext.h"
#import "UserPrefs.h" // appName in +faviconsCacheURL
#import "NSError+Ext.h" #import "NSError+Ext.h"
@implementation NSURL (Ext) @implementation NSURL (Ext)
@@ -36,7 +35,7 @@
static dispatch_once_t onceToken; static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ dispatch_once(&onceToken, ^{
path = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:nil]; path = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:nil];
path = [path URLByAppendingPathComponent:[UserPrefs appName] isDirectory:YES]; path = [path URLByAppendingPathComponent:APP_NAME isDirectory:YES];
}); });
return path; return path;
} }

View File

@@ -22,25 +22,21 @@
#import "SettingsAboutView.h" #import "SettingsAboutView.h"
#import "NSView+Ext.h" #import "NSView+Ext.h"
#import "UserPrefs.h"
@implementation SettingsAboutView @implementation SettingsAboutView
- (instancetype)init { - (instancetype)init {
self = [super initWithFrame: NSZeroRect]; self = [super initWithFrame: NSZeroRect];
NSString *name = [UserPrefs appName]; NSDictionary *info = [[NSBundle mainBundle] infoDictionary];
NSString *version = [NSString stringWithFormat:NSLocalizedString(@"Version %@", nil), NSString *version = [NSString stringWithFormat:NSLocalizedString(@"Version %@", nil), info[@"CFBundleShortVersionString"]];
#if DEBUG #if DEBUG // append build number, e.g., '0.9.4 (9906)'
[UserPrefs appVersionWithBuildNo] version = [version stringByAppendingFormat:@" (%@)", info[@"CFBundleVersion"]];
#else
[UserPrefs appVersion]
#endif #endif
];
// Application icon image (top-centered) // Application icon image (top-centered)
NSImageView *logo = [[NSView imageView:NSImageNameApplicationIcon size:64] placeIn:self x:CENTER yTop:PAD_M]; NSImageView *logo = [[NSView imageView:NSImageNameApplicationIcon size:64] placeIn:self x:CENTER yTop:PAD_M];
// Add app name // Add app name
NSTextField *lblN = [[[[NSView label:name] large] bold] placeIn:self x:CENTER yTop: YFromTop(logo) + PAD_M]; NSTextField *lblN = [[[[NSView label:APP_NAME] large] bold] placeIn:self x:CENTER yTop: YFromTop(logo) + PAD_M];
// Add version info // Add version info
NSTextField *lblV = [[[[NSView label:version] small] selectable] placeIn:self x:CENTER yTop: YFromTop(lblN) + PAD_S]; NSTextField *lblV = [[[[NSView label:version] small] selectable] placeIn:self x:CENTER yTop: YFromTop(lblN) + PAD_S];

View File

@@ -24,6 +24,7 @@
#import "SettingsAppearanceView.h" #import "SettingsAppearanceView.h"
#import "AppHook.h" #import "AppHook.h"
#import "BarStatusItem.h" #import "BarStatusItem.h"
#import "UserPrefs.h"
@implementation SettingsAppearance @implementation SettingsAppearance
@@ -41,10 +42,9 @@
/// Sync new value with UserDefaults and update status bar icon /// Sync new value with UserDefaults and update status bar icon
- (void)didSelectCheckbox:(NSButton*)sender { - (void)didSelectCheckbox:(NSButton*)sender {
BOOL state = (sender.state == NSControlStateValueOn); NSString *pref = sender.identifier;
[[NSUserDefaults standardUserDefaults] setBool:state forKey:sender.identifier]; UserPrefsSetBool(pref, (sender.state == NSControlStateValueOn));
if ([sender.identifier isEqualToString:@"globalUnreadCount"] || if (pref == Pref_globalUnreadCount || pref == Pref_globalTintMenuIcon) { // == because static string
[sender.identifier isEqualToString:@"globalTintMenuBarIcon"]) {
[[(AppHook*)NSApp statusItem] updateBarIcon]; [[(AppHook*)NSApp statusItem] updateBarIcon];
} }
} }

View File

@@ -22,67 +22,64 @@
#import "SettingsAppearanceView.h" #import "SettingsAppearanceView.h"
#import "NSView+Ext.h" #import "NSView+Ext.h"
#import "Constants.h" #import "Constants.h" // column icons
#import "UserPrefs.h" #import "UserPrefs.h" // preference constants & UserPrefsBool()
@interface SettingsAppearanceView() @interface SettingsAppearanceView()
@property (assign) NSUInteger row; @property (assign) CGFloat y;
@end @end
/***/ static CGFloat const IconSize = 18; /***/ static CGFloat const IconSize = 18;
/***/ static CGFloat const colWidth = (IconSize + PAD_M); // checkbox column width /***/ static CGFloat const colWidth = (IconSize + PAD_M); // checkbox column width
/***/ static CGFloat const X__ = PAD_WIN + 0 * colWidth;
/***/ static CGFloat const _X_ = PAD_WIN + 1 * colWidth;
/***/ static CGFloat const __X = PAD_WIN + 2 * colWidth;
@implementation SettingsAppearanceView @implementation SettingsAppearanceView
- (instancetype)init { - (instancetype)init {
self = [super initWithFrame: NSZeroRect]; self = [super initWithFrame: NSZeroRect];
self.row = 0; // Insert matrix header (icons above checkbox matrix)
// Insert matrix header (the three icons) ColumnIcon(self, X__, RSSImageSettingsGlobal, NSLocalizedString(@"Show in menu bar", nil));
[self head:0 img:RSSImageSettingsGlobal tooltip:NSLocalizedString(@"Show in menu bar", nil)]; ColumnIcon(self, _X_, RSSImageSettingsGroup, NSLocalizedString(@"Show in group menu", nil));
[self head:1 img:RSSImageSettingsGroup tooltip:NSLocalizedString(@"Show in group menu", nil)]; ColumnIcon(self, __X, RSSImageSettingsFeed, NSLocalizedString(@"Show in feed menu", nil));
[self head:2 img:RSSImageSettingsFeed tooltip:NSLocalizedString(@"Show in feed menu", nil)]; // Generate checkbox matrix
// Generate checkbox matrix (checkbox state, X: default ON, O: default OFF, blank: hidden) self.y = PAD_WIN + IconSize + PAD_S;
[self entry:"X " label:NSLocalizedString(@"Tint menu bar icon on unread", nil)]; [self entry:NSLocalizedString(@"Tint menu bar icon on unread", nil) c1:Pref_globalTintMenuIcon c2:nil c3:nil];
[self entry:"X " label:NSLocalizedString(@"Update all feeds", nil)]; [self entry:NSLocalizedString(@"Update all feeds", nil) c1:Pref_globalUpdateAll c2:nil c3:nil];
[self entry:"XXX" label:NSLocalizedString(@"Open all unread", nil)]; [self entry:NSLocalizedString(@"Open all unread", nil) c1:Pref_globalOpenUnread c2:Pref_groupOpenUnread c3:Pref_feedOpenUnread];
[self entry:"XXX" label:NSLocalizedString(@"Mark all read", nil)]; [self entry:NSLocalizedString(@"Mark all read", nil) c1:Pref_globalMarkRead c2:Pref_groupMarkRead c3:Pref_feedMarkRead];
[self entry:"XXX" label:NSLocalizedString(@"Mark all unread", nil)]; [self entry:NSLocalizedString(@"Mark all unread", nil) c1:Pref_globalMarkUnread c2:Pref_groupMarkUnread c3:Pref_feedMarkUnread];
[self entry:"XXX" label:NSLocalizedString(@"Number of unread items", nil)]; [self entry:NSLocalizedString(@"Number of unread articles", nil) c1:Pref_globalUnreadCount c2:Pref_groupUnreadCount c3:Pref_feedUnreadCount];
[self entry:" X" label:NSLocalizedString(@"Tick mark unread items", nil)]; [self entry:NSLocalizedString(@"Indicator for unread articles", nil) c1:nil c2:nil c3:Pref_feedUnreadIndicator];
[[self entry:" O" label:NSLocalizedString(@"Short article names", nil)] tooltip:NSLocalizedString(@"Truncate article title after 60 characters", nil)]; [[self entry:NSLocalizedString(@"Truncate article title", nil) c1:nil c2:nil c3:Pref_feedTruncateTitle]
[[self entry:" O" label:NSLocalizedString(@"Limit number of articles", nil)] tooltip:NSLocalizedString(@"Display at most 40 articles in feed menu", nil)]; tooltip:NSLocalizedString(@"Truncate article title after 60 characters", nil)];
[[self entry:NSLocalizedString(@"Limit number of articles", nil) c1:nil c2:nil c3:Pref_feedLimitArticles]
tooltip:NSLocalizedString(@"Display at most 40 articles in feed menu", nil)];
return self; return self;
} }
/// Helper method for matrix table header icons /// Helper method for matrix table header icons
- (void)head:(int)x img:(NSImageName)img tooltip:(NSString*)ttip { static inline void ColumnIcon(id this, CGFloat x, const NSImageName img, NSString *ttip) {
[[[NSView imageView:img size:IconSize] tooltip:ttip] placeIn:self x:PAD_WIN + x * colWidth yTop:PAD_WIN]; [[[NSView imageView:img size:IconSize] placeIn:this x:x yTop:PAD_WIN] tooltip:ttip];
}
/// Helper method for generating a checkbox
static inline NSButton* Checkbox(id this, CGFloat x, CGFloat y, NSString *key) {
NSButton *check = [[NSView checkbox: UserPrefsBool(key)] placeIn:this x:x yTop:y];
check.identifier = key;
return check;
} }
/// Create new entry with 1-3 checkboxes and a descriptive label /// Create new entry with 1-3 checkboxes and a descriptive label
- (NSTextField*)entry:(char*)m label:(NSString*)text { - (NSTextField*)entry:(NSString*)label c1:(NSString*)pref1 c2:(NSString*)pref2 c3:(NSString*)pref3 {
static char* const scope[] = { "global", "group", "feed" }; CGFloat y = self.y;
static char* const ident[] = { "TintMenuBarIcon", "UpdateAll", "OpenUnread", "MarkRead", "MarkUnread", "UnreadCount", "TickMark", "ShortNames", "LimitArticles" }; self.y += (PAD_S + HEIGHT_LABEL);
CGFloat y = PAD_WIN + IconSize + PAD_S + self.row * (PAD_S + HEIGHT_LABEL); // TODO: localize: global, group, feed
if (pref1) Checkbox(self, X__ + 2, y + 2, pref1).accessibilityLabel = [label stringByAppendingString:@" (global)"];
// Add checkboxes: row 0 - 8, col 0 - 2 if (pref2) Checkbox(self, _X_ + 2, y + 2, pref2).accessibilityLabel = [label stringByAppendingString:@" (group)"];
for (NSUInteger col = 0; col < 3; col++) { if (pref3) Checkbox(self, __X + 2, y + 2, pref3).accessibilityLabel = [label stringByAppendingString:@" (feed)"];
NSString *key = [NSString stringWithFormat:@"%s%s", scope[col], ident[self.row]]; return [[[NSView label:label] placeIn:self x:PAD_WIN + 3 * colWidth yTop:y] sizeToRight:PAD_WIN];
BOOL state;
switch (m[col]) {
case 'X': state = [UserPrefs defaultYES:key]; break;
case 'O': state = [UserPrefs defaultNO: key]; break;
default: continue; // ignore blanks
}
NSButton *check = [[NSView checkbox:state] placeIn:self x:PAD_WIN + col * colWidth + 2 yTop:y + 2]; // 2px checkbox offset
check.identifier = key;
check.accessibilityLabel = [text stringByAppendingFormat:@" (%s)", scope[col]]; // TODO: localize: global, group, feed
}
self.row += 1;
// Add label
return [[[NSView label:text] placeIn:self x:PAD_WIN + 3 * colWidth yTop:y] sizeToRight:PAD_WIN];
} }
@end @end

View File

@@ -44,7 +44,7 @@
[pop addItemWithTitle: [self applicationNameForBundleId:bundleID]]; [pop addItemWithTitle: [self applicationNameForBundleId:bundleID]];
pop.lastItem.representedObject = bundleID; pop.lastItem.representedObject = bundleID;
} }
[pop selectItemAtIndex:[pop indexOfItemWithRepresentedObject:[UserPrefs getHttpApplication]]]; [pop selectItemAtIndex:[pop indexOfItemWithRepresentedObject:UserPrefsString(Pref_defaultHttpApplication)]];
// Default RSS Reader application // Default RSS Reader application
NSString *feedBundleId = CFBridgingRelease(LSCopyDefaultHandlerForURLScheme(CFSTR("feed"))); NSString *feedBundleId = CFBridgingRelease(LSCopyDefaultHandlerForURLScheme(CFSTR("feed")));
self.view.defaultReader.objectValue = [self applicationNameForBundleId:feedBundleId]; self.view.defaultReader.objectValue = [self applicationNameForBundleId:feedBundleId];
@@ -65,7 +65,7 @@
// Callback method fired when user selects a different item from popup list // Callback method fired when user selects a different item from popup list
- (void)changeHttpApplication:(NSPopUpButton *)sender { - (void)changeHttpApplication:(NSPopUpButton *)sender {
[UserPrefs setHttpApplication:sender.selectedItem.representedObject]; UserPrefsSet(Pref_defaultHttpApplication, sender.selectedItem.representedObject);
} }
// Callback method from round help button right of default feed reader text // Callback method from round help button right of default feed reader text

View File

@@ -21,6 +21,7 @@
// SOFTWARE. // SOFTWARE.
#import "ModalSheet.h" #import "ModalSheet.h"
#import "UserPrefs.h"
#import "NSView+Ext.h" #import "NSView+Ext.h"
@interface ModalSheet() @interface ModalSheet()
@@ -35,7 +36,7 @@
static NSInteger const maxWidth = 1200; static NSInteger const maxWidth = 1200;
static CGFloat const contentOffsetY = PAD_WIN + HEIGHT_BUTTON + PAD_L; static CGFloat const contentOffsetY = PAD_WIN + HEIGHT_BUTTON + PAD_L;
NSInteger w = [[NSUserDefaults standardUserDefaults] integerForKey:@"modalSheetWidth"]; NSInteger w = UserPrefsInt(Pref_modalSheetWidth);
if (w < minWidth) w = minWidth; if (w < minWidth) w = minWidth;
else if (w > maxWidth) w = maxWidth; else if (w > maxWidth) w = maxWidth;
@@ -90,8 +91,9 @@
return; return;
} }
// Save modal view width for next time // Save modal view width for next time
CGFloat w = NSWidth(self.contentView.frame) - 2 * PAD_WIN; NSInteger width = (NSInteger)(NSWidth(self.contentView.frame) - 2 * PAD_WIN);
[[NSUserDefaults standardUserDefaults] setInteger:(NSInteger)w forKey:@"modalSheetWidth"]; if (UserPrefsInt(Pref_modalSheetWidth) != width)
UserPrefsSetInt(Pref_modalSheetWidth, width);
// Remove subviews to avoid _NSKeyboardFocusClipView issues // Remove subviews to avoid _NSKeyboardFocusClipView issues
[self.contentView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; [self.contentView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
[self.sheetParent endSheet:self returnCode:(successful ? NSModalResponseOK : NSModalResponseCancel)]; [self.sheetParent endSheet:self returnCode:(successful ? NSModalResponseOK : NSModalResponseCancel)];

View File

@@ -49,13 +49,13 @@
flexibleWidth, flexibleWidth,
TabItem(NSImageNameInfo, NSLocalizedString(@"About", nil), [SettingsAbout class]), TabItem(NSImageNameInfo, NSLocalizedString(@"About", nil), [SettingsAbout class]),
]; ];
[self switchToTab:[UserPrefs defaultUInt:0 forKey:@"preferencesTab"]]; [self switchToTab: UserPrefsUInt(Pref_prefSelectedTab)];
} }
return self; return self;
} }
/// Helper method to generate tab item with image, label, and controller. /// Helper method to generate tab item with image, label, and controller.
static NSTabViewItem* TabItem(NSImageName imageName, NSString *text, Class class) { static inline NSTabViewItem* TabItem(NSImageName imageName, NSString *text, Class class) {
NSTabViewItem *item = [NSTabViewItem tabViewItemWithViewController: [class new]]; NSTabViewItem *item = [NSTabViewItem tabViewItemWithViewController: [class new]];
item.image = [NSImage imageNamed:imageName]; item.image = [NSImage imageNamed:imageName];
item.label = text; item.label = text;
@@ -76,10 +76,9 @@ static NSTabViewItem* TabItem(NSImageName imageName, NSString *text, Class class
/// Delegate method, store last selected tab to user preferences /// Delegate method, store last selected tab to user preferences
- (void)tabView:(NSTabView*)tabView didSelectTabViewItem:(nullable NSTabViewItem*)tabViewItem { - (void)tabView:(NSTabView*)tabView didSelectTabViewItem:(nullable NSTabViewItem*)tabViewItem {
[super tabView:tabView didSelectTabViewItem:tabViewItem]; [super tabView:tabView didSelectTabViewItem:tabViewItem];
NSInteger prevIndex = [[NSUserDefaults standardUserDefaults] integerForKey:@"preferencesTab"];
NSInteger newIndex = self.selectedTabViewItemIndex; NSInteger newIndex = self.selectedTabViewItemIndex;
if (prevIndex != newIndex) if (UserPrefsInt(Pref_prefSelectedTab) != newIndex)
[[NSUserDefaults standardUserDefaults] setInteger:newIndex forKey:@"preferencesTab"]; UserPrefsSetInt(Pref_prefSelectedTab, newIndex);
} }
@end @end
@@ -95,7 +94,7 @@ static NSTabViewItem* TabItem(NSImageName imageName, NSString *text, Class class
w.title = [NSString stringWithFormat:NSLocalizedString(@"%@ Preferences", nil), NSProcessInfo.processInfo.processName]; w.title = [NSString stringWithFormat:NSLocalizedString(@"%@ Preferences", nil), NSProcessInfo.processInfo.processName];
w.contentViewController = [PrefTabs new]; w.contentViewController = [PrefTabs new];
w.delegate = w; w.delegate = w;
NSWindowPersistableFrameDescriptor prevFrame = [[NSUserDefaults standardUserDefaults] stringForKey:@"prefWindow"]; NSWindowPersistableFrameDescriptor prevFrame = UserPrefsString(Pref_prefWindowFrame);
if (!prevFrame) { if (!prevFrame) {
[w setContentSize:NSMakeSize(320, 327)]; [w setContentSize:NSMakeSize(320, 327)];
[w center]; [w center];
@@ -111,7 +110,7 @@ static NSTabViewItem* TabItem(NSImageName imageName, NSString *text, Class class
} }
- (void)windowWillClose:(NSNotification *)notification { - (void)windowWillClose:(NSNotification *)notification {
[[NSUserDefaults standardUserDefaults] setObject:self.stringWithSavedFrame forKey:@"prefWindow"]; UserPrefsSet(Pref_prefWindowFrame, self.stringWithSavedFrame);
} }
/// Do not respond to Cmd-Z and Cmd-Shift-Z. Will be handled in subview controllers. /// Do not respond to Cmd-Z and Cmd-Shift-Z. Will be handled in subview controllers.

View File

@@ -105,14 +105,14 @@
/// Generate items for @c FeedArticles menu. /// Generate items for @c FeedArticles menu.
- (void)setArticles:(NSArray<FeedArticle*>*)sortedList forMenu:(NSMenu*)menu { - (void)setArticles:(NSArray<FeedArticle*>*)sortedList forMenu:(NSMenu*)menu {
[menu insertDefaultHeader]; [menu insertDefaultHeader];
NSUInteger mc = 0; NSInteger mc = NSIntegerMax;
if ([UserPrefs defaultNO:@"feedLimitArticles"]) { if (UserPrefsBool(Pref_feedLimitArticles))
mc = [UserPrefs articlesInMenuLimit]; mc = UserPrefsInt(Pref_articlesInMenuLimit);
}
for (FeedArticle *fa in sortedList) { for (FeedArticle *fa in sortedList) {
[menu addItem:[fa newMenuItem]]; if (--mc < 0) // mc == 0 will first decrement to -1, then evaluate
if (--mc == 0) // if mc==0 then unsigned int will underflow and turn into INT_MAX
break; break;
[menu addItem:[fa newMenuItem]];
} }
UnreadTotal *uct = self.unreadMap[menu.titleIndexPath]; UnreadTotal *uct = self.unreadMap[menu.titleIndexPath];
[menu setHeaderHasUnread:(uct.unread > 0) hasRead:(uct.unread < uct.total)]; [menu setHeaderHasUnread:(uct.unread > 0) hasRead:(uct.unread < uct.total)];

View File

@@ -118,18 +118,18 @@
- (void)updateBarIcon { - (void)updateBarIcon {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
BOOL hasNet = [UpdateScheduler allowNetworkConnection]; BOOL hasNet = [UpdateScheduler allowNetworkConnection];
BOOL tint = (self.unreadCountTotal > 0 && hasNet && [UserPrefs defaultYES:@"globalTintMenuBarIcon"]); BOOL tint = (self.unreadCountTotal > 0 && hasNet && UserPrefsBool(Pref_globalTintMenuIcon));
self.statusItem.image = [NSImage imageNamed:(hasNet ? RSSImageMenuBarIconActive : RSSImageMenuBarIconPaused)]; self.statusItem.image = [NSImage imageNamed:(hasNet ? RSSImageMenuBarIconActive : RSSImageMenuBarIconPaused)];
self.statusItem.image.template = !tint; self.statusItem.image.template = !tint;
BOOL showCount = (self.unreadCountTotal > 0 && [UserPrefs defaultYES:@"globalUnreadCount"]); BOOL showCount = (self.unreadCountTotal > 0 && UserPrefsBool(Pref_globalUnreadCount));
self.statusItem.title = (showCount ? [NSString stringWithFormat:@"%ld", self.unreadCountTotal] : @""); self.statusItem.title = (showCount ? [NSString stringWithFormat:@"%ld", self.unreadCountTotal] : @"");
}); });
} }
/// Show popover with a brief notice that baRSS is running in the menu bar /// Show popover with a brief notice that baRSS is running in the menu bar
- (void)showWelcomeMessage { - (void)showWelcomeMessage {
NSString *title = [NSString stringWithFormat:NSLocalizedString(@"Welcome to %@", nil), [UserPrefs appName]]; NSString *title = [NSString stringWithFormat:NSLocalizedString(@"Welcome to %@", nil), APP_NAME];
NSString *message = NSLocalizedString(@"There's no application window.\nEverything is up there.", nil); NSString *message = NSLocalizedString(@"There's no application window.\nEverything is up there.", nil);
NSTextField *head = [[NSView label:title] bold]; NSTextField *head = [[NSView label:title] bold];
NSTextField *body = [[NSView label:message] small]; NSTextField *body = [[NSView label:message] small];
@@ -172,7 +172,7 @@
if ([UpdateScheduler isPaused]) if ([UpdateScheduler isPaused])
pause.title = NSLocalizedString(@"Resume Updates", nil); pause.title = NSLocalizedString(@"Resume Updates", nil);
// 'Update all feeds' item // 'Update all feeds' item
if ([UserPrefs defaultYES:@"globalUpdateAll"]) { if (UserPrefsBool(Pref_globalUpdateAll)) {
NSMenuItem *updateAll = [menu addItemWithTitle:NSLocalizedString(@"Update all feeds", nil) action:@selector(updateAllFeeds) keyEquivalent:@""]; NSMenuItem *updateAll = [menu addItemWithTitle:NSLocalizedString(@"Update all feeds", nil) action:@selector(updateAllFeeds) keyEquivalent:@""];
updateAll.target = self; updateAll.target = self;
updateAll.enabled = [UpdateScheduler allowNetworkConnection]; updateAll.enabled = [UpdateScheduler allowNetworkConnection];

View File

@@ -88,7 +88,8 @@ typedef NS_ENUM(NSInteger, MenuItemTag) {
self.autoenablesItems = NO; self.autoenablesItems = NO;
NSMenuItem *itm = [self addItemIfAllowed:TagOpenAllUnread title:NSLocalizedString(@"Open all unread", nil)]; NSMenuItem *itm = [self addItemIfAllowed:TagOpenAllUnread title:NSLocalizedString(@"Open all unread", nil)];
if (itm) { if (itm) {
[self addItem:[itm alternateWithTitle:[NSString stringWithFormat:NSLocalizedString(@"Open a few unread (%lu)", nil), [UserPrefs openFewLinksLimit]]]]; NSString *altTitle = [NSString stringWithFormat:NSLocalizedString(@"Open a few unread (%lu)", nil), UserPrefsUInt(Pref_openFewLinksLimit)];
[self addItem:[itm alternateWithTitle:altTitle]];
} }
[self addItemIfAllowed:TagMarkAllRead title:NSLocalizedString(@"Mark all read", nil)]; [self addItemIfAllowed:TagMarkAllRead title:NSLocalizedString(@"Mark all read", nil)];
[self addItemIfAllowed:TagMarkAllUnread title:NSLocalizedString(@"Mark all unread", nil)]; [self addItemIfAllowed:TagMarkAllUnread title:NSLocalizedString(@"Mark all unread", nil)];
@@ -151,10 +152,16 @@ typedef NS_ENUM(NSInteger, MenuItemTag) {
/// Check user preferences for preferred display style. /// Check user preferences for preferred display style.
- (BOOL)allowDisplayOfHeaderItem:(MenuItemTag)tag { - (BOOL)allowDisplayOfHeaderItem:(MenuItemTag)tag {
static char* const A[] = {"", "global", "feed", "group"}; static NSString* const mr[] = {Pref_globalMarkRead, Pref_groupMarkRead, Pref_feedMarkRead};
static char* const B[] = {"", "MarkRead", "MarkUnread", "OpenUnread"}; static NSString* const mu[] = {Pref_globalMarkUnread, Pref_groupMarkUnread, Pref_feedMarkUnread};
int idx = (self.isMainMenu ? 1 : (self.isFeedMenu ? 2 : 3)); static NSString* const ou[] = {Pref_globalOpenUnread, Pref_groupOpenUnread, Pref_feedOpenUnread};
return [UserPrefs defaultYES:[NSString stringWithFormat:@"%s%s", A[idx], B[tag & 3]]]; // first 2 bits int i = (self.isMainMenu ? 0 : (self.isFeedMenu ? 2 : 1));
switch (tag) {
case TagMarkAllRead: return UserPrefsBool(mr[i]);
case TagMarkAllUnread: return UserPrefsBool(mu[i]);
case TagOpenAllUnread: return UserPrefsBool(ou[i]);
default: return NO;
}
} }
/// Check user preferences if item should be displayed in menu. If so, add it to the menu and set callback to @c self. /// Check user preferences if item should be displayed in menu. If so, add it to the menu and set callback to @c self.
@@ -176,7 +183,7 @@ typedef NS_ENUM(NSInteger, MenuItemTag) {
NSUInteger limit = 0; NSUInteger limit = 0;
if (sender.tag == TagOpenAllUnread) { if (sender.tag == TagOpenAllUnread) {
if (sender.isAlternate) if (sender.isAlternate)
limit = [UserPrefs openFewLinksLimit]; limit = UserPrefsUInt(Pref_openFewLinksLimit);
openLinks = YES; openLinks = YES;
} else if (sender.tag != TagMarkAllRead && sender.tag != TagMarkAllUnread) { } else if (sender.tag != TagMarkAllRead && sender.tag != TagMarkAllUnread) {
return; // other menu item clicked. abort and return. return; // other menu item clicked. abort and return.
@@ -200,7 +207,8 @@ typedef NS_ENUM(NSInteger, MenuItemTag) {
if (fa.link.length > 0) if (fa.link.length > 0)
[urls addObject:[NSURL URLWithString:fa.link]]; [urls addObject:[NSURL URLWithString:fa.link]];
} }
success = [UserPrefs openURLsWithPreferredBrowser:urls]; if (urls.count > 0)
success = UserPrefsOpenURLs(urls);
} }
// if success == NO, do not modify unread state // if success == NO, do not modify unread state
if (!openLinks || success) { if (!openLinks || success) {
@@ -242,7 +250,7 @@ typedef NS_ENUM(NSInteger, MenuItemTag) {
if (loc != NSNotFound) if (loc != NSNotFound)
self.title = [self.title substringToIndex:loc]; self.title = [self.title substringToIndex:loc];
} }
if (count > 0 && [UserPrefs defaultYES:(self.submenu.isFeedMenu ? @"feedUnreadCount" : @"groupUnreadCount")]) { if (count > 0 && UserPrefsBool(self.submenu.isFeedMenu ? Pref_feedUnreadCount : Pref_groupUnreadCount)) {
self.tag = TagTitleCountVisible; // apply new mask self.tag = TagTitleCountVisible; // apply new mask
self.title = [self.title stringByAppendingFormat:@" (%ld)", count]; self.title = [self.title stringByAppendingFormat:@" (%ld)", count];
} }