Application-wide hotkeys + feed edit modal sheet

This commit is contained in:
relikd
2018-08-03 23:53:22 +02:00
parent f27b54af4f
commit 8080af1a0f
10 changed files with 368 additions and 64 deletions

View File

@@ -8,6 +8,8 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
1968E0AE14B8E8A90E194980 /* PyHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 1968EF7567E06D2A5BB3481A /* PyHandler.m */; }; 1968E0AE14B8E8A90E194980 /* PyHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 1968EF7567E06D2A5BB3481A /* PyHandler.m */; };
544B011A2114B41200386E5C /* FeedEdit.m in Sources */ = {isa = PBXBuildFile; fileRef = 544B01192114B41200386E5C /* FeedEdit.m */; };
544B011D2114EE9100386E5C /* AppHook.m in Sources */ = {isa = PBXBuildFile; fileRef = 544B011C2114EE9100386E5C /* AppHook.m */; };
544FBD4521064AEB008A260C /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 544FBD4421064AEB008A260C /* Python.framework */; }; 544FBD4521064AEB008A260C /* Python.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 544FBD4421064AEB008A260C /* Python.framework */; };
544FBD4721064B2F008A260C /* getFeed.py in Resources */ = {isa = PBXBuildFile; fileRef = 544FBD4621064B2F008A260C /* getFeed.py */; }; 544FBD4721064B2F008A260C /* getFeed.py in Resources */ = {isa = PBXBuildFile; fileRef = 544FBD4621064B2F008A260C /* getFeed.py */; };
544FBD4921064DF0008A260C /* feedparser521.py in Resources */ = {isa = PBXBuildFile; fileRef = 544FBD4821064DF0008A260C /* feedparser521.py */; }; 544FBD4921064DF0008A260C /* feedparser521.py in Resources */ = {isa = PBXBuildFile; fileRef = 544FBD4821064DF0008A260C /* feedparser521.py */; };
@@ -23,6 +25,10 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
1968E7919BAA36F042FCB717 /* PyHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PyHandler.h; sourceTree = "<group>"; }; 1968E7919BAA36F042FCB717 /* PyHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PyHandler.h; sourceTree = "<group>"; };
1968EF7567E06D2A5BB3481A /* PyHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PyHandler.m; sourceTree = "<group>"; }; 1968EF7567E06D2A5BB3481A /* PyHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PyHandler.m; sourceTree = "<group>"; };
544B01182114B41200386E5C /* FeedEdit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FeedEdit.h; sourceTree = "<group>"; };
544B01192114B41200386E5C /* FeedEdit.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FeedEdit.m; sourceTree = "<group>"; };
544B011B2114EE9100386E5C /* AppHook.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppHook.h; sourceTree = "<group>"; };
544B011C2114EE9100386E5C /* AppHook.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppHook.m; sourceTree = "<group>"; };
544FBD4421064AEB008A260C /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Python.framework; path = System/Library/Frameworks/Python.framework; sourceTree = SDKROOT; }; 544FBD4421064AEB008A260C /* Python.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Python.framework; path = System/Library/Frameworks/Python.framework; sourceTree = SDKROOT; };
544FBD4621064B2F008A260C /* getFeed.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = getFeed.py; sourceTree = "<group>"; usesTabs = 0; }; 544FBD4621064B2F008A260C /* getFeed.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = getFeed.py; sourceTree = "<group>"; usesTabs = 0; };
544FBD4821064DF0008A260C /* feedparser521.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = feedparser521.py; sourceTree = "<group>"; usesTabs = 0; }; 544FBD4821064DF0008A260C /* feedparser521.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = feedparser521.py; sourceTree = "<group>"; usesTabs = 0; };
@@ -92,12 +98,16 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
549369F421091E6D001AF895 /* python */, 549369F421091E6D001AF895 /* python */,
544B011B2114EE9100386E5C /* AppHook.h */,
544B011C2114EE9100386E5C /* AppHook.m */,
54ACC27F21061B3B0020715F /* AppDelegate.h */, 54ACC27F21061B3B0020715F /* AppDelegate.h */,
54ACC28021061B3B0020715F /* AppDelegate.m */, 54ACC28021061B3B0020715F /* AppDelegate.m */,
54ACC29321061E270020715F /* NewsController.h */, 54ACC29321061E270020715F /* NewsController.h */,
54ACC29421061E270020715F /* NewsController.m */, 54ACC29421061E270020715F /* NewsController.m */,
54ACC29621061FBA0020715F /* Preferences.h */, 54ACC29621061FBA0020715F /* Preferences.h */,
54ACC29721061FBA0020715F /* Preferences.m */, 54ACC29721061FBA0020715F /* Preferences.m */,
544B01182114B41200386E5C /* FeedEdit.h */,
544B01192114B41200386E5C /* FeedEdit.m */,
54ACC28521061B3C0020715F /* Assets.xcassets */, 54ACC28521061B3C0020715F /* Assets.xcassets */,
54ACC28721061B3C0020715F /* Main.xib */, 54ACC28721061B3C0020715F /* Main.xib */,
54ACC28A21061B3C0020715F /* Info.plist */, 54ACC28A21061B3C0020715F /* Info.plist */,
@@ -188,9 +198,11 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
54F39C2E210BE1F700AEE730 /* DBv1.xcdatamodeld in Sources */, 54F39C2E210BE1F700AEE730 /* DBv1.xcdatamodeld in Sources */,
544B011D2114EE9100386E5C /* AppHook.m in Sources */,
54ACC29521061E270020715F /* NewsController.m in Sources */, 54ACC29521061E270020715F /* NewsController.m in Sources */,
54ACC28C21061B3C0020715F /* main.m in Sources */, 54ACC28C21061B3C0020715F /* main.m in Sources */,
54ACC28121061B3B0020715F /* AppDelegate.m in Sources */, 54ACC28121061B3B0020715F /* AppDelegate.m in Sources */,
544B011A2114B41200386E5C /* FeedEdit.m in Sources */,
54ACC29821061FBA0020715F /* Preferences.m in Sources */, 54ACC29821061FBA0020715F /* Preferences.m in Sources */,
1968E0AE14B8E8A90E194980 /* PyHandler.m in Sources */, 1968E0AE14B8E8A90E194980 /* PyHandler.m in Sources */,
); );

