Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c9362b42e | ||
|
|
9af191834e | ||
|
|
8d2e4e4383 | ||
|
|
473d4b6057 | ||
|
|
cee3780f71 | ||
|
|
58d7660b87 |
17
CHANGELOG.md
17
CHANGELOG.md
@@ -7,6 +7,19 @@ and this project does adhere to [Semantic Versioning](https://semver.org/spec/v2
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
|
||||
## [1.0.2] - 2019-10-25
|
||||
### Fixed
|
||||
- *Status Bar Menu*: Preferences could not be opened on macOS 10.15
|
||||
- *Status Bar Menu*: Menu flickering resulting in a hang on macOS 10.15
|
||||
- *UI*: Text color in `About` tab
|
||||
|
||||
|
||||
## [1.0.1] - 2019-10-04
|
||||
### Fixed
|
||||
- Crash on macOS 10.14 due to a `CGColorRef` null pointer
|
||||
|
||||
|
||||
## [1.0.0] - 2019-10-03
|
||||
### Added
|
||||
- App Signing
|
||||
@@ -115,7 +128,9 @@ and this project does adhere to [Semantic Versioning](https://semver.org/spec/v2
|
||||
Initial release
|
||||
|
||||
|
||||
[Unreleased]: https://github.com/relikd/baRSS/compare/v1.0.0...HEAD
|
||||
[Unreleased]: https://github.com/relikd/baRSS/compare/v1.0.2...HEAD
|
||||
[1.0.2]: https://github.com/relikd/baRSS/compare/v1.0.1...v1.0.2
|
||||
[1.0.1]: https://github.com/relikd/baRSS/compare/v1.0.0...v1.0.1
|
||||
[1.0.0]: https://github.com/relikd/baRSS/compare/v0.9.4...v1.0.0
|
||||
[0.9.4]: https://github.com/relikd/baRSS/compare/v0.9.3...v0.9.4
|
||||
[0.9.3]: https://github.com/relikd/baRSS/compare/v0.9.2...v0.9.3
|
||||
|
||||
95
README.md
95
README.md
@@ -31,39 +31,6 @@ But it will reuse `ETag` and `Last-Modified` headers to avoid unnecessary transm
|
||||
Further, tuning the update frequently will decrease the traffic even more.
|
||||
|
||||
|
||||
### Why create something that already existed?
|
||||
|
||||
First, open source is awesome!
|
||||
Secondly, RSS Menu made some design decisions I didn't like.
|
||||
For example, the new integrated browser window.
|
||||
|
||||
One thing I liked most, was the fact that feeds were opened in the default browser.
|
||||
Not like 99% of the other feed readers on the market that show a separate HTML viewer window.
|
||||
No rendering issues, no broken links, no content that is different from the actual news article.
|
||||
|
||||
I know, the whole purpose of RSS is to deliver content without the need of opening a webpage.
|
||||
But for me RSS is more about being informed whenever a blog or news feed has some updated content.
|
||||
E.g, subscribing to video channels without having to have an account.
|
||||
|
||||
|
||||
### Why is this project not written in Swift?!
|
||||
|
||||
Actually, I started this project with Swift.
|
||||
Even without adding much functionality, the app was exceeding the 10 Mb file size.
|
||||
The working alpha version, written in Objective-C, had only 500 Kb.
|
||||
The reason being that Swift frameworks are always packed into the final application.
|
||||
|
||||
Sadly, this was before Swift 5 and ABI stability.
|
||||
Had I only started the project a year later…
|
||||
But on the other hand, now it is macOS 10.12 compatible.
|
||||
|
||||
|
||||
### 3rd Party Libraries
|
||||
|
||||
This project uses a modified version of Brent Simmons [RSXML](https://github.com/brentsimmons/RSXML) for feed parsing.
|
||||
RSXML is licensed under a MIT license (same as this project).
|
||||
|
||||
|
||||
|
||||
Download & Install
|
||||
------------------
|
||||
@@ -71,19 +38,19 @@ Download & Install
|
||||
Requires macOS Sierra (10.12) or higher.
|
||||
|
||||
### Easy way
|
||||
Go to [releases](https://github.com/relikd/baRSS/releases) and downloaded the latest version.
|
||||
Go to [releases](https://github.com/relikd/baRSS/releases) and downloaded the latest version.
|
||||
Searching for the App Store release? Read this [notice](#app-store-notice).
|
||||
|
||||
### Build from source
|
||||
|
||||
You'll need Xcode and [Carthage](https://github.com/Carthage/Carthage#installing-carthage).
|
||||
The latter is optional, you can build the [RSXML2](https://github.com/relikd/RSXML2) library from source instead.
|
||||
The latter is optional, you can build the [RSXML2] library from source instead.
|
||||
Carthage just makes it more convenient.
|
||||
Download and unzip this project, navigate to the root folder and run `carthage bootstrap --platform macOS`.
|
||||
|
||||
Next, you need to clone [QLOPML](https://github.com/relikd/QLOPML) in the same folder where this project is.
|
||||
Alternatively, you can simply delete the `QLOPML` project reference without much harm.
|
||||
`QLOPML` is a Quick Look plugin for `.opml` files.
|
||||
It will display the file contents whenever you hit space.
|
||||
It will display the file contents whenever you hit spacebar.
|
||||
|
||||
That's it.
|
||||
Open Xcode and build the project.
|
||||
@@ -160,9 +127,61 @@ I may postpone some until demand increases …
|
||||
|
||||
|
||||
|
||||
FAQ / Q&A
|
||||
---------
|
||||
|
||||
### App Store Notice
|
||||
|
||||
In the last couple of months I prepared baRSS to be released on the App Store.
|
||||
With sandboxing enabled and hardened runtime environment, etc.
|
||||
|
||||
But, for the time being, I decided to not publish this app for political reasons.
|
||||
I was not happy about some decisions made in the last weeks.
|
||||
Decisions that were evaluated on monetary aspects and not on ethical considerations.
|
||||
I won't support this conduct with my own money.
|
||||
|
||||
If you find this app somewhere on the App Store, you can be sure that it is a counterfeit.
|
||||
As long as you can read this very notice, I am not responsible for the publication.
|
||||
Further, I can't guarantee the App Store version wasn't modified by a malicious actor to spy on you.
|
||||
|
||||
|
||||
### Why create something that already existed?
|
||||
|
||||
First, open source is awesome!
|
||||
Secondly, RSS Menu made some design decisions I didn't like.
|
||||
For example, the new integrated browser window.
|
||||
|
||||
One thing I liked most, was the fact that feeds were opened in the default browser.
|
||||
Not like 99% of the other feed readers on the market that show a separate HTML viewer window.
|
||||
No rendering issues, no broken links, no content that is different from the actual news article.
|
||||
|
||||
I know, the whole purpose of RSS is to deliver content without the need of opening a webpage.
|
||||
But for me RSS is more about being informed whenever a blog or news feed has some updated content.
|
||||
E.g, subscribing to video channels without having to have an account.
|
||||
|
||||
|
||||
### Why is this project not written in Swift?!
|
||||
|
||||
Actually, I started this project with Swift.
|
||||
Even without adding much functionality, the app was exceeding the 10 Mb file size.
|
||||
The working alpha version, written in Objective-C, had only 500 Kb.
|
||||
The reason being that Swift frameworks are always packed into the final application.
|
||||
|
||||
Sadly, this was before Swift 5 and ABI stability.
|
||||
Had I only started the project a year later…
|
||||
But on the other hand, now it is macOS 10.12 compatible.
|
||||
|
||||
### 3rd Party Libraries
|
||||
|
||||
This project uses a modified version of Brent Simmons' [RSXML](https://github.com/brentsimmons/RSXML) for feed parsing.
|
||||
[RSXML2] is licensed under a MIT license (same as this project).
|
||||
|
||||
|
||||
##### Trivia
|
||||
|
||||
- Start of project: __July 19, 2018__
|
||||
- Estimated development time: __1940h+__
|
||||
- Estimated development time: __1953h+__
|
||||
- First prototype used __feedparser python__ library
|
||||
|
||||
|
||||
[RSXML2]: https://github.com/relikd/RSXML2
|
||||
|
||||
@@ -702,6 +702,7 @@
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
|
||||
@@ -80,5 +80,10 @@
|
||||
return [NSString stringWithFormat:@"http://i.ytimg.com/vi/%@/hqdefault.jpg", videoid];
|
||||
}
|
||||
|
||||
/// @return @c http://i.ytimg.com/vi/<videoid>/maxresdefault.jpg
|
||||
+ (NSString*)videoImage4k:(NSString*)videoid {
|
||||
return [NSString stringWithFormat:@"http://i.ytimg.com/vi/%@/maxresdefault.jpg", videoid];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -312,17 +312,14 @@ static void Register(CGFloat size, NSImageName name, NSString *description, BOOL
|
||||
|
||||
/// Register all icons that require custom drawing in @c ImageNamed cache
|
||||
void RegisterImageViewNames(void) {
|
||||
const CGColorRef black = [NSColor controlTextColor].CGColor;
|
||||
NSColor* const orange = [NSColor colorWithCalibratedRed:251/255.f green:163/255.f blue:58/255.f alpha:1.f]; // #FBA33A
|
||||
|
||||
Register(16, RSSImageDefaultRSSIcon, NSLocalizedString(@"RSS icon", nil), ^(NSRect r) { DrawRSSGradientIcon(r, orange); return YES; });
|
||||
Register(16, RSSImageSettingsGlobal, NSLocalizedString(@"Global settings", nil), ^(NSRect r) { DrawGlobalIcon(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; });
|
||||
|
||||
NSColor *orange = [NSColor colorWithCalibratedRed:251/255.f green:163/255.f blue:58/255.f alpha:1.f]; // #FBA33A
|
||||
NSColor *c1 = UserPrefsColor(Pref_colorStatusIconTint, orange);
|
||||
NSColor *c2 = UserPrefsColor(Pref_colorUnreadIndicator, [NSColor systemBlueColor]);
|
||||
|
||||
Register(16, RSSImageDefaultRSSIcon, NSLocalizedString(@"RSS icon", nil), ^(NSRect r) { DrawRSSGradientIcon(r, orange); return YES; });
|
||||
Register(16, RSSImageSettingsGlobal, NSLocalizedString(@"Global settings", nil), ^(NSRect r) { DrawGlobalIcon(r, [NSColor controlTextColor].CGColor, NO); return YES; });
|
||||
Register(16, RSSImageSettingsGroup, NSLocalizedString(@"Group settings", nil), ^(NSRect r) { DrawGroupIcon(r, [NSColor controlTextColor].CGColor, NO); return YES; });
|
||||
Register(16, RSSImageSettingsFeed, NSLocalizedString(@"Feed settings", nil), ^(NSRect r) { DrawRSSIcon(r, [NSColor controlTextColor].CGColor, NO, 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(14, RSSImageMenuItemUnread, NSLocalizedString(@"Unread icon", nil), ^(NSRect r) { DrawUnreadIcon(r, c2); return YES; });
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0.0</string>
|
||||
<string>1.0.2</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
@@ -70,7 +70,7 @@
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>14360</string>
|
||||
<string>14471</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.news</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
|
||||
@@ -142,7 +142,7 @@
|
||||
NSProgressIndicator *spin = [[NSProgressIndicator alloc] initWithFrame: NSMakeRect(0, 0, HEIGHT_SPINNER, HEIGHT_SPINNER)];
|
||||
spin.indeterminate = YES;
|
||||
spin.displayedWhenStopped = NO;
|
||||
spin.style = NSProgressIndicatorSpinningStyle;
|
||||
spin.style = NSProgressIndicatorStyleSpinning;
|
||||
spin.controlSize = NSControlSizeSmall;
|
||||
return spin;
|
||||
}
|
||||
|
||||
@@ -74,7 +74,8 @@
|
||||
/// Helper method to insert attributed (bold) text
|
||||
- (void)str:(NSMutableAttributedString*)parent add:(NSString*)text bold:(BOOL)flag {
|
||||
NSFont *font = [NSFont systemFontOfSize:NSFont.systemFontSize weight:(flag ? NSFontWeightMedium : NSFontWeightLight)];
|
||||
[parent appendAttributedString:[[NSAttributedString alloc] initWithString:NonLocalized(text) attributes:@{ NSFontAttributeName : font }]];
|
||||
NSDictionary *style = @{ NSFontAttributeName: font, NSForegroundColorAttributeName: [NSColor controlTextColor] };
|
||||
[parent appendAttributedString:[[NSAttributedString alloc] initWithString:NonLocalized(text) attributes:style]];
|
||||
}
|
||||
|
||||
/// Helper method to insert attributed hyperlink text
|
||||
|
||||
@@ -24,20 +24,20 @@
|
||||
@class ModalFeedEdit;
|
||||
|
||||
@interface ModalFeedEditView : NSView
|
||||
@property (weak) IBOutlet NSTextField *url;
|
||||
@property (weak) IBOutlet NSProgressIndicator *spinnerURL;
|
||||
@property (weak) IBOutlet NSImageView *favicon;
|
||||
@property (strong) IBOutlet NSTextField *url;
|
||||
@property (strong) IBOutlet NSProgressIndicator *spinnerURL;
|
||||
@property (strong) IBOutlet NSImageView *favicon;
|
||||
|
||||
@property (weak) IBOutlet NSTextField *name;
|
||||
@property (weak) IBOutlet NSProgressIndicator *spinnerName;
|
||||
@property (strong) IBOutlet NSTextField *name;
|
||||
@property (strong) IBOutlet NSProgressIndicator *spinnerName;
|
||||
|
||||
@property (weak) IBOutlet NSTextField *refreshNum;
|
||||
@property (weak) IBOutlet NSPopUpButton *refreshUnit;
|
||||
@property (strong) IBOutlet NSTextField *refreshNum;
|
||||
@property (strong) IBOutlet NSPopUpButton *refreshUnit;
|
||||
|
||||
@property (weak) IBOutlet NSButton *warningButton;
|
||||
@property (strong) IBOutlet NSButton *warningButton;
|
||||
@property NSPopover *warningPopover;
|
||||
@property (weak) IBOutlet NSTextField *warningText;
|
||||
@property (weak) IBOutlet NSButton *warningReload;
|
||||
@property (strong) IBOutlet NSTextField *warningText;
|
||||
@property (strong) IBOutlet NSButton *warningReload;
|
||||
|
||||
- (instancetype)initWithController:(ModalFeedEdit*)controller NS_DESIGNATED_INITIALIZER;
|
||||
- (instancetype)initWithFrame:(NSRect)frameRect NS_UNAVAILABLE;
|
||||
|
||||
@@ -24,9 +24,9 @@
|
||||
@class SettingsFeeds;
|
||||
|
||||
@interface SettingsFeedsView : NSView
|
||||
@property (weak) IBOutlet NSOutlineView *outline;
|
||||
@property (weak) IBOutlet NSTextField *status;
|
||||
@property (weak) IBOutlet NSProgressIndicator *spinner;
|
||||
@property (strong) IBOutlet NSOutlineView *outline;
|
||||
@property (strong) IBOutlet NSTextField *status;
|
||||
@property (strong) IBOutlet NSProgressIndicator *spinner;
|
||||
|
||||
- (instancetype)initWithController:(SettingsFeeds*)delegate NS_DESIGNATED_INITIALIZER;
|
||||
- (instancetype)initWithFrame:(NSRect)frameRect NS_UNAVAILABLE;
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
@class SettingsGeneral;
|
||||
|
||||
@interface SettingsGeneralView : NSView
|
||||
@property (weak) IBOutlet NSPopUpButton* popupHttpApplication;
|
||||
@property (weak) IBOutlet NSTextField *defaultReader;
|
||||
@property (strong) IBOutlet NSPopUpButton* popupHttpApplication;
|
||||
@property (strong) IBOutlet NSTextField *defaultReader;
|
||||
|
||||
- (instancetype)initWithController:(SettingsGeneral*)controller NS_DESIGNATED_INITIALIZER;
|
||||
- (instancetype)initWithFrame:(NSRect)frameRect NS_UNAVAILABLE;
|
||||
|
||||
@@ -82,11 +82,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
/// Get rid of everything that is not needed.
|
||||
- (void)menuDidClose:(NSMenu*)menu {
|
||||
[menu cleanup];
|
||||
}
|
||||
|
||||
/// Generate items for @c FeedGroup menu.
|
||||
- (void)setFeedGroups:(NSArray<FeedGroup*>*)sortedList forMenu:(NSMenu*)menu {
|
||||
[menu insertDefaultHeader];
|
||||
@@ -125,17 +120,21 @@
|
||||
Fetch @c Feed from core data and find deepest visible @c NSMenuItem.
|
||||
@warning @c item and @c feed will often mismatch.
|
||||
*/
|
||||
- (void)updateFeedMenuItem:(NSManagedObjectID*)oid withBlock:(void(^)(Feed *feed, NSMenuItem *item))block {
|
||||
Feed *feed = [[StoreCoordinator getMainContext] objectWithID:oid];
|
||||
if ([feed isKindOfClass:[Feed class]]) {
|
||||
NSMenuItem *item = [self.statusItem.mainMenu deepestItemWithPath:feed.indexPath];
|
||||
if (item) block(feed, item);
|
||||
}
|
||||
- (BOOL)findDeepest:(NSManagedObjectID*)oid feed:(Feed*__autoreleasing*)feed menuItem:(NSMenuItem*__autoreleasing*)item {
|
||||
Feed *f = [[StoreCoordinator getMainContext] objectWithID:oid];
|
||||
if (![f isKindOfClass:[Feed class]]) return NO;
|
||||
NSMenuItem *mi = [self.statusItem.mainMenu deepestItemWithPath:f.indexPath];
|
||||
if (!mi) return NO;
|
||||
*feed = f;
|
||||
*item = mi;
|
||||
return YES;
|
||||
}
|
||||
|
||||
/// Callback method fired when feed has been updated in the background.
|
||||
- (void)articlesUpdated:(NSNotification*)notify {
|
||||
[self updateFeedMenuItem:notify.object withBlock:^(Feed *feed, NSMenuItem *item) {
|
||||
Feed *feed;
|
||||
NSMenuItem *item;
|
||||
if ([self findDeepest:notify.object feed:&feed menuItem:&item]) {
|
||||
// 1. update in-memory unread count
|
||||
UnreadTotal *updated = [UnreadTotal new];
|
||||
updated.total = feed.articles.count;
|
||||
@@ -160,15 +159,17 @@
|
||||
[item setTitleCount:uct.unread];
|
||||
item = item.parentItem;
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
/// Callback method fired when feed icon has changed.
|
||||
- (void)feedIconUpdated:(NSNotification*)notify {
|
||||
[self updateFeedMenuItem:notify.object withBlock:^(Feed *feed, NSMenuItem *item) {
|
||||
Feed *feed;
|
||||
NSMenuItem *item;
|
||||
if ([self findDeepest:notify.object feed:&feed menuItem:&item]) {
|
||||
if (item.submenu.isFeedMenu)
|
||||
item.image = [feed iconImage16];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -44,10 +44,9 @@
|
||||
self = [super init];
|
||||
// Show icon & prefetch unread count
|
||||
self.statusItem = [NSStatusBar.systemStatusBar statusItemWithLength:NSVariableStatusItemLength];
|
||||
self.statusItem.highlightMode = YES;
|
||||
self.unreadCountTotal = 0;
|
||||
self.statusItem.image = [NSImage imageNamed:RSSImageMenuBarIconActive];
|
||||
self.statusItem.image.template = YES;
|
||||
self.statusItem.button.image = [NSImage imageNamed:RSSImageMenuBarIconActive];
|
||||
self.statusItem.button.image.template = YES;
|
||||
// Add empty menu (will be populated once opened)
|
||||
self.statusItem.menu = [[NSMenu alloc] initWithTitle:@"M"];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mainMenuWillOpen) name:NSMenuDidBeginTrackingNotification object:self.statusItem.menu];
|
||||
@@ -119,11 +118,13 @@
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
BOOL hasNet = [UpdateScheduler allowNetworkConnection];
|
||||
BOOL tint = (self.unreadCountTotal > 0 && hasNet && UserPrefsBool(Pref_globalTintMenuIcon));
|
||||
self.statusItem.image = [NSImage imageNamed:(hasNet ? RSSImageMenuBarIconActive : RSSImageMenuBarIconPaused)];
|
||||
self.statusItem.image.template = !tint;
|
||||
self.statusItem.button.image = [NSImage imageNamed:(hasNet ? RSSImageMenuBarIconActive : RSSImageMenuBarIconPaused)];
|
||||
self.statusItem.button.image.template = !tint;
|
||||
// TODO: use macOS 10.14 contentTintColor, if (@available(macOS 10.14, *)) {} else {}
|
||||
|
||||
BOOL showCount = (self.unreadCountTotal > 0 && UserPrefsBool(Pref_globalUnreadCount));
|
||||
self.statusItem.title = (showCount ? [NSString stringWithFormat:@"%ld", self.unreadCountTotal] : @"");
|
||||
self.statusItem.button.title = (showCount ? [NSString stringWithFormat:@"%ld", self.unreadCountTotal] : @"");
|
||||
self.statusItem.button.imagePosition = (showCount ? NSImageLeft : NSImageOnly);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
- (NSMenuItem*)insertFeedGroupItem:(FeedGroup*)fg;
|
||||
- (void)insertDefaultHeader;
|
||||
// Update menu
|
||||
- (void)cleanup;
|
||||
- (void)setHeaderHasUnread:(BOOL)hasUnread hasRead:(BOOL)hasRead;
|
||||
- (NSMenuItem*)deepestItemWithPath:(nonnull NSString*)path;
|
||||
@end
|
||||
|
||||
@@ -104,12 +104,6 @@ typedef NS_ENUM(NSInteger, MenuItemTag) {
|
||||
|
||||
#pragma mark - Update Menu
|
||||
|
||||
/// Replace this menu with a clean @c NSMenu. Copy old @c title and @c delegate to new menu. @b Won't work without supermenu!
|
||||
- (void)cleanup {
|
||||
NSMenu *m = [[NSMenu alloc] initWithTitle:self.title];
|
||||
m.delegate = self.delegate;
|
||||
self.parentItem.submenu = m;
|
||||
}
|
||||
|
||||
/// Loop over default header and enable 'OpenAllUnread' and 'TagMarkAllRead' based on unread count.
|
||||
- (void)setHeaderHasUnread:(BOOL)hasUnread hasRead:(BOOL)hasRead {
|
||||
|
||||
Reference in New Issue
Block a user