diff --git a/Vienna.xcodeproj/project.pbxproj b/Vienna.xcodeproj/project.pbxproj index a2196cb358..e5a5f207a8 100644 --- a/Vienna.xcodeproj/project.pbxproj +++ b/Vienna.xcodeproj/project.pbxproj @@ -161,11 +161,10 @@ F61901882E0F16B000A7A200 /* FilterBarViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F61901852E0F16B000A7A200 /* FilterBarViewController.m */; }; F619018B2E0F1D5400A7A200 /* FilterBarViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = F619018A2E0F1D5400A7A200 /* FilterBarViewController.xib */; }; F61CEA661F039E57009C878E /* ButtonToolbarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F61CEA651F039E56009C878E /* ButtonToolbarItem.swift */; }; - F61CEA681F03F277009C878E /* PlugInToolbarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F61CEA671F03F277009C878E /* PlugInToolbarItem.swift */; }; + F61CEA681F03F277009C878E /* RepresentingToolbarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F61CEA671F03F277009C878E /* RepresentingToolbarItem.swift */; }; F6209C5A23D3FFA700D5537D /* Vienna.sdef in Resources */ = {isa = PBXBuildFile; fileRef = F6209C5923D3FFA700D5537D /* Vienna.sdef */; }; F62581451E29B90B0035E43C /* SearchPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = F62581441E29B90A0035E43C /* SearchPanel.xib */; }; F626F5F424A76B9100D3AAD8 /* Preferences.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F626F5F624A76B9100D3AAD8 /* Preferences.storyboard */; }; - F62E324E284E0CAF0046EA1F /* SharingServiceToolbarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F62E324D284E0CAF0046EA1F /* SharingServiceToolbarItem.swift */; }; F633157626ED73CB008A3673 /* URLFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F633157526ED73CB008A3673 /* URLFormatter.swift */; }; F633157826EE3D06008A3673 /* URLFormatterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F633157726EE3D06008A3673 /* URLFormatterTests.swift */; }; F6373A231F1D53B500ED3B09 /* MainWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6373A221F1D53B500ED3B09 /* MainWindow.swift */; }; @@ -177,10 +176,10 @@ F64C2CCC1E83825D00ED4E04 /* DateFormatterExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = F64C2CCB1E83825D00ED4E04 /* DateFormatterExtension.m */; }; F65D6D711E74619E00A30974 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 4398241E1666B3DB00FFE219 /* Credits.rtf */; }; F65F2314294E4B5200605F06 /* SeparatorPredicateEditorRowTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F65F2313294E4B5200605F06 /* SeparatorPredicateEditorRowTemplate.swift */; }; + F66BF2BE2F6E85F000496980 /* ToolbarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F66BF2BD2F6E85E900496980 /* ToolbarItem.swift */; }; F672876824A2D2170043432F /* UpdatePreferencesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F672876724A2D2170043432F /* UpdatePreferencesViewController.m */; }; F67294282D1AFC1F0019B57E /* Subscribe.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F67294272D1AFC1F0019B57E /* Subscribe.storyboard */; }; F6786BB72C709880005B985B /* InfoPlist.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = F6786BB62C709880005B985B /* InfoPlist.xcstrings */; }; - F67E7828285DDAB200D1CECE /* Toolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = F67E7827285DDAB100D1CECE /* Toolbar.swift */; }; F6832F631F10C4C9007920D4 /* ExportAccessoryViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = F6832F651F10C4C9007920D4 /* ExportAccessoryViewController.xib */; }; F6832F681F10F5DA007920D4 /* MainWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = F6832F661F10F5DA007920D4 /* MainWindowController.xib */; }; F685D24C292460210097C0D3 /* Predicates.strings in Resources */ = {isa = PBXBuildFile; fileRef = F685D24A292460210097C0D3 /* Predicates.strings */; }; @@ -201,7 +200,6 @@ F6A179D326B82BE3008DDA42 /* NSFileManagerExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6A179D226B82BE3008DDA42 /* NSFileManagerExtensionTests.swift */; }; F6A243E12C69E534000A006F /* Article+Tags.m in Sources */ = {isa = PBXBuildFile; fileRef = F6A243E02C69E534000A006F /* Article+Tags.m */; }; F6A464BC272F47BE0071E3F6 /* NSKeyedUnarchiver+Compatibility.m in Sources */ = {isa = PBXBuildFile; fileRef = F6A464BA272F47BE0071E3F6 /* NSKeyedUnarchiver+Compatibility.m */; }; - F6A4E8481F040D58001C9191 /* PlugInToolbarItemButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6A4E8471F040D58001C9191 /* PlugInToolbarItemButton.swift */; }; F6A7DE341E47138B0017BE5E /* advanced.html in Resources */ = {isa = PBXBuildFile; fileRef = F6A7DE2A1E47138B0017BE5E /* advanced.html */; }; F6A7DE351E47138B0017BE5E /* faq.html in Resources */ = {isa = PBXBuildFile; fileRef = F6A7DE2C1E47138B0017BE5E /* faq.html */; }; F6A7DE361E47138B0017BE5E /* intro.html in Resources */ = {isa = PBXBuildFile; fileRef = F6A7DE2E1E47138B0017BE5E /* intro.html */; }; @@ -252,6 +250,7 @@ F6E01A072C652DEE0082E07B /* RSSFeedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6E01A062C652DEE0082E07B /* RSSFeedTests.swift */; }; F6E01A092C652FA50082E07B /* RSSFeedWithContentElements.rss in Resources */ = {isa = PBXBuildFile; fileRef = F6E01A082C652FA50082E07B /* RSSFeedWithContentElements.rss */; }; F6EB26031E58D37100570B22 /* DirectoryMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6EB26021E58D37100570B22 /* DirectoryMonitor.swift */; }; + F6EBC7912F786B9D000B2279 /* ToggleButtonToolbarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6EBC7902F786B9D000B2279 /* ToggleButtonToolbarItem.swift */; }; F6F12AFD25ABDDE3005B2DCE /* NSFileManager+Paths.m in Sources */ = {isa = PBXBuildFile; fileRef = F6F12AFC25ABDDE3005B2DCE /* NSFileManager+Paths.m */; }; F6F2029F2D19DD3A004BB948 /* SubscribeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F6F2029E2D19DD3A004BB948 /* SubscribeViewController.m */; }; F6F844EA2F0C6EBF00A8D8D6 /* TableHeaderCell.m in Sources */ = {isa = PBXBuildFile; fileRef = F6F844E92F0C6EBF00A8D8D6 /* TableHeaderCell.m */; }; @@ -502,12 +501,11 @@ F61901892E0F1D5400A7A200 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/FilterBarViewController.xib; sourceTree = ""; }; F619018C2E0F1D5400A7A200 /* mul */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; name = mul; path = mul.lproj/FilterBarViewController.xcstrings; sourceTree = ""; }; F61CEA651F039E56009C878E /* ButtonToolbarItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ButtonToolbarItem.swift; sourceTree = ""; }; - F61CEA671F03F277009C878E /* PlugInToolbarItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = PlugInToolbarItem.swift; sourceTree = ""; }; + F61CEA671F03F277009C878E /* RepresentingToolbarItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = RepresentingToolbarItem.swift; sourceTree = ""; }; F6209C5923D3FFA700D5537D /* Vienna.sdef */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; lineEnding = 0; path = Vienna.sdef; sourceTree = ""; usesTabs = 1; }; F6232EF72B4B402F00D01D2A /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Predicates.strings; sourceTree = ""; }; F62581441E29B90A0035E43C /* SearchPanel.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SearchPanel.xib; sourceTree = ""; }; F626F5F524A76B9100D3AAD8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Preferences.storyboard; sourceTree = ""; }; - F62E324D284E0CAF0046EA1F /* SharingServiceToolbarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingServiceToolbarItem.swift; sourceTree = ""; }; F633157526ED73CB008A3673 /* URLFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = URLFormatter.swift; sourceTree = ""; }; F633157726EE3D06008A3673 /* URLFormatterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = URLFormatterTests.swift; sourceTree = ""; }; F63503F32960846D006F1F76 /* FeedItem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FeedItem.h; sourceTree = ""; }; @@ -530,12 +528,12 @@ F64C2CCB1E83825D00ED4E04 /* DateFormatterExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DateFormatterExtension.m; sourceTree = ""; }; F65F2313294E4B5200605F06 /* SeparatorPredicateEditorRowTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeparatorPredicateEditorRowTemplate.swift; sourceTree = ""; }; F66A7E542A019006002F5D5E /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Predicates.strings; sourceTree = ""; }; + F66BF2BD2F6E85E900496980 /* ToolbarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolbarItem.swift; sourceTree = ""; }; F672876624A2D20A0043432F /* UpdatePreferencesViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UpdatePreferencesViewController.h; sourceTree = ""; }; F672876724A2D2170043432F /* UpdatePreferencesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UpdatePreferencesViewController.m; sourceTree = ""; }; F67294262D1AFC1F0019B57E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Subscribe.storyboard; sourceTree = ""; }; F67294292D1AFC1F0019B57E /* mul */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; name = mul; path = mul.lproj/Subscribe.xcstrings; sourceTree = ""; }; F6786BB62C709880005B985B /* InfoPlist.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = InfoPlist.xcstrings; sourceTree = ""; }; - F67E7827285DDAB100D1CECE /* Toolbar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Toolbar.swift; sourceTree = ""; }; F6832F641F10C4C9007920D4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/ExportAccessoryViewController.xib; sourceTree = ""; }; F6832F671F10F5DA007920D4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainWindowController.xib; sourceTree = ""; }; F685D24B292460210097C0D3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Predicates.strings; sourceTree = ""; }; @@ -565,7 +563,6 @@ F6A243E02C69E534000A006F /* Article+Tags.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "Article+Tags.m"; sourceTree = ""; }; F6A464B8272F47BE0071E3F6 /* NSKeyedUnarchiver+Compatibility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = "NSKeyedUnarchiver+Compatibility.h"; sourceTree = ""; }; F6A464BA272F47BE0071E3F6 /* NSKeyedUnarchiver+Compatibility.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = "NSKeyedUnarchiver+Compatibility.m"; sourceTree = ""; }; - F6A4E8471F040D58001C9191 /* PlugInToolbarItemButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = PlugInToolbarItemButton.swift; sourceTree = ""; }; F6A7DDCA1E470E980017BE5E /* Vienna.help */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Vienna.help; sourceTree = BUILT_PRODUCTS_DIR; }; F6A7DDCC1E470E980017BE5E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; F6A7DE2B1E47138B0017BE5E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = en; path = en.lproj/advanced.html; sourceTree = ""; }; @@ -682,6 +679,7 @@ F6E01A062C652DEE0082E07B /* RSSFeedTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSSFeedTests.swift; sourceTree = ""; }; F6E01A082C652FA50082E07B /* RSSFeedWithContentElements.rss */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = RSSFeedWithContentElements.rss; sourceTree = ""; }; F6EB26021E58D37100570B22 /* DirectoryMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DirectoryMonitor.swift; sourceTree = ""; }; + F6EBC7902F786B9D000B2279 /* ToggleButtonToolbarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToggleButtonToolbarItem.swift; sourceTree = ""; }; F6F12AFB25ABDDE3005B2DCE /* NSFileManager+Paths.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = "NSFileManager+Paths.h"; sourceTree = ""; }; F6F12AFC25ABDDE3005B2DCE /* NSFileManager+Paths.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = "NSFileManager+Paths.m"; sourceTree = ""; }; F6F2029D2D19DD3A004BB948 /* SubscribeViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SubscribeViewController.h; sourceTree = ""; }; @@ -1089,11 +1087,10 @@ F6A7EA571F1CE5DD00BD4450 /* Toolbar */ = { isa = PBXGroup; children = ( - F67E7827285DDAB100D1CECE /* Toolbar.swift */, + F66BF2BD2F6E85E900496980 /* ToolbarItem.swift */, F61CEA651F039E56009C878E /* ButtonToolbarItem.swift */, - F61CEA671F03F277009C878E /* PlugInToolbarItem.swift */, - F6A4E8471F040D58001C9191 /* PlugInToolbarItemButton.swift */, - F62E324D284E0CAF0046EA1F /* SharingServiceToolbarItem.swift */, + F6EBC7902F786B9D000B2279 /* ToggleButtonToolbarItem.swift */, + F61CEA671F03F277009C878E /* RepresentingToolbarItem.swift */, ); name = Toolbar; sourceTree = ""; @@ -1927,6 +1924,7 @@ 435026E6165DD8BE0018EDB7 /* ArticleRef.m in Sources */, F6D0089C1EF95C9D008F2D3B /* InfoPanelManager.m in Sources */, F6C983002E11ABD4005BA1F8 /* NSResponder+EventHandler.m in Sources */, + F6EBC7912F786B9D000B2279 /* ToggleButtonToolbarItem.swift in Sources */, F6C9DA73271BB55000FC3027 /* AtomFeed.m in Sources */, 4350283E165DE7F60018EDB7 /* NSNotificationAdditions.m in Sources */, 2FEA5829291FD511008C42D3 /* Criteria+NSPredicate.swift in Sources */, @@ -1946,7 +1944,6 @@ 43502898165DE9E00018EDB7 /* ArticleController.m in Sources */, 4350289A165DE9E00018EDB7 /* ArticleListView.m in Sources */, F615824323834D6500D2BD41 /* FeedDiscoverer.swift in Sources */, - F67E7828285DDAB200D1CECE /* Toolbar.swift in Sources */, 2FE44CB525B79EDE00554E82 /* WebKitContextMenuCustomizer.swift in Sources */, F61CEA661F039E57009C878E /* ButtonToolbarItem.swift in Sources */, F69743EB2ADAC49D006C5BBC /* UserNotificationCenterDelegate.swift in Sources */, @@ -1970,15 +1967,14 @@ 435028A3165DE9E00018EDB7 /* Export.m in Sources */, 435028A4165DE9E00018EDB7 /* FeedCredentials.m in Sources */, 2F3EC7E7296B138700C5F403 /* Criteria.swift in Sources */, - F62E324E284E0CAF0046EA1F /* SharingServiceToolbarItem.swift in Sources */, 435028A6165DE9E00018EDB7 /* FoldersTree.m in Sources */, + F66BF2BE2F6E85F000496980 /* ToolbarItem.swift in Sources */, 435028A7165DE9E00018EDB7 /* FolderView.m in Sources */, B8B9D6D323685C7400EAE65C /* XMLFeedItem.swift in Sources */, 435028A8165DE9E00018EDB7 /* Import.m in Sources */, 435028A9165DE9E00018EDB7 /* InfoPanelController.m in Sources */, 435028AA165DE9E00018EDB7 /* MessageListView.m in Sources */, 435028AB165DE9E00018EDB7 /* NewGroupFolder.m in Sources */, - F6A4E8481F040D58001C9191 /* PlugInToolbarItemButton.swift in Sources */, 2F33DB3229244ADB00A9716A /* DoesNotContainPredicateEditorRowTemplate.swift in Sources */, F672876824A2D2170043432F /* UpdatePreferencesViewController.m in Sources */, 2FC4A1BF25852C0E005FF227 /* ArticleConverter.swift in Sources */, @@ -2018,7 +2014,7 @@ 3A171C00210B551B00B80FBB /* TRVSURLSessionOperation.m in Sources */, F6373A231F1D53B500ED3B09 /* MainWindow.swift in Sources */, 435028B9165DE9E00018EDB7 /* ViennaApp.m in Sources */, - F61CEA681F03F277009C878E /* PlugInToolbarItem.swift in Sources */, + F61CEA681F03F277009C878E /* RepresentingToolbarItem.swift in Sources */, 3A7BD0DD1989AC7700E9444B /* VNAVerticallyCenteredTextFieldCell.m in Sources */, 2F2280352271B041005D1023 /* ArticleContentView.swift in Sources */, F6B7F9CF2E22E7BE005ADB06 /* DownloadViewController.m in Sources */, diff --git a/Vienna/Interfaces/Base.lproj/MainWindowController.xib b/Vienna/Interfaces/Base.lproj/MainWindowController.xib index d9e77c20b0..3418bc0797 100644 --- a/Vienna/Interfaces/Base.lproj/MainWindowController.xib +++ b/Vienna/Interfaces/Base.lproj/MainWindowController.xib @@ -1,8 +1,8 @@ - + - + @@ -214,131 +214,48 @@ - + - - + - - + - - + - - + - - - - - - - - + - - + - - + - - - - - - - @@ -789,12 +706,9 @@ - - - diff --git a/Vienna/Interfaces/mul.lproj/MainWindowController.xcstrings b/Vienna/Interfaces/mul.lproj/MainWindowController.xcstrings index fbe392be0b..81b8fe5175 100644 --- a/Vienna/Interfaces/mul.lproj/MainWindowController.xcstrings +++ b/Vienna/Interfaces/mul.lproj/MainWindowController.xcstrings @@ -3614,366 +3614,6 @@ } } }, - "Nmd-zx-Dys.label" : { - "comment" : "Class = \"NSToolbarItem\"; label = \"Refresh\"; ObjectID = \"Nmd-zx-Dys\";", - "extractionState" : "extracted_with_value", - "localizations" : { - "cs" : { - "stringUnit" : { - "state" : "translated", - "value" : "Obnovit" - } - }, - "da" : { - "stringUnit" : { - "state" : "translated", - "value" : "Opdater" - } - }, - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Aktualisieren" - } - }, - "en" : { - "stringUnit" : { - "state" : "new", - "value" : "Refresh" - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Actualizar" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Rafraîchir" - } - }, - "gl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Actualizar" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Aggiorna" - } - }, - "lt" : { - "stringUnit" : { - "state" : "translated", - "value" : "Atnaujinti" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Vernieuw" - } - }, - "pt-BR" : { - "stringUnit" : { - "state" : "translated", - "value" : "Atualizar" - } - }, - "pt-PT" : { - "stringUnit" : { - "state" : "translated", - "value" : "Actualizar" - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Обновить" - } - }, - "sv" : { - "stringUnit" : { - "state" : "translated", - "value" : "Uppdatera" - } - }, - "tr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Yenile" - } - }, - "uk" : { - "stringUnit" : { - "state" : "translated", - "value" : "Оновити" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "刷新" - } - }, - "zh-Hant" : { - "stringUnit" : { - "state" : "translated", - "value" : "重新整理" - } - } - } - }, - "Nmd-zx-Dys.paletteLabel" : { - "comment" : "Class = \"NSToolbarItem\"; paletteLabel = \"Refresh\"; ObjectID = \"Nmd-zx-Dys\";", - "extractionState" : "extracted_with_value", - "localizations" : { - "cs" : { - "stringUnit" : { - "state" : "translated", - "value" : "Obnovit" - } - }, - "da" : { - "stringUnit" : { - "state" : "translated", - "value" : "Opdater" - } - }, - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Aktualisieren" - } - }, - "en" : { - "stringUnit" : { - "state" : "new", - "value" : "Refresh" - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Actualizar" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Rafraîchir" - } - }, - "gl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Actualizar" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Aggiorna" - } - }, - "lt" : { - "stringUnit" : { - "state" : "translated", - "value" : "Atnaujinti" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Vernieuw" - } - }, - "pt-BR" : { - "stringUnit" : { - "state" : "translated", - "value" : "Atualizar" - } - }, - "pt-PT" : { - "stringUnit" : { - "state" : "translated", - "value" : "Actualizar" - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Обновить" - } - }, - "sv" : { - "stringUnit" : { - "state" : "translated", - "value" : "Uppdatera" - } - }, - "tr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Yenile" - } - }, - "uk" : { - "stringUnit" : { - "state" : "translated", - "value" : "Оновити" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "刷新" - } - }, - "zh-Hant" : { - "stringUnit" : { - "state" : "translated", - "value" : "重新整理" - } - } - } - }, - "Nmd-zx-Dys.toolTip" : { - "comment" : "Class = \"NSToolbarItem\"; toolTip = \"Refresh all your subscriptions\"; ObjectID = \"Nmd-zx-Dys\";", - "extractionState" : "extracted_with_value", - "localizations" : { - "cs" : { - "stringUnit" : { - "state" : "translated", - "value" : "Obnoví všechna odebírání" - } - }, - "da" : { - "stringUnit" : { - "state" : "translated", - "value" : "Opdater alle dine abonnementer" - } - }, - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Alle Abonnements aktualisieren" - } - }, - "en" : { - "stringUnit" : { - "state" : "new", - "value" : "Refresh all your subscriptions" - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Actualizar todas las suscripciones" - } - }, - "eu" : { - "stringUnit" : { - "state" : "translated", - "value" : "Harpidetza guztiak eguneratu" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Rafraîchir tous les abonnements" - } - }, - "gl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Actualizar todas as subscricións" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Aggiorna tutte le tue iscrizioni" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "すべての購読を更新" - } - }, - "ko" : { - "stringUnit" : { - "state" : "translated", - "value" : "모든 구독 갱신" - } - }, - "lt" : { - "stringUnit" : { - "state" : "translated", - "value" : "Atnaujinti visas prenumeratas" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Werk alle abonnementen bij" - } - }, - "pt-BR" : { - "stringUnit" : { - "state" : "translated", - "value" : "Atualizar todas as suas assinaturas" - } - }, - "pt-PT" : { - "stringUnit" : { - "state" : "translated", - "value" : "Actualizar todas as suas assinaturas" - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Обновить все подписки" - } - }, - "sv" : { - "stringUnit" : { - "state" : "translated", - "value" : "Uppdatera alla prenumerationer" - } - }, - "tr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Tüm aboneliklerinizi yenileyin" - } - }, - "uk" : { - "stringUnit" : { - "state" : "translated", - "value" : "Оновити всі підписки" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "更新所有的频道" - } - }, - "zh-Hant" : { - "stringUnit" : { - "state" : "translated", - "value" : "重新整理所有的訂閱頻道" - } - } - } - }, "Pqr-dM-SQQ.title" : { "comment" : "Class = \"NSMenuItem\"; title = \"New Smart Folder…\"; ObjectID = \"Pqr-dM-SQQ\";", "extractionState" : "extracted_with_value", @@ -6328,138 +5968,6 @@ } } }, - "YVR-Ym-Do7.label" : { - "comment" : "Class = \"NSToolbarItem\"; label = \"Share\"; ObjectID = \"YVR-Ym-Do7\";", - "extractionState" : "extracted_with_value", - "localizations" : { - "da" : { - "stringUnit" : { - "state" : "translated", - "value" : "Del" - } - }, - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Teilen" - } - }, - "en" : { - "stringUnit" : { - "state" : "new", - "value" : "Share" - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Compartir" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Partager" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Condividi" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Deel" - } - }, - "pt-PT" : { - "stringUnit" : { - "state" : "translated", - "value" : "Compartilhar" - } - }, - "sv" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dela" - } - }, - "zh-Hant" : { - "stringUnit" : { - "state" : "translated", - "value" : "分享" - } - } - } - }, - "YVR-Ym-Do7.paletteLabel" : { - "comment" : "Class = \"NSToolbarItem\"; paletteLabel = \"Share\"; ObjectID = \"YVR-Ym-Do7\";", - "extractionState" : "extracted_with_value", - "localizations" : { - "da" : { - "stringUnit" : { - "state" : "translated", - "value" : "Del" - } - }, - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Teilen" - } - }, - "en" : { - "stringUnit" : { - "state" : "new", - "value" : "Share" - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Compartir" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Partager" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Condividi" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Deel" - } - }, - "pt-PT" : { - "stringUnit" : { - "state" : "translated", - "value" : "Compartilhar" - } - }, - "sv" : { - "stringUnit" : { - "state" : "translated", - "value" : "Dela" - } - }, - "zh-Hant" : { - "stringUnit" : { - "state" : "translated", - "value" : "分享" - } - } - } - }, "Zar-1U-8CD.ibShadowedToolTip" : { "comment" : "Class = \"NSImageView\"; ibShadowedToolTip = \"An error occurred when this feed was last refreshed\"; ObjectID = \"Zar-1U-8CD\";", "extractionState" : "extracted_with_value", diff --git a/Vienna/Resources/Assets.xcassets/Toolbar/CancelTemplate.imageset/Contents.json b/Vienna/Resources/Assets.xcassets/Toolbar/CancelTemplate.imageset/Contents.json new file mode 100644 index 0000000000..70114aafaf --- /dev/null +++ b/Vienna/Resources/Assets.xcassets/Toolbar/CancelTemplate.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "mac", + "filename" : "ViCancel.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Vienna/Resources/Assets.xcassets/Toolbar/CancelTemplate.imageset/ViCancel.pdf b/Vienna/Resources/Assets.xcassets/Toolbar/CancelTemplate.imageset/ViCancel.pdf new file mode 100644 index 0000000000..bedf2e6d13 Binary files /dev/null and b/Vienna/Resources/Assets.xcassets/Toolbar/CancelTemplate.imageset/ViCancel.pdf differ diff --git a/Vienna/Resources/Assets.xcassets/Toolbar/arrow.trianglehead.clockwise.rotate.90.imageset/Contents.json b/Vienna/Resources/Assets.xcassets/Toolbar/SyncTemplate.imageset/Contents.json similarity index 100% rename from Vienna/Resources/Assets.xcassets/Toolbar/arrow.trianglehead.clockwise.rotate.90.imageset/Contents.json rename to Vienna/Resources/Assets.xcassets/Toolbar/SyncTemplate.imageset/Contents.json diff --git a/Vienna/Resources/Assets.xcassets/Toolbar/arrow.trianglehead.clockwise.rotate.90.imageset/ViSync.pdf b/Vienna/Resources/Assets.xcassets/Toolbar/SyncTemplate.imageset/ViSync.pdf similarity index 100% rename from Vienna/Resources/Assets.xcassets/Toolbar/arrow.trianglehead.clockwise.rotate.90.imageset/ViSync.pdf rename to Vienna/Resources/Assets.xcassets/Toolbar/SyncTemplate.imageset/ViSync.pdf diff --git a/Vienna/Resources/Assets.xcassets/Toolbar/arrow.trianglehead.clockwise.rotate.90.imageset/ViSync@2x.pdf b/Vienna/Resources/Assets.xcassets/Toolbar/SyncTemplate.imageset/ViSync@2x.pdf similarity index 100% rename from Vienna/Resources/Assets.xcassets/Toolbar/arrow.trianglehead.clockwise.rotate.90.imageset/ViSync@2x.pdf rename to Vienna/Resources/Assets.xcassets/Toolbar/SyncTemplate.imageset/ViSync@2x.pdf diff --git a/Vienna/Resources/Localizable.xcstrings b/Vienna/Resources/Localizable.xcstrings index 786110e750..59677a5b33 100644 --- a/Vienna/Resources/Localizable.xcstrings +++ b/Vienna/Resources/Localizable.xcstrings @@ -18938,6 +18938,137 @@ } } }, + "Refresh all your subscriptions" : { + "comment" : "Toolbar item tooltip", + "localizations" : { + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Obnoví všechna odebírání" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opdater alle dine abonnementer" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alle Abonnements aktualisieren" + } + }, + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Refresh all your subscriptions" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actualizar todas las suscripciones" + } + }, + "eu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Harpidetza guztiak eguneratu" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rafraîchir tous les abonnements" + } + }, + "gl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actualizar todas as subscricións" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aggiorna tutte le tue iscrizioni" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "すべての購読を更新" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "모든 구독 갱신" + } + }, + "lt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Atnaujinti visas prenumeratas" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Werk alle abonnementen bij" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Atualizar todas as suas assinaturas" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actualizar todas as suas assinaturas" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Обновить все подписки" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uppdatera alla prenumerationer" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tüm aboneliklerinizi yenileyin" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Оновити всі підписки" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "更新所有的频道" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "重新整理所有的訂閱頻道" + } + } + } + }, "Refresh completed" : { "localizations" : { "cs" : { @@ -19062,6 +19193,234 @@ } } }, + "refresh.toolbarItem.label" : { + "comment" : "Toolbar item label", + "extractionState" : "extracted_with_value", + "localizations" : { + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Obnovit" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opdater" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktualisieren" + } + }, + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Refresh" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actualizar" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rafraîchir" + } + }, + "gl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actualizar" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aggiorna" + } + }, + "lt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Atnaujinti" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vernieuw" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Atualizar" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actualizar" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Обновить" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uppdatera" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yenile" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Оновити" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "刷新" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "重新整理" + } + } + } + }, + "refresh.toolbarItem.paletteLabel" : { + "comment" : "Toolbar item palette label", + "extractionState" : "extracted_with_value", + "localizations" : { + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Obnovit" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Opdater" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktualisieren" + } + }, + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Refresh" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actualizar" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rafraîchir" + } + }, + "gl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actualizar" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aggiorna" + } + }, + "lt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Atnaujinti" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vernieuw" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Atualizar" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actualizar" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Обновить" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uppdatera" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yenile" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Оновити" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "刷新" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "重新整理" + } + } + } + }, "Refreshing folder images…" : { "localizations" : { "cs" : { @@ -21131,6 +21490,138 @@ } } }, + "share.toolbarItem.label" : { + "comment" : "Toolbar item label", + "extractionState" : "extracted_with_value", + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Del" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Teilen" + } + }, + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Share" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Compartir" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Partager" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Condividi" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Deel" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Compartilhar" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dela" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "分享" + } + } + } + }, + "share.toolbarItem.paletteLabel" : { + "comment" : "Toolbar item palette label", + "extractionState" : "extracted_with_value", + "localizations" : { + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Del" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Teilen" + } + }, + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Share" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Compartir" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Partager" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Condividi" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Deel" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Compartilhar" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dela" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "分享" + } + } + } + }, "Show Filter Bar" : { "localizations" : { "cs" : { @@ -26202,4 +26693,4 @@ } }, "version" : "1.0" -} \ No newline at end of file +} diff --git a/Vienna/Sources/Application/AppController.m b/Vienna/Sources/Application/AppController.m index 08fd010be7..cd0f50f4a6 100644 --- a/Vienna/Sources/Application/AppController.m +++ b/Vienna/Sources/Application/AppController.m @@ -1522,22 +1522,20 @@ - (void)handleTabCountChange:(NSNotification *)nc */ -(void)handleRefreshStatusChange:(NSNotification *)nc { - if (self.connecting) { - // Toggle the refresh button - NSToolbarItem *item = [self toolbarItemWithIdentifier:@"Refresh"]; + VNAToggleButtonToolbarItem *item = + (VNAToggleButtonToolbarItem *)[self toolbarItemWithIdentifier:@"Refresh"]; + if (self.connecting) { + // Toggle the refresh button item.action = @selector(cancelAllRefreshesToolbar:); - NSButton *button = (NSButton *)item.view; - button.state = NSControlStateValueOn; + item.state = NSControlStateValueOn; } else { // Run the auto-expire now Preferences * prefs = [Preferences standardPreferences]; [db purgeArticlesOlderThanTag:prefs.autoExpireDuration]; // Toggle the refresh button - NSToolbarItem *item = [self toolbarItemWithIdentifier:@"Refresh"]; item.action = @selector(refreshAllSubscriptions:); - NSButton *button = (NSButton *)item.view; - button.state = NSControlStateValueOff; + item.state = NSControlStateValueOff; [self showUnreadCountOnApplicationIconAndWindowTitle]; diff --git a/Vienna/Sources/Main window/ButtonToolbarItem.swift b/Vienna/Sources/Main window/ButtonToolbarItem.swift index eb12b8d99e..150b20a7a3 100644 --- a/Vienna/Sources/Main window/ButtonToolbarItem.swift +++ b/Vienna/Sources/Main window/ButtonToolbarItem.swift @@ -24,56 +24,60 @@ import Cocoa @objc(VNAButtonToolbarItem) class ButtonToolbarItem: NSToolbarItem { - // image property - override var image: NSImage? { - get { - if let button = view as? NSButton { - return button.image - } else { - return super.image - } - } - set { - if let button = view as? NSButton { - button.image = newValue - } else { - super.image = newValue - } + override init(itemIdentifier: NSToolbarItem.Identifier) { + super.init(itemIdentifier: itemIdentifier) + + let button = NSButton() + button.bezelStyle = .toolbar + // Ensure that the toolbar item has the same width as the other toolbar + // items in macOS 10.15. + if #unavailable(macOS 11) { + let width = NSToolbarItem.defaultSize.width - 2.0 + button.translatesAutoresizingMaskIntoConstraints = false + button.widthAnchor.constraint(equalToConstant: width).isActive = true } + view = button + + // The default menuFormRepresentation cannot be used, because it will + // reset the toolbar's display mode. However, the toolbar will validate + // and enable a custom menu item, provided it implements the action. + let menuItem = NSMenuItem( + title: "", + action: #selector(menuFormRepresentationClicked(_:)), + keyEquivalent: "" + ) + menuItem.target = self + menuFormRepresentation = menuItem } - // Assign the item's target to the menu-form representation. - override var target: AnyObject? { + override var label: String { didSet { - if view is NSButton { - menuFormRepresentation?.target = target - } + menuFormRepresentation?.title = label } } - // Assign the item's action to the menu-form representation. - override var action: Selector? { + override var image: NSImage? { didSet { - if view is NSButton { - menuFormRepresentation?.action = action + if #available(macOS 26, *) { + menuFormRepresentation?.image = image } } } - // Assign the item's enabled state to the menu-form representation. - override var isEnabled: Bool { - didSet { - if view is NSButton { - menuFormRepresentation?.isEnabled = isEnabled - } + var button: NSButton? { + get { + view as? NSButton + } + set { + view = newValue } } - // The default implementation of this method does nothing. Overriding this - // will allow any responder object to validate the toolbar item. This method - // is also invoked in text-only mode. + // The default implementation of this method does nothing for toolbar items + // with a custom view. Overriding this will allow any responder object to + // validate the toolbar item. override func validate() { - guard let action = action else { + guard let action else { isEnabled = false return } @@ -86,4 +90,14 @@ class ButtonToolbarItem: NSToolbarItem { } } + // This method mimics a private method of NSToolbarItem that is used by + // default menuFormRepresentation objects. NSToolbarItem conforms to the + // NSMenuItemValidation protocol and will validate the menu item for this + // action, presumably referencing the isEnabled property. + @objc + private func menuFormRepresentationClicked(_ menuItem: NSMenuItem) { + if let action, menuItem == menuFormRepresentation { + NSApp.sendAction(action, to: target, from: self) + } + } } diff --git a/Vienna/Sources/Main window/MainWindowController.swift b/Vienna/Sources/Main window/MainWindowController.swift index e1a66e5bae..7468ab13e7 100644 --- a/Vienna/Sources/Main window/MainWindowController.swift +++ b/Vienna/Sources/Main window/MainWindowController.swift @@ -209,35 +209,51 @@ final class MainWindowController: NSWindowController { forSharingService service: NSSharingService, identifier: NSToolbarItem.Identifier ) -> NSToolbarItem { - let item = SharingServiceToolbarItem(itemIdentifier: identifier, sharingService: service) - item.isBordered = true + let item = RepresentingToolbarItem(itemIdentifier: identifier) + item.representedObject = service item.action = #selector(performSharingService(_:)) return item } @IBAction private func invokeSharingServicePicker(_ sender: Any) { - // The sender is either the menu item in the main menu or the menu-item - // representation of the toolbar item in text-only mode. - if sender is NSMenuItem, let window, let contentView = window.contentView { + // The sender is either the menu item in the main menu, the menu-form + // representation of the toolbar item in label-only mode or the toolbar + // item itself when its isBordered property is enabled. + if sender is NSMenuItem || sender is NSToolbarItem, + let window, + let contentView = window.contentView + { + let layoutRect = window.contentLayoutRect + // The menu or toolbar item does not have a view to which the picker + // could be attached. The window's content view is used instead, but + // a location is still needed. + let xCoordinate: CGFloat + // If the action was sent from within the window (e.g. label-only + // mode), an approximate horizontal location can be retrieved from + // the current NSEvent, otherwise the midpoint of the window content + // layout rect is used. + if let event = NSApp.currentEvent, event.window == window { + xCoordinate = event.locationInWindow.x + } else { + xCoordinate = layoutRect.midX - 1 + } + // Subtract 1 point from the Y coordinate and make the rect 1 point + // in size, so that it fits within the coordinates of the view. + let origin = NSPoint(x: xCoordinate, y: layoutRect.maxY - 1) + let topEdge = NSRect(origin: origin, size: NSSize(width: 1, height: 1)) let picker = NSSharingServicePicker(items: shareableItems) picker.delegate = self - // The menu item does not have a view to which the picker could be - // attached. The window's content view is used instead. The picker - // should attach to the top middle point of the content view. - let layoutRect = window.contentLayoutRect - // Subtract 1 point from the coordinates and make the rect 1 point in - // size, so that it fits within the coordinates of the view. - let xCoordinate = layoutRect.midX - 1 - let yCoordinate = layoutRect.maxY - 1 - let topEdgeRect = NSRect(x: xCoordinate, y: yCoordinate, width: 1, height: 1) - picker.show(relativeTo: topEdgeRect, of: contentView, preferredEdge: .minY) + picker.show(relativeTo: topEdge, of: contentView, preferredEdge: .minY) + return } - // The sender is a button if the user clicked on the toolbar item. + // The sender is a button if the user clicked on the toolbar item in + // icon-and-label mode or icon-only mode. if let button = sender as? NSButton { let picker = NSSharingServicePicker(items: shareableItems) picker.delegate = self picker.show(relativeTo: .zero, of: button, preferredEdge: .minY) + return } } @@ -256,7 +272,7 @@ final class MainWindowController: NSWindowController { } } - if let sharingService = (sender as? SharingServiceToolbarItem)?.service { + if let sharingService = (sender as? RepresentingToolbarItem)?.representedObject as? NSSharingService { sharingService.delegate = self sharingService.perform(withItems: shareableItems) } @@ -416,6 +432,59 @@ extension MainWindowController: NSToolbarDelegate { return item } + if itemIdentifier == .refresh { + let item = ToggleButtonToolbarItem(itemIdentifier: itemIdentifier) + item.action = #selector(AppController.refreshAllSubscriptions(_:)) + if #available(macOS 15, *) { + item.image = NSImage( + systemSymbolName: "arrow.trianglehead.clockwise.rotate.90", + accessibilityDescription: nil + )! + // stopProgressTemplateName is available for macOS 10.13 too, + // but on macOS 11+ it is backed by an SF Symbol, whereas on + // macOS 10.13 it is a bitmap image. Due to a bug in NSButton, + // both image and alternateImage must have the same NSImageRep, + // otherwise the images are not aligned correctly. + item.alternateImage = NSImage(named: NSImage.stopProgressTemplateName) + } else { + item.image = NSImage(resource: .syncTemplate) + item.alternateImage = NSImage(resource: .cancelTemplate) + } + item.label = NSLocalizedString( + "refresh.toolbarItem.label", + value: "Refresh", + comment: "Toolbar item label") + item.paletteLabel = NSLocalizedString( + "refresh.toolbarItem.paletteLabel", + value: "Refresh", + comment: "Toolbar item palette label") + item.toolTip = NSLocalizedString( + "Refresh all your subscriptions", + comment: "Toolbar item tooltip") + return item + } + + if itemIdentifier == .share { + let item = ButtonToolbarItem(itemIdentifier: itemIdentifier) + item.action = #selector(invokeSharingServicePicker(_:)) + if let button = item.button { + // The share sheet should also appear when the user presses down + // the left mouse button, not when the user releases the button. + // This is the behavior of the share button elsewhere in macOS. + button.sendAction(on: .leftMouseDown) + } + item.image = NSImage(named: NSImage.shareTemplateName) + item.label = NSLocalizedString( + "share.toolbarItem.label", + value: "Share", + comment: "Toolbar item label") + item.paletteLabel = NSLocalizedString( + "share.toolbarItem.paletteLabel", + value: "Share", + comment: "Toolbar item palette label") + return item + } + if itemIdentifier == .email { guard let service = NSSharingService(named: .composeEmail) else { return nil diff --git a/Vienna/Sources/Main window/PlugInToolbarItemButton.swift b/Vienna/Sources/Main window/PlugInToolbarItemButton.swift deleted file mode 100644 index 037d835757..0000000000 --- a/Vienna/Sources/Main window/PlugInToolbarItemButton.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// PlugInToolbarItemButton.swift -// Vienna -// -// Copyright 2017 -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Cocoa - -/// A button that can be embedded within a toolbar item. -@objc(VNAPlugInToolbarItemButton) -class PlugInToolbarItemButton: NSButton { - - /// The toolbar item to which the button belongs. - @objc weak var toolbarItem: NSToolbarItem? - -} diff --git a/Vienna/Sources/Main window/SharingServiceToolbarItem.swift b/Vienna/Sources/Main window/RepresentingToolbarItem.swift similarity index 52% rename from Vienna/Sources/Main window/SharingServiceToolbarItem.swift rename to Vienna/Sources/Main window/RepresentingToolbarItem.swift index 163eb6785b..6080ca44ae 100644 --- a/Vienna/Sources/Main window/SharingServiceToolbarItem.swift +++ b/Vienna/Sources/Main window/RepresentingToolbarItem.swift @@ -1,8 +1,8 @@ // -// SharingServiceToolbarItem.swift +// RepresentingToolbarItem.swift // Vienna // -// Copyright 2022 Eitot +// Copyright 2026 Eitot // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -19,18 +19,28 @@ import Cocoa -class SharingServiceToolbarItem: ButtonToolbarItem { +@objc(VNARepresentingToolbarItem) +class RepresentingToolbarItem: NSToolbarItem { - let service: NSSharingService + @objc var representedObject: Any? - init(itemIdentifier: NSToolbarItem.Identifier, sharingService service: NSSharingService) { - self.service = service + override init(itemIdentifier: NSToolbarItem.Identifier) { super.init(itemIdentifier: itemIdentifier) - } - @available(*, unavailable) - override init(itemIdentifier: NSToolbarItem.Identifier) { - fatalError("Use init(itemIdentifier:sharingService:)") + isBordered = true + if #unavailable(macOS 11) { + // In macOS 10.15, toolbar items should have a uniform size. + minSize = NSToolbarItem.defaultSize + maxSize = NSToolbarItem.defaultSize + } } + override var image: NSImage? { + didSet { + if #unavailable(macOS 11) { + // In macOS 10.15, the menu item should not have an image. + menuFormRepresentation?.image = nil + } + } + } } diff --git a/Vienna/Sources/Main window/PlugInToolbarItem.swift b/Vienna/Sources/Main window/ToggleButtonToolbarItem.swift similarity index 51% rename from Vienna/Sources/Main window/PlugInToolbarItem.swift rename to Vienna/Sources/Main window/ToggleButtonToolbarItem.swift index c6b6302c83..288b385557 100644 --- a/Vienna/Sources/Main window/PlugInToolbarItem.swift +++ b/Vienna/Sources/Main window/ToggleButtonToolbarItem.swift @@ -1,8 +1,8 @@ // -// PlugInToolbarItem.swift +// ToggleButtonToolbarItem.swift // Vienna // -// Copyright 2017 +// Copyright 2026 Eitot // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -19,19 +19,33 @@ import Cocoa -/// A toolbar item with a button as its view. -/// -/// - Note: This item should only be initialized programmatically. -@objc(VNAPlugInToolbarItem) -class PlugInToolbarItem: ButtonToolbarItem { +@objc(VNAToggleButtonToolbarItem) +class ToggleButtonToolbarItem: ButtonToolbarItem { override init(itemIdentifier: NSToolbarItem.Identifier) { super.init(itemIdentifier: itemIdentifier) - let button = PlugInToolbarItemButton(frame: NSRect(x: 0, y: 0, width: 41, height: 25)) - button.bezelStyle = .toolbar - button.toolbarItem = self - view = button + if let buttonCell = button?.cell as? NSButtonCell { + // This displays the alternateImage when changing the button state. + buttonCell.showsStateBy = .contentsCellMask + } } + @objc var state: NSControl.StateValue { + get { + button?.state ?? .off + } + set { + button?.state = newValue + } + } + + var alternateImage: NSImage? { + get { + button?.alternateImage + } + set { + button?.alternateImage = newValue + } + } } diff --git a/Vienna/Sources/Main window/Toolbar.swift b/Vienna/Sources/Main window/Toolbar.swift deleted file mode 100644 index 6a13cef3db..0000000000 --- a/Vienna/Sources/Main window/Toolbar.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// Toolbar.swift -// Vienna -// -// Copyright 2022 Eitot -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Cocoa - -@objc(VNAToolbar) -class Toolbar: NSToolbar { - - // Contrary to Apple's documentation, toolbar items are not validated if - // text-only mode is used. This affects toolbar items that do not have a - // menu, currently only ButtonToolbarItem. - override func validateVisibleItems() { - guard displayMode == .labelOnly else { - super.validateVisibleItems() - return - } - - for item in visibleItems ?? [] where item is ButtonToolbarItem { - item.validate() - } - } - -} diff --git a/Vienna/Sources/Main window/ToolbarItem.swift b/Vienna/Sources/Main window/ToolbarItem.swift new file mode 100644 index 0000000000..cf8b15f4fc --- /dev/null +++ b/Vienna/Sources/Main window/ToolbarItem.swift @@ -0,0 +1,54 @@ +// +// ToolbarItem.swift +// Vienna +// +// Copyright 2026 Eitot +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Cocoa + +@available(macOS, deprecated: 11) +@objc(VNAToolbarItem) +class ToolbarItem: NSToolbarItem { + + @available(*, unavailable, message: "Use Interface Builder") + override init(itemIdentifier: NSToolbarItem.Identifier) { + super.init(itemIdentifier: itemIdentifier) + } + + override func awakeFromNib() { + super.awakeFromNib() + + if #unavailable(macOS 11) { + // As of macOS 10.15, toolbar items with the isBordered property set + // will have a button appearance. However, setting this property in + // Interface Builder will only work in macOS 11 and newer. + isBordered = true + + // In macOS 10.15, toolbar items should have a uniform size. + minSize = NSToolbarItem.defaultSize + maxSize = NSToolbarItem.defaultSize + + // In macOS 10.15, the menu item should not have an image. + menuFormRepresentation?.image = nil + } + } +} + +extension NSToolbarItem { + + @available(macOS, deprecated: 11) + static let defaultSize = NSSize(width: 40.0, height: 24.0) +} diff --git a/Vienna/Sources/Plug-ins/PluginManager.m b/Vienna/Sources/Plug-ins/PluginManager.m index f07e20a564..f68051e32f 100644 --- a/Vienna/Sources/Plug-ins/PluginManager.m +++ b/Vienna/Sources/Plug-ins/PluginManager.m @@ -270,14 +270,15 @@ -(NSToolbarItem *)toolbarItemForIdentifier:(NSString *)itemIdentifier return nil; } - VNAPlugInToolbarItem *item = [[VNAPlugInToolbarItem alloc] initWithItemIdentifier:itemIdentifier]; + VNARepresentingToolbarItem *item = + [[VNARepresentingToolbarItem alloc] initWithItemIdentifier:itemIdentifier]; + item.representedObject = plugin; item.label = plugin.displayName; item.paletteLabel = item.label; item.image = plugin.toolbarItemImage; item.target = self; item.action = @selector(pluginInvocator:); item.toolTip = plugin.toolbarItemToolTip; - item.menuFormRepresentation.representedObject = plugin; return item; } @@ -328,15 +329,15 @@ -(BOOL)validateMenuItem:(NSMenuItem *)menuItem -(IBAction)pluginInvocator:(id)sender { VNAActionPlugin *plugin; - if ([sender isKindOfClass:[VNAPlugInToolbarItemButton class]]) { - VNAPlugInToolbarItemButton *button = sender; - plugin = [self actionPluginWithIdentifier:button.toolbarItem.itemIdentifier]; + if ([sender isKindOfClass:[VNARepresentingToolbarItem class]]) { + VNARepresentingToolbarItem *toolbarItem = sender; + plugin = toolbarItem.representedObject; } else { NSMenuItem * menuItem = (NSMenuItem *)sender; plugin = menuItem.representedObject; } - if (plugin) { + if (plugin && [plugin isKindOfClass:[VNAActionPlugin class]]) { // This is a link plugin. There should be a URL field which we invoke and possibly // placeholders to be filled from the current article or website. if ([plugin isKindOfClass:[VNALinkPlugin class]]) {