27
baRSS/AppHook.h Normal file
View File

@@ -0,0 +1,27 @@
//
// The MIT License (MIT)
// Copyright (c) 2018 Oleg Geier
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import <Cocoa/Cocoa.h>
@interface AppHook : NSApplication
@end

57
baRSS/AppHook.m Normal file
View File

@@ -0,0 +1,57 @@
//
// The MIT License (MIT)
// Copyright (c) 2018 Oleg Geier
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "AppHook.h"
static NSEventModifierFlags fnKeyFlags = NSEventModifierFlagShift | NSEventModifierFlagControl | NSEventModifierFlagOption | NSEventModifierFlagCommand | NSEventModifierFlagFunction;
@implementation AppHook
- (void) sendEvent:(NSEvent *)event {
if ([event type] == NSEventTypeKeyDown) {
NSEventModifierFlags flags = (event.modifierFlags & fnKeyFlags); // ignore caps lock, etc.
unichar key = [event.characters characterAtIndex:0]; // charactersIgnoringModifiers
if (flags == NSEventModifierFlagCommand) {
switch (key) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
case 'z': if ([self sendAction:@selector(undo:) to:nil from:self]) return; break;
#pragma clang diagnostic pop
case 'x': if ([self sendAction:@selector(cut:) to:nil from:self]) return; break;
case 'c': if ([self sendAction:@selector(copy:) to:nil from:self]) return; break;
case 'v': if ([self sendAction:@selector(paste:) to:nil from:self]) return; break;
case 'a': if ([self sendAction:@selector(selectAll:) to:nil from:self]) return; break;
case 'q': if ([self sendAction:@selector(terminate:) to:nil from:self]) return; break;
case 'w': if ([self sendAction:@selector(performClose:) to:nil from:self]) return; break;
}
} else if (flags == (NSEventModifierFlagCommand | NSEventModifierFlagShift)) {
if (key == 'z') {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
if ([self sendAction:@selector(redo:) to:nil from:self])
return;
#pragma clang diagnostic pop
}
}
}
[super sendEvent:event];
}
@end

View File

@@ -19,22 +19,6 @@
</connections> </connections>
</customObject> </customObject>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/> <customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<treeController mode="entity" entityName="FeedConfig" fetchPredicateFormat="parent == nil" automaticallyPreparesContent="YES" childrenKeyPath="children" leafKeyPath="type" id="1oZ-Uo-mIu" customClass="NewsController">
<connections>
<binding destination="Voe-Tx-rLC" name="managedObjectContext" keyPath="self.persistentContainer.viewContext" id="Q1f-VN-9qq"/>
<outlet property="outlineView" destination="hKk-G1-1po" id="Z4y-uD-Zse"/>
</connections>
</treeController>
<customObject id="bgB-po-1IK" customClass="Preferences">
<connections>
<outlet property="feedsOutline" destination="hKk-G1-1po" id="jcw-Fq-DyK"/>
<outlet property="newsController" destination="1oZ-Uo-mIu" id="hAk-g4-Oii"/>
<outlet property="toolbar" destination="wLj-fD-HpE" id="bMA-dd-1H1"/>
<outlet property="viewFeeds" destination="EwH-fT-yW8" id="pmQ-as-O2V"/>
<outlet property="viewGeneral" destination="8H4-sI-Ub8" id="lWU-Il-4in"/>
<outlet property="window" destination="ai3-RW-O8g" id="Ncu-vV-dTy"/>
</connections>
</customObject>
<menu id="tDc-nD-HS2"> <menu id="tDc-nD-HS2">
<items> <items>
<menuItem title="Pause Updates" id="D7r-Vb-9eO"> <menuItem title="Pause Updates" id="D7r-Vb-9eO">
@@ -70,10 +54,16 @@
</items> </items>
<point key="canvasLocation" x="277" y="419"/> <point key="canvasLocation" x="277" y="419"/>
</menu> </menu>
<window title="baRSS Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" showsToolbarButton="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" tabbingMode="disallowed" id="ai3-RW-O8g" userLabel="Preferences"> <treeController mode="entity" entityName="FeedConfig" fetchPredicateFormat="parent == nil" automaticallyPreparesContent="YES" childrenKeyPath="children" leafKeyPath="type" id="1oZ-Uo-mIu" customClass="NewsController">
<connections>
<binding destination="Voe-Tx-rLC" name="managedObjectContext" keyPath="self.persistentContainer.viewContext" id="Q1f-VN-9qq"/>
<outlet property="outlineView" destination="hKk-G1-1po" id="Z4y-uD-Zse"/>
<outlet property="preferencesWindow" destination="ai3-RW-O8g" id="cNF-TI-ups"/>
</connections>
</treeController>
<window title="baRSS Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" frameAutosaveName="prefWindow" animationBehavior="default" tabbingMode="disallowed" id="ai3-RW-O8g" userLabel="Preferences" customClass="Preferences">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/> <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/> <rect key="contentRect" x="948" y="431" width="320" height="327"/>
<rect key="contentRect" x="131" y="159" width="320" height="327"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="878"/> <rect key="screenRect" x="0.0" y="0.0" width="1440" height="878"/>
<value key="minSize" type="size" width="320" height="327"/> <value key="minSize" type="size" width="320" height="327"/>
<view key="contentView" id="ct3-G7-3SQ"> <view key="contentView" id="ct3-G7-3SQ">
@@ -84,12 +74,12 @@
<allowedToolbarItems> <allowedToolbarItems>
<toolbarItem implicitItemIdentifier="AF4483E3-0457-47B7-AE52-AC11B1545455" explicitItemIdentifier="preferencesTabGeneral" label="General" paletteLabel="General" tag="-1" image="NSPreferencesGeneral" selectable="YES" id="0qP-ah-ojT"> <toolbarItem implicitItemIdentifier="AF4483E3-0457-47B7-AE52-AC11B1545455" explicitItemIdentifier="preferencesTabGeneral" label="General" paletteLabel="General" tag="-1" image="NSPreferencesGeneral" selectable="YES" id="0qP-ah-ojT">
<connections> <connections>
<action selector="clickGeneral:" target="bgB-po-1IK" id="G4I-Zs-FZR"/> <action selector="tabGeneralClicked:" target="ai3-RW-O8g" id="OiJ-PY-QCu"/>
</connections> </connections>
</toolbarItem> </toolbarItem>
<toolbarItem implicitItemIdentifier="E8527558-3D5F-4B79-99E4-675C1557D10B" explicitItemIdentifier="preferencesTabFeeds" label="Feeds" paletteLabel="Feeds" tag="-1" image="NSUserAccounts" selectable="YES" id="Av5-VA-zps"> <toolbarItem implicitItemIdentifier="E8527558-3D5F-4B79-99E4-675C1557D10B" explicitItemIdentifier="preferencesTabFeeds" label="Feeds" paletteLabel="Feeds" tag="-1" image="NSUserAccounts" selectable="YES" id="Av5-VA-zps">
<connections> <connections>
<action selector="clickFeeds:" target="bgB-po-1IK" id="Daq-b8-lpE"/> <action selector="tabFeedsClicked:" target="ai3-RW-O8g" id="l5x-nd-qBx"/>
</connections> </connections>
</toolbarItem> </toolbarItem>
</allowedToolbarItems> </allowedToolbarItems>
@@ -98,6 +88,12 @@
<toolbarItem reference="Av5-VA-zps"/> <toolbarItem reference="Av5-VA-zps"/>
</defaultToolbarItems> </defaultToolbarItems>
</toolbar> </toolbar>
<connections>
<outlet property="feedDetailSheet" destination="6yF-Ii-jhG" id="ozt-yn-eEM"/>
<outlet property="newsController" destination="1oZ-Uo-mIu" id="1HA-Vz-PBK"/>
<outlet property="viewFeeds" destination="EwH-fT-yW8" id="aBH-fR-foa"/>
<outlet property="viewGeneral" destination="8H4-sI-Ub8" id="PS7-gG-GJs"/>
</connections>
<point key="canvasLocation" x="-20" y="521"/> <point key="canvasLocation" x="-20" y="521"/>
</window> </window>
<customView id="8H4-sI-Ub8" userLabel="Preferences-General"> <customView id="8H4-sI-Ub8" userLabel="Preferences-General">
@@ -430,6 +426,7 @@
</tableColumn> </tableColumn>
</tableColumns> </tableColumns>
<connections> <connections>
<action trigger="doubleAction" selector="presentModalFeedProperties:" target="1oZ-Uo-mIu" id="zNP-kQ-Xmh"/>
<outlet property="dataSource" destination="1oZ-Uo-mIu" id="sch-o5-yEm"/> <outlet property="dataSource" destination="1oZ-Uo-mIu" id="sch-o5-yEm"/>
<outlet property="delegate" destination="1oZ-Uo-mIu" id="Nye-dQ-vdy"/> <outlet property="delegate" destination="1oZ-Uo-mIu" id="Nye-dQ-vdy"/>
</connections> </connections>
@@ -459,7 +456,7 @@
<modifierMask key="keyEquivalentModifierMask" command="YES"/> <modifierMask key="keyEquivalentModifierMask" command="YES"/>
</buttonCell> </buttonCell>
<connections> <connections>
<action selector="addFeed:" target="1oZ-Uo-mIu" id="wR9-Np-joC"/> <action selector="addFeed:" target="1oZ-Uo-mIu" id="jX0-xl-p0f"/>
<binding destination="1oZ-Uo-mIu" name="enabled" keyPath="canInsert" id="1xq-Nj-Acq"/> <binding destination="1oZ-Uo-mIu" name="enabled" keyPath="canInsert" id="1xq-Nj-Acq"/>
</connections> </connections>
</button> </button>
@@ -514,6 +511,131 @@ CA
<point key="canvasLocation" x="350" y="903.5"/> <point key="canvasLocation" x="350" y="903.5"/>
</customView> </customView>
<userDefaultsController representsSharedInstance="YES" id="K8S-BW-Na6"/> <userDefaultsController representsSharedInstance="YES" id="K8S-BW-Na6"/>
<window title="Feed Detail Sheet" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" showsToolbarButton="NO" visibleAtLaunch="NO" frameAutosaveName="formFeedDetail" animationBehavior="default" id="6yF-Ii-jhG" customClass="FeedEdit">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<rect key="contentRect" x="131" y="159" width="400" height="153"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="878"/>
<value key="minSize" type="size" width="400" height="153"/>
<value key="maxSize" type="size" width="1200" height="153"/>
<view key="contentView" id="bH3-c8-m3D">
<rect key="frame" x="0.0" y="0.0" width="400" height="153"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="u2j-52-0kN">
<rect key="frame" x="18" y="114" width="103" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="URL" id="FDr-0e-igw">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9nh-vY-v6W">
<rect key="frame" x="127" y="112" width="253" height="21"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" placeholderString="https://example.org/feed.rss" drawsBackground="YES" usesSingleLineMode="YES" id="VSm-J7-qAA">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Znc-BK-dhw">
<rect key="frame" x="18" y="85" width="103" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Name" id="Uda-8X-onH">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="5uB-Zy-4u6">
<rect key="frame" x="127" y="83" width="253" height="21"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" placeholderString="Example Title" drawsBackground="YES" usesSingleLineMode="YES" id="vzt-hb-cGT">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="sMQ-B7-WTo">
<rect key="frame" x="127" y="54" width="86" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" placeholderString="30" drawsBackground="YES" usesSingleLineMode="YES" id="bNw-iS-Gfz">
<customFormatter key="formatter" id="NEE-2h-FN9" customClass="StrictUIntFormatter"/>
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Az1-ZL-15b">
<rect key="frame" x="232" y="13" width="82" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="V93-O8-bjm">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
Gw
</string>
</buttonCell>
<connections>
<action selector="didTapCancelButton:" target="6yF-Ii-jhG" id="uS5-n0-iZ3"/>
</connections>
</button>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="CRz-wU-u6m">
<rect key="frame" x="314" y="13" width="72" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<buttonCell key="cell" type="push" title="Done" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="fdr-rD-bgC">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
DQ
</string>
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</buttonCell>
<connections>
<action selector="didTapDoneButton:" target="6yF-Ii-jhG" id="vK2-Ou-z3b"/>
</connections>
</button>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="FYV-ch-Bjd" userLabel="TimeUnit">
<rect key="frame" x="219" y="51" width="127" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Minutes" bezelStyle="rounded" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" autoenablesItems="NO" selectedItem="7DG-HF-41v" id="54G-6B-9Vg">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" showsStateColumn="NO" autoenablesItems="NO" id="FA5-Wc-aKF">
<items>
<menuItem title="Seconds" id="ObP-NX-RUr">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Minutes" state="on" id="7DG-HF-41v">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Hours" id="buP-XT-lMO">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Days" id="wJ7-Ui-a4q">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Weeks" id="Py4-7n-CXD">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
</items>
</menu>
</popUpButtonCell>
</popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="hlP-VB-yeu">
<rect key="frame" x="18" y="56" width="103" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Refresh" id="c0W-IR-MMR">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
</view>
<point key="canvasLocation" x="20" y="1225.5"/>
</window>
</objects> </objects>
<resources> <resources>
<image name="NSActionTemplate" width="14" height="14"/> <image name="NSActionTemplate" width="14" height="14"/>

27
baRSS/FeedEdit.h Normal file
View File

@@ -0,0 +1,27 @@
//
// The MIT License (MIT)
// Copyright (c) 2018 Oleg Geier
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import <Cocoa/Cocoa.h>
@interface FeedEdit : NSWindow
@end

58
baRSS/FeedEdit.m Normal file
View File

@@ -0,0 +1,58 @@
//
// The MIT License (MIT)
// Copyright (c) 2018 Oleg Geier
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "FeedEdit.h"
@implementation FeedEdit
- (IBAction)didTapDoneButton:(id)sender {
[self.parentWindow endSheet:self returnCode:NSModalResponseOK];
}
- (IBAction)didTapCancelButton:(id)sender {
[self.parentWindow endSheet:self returnCode:NSModalResponseAbort];
}
@end
@interface StrictUIntFormatter : NSFormatter
@end
@implementation StrictUIntFormatter
- (NSString *)stringForObjectValue:(id)obj {
return [NSString stringWithFormat:@"%d", [[NSString stringWithFormat:@"%@", obj] intValue]];
}
- (BOOL)getObjectValue:(out id _Nullable __autoreleasing *)obj forString:(NSString *)string errorDescription:(out NSString *__autoreleasing _Nullable *)error {
*obj = [[NSNumber numberWithInt:[string intValue]] stringValue];
return YES;
}
- (BOOL)isPartialStringValid:(NSString *__autoreleasing _Nonnull *)partialStringPtr proposedSelectedRange:(NSRangePointer)proposedSelRangePtr originalString:(NSString *)origString originalSelectedRange:(NSRange)origSelRange errorDescription:(NSString *__autoreleasing _Nullable *)error {
for (NSUInteger i = 0; i < [*partialStringPtr length]; i++) {
unichar c = [*partialStringPtr characterAtIndex:i];
if (c < '0' || c > '9')
return NO;
}
return YES;
}
@end

View File

@@ -29,6 +29,6 @@
<key>NSMainNibFile</key> <key>NSMainNibFile</key>
<string>Main</string> <string>Main</string>
<key>NSPrincipalClass</key> <key>NSPrincipalClass</key>
<string>NSApplication</string> <string>AppHook</string>
</dict> </dict>
</plist> </plist>

View File

@@ -22,10 +22,11 @@
#import "NewsController.h" #import "NewsController.h"
#import "PyHandler.h" #import "PyHandler.h"
#import "AppDelegate.h" #import "Preferences.h"
#import "DBv1+CoreDataModel.h" #import "DBv1+CoreDataModel.h"
@interface NewsController () @interface NewsController ()
@property (weak) IBOutlet Preferences *preferencesWindow;
@property (weak) IBOutlet NSOutlineView *outlineView; @property (weak) IBOutlet NSOutlineView *outlineView;
@property (strong) NSArray<NSTreeNode*> *currentlyDraggedNodes; @property (strong) NSArray<NSTreeNode*> *currentlyDraggedNodes;
@@ -98,8 +99,22 @@ static NSString *dragNodeType = @"baRSS-feed-type";
NSLog(@"all unread"); NSLog(@"all unread");
} }
- (IBAction)presentModalFeedProperties:(id)sender {
self.preferencesWindow.feedDetailSheet.parentWindow = self.preferencesWindow;
[self.preferencesWindow beginSheet:self.preferencesWindow.feedDetailSheet completionHandler:^(NSModalResponse returnCode) {
NSLog(@"%ld", (long)returnCode);
}];
// if ([sender isKindOfClass:[NSOutlineView class]]) {
// NSOutlineView *ov = sender;
// if (ov.clickedRow == -1)
// return; // ignore clicks on column headers and where no row was selected
//
// id vop = [ov itemAtRow:ov.clickedRow];
// NSLog(@"%@", vop);
// }
}
- (IBAction)addFeed:(NSButton *)sender { - (IBAction)addFeed:(NSButton *)sender {
NSLog(@"add feed");
[self.managedObjectContext.undoManager beginUndoGrouping]; [self.managedObjectContext.undoManager beginUndoGrouping];
FeedConfig *nf = [self insertSortedItemAtSelection]; FeedConfig *nf = [self insertSortedItemAtSelection];
nf.type = 1; nf.type = 1;
@@ -117,10 +132,9 @@ static NSString *dragNodeType = @"baRSS-feed-type";
} }
- (IBAction)addSeparator:(NSButton *)sender { - (IBAction)addSeparator:(NSButton *)sender {
NSLog(@"add separator");
[self.managedObjectContext.undoManager beginUndoGrouping]; [self.managedObjectContext.undoManager beginUndoGrouping];
FeedConfig *sp = [self insertSortedItemAtSelection]; FeedConfig *sp = [self insertSortedItemAtSelection];
sp.name = @"-------------"; sp.name = @"---";
sp.type = 2; sp.type = 2;
[self.managedObjectContext.undoManager endUndoGrouping]; [self.managedObjectContext.undoManager endUndoGrouping];
} }

View File

@@ -22,6 +22,7 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
@interface Preferences : NSWindowController @interface Preferences : NSWindow
@property (weak) IBOutlet NSWindow *feedDetailSheet;
@end @end

View File

@@ -24,59 +24,45 @@
#import "NewsController.h" #import "NewsController.h"
@interface Preferences () @interface Preferences ()
@property (weak) IBOutlet NSToolbar *toolbar;
@property (weak) IBOutlet NSView *viewGeneral; @property (weak) IBOutlet NSView *viewGeneral;
@property (weak) IBOutlet NSView *viewFeeds; @property (weak) IBOutlet NSView *viewFeeds;
@property (weak) IBOutlet NewsController *newsController; @property (weak) IBOutlet NewsController *newsController;
@property (weak) IBOutlet NSOutlineView *feedsOutline;
@end @end
@implementation Preferences @implementation Preferences
- (void)awakeFromNib { - (void)awakeFromNib {
[super awakeFromNib]; [super awakeFromNib];
if (self.window.contentView.subviews.count == 0) { if (self.contentView.subviews.count == 0) {
self.window.contentView = self.viewGeneral; self.contentView = self.viewGeneral;
self.toolbar.selectedItemIdentifier = self.toolbar.items.firstObject.itemIdentifier; self.toolbar.selectedItemIdentifier = self.toolbar.items.firstObject.itemIdentifier;
} }
} }
- (IBAction)clickGeneral:(NSToolbarItem *)sender { - (IBAction)tabGeneralClicked:(NSToolbarItem *)sender {
self.window.contentView = self.viewGeneral; self.contentView = self.viewGeneral;
} }
- (IBAction)clickFeeds:(NSToolbarItem *)sender { - (IBAction)tabFeedsClicked:(NSToolbarItem *)sender {
self.window.contentView = self.viewFeeds; self.contentView = self.viewFeeds;
} }
- (BOOL)acceptsFirstResponder { - (void)undo:(id)sender {
return YES; if (self.contentView == self.viewFeeds) {
} [self.newsController.managedObjectContext.undoManager undo];
[self.newsController rearrangeObjects]; // update the ordering
- (void)keyDown:(NSEvent *)event {
if (event.modifierFlags & NSEventModifierFlagCommand) {
bool holdShift = event.modifierFlags & NSEventModifierFlagShift;
unichar key = [event.characters characterAtIndex:0];
switch (key) {
case 'w': [self close]; break;
case 'q': [NSApplication.sharedApplication terminate:self]; break;
}
if (self.window.contentView == self.viewFeeds) { // these only apply for NSOutlineView
switch (key) {
case 'z':
if (holdShift) [self.newsController.managedObjectContext.undoManager redo];
else [self.newsController.managedObjectContext.undoManager undo];
[self.newsController rearrangeObjects]; // update the ordering
break;
case 'o': break; // open .opml file
case 's': break; // save data or backup .opml file
case 'c': // copy row entry
[self.newsController copyDescriptionOfSelectedItems];
break;
case 'a': [self.feedsOutline selectAll:nil]; break;
}
}
} }
} }
- (void)redo:(id)sender {
if (self.contentView == self.viewFeeds) {
[self.newsController.managedObjectContext.undoManager redo];
[self.newsController rearrangeObjects]; // update the ordering
}
}
- (void)copy:(id)sender {
[self.newsController copyDescriptionOfSelectedItems];
}
@end @end