diff options
author | Brendan Long <self@brendanlong.com> | 2019-02-20 22:21:14 +0300 |
---|---|---|
committer | Brendan Long <self@brendanlong.com> | 2019-02-20 22:21:38 +0300 |
commit | 6905f466acb4b0fefd7f6269940202ff389a52dc (patch) | |
tree | 65e0fa120dca3e45a5ba897876589883315c3997 /src | |
parent | c177d26d40562ea1a4659429180a8a262156bf64 (diff) |
Remove indented empty lines
Diffstat (limited to 'src')
87 files changed, 3337 insertions, 3337 deletions
diff --git a/src/ActionCache.vala b/src/ActionCache.vala index 4bcdd270..4ea0b43c 100644 --- a/src/ActionCache.vala +++ b/src/ActionCache.vala @@ -14,33 +14,33 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.ActionCache : GLib.Object { - + // Similar to CachedActionManager this class collects all the actions done during a period of time // to use that information at a later point of time // however this class does not write the actions to the database // this class is used cache the actions during a sync so the synced data can be updated accordingly afterwards // this prevents articles that were marked as read after the sync began but before the data was written to the database // to suddenly become unread again after the sync (and similar issues) - + private Gee.List<CachedAction> m_list; - + private static ActionCache? m_cache = null; - + public static ActionCache get_default() { if(m_cache == null) { m_cache = new ActionCache(); } - + return m_cache; } - + private ActionCache() { m_list = new Gee.ArrayList<CachedAction>(); } - + public void markArticleRead(string id, ArticleStatus read) { var cachedAction = CachedActions.MARK_READ; @@ -48,11 +48,11 @@ public class FeedReader.ActionCache : GLib.Object { { cachedAction = CachedActions.MARK_UNREAD; } - + var action = new CachedAction(cachedAction, id, ""); addAction(action); } - + public void markArticleStarred(string id, ArticleStatus marked) { var cachedAction = CachedActions.MARK_STARRED; @@ -60,29 +60,29 @@ public class FeedReader.ActionCache : GLib.Object { { cachedAction = CachedActions.MARK_UNSTARRED; } - + var action = new CachedAction(cachedAction, id, ""); addAction(action); } - + public void markFeedRead(string id) { var action = new CachedAction(CachedActions.MARK_READ_FEED, id, ""); addAction(action); } - + public void markCategoryRead(string id) { var action = new CachedAction(CachedActions.MARK_READ_CATEGORY, id, ""); addAction(action); } - + public void markAllRead() { var action = new CachedAction(CachedActions.MARK_READ_ALL, "", ""); addAction(action); } - + private void addAction(CachedAction action) { switch(action.getType()) @@ -93,23 +93,23 @@ public class FeedReader.ActionCache : GLib.Object { case CachedActions.MARK_UNSTARRED: removeOpposite(action); break; - + case CachedActions.MARK_READ_FEED: removeForFeed(action.getID()); break; - + case CachedActions.MARK_READ_CATEGORY: removeForCategory(action.getID()); break; - + case CachedActions.MARK_READ_ALL: removeForALL(); break; } - + m_list.add(action); } - + private void removeOpposite(CachedAction action) { foreach(CachedAction a in m_list) @@ -122,7 +122,7 @@ public class FeedReader.ActionCache : GLib.Object { } } } - + private void removeForFeed(string feedID) { DataBaseReadOnly db = null; @@ -142,7 +142,7 @@ public class FeedReader.ActionCache : GLib.Object { } } } - + private void removeForCategory(string catID) { var feedIDs = DataBase.readOnly().getFeedIDofCategorie(catID); @@ -156,11 +156,11 @@ public class FeedReader.ActionCache : GLib.Object { m_list.remove(a); } } - + removeForFeed(feedID); } } - + private void removeForALL() { foreach(CachedAction a in m_list) @@ -177,7 +177,7 @@ public class FeedReader.ActionCache : GLib.Object { } } } - + public ArticleStatus checkStarred(string articleID, ArticleStatus marked) { var type = CachedActions.NONE; @@ -189,7 +189,7 @@ public class FeedReader.ActionCache : GLib.Object { { type = CachedActions.MARK_UNSTARRED; } - + foreach(CachedAction a in m_list) { if(a.getType() == type @@ -199,17 +199,17 @@ public class FeedReader.ActionCache : GLib.Object { { return ArticleStatus.MARKED; } - + if(type == CachedActions.MARK_UNSTARRED) { return ArticleStatus.UNMARKED; } } } - + return marked; } - + public ArticleStatus checkRead(Article a) { if(a.getUnread() == ArticleStatus.READ) @@ -232,14 +232,14 @@ public class FeedReader.ActionCache : GLib.Object { { case CachedActions.MARK_READ_ALL: return ArticleStatus.READ; - + case CachedActions.MARK_READ_FEED: if(action.getID() == a.getFeedID()) { return ArticleStatus.READ; } break; - + case CachedActions.MARK_READ_CATEGORY: if (db == null) { @@ -257,7 +257,7 @@ public class FeedReader.ActionCache : GLib.Object { } } } - + return a.getUnread(); } } diff --git a/src/Backend/Backend.vala b/src/Backend/Backend.vala index cb9c6b82..578aae0c 100644 --- a/src/Backend/Backend.vala +++ b/src/Backend/Backend.vala @@ -14,9 +14,9 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. namespace FeedReader { - + public class FeedReaderBackend : GLib.Object { - + #if LIBUNITY private Unity.LauncherEntry m_launcher; #endif @@ -27,7 +27,7 @@ namespace FeedReader { private uint m_timeout_source_id = 0; private Mutex m_sync_lock; private delegate void asyncPayload(); - + public signal void syncStarted(); public signal void syncFinished(); public signal void springCleanStarted(); @@ -42,24 +42,24 @@ namespace FeedReader { public signal void opmlImported(); public signal void updateSyncProgress(string progress); public signal void tryLogin(); - + private static FeedReaderBackend? m_backend; - + public static FeedReaderBackend get_default() { if(m_backend == null) { m_backend = new FeedReaderBackend(); } - + return m_backend; } - + private FeedReaderBackend() { Logger.debug("backend: constructor"); var plugID = Settings.general().get_string("plugin"); - + if(plugID == "none") { m_loggedin = LoginResponse.NO_BACKEND; @@ -68,14 +68,14 @@ namespace FeedReader { { login(plugID); } - + #if LIBUNITY m_launcher = Unity.LauncherEntry.get_for_desktop_id("org.gnome.FeedReader.desktop"); updateBadge(); #endif m_cancellable = new GLib.Cancellable(); scheduleSync(Settings.general().get_int("sync")); - + GLib.NetworkMonitor.get_default().network_changed.connect((available) => { if(available) { @@ -86,7 +86,7 @@ namespace FeedReader { setOffline(); } }); - + this.setOffline.connect(() => { m_offline = true; }); @@ -95,7 +95,7 @@ namespace FeedReader { CachedActionManager.get_default().executeActions(); }); } - + public void startSync(bool initSync = false) { m_cancellable.reset(); @@ -104,70 +104,70 @@ namespace FeedReader { callAsync.end(res); }); } - + public void cancelSync() { Logger.warning("backend: Cancel current sync"); m_cancellable.cancel(); } - + public string getVersion() { return AboutInfo.version; } - - + + public bool supportTags() { return FeedServer.get_default().supportTags(); } - + public bool supportCategories() { return FeedServer.get_default().supportCategories(); } - + public bool supportFeedManipulation() { return FeedServer.get_default().supportFeedManipulation(); } - + public bool supportMultiLevelCategories() { return FeedServer.get_default().supportMultiLevelCategories(); } - + public string symbolicIcon() { Logger.debug("backend: symbolicIcon"); return FeedServer.get_default().symbolicIcon(); } - + public string accountName() { return FeedServer.get_default().accountName(); } - + public string getServerURL() { return FeedServer.get_default().getServerURL(); } - + public string uncategorizedID() { return FeedServer.get_default().uncategorizedID(); } - + public bool hideCategoryWhenEmpty(string catID) { return FeedServer.get_default().hideCategoryWhenEmpty(catID); } - + public bool useMaxArticles() { return FeedServer.get_default().useMaxArticles(); } - + public void scheduleSync(int time) { if (m_timeout_source_id > 0) @@ -175,12 +175,12 @@ namespace FeedReader { GLib.Source.remove(m_timeout_source_id); m_timeout_source_id = 0; } - + if(time == 0) { return; } - + m_timeout_source_id = GLib.Timeout.add_seconds_full(GLib.Priority.DEFAULT, time*60, () => { if(!Settings.state().get_boolean("currently-updating") && FeedServer.get_default().pluginLoaded()) @@ -191,7 +191,7 @@ namespace FeedReader { return true; }); } - + private void sync(bool initSync = false, GLib.Cancellable? cancellable = null) { // Prevent multiple concurrent syncs or spring cleanings @@ -204,7 +204,7 @@ namespace FeedReader { Logger.debug("Cant sync because login failed or sync/clean already ongoing"); return; } - + if(Utils.springCleaningNecessary()) { Logger.info("backend: spring cleaning"); @@ -212,31 +212,31 @@ namespace FeedReader { DataBase.writeAccess().springCleaning(); springCleanFinished(); } - + if(cancellable != null && cancellable.is_cancelled()) { return; } - + Logger.info("backend: sync started"); syncStarted(); Settings.state().set_boolean("currently-updating", true); - + if(!checkOnline()) { Logger.info("Cancelling sync because we're not online"); finishSync(); return; } - + if(cancellable != null && cancellable.is_cancelled()) { finishSync(); return; } - + m_cacheSync = true; - + if(initSync && FeedServer.get_default().doInitSync()) { FeedServer.get_default().InitSyncContent(cancellable); @@ -245,13 +245,13 @@ namespace FeedReader { { FeedServer.get_default().syncContent(cancellable); } - + if(cancellable != null && cancellable.is_cancelled()) { finishSync(); return; } - + updateBadge(); m_cacheSync = false; FeedServer.get_default().grabContent.begin(cancellable, (obj, res) => { @@ -264,7 +264,7 @@ namespace FeedReader { m_sync_lock.unlock(); } } - + private void finishSync() { Settings.state().set_boolean("currently-updating", false); @@ -272,23 +272,23 @@ namespace FeedReader { Logger.info("backend: sync finished/cancelled"); syncFinished(); } - + public bool checkOnline() { Logger.debug("backend: checkOnline"); - + if(GLib.NetworkMonitor.get_default().get_connectivity() != GLib.NetworkConnectivity.FULL) { Logger.error("backend: no network available"); } - + if(!FeedServer.get_default().serverAvailable()) { m_loggedin = LoginResponse.UNKNOWN_ERROR; setOffline(); return false; } - + if(m_loggedin != LoginResponse.SUCCESS) { FeedServer.get_default().logout(); @@ -299,19 +299,19 @@ namespace FeedReader { return false; } } - + setOnline(); return true; } - - + + public async bool checkOnlineAsync() { if(!FeedServer.get_default().pluginLoaded()) { return false; } - + Logger.debug("backend: checkOnlineAsync"); bool online = false; SourceFunc callback = checkOnlineAsync.callback; @@ -320,27 +320,27 @@ namespace FeedReader { online = checkOnline(); return null; }; - + new Thread<void*>("checkOnlineAsync", run); yield; return online; } - + public LoginResponse login(string plugName) { Logger.debug("backend: new FeedServer and login"); - + FeedServer.get_default().setActivePlugin(plugName); - + if(!FeedServer.get_default().pluginLoaded()) { Logger.error(@"backend: no active plugin"); m_loggedin = LoginResponse.NO_BACKEND; return m_loggedin; } - + m_loggedin = FeedServer.get_default().login(); - + if(m_loggedin == LoginResponse.SUCCESS) { Settings.general().set_string("plugin", plugName); @@ -354,27 +354,27 @@ namespace FeedReader { { setOffline(); } - - + + Logger.debug("backend: login status = " + m_loggedin.to_string()); return m_loggedin; } - + public LoginResponse isLoggedIn() { return m_loggedin; } - + public bool isOnline() { if(m_loggedin != LoginResponse.SUCCESS) { return false; } - + return true; } - + public void updateArticleRead(Article article) { if(m_offline) @@ -387,11 +387,11 @@ namespace FeedReader { { ActionCache.get_default().markArticleRead(article.getArticleID(), article.getUnread()); } - + asyncPayload pl = () => { FeedServer.get_default().setArticleIsRead(article.getArticleID(), article.getUnread()); }; callAsync.begin((owned)pl, (obj, res) => { callAsync.end(res); }); } - + asyncPayload pl = () => { DataBase.writeAccess().update_article(article); }; callAsync.begin((owned)pl, (obj, res) => { callAsync.end(res); @@ -399,7 +399,7 @@ namespace FeedReader { updateBadge(); }); } - + public void updateArticleMarked(Article article) { if(m_offline) @@ -415,65 +415,65 @@ namespace FeedReader { asyncPayload pl = () => { FeedServer.get_default().setArticleIsMarked(article.getArticleID(), article.getMarked()); }; callAsync.begin((owned)pl, (obj, res) => { callAsync.end(res); }); } - - + + asyncPayload pl = () => { DataBase.writeAccess().update_article(article); }; callAsync.begin((owned)pl, (obj, res) => { callAsync.end(res); refreshFeedListCounter(); }); } - - + + public Tag? createTag(string caption) { if(m_offline) { return null; } - + string tagID = FeedServer.get_default().createTag(caption); var tag = new Tag(tagID, caption, 0); DataBase.writeAccess().write_tag(tag); newFeedList(); - + return tag; } - + public void tagArticle(Article article, Tag tag, bool add) { if(m_offline) { return; } - + if(add) { asyncPayload pl = () => { FeedServer.get_default().tagArticle(article, tag); }; callAsync.begin((owned)pl, (obj, res) => { callAsync.end(res); }); - + article.addTag(tag.getTagID()); - + } else { Logger.debug("backend: remove tag: " + tag.getTagID() + " from article: " + article.getArticleID()); - + asyncPayload pl = () => { FeedServer.get_default().removeArticleTag(article, tag); }; callAsync.begin((owned)pl, (obj, res) => { callAsync.end(res); }); - + article.removeTag(tag.getTagID()); } - + var db = DataBase.writeAccess(); db.update_article(article); - + if(!add && !db.tag_still_used(tag)) { Logger.debug("backend: remove tag completely"); asyncPayload pl2 = () => { FeedServer.get_default().deleteTag(tag.getTagID()); }; callAsync.begin((owned)pl2, (obj, res) => { callAsync.end(res); }); - + asyncPayload pl3 = () => { db.dropTag(tag); }; callAsync.begin((owned)pl3, (obj, res) => { callAsync.end(res); @@ -481,62 +481,62 @@ namespace FeedReader { }); } } - + public Tag renameTag(Tag tag, string newName) { if(m_offline) { return tag; } - + tag.setTitle(newName); - + asyncPayload pl = () => { FeedServer.get_default().renameTag(tag.getTagID(), newName); }; callAsync.begin((owned)pl, (obj, res) => { callAsync.end(res); }); - + asyncPayload pl2 = () => { DataBase.writeAccess().update_tag(tag); }; callAsync.begin((owned)pl2, (obj, res) => { callAsync.end(res); newFeedList(); }); - + return tag; } - + public void deleteTag(Tag tag) { if(m_offline) { return; } - + asyncPayload pl = () => { FeedServer.get_default().deleteTag(tag.getTagID()); }; callAsync.begin((owned)pl, (obj, res) => { callAsync.end(res); }); - + asyncPayload pl2 = () => { DataBase.writeAccess().dropTag(tag); }; callAsync.begin((owned)pl2, (obj, res) => { callAsync.end(res); newFeedList(); }); } - + public void updateTagColor(Tag tag) { DataBase.writeAccess().update_tag(tag); } - + public void resetDB() { var db = DataBase.writeAccess(); db.resetDB(); db.init(); } - + public void resetAccount() { FeedServer.get_default().resetAccount(); } - + public void markFeedAsRead(string feedID, bool isCat) { var useID = FeedServer.get_default().alwaysSetReadByID(); @@ -552,7 +552,7 @@ namespace FeedReader { } articleIDs = StringUtils.join(articleIDsList, ","); } - + if(isCat) { if(m_offline) @@ -591,7 +591,7 @@ namespace FeedReader { }; callAsync.begin((owned)pl, (obj, res) => { callAsync.end(res); }); } - + asyncPayload pl = () => { DataBase.writeAccess().markCategorieRead(feedID); }; callAsync.begin((owned)pl, (obj, res) => { callAsync.end(res); @@ -638,7 +638,7 @@ namespace FeedReader { }; callAsync.begin((owned)pl, (obj, res) => { callAsync.end(res); }); } - + asyncPayload pl = () => { DataBase.writeAccess().markFeedRead(feedID); }; callAsync.begin((owned)pl, (obj, res) => { callAsync.end(res); @@ -648,7 +648,7 @@ namespace FeedReader { }); } } - + public void markAllItemsRead() { var useID = FeedServer.get_default().alwaysSetReadByID(); @@ -663,7 +663,7 @@ namespace FeedReader { } articleIDs = StringUtils.join(articleIDsList, ","); } - + if(m_offline) { if(useID) @@ -700,7 +700,7 @@ namespace FeedReader { }; callAsync.begin((owned)pl, (obj, res) => { callAsync.end(res); }); } - + asyncPayload pl = () => { DataBase.writeAccess().markAllRead(); }; callAsync.begin((owned)pl, (obj, res) => { callAsync.end(res); @@ -709,7 +709,7 @@ namespace FeedReader { updateArticleList(); }); } - + public void removeCategory(string catID) { var db = DataBase.writeAccess(); @@ -721,7 +721,7 @@ namespace FeedReader { moveFeed(feed.getFeedID(), catID); } } - + var cats = db.read_categories(feeds); foreach(var cat in cats) { @@ -730,34 +730,34 @@ namespace FeedReader { moveCategory(cat.getCatID(), uncategorizedID()); } } - + asyncPayload pl = () => { FeedServer.get_default().deleteCategory(catID); }; callAsync.begin((owned)pl, (obj, res) => { callAsync.end(res); }); - + asyncPayload pl2 = () => { db.delete_category(catID); }; callAsync.begin((owned)pl2, (obj, res) => { callAsync.end(res); newFeedList(); }); } - + public void moveCategory(string catID, string newParentID) { asyncPayload pl = () => { FeedServer.get_default().moveCategory(catID, newParentID); }; callAsync.begin((owned)pl, (obj, res) => { callAsync.end(res); }); - + asyncPayload pl2 = () => { DataBase.writeAccess().move_category(catID, newParentID); }; callAsync.begin((owned)pl2, (obj, res) => { callAsync.end(res); newFeedList(); }); } - + public string addCategory(string title, string? parentID = null, bool createLocally = false) { Logger.debug("backend: addCategory " + title); string catID = FeedServer.get_default().createCategory(title, parentID); - + if(createLocally) { string? parent = parentID; @@ -772,22 +772,22 @@ namespace FeedReader { var parentCat = db.read_category(parentID); level = parentCat.getLevel()+1; } - + var cat = new Category(catID, title, 0, 99, parent, level); var list = new Gee.LinkedList<Category>(); list.add(cat); db.write_categories(list); } - + return catID; } - + public void removeCategoryWithChildren(string catID) { var db = DataBase.readOnly(); var feeds = db.read_feeds(); deleteFeedsInCategory(catID, feeds); - + var cats = db.read_categories(feeds); foreach(var cat in cats) { @@ -796,10 +796,10 @@ namespace FeedReader { removeCategoryWithChildren(catID); } } - + removeCategory(catID); } - + private void deleteFeedsInCategory(string catID, Gee.List<Feed> feeds) { foreach(Feed feed in feeds) @@ -810,49 +810,49 @@ namespace FeedReader { } } } - + public void renameCategory(string catID, string newName) { asyncPayload pl = () => { FeedServer.get_default().renameCategory(catID, newName); }; callAsync.begin((owned)pl, (obj, res) => { callAsync.end(res); }); - + asyncPayload pl2 = () => { DataBase.writeAccess().rename_category(catID, newName); }; callAsync.begin((owned)pl2, (obj, res) => { callAsync.end(res); newFeedList(); }); } - + public void renameFeed(string feedID, string newName) { asyncPayload pl = () => { FeedServer.get_default().renameFeed(feedID, newName); }; callAsync.begin((owned)pl, (obj, res) => { callAsync.end(res); }); - + asyncPayload pl2 = () => { DataBase.writeAccess().rename_feed(feedID, newName); }; callAsync.begin((owned)pl2, (obj, res) => { callAsync.end(res); newFeedList(); }); } - + public void moveFeed(string feedID, string currentCatID, string? newCatID = null) { asyncPayload pl = () => { FeedServer.get_default().moveFeed(feedID, newCatID, currentCatID); }; callAsync.begin((owned)pl, (obj, res) => { callAsync.end(res); }); - + asyncPayload pl2 = () => { DataBase.writeAccess().move_feed(feedID, currentCatID, newCatID); }; callAsync.begin((owned)pl2, (obj, res) => { callAsync.end(res); newFeedList(); }); } - + public void addFeed(string feedURL, string cat, bool isID) { string? catID = null; string? newCatName = null; string? feedID = null; - + if(cat != "") { if(isID) @@ -864,7 +864,7 @@ namespace FeedReader { newCatName = cat; } } - + string errmsg; bool success = FeedServer.get_default().addFeed(feedURL, catID, newCatName, out feedID, out errmsg); errmsg = success ? "" : errmsg; @@ -874,12 +874,12 @@ namespace FeedReader { startSync(); } } - + public void removeFeed(string feedID) { asyncPayload pl = () => { FeedServer.get_default().removeFeed(feedID); }; callAsync.begin((owned)pl, (obj, res) => { callAsync.end(res); }); - + asyncPayload pl2 = () => { FeedReader.FavIcon.delete_feed(feedID); DataBase.writeAccess().delete_feed(feedID); @@ -890,19 +890,19 @@ namespace FeedReader { updateArticleList(); }); } - + public void removeFeedOnlyFromCat(string feedID, string catID) { asyncPayload pl = () => { FeedServer.get_default().removeCatFromFeed(feedID, catID); }; callAsync.begin((owned)pl, (obj, res) => { callAsync.end(res); }); - + asyncPayload pl2 = () => { DataBase.writeAccess().removeCatFromFeed(feedID, catID); }; callAsync.begin((owned)pl2, (obj, res) => { callAsync.end(res); newFeedList(); }); } - + public void importOPML(string opml) { asyncPayload pl = () => { FeedServer.get_default().importOPML(opml); }; @@ -911,7 +911,7 @@ namespace FeedReader { opmlImported(); }); } - + public void updateBadge() { #if LIBUNITY @@ -931,7 +931,7 @@ namespace FeedReader { } #endif } - + private async void callAsync(owned asyncPayload func) { SourceFunc callback = callAsync.callback; @@ -942,6 +942,6 @@ namespace FeedReader { }); yield; } - + } } diff --git a/src/Backend/FeedServer.vala b/src/Backend/FeedServer.vala index 622ff868..2e57f12e 100644 --- a/src/Backend/FeedServer.vala +++ b/src/Backend/FeedServer.vala @@ -14,39 +14,39 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.FeedServer : GLib.Object { - + private bool m_pluginLoaded = false; private Peas.ExtensionSet m_extensions; private string? m_activeExtension = null; private FeedServerInterface? m_plugin; private Peas.Engine m_engine; - + public signal void PluginsChanedEvent(); - + private static FeedServer? m_server; - - + + public static FeedServer get_default() { if(m_server == null) { m_server = new FeedServer(); } - + return m_server; } - + private FeedServer() { string pluginPath = Constants.INSTALL_LIBDIR + "/plugins/"; Logger.debug(@"FeedServer: search path for plugins is $pluginPath"); - + m_engine = Peas.Engine.get_default(); m_engine.add_search_path(pluginPath, null); m_engine.enable_loader("python3"); - + m_extensions = new Peas.ExtensionSet(m_engine, typeof(FeedServerInterface)); - + m_extensions.extension_added.connect((info, extension) => { Logger.debug("feedserver: plugin loaded %s".printf(info.get_name())); try @@ -59,7 +59,7 @@ public class FeedReader.FeedServer : GLib.Object { { secrets = Secret.Collection.create_sync(secret_service, "Login", Secret.COLLECTION_DEFAULT, Secret.CollectionCreateFlags.COLLECTION_CREATE_NONE); } - + var settings_backend = null; // FIXME: Why does SettingsBackend.get_default() crash on Arch Linux? (extension as FeedServerInterface).init(settings_backend, secrets); PluginsChanedEvent(); @@ -70,20 +70,20 @@ public class FeedReader.FeedServer : GLib.Object { Logger.error("FeedServer: " + e.message); } }); - + m_extensions.extension_removed.connect((info, extension) => { Logger.debug("feedserver: plugin removed %s".printf(info.get_name())); PluginsChanedEvent(); }); - + m_engine.load_plugin.connect((info) => { Logger.debug("feedserver: engine load %s".printf(info.get_name())); }); - + m_engine.unload_plugin.connect((info) => { Logger.debug("feedserver: engine unload %s".printf(info.get_name())); }); - + if(Settings.general().get_string("plugin") == "none") { LoadAllPlugins(); @@ -93,7 +93,7 @@ public class FeedReader.FeedServer : GLib.Object { LoadPlugin(Settings.general().get_string("plugin")); } } - + public void LoadAllPlugins() { Logger.debug("FeedServer: load all available plugins"); @@ -101,11 +101,11 @@ public class FeedReader.FeedServer : GLib.Object { { m_engine.try_load_plugin(plugin); } - + // have to readd this path, otherwise new icons won't show up Gtk.IconTheme.get_default().add_resource_path("/org/gnome/FeedReader/icons"); } - + private void LoadPlugin(string pluginID) { Logger.debug(@"FeedServer: load plugin $pluginID"); @@ -116,36 +116,36 @@ public class FeedReader.FeedServer : GLib.Object { LoadAllPlugins(); } } - + public bool pluginLoaded() { return m_pluginLoaded; } - + public Peas.ExtensionSet getPlugins() { return m_extensions; } - + public bool setActivePlugin(string pluginID) { m_pluginLoaded = false; m_plugin = null; - + var plugin = m_engine.get_plugin_info(pluginID); - + if(plugin == null) { Logger.error(@"feedserver: failed to load info for \"$pluginID\""); return m_pluginLoaded; } - + Logger.info("Plugin Name: " + plugin.get_name()); Logger.info("Plugin Version: " + plugin.get_version()); Logger.info("Plugin Website: " + plugin.get_website()); Logger.info("Plugin Dir: " + plugin.get_module_dir()); - - + + m_activeExtension = pluginID; m_extensions.foreach((extSet, info, ext) => { var plug = ext as FeedServerInterface; @@ -157,19 +157,19 @@ public class FeedReader.FeedServer : GLib.Object { plug.updateArticleList.connect(() => { FeedReaderBackend.get_default().updateArticleList(); }); plug.showArticleListOverlay.connect(() => { FeedReaderBackend.get_default().showArticleListOverlay(); }); plug.writeArticles.connect((articles) => { writeArticles(articles); }); - + m_plugin = plug; m_pluginLoaded = true; } }); return m_pluginLoaded; } - + public FeedServerInterface? getActivePlugin() { return m_plugin; } - + public void syncContent(GLib.Cancellable? cancellable = null) { if(!serverAvailable()) @@ -177,62 +177,62 @@ public class FeedReader.FeedServer : GLib.Object { Logger.debug("FeedServer: can't sync - not logged in or unreachable"); return; } - + var db = DataBase.writeAccess(); if(syncFeedsAndCategories()) { var categories = new Gee.LinkedList<Category>(); var feeds = new Gee.LinkedList<Feed>(); var tags = new Gee.LinkedList<Tag>(); - + if(cancellable != null && cancellable.is_cancelled()) { return; } - + syncProgress(_("Getting feeds and categories")); - + if(!getFeedsAndCats(feeds, categories, tags, cancellable)) { Logger.error("FeedServer: something went wrong getting categories and feeds"); return; } - + if(cancellable != null && cancellable.is_cancelled()) { return; } - + if(cancellable != null && cancellable.is_cancelled()) { return; } - + // write categories db.reset_exists_flag(); db.write_categories(categories); db.delete_nonexisting_categories(); - + // write feeds db.reset_subscribed_flag(); db.write_feeds(feeds); db.delete_articles_without_feed(); db.delete_unsubscribed_feeds(); - + // write tags db.reset_exists_tag(); db.write_tags(tags); db.update_tags(tags); db.delete_nonexisting_tags(); - + FeedReaderBackend.get_default().newFeedList(); } - + if(cancellable != null && cancellable.is_cancelled()) { return; } - + var drop_articles = (DropArticles)Settings.general().get_enum("drop-articles-after"); DateTime? since = drop_articles.to_start_date(); if(!db.isTableEmpty("articles")) @@ -243,15 +243,15 @@ public class FeedReader.FeedServer : GLib.Object { since = last_sync; } } - + int unread = getUnreadCount(); int max = ArticleSyncCount(); - - + + syncProgress(_("Getting articles")); string row_id = db.getMaxID("articles", "rowid"); int before = row_id != null ? int.parse(row_id) : 0; - + if(unread > max && useMaxArticles()) { getArticles(20, ArticleStatus.MARKED, since, null, false, cancellable); @@ -261,15 +261,15 @@ public class FeedReader.FeedServer : GLib.Object { { getArticles(max, ArticleStatus.ALL, since, null, false, cancellable); } - + if(cancellable != null && cancellable.is_cancelled()) { return; } - + //update fulltext table db.updateFTS(); - + int new_and_unread = db.get_new_unread_count(row_id != null ? int.parse(row_id) : 0); row_id = db.getMaxID("articles", "rowid"); int after = row_id != null ? int.parse(row_id) : 0; @@ -278,75 +278,75 @@ public class FeedReader.FeedServer : GLib.Object { { Notification.send(newArticles, new_and_unread); } - + var drop_weeks = drop_articles.to_weeks(); if(drop_weeks != null) { db.dropOldArticles(-(int)drop_weeks); } - + var now = new DateTime.now_local(); Settings.state().set_int("last-sync", (int)now.to_unix()); - + db.checkpoint(); FeedReaderBackend.get_default().newFeedList(); return; } - + public void InitSyncContent(GLib.Cancellable? cancellable = null) { Logger.debug("FeedServer: initial sync"); - + var db = DataBase.writeAccess(); if(syncFeedsAndCategories()) { var categories = new Gee.LinkedList<Category>(); var feeds = new Gee.LinkedList<Feed>(); var tags = new Gee.LinkedList<Tag>(); - + syncProgress(_("Getting feeds and categories")); - + getFeedsAndCats(feeds, categories, tags, cancellable); - + if(cancellable != null && cancellable.is_cancelled()) { return; } - + if(cancellable != null && cancellable.is_cancelled()) { return; } - + // write categories db.write_categories(categories); - + // write feeds db.write_feeds(feeds); - + // write tags db.write_tags(tags); - + FeedReaderBackend.get_default().newFeedList(); } - + if(cancellable != null && cancellable.is_cancelled()) { return; } - + var drop_articles = (DropArticles)Settings.general().get_enum("drop-articles-after"); DateTime? since = drop_articles.to_start_date(); - + // get marked articles syncProgress(_("Getting starred articles")); getArticles(Settings.general().get_int("max-articles"), ArticleStatus.MARKED, since, null, false, cancellable); - + if(cancellable != null && cancellable.is_cancelled()) { return; } - + // get articles for each tag syncProgress(_("Getting tagged articles")); foreach(var tag_item in db.read_tags()) @@ -357,58 +357,58 @@ public class FeedReader.FeedServer : GLib.Object { return; } } - + if(useMaxArticles()) { //get max-articls amunt like normal sync getArticles(Settings.general().get_int("max-articles"), ArticleStatus.ALL, since, null, false, cancellable); } - + if(cancellable != null && cancellable.is_cancelled()) { return; } - + // get unread articles syncProgress(_("Getting unread articles")); getArticles(getUnreadCount(), ArticleStatus.UNREAD, since, null, false, cancellable); - + if(cancellable != null && cancellable.is_cancelled()) { return; } - + //update fulltext table db.updateFTS(); - + Settings.general().reset("content-grabber"); - + var now = new DateTime.now_local(); Settings.state().set_int("last-sync", (int)now.to_unix()); - + return; } - + private void writeArticles(Gee.List<Article> articles) { if(articles.size > 0) { var db = DataBase.writeAccess(); db.update_articles(articles); - + // Reverse the list var new_articles = new Gee.ArrayList<Article>(); foreach(var article in articles) { new_articles.insert(0, article); } - + db.write_articles(new_articles); FeedReaderBackend.get_default().refreshFeedListCounter(); FeedReaderBackend.get_default().updateArticleList(); } } - + public async void grabContent(GLib.Cancellable? cancellable = null) { if(!Settings.general().get_boolean("download-images") @@ -416,7 +416,7 @@ public class FeedReader.FeedServer : GLib.Object { { return; } - + Logger.debug("FeedServer: grabContent"); var db = DataBase.writeAccess(); var articles = db.readUnfetchedArticles(); @@ -424,14 +424,14 @@ public class FeedReader.FeedServer : GLib.Object { int i = 0; var new_article_content = new Gee.ArrayList<Article>(); GLib.Mutex mutex = GLib.Mutex(); - + if(size > 0) { var session = new Soup.Session(); session.user_agent = Constants.USER_AGENT; session.timeout = 5; session.ssl_strict = false; - + try { var threads = new ThreadPool<Article>.with_owned_data((article) => { @@ -439,7 +439,7 @@ public class FeedReader.FeedServer : GLib.Object { { return; } - + if(Settings.general().get_boolean("content-grabber")) { var grabber = new Grabber(session, article); @@ -456,13 +456,13 @@ public class FeedReader.FeedServer : GLib.Object { } string html = grabber.getArticle(); string xml = "<?xml"; - + while(html.has_prefix(xml)) { int end = html.index_of_char('>'); html = html.slice(end+1, html.length).chug(); } - + article.setHTML(html); } else @@ -474,23 +474,23 @@ public class FeedReader.FeedServer : GLib.Object { { downloadImages(session, article, cancellable); } - + if(cancellable == null || !cancellable.is_cancelled()) { mutex.lock(); new_article_content.add(article); mutex.unlock(); } - + ++i; syncProgress(_(@"Grabbing full content: $i / $size")); }, (int)GLib.get_num_processors(), true); - + foreach(var Article in articles) { threads.add(Article); } - + bool immediate = false; // allow to queue up additional tasks bool wait = true; // function will block until all tasks are done ThreadPool.free((owned)threads, immediate, wait); @@ -499,7 +499,7 @@ public class FeedReader.FeedServer : GLib.Object { { Logger.error("FeedServer.grabContent: " + e.message); } - + foreach(var content in new_article_content) { if (cancellable != null && cancellable.is_cancelled()) @@ -508,24 +508,24 @@ public class FeedReader.FeedServer : GLib.Object { } db.writeContent(content); } - + if (cancellable != null && cancellable.is_cancelled()) { return; } - + //update fulltext table db.updateFTS(); } } - + private void downloadImages(Soup.Session session, Article article, GLib.Cancellable? cancellable = null) { if(!Settings.general().get_boolean("download-images")) { return; } - + var html_cntx = new Html.ParserCtxt(); html_cntx.use_options(Html.ParserOption.NOERROR + Html.ParserOption.NOWARNING); Html.Doc* doc = html_cntx.read_doc(article.getHTML(), ""); @@ -543,32 +543,32 @@ public class FeedReader.FeedServer : GLib.Object { grabberUtils.removeAttributes(doc, "img", "srcset"); grabberUtils.removeAttributes(doc, "img", "sizes"); grabberUtils.addAttributes(doc, "a", "target", "_blank"); - + if(cancellable != null && cancellable.is_cancelled()) { delete doc; return; } - + grabberUtils.saveImages(session, doc, article, cancellable); - + string html = ""; doc->dump_memory_enc(out html); html = grabberUtils.postProcessing(ref html); article.setHTML(html); delete doc; } - + private int ArticleSyncCount() { if(!useMaxArticles()) { return -1; } - + return Settings.general().get_int("max-articles"); } - + // Only used with command-line public static void grabArticle(string url) { @@ -576,7 +576,7 @@ public class FeedReader.FeedServer : GLib.Object { session.user_agent = Constants.USER_AGENT; session.timeout = 5; session.ssl_strict = false; - + var a = new Article ("", "", url, @@ -587,29 +587,29 @@ public class FeedReader.FeedServer : GLib.Object { "", null, new GLib.DateTime.now_local()); - + var grabber = new Grabber(session, a); if(grabber.process()) { grabber.print(); - + string html = Utils.UTF8fix(grabber.getArticle(), false); string title = Utils.UTF8fix(grabber.getTitle(), true); string xml = "<?xml"; - + while(html.has_prefix(xml)) { int end = html.index_of_char('>'); html = html.slice(end+1, html.length).chug(); } - + string path = GLib.Environment.get_user_data_dir() + "/feedreader/debug-article/%s.html".printf(title); - + if(FileUtils.test(path, GLib.FileTest.EXISTS)) { GLib.FileUtils.remove(path); } - + try { var file = GLib.File.new_for_path(path); @@ -618,33 +618,33 @@ public class FeedReader.FeedServer : GLib.Object { { parent.make_directory_with_parents(); } - + var stream = file.create(FileCreateFlags.REPLACE_DESTINATION); - + stream.write(html.data); Logger.debug("Grabber: article html written to " + path); - + string output = Utils.UTF8fix(html, true); - + if(output == "" || output == null) { Logger.error("could not generate preview text"); return; } - + output = output.replace("\n"," "); output = output.replace("_"," "); - + path = GLib.Environment.get_user_data_dir() + "/feedreader/debug-article/%s.txt".printf(title); - + if(FileUtils.test(path, GLib.FileTest.EXISTS)) { GLib.FileUtils.remove(path); } - + file = GLib.File.new_for_path(path); stream = file.create(FileCreateFlags.REPLACE_DESTINATION); - + stream.write(output.data); Logger.debug("Grabber: preview written to " + path); } @@ -658,7 +658,7 @@ public class FeedReader.FeedServer : GLib.Object { Logger.error("FeedServer.grabArticle: article could not be processed " + url); } } - + // Only used with command-line public static void grabImages(string htmlFile, string url) { @@ -666,7 +666,7 @@ public class FeedReader.FeedServer : GLib.Object { session.user_agent = Constants.USER_AGENT; session.timeout = 5; session.ssl_strict = false; - + var html_cntx = new Html.ParserCtxt(); html_cntx.use_options(Html.ParserOption.NOERROR + Html.ParserOption.NOWARNING); Html.Doc* doc = html_cntx.read_file(htmlFile); @@ -675,7 +675,7 @@ public class FeedReader.FeedServer : GLib.Object { Logger.debug("Grabber: parsing failed"); return; } - + var a = new Article ("", "", url, @@ -686,15 +686,15 @@ public class FeedReader.FeedServer : GLib.Object { "", null, new GLib.DateTime.now_local()); - - + + grabberUtils.repairURL("//img", "src", doc, url); grabberUtils.saveImages(session, doc, a); - + string html = ""; doc->dump_memory_enc(out html); html = html.replace("<h3/>", "<h3></h3>"); - + int pos1 = html.index_of("<iframe", 0); int pos2 = -1; while(pos1 != -1) @@ -713,7 +713,7 @@ public class FeedReader.FeedServer : GLib.Object { pos1 = pos3; } } - + try { var file = GLib.File.new_for_path(GLib.Environment.get_user_data_dir() + "/debug-article/ArticleLocalImages.html"); @@ -724,132 +724,132 @@ public class FeedReader.FeedServer : GLib.Object { { Logger.error("FeedServer.grabImages: %s".printf(e.message)); } - + delete doc; } - + public bool supportTags() { if(!m_pluginLoaded) { return false; } - + return m_plugin.supportTags(); } - + public bool doInitSync() { if(!m_pluginLoaded) { return false; } - + return m_plugin.doInitSync(); } - + public string symbolicIcon() { Logger.debug("feedserver: symbolicIcon"); - + if(!m_pluginLoaded) { return "none"; } - + return m_plugin.symbolicIcon(); } - + public string accountName() { if(!m_pluginLoaded) { return "none"; } - + return m_plugin.accountName(); } - + public string getServerURL() { if(!m_pluginLoaded) { return "none"; } - + return m_plugin.getServerURL(); } - + public string uncategorizedID() { if(!m_pluginLoaded) { return ""; } - + return m_plugin.uncategorizedID(); } - + public bool hideCategoryWhenEmpty(string catID) { if(!m_pluginLoaded) { return false; } - + return m_plugin.hideCategoryWhenEmpty(catID); } - + public bool supportCategories() { if(!m_pluginLoaded) { return false; } - + return m_plugin.supportCategories(); } - + public bool supportFeedManipulation() { if(!m_pluginLoaded) { return false; } - + return m_plugin.supportFeedManipulation(); } - + public bool supportMultiLevelCategories() { if(!m_pluginLoaded) { return false; } - + return m_plugin.supportMultiLevelCategories(); } - + public bool supportMultiCategoriesPerFeed() { if(!m_pluginLoaded) { return false; } - + return m_plugin.supportMultiCategoriesPerFeed(); } - + public bool syncFeedsAndCategories() { if(!m_pluginLoaded) { return false; } - + return m_plugin.syncFeedsAndCategories(); } - + // some backends (inoreader, feedly) have the tag-name as part of the ID // but for some of them the tagID changes when the name was changed (inoreader) public bool tagIDaffectedByNameChange() @@ -858,20 +858,20 @@ public class FeedReader.FeedServer : GLib.Object { { return false; } - + return m_plugin.tagIDaffectedByNameChange(); } - + public void resetAccount() { if(!m_pluginLoaded) { return; } - + m_plugin.resetAccount(); } - + // whether or not to use the "max-articles"-setting public bool useMaxArticles() { @@ -879,145 +879,145 @@ public class FeedReader.FeedServer : GLib.Object { { return true; } - + return m_plugin.useMaxArticles(); } - + public LoginResponse login() { return m_plugin.login(); } - + public bool logout() { if(!m_pluginLoaded) { return false; } - + return m_plugin.logout(); } - + public void setArticleIsRead(string articleIDs, ArticleStatus read) { if(!m_pluginLoaded) { return; } - + m_plugin.setArticleIsRead(articleIDs, read); } - + public void setArticleIsMarked(string articleID, ArticleStatus marked) { if(!m_pluginLoaded) { return; } - + m_plugin.setArticleIsMarked(articleID, marked); } - + public bool alwaysSetReadByID() { if(!m_pluginLoaded) { return false; } - + return m_plugin.alwaysSetReadByID(); } - + public void setFeedRead(string feedID) { if(!m_pluginLoaded) { return; } - + m_plugin.setFeedRead(feedID); } - + public void setCategoryRead(string catID) { if(!m_pluginLoaded) { return; } - + m_plugin.setCategoryRead(catID); } - + public void markAllItemsRead() { if(!m_pluginLoaded) { return; } - + m_plugin.markAllItemsRead(); } - + public void tagArticle(Article article, Tag tag) { if(!m_pluginLoaded) { return; } - + m_plugin.tagArticle(article.getArticleID(), tag.getTagID()); } - + public void removeArticleTag(Article article, Tag tag) { if(!m_pluginLoaded) { return; } - + m_plugin.removeArticleTag(article.getArticleID(), tag.getTagID()); } - + public string createTag(string caption) { if(!m_pluginLoaded) { return ""; } - + return m_plugin.createTag(caption); } - + public void deleteTag(string tagID) { if(!m_pluginLoaded) { return; } - + m_plugin.deleteTag(tagID); } - + public void renameTag(string tagID, string title) { if(!m_pluginLoaded) { return; } - + m_plugin.renameTag(tagID, title); } - + public bool serverAvailable() { if(!m_pluginLoaded) { return false; } - + return m_plugin.serverAvailable(); } - + public bool addFeed(string feedURL, string? catID, string? newCatName, out string? feedID, out string errmsg) { if(!m_pluginLoaded) @@ -1026,12 +1026,12 @@ public class FeedReader.FeedServer : GLib.Object { errmsg = "Plugin not loaded"; return false; } - + if(!m_plugin.addFeed(feedURL, catID, newCatName, out feedID, out errmsg)) { return false; } - + int maxArticles = ArticleSyncCount(); DateTime? since = ((DropArticles)Settings.general().get_enum("drop-articles-after")).to_start_date(); var sinceStr = since == null ? "(null)" : since.to_string(); @@ -1039,127 +1039,127 @@ public class FeedReader.FeedServer : GLib.Object { getArticles(maxArticles, ArticleStatus.ALL, since, feedID); return true; } - + public void addFeeds(Gee.List<Feed> feeds) { if(!m_pluginLoaded) { return; } - + m_plugin.addFeeds(feeds); } - + public void removeFeed(string feedID) { if(!m_pluginLoaded) { return; } - + m_plugin.removeFeed(feedID); } - + public void renameFeed(string feedID, string title) { if(!m_pluginLoaded) { return; } - + m_plugin.renameFeed(feedID, title); } - + public void moveFeed(string feedID, string newCatID, string? currentCatID = null) { if(!m_pluginLoaded) { return; } - + m_plugin.moveFeed(feedID, newCatID, currentCatID); } - + public string createCategory(string title, string? parentID = null) { if(!m_pluginLoaded) { return ""; } - + return m_plugin.createCategory(title, parentID); } - + public void renameCategory(string catID, string title) { if(!m_pluginLoaded) { return; } - + m_plugin.renameCategory(catID, title); } - + public void moveCategory(string catID, string newParentID) { if(!m_pluginLoaded) { return; } - + m_plugin.moveCategory(catID, newParentID); } - + public void deleteCategory(string catID) { if(!m_pluginLoaded) { return; } - + m_plugin.deleteCategory(catID); } - + public void removeCatFromFeed(string feedID, string catID) { if(!m_pluginLoaded) { return; } - + m_plugin.removeCatFromFeed(feedID, catID); } - + public void importOPML(string opml) { if(!m_pluginLoaded) { return; } - + m_plugin.importOPML(opml); } - + public bool getFeedsAndCats(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags, GLib.Cancellable? cancellable = null) { if(!m_pluginLoaded) { return false; } - + return m_plugin.getFeedsAndCats(feeds, categories, tags); } - + public int getUnreadCount() { if(!m_pluginLoaded) { return 0; } - + return m_plugin.getUnreadCount(); } - + public void getArticles(int count, ArticleStatus whatToGet = ArticleStatus.ALL, DateTime? since = null, string? feedID = null, bool isTagID = false, GLib.Cancellable? cancellable = null) { if(!m_pluginLoaded) @@ -1167,14 +1167,14 @@ public class FeedReader.FeedServer : GLib.Object { Logger.error("getArticles() called with no plugin loaded"); return; } - + m_plugin.getArticles(count, whatToGet, since, feedID, isTagID); } - + private void syncProgress(string text) { FeedReaderBackend.get_default().updateSyncProgress(text); Settings.state().set_string("sync-status", text); } - + } diff --git a/src/Backend/FeedServerInterface.vala b/src/Backend/FeedServerInterface.vala index 5682bf7e..e17e89ef 100644 --- a/src/Backend/FeedServerInterface.vala +++ b/src/Backend/FeedServerInterface.vala @@ -14,81 +14,81 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public abstract class FeedReader.FeedServerInterface : Peas.ExtensionBase { - + public signal void newFeedList(); public signal void refreshFeedListCounter(); public signal void updateArticleList(); public signal void showArticleListOverlay(); public signal void writeArticles(Gee.List<Article> articles); - + public abstract void init(GLib.SettingsBackend? settings_backend, Secret.Collection secrets); - + public abstract bool supportTags(); - + public abstract bool doInitSync(); - + public abstract string symbolicIcon(); - + public abstract string accountName(); - + public abstract string getServerURL(); - + public abstract string uncategorizedID(); - + public abstract bool hideCategoryWhenEmpty(string catID); - + public abstract bool supportCategories(); - + public abstract bool supportFeedManipulation(); - + public abstract bool supportMultiLevelCategories(); - + public abstract bool supportMultiCategoriesPerFeed(); - + public abstract bool syncFeedsAndCategories(); - + // some backends (inoreader, feedly) have the tag-name as part of the ID // but for some of them the tagID changes when the name was changed (inoreader) public abstract bool tagIDaffectedByNameChange(); - + public abstract void resetAccount(); - + // whether or not to use the "max-articles"-setting public abstract bool useMaxArticles(); - + public abstract LoginResponse login(); - + public virtual bool logout() { return true; } - + public abstract bool alwaysSetReadByID(); - + public abstract void setArticleIsRead(string articleIDs, ArticleStatus read); - + public abstract void setArticleIsMarked(string articleID, ArticleStatus marked); - + public abstract void setFeedRead(string feedID); - + public abstract void setCategoryRead(string catID); - + public abstract void markAllItemsRead(); - + public abstract void tagArticle(string articleID, string tagID); - + public abstract void removeArticleTag(string articleID, string tagID); - + public abstract string createTag(string caption); - + public abstract void deleteTag(string tagID); - + public abstract void renameTag(string tagID, string title); - + public abstract bool serverAvailable(); - + public abstract bool addFeed(string feedURL, string? catID, string? newCatName, out string feedID, out string errmsg); - + public virtual void addFeeds(Gee.List<Feed> feeds) { string feedID, errmsg; @@ -98,76 +98,76 @@ public abstract class FeedReader.FeedServerInterface : Peas.ExtensionBase { addFeed(feed.getXmlUrl(), catString != "" ? catString : null, null, out feedID, out errmsg); } } - + public abstract void removeFeed(string feedID); - + public abstract void renameFeed(string feedID, string title); - + public abstract void moveFeed(string feedID, string newCatID, string? currentCatID = null); - + public abstract string createCategory(string title, string? parentID = null); - + public abstract void renameCategory(string catID, string title); - + public abstract void moveCategory(string catID, string newParentID); - + public abstract void deleteCategory(string catID); - + public abstract void removeCatFromFeed(string feedID, string catID); - + public virtual void importOPML(string opml) { var parser = new OPMLparser(opml); var feeds = parser.parse(); addFeeds(feeds); } - + public abstract bool getFeedsAndCats(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags, GLib.Cancellable? cancellable = null); - + public abstract int getUnreadCount(); - + public abstract void getArticles(int count, ArticleStatus whatToGet = ArticleStatus.ALL, DateTime? since = null, string? feedID = null, bool isTagID = false, GLib.Cancellable? cancellable = null); - + // UI stuff public signal void tryLogin(); - + public abstract string getWebsite(); - + public abstract BackendFlags getFlags(); - + public abstract string getID(); - + public virtual Gtk.Box? getWidget() { return null; } - + public abstract string iconName(); - + public abstract string serviceName(); - + public abstract bool needWebLogin(); - + public virtual void showHtAccess() { } - + public virtual void writeData() { } - + public virtual async void postLoginAction() { } - + public virtual bool extractCode(string redirectURL) { return false; } - + public virtual string buildLoginURL() { return ""; } - + } diff --git a/src/Backend/OPMLparser.vala b/src/Backend/OPMLparser.vala index 734d852d..2f4884dd 100644 --- a/src/Backend/OPMLparser.vala +++ b/src/Backend/OPMLparser.vala @@ -14,16 +14,16 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.OPMLparser : GLib.Object { - + private string m_opmlString; private uint m_level = 0; private Gee.List<Feed> m_feeds = new Gee.ArrayList<Feed>(); - + public OPMLparser(string opml) { m_opmlString = opml; } - + public Gee.List<Feed> parse() { if(!m_feeds.is_empty) @@ -31,22 +31,22 @@ public class FeedReader.OPMLparser : GLib.Object { Logger.error("OPMLParser is not meant to be re-used!"); m_feeds = new Gee.ArrayList<Feed>(); } - + Xml.Doc* doc = Xml.Parser.read_doc(m_opmlString, null, null, 0); if(doc == null) { Logger.error("OPML: parsing xml failed"); return m_feeds; } - + Xml.Node* root = doc->get_root_element(); if(root->name != "opml") { return m_feeds; } - + Logger.debug("OPML version: " + root->get_prop("version")); - + for(var node = root->children; node != null; node = node->next) { if(node->type == Xml.ElementType.ELEMENT_NODE) @@ -56,17 +56,17 @@ public class FeedReader.OPMLparser : GLib.Object { case "head": parseHead(node); break; - + case "body": parseTree(node); break; } } } - + return m_feeds; } - + private void parseHead(Xml.Node* root) { Logger.debug("Parse OPML head"); @@ -79,11 +79,11 @@ public class FeedReader.OPMLparser : GLib.Object { case "title": Logger.debug("Title: " + node->get_content()); break; - + case "dateCreated": Logger.debug("dateCreated: " + node->get_content()); break; - + case "dateModified": Logger.debug("dateModified: " + node->get_content()); break; @@ -91,7 +91,7 @@ public class FeedReader.OPMLparser : GLib.Object { } } } - + private void parseTree(Xml.Node* root, string? catID = null) { m_level++; @@ -115,7 +115,7 @@ public class FeedReader.OPMLparser : GLib.Object { } m_level--; } - + private void parseCat(Xml.Node* node, string? parentCatID = null) { string title = "No Title"; @@ -127,12 +127,12 @@ public class FeedReader.OPMLparser : GLib.Object { { title = node->get_prop("title"); } - + Logger.debug(space() + "Category: " + title); string catID = FeedReaderBackend.get_default().addCategory(title, parentCatID, true); parseTree(node, catID); } - + private void parseFeed(Xml.Node* node, string? catID = null) { if(node->get_prop("type") == "rss" || node->get_prop("type") == "atom") @@ -147,9 +147,9 @@ public class FeedReader.OPMLparser : GLib.Object { title = node->get_prop("title"); } string feedURL = node->get_prop("xmlUrl"); - + string website = ""; - + if(hasProp(node, "htmlUrl")) { website = node->get_prop("htmlUrl"); @@ -159,7 +159,7 @@ public class FeedReader.OPMLparser : GLib.Object { { Logger.debug(space() + "Feed: " + title + " feedURL: " + feedURL); } - + var categories = new Gee.ArrayList<string>(); if(catID == null) { @@ -169,21 +169,21 @@ public class FeedReader.OPMLparser : GLib.Object { { categories.add(catID); } - + m_feeds.add(new Feed("", title, website, 0, categories, null, feedURL)); } } - + private bool hasProp(Xml.Node* node, string prop) { if(node->get_prop(prop) != null) { return true; } - + return false; } - + private string space() { string tmp = ""; @@ -191,7 +191,7 @@ public class FeedReader.OPMLparser : GLib.Object { { tmp += " "; } - + return tmp; } } diff --git a/src/CachedActionManager.vala b/src/CachedActionManager.vala index 128d0f1d..b6ee6743 100644 --- a/src/CachedActionManager.vala +++ b/src/CachedActionManager.vala @@ -14,28 +14,28 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.CachedActionManager : GLib.Object { - + private CachedActions m_lastAction = CachedActions.NONE; private string m_ids = ""; - + private static CachedActionManager? m_manager = null; - + public static CachedActionManager get_default() { if(m_manager == null) { m_manager = new CachedActionManager(); } - + return m_manager; } - + private CachedActionManager() { - + } - - + + public void markArticleRead(string id, ArticleStatus read) { var cachedAction = CachedActions.MARK_READ; @@ -43,11 +43,11 @@ public class FeedReader.CachedActionManager : GLib.Object { { cachedAction = CachedActions.MARK_UNREAD; } - + var action = new CachedAction(cachedAction, id, ""); addAction(action); } - + public void markArticleStarred(string id, ArticleStatus marked) { var cachedAction = CachedActions.MARK_STARRED; @@ -55,29 +55,29 @@ public class FeedReader.CachedActionManager : GLib.Object { { cachedAction = CachedActions.MARK_UNSTARRED; } - + var action = new CachedAction(cachedAction, id, ""); addAction(action); } - + public void markFeedRead(string id) { var action = new CachedAction(CachedActions.MARK_READ_FEED, id, ""); addAction(action); } - + public void markCategoryRead(string id) { var action = new CachedAction(CachedActions.MARK_READ_CATEGORY, id, ""); addAction(action); } - + public void markAllRead() { var action = new CachedAction(CachedActions.MARK_READ_ALL, "", ""); addAction(action); } - + private void addAction(CachedAction action) { var db = DataBase.writeAccess(); @@ -90,7 +90,7 @@ public class FeedReader.CachedActionManager : GLib.Object { db.deleteOppositeCachedAction(action); } } - + public void executeActions() { var db = DataBase.writeAccess(); @@ -99,12 +99,12 @@ public class FeedReader.CachedActionManager : GLib.Object { Logger.debug("CachedActionManager - executeActions: no actions to perform"); return; } - - + + Logger.debug("CachedActionManager: executeActions"); - + var actions = db.readCachedActions(); - + foreach(CachedAction action in actions) { Logger.debug("CachedActionManager: executeActions %s %s".printf(action.getID(), action.getType().to_string())); @@ -140,18 +140,18 @@ public class FeedReader.CachedActionManager : GLib.Object { FeedServer.get_default().markAllItemsRead(); break; } - + m_lastAction = action.getType(); } - + if(m_ids != "") { execute(m_ids.substring(1), m_lastAction); } - + db.resetCachedActions(); } - + private void execute(string ids, CachedActions action) { Logger.debug("CachedActionManager: execute %s %s".printf(ids, action.to_string())); @@ -165,5 +165,5 @@ public class FeedReader.CachedActionManager : GLib.Object { break; } } - + } diff --git a/src/ContentGrabber/grabber.vala b/src/ContentGrabber/grabber.vala index 8ea5615f..70d7bbfd 100644 --- a/src/ContentGrabber/grabber.vala +++ b/src/ContentGrabber/grabber.vala @@ -26,13 +26,13 @@ public class FeedReader.Grabber : GLib.Object { private Xml.Ns* m_ns; private bool m_foundSomething; private bool m_singlePage; - - + + public string m_author; public string m_title; public string m_date; public string m_html; - + public Grabber(Soup.Session session, Article article) { m_article = article; @@ -40,24 +40,24 @@ public class FeedReader.Grabber : GLib.Object { { m_article.setURL("http:" + m_article.getURL()); } - + m_articleURL = m_article.getURL(); m_firstPage = true; m_foundSomething = false; m_singlePage = false; m_session = session; } - + ~Grabber() { delete m_doc; delete m_ns; } - + private bool checkConfigFile() { string filepath = Constants.INSTALL_PREFIX + "/share/feedreader/GrabberConfig/"; - + string hostName = grabberUtils.buildHostName(m_articleURL, false); string filename = filepath + hostName + ".txt"; if(FileUtils.test(filename, GLib.FileTest.EXISTS)) @@ -66,9 +66,9 @@ public class FeedReader.Grabber : GLib.Object { Logger.debug("Grabber: using config %s.txt".printf(hostName)); return true; } - + Logger.debug("Grabber: no config (%s.txt) found for article: %s".printf(hostName, m_articleURL)); - + string newHostName = grabberUtils.buildHostName(m_articleURL, true); if(hostName != newHostName) { @@ -80,35 +80,35 @@ public class FeedReader.Grabber : GLib.Object { return true; } } - - + + Logger.debug("Grabber: no config (%s.txt) - cutSubdomain - found for article: %s".printf(newHostName, m_articleURL)); return false; } - + public bool process(GLib.Cancellable? cancellable = null) { Logger.debug("Grabber: process article: " + m_articleURL); - + var uri = new Soup.URI(m_articleURL); if(uri == null) { Logger.error("No valid article-url?!?"); return false; } - + if(!checkContentType()) { return false; } - + if(cancellable != null && cancellable.is_cancelled()) { return false; } - + bool downloaded = false; - + if(!checkConfigFile()) { // special treatment for sites like youtube @@ -120,16 +120,16 @@ public class FeedReader.Grabber : GLib.Object { int start = m_articleURL.index_of(youtube) + youtube.length; int end = m_articleURL.index_of("?", start); string id = m_articleURL.substring(start, (end == -1) ? -1 : end-start); - + m_html = ytHTML.printf(id); return true; } - + string oldURL = m_articleURL; - + // download to check if website redirects downloaded = download(); - + // if URL is different now after redirect if(m_articleURL != oldURL) { @@ -145,50 +145,50 @@ public class FeedReader.Grabber : GLib.Object { return false; } } - + if(cancellable != null && cancellable.is_cancelled()) { return false; } - + Logger.debug("Grabber: config found"); - + if(!downloaded && !download()) { return false; } - - + + Logger.debug("Grabber: download success"); - + prepArticle(); - + Logger.debug("Grabber: empty article preped"); - + if(!parse(cancellable)) { return false; } - + if(!m_foundSomething) { Logger.error("Grabber: no body found"); return false; } - + return true; } - + private bool checkContentType() { Logger.debug("Grabber: check contentType"); var message = new Soup.Message("HEAD", m_articleURL.escape("")); - + if(message == null) { return false; } - + m_session.send_message(message); var params = new GLib.HashTable<string, string>(null, null); string? contentType = message.response_headers.get_content_type(out params); @@ -199,13 +199,13 @@ public class FeedReader.Grabber : GLib.Object { return true; } } - + Logger.debug(@"Grabber: type $contentType"); Logger.debug(@"Grabber: type != text/html so not going to proceed further"); - + return false; } - + private bool download() { var msg = new Soup.Message("GET", m_articleURL.escape("")); @@ -219,39 +219,39 @@ public class FeedReader.Grabber : GLib.Object { Logger.debug("Grabber: new url is: " + m_articleURL); } }); - + if(msg == null) { return false; } - + if(Settings.tweaks().get_boolean("do-not-track")) { msg.request_headers.append("DNT", "1"); } - + m_session.send_message(msg); msg.disconnect(handlerID); - + if(msg.response_body == null) { Logger.debug("Grabber: download failed - no response"); return false; } - + if((string)msg.response_body.flatten().data == "") { Logger.debug("Grabber: download failed - empty response"); return false; } - + m_rawHtml = (string)msg.response_body.flatten().data; if(!m_rawHtml.validate()) { string needle = "content=\"text/html; charset="; int start = m_rawHtml.index_of(needle) + needle.length; string locale = ""; - + if(start != -1) { int end = m_rawHtml.index_of("\"", start); @@ -271,7 +271,7 @@ public class FeedReader.Grabber : GLib.Object { return false; } } - + Logger.info(locale); try { @@ -282,15 +282,15 @@ public class FeedReader.Grabber : GLib.Object { Logger.error("grabber: failed to convert locale - " + e.message); } } - + return true; } - + private bool parse(GLib.Cancellable? cancellable = null) { m_nexPageURL = null; Logger.debug("Grabber: start parsing"); - + // replace strings before parsing html unowned Gee.List<StringPair> replace = m_config.getReplace(); if(replace.size != 0) @@ -300,9 +300,9 @@ public class FeedReader.Grabber : GLib.Object { m_rawHtml = m_rawHtml.replace(pair.getString1(), pair.getString2()); } } - + Logger.debug("Grabber: parse html"); - + // parse html var html_cntx = new Html.ParserCtxt(); html_cntx.use_options(Html.ParserOption.NOERROR + Html.ParserOption.NOWARNING); @@ -312,17 +312,17 @@ public class FeedReader.Grabber : GLib.Object { Logger.debug("Grabber: parsing failed"); return false; } - + Logger.debug("Grabber: html parsed"); - - + + // get link to next page of article if there are more than one pages if(m_config.getXPathNextPageURL() != null) { Logger.debug("Grabber: grab next page url"); m_nexPageURL = grabberUtils.getURL(doc, m_config.getXPathNextPageURL()); } - + // get link to single-page view if it exists and download that page if(m_config.getXPathSinglePageURL() != null && m_nexPageURL == null) { @@ -341,7 +341,7 @@ public class FeedReader.Grabber : GLib.Object { doc = html_cntx.read_doc(m_rawHtml, ""); } } - + // get the title from the html (useful if feed doesn't provide one) unowned Gee.List<string> title = m_config.getXPathTitle(); if(title.size != 0 && m_firstPage) @@ -356,13 +356,13 @@ public class FeedReader.Grabber : GLib.Object { } } } - + if(cancellable != null && cancellable.is_cancelled()) { delete doc; return false; } - + // get the author from the html (useful if feed doesn't provide one) unowned Gee.List<string> author = m_config.getXPathAuthor(); if(author.size != 0) @@ -377,13 +377,13 @@ public class FeedReader.Grabber : GLib.Object { } } } - + if(cancellable != null && cancellable.is_cancelled()) { delete doc; return false; } - + // get the date from the html (useful if feed doesn't provide one) unowned Gee.List<string> date = m_config.getXPathDate(); if(date.size != 0) @@ -398,13 +398,13 @@ public class FeedReader.Grabber : GLib.Object { } } } - + if(cancellable != null && cancellable.is_cancelled()) { delete doc; return false; } - + // strip junk unowned Gee.List<string> strip = m_config.getXPathStrip(); if(strip.size != 0) @@ -416,13 +416,13 @@ public class FeedReader.Grabber : GLib.Object { grabberUtils.stripNode(doc, xpath); } } - + if(cancellable != null && cancellable.is_cancelled()) { delete doc; return false; } - + // strip any element whose @id or @class contains this substring unowned Gee.List<string> _stripIDorClass = m_config.getXPathStripIDorClass(); if(_stripIDorClass.size != 0) @@ -433,13 +433,13 @@ public class FeedReader.Grabber : GLib.Object { grabberUtils.stripIDorClass(doc, IDorClass); } } - + if(cancellable != null && cancellable.is_cancelled()) { delete doc; return false; } - + //strip any <img> element where @src attribute contains this substring unowned Gee.List<string> stripImgSrc = m_config.getXPathStripImgSrc(); if(stripImgSrc.size != 0) @@ -450,13 +450,13 @@ public class FeedReader.Grabber : GLib.Object { grabberUtils.stripNode(doc, "//img[contains(@src,'%s')]".printf(ImgSrc)); } } - + if(cancellable != null && cancellable.is_cancelled()) { delete doc; return false; } - + grabberUtils.fixLazyImg(doc, "lazyload", "data-src"); grabberUtils.fixIframeSize(doc, "youtube.com"); grabberUtils.removeAttributes(doc, null, "style"); @@ -464,13 +464,13 @@ public class FeedReader.Grabber : GLib.Object { grabberUtils.removeAttributes(doc, "img", "srcset"); grabberUtils.removeAttributes(doc, "img", "sizes"); grabberUtils.addAttributes(doc, "a", "target", "_blank"); - + if(cancellable != null && cancellable.is_cancelled()) { delete doc; return false; } - + // complete relative source urls of images Logger.debug("Grabber: complete urls"); grabberUtils.repairURL("//img", "src", doc, m_articleURL); @@ -478,7 +478,7 @@ public class FeedReader.Grabber : GLib.Object { grabberUtils.repairURL("//a", "href", doc, m_articleURL); grabberUtils.repairURL("//object", "data", doc, m_articleURL); grabberUtils.repairURL("//iframe", "src", doc, m_articleURL); - + // strip elements using Readability.com and Instapaper.com ignore class names // .entry-unrelated and .instapaper_ignore // See https://www.readability.com/publishers/guidelines/#view-plainGuidelines @@ -486,43 +486,43 @@ public class FeedReader.Grabber : GLib.Object { Logger.debug("Grabber: strip instapaper and readability"); grabberUtils.stripNode(doc, "//*[contains(concat(' ',normalize-space(@class),' '),' entry-unrelated ') or contains(concat(' ',normalize-space(@class),' '),' instapaper_ignore ')]"); - + if(cancellable != null && cancellable.is_cancelled()) { delete doc; return false; } - + // strip elements that contain style="display: none;" Logger.debug("Grabber: strip invisible elements"); grabberUtils.stripNode(doc, "//*[contains(@style,'display:none')]"); - + // strip all scripts Logger.debug("Grabber: strip all scripts"); grabberUtils.stripNode(doc, "//script"); - + // strip <noscript> Logger.debug("Grabber: strip all <noscript>"); grabberUtils.onlyRemoveNode(doc, "//noscript"); - + // strip all comments Logger.debug("Grabber: strip all comments"); grabberUtils.stripNode(doc, "//comment()"); - + // strip all empty url-tags <a/> Logger.debug("Grabber: strip all empty url-tags"); grabberUtils.stripNode(doc, "//a[not(node())]"); - + // strip all external css and fonts Logger.debug("Grabber: strip all external css and fonts"); grabberUtils.stripNode(doc, "//*[@type='text/css']"); - + if(cancellable != null && cancellable.is_cancelled()) { delete doc; return false; } - + // get the content of the article unowned Gee.List<string> bodyList = m_config.getXPathBody(); if(bodyList.size != 0) @@ -539,7 +539,7 @@ public class FeedReader.Grabber : GLib.Object { Logger.info("Failed to find: " + bodyXPath); } } - + if(m_foundSomething) { Logger.debug("Grabber: body found"); @@ -554,16 +554,16 @@ public class FeedReader.Grabber : GLib.Object { { Logger.error("Grabber: config file has no rule for 'body'"); } - + delete doc; - + if(cancellable != null && cancellable.is_cancelled()) { return false; } - + m_firstPage = false; - + if(m_nexPageURL != null && !m_singlePage) { Logger.debug("Grabber: load next page"); @@ -580,22 +580,22 @@ public class FeedReader.Grabber : GLib.Object { return true; } } - + if(Settings.general().get_boolean("download-images")) { grabberUtils.saveImages(m_session, m_doc, m_article, cancellable); } - + if(cancellable != null && cancellable.is_cancelled()) { return false; } - + m_doc->dump_memory_enc(out m_html); m_html = grabberUtils.postProcessing(ref m_html); return true; } - + private void prepArticle() { m_doc = new Html.Doc("1.0"); @@ -604,35 +604,35 @@ public class FeedReader.Grabber : GLib.Object { m_root = new Xml.Node(m_ns, "body"); m_doc->set_root_element(m_root); } - + public string getArticle() { return m_html; } - + public void print() { if(m_title != null) { Logger.debug("Grabber: title: %s".printf(m_title)); } - + if(m_author != null) { Logger.debug("Grabber: author: %s".printf(m_author)); } - + if(m_date != null) { Logger.debug("Grabber: date: %s".printf(m_date)); } } - + public string? getAuthor() { return m_author; } - + public string? getTitle() { return m_title; diff --git a/src/ContentGrabber/grabberConfig.vala b/src/ContentGrabber/grabberConfig.vala index b2982cba..3afdb498 100644 --- a/src/ContentGrabber/grabberConfig.vala +++ b/src/ContentGrabber/grabberConfig.vala @@ -14,7 +14,7 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.GrabberConfig : GLib.Object { - + private Gee.List<string> m_xpath_title; private Gee.List<string> m_xpath_author; private Gee.List<string> m_xpath_date; @@ -29,7 +29,7 @@ public class FeedReader.GrabberConfig : GLib.Object { private string m_nextPageLink; private Gee.List<StringPair> m_replace; private string m_testURL; - + public GrabberConfig(string filename) { m_xpath_title = new Gee.ArrayList<string>(); @@ -40,25 +40,25 @@ public class FeedReader.GrabberConfig : GLib.Object { m_xpath_stripIDorClass = new Gee.ArrayList<string>(); m_xpath_stripImgSrc = new Gee.ArrayList<string>(); m_replace = new Gee.ArrayList<StringPair>(); - + // init defaults: m_tidy = true; m_prune = true; m_autodetectOnFailure = true; - + var file = File.new_for_path(filename); - + if(!file.query_exists()) { Logger.error("File '%s' doesn't exist.".printf(file.get_path())); return; } - + try { var dis = new DataInputStream(file.read ()); string line; - + while((line = dis.read_line()) != null) { line = line.chug(); @@ -146,20 +146,20 @@ public class FeedReader.GrabberConfig : GLib.Object { error("%s", e.message); } } - + private string extractValue(string identifier, string line) { string res = line.splice(0, identifier.length); - + int index = res.index_of("#"); if(index != -1) { res = res.splice(index, res.length); } - + return res.chug().chomp(); } - + private void splitValues(ref Gee.List<string> list, string line) { var array = line.split(" | "); @@ -168,11 +168,11 @@ public class FeedReader.GrabberConfig : GLib.Object { list.add(tmp); } } - + public void print() { const string TAB = " "; - + if(m_xpath_title.size != 0) { Logger.debug("title:"); @@ -181,7 +181,7 @@ public class FeedReader.GrabberConfig : GLib.Object { Logger.debug(TAB + title); } } - + if(m_xpath_author.size != 0) { Logger.debug("author:"); @@ -190,7 +190,7 @@ public class FeedReader.GrabberConfig : GLib.Object { Logger.debug(TAB + author); } } - + if(m_xpath_date.size != 0) { Logger.debug("date:"); @@ -199,7 +199,7 @@ public class FeedReader.GrabberConfig : GLib.Object { Logger.debug(TAB + date); } } - + if(m_xpath_body.size != 0) { Logger.debug("body:"); @@ -208,7 +208,7 @@ public class FeedReader.GrabberConfig : GLib.Object { Logger.debug(TAB + body); } } - + if(m_xpath_strip.size != 0) { Logger.debug("strip:"); @@ -217,7 +217,7 @@ public class FeedReader.GrabberConfig : GLib.Object { Logger.debug(TAB + strip); } } - + if(m_xpath_stripIDorClass.size != 0) { Logger.debug("stripIDorClass:"); @@ -226,7 +226,7 @@ public class FeedReader.GrabberConfig : GLib.Object { Logger.debug(TAB + stripIDorClass); } } - + if(m_xpath_stripImgSrc.size != 0) { Logger.debug("stripImgSrc:"); @@ -235,7 +235,7 @@ public class FeedReader.GrabberConfig : GLib.Object { Logger.debug(TAB + stripImgSrc); } } - + if(m_tidy) { Logger.debug("tidy: yes"); @@ -244,7 +244,7 @@ public class FeedReader.GrabberConfig : GLib.Object { { Logger.debug("tidy: no"); } - + if(m_prune) { Logger.debug("prune: yes"); @@ -253,7 +253,7 @@ public class FeedReader.GrabberConfig : GLib.Object { { Logger.debug("prune: no"); } - + if(m_autodetectOnFailure) { Logger.debug("autodetectOnFailure: yes"); @@ -262,17 +262,17 @@ public class FeedReader.GrabberConfig : GLib.Object { { Logger.debug("autodetectOnFailure: no"); } - + if(m_singlePageLink != null) { Logger.debug("singlePageLink: " + m_singlePageLink); } - + if(m_nextPageLink != null) { Logger.debug("nextPageLink: " + m_nextPageLink); } - + if(m_replace.size != 0) { Logger.debug("replace:"); @@ -281,59 +281,59 @@ public class FeedReader.GrabberConfig : GLib.Object { Logger.debug("replace %s with %s".printf(tmp.getString1(), tmp.getString2())); } } - + if(m_testURL != null) { Logger.debug("testURL: " + m_testURL); } } - - + + public string getXPathNextPageURL() { return m_nextPageLink; } - + public string getXPathSinglePageURL() { return m_singlePageLink; } - + public unowned Gee.List<string> getXPathTitle() { return m_xpath_title; } - + public unowned Gee.List<string> getXPathAuthor() { return m_xpath_author; } - + public unowned Gee.List<string> getXPathDate() { return m_xpath_date; } - + public unowned Gee.List<string> getXPathStrip() { return m_xpath_strip; } - + public unowned Gee.List<string> getXPathStripIDorClass() { return m_xpath_stripIDorClass; } - + public unowned Gee.List<string> getXPathStripImgSrc() { return m_xpath_stripImgSrc; } - + public unowned Gee.List<string> getXPathBody() { return m_xpath_body; } - + public unowned Gee.List<StringPair> getReplace() { return m_replace; diff --git a/src/ContentGrabber/grabberUtils.vala b/src/ContentGrabber/grabberUtils.vala index 072216ba..3d7a7eaf 100644 --- a/src/ContentGrabber/grabberUtils.vala +++ b/src/ContentGrabber/grabberUtils.vala @@ -14,18 +14,18 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.grabberUtils : GLib.Object { - + public grabberUtils() { - + } - + public static bool extractBody(Html.Doc* doc, string xpath, Xml.Node* destination) { bool foundSomething = false; var cntx = new Xml.XPath.Context(doc); var res = cntx.eval_expression(xpath); - + if(res == null) { return false; @@ -35,32 +35,32 @@ public class FeedReader.grabberUtils : GLib.Object { delete res; return false; } - + for(int i = 0; i < res->nodesetval->length(); i++) { Xml.Node* node = res->nodesetval->item(i); - + // remove property "style" of all tags node->has_prop("style")->remove(); - + node->unlink(); destination->add_child(node); - + if(!foundSomething) { foundSomething = true; } } - + delete res; return foundSomething; } - + public static string? getURL(Html.Doc* doc, string xpath) { var cntx = new Xml.XPath.Context(doc); var res = cntx.eval_expression(xpath); - + if(res == null) { return null; @@ -70,21 +70,21 @@ public class FeedReader.grabberUtils : GLib.Object { delete res; return null; } - + Xml.Node* node = res->nodesetval->item(0); string URL = node->get_prop("href"); - + node->unlink(); node->free_list(); delete res; return URL; } - + public static string? getValue(Html.Doc* doc, string xpath, bool remove = false) { Xml.XPath.Context cntx = new Xml.XPath.Context(doc); Xml.XPath.Object* res = cntx.eval_expression(xpath); - + if(res == null) { return null; @@ -94,26 +94,26 @@ public class FeedReader.grabberUtils : GLib.Object { delete res; return null; } - + Xml.Node* node = res->nodesetval->item(0); string result = cleanString(node->get_content()); - + if(remove) { node->unlink(); node->free_list(); } - + delete res; return result; } - + public static bool repairURL(string xpath, string attr, Html.Doc* doc, string articleURL) { Logger.debug("GrabberUtils: repairURL xpath:\"%s\" attr:\"%s\"".printf(xpath, attr)); Xml.XPath.Context cntx = new Xml.XPath.Context(doc); Xml.XPath.Object* res = cntx.eval_expression(xpath); - + if(res == null) { return false; @@ -123,7 +123,7 @@ public class FeedReader.grabberUtils : GLib.Object { delete res; return false; } - + for(int i = 0; i < res->nodesetval->length(); i++) { Xml.Node* node = res->nodesetval->item(i); @@ -132,17 +132,17 @@ public class FeedReader.grabberUtils : GLib.Object { node->set_prop(attr, completeURL(node->get_prop(attr), articleURL)); } } - + delete res; return true; } - + public static bool fixLazyImg(Html.Doc* doc, string className, string correctURL) { Logger.debug("grabberUtils: fixLazyImg"); Xml.XPath.Context cntx = new Xml.XPath.Context(doc); Xml.XPath.Object* res = cntx.eval_expression("//img[contains(@class, '%s')]".printf(className)); - + if(res == null) { return false; @@ -152,23 +152,23 @@ public class FeedReader.grabberUtils : GLib.Object { delete res; return false; } - + for(int i = 0; i < res->nodesetval->length(); i++) { Xml.Node* node = res->nodesetval->item(i); node->set_prop("src", node->get_prop(correctURL)); } - + delete res; return true; } - + public static bool fixIframeSize(Html.Doc* doc, string siteName) { Logger.debug("grabberUtils: fixIframeSize"); Xml.XPath.Context cntx = new Xml.XPath.Context(doc); Xml.XPath.Object* res = cntx.eval_expression(@"//iframe[contains(@src, '$siteName')]"); - + if(res == null) { return false; @@ -178,26 +178,26 @@ public class FeedReader.grabberUtils : GLib.Object { delete res; return false; } - + for(int i = 0; i < res->nodesetval->length(); i++) { Xml.Node* node = res->nodesetval->item(i); Xml.Node* videoWrapper = new Xml.Node(null, "div"); Xml.Node* parent = node->parent; - + videoWrapper->set_prop("class", "videoWrapper"); node->set_prop("width", "100%"); node->unset_prop("height"); - + node->unlink(); videoWrapper->add_child(node); parent->add_child(videoWrapper); } - + delete res; return true; } - + public static void stripNode(Html.Doc* doc, string xpath) { string ancestor = xpath; @@ -206,10 +206,10 @@ public class FeedReader.grabberUtils : GLib.Object { ancestor = ancestor.substring(2); } string query = "%s[not(ancestor::%s)]".printf(xpath, ancestor); - + Xml.XPath.Context cntx = new Xml.XPath.Context(doc); Xml.XPath.Object* res = cntx.eval_expression(query); - + if(res != null && res->type == Xml.XPath.ObjectType.NODESET && res->nodesetval != null) @@ -221,15 +221,15 @@ public class FeedReader.grabberUtils : GLib.Object { { continue; } - + node->unlink(); node->free_list(); } } - + delete res; } - + public static void onlyRemoveNode(Html.Doc* doc, string xpath) { Xml.XPath.Context cntx = new Xml.XPath.Context(doc); @@ -238,7 +238,7 @@ public class FeedReader.grabberUtils : GLib.Object { { changed = false; Xml.XPath.Object* res = cntx.eval_expression(xpath); - + if(res != null && res->type == Xml.XPath.ObjectType.NODESET && res->nodesetval != null) @@ -250,29 +250,29 @@ public class FeedReader.grabberUtils : GLib.Object { { continue; } - + Xml.Node* parent = node->parent; Xml.Node* children = node->children; - + children->unlink(); parent->add_child(children); - + node->unlink(); node->free_list(); changed = true; break; } } - + delete res; } while(changed); } - + public static bool setAttributes(Html.Doc* doc, string attribute, string newValue) { Xml.XPath.Context cntx = new Xml.XPath.Context(doc); Xml.XPath.Object* res = cntx.eval_expression("//*[@%s]".printf(attribute)); - + if(res == null) { return false; @@ -282,17 +282,17 @@ public class FeedReader.grabberUtils : GLib.Object { delete res; return false; } - + for(int i = 0; i < res->nodesetval->length(); i++) { Xml.Node* node = res->nodesetval->item(i); node->set_prop(attribute, newValue); } - + delete res; return true; } - + public static bool removeAttributes(Html.Doc* doc, string? tag, string attribute) { Xml.XPath.Context cntx = new Xml.XPath.Context(doc); @@ -305,7 +305,7 @@ public class FeedReader.grabberUtils : GLib.Object { { res = cntx.eval_expression("//%s[@%s]".printf(tag, attribute)); } - + if(res == null) { return false; @@ -315,17 +315,17 @@ public class FeedReader.grabberUtils : GLib.Object { delete res; return false; } - + for(int i = 0; i < res->nodesetval->length(); i++) { Xml.Node* node = res->nodesetval->item(i); node->unset_prop(attribute); } - + delete res; return true; } - + public static bool addAttributes(Html.Doc* doc, string? tag, string attribute, string val) { Xml.XPath.Context cntx = new Xml.XPath.Context(doc); @@ -340,7 +340,7 @@ public class FeedReader.grabberUtils : GLib.Object { Logger.debug(@"addAttributes: //$tag $attribute $val"); res = cntx.eval_expression(@"//$tag"); } - + if(res == null) { return false; @@ -350,23 +350,23 @@ public class FeedReader.grabberUtils : GLib.Object { delete res; return false; } - + for(int i = 0; i < res->nodesetval->length(); i++) { Xml.Node* node = res->nodesetval->item(i); node->set_prop(attribute, val); } - + delete res; return true; } - + public static void stripIDorClass(Html.Doc* doc, string IDorClass) { Xml.XPath.Context cntx = new Xml.XPath.Context(doc); string xpath = "//*[contains(@class, '%s') or contains(@id, '%s')]".printf(IDorClass, IDorClass); Xml.XPath.Object* res = cntx.eval_expression(xpath); - + if(res != null && res->type == Xml.XPath.ObjectType.NODESET && res->nodesetval != null) @@ -383,21 +383,21 @@ public class FeedReader.grabberUtils : GLib.Object { } } } - + delete res; } - + public static string cleanString(string? text) { if(text == null) { return ""; } - + var tmpText = text.replace("\n", ""); var array = tmpText.split(" "); tmpText = ""; - + foreach(string word in array) { if(word.chug() != "") @@ -405,10 +405,10 @@ public class FeedReader.grabberUtils : GLib.Object { tmpText += word + " "; } } - + return tmpText.chomp(); } - + public static string completeURL(string incompleteURL, string articleURL) { int index = 0; @@ -420,9 +420,9 @@ public class FeedReader.grabberUtils : GLib.Object { { index = articleURL.index_of_char('.', 0); } - + string baseURL = ""; - + if(incompleteURL.has_prefix("/") && !incompleteURL.has_prefix("//")) { index = articleURL.index_of_char('/', index); @@ -455,10 +455,10 @@ public class FeedReader.grabberUtils : GLib.Object { { return "http:" + incompleteURL; } - + return incompleteURL; } - + public static string buildHostName(string URL, bool cutSubdomain = true) { string hostname = URL; @@ -470,15 +470,15 @@ public class FeedReader.grabberUtils : GLib.Object { { hostname = hostname.substring(8); } - + if(hostname.has_prefix("www.")) { hostname = hostname.substring(4); } - + int index = hostname.index_of_char('/'); hostname = hostname.substring(0, index); - + if(cutSubdomain) { index = hostname.index_of_char('.'); @@ -487,17 +487,17 @@ public class FeedReader.grabberUtils : GLib.Object { hostname = hostname.substring(index); } } - + return hostname; } - - + + public static bool saveImages(Soup.Session session, Html.Doc* doc, Article article, GLib.Cancellable? cancellable = null) { Logger.debug("GrabberUtils: save Images: %s, %s".printf(article.getArticleID(), article.getFeedID())); Xml.XPath.Context cntx = new Xml.XPath.Context(doc); Xml.XPath.Object* res = cntx.eval_expression("//img"); - + if(res == null) { return false; @@ -507,14 +507,14 @@ public class FeedReader.grabberUtils : GLib.Object { delete res; return false; } - + for(int i = 0; i < res->nodesetval->length(); i++) { if(cancellable != null && cancellable.is_cancelled()) { break; } - + Xml.Node* node = res->nodesetval->item(i); if(node->get_prop("src") != null) { @@ -527,17 +527,17 @@ public class FeedReader.grabberUtils : GLib.Object { ) { string? original = downloadImage(session, node->get_prop("src"), article, i+1); - + if(original == null) { continue; } - + string? parentURL = checkParent(session, node); if(parentURL != null) { string parent = downloadImage(session, parentURL, article, i+1, true); - + if(compareImageSize(parent, original) > 0) { // parent is bigger than orignal image @@ -568,27 +568,27 @@ public class FeedReader.grabberUtils : GLib.Object { } } } - + delete res; return true; } - - + + public static string? downloadImage(Soup.Session session, string? url, Article article, int nr, bool parent = false) { if(url == null || url.down().has_prefix("data:image")) { return null; } - + string fixedURL = url; string imgPath = GLib.Environment.get_user_data_dir(); - + if(fixedURL.has_prefix("//")) { fixedURL = "http:" + fixedURL; } - + if(article.getArticleID() == "" && article.getFeedID() == "") { imgPath += "/debug-article/ArticleImages/"; @@ -597,7 +597,7 @@ public class FeedReader.grabberUtils : GLib.Object { { imgPath += "/feedreader/data/images/%s/%s/".printf(article.getFeedFileName(), article.getArticleFileName()); } - + var path = GLib.File.new_for_path(imgPath); try { @@ -607,29 +607,29 @@ public class FeedReader.grabberUtils : GLib.Object { { //Logger.debug(e.message); } - + string localFilename = imgPath + nr.to_string(); - + if(parent) { localFilename += "_parent"; } - + if(!FileUtils.test(localFilename, GLib.FileTest.EXISTS)) { var message_dlImg = new Soup.Message("GET", fixedURL); - + if(message_dlImg == null) { Logger.warning(@"grabberUtils.downloadImage: could not create soup message $fixedURL"); return url; } - + if(Settings.tweaks().get_boolean("do-not-track")) { message_dlImg.request_headers.append("DNT", "1"); } - + var status = session.send_message(message_dlImg); if(status == 200) { @@ -643,7 +643,7 @@ public class FeedReader.grabberUtils : GLib.Object { localFilename += ".svg"; } } - + try{ FileUtils.set_contents( localFilename, (string)message_dlImg.response_body.flatten().data, @@ -661,11 +661,11 @@ public class FeedReader.grabberUtils : GLib.Object { return url; } } - + return localFilename.replace("?", "%3F"); } - - + + // if image is >2000px then resize it to 1000px and add FR_huge attribute // with url to original image private static string? resizeImg(string path) @@ -675,12 +675,12 @@ public class FeedReader.grabberUtils : GLib.Object { int? height = 0; int? width = 0; Gdk.PixbufFormat? format = Gdk.Pixbuf.get_file_info(path, out width, out height); - + if(format == null || height == null || width == null) { return null; } - + if(width > 2000 || height > 2000) { int nHeight = 1000; @@ -693,7 +693,7 @@ public class FeedReader.grabberUtils : GLib.Object { { nWidth = -1; } - + var img = new Gdk.Pixbuf.from_file_at_scale(path, nWidth, nHeight, true); img.save(path + "_resized", "png"); return path + "_resized"; @@ -706,7 +706,7 @@ public class FeedReader.grabberUtils : GLib.Object { } return null; } - + // receives 2 paths to images stored on the hdd and compares the size // 1: file1 > file2 // 0: file1 = file2 @@ -716,11 +716,11 @@ public class FeedReader.grabberUtils : GLib.Object { int? height1 = 0; int? width1 = 0; Gdk.Pixbuf.get_file_info(file1, out width1, out height1); - + int? height2 = 0; int? width2 = 0; Gdk.Pixbuf.get_file_info(file2, out width2, out height2); - + if(height1 == null || width1 == null || height2 == null @@ -729,7 +729,7 @@ public class FeedReader.grabberUtils : GLib.Object { Logger.warning("Utils.compareImageSize: couldn't read image sizes"); return 0; } - + if(height1 == height2 && width1 == width2) { @@ -744,7 +744,7 @@ public class FeedReader.grabberUtils : GLib.Object { return -1; } } - + // check if the parent node is a link that points to a picture // (most likely a bigger version of said picture) private static string? checkParent(Soup.Session session, Xml.Node* node) @@ -759,14 +759,14 @@ public class FeedReader.grabberUtils : GLib.Object { if(name == "a") { string url = parent->get_prop("href"); - + if(url != "" && url != null) { if(url.has_prefix("//")) { url = "http:" + url; } - + var message = new Soup.Message("HEAD", url); if(message == null) { @@ -807,15 +807,15 @@ public class FeedReader.grabberUtils : GLib.Object { } } } - + return null; } - + public static string postProcessing(ref string html) { Logger.debug("GrabberUtils: postProcessing"); html = html.replace("<h3/>", "<h3></h3>"); - + int pos1 = html.index_of("<iframe", 0); int pos2 = -1; int pos3 = -1; @@ -823,23 +823,23 @@ public class FeedReader.grabberUtils : GLib.Object { { pos2 = html.index_of("/>", pos1); pos3 = html.index_of("</iframe>", pos1); - + if(pos3 == -1 && pos2 == -1) { Logger.error("GrabberUtils.postProcessing: could not find closing for iframe tag"); pos1 = html.index_of("<iframe", pos1+7); continue; } - + if((pos2 != -1 && pos3 != -1 && pos3 < pos2) || pos2 == -1) { Logger.debug("GrabberUtils.postProcessing: iframe not broken"); pos1 = html.index_of("<iframe", pos1+7); continue; } - - - + + + string broken_iframe = html.substring(pos1, pos2+2-pos1); Logger.debug("GrabberUtils: broken = %s".printf(broken_iframe)); string fixed_iframe = broken_iframe.substring(0, broken_iframe.length-2) + "></iframe>"; diff --git a/src/ContentGrabber/stringPair.vala b/src/ContentGrabber/stringPair.vala index 6b8b16ef..b4eb99cd 100644 --- a/src/ContentGrabber/stringPair.vala +++ b/src/ContentGrabber/stringPair.vala @@ -16,18 +16,18 @@ public class FeedReader.StringPair : GLib.Object { private string m_string1; private string m_string2; - + public StringPair(string string1, string string2) { m_string1 = string1; m_string2 = string2; } - + public string getString1() { return m_string1; } - + public string getString2() { return m_string2; diff --git a/src/DataBaseReadOnly.vala b/src/DataBaseReadOnly.vala index cce90f3c..7e8b494a 100644 --- a/src/DataBaseReadOnly.vala +++ b/src/DataBaseReadOnly.vala @@ -14,33 +14,33 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.DataBaseReadOnly : GLib.Object { - + protected SQLite m_db; - + static construct { Sqlite.config(Sqlite.Config.LOG, errorLogCallback); } - + public DataBaseReadOnly(string db_file = "feedreader-%01i.db".printf(Constants.DB_SCHEMA_VERSION)) { string db_path = GLib.Environment.get_user_data_dir() + "/feedreader/data/" + db_file; - + Logger.debug(@"Opening Database: $db_path"); m_db = new SQLite(db_path); } - + private void errorLogCallback(int code, string msg) { Logger.error(@"dbErrorLog: $code: $msg"); } - + public void init() { Logger.debug("init database"); m_db.simple_query("PRAGMA journal_mode = WAL"); m_db.simple_query("PRAGMA page_size = 4096"); m_db.simple_query("PRAGMA foreign_keys = ON"); - + m_db.simple_query(""" CREATE TABLE IF NOT EXISTS "main"."feeds" ( @@ -53,7 +53,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { "iconURL" TEXT ) """); - + m_db.simple_query(""" CREATE TABLE IF NOT EXISTS "main"."categories" ( @@ -65,7 +65,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { "Level" INTEGER ) """); - + m_db.simple_query(""" CREATE TABLE IF NOT EXISTS "main"."articles" ( @@ -84,7 +84,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { "contentFetched" INTEGER NOT NULL ) """); - + m_db.simple_query(""" CREATE TABLE IF NOT EXISTS "main"."tags" ( @@ -94,7 +94,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { "color" INTEGER ) """); - + m_db.simple_query(""" CREATE TABLE IF NOT EXISTS "main"."CachedActions" ( @@ -103,7 +103,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { "argument" INTEGER ) """); - + m_db.simple_query(""" CREATE TABLE IF NOT EXISTS "main"."Enclosures" ( @@ -113,7 +113,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { FOREIGN KEY(articleID) REFERENCES articles(articleID) ) """); - + m_db.simple_query(""" CREATE TABLE IF NOT EXISTS "main"."taggings" ( @@ -123,18 +123,18 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { FOREIGN KEY(tagID) REFERENCES tags(tagID) ) """); - + m_db.simple_query(""" CREATE INDEX IF NOT EXISTS "index_articles" ON "articles" ("feedID" DESC, "unread" ASC, "marked" ASC) """); - + m_db.simple_query(""" CREATE VIRTUAL TABLE IF NOT EXISTS fts_table USING fts4 (content='articles', articleID, preview, title, author) """); } - + public bool uninitialized() { string query = "SELECT count(*) FROM sqlite_master WHERE type='table' AND name='articles'"; @@ -142,7 +142,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { assert(rows.size == 1 && rows[0].size == 1); return rows[0][0].to_int() == 0; } - + public bool isEmpty() { return isTableEmpty("articles") @@ -150,7 +150,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { && isTableEmpty("feeds") && isTableEmpty("tags"); } - + public bool isTableEmpty(string table) requires (table != "") { @@ -159,7 +159,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { assert(rows.size == 1 && rows[0].size == 1); return rows[0][0].to_int() == 0; } - + private uint count_article_status(ArticleStatus status) { var query = "SELECT COUNT(*) FROM articles"; @@ -172,17 +172,17 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { assert(rows.size == 1 && rows[0].size == 1); return rows[0][0].to_int(); } - + public uint get_unread_total() { return count_article_status(ArticleStatus.UNREAD); } - + public uint get_marked_total() { return count_article_status(ArticleStatus.MARKED); } - + private uint count_status_uncategorized(ArticleStatus status) { var query = new QueryBuilder(QueryType.SELECT, "articles"); @@ -192,15 +192,15 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { { query.where_equal_int(status_column, status.to_int()); } - - + + var subquery = new QueryBuilder(QueryType.SELECT, "feeds"); subquery.select_field("feed_id"); subquery.where(getUncategorizedQuery()); query.where("feedID IN (%s)".printf(subquery.to_string())); - + Sqlite.Statement stmt = m_db.prepare(query.to_string()); - + int unread = 0; while (stmt.step() == Sqlite.ROW) { unread = stmt.column_int(0); @@ -208,30 +208,30 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { stmt.reset(); return unread; } - + public uint get_unread_uncategorized() { return count_status_uncategorized(ArticleStatus.UNREAD); } - + public uint get_marked_uncategorized() { return count_status_uncategorized(ArticleStatus.MARKED); } - + public int get_new_unread_count(int row_id) { if(row_id == 0) { return 0; } - + string query = "SELECT count(*) FROM articles WHERE unread = ? AND rowid > ?"; var rows = m_db.execute(query, { ArticleStatus.UNREAD, row_id }); assert(rows.size == 1 && rows[0].size == 1); return rows[0][0].to_int(); } - + public int getTagColor() { var rows = m_db.execute("SELECT COUNT(*) FROM tags WHERE instr(tagID, 'global.') = 0"); @@ -239,14 +239,14 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { int tagCount = rows[0][0].to_int(); return tagCount % Constants.COLORS.length; } - + public bool tag_still_used(Tag tag) { var query = "SELECT 1 FROM main.taggings WHERE tagID = ? LIMIT 1"; var rows = m_db.execute(query, { tag.getTagID() }); return rows.size > 0; } - + public string? getTagName(string tag_id) { var query = "SELECT title FROM tags WHERE tagID = ?"; @@ -258,7 +258,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { } return _("Unknown tag"); } - + public int getLastModified() { var query = "SELECT MAX(lastModified) FROM articles"; @@ -273,31 +273,31 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { return 0; } } - + public string getCategoryName(string catID) { if(catID == CategoryID.TAGS.to_string()) { return "Tags"; } - + var query = "SELECT title FROM categories WHERE categorieID = ?"; var rows = m_db.execute(query, { catID }); - + string result = ""; if(rows.size != 0) { result = rows[0][0].to_string(); } - + if(result == "") { result = _("Uncategorized"); } - + return result; } - + public string? getCategoryID(string catname) { var query = "SELECT categorieID FROM categories WHERE title = ?"; @@ -311,7 +311,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { return rows[0][0].to_string(); } } - + public bool preview_empty(string articleID) { var query = "SELECT COUNT(*) FROM articles WHERE articleID = ? AND preview != ''"; @@ -319,7 +319,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { assert(rows.size == 1 && rows[0].size == 1); return rows[0][0].to_int() != 0; } - + public Gee.List<Article> read_article_between( string feedID, FeedListType selectedType, @@ -332,7 +332,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { { var query = articleQuery(feedID, selectedType, state, searchTerm); var sorting = (ArticleListSort)Settings.general().get_enum("articlelist-sort-by"); - + if(sorting == ArticleListSort.RECEIVED) { query.where( @@ -348,9 +348,9 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { var smallerDate = (bigger) ? date2.to_unix() : date1.to_unix(); query.where(@"date BETWEEN $smallerDate AND $biggerDate"); } - + Sqlite.Statement stmt = m_db.prepare(query.to_string()); - + var articles = new Gee.ArrayList<Article>(); while (stmt.step () == Sqlite.ROW) { @@ -359,7 +359,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { { continue; } - + articles.add(new Article( stmt.column_text(2), // articleID stmt.column_text(3), // title @@ -380,32 +380,32 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { stmt.reset(); return articles; } - + private Gee.List<Enclosure> read_enclosures(string article_id) { var list = new Gee.ArrayList<Enclosure>(); - + var query = "SELECT url, type FROM Enclosures WHERE articleID = ?"; var rows = m_db.execute(query, { article_id }); - + foreach(var row in rows) { list.add(new Enclosure(article_id, row[0].to_string(), (EnclosureType)row[1].to_int())); } - + return list; } - + public Gee.HashMap<string, Article> read_article_stats(Gee.List<string> ids) { var query = new QueryBuilder(QueryType.SELECT, "articles"); query.select_field("articleID, unread, marked"); query.where_in_strings("articleID", ids); - + Sqlite.Statement stmt = m_db.prepare(query.to_string()); - + var articles = new Gee.HashMap<string, Article>(); - + while(stmt.step() == Sqlite.ROW) { articles.set(stmt.column_text(0), @@ -415,7 +415,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { stmt.reset(); return articles; } - + public Article? read_article(string articleID) { Logger.debug(@"DataBaseReadOnly.read_article(): $articleID"); @@ -430,7 +430,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { { author = null; } - + return new Article( articleID, row[3].to_string(), @@ -448,7 +448,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { row[11].to_string() // guid ); } - + public int getMaxCatLevel() { var rows = m_db.execute("SELECT MAX(Level) FROM categories"); @@ -460,18 +460,18 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { } return maxCatLevel; } - + public bool haveFeedsWithoutCat() { var query = new QueryBuilder(QueryType.SELECT, "feeds"); query.select_field("count(*)"); query.where(getUncategorizedQuery()); - + Sqlite.Statement stmt = m_db.prepare(query.to_string()); - + while (stmt.step () == Sqlite.ROW) { int count = stmt.column_int(0); - + if(count > 0) { return true; @@ -479,34 +479,34 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { } return false; } - + public bool haveCategories() { var rows = m_db.execute("SELECT COUNT(*) FROM categories"); assert(rows.size == 1 && rows[0].size == 1); return rows[0][0].to_int() > 0; } - + public bool article_exists(string articleID) { var rows = m_db.execute("SELECT 1 FROM articles WHERE articleID = ? LIMIT 1", { articleID }); return rows.size != 0; } - + public int getArticleCountNewerThanID(string articleID, string feedID, FeedListType selectedType, ArticleListState state, string searchTerm) ensures (result >= 0) { string order_by = ((ArticleListSort)Settings.general().get_enum("articlelist-sort-by") == ArticleListSort.RECEIVED) ? "rowid" : "date"; - + var query = new QueryBuilder(QueryType.SELECT, "articles"); query.where_equal_string("articleID", articleID); - + var query2 = new QueryBuilder(QueryType.SELECT, "articles"); query2.select_field("count(*)"); - - + + query.select_field(order_by); - + if(Settings.general().get_boolean("articlelist-oldest-first") && state == ArticleListState.UNREAD) { query2.where(@"$order_by < (%s)".printf(query.to_string())); @@ -515,8 +515,8 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { { query2.where(@"$order_by > (%s)".printf(query.to_string())); } - - + + if(selectedType == FeedListType.FEED && feedID != FeedID.ALL.to_string()) { query2.where_equal_string("feedID", feedID); @@ -533,7 +533,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { { query2.where_in_strings("articleID", read_taggings_by_tag_id(feedID)); } - + if(state == ArticleListState.UNREAD) { query2.where_equal_int("unread", ArticleStatus.UNREAD.to_int()); @@ -542,7 +542,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { { query2.where_equal_int("marked", ArticleStatus.MARKED.to_int()); } - + if(searchTerm != "") { string search_column; @@ -568,37 +568,37 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { search_column, SQLite.quote_string(Utils.prepareSearchQuery(searchTerm)))); } - + bool desc = true; if(Settings.general().get_boolean("articlelist-oldest-first") && state == ArticleListState.UNREAD) { desc = false; } - + query2.order_by(order_by, desc); - + Sqlite.Statement stmt = m_db.prepare(query2.to_string()); - + int res = 0; while (stmt.step () == Sqlite.ROW) { res = stmt.column_int(0); } return res; } - + public Gee.List<string> getFeedIDofCategorie(string categorieID) { var feedIDs = new Gee.ArrayList<string>(); - + var query = new QueryBuilder(QueryType.SELECT, "feeds"); query.select_field("feed_id, category_id"); - + Sqlite.Statement stmt = m_db.prepare(query.to_string()); - + while (stmt.step() == Sqlite.ROW) { string catString = stmt.column_text(1); string[] categories = catString.split(","); - + if(categorieID == "") { if((categories.length == 0) @@ -620,13 +620,13 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { } return feedIDs; } - + protected string getUncategorizedQuery() { string catID = FeedServer.get_default().uncategorizedID(); return "category_id = %s".printf(SQLite.quote_string(catID)); } - + protected bool showCategory(string catID, Gee.List<Feed> feeds) { if(FeedServer.get_default().hideCategoryWhenEmpty(catID) @@ -636,7 +636,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { } return true; } - + public string getFeedIDofArticle(string articleID) { var rows = m_db.execute("SELECT feedID FROM articles WHERE articleID = ?", { articleID }); @@ -651,7 +651,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { } return id; } - + public string getNewestArticle() { var rows = m_db.execute("SELECT articleID FROM articles WHERE rowid = ?", { getMaxID("articles", "rowid") }); @@ -661,7 +661,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { } return rows[0][0].to_string(); } - + public string getMaxID(string table, string field) { var rows = m_db.execute(@"SELECT MAX($field) FROM $table"); @@ -676,7 +676,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { } return id; } - + public bool feed_exists(string feed_url) { var rows = m_db.execute("SELECT COUNT(*) FROM main.feeds WHERE url = ? LIMIT 1", { feed_url }); @@ -684,7 +684,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { // FIXME: Why > 1 and not > 0? return rows[0][0].to_int() > 1; } - + public Feed? read_feed(string feedID) { var rows = m_db.execute("SELECT * FROM feeds WHERE feed_id = ?", { feedID }); @@ -692,7 +692,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { { return null; } - + var row = rows[0]; return new Feed( feedID, @@ -703,20 +703,20 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { row[6].to_string(), row[5].to_string()); } - + public Gee.List<Feed> read_feeds(bool starredCount = false) { Gee.List<Feed> feeds = new Gee.ArrayList<Feed>(); - + var query = new QueryBuilder(QueryType.SELECT, "feeds"); query.select_field("*"); if(Settings.general().get_enum("feedlist-sort-by") == FeedListSort.ALPHABETICAL) { query.order_by("name", true); } - + Sqlite.Statement stmt = m_db.prepare(query.to_string()); - + while (stmt.step () == Sqlite.ROW) { string feedID = stmt.column_text(0); string catString = stmt.column_text(3); @@ -725,7 +725,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { string url = stmt.column_text(2); string name = stmt.column_text(1); var categories = StringUtils.split(catString, ",", true); - + uint count = 0; if(starredCount) { @@ -735,14 +735,14 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { { count = getFeedUnread(feedID); } - + var feed = new Feed(feedID, name, url, count, categories, iconURL, xmlURL); feeds.add(feed); } - + return feeds; } - + public uint getFeedUnread(string feedID) { var query = "SELECT COUNT(*) FROM articles WHERE unread = ? AND feedID = ?"; @@ -750,7 +750,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { assert(rows.size == 1 && rows[0].size == 1); return rows[0][0].to_int(); } - + public uint getFeedStarred(string feedID) { var query = "SELECT COUNT(*) FROM articles WHERE marked = ? AND feedID = ?"; @@ -758,11 +758,11 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { assert(rows.size == 1 && rows[0].size == 1); return rows[0][0].to_int(); } - + public Gee.List<Feed> read_feeds_without_cat() { var feeds = new Gee.ArrayList<Feed>(); - + var query = new QueryBuilder(QueryType.SELECT, "feeds"); query.select_field("*"); query.where(getUncategorizedQuery()); @@ -770,9 +770,9 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { { query.order_by("name", true); } - + Sqlite.Statement stmt = m_db.prepare(query.to_string()); - + while (stmt.step () == Sqlite.ROW) { string feedID = stmt.column_text(0); string catString = stmt.column_text(3); @@ -784,10 +784,10 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { var feed = new Feed(feedID, name, url, getFeedUnread(feedID), categories, iconURL, xmlURL); feeds.add(feed); } - + return feeds; } - + public Category? read_category(string catID) { var query = "SELECT * FROM categories WHERE categorieID = ?"; @@ -796,7 +796,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { { return null; } - + var row = rows[0]; return new Category( catID, @@ -807,11 +807,11 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { row[5].to_int() ); } - + public Gee.List<Tag> read_tags() { var rows = m_db.execute("SELECT * FROM tags WHERE instr(tagID, 'global.') = 0"); - + var tags = new Gee.ArrayList<Tag>(); foreach(var row in rows) { @@ -821,38 +821,38 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { row[3].to_int()); tags.add(tag); } - + return tags; } - + private Gee.List<string> read_taggings_by_article_id(string articleID) { var list = new Gee.LinkedList<string>(); - + var rows = m_db.execute("SELECT tagID FROM taggings WHERE articleID = ?", { articleID }); - + foreach(var row in rows) { list.add(row[0].to_string()); } - + return list; } - + private Gee.List<string> read_taggings_by_tag_id(string tagID) { var list = new Gee.LinkedList<string>(); - + var rows = m_db.execute("SELECT articleID FROM taggings WHERE tagID = ?", { tagID }); - + foreach(var row in rows) { list.add(row[0].to_string()); } - + return list; } - + public Tag? read_tag(string tagID) { var query = "SELECT * FROM tags WHERE tagID = ?"; @@ -861,24 +861,24 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { { return null; } - + var row = rows[0]; return new Tag( row[0].to_string(), row[1].to_string(), row[3].to_int()); } - + protected string getAllTagsCondition() { return "articleID IN (SELECT articleID FROM taggings WHERE instr(tagID, 'global.') = 0)"; } - + public Gee.List<Category> read_categories_level(int level, Gee.List<Feed>? feeds = null) { var categories = read_categories(feeds); var results = new Gee.ArrayList<Category>(); - + foreach(Category cat in categories) { if(cat.getLevel() == level) @@ -886,15 +886,15 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { results.add(cat); } } - + return results; } - + public Gee.List<Category> read_categories(Gee.List<Feed>? feeds = null) { var query = new QueryBuilder(QueryType.SELECT, "categories"); query.select_field("*"); - + if(Settings.general().get_enum("feedlist-sort-by") == FeedListSort.ALPHABETICAL) { query.order_by("title", true); @@ -903,14 +903,14 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { { query.order_by("orderID", true); } - + Sqlite.Statement stmt = m_db.prepare(query.to_string()); - + var results = new Gee.ArrayList<Category>(); while(stmt.step () == Sqlite.ROW) { string catID = stmt.column_text(0); - + if(feeds == null || showCategory(catID, feeds)) { var category = new Category( @@ -921,18 +921,18 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { stmt.column_text(4), stmt.column_int(5) ); - + results.add(category); } } - + return results; } - + public Gee.List<Article> readUnfetchedArticles() { var rows = m_db.execute("SELECT articleID, url, preview, html, feedID FROM articles WHERE contentFetched = 0"); - + var articles = new Gee.LinkedList<Article>(); foreach(var row in rows) { @@ -951,11 +951,11 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { } return articles; } - + public QueryBuilder articleQuery(string id, FeedListType selectedType, ArticleListState state, string searchTerm) { string order_by = ((ArticleListSort)Settings.general().get_enum("articlelist-sort-by") == ArticleListSort.RECEIVED) ? "rowid" : "date"; - + var query = new QueryBuilder(QueryType.SELECT, "articles"); query.select_field("ROWID"); query.select_field("feedID"); @@ -968,7 +968,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { query.select_field("marked"); query.select_field("date"); query.select_field("guidHash"); - + if(selectedType == FeedListType.FEED && id != FeedID.ALL.to_string()) { query.where_equal_string("feedID", id); @@ -985,7 +985,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { { query.where_in_strings("articleID", read_taggings_by_tag_id(id)); } - + if(state == ArticleListState.UNREAD) { query.where_equal_int("unread", ArticleStatus.UNREAD.to_int()); @@ -994,7 +994,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { { query.where_equal_int("marked", ArticleStatus.MARKED.to_int()); } - + if(searchTerm != "") { if(searchTerm.has_prefix("title: ")) @@ -1014,40 +1014,40 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { query.where("articleID IN (SELECT articleID FROM fts_table WHERE fts_table MATCH '%s')".printf(Utils.prepareSearchQuery(searchTerm))); } } - + bool desc = true; if(Settings.general().get_boolean("articlelist-oldest-first") && state == ArticleListState.UNREAD) { desc = false; } - + query.order_by(order_by, desc); - + return query; } - + public Gee.List<Article> read_articles(string id, FeedListType selectedType, ArticleListState state, string searchTerm, uint limit = 20, uint offset = 0, int searchRows = 0) requires (limit > 0) { var query = articleQuery(id, selectedType, state, searchTerm); - + string desc = "DESC"; if(Settings.general().get_boolean("articlelist-oldest-first") && state == ArticleListState.UNREAD) { desc = "ASC"; } - + if(searchRows != 0) { string order_by = ((ArticleListSort)Settings.general().get_enum("articlelist-sort-by") == ArticleListSort.RECEIVED) ? "rowid" : "date"; query.where(@"articleID in (SELECT articleID FROM articles ORDER BY $order_by $desc LIMIT $searchRows)"); } - + query.limit(limit); query.offset(offset); - + Sqlite.Statement stmt = m_db.prepare(query.to_string()); - + var results = new Gee.LinkedList<Article>(); while (stmt.step () == Sqlite.ROW) { @@ -1068,7 +1068,7 @@ public class FeedReader.DataBaseReadOnly : GLib.Object { stmt.column_text(10) // guid )); } - + return results; } } diff --git a/src/DataBaseWriteAccess.vala b/src/DataBaseWriteAccess.vala index d0357776..26274b7f 100644 --- a/src/DataBaseWriteAccess.vala +++ b/src/DataBaseWriteAccess.vala @@ -14,7 +14,7 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.DataBase : DataBaseReadOnly { - + public static new DataBase writeAccess() { var database = new DataBase(); @@ -22,25 +22,25 @@ public class FeedReader.DataBase : DataBaseReadOnly { { database.init(); } - + return database; } - + public static new DataBaseReadOnly readOnly() { return writeAccess() as DataBaseReadOnly; } - + public DataBase(string dbFile = "feedreader-%01i.db".printf(Constants.DB_SCHEMA_VERSION)) { base(dbFile); } - + public void checkpoint() { m_db.checkpoint(); } - + public bool resetDB() { Logger.warning("resetDB"); @@ -53,10 +53,10 @@ public class FeedReader.DataBase : DataBaseReadOnly { m_db.simple_query("DROP TABLE main.categories"); m_db.simple_query("DROP TABLE main.feeds"); m_db.simple_query("VACUUM"); - + string query = "PRAGMA INTEGRITY_CHECK"; Sqlite.Statement stmt = m_db.prepare(query); - + int cols = stmt.column_count (); while (stmt.step () == Sqlite.ROW) { for (int i = 0; i < cols; i++) { @@ -70,19 +70,19 @@ public class FeedReader.DataBase : DataBaseReadOnly { stmt.reset(); return true; } - + public void updateFTS() { m_db.simple_query("INSERT INTO fts_table(fts_table) VALUES('rebuild')"); } - + public void springCleaning() { m_db.simple_query("VACUUM"); var now = new DateTime.now_local(); Settings.state().set_int("last-spring-cleaning", (int)now.to_unix()); } - + public void dropOldArticles(int weeks) { var query = new QueryBuilder(QueryType.SELECT, "main.articles"); @@ -95,13 +95,13 @@ public class FeedReader.DataBase : DataBaseReadOnly { int syncCount = Settings.general().get_int("max-articles"); query.where(@"rowid BETWEEN 1 AND (SELECT rowid FROM articles ORDER BY rowid DESC LIMIT 1 OFFSET $syncCount)"); } - + Sqlite.Statement stmt = m_db.prepare(query.to_string()); while (stmt.step () == Sqlite.ROW) { delete_article(stmt.column_text(0), stmt.column_text(1)); } } - + private void delete_article(string articleID, string feedID) { Logger.info(@"Deleting article \"$articleID\""); @@ -110,17 +110,17 @@ public class FeedReader.DataBase : DataBaseReadOnly { string folder_path = GLib.Environment.get_user_data_dir() + @"/feedreader/data/images/$feedID/$articleID/"; Utils.remove_directory(folder_path); } - + public void dropTag(Tag tag) { m_db.execute("DELETE FROM main.tags WHERE tagID = ?", { tag.getTagID() }); m_db.execute("DELETE FROM main.taggings WHERE tagID = ?", { tag.getTagID() }); } - + public void write_feeds(Gee.Collection<Feed> feeds) { m_db.simple_query("BEGIN TRANSACTION"); - + var query = new QueryBuilder(QueryType.INSERT_OR_REPLACE, "main.feeds"); query.insert_param("feed_id", "$FEEDID"); query.insert_param("name", "$FEEDNAME"); @@ -129,9 +129,9 @@ public class FeedReader.DataBase : DataBaseReadOnly { query.insert_int("subscribed", 1); query.insert_param("xmlURL", "$XMLURL"); query.insert_param("iconURL", "$ICONURL"); - + Sqlite.Statement stmt = m_db.prepare(query.to_string()); - + int feedID_pos = stmt.bind_parameter_index("$FEEDID"); int feedName_pos = stmt.bind_parameter_index("$FEEDNAME"); int feedURL_pos = stmt.bind_parameter_index("$FEEDURL"); @@ -144,7 +144,7 @@ public class FeedReader.DataBase : DataBaseReadOnly { assert (catID_pos > 0); assert (xmlURL_pos > 0); assert (iconURL_pos > 0); - + foreach(var feed_item in feeds) { stmt.bind_text(feedID_pos, feed_item.getFeedID()); @@ -153,80 +153,80 @@ public class FeedReader.DataBase : DataBaseReadOnly { stmt.bind_text(catID_pos, StringUtils.join(feed_item.getCatIDs(), ",")); stmt.bind_text(xmlURL_pos, feed_item.getXmlUrl()); stmt.bind_text(iconURL_pos, feed_item.getIconURL()); - + while(stmt.step() == Sqlite.ROW) {} stmt.reset(); } - + m_db.simple_query("COMMIT TRANSACTION"); } - + public void write_tag(Tag tag) { var list = new Gee.ArrayList<Tag>(); list.add(tag); write_tags(list); } - + public void write_tags(Gee.Collection<Tag> tags) { m_db.simple_query("BEGIN TRANSACTION"); - + var query = new QueryBuilder(QueryType.INSERT_OR_IGNORE, "main.tags"); query.insert_param("tagID", "$TAGID"); query.insert_param("title", "$LABEL"); query.insert_int("\"exists\"", 1); query.insert_param("color", "$COLOR"); - + Sqlite.Statement stmt = m_db.prepare(query.to_string()); - + int tagID_position = stmt.bind_parameter_index("$TAGID"); int label_position = stmt.bind_parameter_index("$LABEL"); int color_position = stmt.bind_parameter_index("$COLOR"); assert (tagID_position > 0); assert (label_position > 0); assert (color_position > 0); - + foreach(var tag_item in tags) { stmt.bind_text(tagID_position, tag_item.getTagID()); stmt.bind_text(label_position, tag_item.getTitle()); stmt.bind_int (color_position, tag_item.getColor()); - + while(stmt.step() == Sqlite.ROW) {} stmt.reset (); } - + m_db.simple_query("COMMIT TRANSACTION"); } - + public void update_tag(Tag tag) { update_tags(ListUtils.single(tag)); - + if(FeedServer.get_default().tagIDaffectedByNameChange()) { string newID = tag.getTagID().replace(tag.getTitle(), tag.getTitle()); m_db.execute("UPDATE tags SET tagID = ? WHERE tagID = ?", { newID, tag.getTagID() }); } } - + public void update_tags(Gee.List<Tag> tags) { m_db.simple_query("BEGIN TRANSACTION"); - + var query = new QueryBuilder(QueryType.UPDATE, "main.tags"); query.update_param("title", "$TITLE"); query.update_int("\"exists\"", 1); query.where_equal_param("tagID", "$TAGID"); - + Sqlite.Statement stmt = m_db.prepare(query.to_string()); - + int title_position = stmt.bind_parameter_index("$TITLE"); int tagID_position = stmt.bind_parameter_index("$TAGID"); assert (title_position > 0); assert (tagID_position > 0); - + foreach(var tag_item in tags) { stmt.bind_text(title_position, tag_item.getTitle()); @@ -234,15 +234,15 @@ public class FeedReader.DataBase : DataBaseReadOnly { while (stmt.step () == Sqlite.ROW) {} stmt.reset (); } - + m_db.simple_query("COMMIT TRANSACTION"); } - - + + public void write_categories(Gee.List<Category> categories) { m_db.simple_query("BEGIN TRANSACTION"); - + var query = new QueryBuilder(QueryType.INSERT_OR_REPLACE, "main.categories"); query.insert_param("categorieID", "$CATID"); query.insert_param("title", "$FEEDNAME"); @@ -250,9 +250,9 @@ public class FeedReader.DataBase : DataBaseReadOnly { query.insert_int("\"exists\"", 1); query.insert_param("Parent", "$PARENT"); query.insert_param("Level", "$LEVEL"); - + Sqlite.Statement stmt = m_db.prepare(query.to_string()); - + int catID_position = stmt.bind_parameter_index("$CATID"); int feedName_position = stmt.bind_parameter_index("$FEEDNAME"); int orderID_position = stmt.bind_parameter_index("$ORDERID"); @@ -263,7 +263,7 @@ public class FeedReader.DataBase : DataBaseReadOnly { assert (orderID_position > 0); assert (parent_position > 0); assert (level_position > 0); - + foreach(var cat_item in categories) { stmt.bind_text(catID_position, cat_item.getCatID()); @@ -271,14 +271,14 @@ public class FeedReader.DataBase : DataBaseReadOnly { stmt.bind_int (orderID_position, cat_item.getOrderID()); stmt.bind_text(parent_position, cat_item.getParent()); stmt.bind_int (level_position, cat_item.getLevel()); - + while (stmt.step () == Sqlite.ROW) {} stmt.reset (); } - + m_db.simple_query("COMMIT TRANSACTION"); } - + public void updateArticlesByID(Gee.List<string> ids, string field) { // first reset all articles @@ -292,13 +292,13 @@ public class FeedReader.DataBase : DataBaseReadOnly { reset_query.update_int(field, ArticleStatus.UNMARKED.to_int()); } m_db.simple_query(reset_query.to_string()); - - + + m_db.simple_query("BEGIN TRANSACTION"); - + // then reapply states of the synced articles var update_query = new QueryBuilder(QueryType.UPDATE, "main.articles"); - + if(field == "unread") { update_query.update_int(field, ArticleStatus.UNREAD.to_int()); @@ -307,25 +307,25 @@ public class FeedReader.DataBase : DataBaseReadOnly { { update_query.update_int(field, ArticleStatus.MARKED.to_int()); } - + update_query.where_equal_param("articleID", "$ARTICLEID"); - + Sqlite.Statement stmt = m_db.prepare(update_query.to_string()); - + int articleID_position = stmt.bind_parameter_index("$ARTICLEID"); assert (articleID_position > 0); - - + + foreach(string id in ids) { stmt.bind_text(articleID_position, id); while(stmt.step() != Sqlite.DONE) {} stmt.reset(); } - + m_db.simple_query("COMMIT TRANSACTION"); } - + public void writeContent(Article article) { var update_query = new QueryBuilder(QueryType.UPDATE, "main.articles"); @@ -333,39 +333,39 @@ public class FeedReader.DataBase : DataBaseReadOnly { update_query.update_param("preview", "$PREVIEW"); update_query.update_int("contentFetched", 1); update_query.where_equal_string("articleID", article.getArticleID()); - + Sqlite.Statement stmt = m_db.prepare(update_query.to_string()); - + int html_position = stmt.bind_parameter_index("$HTML"); int preview_position = stmt.bind_parameter_index("$PREVIEW"); assert (html_position > 0); assert (preview_position > 0); - - + + stmt.bind_text(html_position, article.getHTML()); stmt.bind_text(preview_position, article.getPreview()); - + while(stmt.step() != Sqlite.DONE) {} stmt.reset(); } - + public void update_article(Article article) { update_articles(ListUtils.single(article)); } - + public void update_articles(Gee.List<Article> articles) { m_db.simple_query("BEGIN TRANSACTION"); - + var update_query = new QueryBuilder(QueryType.UPDATE, "main.articles"); update_query.update_param("unread", "$UNREAD"); update_query.update_param("marked", "$MARKED"); update_query.update_param("lastModified", "$LASTMODIFIED"); update_query.where_equal_param("articleID", "$ARTICLEID"); - + Sqlite.Statement stmt = m_db.prepare(update_query.to_string()); - + int unread_position = stmt.bind_parameter_index("$UNREAD"); int marked_position = stmt.bind_parameter_index("$MARKED"); int modified_position = stmt.bind_parameter_index("$LASTMODIFIED"); @@ -374,45 +374,45 @@ public class FeedReader.DataBase : DataBaseReadOnly { assert (marked_position > 0); assert (modified_position > 0); assert (articleID_position > 0); - - + + foreach(Article a in articles) { var unread = ActionCache.get_default().checkRead(a); var marked = ActionCache.get_default().checkStarred(a.getArticleID(), a.getMarked()); - + if(unread != ArticleStatus.READ && unread != ArticleStatus.UNREAD) { Logger.warning(@"DataBase.update_articles: writing invalid unread status $unread for article " + a.getArticleID()); } - + if(marked != ArticleStatus.MARKED && marked != ArticleStatus.UNMARKED) { Logger.warning(@"DataBase.update_articles: writing invalid marked status $marked for article " + a.getArticleID()); } - + stmt.bind_int (unread_position, unread); stmt.bind_int (marked_position, marked); stmt.bind_int (modified_position, a.getLastModified()); stmt.bind_text(articleID_position, a.getArticleID()); - + while(stmt.step() != Sqlite.DONE) {} stmt.reset(); - + write_taggings(a); } - + m_db.simple_query("COMMIT TRANSACTION"); } - - + + public void write_articles(Gee.List<Article> articles) { Utils.generatePreviews(articles); Utils.checkHTML(articles); - + m_db.simple_query("BEGIN TRANSACTION"); - + var query = new QueryBuilder(QueryType.INSERT_OR_IGNORE, "main.articles"); query.insert_param("articleID", "$ARTICLEID"); query.insert_param("feedID", "$FEEDID"); @@ -427,9 +427,9 @@ public class FeedReader.DataBase : DataBaseReadOnly { query.insert_param("guidHash", "$GUIDHASH"); query.insert_param("lastModified", "$LASTMODIFIED"); query.insert_int("contentFetched", 0); - + Sqlite.Statement stmt = m_db.prepare(query.to_string()); - + int articleID_position = stmt.bind_parameter_index("$ARTICLEID"); int feedID_position = stmt.bind_parameter_index("$FEEDID"); int url_position = stmt.bind_parameter_index("$URL"); @@ -442,7 +442,7 @@ public class FeedReader.DataBase : DataBaseReadOnly { int date_position = stmt.bind_parameter_index("$DATE"); int guidHash_position = stmt.bind_parameter_index("$GUIDHASH"); int modified_position = stmt.bind_parameter_index("$LASTMODIFIED"); - + assert (articleID_position > 0); assert (feedID_position > 0); assert (url_position > 0); @@ -455,7 +455,7 @@ public class FeedReader.DataBase : DataBaseReadOnly { assert (date_position > 0); assert (guidHash_position > 0); assert (modified_position > 0); - + foreach(var article in articles) { // if article time is in the future @@ -464,14 +464,14 @@ public class FeedReader.DataBase : DataBaseReadOnly { { article.SetDate(now); } - + int? weeks = ((DropArticles)Settings.general().get_enum("drop-articles-after")).to_weeks(); if(weeks != null && article.getDate().compare(now.add_weeks(-(int)weeks)) == -1) { Logger.info("Ignoring old article: %s".printf(article.getTitle())); continue; } - + stmt.bind_text(articleID_position, article.getArticleID()); stmt.bind_text(feedID_position, article.getFeedID()); stmt.bind_text(url_position, article.getURL()); @@ -484,69 +484,69 @@ public class FeedReader.DataBase : DataBaseReadOnly { stmt.bind_int64(date_position, article.getDate().to_unix()); stmt.bind_text(guidHash_position, article.getHash()); stmt.bind_int (modified_position, article.getLastModified()); - + while(stmt.step() != Sqlite.DONE) {} stmt.reset(); - + write_enclosures(article); write_taggings(article); } - + m_db.simple_query("COMMIT TRANSACTION"); } - + private void write_taggings(Article article) { var query = new QueryBuilder(QueryType.INSERT_OR_REPLACE, "main.taggings"); query.insert_param("articleID", "$ARTICLEID"); query.insert_param("tagID", "$TAGID"); - + Sqlite.Statement stmt = m_db.prepare(query.to_string()); - + int articleID_position = stmt.bind_parameter_index("$ARTICLEID"); int tagID_position = stmt.bind_parameter_index("$TAGID"); - + assert (articleID_position > 0); assert (tagID_position > 0); - + foreach(string tagID in article.getTagIDs()) { stmt.bind_text(articleID_position, article.getArticleID()); stmt.bind_text(tagID_position, tagID); - + while(stmt.step() != Sqlite.DONE) {} stmt.reset(); } } - + private void write_enclosures(Article article) { var query = new QueryBuilder(QueryType.INSERT_OR_REPLACE, "main.Enclosures"); query.insert_param("articleID", "$ARTICLEID"); query.insert_param("url", "$URL"); query.insert_param("type", "$TYPE"); - + Sqlite.Statement stmt = m_db.prepare(query.to_string()); - + int articleID_position = stmt.bind_parameter_index("$ARTICLEID"); int url_position = stmt.bind_parameter_index("$URL"); int type_position = stmt.bind_parameter_index("$TYPE"); - + assert (articleID_position > 0); assert (url_position > 0); assert (type_position > 0); - + foreach(Enclosure enc in article.getEnclosures()) { stmt.bind_text(articleID_position, article.getArticleID()); stmt.bind_text(url_position, enc.get_url()); stmt.bind_int (type_position, enc.get_enclosure_type()); - + while(stmt.step() != Sqlite.DONE) {} stmt.reset(); } } - + public void markCategorieRead(string catID) { var query = new QueryBuilder(QueryType.UPDATE, "main.articles"); @@ -554,64 +554,64 @@ public class FeedReader.DataBase : DataBaseReadOnly { query.where_in_strings("feedID", getFeedIDofCategorie(catID)); m_db.simple_query(query.to_string()); } - + public void markFeedRead(string feedID) { m_db.execute("UPDATE main.articles SET unread = ? WHERE feedID = ?", { ArticleStatus.READ, feedID }); } - + public void markAllRead() { m_db.execute("UPDATE main.articles SET unread = ?", { ArticleStatus.READ }); } - + public void reset_subscribed_flag() { m_db.simple_query("UPDATE main.feeds SET \"subscribed\" = 0"); } - + public void reset_exists_tag() { m_db.simple_query("UPDATE main.tags SET \"exists\" = 0"); } - + public void reset_exists_flag() { m_db.simple_query("UPDATE main.categories SET \"exists\" = 0"); } - + public void delete_unsubscribed_feeds() { Logger.warning("DataBase: Deleting unsubscribed feeds"); m_db.simple_query("DELETE FROM main.feeds WHERE \"subscribed\" = 0"); } - + public void delete_nonexisting_categories() { Logger.warning("DataBase: Deleting nonexisting categories"); m_db.simple_query("DELETE FROM main.categories WHERE \"exists\" = 0"); } - + public void delete_nonexisting_tags() { Logger.warning("DataBase: Deleting nonexisting tags"); m_db.simple_query("DELETE FROM main.tags WHERE \"exists\" = 0"); } - + public void delete_articles_without_feed() { Logger.warning("DataBase: Deleting articles without feed"); var query = new QueryBuilder(QueryType.SELECT, "main.feeds"); query.select_field("feed_id"); query.where_equal_int("subscribed", 0); - + Sqlite.Statement stmt = m_db.prepare(query.to_string()); while(stmt.step () == Sqlite.ROW) { delete_articles(stmt.column_text(0)); } } - + public void delete_articles(string feedID) { Logger.warning(@"DataBase: Deleting all articles of feed \"$feedID\""); @@ -620,12 +620,12 @@ public class FeedReader.DataBase : DataBaseReadOnly { string folder_path = GLib.Environment.get_user_data_dir() + @"/feedreader/data/images/$feedID/"; Utils.remove_directory(folder_path); } - + public void delete_category(string catID) { m_db.execute("DELETE FROM main.categories WHERE categorieID = ?", { catID }); } - + public void rename_category(string catID, string newName) { if(FeedServer.get_default().tagIDaffectedByNameChange()) @@ -634,7 +634,7 @@ public class FeedReader.DataBase : DataBaseReadOnly { string newID = catID.replace(cat.getTitle(), newName); var query = "UPDATE categories SET categorieID = ?, title = ? WHERE categorieID = ?"; m_db.execute(query, {newID, newName, catID }); - + query = "UPDATE feeds SET category_id = replace(category_id, ?, ?) WHERE instr(category_id, ?)"; m_db.execute(query, { catID, newID, catID }); } @@ -644,79 +644,79 @@ public class FeedReader.DataBase : DataBaseReadOnly { m_db.execute(query, { newName, catID }); } } - + public void move_category(string catID, string newParentID) { var parent = read_category(newParentID); var query = "UPDATE categories SET Parent = ?, Level = ? WHERE categorieID = ?"; m_db.execute(query, { newParentID, parent.getLevel() + 1, catID }); } - + public void rename_feed(string feedID, string newName) { var query = "UPDATE feeds SET name = ? WHERE feed_id = ?"; m_db.execute(query, { newName, feedID }); } - + public void move_feed(string feedID, string currentCatID, string? newCatID = null) { var Feed = read_feed(feedID); var categories = Feed.getCatIDs(); categories.remove(currentCatID); - + if(newCatID != null) { categories.add(newCatID); } - + string catString = StringUtils.join(categories, ","); - + var query = "UPDATE feeds SET category_id = ? WHERE feed_id = ?"; m_db.execute(query, { catString, feedID }); } - + public void removeCatFromFeed(string feedID, string catID) { var feed = read_feed(feedID); m_db.execute("UPDATE feeds SET category_id = ? WHERE feed_id = ?", { feed.getCatString().replace(catID + ",", ""), feedID }); } - + public void delete_feed(string feedID) { m_db.execute("DELETE FROM feeds WHERE feed_id = ?", { feedID }); delete_articles(feedID); } - + public void addCachedAction(CachedActions action, string id, string? argument = "") { m_db.simple_query("BEGIN TRANSACTION"); - + var query = new QueryBuilder(QueryType.INSERT_OR_IGNORE, "main.CachedActions"); query.insert_param("action", "$ACTION"); query.insert_param("id", "$ID"); query.insert_param("argument", "$ARGUMENT"); - + Sqlite.Statement stmt = m_db.prepare(query.to_string()); - + int action_position = stmt.bind_parameter_index("$ACTION"); int id_position = stmt.bind_parameter_index("$ID"); int argument_position = stmt.bind_parameter_index("$ARGUMENT"); assert (action_position > 0); assert (id_position > 0); assert (argument_position > 0); - + stmt.bind_int (action_position, action); stmt.bind_text(id_position, id); stmt.bind_text(argument_position, argument); - + while (stmt.step () == Sqlite.ROW) {} stmt.reset (); - + m_db.simple_query("COMMIT TRANSACTION"); } - - + + public Gee.List<CachedAction> readCachedActions() { var query = "SELECT * FROM CachedActions"; @@ -733,13 +733,13 @@ public class FeedReader.DataBase : DataBaseReadOnly { } return actions; } - + public void resetCachedActions() { Logger.warning("resetCachedActions"); m_db.simple_query("DELETE FROM CachedActions"); } - + public bool cachedActionNecessary(CachedAction action) { var query = "SELECT COUNT(*) FROM CachedActions WHERE argument = ? AND id = ? AND action = ?"; @@ -747,11 +747,11 @@ public class FeedReader.DataBase : DataBaseReadOnly { assert(rows.size == 1 && rows[0].size == 1); return rows[0][0].to_int() == 0; } - + public void deleteOppositeCachedAction(CachedAction action) { var query = "DELETE FROM CachedActions WHERE argument = ? AND id = ? AND action = ?"; m_db.execute(query, { action.getArgument(), action.getID(), action.opposite() }); } - + } diff --git a/src/Enums.vala b/src/Enums.vala index d86b5721..1fe545f9 100644 --- a/src/Enums.vala +++ b/src/Enums.vala @@ -14,30 +14,30 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. namespace FeedReader { - + public enum FeedID { SEPARATOR = -77, ALL, CATEGORIES; - + public string to_string() { return ((int)this).to_string(); } } - + public enum ArticleListState { ALL, UNREAD, MARKED } - + public enum DragTarget { TAG, FEED, CAT } - + public enum ConsoleColor { BLACK, RED, @@ -48,14 +48,14 @@ namespace FeedReader { CYAN, WHITE, } - + public enum LogMessage { ERROR, WARNING, INFO, DEBUG } - + public enum ConnectionError { SUCCESS, NO_RESPONSE, @@ -66,24 +66,24 @@ namespace FeedReader { UNAUTHORIZED, UNKNOWN } - + public enum ArticleStatus { READ = 8, UNREAD, UNMARKED, MARKED, ALL; - + public string to_string() { return ((int)this).to_string(); } - + public int to_int() { return (int)this; } - + public string? column() { switch(this) { @@ -98,7 +98,7 @@ namespace FeedReader { } } } - + public enum LoginResponse { SUCCESS, MISSING_USER, @@ -117,12 +117,12 @@ namespace FeedReader { CA_ERROR, PLUGIN_NEEDED } - + public enum ArticleListSort { RECEIVED, DATE } - + public enum CachedActions { NONE, MARK_READ, @@ -133,50 +133,50 @@ namespace FeedReader { MARK_READ_CATEGORY, MARK_READ_ALL } - + public enum MediaType { VIDEO, AUDIO } - + public enum DisplayPosition { ALL, POS, LEFT } - + public enum MouseButton { LEFT = 1, MIDDLE, RIGHT, } - + public enum ArticleTheme { DEFAULT, SPRING, MIDNIGHT, PARCHMENT } - + public enum FeedListTheme { GTK, DARK, ELEMENTARY } - + public enum FontSize { SMALL, NORMAL, LARGE, HUGE } - + public enum DropArticles { NEVER, ONE_WEEK, ONE_MONTH, SIX_MONTHS; - + public int? to_weeks() { switch(this) @@ -193,7 +193,7 @@ namespace FeedReader { assert_not_reached(); } } - + public DateTime? to_start_date() { int? weeks = to_weeks(); @@ -201,52 +201,52 @@ namespace FeedReader { { return null; } - + return new DateTime.now_utc().add_weeks(-(int)weeks); } } - + public enum FeedListType { ALL_FEEDS, CATEGORY, FEED, TAG } - + public enum FeedListSort { RECEIVED, ALPHABETICAL } - + public enum CategoryID { NONE = -99, MASTER = -2, TAGS = -3, NEW = -4; - + public string to_string() { return ((int)this).to_string(); } } - + public enum ArticleListBalance { NONE, TOP, BOTTOM } - + public enum ScrollDirection { UP, DOWN } - + public enum EnclosureType { IMAGE, VIDEO, AUDIO, FILE; - + public static EnclosureType from_string(string? str) { if (str != null) @@ -267,7 +267,7 @@ namespace FeedReader { return FILE; } } - + [Flags] public enum BackendFlags { LOCAL, HOSTED, diff --git a/src/FavIcon.vala b/src/FavIcon.vala index fe177802..1afa0c41 100644 --- a/src/FavIcon.vala +++ b/src/FavIcon.vala @@ -16,14 +16,14 @@ public class FeedReader.FavIcon : GLib.Object { private static string m_icon_path = GLib.Environment.get_user_data_dir() + "/feedreader/data/feed_icons/"; private static Gee.Map<string?, FavIcon?> m_map = null; - + public static FavIcon for_feed(Feed? feed) { if(m_map == null) { m_map = new Gee.HashMap<string?, FavIcon?>(); } - + var feed_id = feed != null ? feed.getFeedID() : null; var icon = m_map.get(feed_id); if(icon == null) @@ -31,10 +31,10 @@ public class FeedReader.FavIcon : GLib.Object icon = new FavIcon(feed); m_map.set(feed_id, icon); } - + return icon; } - + public static void delete_feed(string feed_id) ensures (m_map == null || !m_map.has_key(feed_id)) { @@ -42,36 +42,36 @@ public class FeedReader.FavIcon : GLib.Object { return; } - + FavIcon icon; m_map.unset(feed_id, out icon); if(icon == null) { return; } - + icon.delete.begin((obj,res) => { icon.delete.end(res); }); } - + private Feed? m_feed; private Gee.Promise<Gdk.Pixbuf?> m_pixbuf = null; private ResourceMetadata m_metadata; - + public signal void surface_changed(Feed feed, Cairo.Surface surface); - + private FavIcon(Feed? feed) { m_feed = feed; } - + private int get_scale_factor() ensures (result > 0) { return MainWindow.get_default().get_style_context().get_scale(); } - + private Cairo.Surface create_surface_from_pixbuf(Gdk.Pixbuf pixbuf) requires (pixbuf.width > 0) requires (pixbuf.height > 0) @@ -79,7 +79,7 @@ public class FeedReader.FavIcon : GLib.Object { return Gdk.cairo_surface_create_from_pixbuf(pixbuf, get_scale_factor(), null); } - + public async Cairo.Surface? get_surface() { // This can happen if an API gives us weird data by returning an article @@ -88,7 +88,7 @@ public class FeedReader.FavIcon : GLib.Object { return null; } - + // wait for ready + expired so we don't make a bunch of requests at once if(m_pixbuf == null || (m_pixbuf.future.ready && m_metadata.is_expired())) { @@ -102,7 +102,7 @@ public class FeedReader.FavIcon : GLib.Object { return null; } - + return create_surface_from_pixbuf(pixbuf); } catch(Error e) @@ -111,7 +111,7 @@ public class FeedReader.FavIcon : GLib.Object return null; } } - + private async void load() { try @@ -121,10 +121,10 @@ public class FeedReader.FavIcon : GLib.Object { return; } - + var pixbuf = yield new Gdk.Pixbuf.from_stream_async(stream); stream.close(); - + if(pixbuf.get_height() <= 1 && pixbuf.get_width() <= 1) { Logger.warning("FavIcon: Icon for feed %s is too small".printf(m_feed.getTitle())); @@ -132,7 +132,7 @@ public class FeedReader.FavIcon : GLib.Object } int scale = get_scale_factor(); pixbuf = pixbuf.scale_simple(24 * scale, 24 * scale, Gdk.InterpType.BILINEAR); - + m_pixbuf.set_value(pixbuf); if(pixbuf != null) { @@ -151,14 +151,14 @@ public class FeedReader.FavIcon : GLib.Object } } } - + private InputStream? try_load_data_uri(string? icon_url) { if(icon_url == null || !icon_url.has_prefix("data")) { return null; } - + // LibSoup doesn't seem to handle data URI's properly, so handle them // ourselves.. int comma = icon_url.index_of_char(','); @@ -180,32 +180,32 @@ public class FeedReader.FavIcon : GLib.Object } return new MemoryInputStream.from_data(data); } - + private string fileNamePrefix() requires(m_feed != null) { return m_icon_path + m_feed.getFeedFileName(); } - + private string iconFileName() { string filename_prefix = fileNamePrefix(); return @"$filename_prefix.ico"; } - + private string metadataFileName() { string filename_prefix = fileNamePrefix(); return @"$filename_prefix.txt"; } - + private async void delete() { if (m_feed == null) { return; } - + try { var icon_file = File.new_for_path(iconFileName()); @@ -216,7 +216,7 @@ public class FeedReader.FavIcon : GLib.Object var feed_id = m_feed.getFeedID(); Logger.warning(@"Error deleting icon for feed $feed_id: " + e.message); } - + try { var metadata_file = File.new_for_path(metadataFileName()); @@ -228,7 +228,7 @@ public class FeedReader.FavIcon : GLib.Object Logger.warning(@"Error deleting icon metadata for feed $feed_id: " + e.message); } } - + private async InputStream? downloadFavIcon(GLib.Cancellable? cancellable = null) throws GLib.Error requires(m_feed != null) { @@ -237,23 +237,23 @@ public class FeedReader.FavIcon : GLib.Object { return datastream; } - + string icon_filename = iconFileName(); string metadata_filename = metadataFileName(); - + if(!yield Utils.ensure_path(m_icon_path)) { return null; } - + m_metadata = yield ResourceMetadata.from_file_async(metadata_filename); DateTime? expires = m_metadata.expires; - + if(cancellable != null && cancellable.is_cancelled()) { return null; } - + bool use_cached = false; if(!m_metadata.is_expired()) { @@ -277,16 +277,16 @@ public class FeedReader.FavIcon : GLib.Object return null; } } - + try { var obvious_icons = new Gee.ArrayList<string>(); - + if(m_feed.getIconURL() != null) { obvious_icons.add(m_feed.getIconURL()); } - + // try domainname/favicon.ico var uri = new Soup.URI(m_feed.getURL()); string? siteURL = null; @@ -294,7 +294,7 @@ public class FeedReader.FavIcon : GLib.Object { string hostname = uri.get_host(); siteURL = uri.get_scheme() + "://" + hostname; - + var icon_url = siteURL; if(!icon_url.has_suffix("/")) { @@ -303,7 +303,7 @@ public class FeedReader.FavIcon : GLib.Object icon_url += "favicon.ico"; obvious_icons.add(icon_url); } - + // Try to find one of those icons foreach(var url in obvious_icons) { @@ -312,25 +312,25 @@ public class FeedReader.FavIcon : GLib.Object { return stream; } - + if(cancellable != null && cancellable.is_cancelled()) { return null; } } - + // If all else fails, download html and parse to find location of favicon if(siteURL == null) { return null; } - + var message_html = new Soup.Message("GET", siteURL); if(Settings.tweaks().get_boolean("do-not-track")) { message_html.request_headers.append("DNT", "1"); } - + string html; try { @@ -352,24 +352,24 @@ public class FeedReader.FavIcon : GLib.Object Logger.debug(@"Utils.downloadFavIcon: parsing html on $siteURL failed"); return null; } - + try { // check for <link rel="icon"> var xpath = grabberUtils.getURL(doc, "//link[@rel='icon']"); - + if(xpath == null) { // check for <link rel="shortcut icon"> xpath = grabberUtils.getURL(doc, "//link[@rel='shortcut icon']"); } - + if(xpath == null) { // check for <link rel="apple-touch-icon"> xpath = grabberUtils.getURL(doc, "//link[@rel='apple-touch-icon']"); } - + if(xpath != null) { xpath = grabberUtils.completeURL(xpath, siteURL); @@ -381,7 +381,7 @@ public class FeedReader.FavIcon : GLib.Object delete doc; } } - + return null; } finally @@ -394,7 +394,7 @@ public class FeedReader.FavIcon : GLib.Object yield m_metadata.save_to_file_async(metadata_filename); } } - + private async InputStream? downloadIcon(string? icon_url, Cancellable? cancellable) throws GLib.Error { if(icon_url == "" || icon_url == null || GLib.Uri.parse_scheme(icon_url) == null) @@ -402,12 +402,12 @@ public class FeedReader.FavIcon : GLib.Object Logger.warning(@"Utils.downloadIcon: icon_url not valid $icon_url"); return null; } - + string icon_filename = iconFileName(); - + string etag = m_metadata.etag; string last_modified = m_metadata.last_modified; - + Logger.debug(@"Utils.downloadIcon: url = $icon_url"); var message = new Soup.Message("GET", icon_url); if(message == null) @@ -419,7 +419,7 @@ public class FeedReader.FavIcon : GLib.Object { message.request_headers.append("DNT", "1"); } - + if(etag != null) { message.request_headers.append("If-None-Match", etag); @@ -428,7 +428,7 @@ public class FeedReader.FavIcon : GLib.Object { message.request_headers.append("If-Modified-Since", last_modified); } - + uint8[]? data; try { @@ -462,10 +462,10 @@ public class FeedReader.FavIcon : GLib.Object Logger.error("Error writing icon: %s".printf(e.message)); return null; } - + m_metadata.etag = message.response_headers.get_one("ETag"); m_metadata.last_modified = message.response_headers.get_one("Last-Modified"); - + var cache_control = message.response_headers.get_list("Cache-Control"); if(cache_control != null) { @@ -482,11 +482,11 @@ public class FeedReader.FavIcon : GLib.Object m_metadata.expires = expires; } } - + m_metadata.last_modified = message.response_headers.get_one("Last-Modified"); return new MemoryInputStream.from_data(data); } - + Logger.warning(@"Could not download icon for feed: %s $icon_url, got response code $status".printf(m_feed.getFeedID())); return null; } diff --git a/src/FeedReader.vala b/src/FeedReader.vala index f8bfbe92..4c4df4bc 100644 --- a/src/FeedReader.vala +++ b/src/FeedReader.vala @@ -17,48 +17,48 @@ using GLib; using Gtk; namespace FeedReader { - + public const string QUICKLIST_ABOUT_STOCK = N_("About FeedReader"); - + public class FeedReaderApp : Gtk.Application { - + private MainWindow m_window; private bool m_online = true; private static FeedReaderApp? m_app = null; public static bool m_verbose = false; public signal void callback (string content); - - + + public new static FeedReaderApp get_default() { if(m_app == null) { m_app = new FeedReaderApp(); } - + return m_app; } - + public bool isOnline() { return m_online; } - + public void setOnline(bool online) { m_online = online; } - + protected override void startup() { Logger.init(m_verbose); Logger.info("FeedReader " + AboutInfo.version); - + Settings.state().set_boolean("currently-updating", false); - + base.startup(); } - + public override void activate() { base.activate(); @@ -67,14 +67,14 @@ namespace FeedReader { Intl.bindtextdomain (Constants.GETTEXT_PACKAGE, Constants.LOCALE_DIR); Intl.bind_textdomain_codeset (Constants.GETTEXT_PACKAGE, "UTF-8"); Intl.textdomain (Constants.GETTEXT_PACKAGE); - + if(m_window == null) { SetupActions(); m_window = MainWindow.get_default(); m_window.set_icon_name("org.gnome.FeedReader"); Gtk.IconTheme.get_default().add_resource_path("/org/gnome/FeedReader/icons"); - + FeedReaderBackend.get_default().newFeedList.connect(() => { GLib.Idle.add(() => { Logger.debug("FeedReader: newFeedList"); @@ -82,7 +82,7 @@ namespace FeedReader { return GLib.Source.REMOVE; }); }); - + FeedReaderBackend.get_default().refreshFeedListCounter.connect(() => { GLib.Idle.add(() => { Logger.debug("FeedReader: refreshFeedListCounter"); @@ -90,7 +90,7 @@ namespace FeedReader { return GLib.Source.REMOVE; }); }); - + FeedReaderBackend.get_default().updateArticleList.connect(() => { GLib.Idle.add(() => { Logger.debug("FeedReader: updateArticleList"); @@ -98,7 +98,7 @@ namespace FeedReader { return GLib.Source.REMOVE; }); }); - + FeedReaderBackend.get_default().syncStarted.connect(() => { GLib.Idle.add(() => { Logger.debug("FeedReader: syncStarted"); @@ -107,7 +107,7 @@ namespace FeedReader { return GLib.Source.REMOVE; }); }); - + FeedReaderBackend.get_default().syncFinished.connect(() => { GLib.Idle.add(() => { Logger.debug("FeedReader: syncFinished"); @@ -117,7 +117,7 @@ namespace FeedReader { return GLib.Source.REMOVE; }); }); - + FeedReaderBackend.get_default().springCleanStarted.connect(() => { GLib.Idle.add(() => { Logger.debug("FeedReader: springCleanStarted"); @@ -125,7 +125,7 @@ namespace FeedReader { return GLib.Source.REMOVE; }); }); - + FeedReaderBackend.get_default().springCleanFinished.connect(() => { GLib.Idle.add(() => { Logger.debug("FeedReader: springCleanFinished"); @@ -133,7 +133,7 @@ namespace FeedReader { return GLib.Source.REMOVE; }); }); - + FeedReaderBackend.get_default().showArticleListOverlay.connect(() => { GLib.Idle.add(() => { Logger.debug("FeedReader: showArticleListOverlay"); @@ -141,7 +141,7 @@ namespace FeedReader { return GLib.Source.REMOVE; }); }); - + FeedReaderBackend.get_default().setOffline.connect(() => { GLib.Idle.add(() => { Logger.debug("FeedReader: setOffline"); @@ -153,7 +153,7 @@ namespace FeedReader { return GLib.Source.REMOVE; }); }); - + FeedReaderBackend.get_default().setOnline.connect(() => { GLib.Idle.add(() => { Logger.debug("FeedReader: setOnline"); @@ -165,7 +165,7 @@ namespace FeedReader { return GLib.Source.REMOVE; }); }); - + FeedReaderBackend.get_default().feedAdded.connect((error, errmsg) => { GLib.Idle.add(() => { Logger.debug("FeedReader: feedAdded"); @@ -177,7 +177,7 @@ namespace FeedReader { return GLib.Source.REMOVE; }); }); - + FeedReaderBackend.get_default().opmlImported.connect(() => { GLib.Idle.add(() => { Logger.debug("FeedReader: opmlImported"); @@ -186,7 +186,7 @@ namespace FeedReader { return GLib.Source.REMOVE; }); }); - + FeedReaderBackend.get_default().updateSyncProgress.connect((progress) => { GLib.Idle.add(() => { Logger.debug("FeedReader: updateSyncProgress"); @@ -194,15 +194,15 @@ namespace FeedReader { return GLib.Source.REMOVE; }); }); - + FeedReaderBackend.get_default().updateBadge(); FeedReaderBackend.get_default().checkOnlineAsync.begin(); } - + m_window.show_all(); m_window.present(); } - + public override int command_line(ApplicationCommandLine command_line) { var args = command_line.get_arguments(); @@ -211,42 +211,42 @@ namespace FeedReader { Logger.debug("FeedReader: callback %s".printf(args[1])); callback (args[1]); } - + activate(); - + return 0; } - + protected override void shutdown() { Logger.debug("Shutdown!"); Gst.deinit(); base.shutdown(); } - + public void sync() { FeedReaderBackend.get_default().startSync(); } - + public void cancelSync() { FeedReaderBackend.get_default().cancelSync(); } - + private FeedReaderApp() { GLib.Object(application_id: "org.gnome.FeedReader", flags : ApplicationFlags.HANDLES_COMMAND_LINE); } - + private void SetupActions() { var quit_action = new SimpleAction("quit", null); quit_action.activate.connect(() => { - + MainWindow.get_default().writeInterfaceState(true); m_window.close(); - + if(Settings.state().get_boolean("currently-updating")) { Logger.debug("Quit: FeedReader seems to be syncing -> trying to cancel"); @@ -255,20 +255,20 @@ namespace FeedReader { { Gtk.main_iteration(); } - + Logger.debug("Quit: Sync cancelled -> shutting down"); } else { Logger.debug("No Sync ongoing -> Quit right away"); } - + FeedReaderApp.get_default().quit(); }); this.add_action(quit_action); } } - + public static void show_about(string[] args) { Gtk.init(ref args); @@ -279,12 +279,12 @@ namespace FeedReader { Gtk.main_quit(); } }); - + dialog.artists = AboutInfo.artists; dialog.authors = AboutInfo.authors; dialog.documenters = null; dialog.translator_credits = AboutInfo.translators; - + dialog.program_name = AboutInfo.programmName; dialog.comments = AboutInfo.comments; dialog.copyright = AboutInfo.copyright; @@ -292,11 +292,11 @@ namespace FeedReader { dialog.logo_icon_name = AboutInfo.iconName; dialog.license_type = Gtk.License.GPL_3_0; dialog.wrap_license = true; - + dialog.website = AboutInfo.website; dialog.present(); - + Gtk.main(); } - + } diff --git a/src/FeedReaderMain.vala b/src/FeedReaderMain.vala index 6fc77411..992cdec3 100644 --- a/src/FeedReaderMain.vala +++ b/src/FeedReaderMain.vala @@ -14,7 +14,7 @@ namespace FeedReader.Main { { "unreadCount", 0, 0, OptionArg.NONE, ref unreadCount, "current count of unread articles in the database", null }, { null } }; - + private static bool version = false; private static bool about = false; private static bool verbose = false; @@ -25,11 +25,11 @@ namespace FeedReader.Main { private static string? grabImages = null; private static string? articleUrl = null; private static bool unreadCount = false; - + public static int main (string[] args) { Ivy.Stacktrace.register_handlers(); - + try { var opt_context = new OptionContext(); @@ -42,28 +42,28 @@ namespace FeedReader.Main { print(e.message + "\n"); return 0; } - + FeedReaderApp.m_verbose = verbose; - + if(version) { stdout.printf("Version: %s\n", AboutInfo.version); stdout.printf("Git Commit: %s\n", Constants.GIT_SHA1); return 0; } - + if(about) { FeedReader.show_about(args); return 0; } - + if(media != null) { Utils.playMedia(args, media); return 0; } - + if(pingURL != null) { Logger.init(verbose); @@ -73,7 +73,7 @@ namespace FeedReader.Main { } return 0; } - + if(feedURL != null) { Logger.init(verbose); @@ -81,21 +81,21 @@ namespace FeedReader.Main { FeedReaderBackend.get_default().addFeed(feedURL, "", false); return 0; } - + if(grabImages != null && articleUrl != null) { Logger.init(verbose); FeedServer.grabImages(grabImages, articleUrl); return 0; } - + if(grabArticle != null) { Logger.init(verbose); FeedServer.grabArticle(grabArticle); return 0; } - + if(unreadCount) { var old_stdout =(owned)stdout; @@ -105,7 +105,7 @@ namespace FeedReader.Main { stdout.printf("%u\n", DataBase.readOnly().get_unread_total()); return 0; } - + try { Gst.init_check(ref args); @@ -114,10 +114,10 @@ namespace FeedReader.Main { { Logger.error("Gst.init: " + e.message); } - + var app = FeedReaderApp.get_default(); app.run(args); - + return 0; } } diff --git a/src/Logger.vala b/src/Logger.vala index 5e9480d9..e0dd4ba8 100644 --- a/src/Logger.vala +++ b/src/Logger.vala @@ -15,29 +15,29 @@ public class FeedReader.Logger : GLib.Object { const string LOG_DOMAIN = "feedreader"; - + private static bool m_log_debug_information = false; - + private static void log(LogLevelFlags level, string message) { GLib.log_structured(LOG_DOMAIN, level, "MESSAGE", "%s", message); } - + public static void error(string message) { log(GLib.LogLevelFlags.LEVEL_CRITICAL, message); } - + public static void warning(string message) { log(GLib.LogLevelFlags.LEVEL_WARNING, message); } - + public static void info(string message) { log(GLib.LogLevelFlags.LEVEL_INFO, message); } - + public static void debug(string message) { if(m_log_debug_information) @@ -45,7 +45,7 @@ public class FeedReader.Logger : GLib.Object { log(GLib.LogLevelFlags.LEVEL_DEBUG, message); } } - + public static void init(bool verbose) { m_log_debug_information = verbose; diff --git a/src/Model/Article.vala b/src/Model/Article.vala index eeb6a3c7..1888ab62 100644 --- a/src/Model/Article.vala +++ b/src/Model/Article.vala @@ -14,7 +14,7 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.Article : GLib.Object { - + private string m_articleID; private string m_title; private string m_url; @@ -31,10 +31,10 @@ public class FeedReader.Article : GLib.Object { private string m_guidHash; private int m_lastModified; private int m_pos; - + private static GLib.Settings? m_gnome_settings; private static bool m_clock_12_hour = false; - + static construct { // Lookup the schema in a complicated way so we don't require users @@ -50,7 +50,7 @@ public class FeedReader.Article : GLib.Object { }); } } - + public Article (string articleID, string? title, string? url, @@ -80,102 +80,102 @@ public class FeedReader.Article : GLib.Object { m_date = date != null ? date : new DateTime.now_utc(); m_guidHash = guidHash; m_lastModified = lastModified; - + m_tags = tags == null ? Gee.List.empty<string>() : tags; m_enclosures = enclosures == null ? Gee.List.empty<Enclosure>() : enclosures; } - + public string getArticleID() { return m_articleID; } - + public string getArticleFileName() { return GLib.Base64.encode(m_articleID.data); } - + public string getFeedFileName() { return GLib.Base64.encode(m_articleID.data); } - + public string getTitle() { return m_title; } - + public void setTitle(string title) { m_title = title; } - + public string getHTML() { return m_html; } - + public void setHTML(string html) { m_html = html; } - + public string getPreview() { return m_preview; } - + public void setPreview(string preview) { m_preview = preview; } - + public string? getAuthor() { return m_author; } - + public void setAuthor(string? author) { m_author = author; } - + public string getURL() { return m_url; } - + public void setURL(string url) { m_url = url; } - + public int getSortID() { return m_sortID; } - + public GLib.DateTime getDate() { return m_date; } - + public void SetDate(GLib.DateTime date) { m_date = date; } - + public string getDateNice(bool addTime = false) { var now = new GLib.DateTime.now_local(); var now_year = now.get_year(); var now_day = now.get_day_of_year(); var now_week = now.get_week_of_year(); - + var date_year = m_date.get_year(); var date_day = m_date.get_day_of_year(); var date_week = m_date.get_week_of_year(); - + var formats = new Gee.ArrayList<string>(); if(date_year == now_year) { @@ -201,7 +201,7 @@ public class FeedReader.Article : GLib.Object { { formats.add("%Y-%m-%d"); } - + if(addTime) { if(m_clock_12_hour) @@ -213,46 +213,46 @@ public class FeedReader.Article : GLib.Object { formats.add("%H:%M"); } } - + string format = StringUtils.join(formats, ", "); return m_date.format(format); } - + public string getFeedID() { return m_feedID; } - + public ArticleStatus getUnread() { return m_unread; } - + public void setUnread(ArticleStatus unread) { m_unread = unread; } - + public ArticleStatus getMarked() { return m_marked; } - + public void setMarked(ArticleStatus marked) { m_marked = marked; } - + public unowned Gee.List<string> getTagIDs() { return m_tags; } - + public void setTags(Gee.List<string> tags) { m_tags = tags; } - + public void addTag(string tagID) { if(!m_tags.contains(tagID)) @@ -260,7 +260,7 @@ public class FeedReader.Article : GLib.Object { m_tags.add(tagID); } } - + public void removeTag(string tagID) { if(m_tags.contains(tagID)) @@ -268,17 +268,17 @@ public class FeedReader.Article : GLib.Object { m_tags.remove(tagID); } } - + public unowned Gee.List<Enclosure> getEnclosures() { return m_enclosures; } - + public void setImages(Gee.List<Enclosure> enclosures) { m_enclosures = enclosures; } - + public void addEnclosure(Enclosure enc) { if(!m_enclosures.contains(enc)) @@ -286,7 +286,7 @@ public class FeedReader.Article : GLib.Object { m_enclosures.add(enc); } } - + public bool haveMedia() { foreach(Enclosure enc in m_enclosures) @@ -297,25 +297,25 @@ public class FeedReader.Article : GLib.Object { return true; } } - + return false; } - + public string getHash() { return m_guidHash; } - + public int getLastModified() { return m_lastModified; } - + public int getPos() { return m_pos; } - + public void setPos(int pos) { m_pos = pos; diff --git a/src/Model/CachedAction.vala b/src/Model/CachedAction.vala index cd484a63..3340e75e 100644 --- a/src/Model/CachedAction.vala +++ b/src/Model/CachedAction.vala @@ -14,47 +14,47 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.CachedAction : GLib.Object { - + private CachedActions m_action; private string m_id; private string m_argument; - + public CachedAction(CachedActions action, string id, string argument) { m_action = action; m_id = id; m_argument = argument; } - + public string getID() { return m_id; } - + public void setID(string id) { m_id = id; } - + public CachedActions getType() { return m_action; } - + public void setType(CachedActions action) { m_action = action; } - + public string getArgument() { return m_argument; } - + public void setArgument(string argument) { m_argument = argument; } - + public CachedActions opposite() { switch(m_action) @@ -68,10 +68,10 @@ public class FeedReader.CachedAction : GLib.Object { case CachedActions.MARK_UNSTARRED: return CachedActions.MARK_STARRED; } - + return CachedActions.NONE; } - + public void print() { Logger.debug("CachedAction: %s %s".printf(m_action.to_string(), m_id)); diff --git a/src/Model/Category.vala b/src/Model/Category.vala index 220becf8..dcb794f9 100644 --- a/src/Model/Category.vala +++ b/src/Model/Category.vala @@ -14,14 +14,14 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.Category : GLib.Object { - + private string m_categorieID; private string m_title; private uint m_unread_count; private int m_orderID; private string m_parent; private int m_level; - + public Category (string categorieID, string title, uint unread_count, int orderID, string parent, int level) { m_categorieID = categorieID; m_title = title; @@ -30,42 +30,42 @@ public class FeedReader.Category : GLib.Object { m_parent = parent; m_level = level; } - + public string getCatID() { return m_categorieID; } - + public string getFileName() { return GLib.Base64.encode(m_categorieID.data); } - + public string getTitle() { return m_title; } - + public uint getUnreadCount() { return m_unread_count; } - + public int getOrderID() { return m_orderID; } - + public string getParent() { return m_parent; } - + public int getLevel() { return m_level; } - + public void print() { Logger.debug("\ntitle: %s\nid: %s\nunread: %u".printf(m_title, m_categorieID, m_unread_count)); diff --git a/src/Model/Enclosure.vala b/src/Model/Enclosure.vala index 9bc3a87b..3506cb8c 100644 --- a/src/Model/Enclosure.vala +++ b/src/Model/Enclosure.vala @@ -14,23 +14,23 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.Enclosure : GLib.Object { - + private string m_article_id; private string m_url; private EnclosureType m_type; - + public Enclosure(string article_id, string url, EnclosureType type) { m_article_id = article_id; m_url = url; m_type = type; } - + public string get_url() { return m_url; } - + public EnclosureType get_enclosure_type() { return m_type; diff --git a/src/Model/Feed.vala b/src/Model/Feed.vala index c21de71d..c01dace9 100644 --- a/src/Model/Feed.vala +++ b/src/Model/Feed.vala @@ -14,7 +14,7 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.Feed : GLib.Object { - + private string m_feedID; private string m_title; private string m_url; @@ -22,7 +22,7 @@ public class FeedReader.Feed : GLib.Object { private uint m_unread; private Gee.List<string> m_catIDs; private string? m_iconURL; - + public Feed(string feedID, string? title, string? url, uint unread, Gee.List<string>? catIDs = null, string? iconURL = null, string? xmlURL = null) { m_feedID = feedID; @@ -33,110 +33,110 @@ public class FeedReader.Feed : GLib.Object { m_iconURL = iconURL == "" ? null : iconURL; m_xmlURL = xmlURL; } - + public string getFeedID() { return m_feedID; } - + public string getFeedFileName() { return GLib.Base64.encode(m_feedID.data); } - + public string getTitle() { return m_title; } - + public void setTitle(string title) { m_title = title; } - + public string getURL() { return m_url; } - + public void setURL(string url) { m_url = url; } - + public uint getUnread() { return m_unread; } - + public void setUnread(uint unread) { m_unread = unread; } - + public Gee.List<string> getCatIDs() { return m_catIDs; } - + public string getCatString() { return StringUtils.join(m_catIDs, ","); } - + public bool hasCat(string catID) { return m_catIDs.contains(catID); } - + public void addCat(string catID) { m_catIDs.add(catID); } - + public void setCats(Gee.Collection<string> catIDs) { m_catIDs.clear(); m_catIDs.add_all(catIDs); } - + // Helper function for backends that only support one category per feed public void setCategory(string id) { m_catIDs.clear(); m_catIDs.add(id); } - + public bool isUncategorized() { if(m_catIDs.size == 0) { return true; } - + if(m_catIDs.size == 1 && m_catIDs[0].contains("global.must")) { return true; } - + return false; } - + public string? getIconURL() { return m_iconURL; } - + public void setIconURL(string? url) { m_iconURL = url; } - + public string? getXmlUrl() { return m_xmlURL; } - + public void print() { Logger.debug("\ntitle: %s\nid: %s\nurl: %s\nunread: %u".printf(m_title, m_feedID, m_url, m_unread)); diff --git a/src/Model/InterfaceState.vala b/src/Model/InterfaceState.vala index 69898701..1c3272d1 100644 --- a/src/Model/InterfaceState.vala +++ b/src/Model/InterfaceState.vala @@ -14,7 +14,7 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.InterfaceState : GLib.Object { - + private int m_WindowHeight = 900; private int m_WindowWidth = 1600; private int m_FeedsAndArticleWidth = 600; @@ -30,12 +30,12 @@ public class FeedReader.InterfaceState : GLib.Object { private string? m_ArticleListTopRow = null; private string[] m_ExpandedCategories = {}; private ArticleListState m_ArticleListState = ArticleListState.ALL; - + public InterfaceState() { - + } - + public void write(bool shutdown) { Settings.state().set_int ("window-width", m_WindowWidth); @@ -57,93 +57,93 @@ public class FeedReader.InterfaceState : GLib.Object { Settings.state().set_int ("articlelist-row-offset", m_ArticleListRowOffset); } } - + public void setWindowSize(int height, int width) { m_WindowHeight = height; m_WindowWidth = width; } - + public int getWindowHeight() { return m_WindowHeight; } - + public int getWindowWidth() { return m_WindowWidth; } - + public void setFeedsAndArticleWidth(int size) { m_FeedsAndArticleWidth = size; } - + public int getFeedsAndArticleWidth() { return m_FeedsAndArticleWidth; } - + public void setFeedListWidth(int size) { m_FeedListWidth = size; } - + public int getFeedListWidth() { return m_FeedListWidth; } - + public void setFeedListScrollPos(double pos) { m_FeedListScrollPos = pos; } - + public double getFeedListScrollPos() { return m_FeedListScrollPos; } - + public void setArticleViewScrollPos(int pos) { m_ArticleViewScrollPos = pos; } - + public int getArticleViewScrollPos() { return m_ArticleViewScrollPos; } - + public void setArticleListScrollPos(double pos) { m_ArticleListScrollPos = pos; } - + public double getArticleListScrollPos() { return m_ArticleListScrollPos; } - + public void setArticleListRowOffset(int count) { m_ArticleListRowOffset = count; } - + public int getArticleListRowOffset() { return m_ArticleListRowOffset; } - + public void setArticleListSelectedRow(string articleID) { m_ArticleListSelectedRow = articleID; } - + public string getArticleListSelectedRow() { return m_ArticleListSelectedRow; } - + public void setArticleListTopRow(Article? article) { if(article == null) @@ -155,60 +155,60 @@ public class FeedReader.InterfaceState : GLib.Object { m_ArticleListTopRow = article.getArticleID(); } } - + public string getArticleListTopRow() { return m_ArticleListTopRow; } - + public void setWindowMaximized(bool max) { m_WindowMaximized = max; } - + public bool getWindowMaximized() { return m_WindowMaximized; } - + public void setSearchTerm(string search) { m_SearchTerm = search; } - + public string getSearchTerm() { return m_SearchTerm; } - + public void setFeedListSelectedRow(string code) { m_FeedListSelectedRow = code; } - + public string getFeedListSelectedRow() { return m_FeedListSelectedRow; } - + public void setExpandedCategories(string[] array) { m_ExpandedCategories = array; } - + public string[] getExpandedCategories() { return m_ExpandedCategories; } - + public void setArticleListState(ArticleListState state) { m_ArticleListState = state; } - + public ArticleListState getArticleListState() { return m_ArticleListState; } - + } diff --git a/src/Model/ShareAccount.vala b/src/Model/ShareAccount.vala index 74b61cf3..f629360b 100644 --- a/src/Model/ShareAccount.vala +++ b/src/Model/ShareAccount.vala @@ -14,14 +14,14 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.ShareAccount : GLib.Object { - + private string m_id; private string m_type; private string m_accountName; private string m_username; private string m_iconName; private bool m_systemAccount; - + public ShareAccount(string id, string type, string username, string iconName, string accountName, bool system = false) { m_id = id; @@ -31,32 +31,32 @@ public class FeedReader.ShareAccount : GLib.Object { m_accountName = accountName; m_systemAccount = system; } - + public string getID() { return m_id; } - + public string getType() { return m_type; } - + public string getUsername() { return m_username; } - + public string getIconName() { return m_iconName; } - + public string getAccountName() { return m_accountName; } - + public bool isSystemAccount() { return m_systemAccount; diff --git a/src/Model/Tag.vala b/src/Model/Tag.vala index d1f6e347..19f88656 100644 --- a/src/Model/Tag.vala +++ b/src/Model/Tag.vala @@ -14,37 +14,37 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.Tag : GLib.Object { - + private string m_tagID; private string m_title; private int m_color; - + public Tag (string tagID, string title, int color) { m_tagID = tagID; m_title = title; m_color = color; } - + public string getTagID() { return m_tagID; } - + public string getTitle() { return m_title; } - + public void setTitle(string title) { m_title = title; } - + public int getColor() { return m_color; } - + public void setColor(int color) { m_color = color; diff --git a/src/Notification.vala b/src/Notification.vala index 05731cf2..2478dfba 100644 --- a/src/Notification.vala +++ b/src/Notification.vala @@ -14,13 +14,13 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.Notification : GLib.Object { - + public static void send(uint new_articles, int new_and_unread) { string message = ""; string summary = _("New articles"); uint unread = DataBase.readOnly().get_unread_total(); - + if(new_articles > 0 && new_and_unread > 0) { if(new_articles == 1) @@ -31,18 +31,18 @@ public class FeedReader.Notification : GLib.Object { { message = _("There are %u new articles (%u unread)").printf(new_articles, unread); } - + var notification = new GLib.Notification(summary); notification.set_body(message); notification.set_priority(GLib.NotificationPriority.NORMAL); notification.set_icon(new GLib.ThemedIcon("org.gnome.FeedReader")); - + GLib.Application.get_default().send_notification("feedreader_default", notification); } } - + private Notification() { - + } } diff --git a/src/Password.vala b/src/Password.vala index a43b7ce1..db28c8a8 100644 --- a/src/Password.vala +++ b/src/Password.vala @@ -14,14 +14,14 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.Password : GLib.Object { - + public delegate HashTable<string, string> GetAttributesFunc(); - + private Secret.Collection m_secrets; private Secret.Schema m_schema; private GetAttributesFunc m_get_attributes; private string m_label; - + public Password(Secret.Collection secrets, Secret.Schema schema, string label, owned GetAttributesFunc get_attributes) { m_secrets = secrets; @@ -29,39 +29,39 @@ public class FeedReader.Password : GLib.Object { m_label = label; m_get_attributes = (owned)get_attributes; } - + private void unlock_keyring(Cancellable? cancellable = null) throws Error { if (!m_secrets.get_locked()) { return; } - + var collections_to_unlock = new List<Secret.Collection>(); collections_to_unlock.append(m_secrets); var service = m_secrets.get_service(); service.unlock_sync(collections_to_unlock, cancellable, null); } - + public string get_password(Cancellable? cancellable = null) { var attributes = m_get_attributes(); try { unlock_keyring(cancellable); - + if(cancellable != null && cancellable.is_cancelled()) { return ""; } - + var secrets = m_secrets.search_sync(m_schema, attributes, Secret.SearchFlags.NONE, cancellable); - + if(cancellable != null && cancellable.is_cancelled()) { return ""; } - + if(secrets.length() != 0) { var item = secrets.data; @@ -70,7 +70,7 @@ public class FeedReader.Password : GLib.Object { { return ""; } - + var secret = item.get_secret(); if(secret == null) { @@ -92,19 +92,19 @@ public class FeedReader.Password : GLib.Object { } return ""; } - + public void set_password(string password, Cancellable? cancellable = null) { var attributes = m_get_attributes(); try { unlock_keyring(cancellable); - + if(cancellable != null && cancellable.is_cancelled()) { return; } - + var value = new Secret.Value(password, password.length, "text/plain"); Secret.Item.create_sync(m_secrets, m_schema, attributes, m_label, value, Secret.ItemCreateFlags.REPLACE, cancellable); } @@ -113,26 +113,26 @@ public class FeedReader.Password : GLib.Object { Logger.error("Password.setPassword: " + e.message); } } - + public bool delete_password(Cancellable? cancellable = null) { var attributes = m_get_attributes(); try { unlock_keyring(cancellable); - + if(cancellable != null && cancellable.is_cancelled()) { return false; } - + var secrets = m_secrets.search_sync(m_schema, attributes, Secret.SearchFlags.NONE, cancellable); - + if(cancellable != null && cancellable.is_cancelled()) { return false; } - + if(secrets.length() != 0) { var item = secrets.data; diff --git a/src/QueryBuilder.vala b/src/QueryBuilder.vala index a9e0dd05..6a41f836 100644 --- a/src/QueryBuilder.vala +++ b/src/QueryBuilder.vala @@ -32,13 +32,13 @@ public class FeedReader.QueryBuilder : GLib.Object { private bool m_order_descending = false; private uint? m_limit = null; private uint? m_offset = null; - + public QueryBuilder(QueryType type, string table) { m_type = type; m_table = table; } - + private void insert_value_pair(string field, string value) requires (m_type == QueryType.INSERT || m_type == QueryType.INSERT_OR_IGNORE @@ -47,72 +47,72 @@ public class FeedReader.QueryBuilder : GLib.Object { m_fields.add(field); m_values.add(value); } - + public void insert_param(string field, string value) requires (value.has_prefix("$") && !value.contains("'")) { insert_value_pair(field, value); } - + public void insert_int(string field, int64 value) { insert_value_pair(field, value.to_string()); } - + public void select_field(string field) requires (m_type == QueryType.SELECT) { m_fields.add(field); } - + private void update(string field, string value) requires (m_type == QueryType.UPDATE) { m_fields.add(field); m_values.add(value); } - + public void update_param(string field, string value) requires (value.has_prefix("$") && !value.contains("'")) { update(field, value); } - + public void update_string(string field, string value) { update(field, SQLite.quote_string(value)); } - + public void update_int(string field, int64 value) { update(field, value.to_string()); } - + private void where_equal(string field, string safe_value) requires (m_type == QueryType.UPDATE || m_type == QueryType.SELECT || m_type == QueryType.DELETE) { - + m_conditions.add("%s = %s".printf(field, safe_value)); } - + public void where_equal_param(string field, string value) requires (value.has_prefix("$") && !value.contains("'")) { where_equal(field, value); } - + public void where_equal_int(string field, int64 value) { where_equal(field, value.to_string()); } - + public void where_equal_string(string field, string value) { where_equal(field, SQLite.quote_string(value)); } - + public void where(string condition) requires (m_type == QueryType.UPDATE || m_type == QueryType.SELECT @@ -120,7 +120,7 @@ public class FeedReader.QueryBuilder : GLib.Object { { m_conditions.add(condition); } - + public void where_in_strings(string field, Gee.List<string> values) requires (m_type == QueryType.UPDATE || m_type == QueryType.SELECT @@ -142,26 +142,26 @@ public class FeedReader.QueryBuilder : GLib.Object { m_conditions.add("%s IN (%s)".printf(field, compound_values.str)); } } - + public void order_by(string field, bool desc) requires (m_type == QueryType.SELECT) { m_order_by_column = field; m_order_descending = desc; } - + public void limit(uint limit) requires (m_type == QueryType.SELECT) { m_limit = limit; } - + public void offset(uint offset) requires (m_type == QueryType.SELECT) { m_offset = offset; } - + public string to_string() { var query = new GLib.StringBuilder(); @@ -171,7 +171,7 @@ public class FeedReader.QueryBuilder : GLib.Object { case QueryType.INSERT_OR_IGNORE: case QueryType.INSERT_OR_REPLACE: query.append("INSERT "); - + if(m_type == QueryType.INSERT_OR_IGNORE) { query.append("OR IGNORE "); @@ -180,17 +180,17 @@ public class FeedReader.QueryBuilder : GLib.Object { { query.append("OR REPLACE "); } - + query.append_printf("INTO %s (", m_table); StringUtils.stringbuilder_append_join(query, m_fields, ", "); query.append(") VALUES ("); StringUtils.stringbuilder_append_join(query, m_values, ", "); query.append_c(')'); break; - + case QueryType.UPDATE: query.append_printf("UPDATE %s SET ", m_table); - + assert(m_fields.size > 0); for(int i = 0; i < m_fields.size; i++) { @@ -198,30 +198,30 @@ public class FeedReader.QueryBuilder : GLib.Object { { query.append(", "); } - + query.append(m_fields.get(i)); query.append(" = "); query.append(m_values.get(i)); } - + append_conditions(query); break; - - + + case QueryType.DELETE: query.append("DELETE FROM "); query.append(m_table); append_conditions(query); break; - - + + case QueryType.SELECT: query.append("SELECT "); StringUtils.stringbuilder_append_join(query, m_fields, ", "); query.append_printf(" FROM %s", m_table); - + append_conditions(query); - + if (m_order_by_column != null) { query.append_printf( @@ -229,33 +229,33 @@ public class FeedReader.QueryBuilder : GLib.Object { m_order_by_column, m_order_descending ? "DESC" : "ASC"); } - + if (m_limit != null) { query.append_printf(" LIMIT %u", m_limit); } - + if (m_offset != null) { query.append_printf(" OFFSET %u", m_offset); } break; } - + return query.str; } - + private void append_conditions(StringBuilder query) { if(m_conditions.size == 0) { return; } - + query.append(" WHERE "); StringUtils.stringbuilder_append_join(query, m_conditions, " AND "); } - + public void print() { Logger.debug(to_string()); diff --git a/src/Rfc822.vala b/src/Rfc822.vala index 053c035a..021a3f5e 100644 --- a/src/Rfc822.vala +++ b/src/Rfc822.vala @@ -1,5 +1,5 @@ namespace FeedReader.Rfc822 { - + /** * Parse a date string in RFC 822 format * Note that we don't use Time.strptime because it uses the current locale @@ -14,7 +14,7 @@ namespace FeedReader.Rfc822 { { return null; } - + Regex re; try { re = new Regex(""" @@ -39,16 +39,16 @@ namespace FeedReader.Rfc822 { assert(false); return null; } - + MatchInfo info; if (!re.match(str, 0, out info)) { return null; } - + var dayStr = info.fetch_named("day"); var day = int.parse(dayStr); - + var monthStr = info.fetch_named("month").ascii_down(); int month; switch(monthStr) { @@ -93,7 +93,7 @@ namespace FeedReader.Rfc822 { assert(false); return null; } - + var yearStr = info.fetch_named("year"); var year = int.parse(yearStr); // Two-digit years from 00 to 49 should be interpreted as 2000 to 2049 @@ -112,7 +112,7 @@ namespace FeedReader.Rfc822 { var minute = int.parse(minuteStr); var secondStr = info.fetch_named("second"); var second = secondStr == null || secondStr == "" ? 0 : int.parse(secondStr); - + var zoneStr = info.fetch_named("zone"); TimeZone zone; switch(zoneStr.ascii_up()) { @@ -136,7 +136,7 @@ namespace FeedReader.Rfc822 { case "PST": zone = new TimeZone("-08"); break; - + case "GMT": case "UT": case "Z": @@ -146,7 +146,7 @@ namespace FeedReader.Rfc822 { zone = new TimeZone(zoneStr); break; } - + return new DateTime(zone, year, month, day, hour, minute, second); } } diff --git a/src/SQLite.vala b/src/SQLite.vala index fdfa87cd..2e85fa16 100644 --- a/src/SQLite.vala +++ b/src/SQLite.vala @@ -16,7 +16,7 @@ /* A wrapper around the low-level SQLite API */ public class FeedReader.SQLite : GLib.Object { private Sqlite.Database m_db; - + public SQLite(string db_path, int busy_timeout = 1000) { var path = GLib.File.new_for_path(db_path); @@ -35,16 +35,16 @@ public class FeedReader.SQLite : GLib.Object { Logger.error("SQLite: " + e.message); } } - + int rc = Sqlite.Database.open_v2(db_path, out m_db); if(rc != Sqlite.OK) { error("Can't open database: %d: %s".printf(m_db.errcode(), m_db.errmsg())); } - + m_db.busy_timeout(busy_timeout); } - + // Backwards compatibility interface public Sqlite.Statement prepare(string query) requires (query != "") @@ -57,17 +57,17 @@ public class FeedReader.SQLite : GLib.Object { } return stmt; } - + public string errmsg() { return m_db.errmsg(); } - + public void checkpoint() { m_db.wal_checkpoint(""); } - + public void simple_query(string query) requires (query != "") { @@ -78,7 +78,7 @@ public class FeedReader.SQLite : GLib.Object { error("Failed to execute simple query: %d: %s\nSQL is: %s".printf(ec, errmsg, query)); } } - + public Gee.List<Gee.List<Sqlite.Value?>> execute(string query, GLib.Value?[]? params = null) requires (query != "") { @@ -88,7 +88,7 @@ public class FeedReader.SQLite : GLib.Object { { error("Can't prepare statement: %d: %s\nSQL is: %s".printf(m_db.errcode(), m_db.errmsg(), query)); } - + if(params != null) { int i = 1; @@ -125,7 +125,7 @@ public class FeedReader.SQLite : GLib.Object { ++i; } } - + var rows = new Gee.ArrayList<Gee.List<Sqlite.Value?>>(); while(stmt.step() == Sqlite.ROW) { @@ -137,10 +137,10 @@ public class FeedReader.SQLite : GLib.Object { rows.add(row); } stmt.reset (); - + return rows; } - + public static string quote_string(string str) { var escaped = str.replace("'", "''"); diff --git a/src/Settings.vala b/src/Settings.vala index 9365ef97..f320ebb0 100644 --- a/src/Settings.vala +++ b/src/Settings.vala @@ -14,60 +14,60 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.Settings : GLib.Object { - + private static GLib.Settings? m_general = null; private static GLib.Settings? m_tweaks = null; private static GLib.Settings? m_state = null; private static GLib.Settings? m_keys = null; private static Gee.HashMap<string, GLib.Settings>? m_share = null; - + public static GLib.Settings general() { if(m_general == null) { m_general = new GLib.Settings("org.gnome.feedreader"); } - + return m_general; } - + public static GLib.Settings tweaks() { if(m_tweaks == null) { m_tweaks = new GLib.Settings("org.gnome.feedreader.tweaks"); } - + return m_tweaks; } - + public static GLib.Settings state() { if(m_state == null) { m_state = new GLib.Settings("org.gnome.feedreader.saved-state"); } - + return m_state; } - + public static GLib.Settings keybindings() { if(m_keys == null) { m_keys = new GLib.Settings("org.gnome.feedreader.keybindings"); } - + return m_keys; } - + public static GLib.Settings? share(string pluginName) { if(m_share == null) { m_share = new Gee.HashMap<string, GLib.Settings>(); } - + if(m_share.has_key(pluginName)) { return m_share.get(pluginName); @@ -79,9 +79,9 @@ public class FeedReader.Settings : GLib.Object { return settings; } } - + private Settings() { - + } } diff --git a/src/Share/ServiceSetup.vala b/src/Share/ServiceSetup.vala index 02ca116d..096c7288 100644 --- a/src/Share/ServiceSetup.vala +++ b/src/Share/ServiceSetup.vala @@ -14,7 +14,7 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.ServiceSetup : Gtk.ListBoxRow { - + protected string m_name; protected Gtk.Revealer m_revealer; protected Gtk.Label m_label; @@ -32,7 +32,7 @@ public class FeedReader.ServiceSetup : Gtk.ListBoxRow { protected bool m_systemAccount; public signal void removeRow(); public signal void showInfoBar(string text); - + public ServiceSetup(string name, string iconName, bool loggedIn, string username, bool system = false) { m_name = name; @@ -42,7 +42,7 @@ public class FeedReader.ServiceSetup : Gtk.ListBoxRow { m_iconStack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT); m_iconStack.set_transition_duration(300); m_labelStack = new Gtk.Stack(); - + m_eventbox = new Gtk.EventBox(); m_eventbox.set_events(Gdk.EventMask.ENTER_NOTIFY_MASK); m_eventbox.set_events(Gdk.EventMask.LEAVE_NOTIFY_MASK); @@ -52,37 +52,37 @@ public class FeedReader.ServiceSetup : Gtk.ListBoxRow { m_eventbox.leave_notify_event.connect(onLeave); } m_eventbox.add(m_iconStack); - + m_login_button = new Gtk.Button.with_label(_("Login")); m_login_button.hexpand = false; m_login_button.margin = 10; m_login_button.clicked.connect(login); - + m_logout_button = new Gtk.Button.with_label(_("Logout")); m_logout_button.hexpand = false; m_logout_button.margin = 10; m_logout_button.clicked.connect(logout); m_logout_button.get_style_context().add_class(Gtk.STYLE_CLASS_DESTRUCTIVE_ACTION); - + var loggedIN = new Gtk.Image.from_icon_name("feed-status-ok", Gtk.IconSize.LARGE_TOOLBAR); m_spinner = new Gtk.Spinner(); m_spinner.set_size_request(24, 24); - + m_iconStack.add_named(m_login_button, "button"); m_iconStack.add_named(loggedIN, "loggedIN"); m_iconStack.add_named(m_logout_button, "logOUT"); m_iconStack.add_named(m_spinner, "spinner"); m_iconStack.set_size_request(100, 0); - + m_box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0); m_box.set_size_request(0, 50); - + var icon = new Gtk.Image.from_icon_name(iconName, Gtk.IconSize.DND); icon.set_size_request(100, 0); - + var label = new Gtk.Label(m_name); label.set_alignment(0.5f, 0.5f); - + var label1 = new Gtk.Label(m_name); m_label = new Gtk.Label(username); label1.set_alignment(0.5f, 1.0f); @@ -91,28 +91,28 @@ public class FeedReader.ServiceSetup : Gtk.ListBoxRow { m_labelBox = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); m_labelBox.pack_start(label1, true, true, 0); m_labelBox.pack_start(m_label, true, true, 0); - + m_labelStack.add_named(label, "loggedOUT"); m_labelStack.add_named(m_labelBox, "loggedIN"); - + m_box.pack_start(icon, false, false, 0); m_box.pack_start(m_labelStack, true, true, 0); m_box.pack_end(m_eventbox, false, false, 0); - + m_seperator_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); var separator = new Gtk.Separator(Gtk.Orientation.HORIZONTAL); m_seperator_box.pack_start(m_box, true, true, 0); m_seperator_box.pack_end(separator, false, false, 0); - - + + m_revealer = new Gtk.Revealer(); m_revealer.set_transition_type(Gtk.RevealerTransitionType.SLIDE_DOWN); m_revealer.add(m_seperator_box); m_revealer.set_reveal_child(false); - + this.add(m_revealer); this.show_all(); - + if(m_isLoggedIN) { m_iconStack.set_visible_child_name("loggedIN"); @@ -124,17 +124,17 @@ public class FeedReader.ServiceSetup : Gtk.ListBoxRow { m_labelStack.set_visible_child_name("loggedOUT"); } } - + public virtual void login() { - + } - + public virtual void logout() { - + } - + private bool onEnter() { if(m_isLoggedIN) @@ -143,7 +143,7 @@ public class FeedReader.ServiceSetup : Gtk.ListBoxRow { } return false; } - + private bool onLeave() { if(m_isLoggedIN) @@ -152,46 +152,46 @@ public class FeedReader.ServiceSetup : Gtk.ListBoxRow { } return false; } - + public void reveal(bool animate = true) { if(!animate) { m_revealer.set_transition_type(Gtk.RevealerTransitionType.NONE); } - + m_revealer.set_reveal_child(true); this.show_all(); } - + public void unreveal() { m_revealer.set_reveal_child(false); } - + public bool isLoggedIn() { return m_isLoggedIN; } - + public string getID() { return m_id; } - + public bool isSystemAccount() { return m_systemAccount; } - + public string getUserName() { return m_label.get_text(); } - + public string getName() { return m_name; } - + } diff --git a/src/Share/ShareAccountInterface.vala b/src/Share/ShareAccountInterface.vala index b6246779..a179ec36 100644 --- a/src/Share/ShareAccountInterface.vala +++ b/src/Share/ShareAccountInterface.vala @@ -14,36 +14,36 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public interface FeedReader.ShareAccountInterface : GLib.Object { - + public signal void addAccount(string id, string type, string username, string iconName, string accountName); - + public signal void deleteAccount(string id); - + public abstract void setupSystemAccounts(Gee.List<ShareAccount> accounts); - + public abstract bool useSystemAccounts(); - + public abstract string pluginID(); - + public abstract string pluginName(); - + public abstract bool addBookmark(string id, string url, bool system); - + public abstract bool logout(string id); - + public abstract string getIconName(); - + public abstract string getUsername(string id); - + public abstract bool needSetup(); - + public abstract bool singleInstance(); - + public abstract ServiceSetup? newSetup_withID(string id, string username); - + public abstract ServiceSetup? newSetup(); - + public abstract ServiceSetup? newSystemAccount(string id, string username); - + public abstract ShareForm? shareWidget(string url); } diff --git a/src/Share/share.vala b/src/Share/share.vala index 331d1274..5862f36a 100644 --- a/src/Share/share.vala +++ b/src/Share/share.vala @@ -14,30 +14,30 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.Share : GLib.Object { - + private Gee.List<ShareAccount> m_accounts; private Peas.ExtensionSet m_plugins; private static Share? m_share = null; private Goa.Client? m_client = null; - + public static Share get_default() { if(m_share == null) { m_share = new Share(); } - + return m_share; } - + private Share() { var engine = Peas.Engine.get_default(); engine.add_search_path(Constants.INSTALL_LIBDIR + "/pluginsShare/", null); engine.enable_loader("python3"); - + m_plugins = new Peas.ExtensionSet(engine, typeof(ShareAccountInterface)); - + m_plugins.extension_added.connect((info, extension) => { var plugin = (extension as ShareAccountInterface); plugin.addAccount.connect(accountAdded); @@ -45,19 +45,19 @@ public class FeedReader.Share : GLib.Object { refreshAccounts(); }); }); - + //m_plugins.extension_removed.connect((info, extension) => {}); - + checkSystemAccounts(); - + foreach(var plugin in engine.get_plugin_list()) { engine.try_load_plugin(plugin); } - + refreshAccounts(); } - + public void refreshAccounts() { Logger.debug("Share: refreshAccounts"); @@ -96,35 +96,35 @@ public class FeedReader.Share : GLib.Object { ); } }); - + // load gresource-icons from the plugins Gtk.IconTheme.get_default().add_resource_path("/org/gnome/FeedReader/icons"); } - + private ShareAccountInterface? getInterface(string type) { ShareAccountInterface? plug = null; - + m_plugins.foreach((@set, info, exten) => { var plugin = (exten as ShareAccountInterface); - + if(plugin.pluginID() == type) { plug = plugin; } }); - + return plug; } - + public Gee.List<ShareAccount> getAccountTypes() { var accounts = new Gee.ArrayList<ShareAccount>(); - + m_plugins.foreach((@set, info, exten) => { var plugin = (exten as ShareAccountInterface); var pluginID = plugin.pluginID(); - + bool singleInstance = false; if(plugin.singleInstance()) { @@ -137,27 +137,27 @@ public class FeedReader.Share : GLib.Object { { singleInstance = true; } - + if(plugin.needSetup() && !plugin.useSystemAccounts() && singleInstance) { accounts.add(new ShareAccount("", pluginID, "", plugin.getIconName(), plugin.pluginName())); } }); - + return accounts; } - - + + public Gee.List<ShareAccount> getAccounts() { return m_accounts; } - + public string generateNewID() { string id = Utils.string_random(12); bool unique = true; - + m_plugins.foreach((@set, info, exten) => { var plugin = (exten as ShareAccountInterface); var plugID = plugin.pluginID(); @@ -174,22 +174,22 @@ public class FeedReader.Share : GLib.Object { } } }); - + if(!unique) { return generateNewID(); } - + return id; } - + public void accountAdded(string id, string type, string username, string iconName, string accountName) { Logger.debug("Share: %s account added for user: %s".printf(type, username)); m_accounts.add(new ShareAccount(id, type, username, iconName, accountName)); } - - + + public string getUsername(string accountID) { foreach(var account in m_accounts) @@ -199,11 +199,11 @@ public class FeedReader.Share : GLib.Object { return getInterface(account.getType()).getUsername(accountID); } } - + return ""; } - - + + public bool addBookmark(string accountID, string url) { foreach(var account in m_accounts) @@ -213,10 +213,10 @@ public class FeedReader.Share : GLib.Object { return getInterface(account.getType()).addBookmark(accountID, url, account.isSystemAccount()); } } - + return false; } - + public bool needSetup(string accountID) { foreach(var account in m_accounts) @@ -226,10 +226,10 @@ public class FeedReader.Share : GLib.Object { return getInterface(account.getType()).needSetup(); } } - + return false; } - + public ServiceSetup? newSetup_withID(string accountID) { foreach(var account in m_accounts) @@ -239,15 +239,15 @@ public class FeedReader.Share : GLib.Object { return getInterface(account.getType()).newSetup_withID(account.getID(), account.getUsername()); } } - + return null; } - + public ServiceSetup? newSetup(string type) { return getInterface(type).newSetup(); } - + public ServiceSetup? newSystemAccount(string accountID) { foreach(var account in m_accounts) @@ -257,26 +257,26 @@ public class FeedReader.Share : GLib.Object { return getInterface(account.getType()).newSystemAccount(account.getID(), account.getUsername()); } } - + return null; } - + public ShareForm? shareWidget(string type, string url) { ShareForm? form = null; - + m_plugins.foreach((@set, info, exten) => { var plugin = (exten as ShareAccountInterface); - + if(plugin.pluginID() == type) { form = plugin.shareWidget(url); } }); - + return form; } - + private void checkSystemAccounts() { try @@ -307,7 +307,7 @@ public class FeedReader.Share : GLib.Object { Logger.error("share.checkSystemAccounts: %s".printf(e.message)); } } - + private void accountsChanged(Goa.Object object) { refreshAccounts(); diff --git a/src/StringUtils.vala b/src/StringUtils.vala index ce4ecfda..95b02b64 100644 --- a/src/StringUtils.vala +++ b/src/StringUtils.vala @@ -21,7 +21,7 @@ public class FeedReader.StringUtils { { return new Gee.ArrayList<string>.wrap(items); } - + var res = new Gee.ArrayList<string>(); foreach(string item in items) { @@ -29,16 +29,16 @@ public class FeedReader.StringUtils { { res.add(item); } - + } return res; } - + public static string join(Gee.Collection<string> l, string sep) { return string.joinv(sep, l.to_array()); } - + public static void stringbuilder_append_join(StringBuilder out, Gee.Collection<string> l, string sep) { bool first = true; @@ -52,7 +52,7 @@ public class FeedReader.StringUtils { first = false; } } - + public static Gee.List<string> sql_quote(Gee.List<string> l) ensures (result.size == l.size) { @@ -60,7 +60,7 @@ public class FeedReader.StringUtils { { l[i] = SQLite.quote_string(l[i]); } - + return l; } } diff --git a/src/Structs.vala b/src/Structs.vala index a7383c40..f0ab106a 100644 --- a/src/Structs.vala +++ b/src/Structs.vala @@ -14,7 +14,7 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. namespace FeedReader { - + public struct BackendInfo { string ID; string name; @@ -22,33 +22,33 @@ namespace FeedReader { string website; string iconName; } - + public struct Response { uint status; string data; Soup.MessageHeaders headers; - + public bool is_ok() { return status >= 200 && status < 400; } } - + public struct ResourceMetadata { private const string CACHE_GROUP = "cache"; private const string ETAG_KEY = "etag"; private const string LAST_MODIFIED_KEY = "last_modified"; private const string EXPIRES_KEY = "last_checked"; - + string? etag; string? last_modified; DateTime? expires; - + public ResourceMetadata() { } - + public ResourceMetadata.from_data(string data) { try @@ -61,7 +61,7 @@ namespace FeedReader { try { this.last_modified = config.get_string(CACHE_GROUP, LAST_MODIFIED_KEY); } catch (KeyFileError.KEY_NOT_FOUND e) {} catch (KeyFileError.GROUP_NOT_FOUND e) {} - + int64? expires = null; try { expires = config.get_int64(CACHE_GROUP, EXPIRES_KEY); } catch (KeyFileError.KEY_NOT_FOUND e) {} @@ -76,7 +76,7 @@ namespace FeedReader { Logger.warning(@"FaviconMetadata.from_file: Failed to load from $data"); } } - + public static async ResourceMetadata from_file_async(string filename) { try @@ -95,7 +95,7 @@ namespace FeedReader { } return ResourceMetadata(); } - + public async void save_to_file_async(string filename) { var file = File.new_for_path(filename); @@ -147,21 +147,21 @@ namespace FeedReader { } } } - + public bool is_expired() { if(expires == null) { return true; } - + if(expires.compare(new DateTime.now_utc()) == 1) { return false; } - + return true; } } - + } diff --git a/src/TestQueryBuilder.vala b/src/TestQueryBuilder.vala index 2aec8310..78d11062 100644 --- a/src/TestQueryBuilder.vala +++ b/src/TestQueryBuilder.vala @@ -3,54 +3,54 @@ using FeedReader; void main(string[] args) { Test.init(ref args); - + Test.add_data_func("/querybuilder/simple-select", () => { var query = new QueryBuilder(QueryType.SELECT, "example"); query.select_field("column1"); query.select_field("column2"); - + assert(query.to_string() == "SELECT column1, column2 FROM example"); }); - + Test.add_data_func("/querybuilder/simple-insert", () => { var query = new QueryBuilder(QueryType.INSERT, "example"); query.insert_param("asdf", "$VALUE"); query.insert_int("othercol", 5); - + assert(query.to_string() == "INSERT INTO example (asdf, othercol) VALUES ($VALUE, 5)"); }); - + Test.add_data_func("/querybuilder/simple-insert-or-ignore", () => { var query = new QueryBuilder(QueryType.INSERT_OR_IGNORE, "example"); query.insert_param("asdf", "$VALUE"); query.insert_int("othercol", 5); - + assert(query.to_string() == "INSERT OR IGNORE INTO example (asdf, othercol) VALUES ($VALUE, 5)"); }); - + Test.add_data_func("/querybuilder/simple-insert-or-replace", () => { var query = new QueryBuilder(QueryType.INSERT_OR_REPLACE, "example"); query.insert_param("asdf", "$VALUE"); query.insert_int("othercol", 5); - + assert(query.to_string() == "INSERT OR REPLACE INTO example (asdf, othercol) VALUES ($VALUE, 5)"); }); - + Test.add_data_func("/querybuilder/simple-update", () => { var query = new QueryBuilder(QueryType.UPDATE, "example"); query.update_int("asdf", 5); query.update_string("othercol", "asd'"); query.update_param("test", "$TEST"); - + assert(query.to_string() == "UPDATE example SET asdf = 5, othercol = 'asd''', test = $TEST"); }); - + Test.add_data_func("/querybuilder/simple-delete", () => { var query = new QueryBuilder(QueryType.DELETE, "example"); - + assert(query.to_string() == "DELETE FROM example"); }); - + Test.add_data_func("/querybuilder/complex-select", () => { var query = new QueryBuilder(QueryType.SELECT, "test"); query.select_field("column1"); @@ -68,7 +68,7 @@ void main(string[] args) })); query.limit(100); query.offset(5); - + assert(query.to_string() == "SELECT column1, column2, column3 " + "FROM test " + "WHERE column3 = '\"something''' " + @@ -80,6 +80,6 @@ void main(string[] args) "LIMIT 100 " + "OFFSET 5"); }); - + Test.run(); } diff --git a/src/TestStringUtils.vala b/src/TestStringUtils.vala index 6784802f..648bc457 100644 --- a/src/TestStringUtils.vala +++ b/src/TestStringUtils.vala @@ -3,61 +3,61 @@ using FeedReader; void main(string[] args) { Test.init(ref args); - + Test.add_data_func("/stringutils/join", () => { var inputs = new Gee.ArrayList<string>(); inputs.add("one"); inputs.add("two"); - + var output = StringUtils.join(inputs, ","); assert(output == "one,two"); - + output = StringUtils.join(inputs, " "); assert(output == "one two"); }); - + Test.add_data_func("/stringutils/split", () => { var output = StringUtils.split("", ","); assert(output.is_empty); - + output = StringUtils.split(" ", ","); assert(output.size == 1); assert(output[0] == " "); - + output = StringUtils.split(" ", " "); assert(output.size == 2); assert(output[0] == ""); assert(output[1] == ""); - + output = StringUtils.split(" ", " ", true); assert(output.size == 0); - + output = StringUtils.split("$one#$t#wo#$", "#$"); assert(output.size == 3); assert(output[0] == "$one"); assert(output[1] == "t#wo"); assert(output[2] == ""); - + output = StringUtils.split("$one#$t#wo#$", "#$", true); assert(output.size == 2); assert(output[0] == "$one"); assert(output[1] == "t#wo"); }); - + Test.add_data_func("/stringutils/sqlquote", () => { var inputs = new Gee.ArrayList<string>(); assert(StringUtils.sql_quote(inputs).size == 0); - + inputs.add("one"); inputs.add("t'wo"); inputs.add("''"); - + var output = StringUtils.sql_quote(inputs); assert(output.size == inputs.size); assert(output[0] == "'one'"); assert(output[1] == "'t''wo'"); assert(output[2] == "''''''"); }); - + Test.run(); } diff --git a/src/Utils.vala b/src/Utils.vala index 02e91837..92d9e458 100644 --- a/src/Utils.vala +++ b/src/Utils.vala @@ -14,9 +14,9 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.Utils : GLib.Object { - + private static Soup.Session? m_session; - + public static Soup.Session getSession() { if(m_session == null) @@ -26,10 +26,10 @@ public class FeedReader.Utils : GLib.Object { m_session.ssl_strict = false; m_session.timeout = 5; } - + return m_session; } - + public static void generatePreviews(Gee.List<Article> articles) { string noPreview = _("No Preview Available"); @@ -54,26 +54,26 @@ public class FeedReader.Utils : GLib.Object { { output = output.strip(); } - + if(output == "" || output == null) { Logger.info("generatePreviews: no Preview"); Article.setPreview(noPreview); continue; } - + string xml = "<?xml"; - + while(output.has_prefix(xml)) { int end = output.index_of_char('>'); output = output.slice(end+1, output.length).chug(); output = output.strip(); } - + output = output.replace("\n"," "); output = output.replace("_"," "); - + Article.setPreview(output.chug()); } else @@ -85,7 +85,7 @@ public class FeedReader.Utils : GLib.Object { } } } - + public static void checkHTML(Gee.List<Article> articles) { var db = DataBase.readOnly(); @@ -102,7 +102,7 @@ public class FeedReader.Utils : GLib.Object { } } } - + public static string UTF8fix(string? old_string, bool remove_html = false) { if(old_string == null) @@ -110,7 +110,7 @@ public class FeedReader.Utils : GLib.Object { Logger.warning("Utils.UTF8fix: string is NULL"); return "NULL"; } - + string output = old_string; if (remove_html) { @@ -121,12 +121,12 @@ public class FeedReader.Utils : GLib.Object { output = output.make_valid().replace("\n"," ").strip(); return output; } - + public static string[] getDefaultExpandedCategories() { return {CategoryID.MASTER.to_string(), CategoryID.TAGS.to_string()}; } - + /*public static GLib.DateTime convertStringToDate(string date) { return new GLib.DateTime( @@ -139,42 +139,42 @@ public class FeedReader.Utils : GLib.Object { int.parse(date.substring(date.index_of_nth_char(17), date.index_of_nth_char(19) - date.index_of_nth_char(17))) // sec ); }*/ - + public static bool springCleaningNecessary() { var lastClean = new DateTime.from_unix_local(Settings.state().get_int("last-spring-cleaning")); var now = new DateTime.now_local(); - + var difference = now.difference(lastClean); bool doCleaning = false; - + Logger.debug("last clean: %s".printf(lastClean.format("%Y-%m-%d %H:%M:%S"))); Logger.debug("now: %s".printf(now.format("%Y-%m-%d %H:%M:%S"))); Logger.debug("difference: %f".printf(difference/GLib.TimeSpan.DAY)); - + if((difference/GLib.TimeSpan.DAY) >= Settings.general().get_int("spring-clean-after")) { doCleaning = true; } - + return doCleaning; } - + // thanks to // http://kuikie.com/snippet/79-8/vala/strings/vala-generate-random-string/%7B$ROOT_URL%7D/terms/ public static string string_random(int length = 8, string charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890") { string random = ""; - + for(int i=0; i<length; i++) { int random_index = Random.int_range(0,charset.length); string ch = charset.get_char(charset.index_of_nth_char(random_index)).to_string(); random += ch; } - + return random; } - + public static bool arrayContains(string[] array, string key) { foreach(string s in array) @@ -184,16 +184,16 @@ public class FeedReader.Utils : GLib.Object { return true; } } - + return false; } - + public static void copyAutostart() { string desktop = "org.gnome.FeedReader-autostart.desktop"; string filename = GLib.Environment.get_user_data_dir() + "/" + desktop; - - + + if(Settings.tweaks().get_boolean("feedreader-autostart") && !FileUtils.test(filename, GLib.FileTest.EXISTS)) { try @@ -208,117 +208,117 @@ public class FeedReader.Utils : GLib.Object { } } } - + public static string printTlsCertificateFlags(GLib.TlsCertificateFlags flag) { string errors = ""; int flags = flag; - + if(flags - GLib.TlsCertificateFlags.GENERIC_ERROR >= 0) { errors += "GENERIC_ERROR "; flags -= GLib.TlsCertificateFlags.VALIDATE_ALL; } - + if(flags - GLib.TlsCertificateFlags.INSECURE >= 0) { errors += "INSECURE "; flags -= GLib.TlsCertificateFlags.INSECURE; } - + if(flags - GLib.TlsCertificateFlags.REVOKED >= 0) { errors += "REVOKED "; flags -= GLib.TlsCertificateFlags.REVOKED; } - + if(flags - GLib.TlsCertificateFlags.EXPIRED >= 0) { errors += "EXPIRED "; flags -= GLib.TlsCertificateFlags.EXPIRED; } - + if(flags - GLib.TlsCertificateFlags.NOT_ACTIVATED >= 0) { errors += "NOT_ACTIVATED "; flags -= GLib.TlsCertificateFlags.NOT_ACTIVATED; } - + if(flags - GLib.TlsCertificateFlags.BAD_IDENTITY >= 0) { errors += "BAD_IDENTITY "; flags -= GLib.TlsCertificateFlags.BAD_IDENTITY; } - + if(flags - GLib.TlsCertificateFlags.UNKNOWN_CA >= 0) { errors += "UNKNOWN_CA "; flags -= GLib.TlsCertificateFlags.UNKNOWN_CA; } - + return errors; } - + public static bool ping(string link) { Logger.debug("Ping: " + link); var uri = new Soup.URI(link); - + if(uri == null) { Logger.error(@"Ping failed: can't parse url $link! Seems to be not valid."); return false; } - + var message = new Soup.Message.from_uri("HEAD", uri); - + if(message == null) { Logger.error(@"Ping failed: can't send message to $link! Seems to be not valid."); return false; } - + var status = getSession().send_message(message); - + Logger.debug(@"Ping: status $status"); - + if(status >= 200 && status <= 208) { Logger.debug("Ping successful"); return true; } - + Logger.error(@"Ping: failed %u - %s".printf(status, Soup.Status.get_phrase(status))); - + return false; } - - + + public static bool remove_directory(string path, uint level = 0) { ++level; bool flag = false; - + try { var directory = GLib.File.new_for_path(path); - + var enumerator = directory.enumerate_children(GLib.FileAttribute.STANDARD_NAME, 0); - + GLib.FileInfo file_info; while((file_info = enumerator.next_file()) != null) { string file_name = file_info.get_name(); - + if((file_info.get_file_type()) == GLib.FileType.DIRECTORY) { remove_directory(path + file_name + "/", level); } - + var file = directory.get_child(file_name); file.delete(); } - + if(level == 1) { directory.delete(); @@ -331,12 +331,12 @@ public class FeedReader.Utils : GLib.Object { { Logger.error("Utils - remove_directory: " + e.message); } - - + + return flag; } - - + + public static string shortenURL(string url) { string longURL = url; @@ -348,20 +348,20 @@ public class FeedReader.Utils : GLib.Object { { longURL = longURL.substring(7); } - + if(longURL.has_prefix("www.")) { longURL = longURL.substring(4); } - + if(longURL.has_suffix("api/")) { longURL = longURL.substring(0, longURL.length - 4); } - + return longURL; } - + // thx to geary :) public static string prepareSearchQuery(string raw_query) { @@ -390,14 +390,14 @@ StringBuilder prepared_query = new StringBuilder(); foreach(string s in words) { s = s.strip(); - + int quotes = countChar(s, '"'); if(!in_quote && quotes > 0) { in_quote = true; --quotes; } - + if(!in_quote) { string lower = s.down(); @@ -405,25 +405,25 @@ foreach(string s in words) { continue; } - + if(s.has_prefix("-")) { s = s.substring(1); } - + if(s == "") { continue; } - + s = "\"" + s + "*\""; } - + if(in_quote && quotes % 2 != 0) { in_quote = false; } - + prepared_query.append(s); prepared_query.append(" "); } @@ -677,15 +677,15 @@ switch(Settings.general().get_enum("article-theme")) case ArticleTheme.DEFAULT: theme += "default"; break; - + case ArticleTheme.SPRING: theme += "spring"; break; - + case ArticleTheme.MIDNIGHT: theme += "midnight"; break; - + case ArticleTheme.PARCHMENT: theme += "parchment"; break; @@ -876,18 +876,18 @@ try { articleName = DataBase.readOnly().read_article(articleID).getTitle(); } - + var file = GLib.File.new_for_path(imagePath); var mimeType = file.query_info("standard::content-type", 0, null).get_content_type(); var filter = new Gtk.FileFilter(); filter.add_mime_type(mimeType); - + var map = new Gee.HashMap<string, string>(); map.set("image/gif", ".gif"); map.set("image/jpeg", ".jpeg"); map.set("image/png", ".png"); map.set("image/x-icon", ".ico"); - + var save_dialog = new Gtk.FileChooserDialog("Save Image", MainWindow.get_default(), Gtk.FileChooserAction.SAVE, @@ -917,7 +917,7 @@ try Logger.debug("imagePopup: save file: " + e.message); } break; - + case Gtk.ResponseType.CANCEL: default: break; @@ -1006,11 +1006,11 @@ switch(selectedRow[0]) case "feed": IDtype = FeedListType.FEED; break; - + case "cat": IDtype = FeedListType.CATEGORY; break; - + case "tag": IDtype = FeedListType.TAG; break; diff --git a/src/Widgets/AddPopover.vala b/src/Widgets/AddPopover.vala index 505c9f2a..df751582 100644 --- a/src/Widgets/AddPopover.vala +++ b/src/Widgets/AddPopover.vala @@ -14,7 +14,7 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.AddPopover : Gtk.Popover { - + private Gtk.Stack m_stack; private Gtk.Box m_box; private Gtk.Grid m_feedGrid; @@ -24,12 +24,12 @@ public class FeedReader.AddPopover : Gtk.Popover { private Gtk.FileChooserButton m_chooser; private Gtk.EntryCompletion m_complete; private Gee.List<Category> m_cats; - + public AddPopover(Gtk.Widget parent) { this.relative_to = parent; this.position = Gtk.PositionType.TOP; - + m_urlEntry = new Gtk.Entry(); m_urlEntry.activate.connect(() => { m_catEntry.grab_focus(); @@ -52,7 +52,7 @@ public class FeedReader.AddPopover : Gtk.Popover { addButton.get_style_context().add_class("suggested-action"); addButton.halign = Gtk.Align.END; addButton.clicked.connect(addFeed); - + m_feedGrid = new Gtk.Grid(); m_feedGrid.row_spacing = 5; m_feedGrid.column_spacing = 8; @@ -61,7 +61,7 @@ public class FeedReader.AddPopover : Gtk.Popover { m_feedGrid.attach(catLabel, 0, 1, 1, 1); m_feedGrid.attach(m_catEntry, 1, 1, 1, 1); m_feedGrid.attach(addButton, 0, 2, 2, 1); - + var opmlLabel = new Gtk.Label(_("OPML File:")); opmlLabel.expand = true; m_chooser = new Gtk.FileChooserButton(_("Select OPML File"), Gtk.FileChooserAction.OPEN); @@ -69,47 +69,47 @@ public class FeedReader.AddPopover : Gtk.Popover { filter.add_mime_type("text/x-opml"); m_chooser.set_filter(filter); m_chooser.expand = true; - + var importButton = new Gtk.Button.with_label(_("Import")); importButton.get_style_context().add_class("suggested-action"); importButton.halign = Gtk.Align.END; importButton.clicked.connect(importOPML); importButton.sensitive = false; - + m_chooser.file_set.connect(() => { importButton.sensitive = true; }); - + m_opmlGrid = new Gtk.Grid(); m_opmlGrid.row_spacing = 10; m_opmlGrid.column_spacing = 8; m_opmlGrid.attach(opmlLabel, 0, 0, 1, 1); m_opmlGrid.attach(m_chooser, 1, 0, 1, 1); m_opmlGrid.attach(importButton, 0, 1, 2, 1); - - + + m_stack = new Gtk.Stack(); m_stack.add_titled(m_feedGrid, "feeds", _("Add feed")); m_stack.add_titled(m_opmlGrid, "opml", _("Import OPML")); - + var switcher = new Gtk.StackSwitcher(); switcher.halign = Gtk.Align.CENTER; switcher.set_stack(m_stack); - + m_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 10); m_box.margin = 10; m_box.pack_start(switcher); m_box.pack_start(m_stack); - + this.add(m_box); this.show_all(); m_urlEntry.grab_focus(); - + GLib.Idle.add(() => { Gtk.ListStore list_store = new Gtk.ListStore(1, typeof (string)); Gtk.TreeIter iter; m_cats = DataBase.readOnly().read_categories(); - + foreach(var cat in m_cats) { list_store.append(out iter); @@ -122,7 +122,7 @@ public class FeedReader.AddPopover : Gtk.Popover { return false; }, GLib.Priority.HIGH_IDLE); } - + private void addFeed() { string url = m_urlEntry.text; @@ -131,28 +131,28 @@ public class FeedReader.AddPopover : Gtk.Popover { m_urlEntry.grab_focus(); return; } - + string? catID = DataBase.readOnly().getCategoryID(m_catEntry.text); bool isID = true; - + if(catID == null) { catID = m_catEntry.text; isID = false; } - - + + if (GLib.Uri.parse_scheme(url) == null) { url = "http://" + url; } - + Logger.debug("addFeed: %s, %s".printf(url, (catID == "") ? "null" : catID)); FeedReaderBackend.get_default().addFeed(url, catID, isID); - + setBusy(); } - + private void importOPML() { try @@ -170,7 +170,7 @@ public class FeedReader.AddPopover : Gtk.Popover { } setBusy(); } - + private void setBusy() { ColumnView.get_default().footerSetBusy(); diff --git a/src/Widgets/ArticleList/ArticleList.vala b/src/Widgets/ArticleList/ArticleList.vala index 81f0bd3c..e4db6369 100644 --- a/src/Widgets/ArticleList/ArticleList.vala +++ b/src/Widgets/ArticleList/ArticleList.vala @@ -15,7 +15,7 @@ public class FeedReader.ArticleList : Gtk.Overlay { - + private Gtk.Stack m_stack; private ArticleListEmptyLabel m_emptyList; private FeedListType m_selectedFeedListType = FeedListType.FEED; @@ -37,9 +37,9 @@ public class FeedReader.ArticleList : Gtk.Overlay { private ulong m_handlerID1 = 0; private ulong m_handlerID2 = 0; private ulong m_handlerID3 = 0; - + public signal void row_activated(ArticleRow? row); - + public ArticleList() { m_emptyList = new ArticleListEmptyLabel(); @@ -57,7 +57,7 @@ public class FeedReader.ArticleList : Gtk.Overlay { syncingBox.set_margin_right(30); syncingBox.pack_start(m_syncSpinner); syncingBox.pack_start(syncingLabel); - + m_scroll1 = new ArticleListScroll(); m_scroll2 = new ArticleListScroll(); m_scroll1.scrolledTop.connect(dismissOverlay); @@ -84,10 +84,10 @@ public class FeedReader.ArticleList : Gtk.Overlay { m_List2.drag_failed.connect((context, result) => {drag_failed(context, result); return false;}); m_scroll1.add(m_List1); m_scroll2.add(m_List2); - + m_currentList = m_List1; m_currentScroll = m_scroll1; - + m_stack = new Gtk.Stack(); m_stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE); m_stack.set_transition_duration(100); @@ -114,16 +114,16 @@ public class FeedReader.ArticleList : Gtk.Overlay { } }); } - + public void newList(Gtk.StackTransitionType transition = Gtk.StackTransitionType.CROSSFADE) { Logger.debug("ArticleList: newList"); - + if(m_overlay != null) { m_overlay.dismiss(); } - + Logger.debug("ArticleList: disallow signals from scroll"); m_currentScroll.allowSignals(false); Gee.List<Article> articles = new Gee.ArrayList<Article>(); @@ -131,7 +131,7 @@ public class FeedReader.ArticleList : Gtk.Overlay { int height = this.get_allocated_height(); uint limit = height / 100 + 5; offset = getListOffset(); - + Logger.debug("load articles from db"); articles = DataBase.readOnly().read_articles(m_selectedFeedListID, m_selectedFeedListType, @@ -140,7 +140,7 @@ public class FeedReader.ArticleList : Gtk.Overlay { limit, offset); Logger.debug("actual articles loaded: " + articles.size.to_string()); - + if(articles.size == 0) { m_currentList.emptyList(); @@ -163,7 +163,7 @@ public class FeedReader.ArticleList : Gtk.Overlay { m_currentList.disconnect(m_handlerID1); m_handlerID1 = 0; } - + // switch up lists if(m_currentList == m_List1) { @@ -179,27 +179,27 @@ public class FeedReader.ArticleList : Gtk.Overlay { m_currentScroll = m_scroll1; m_stack.set_visible_child_full("list1", transition); } - + m_currentScroll.scrollToPos(0, false); - + // restore the previous selected row m_handlerID1 = m_currentList.loadDone.connect(() => { restoreSelectedRow(); restoreScrollPos(); Logger.debug("ArticleList: allow signals from scroll"); m_currentScroll.allowSignals(true); - + if(m_handlerID1 != 0) { m_currentList.disconnect(m_handlerID1); m_handlerID1 = 0; } }); - + m_currentList.newList(articles); } } - + private void checkForNewRows() { Logger.debug("ArticleList: checkForNewRows"); @@ -211,19 +211,19 @@ public class FeedReader.ArticleList : Gtk.Overlay { loadNewer(count, offset); } } - + private void loadMore() { if(m_currentList == null) { return; } - + Logger.debug("ArticleList.loadmore()"); - + Logger.debug("load articles from db"); uint offset = m_currentList.getSizeForState() + determineNewRowCount(null, null); - + var articles = DataBase.readOnly().read_articles(m_selectedFeedListID, m_selectedFeedListType, m_state, @@ -231,7 +231,7 @@ public class FeedReader.ArticleList : Gtk.Overlay { m_dynamicRowThreshold, offset); Logger.debug("actual articles loaded: " + articles.size.to_string()); - + if(articles.size > 0) { m_currentScroll.valueChanged.disconnect(updateVisibleRows); @@ -239,7 +239,7 @@ public class FeedReader.ArticleList : Gtk.Overlay { m_handlerID2 = m_currentList.loadDone.connect(() => { m_currentScroll.startScrolledDownCooldown(); m_currentScroll.valueChanged.connect(updateVisibleRows); - + if(m_handlerID2 != 0) { m_currentList.disconnect(m_handlerID2); @@ -252,11 +252,11 @@ public class FeedReader.ArticleList : Gtk.Overlay { m_currentScroll.startScrolledDownCooldown(); } } - + private void loadNewer(int newCount, int offset) { Logger.debug(@"ArticleList: loadNewer($newCount)"); - + Logger.debug("load articles from db"); var articles = DataBase.readOnly().read_articles(m_selectedFeedListID, m_selectedFeedListType, @@ -265,7 +265,7 @@ public class FeedReader.ArticleList : Gtk.Overlay { newCount, offset); Logger.debug("actual articles loaded: " + articles.size.to_string()); - + if(articles.size > 0) { if(m_stack.get_visible_child_name() == "empty") @@ -279,12 +279,12 @@ public class FeedReader.ArticleList : Gtk.Overlay { m_stack.set_visible_child_full("list2", Gtk.StackTransitionType.CROSSFADE); } } - + m_currentScroll.valueChanged.disconnect(updateVisibleRows); m_currentList.addTop(articles); m_handlerID3 = m_currentList.loadDone.connect(() => { m_currentScroll.valueChanged.connect(updateVisibleRows); - + if(m_handlerID3 != 0) { m_currentList.disconnect(m_handlerID3); @@ -296,13 +296,13 @@ public class FeedReader.ArticleList : Gtk.Overlay { { m_stack.set_visible_child_full("empty", Gtk.StackTransitionType.CROSSFADE); } - + } - + public void updateArticleList() { Logger.debug(@"ArticleList: updateArticleList()"); - + if(m_stack.get_visible_child_name() == "empty" || m_stack.get_visible_child_name() == "syncing") { @@ -310,11 +310,11 @@ public class FeedReader.ArticleList : Gtk.Overlay { newList(Gtk.StackTransitionType.CROSSFADE); return; } - + m_currentList.setAllUpdated(false); var articles = DataBase.readOnly().read_article_stats(m_currentList.getIDs()); var children = m_currentList.get_children(); - + foreach(var row in children) { var tmpRow = row as ArticleRow; @@ -326,21 +326,21 @@ public class FeedReader.ArticleList : Gtk.Overlay { tmpRow.setUpdated(true); } } - + m_currentList.removeObsoleteRows(); int length = (int)m_currentList.get_children().length(); - + for(int i = 1; i < length; i++) { ArticleRow? first = m_currentList.get_row_at_index(i-1) as ArticleRow; ArticleRow? second = m_currentList.get_row_at_index(i) as ArticleRow; - + if(first == null || second == null) { continue; } - + var insertArticles = DataBase.readOnly().read_article_between( m_selectedFeedListID, m_selectedFeedListType, m_state, @@ -349,7 +349,7 @@ public class FeedReader.ArticleList : Gtk.Overlay { first.getDate(), second.getID(), second.getDate()); - + foreach(Article a in insertArticles) { if(m_currentList.insertArticle(a, i)) @@ -359,15 +359,15 @@ public class FeedReader.ArticleList : Gtk.Overlay { } } } - + checkForNewRows(); } - + private int determineNewRowCount(int? newCount, out int? offset) { int count = 0; ArticleRow? firstRow = m_currentList.getFirstRow(); - + if(firstRow != null) { count = DataBase.readOnly().getArticleCountNewerThanID( @@ -377,7 +377,7 @@ public class FeedReader.ArticleList : Gtk.Overlay { m_state, m_searchTerm); } - + if(newCount != null && newCount < count) { offset = count - newCount; @@ -387,10 +387,10 @@ public class FeedReader.ArticleList : Gtk.Overlay { { offset = 0; } - + return count; } - + private void removeInvisibleRows(ScrollDirection direction) { if(m_scrollChangedTimeout != 0) @@ -398,7 +398,7 @@ public class FeedReader.ArticleList : Gtk.Overlay { GLib.Source.remove(m_scrollChangedTimeout); m_scrollChangedTimeout = 0; } - + // remove lower ArticleRows only after scrolling up if(direction == ScrollDirection.UP) { @@ -406,7 +406,7 @@ public class FeedReader.ArticleList : Gtk.Overlay { Logger.debug("ArticleList: remove invisible rows below"); var children = m_currentList.get_children(); children.reverse(); - + foreach(var r in children) { var row = r as ArticleRow; @@ -427,7 +427,7 @@ public class FeedReader.ArticleList : Gtk.Overlay { }); } } - + private void updateVisibleRows(ScrollDirection direction) { if(direction == ScrollDirection.DOWN && Settings.general().get_boolean("articlelist-mark-scrolling")) @@ -435,7 +435,7 @@ public class FeedReader.ArticleList : Gtk.Overlay { var children = m_currentList.get_children(); children.reverse(); var visibleArticles = new Gee.HashSet<string>(); - + foreach(var r in children) { var row = r as ArticleRow; @@ -454,10 +454,10 @@ public class FeedReader.ArticleList : Gtk.Overlay { } m_currentList.setVisibleRows(visibleArticles); } - + removeInvisibleRows(direction); } - + private bool keyPressed(Gdk.EventKey event) { switch(event.keyval) @@ -469,7 +469,7 @@ public class FeedReader.ArticleList : Gtk.Overlay { m_currentScroll.scrollDiff(diff); } break; - + case Gdk.Key.Up: int diff = m_currentList.move(false); if(m_state != ArticleListState.UNREAD) @@ -477,30 +477,30 @@ public class FeedReader.ArticleList : Gtk.Overlay { m_currentScroll.scrollDiff(diff); } break; - + case Gdk.Key.Page_Down: m_currentScroll.scrollToPos(-1); break; - + case Gdk.Key.Page_Up: m_currentScroll.scrollToPos(0); break; } return true; } - + public int move(bool down) { int diff = m_currentList.move(down); - + if(m_state != ArticleListState.UNREAD) { m_currentScroll.scrollDiff(diff); } - + return diff; } - + public void showOverlay() { Logger.debug("ArticleList: showOverlay"); @@ -509,7 +509,7 @@ public class FeedReader.ArticleList : Gtk.Overlay { showNotification(); } } - + private void showNotification() { if(m_overlay != null @@ -517,7 +517,7 @@ public class FeedReader.ArticleList : Gtk.Overlay { { return; } - + m_overlay = new InAppNotification.withIcon( _("New articles"), "feed-arrow-up-symbolic", @@ -531,7 +531,7 @@ public class FeedReader.ArticleList : Gtk.Overlay { this.add_overlay(m_overlay); this.show_all(); } - + public void dismissOverlay() { if(m_overlay != null) @@ -539,7 +539,7 @@ public class FeedReader.ArticleList : Gtk.Overlay { m_overlay.dismiss(); } } - + public Article? getSelectedArticle() { if(m_stack.get_visible_child_name() == "empty" @@ -547,10 +547,10 @@ public class FeedReader.ArticleList : Gtk.Overlay { { return null; } - + return m_currentList.getSelectedArticle(); } - + public Article? getFirstArticle() { ArticleRow? selectedRow = m_currentList.getFirstRow(); @@ -558,32 +558,32 @@ public class FeedReader.ArticleList : Gtk.Overlay { { return null; } - + return selectedRow.getArticle(); } - + public ArticleStatus toggleReadSelected() { return m_currentList.toggleReadSelected(); } - + public ArticleStatus toggleMarkedSelected() { return m_currentList.toggleMarkedSelected(); } - + public void getSavedState(out double scrollPos, out int rowOffset) { Logger.debug("ArticleList: get State"); - + // get current scroll position scrollPos = m_currentScroll.getScroll(); - + // the amount of rows that are above the the current viewport // and thus are not visible at the moment // they can be skipped on startup and lazy-loaded later rowOffset = 0; - + var children = m_currentList.get_children(); foreach(var row in children) { @@ -591,7 +591,7 @@ public class FeedReader.ArticleList : Gtk.Overlay { if(tmpRow != null) { var height = tmpRow.get_allocated_height(); - + if((scrollPos-height) >= 0) { scrollPos -= height; @@ -603,33 +603,33 @@ public class FeedReader.ArticleList : Gtk.Overlay { } } } - + rowOffset += determineNewRowCount(null, null); Logger.debug("scrollpos %f".printf(scrollPos)); Logger.debug("offset %i".printf(rowOffset)); } - + private uint getListOffset() { uint offset = (uint)Settings.state().get_int("articlelist-row-offset"); Settings.state().set_int("articlelist-row-offset", 0); return offset; } - + private void restoreSelectedRow() { string selectedRow = Settings.state().get_string("articlelist-selected-row"); - + if(selectedRow != "") { m_currentList.selectRow(selectedRow, 300); } if(!m_currentList.has_id(selectedRow)) { - Settings.state().set_string("articlelist-selected-row", ""); + Settings.state().set_string("articlelist-selected-row", ""); } } - + private void restoreScrollPos() { var pos = Settings.state().get_double("articlelist-scrollpos"); @@ -640,27 +640,27 @@ public class FeedReader.ArticleList : Gtk.Overlay { Settings.state().set_double("articlelist-scrollpos", 0); } } - + public void removeTagFromSelectedRow(string tagID) { m_currentList.removeTagFromSelectedRow(tagID); } - + public string getSelectedURL() { return m_currentList.getSelectedURL(); } - + public bool selectedIsFirst() { return m_currentList.selectedIsFirst(); } - + public bool selectedIsLast() { return m_currentList.selectedIsLast(); } - + public Gdk.RGBA getBackgroundColor() { // code according to: https://blogs.gnome.org/mclasen/2015/11/20/a-gtk-update/ @@ -671,43 +671,43 @@ public class FeedReader.ArticleList : Gtk.Overlay { context.restore(); return color; } - + public void setSelectedFeed(string feedID) { m_selectedFeedListID = feedID; m_List1.setSelectedFeed(feedID); m_List2.setSelectedFeed(feedID); } - + public void setSelectedType(FeedListType type) { m_selectedFeedListType = type; m_List1.setSelectedType(type); m_List2.setSelectedType(type); } - + public void setState(ArticleListState state) { m_state = state; m_List1.setState(state); m_List2.setState(state); } - + public ArticleListState getState() { return m_state; } - + public void setSearchTerm(string searchTerm) { m_searchTerm = searchTerm; } - + public void markAllAsRead() { m_currentList.markAllAsRead(); } - + public void openSelected() { Article? selectedArticle = m_currentList.getSelectedArticle(); @@ -723,14 +723,14 @@ public class FeedReader.ArticleList : Gtk.Overlay { } } } - + public void centerSelectedRow() { int scroll = -(int)(m_currentScroll.getPageSize()/2); scroll += m_currentList.selectedRowPosition(); m_currentScroll.scrollToPos(scroll); } - + public void syncStarted() { m_syncing = true; @@ -740,7 +740,7 @@ public class FeedReader.ArticleList : Gtk.Overlay { m_syncSpinner.start(); } } - + public void syncFinished() { m_syncing = false; @@ -749,7 +749,7 @@ public class FeedReader.ArticleList : Gtk.Overlay { m_stack.set_visible_child_full("empty", Gtk.StackTransitionType.CROSSFADE); } } - + private void rowActivated(Gtk.ListBoxRow row) { var article_row = row as ArticleRow; @@ -757,7 +757,7 @@ public class FeedReader.ArticleList : Gtk.Overlay { Settings.state().set_string("articlelist-selected-row", article_row.getID()); row_activated(article_row); } - + public void clear() { m_currentList.emptyList(); diff --git a/src/Widgets/ArticleList/ArticleListBox.vala b/src/Widgets/ArticleList/ArticleListBox.vala index f20a1108..d47dc9d4 100644 --- a/src/Widgets/ArticleList/ArticleListBox.vala +++ b/src/Widgets/ArticleList/ArticleListBox.vala @@ -14,7 +14,7 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.ArticleListBox : Gtk.ListBox { - + private Gee.List<Article> m_lazyQeue; private uint m_idleID = 0; private string m_name; @@ -25,10 +25,10 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { private string m_selectedArticle = ""; private Gee.HashMap<string, ArticleRow> m_articles; private Gee.HashSet<string> m_visibleArticles; - + public signal void balanceNextScroll(ArticleListBalance mode); public signal void loadDone(); - + public ArticleListBox(string name) { m_name = name; @@ -38,7 +38,7 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { this.set_selection_mode(Gtk.SelectionMode.BROWSE); this.row_activated.connect(rowActivated); } - + public void newList(Gee.List<Article> articles) { stopLoading(); @@ -47,7 +47,7 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { m_lazyQeue = articles; addRow(ArticleListBalance.NONE); } - + public void addTop(Gee.List<Article> articles) { stopLoading(); @@ -55,7 +55,7 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { m_lazyQeue = articles; addRow(ArticleListBalance.TOP, true); } - + public void addBottom(Gee.List<Article> articles) { stopLoading(); @@ -63,7 +63,7 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { m_lazyQeue = articles; addRow(ArticleListBalance.NONE); } - + private bool stopLoading() { if(m_idleID > 0) @@ -72,10 +72,10 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { m_idleID = 0; return true; } - + return false; } - + private void setPos(Gee.List<Article> articles, int pos) { foreach(Article a in articles) @@ -83,7 +83,7 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { a.setPos(pos); } } - + private void addRow(ArticleListBalance balance, bool reverse = false, bool animate = false) { if(m_lazyQeue.size == 0) @@ -91,23 +91,23 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { Logger.debug(@"ArticleListbox$m_name: lazyQueu == 0 -> return"); return; } - - + + var priority = GLib.Priority.DEFAULT_IDLE; if(ColumnView.get_default().playingMedia()) { priority = GLib.Priority.HIGH_IDLE; } - + m_idleID = GLib.Idle.add(() => { - + if(m_lazyQeue == null || m_lazyQeue.size == 0) { return false; } - + Article item; - + if(reverse) { item = m_lazyQeue.last(); @@ -116,7 +116,7 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { { item = m_lazyQeue.first(); } - + // check if row is already there if(m_articles.has_key(item.getArticleID())) { @@ -124,9 +124,9 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { checkQueue(item, balance, reverse, animate); return false; } - + balanceNextScroll(balance); - + var newRow = new ArticleRow(item); newRow.rowStateChanged.connect(rowStateChanged); newRow.drag_begin.connect((widget, context) => { @@ -141,15 +141,15 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { drag_failed(context, result); return false; }); - + newRow.realize.connect(() => { checkQueue(item, balance, reverse, animate); }); - + m_articles.set(item.getArticleID(), newRow); - + this.insert(newRow, item.getPos()); - + if(animate) { newRow.reveal(true, 150); @@ -158,11 +158,11 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { { newRow.reveal(true, 0); } - + return false; }, priority); } - + private void checkQueue(Article item, ArticleListBalance balance, bool reverse = false, bool animate = false) { if(m_lazyQeue.size > 1) @@ -182,7 +182,7 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { m_idleID = 0; } } - + public void emptyList() { var children = get_children(); @@ -193,28 +193,28 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { } m_articles.clear(); } - + public void setSelectedFeed(string feedID) { m_selectedFeedListID = feedID; } - + public void setSelectedType(FeedListType type) { m_selectedFeedListType = type; } - + private void selectAfter(ArticleRow row, int time) { this.select_row(row); setRead(row); - + if(m_selectSourceID > 0) { GLib.Source.remove(m_selectSourceID); m_selectSourceID = 0; } - + m_selectSourceID = Timeout.add(time, () => { if(!ColumnView.get_default().searchFocused()) { @@ -224,7 +224,7 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { return false; }); } - + private void setRead(ArticleRow row) { if(row.getArticle().getUnread() == ArticleStatus.UNREAD) @@ -233,36 +233,36 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { FeedReaderBackend.get_default().updateArticleRead(row.getArticle()); } } - + public ArticleStatus toggleReadSelected() { ArticleRow selectedRow = this.get_selected_row() as ArticleRow; - + if(selectedRow == null) { return ArticleStatus.READ; } - + return selectedRow.toggleUnread(); } - + public ArticleStatus toggleMarkedSelected() { ArticleRow selectedRow = this.get_selected_row() as ArticleRow; - + if(selectedRow == null) { return ArticleStatus.UNMARKED; } - + return selectedRow.toggleMarked(); } - + public void setState(ArticleListState state) { m_state = state; } - + public Article? getSelectedArticle() { ArticleRow selectedRow = this.get_selected_row() as ArticleRow; @@ -270,10 +270,10 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { { return selectedRow.getArticle(); } - + return null; } - + public string getSelectedURL() { ArticleRow selectedRow = this.get_selected_row() as ArticleRow; @@ -281,15 +281,15 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { { return selectedRow.getURL(); } - + if(this.get_children().length() == 0) { return "empty"; } - + return ""; } - + public int move(bool down) { int time = 300; @@ -307,21 +307,21 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { return 0; } } - + var selectedRow = this.get_selected_row() as ArticleRow; var height = selectedRow.get_allocated_height(); ArticleRow nextRow = null; - + var rows = this.get_children(); - + if(!down) { rows.reverse(); } - + int current = rows.index(selectedRow); uint length = rows.length(); - + do { current++; @@ -329,23 +329,23 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { { return 0; } - + nextRow = rows.nth_data(current) as ArticleRow; } while(!nextRow.isBeingRevealed()); - + selectAfter(nextRow, time); - + Logger.debug(@"ArticleListBox.move: height: $height"); - + if(down) { return height; } - + return -height; } - + public void removeRow(ArticleRow row, int animateDuration = 700) { var id = row.getID(); @@ -359,13 +359,13 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { return false; }); } - + private void rowActivated(Gtk.ListBoxRow row) { var selectedRow = (ArticleRow)row; string selectedID = selectedRow.getID(); setRead(selectedRow); - + if(m_selectedArticle != selectedID) { if(m_state != ArticleListState.ALL || m_selectedFeedListType == FeedListType.TAG) @@ -389,10 +389,10 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { } } } - + m_selectedArticle = selectedID; } - + private void rowStateChanged(ArticleStatus status) { Logger.debug("state changed"); @@ -428,7 +428,7 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { break; } } - + public void setVisibleRows(Gee.HashSet<string> visibleArticles) { var invisibleRows = new Gee.HashSet<string>(); @@ -440,9 +440,9 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { } return true; }); - + m_visibleArticles = visibleArticles; - + var children = this.get_children(); foreach(var row in children) { @@ -458,64 +458,64 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { } } } - + public void removeTagFromSelectedRow(string tagID) { ArticleRow selectedRow = this.get_selected_row() as ArticleRow; - + if(selectedRow == null) { return; } - + selectedRow.removeTag(tagID); } - + public ArticleRow? getFirstRow() { var children = this.get_children(); - + if(children == null) { return null; } - + var firstRow = children.first().data as ArticleRow; - + if(firstRow == null) { return null; } - + return firstRow; } - + public ArticleRow? getLastRow() { var children = this.get_children(); - + if(children == null) { return null; } - + var lastRow = children.last().data as ArticleRow; - + if(lastRow == null) { return null; } - + return lastRow; } - + public bool selectedIsFirst() { var selectedRow = this.get_selected_row() as ArticleRow; var children = this.get_children(); int n = children.index(selectedRow); var lastRow = children.first().data as ArticleRow; - + if(n == 0) { return true; @@ -524,10 +524,10 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { { return true; } - + return false; } - + public bool selectedIsLast() { var selectedRow = this.get_selected_row() as ArticleRow; @@ -535,7 +535,7 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { int n = children.index(selectedRow); uint length = children.length(); var lastRow = children.last().data as ArticleRow; - + if(n + 1 == length) { return true; @@ -544,14 +544,14 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { { return true; } - + return false; } - + public void markAllAsRead() { var children = this.get_children(); - + foreach(var row in children) { var tmpRow = row as ArticleRow; @@ -561,17 +561,17 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { } } } - + public int selectedRowPosition() { ArticleRow selectedRow = this.get_selected_row() as ArticleRow; - + int scroll = 0; if(selectedRow == null) { return scroll; } - + var FeedChildList = this.get_children(); foreach(Gtk.Widget row in FeedChildList) { @@ -606,7 +606,7 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { selectAfter(m_articles.get(articleID), time); } } - + private void highlightRow(string articleID) { var children = this.get_children(); @@ -619,7 +619,7 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { } } } - + private void unHighlightRow() { var children = this.get_children(); @@ -632,12 +632,12 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { } } } - + public int getSize() { return m_articles.size; } - + public int getSizeForState() { if(m_state == ArticleListState.UNREAD) @@ -654,14 +654,14 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { } return unread; } - + return getSize(); } - + public bool needLoadMore(int height) { int rowHeight = 0; - + var children = this.get_children(); foreach(var row in children) { @@ -671,15 +671,15 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { rowHeight += tmpRow.get_allocated_height(); } } - + if(rowHeight < height + 100) { return true; } - + return false; } - + public Gee.List<string> getIDs() { var tmp = new Gee.LinkedList<string>(); @@ -689,7 +689,7 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { }); return tmp; } - + public void setAllUpdated(bool updated = false) { var children = this.get_children(); @@ -702,7 +702,7 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { } } } - + public void removeObsoleteRows() { var children = this.get_children(); @@ -715,7 +715,7 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { } } } - + public bool insertArticle(Article a, int pos) { if(m_articles.has_key(a.getArticleID())) @@ -723,7 +723,7 @@ public class FeedReader.ArticleListBox : Gtk.ListBox { Logger.debug(@"ArticleListbox$m_name: row with ID %s is already present".printf(a.getArticleID())); return false; } - + a.setPos(pos); stopLoading(); var list = new Gee.LinkedList<Article>(); diff --git a/src/Widgets/ArticleList/ArticleListEmptyLabel.vala b/src/Widgets/ArticleList/ArticleListEmptyLabel.vala index 5a3aab11..ddf18c74 100644 --- a/src/Widgets/ArticleList/ArticleListEmptyLabel.vala +++ b/src/Widgets/ArticleList/ArticleListEmptyLabel.vala @@ -14,7 +14,7 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.ArticleListEmptyLabel : Gtk.Label { - + public ArticleListEmptyLabel() { this.set_text(_("No Articles")); @@ -28,7 +28,7 @@ public class FeedReader.ArticleListEmptyLabel : Gtk.Label { this.set_justify(Gtk.Justification.CENTER); this.show_all(); } - + public void build(string selectedID, FeedListType type, ArticleListState state, string searchTerm) { string message = ""; @@ -184,10 +184,10 @@ public class FeedReader.ArticleListEmptyLabel : Gtk.Label { message = _("No articles"); } } - + } this.get_style_context().add_class("dim-label"); this.set_text(message); } - + } diff --git a/src/Widgets/ArticleList/ArticleListScroll.vala b/src/Widgets/ArticleList/ArticleListScroll.vala index fdfc647f..dbbd6be5 100644 --- a/src/Widgets/ArticleList/ArticleListScroll.vala +++ b/src/Widgets/ArticleList/ArticleListScroll.vala @@ -14,22 +14,22 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.ArticleListScroll : Gtk.ScrolledWindow { - + public signal void scrolledTop(); public signal void scrolledBottom(); public signal void valueChanged(ScrollDirection direction); - + private double m_upperCache = 0.0; private double m_valueCache = 0.0; private double m_valueThreshold = 50.0; private double m_bottomThreshold = 200.0; private ArticleListBalance m_balance = ArticleListBalance.NONE; - + private bool m_allowSignals = true; private bool m_scrolledTopOnCooldown = false; private bool m_scrolledBottomOnCooldown = false; private int m_scrollCooldown = 500; // cooldown in ms - + //Transition times private int64 m_startTime = 0; private int64 m_endTime = 0; @@ -39,16 +39,16 @@ public class FeedReader.ArticleListScroll : Gtk.ScrolledWindow { private uint m_scrollCallbackID = 0; private uint m_savetyFallbackID = 0; private uint m_scrollCooldownID = 0; - - - + + + public ArticleListScroll() { vadjustment.notify["upper"].connect(trackUpper); vadjustment.notify["value"].connect(trackValue); this.set_size_request(250, 0); } - + private void trackUpper() { double upper = vadjustment.upper; @@ -66,16 +66,16 @@ public class FeedReader.ArticleListScroll : Gtk.ScrolledWindow { this.vadjustment.value -= inc; m_balance = ArticleListBalance.NONE; } - + if(GLib.Math.fabs(vadjustment.upper - m_upperCache) > 2.0) { checkScrolledDown(); } - + m_upperCache = vadjustment.upper; m_valueCache = vadjustment.value; } - + private void trackValue() { if(vadjustment.value > (m_valueCache + m_valueThreshold)) @@ -86,14 +86,14 @@ public class FeedReader.ArticleListScroll : Gtk.ScrolledWindow { { valueChanged(ScrollDirection.UP); } - + checkScrolledTop(); checkScrolledDown(); - + m_upperCache = vadjustment.upper; m_valueCache = vadjustment.value; } - + private void checkScrolledTop() { if(m_allowSignals @@ -112,7 +112,7 @@ public class FeedReader.ArticleListScroll : Gtk.ScrolledWindow { }); } } - + private void checkScrolledDown() { double max = vadjustment.upper - vadjustment.page_size; @@ -132,7 +132,7 @@ public class FeedReader.ArticleListScroll : Gtk.ScrolledWindow { }); } } - + public void startScrolledDownCooldown() { if(m_scrollCooldownID != 0) @@ -140,7 +140,7 @@ public class FeedReader.ArticleListScroll : Gtk.ScrolledWindow { GLib.Source.remove(m_scrollCooldownID); m_scrollCooldownID = 0; } - + m_scrollCooldownID = GLib.Timeout.add(m_scrollCooldown, () => { Logger.debug("ArticleListScroll: scrolled down off cooldown"); m_scrollCooldownID = 0; @@ -156,22 +156,22 @@ public class FeedReader.ArticleListScroll : Gtk.ScrolledWindow { Logger.debug("ArticleListScroll: trigger scrolledBottom()"); scrolledBottom(); } - + return GLib.Source.REMOVE; }); } - + public void balanceNextScroll(ArticleListBalance mode) { m_balance = mode; } - + public void scrollDiff(double diff, bool animate = true) { Logger.debug("ArticleListScroll.scrollDiff: value: %f - diff: %f".printf(this.vadjustment.value, diff)); scrollToPos(this.vadjustment.value + diff, animate); } - + public void scrollToPos(double pos, bool animate = true) { if(!this.get_mapped()) @@ -179,13 +179,13 @@ public class FeedReader.ArticleListScroll : Gtk.ScrolledWindow { setScroll(pos); return; } - + if(Gtk.Settings.get_default().gtk_enable_animations && animate) { Logger.debug(@"ArticleListScroll.scrollToPos: $pos"); m_startTime = this.get_frame_clock().get_frame_time(); m_endTime = m_startTime + m_transitionDuration; - + double leftOverScroll = 0.0; if(m_scrollCallbackID != 0) { @@ -193,10 +193,10 @@ public class FeedReader.ArticleListScroll : Gtk.ScrolledWindow { this.remove_tick_callback(m_scrollCallbackID); m_scrollCallbackID = 0; } - + Logger.debug(@"ArticleListScroll.scrollToPos: leftOverScroll $leftOverScroll"); Logger.debug(@"ArticleListScroll.scrollToPos: %f".printf(pos+leftOverScroll)); - + if(pos == -1) { m_transitionDiff = (vadjustment.upper - vadjustment.page_size - vadjustment.value); @@ -205,7 +205,7 @@ public class FeedReader.ArticleListScroll : Gtk.ScrolledWindow { { m_transitionDiff = (pos-this.vadjustment.value)+leftOverScroll; } - + m_transitionStartValue = this.vadjustment.value; Logger.debug(@"ArticleListScroll.scrollDiff: startValue $m_transitionStartValue"); m_scrollCallbackID = this.add_tick_callback(scrollTick); @@ -215,12 +215,12 @@ public class FeedReader.ArticleListScroll : Gtk.ScrolledWindow { setScroll(pos); } } - + public double getScroll() { return this.vadjustment.value; } - + private void setScroll(double pos) { if(pos == -1) @@ -232,36 +232,36 @@ public class FeedReader.ArticleListScroll : Gtk.ScrolledWindow { this.vadjustment.value = pos; } } - + public double getPageSize() { return this.vadjustment.page_size; } - + public int isVisible(Gtk.ListBoxRow row, int additionalRows = 0) { var rowHeight = row.get_allocated_height(); var scrollHeight = this.get_allocated_height(); int x, y = 0; - + row.translate_coordinates(this, 0, 0, out x, out y); - + // row is (additionalRows * rowHeight) above the current viewport if(y < -( (1+additionalRows) * rowHeight)) { return -1; } - + // row is (additionalRows * rowHeight) below the current viewport if(y > additionalRows * rowHeight + scrollHeight) { return 1; } - + // row is visible return 0; } - + private bool scrollTick(Gtk.Widget widget, Gdk.FrameClock frame_clock) { if(!this.get_mapped()) @@ -269,19 +269,19 @@ public class FeedReader.ArticleListScroll : Gtk.ScrolledWindow { vadjustment.value = m_transitionStartValue + m_transitionDiff; return false; } - + int64 now = frame_clock.get_frame_time(); double t = 1.0; - + if(now < this.m_endTime) { t = (now - m_startTime) / (double)(m_endTime - m_startTime); } - + t = easeOutCubic(t); - + this.vadjustment.value = m_transitionStartValue + (t * m_transitionDiff); - + if(this.vadjustment.value <= 0 || now >= m_endTime) { this.queue_draw(); @@ -289,19 +289,19 @@ public class FeedReader.ArticleListScroll : Gtk.ScrolledWindow { m_scrollCallbackID = 0; return false; } - + return true; } - + inline double easeOutCubic(double t) { double p = t - 1; return p * p * p +1; } - + public void allowSignals(bool allow) { m_allowSignals = allow; } - + } diff --git a/src/Widgets/ArticleRow.vala b/src/Widgets/ArticleRow.vala index 94eee403..9b20a005 100644 --- a/src/Widgets/ArticleRow.vala +++ b/src/Widgets/ArticleRow.vala @@ -14,7 +14,7 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.ArticleRow : Gtk.ListBoxRow { - + private Article m_article; private Gtk.Label m_label; private Gtk.Image m_icon; @@ -30,21 +30,21 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { private bool m_hovering_row = false; private bool m_populated = false; public signal void rowStateChanged(ArticleStatus status); - + public ArticleRow(Article article) { m_article = article; - + m_revealer = new Gtk.Revealer(); m_revealer.set_transition_type(Gtk.RevealerTransitionType.SLIDE_DOWN); m_revealer.set_reveal_child(false); this.set_size_request(0, 100); this.add(m_revealer); this.show_all(); - + GLib.Idle.add(populate, GLib.Priority.HIGH_IDLE); } - + ~ArticleRow() { if(m_unread_eventbox != null) @@ -53,26 +53,26 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { m_unread_eventbox.leave_notify_event.disconnect(unreadIconLeave); m_unread_eventbox.button_press_event.disconnect(unreadIconClicked); } - + if(m_marked_eventbox != null) { m_marked_eventbox.enter_notify_event.disconnect(markedIconEnter); m_marked_eventbox.leave_notify_event.disconnect(markedIconLeave); m_marked_eventbox.button_press_event.disconnect(markedIconClicked); } - + if(m_eventBox != null) { m_eventBox.enter_notify_event.disconnect(rowEnter); m_eventBox.leave_notify_event.disconnect(rowLeave); m_eventBox.button_press_event.disconnect(rowClick); } - + this.drag_begin.disconnect(onDragBegin); this.drag_data_get.disconnect(onDragDataGet); this.drag_failed.disconnect(onDragFailed); } - + private bool populate() { m_unread_stack = new Gtk.Stack(); @@ -81,7 +81,7 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { m_marked_stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE); m_unread_stack.set_transition_duration(50); m_marked_stack.set_transition_duration(50); - + m_label = new Gtk.Label(m_article.getTitle()); m_label.set_line_wrap_mode(Pango.WrapMode.WORD); m_label.set_line_wrap(true); @@ -97,22 +97,22 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { m_label.set_ellipsize(Pango.EllipsizeMode.END); m_label.set_alignment(0.0f, 0.2f); m_label.set_tooltip_text(m_article.getTitle()); - + var icon_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); icon_box.set_size_request(24, 0); - + var marked_icon = new Gtk.Image.from_icon_name("feed-starred", Gtk.IconSize.SMALL_TOOLBAR); var unread_icon = new Gtk.Image.from_icon_name("feed-article-unread", Gtk.IconSize.SMALL_TOOLBAR); var unmarked_icon = new Gtk.Image.from_icon_name("feed-non-starred", Gtk.IconSize.SMALL_TOOLBAR); var read_icon = new Gtk.Image.from_icon_name("feed-article-read", Gtk.IconSize.SMALL_TOOLBAR); - + m_unread_stack.add_named(unread_icon, "unread"); m_unread_stack.add_named(read_icon, "read"); m_unread_stack.add_named(new Gtk.Label(""), "empty"); m_marked_stack.add_named(marked_icon, "marked"); m_marked_stack.add_named(unmarked_icon, "unmarked"); m_marked_stack.add_named(new Gtk.Label(""), "empty"); - + m_unread_eventbox = new Gtk.EventBox(); m_unread_eventbox.set_events(Gdk.EventMask.BUTTON_PRESS_MASK); m_unread_eventbox.set_events(Gdk.EventMask.ENTER_NOTIFY_MASK); @@ -132,12 +132,12 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { { Logger.warning("ArticleRow: id %s - unread status undefined %i".printf(m_article.getArticleID(), m_article.getUnread())); } - + m_unread_eventbox.enter_notify_event.connect(unreadIconEnter); m_unread_eventbox.leave_notify_event.connect(unreadIconLeave); m_unread_eventbox.button_press_event.connect(unreadIconClicked); - - + + m_marked_eventbox = new Gtk.EventBox(); m_marked_eventbox.set_events(Gdk.EventMask.BUTTON_PRESS_MASK); m_marked_eventbox.set_events(Gdk.EventMask.ENTER_NOTIFY_MASK); @@ -157,19 +157,19 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { { Logger.warning("ArticleRow: id %s - unread status undefined %i".printf(m_article.getArticleID(), m_article.getMarked())); } - + m_marked_eventbox.enter_notify_event.connect(markedIconEnter); m_marked_eventbox.leave_notify_event.connect(markedIconLeave); m_marked_eventbox.button_press_event.connect(markedIconClicked); - + m_icon = createFavIcon(); - + icon_box.pack_start(m_icon, true, true, 0); icon_box.pack_end(m_unread_eventbox, false, false, 10); icon_box.pack_end(m_marked_eventbox, false, false, 0); - + string short_preview = ""; - + if(m_article.getPreview() != "") { if(m_article.getPreview().length > 300) @@ -183,8 +183,8 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { short_preview = m_article.getPreview(); } } - - + + var body_label = new Gtk.Label(short_preview); body_label.opacity = 0.7; body_label.get_style_context().add_class("preview"); @@ -193,7 +193,7 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { body_label.set_line_wrap_mode(Pango.WrapMode.WORD_CHAR); body_label.set_line_wrap(true); body_label.set_lines(2); - + var feed = DataBase.readOnly().read_feed(m_article.getFeedID()); var feedLabel = new Gtk.Label(feed != null ? feed.getTitle() : ""); feedLabel.get_style_context().add_class("preview"); @@ -207,8 +207,8 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { var date_box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 10); date_box.pack_start(feedLabel, true, true, 0); date_box.pack_end(dateLabel, true, true, 0); - - + + var text_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); text_box.margin_end = 15; text_box.margin_top = 8; @@ -216,11 +216,11 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { text_box.pack_start(date_box, true, true, 0); text_box.pack_start(m_label, true, true, 0); text_box.pack_end(body_label, true, true, 0); - + var box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0); box.pack_start(icon_box, false, false, 8); box.pack_start(text_box, true, true, 0); - + m_eventBox = new Gtk.EventBox(); m_eventBox.set_events(Gdk.EventMask.ENTER_NOTIFY_MASK); m_eventBox.set_events(Gdk.EventMask.LEAVE_NOTIFY_MASK); @@ -230,7 +230,7 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { m_eventBox.button_press_event.connect(rowClick); m_eventBox.add(box); m_eventBox.show_all(); - + // Make the this widget a DnD source. if(!Settings.general().get_boolean("only-feeds") && FeedReaderBackend.get_default().isOnline() @@ -239,34 +239,34 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { const Gtk.TargetEntry[] provided_targets = { { "STRING", 0, DragTarget.TAG } }; - + Gtk.drag_source_set( this, Gdk.ModifierType.BUTTON1_MASK, provided_targets, Gdk.DragAction.COPY ); - + this.drag_begin.connect(onDragBegin); this.drag_data_get.connect(onDragDataGet); this.drag_failed.connect(onDragFailed); } - + m_revealer.add(m_eventBox); m_populated = true; return false; } - + private void onDragBegin(Gtk.Widget widget, Gdk.DragContext context) { Logger.debug("ArticleRow: onDragBegin"); Gtk.drag_set_icon_widget(context, getFeedIconWindow(), 0, 0); } - + public void onDragDataGet(Gtk.Widget widget, Gdk.DragContext context, Gtk.SelectionData selection_data, uint target_type, uint time) { Logger.debug("ArticleRow: onDragDataGet"); - + if(target_type == DragTarget.TAG) { selection_data.set_text(m_article.getArticleID(), -1); @@ -276,17 +276,17 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { selection_data.set_text("ERROR!!!!!1111eleven", -1); } } - + private bool onDragFailed(Gdk.DragContext context, Gtk.DragResult result) { Logger.debug("ArticleRow: onDragFailed " + result.to_string()); return false; } - + private Gtk.Image createFavIcon() { var icon = new Gtk.Image.from_icon_name("feed-rss-symbolic", Gtk.IconSize.LARGE_TOOLBAR); - + Feed? feed = DataBase.readOnly().read_feed(m_article.getFeedID()); var favicon = FavIcon.for_feed(feed); favicon.get_surface.begin((obj, res) => { @@ -302,10 +302,10 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { icon.destroy.connect(() => { favicon.disconnect(handler_id); }); - + return icon; } - + private Gtk.Window getFeedIconWindow() { var icon = createFavIcon(); @@ -317,17 +317,17 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { window.show_all(); return window; } - - + + private bool rowEnter(Gdk.EventCrossing event) { if(event.detail == Gdk.NotifyType.INFERIOR) { return true; } - + m_hovering_row = true; - + switch(m_article.getUnread()) { case ArticleStatus.READ: @@ -337,7 +337,7 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { m_unread_stack.set_visible_child_name("unread"); break; } - + switch(m_article.getMarked()) { case ArticleStatus.MARKED: @@ -347,19 +347,19 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { m_marked_stack.set_visible_child_name("unmarked"); break; } - + return true; } - + private bool rowLeave(Gdk.EventCrossing event) { if(event.detail == Gdk.NotifyType.INFERIOR) { return true; } - + m_hovering_row = false; - + switch(m_article.getUnread()) { case ArticleStatus.READ: @@ -369,7 +369,7 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { m_unread_stack.set_visible_child_name("unread"); break; } - + switch(m_article.getMarked()) { case ArticleStatus.MARKED: @@ -379,10 +379,10 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { m_marked_stack.set_visible_child_name("empty"); break; } - + return true; } - + private bool rowClick(Gdk.EventButton event) { switch (event.button) { @@ -392,7 +392,7 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { { return false; } - + try{ Gtk.show_uri_on_window(MainWindow.get_default(), m_article.getURL(), Gdk.CURRENT_TIME); } @@ -400,20 +400,20 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { Logger.debug("could not open the link in an external browser: %s".printf(e.message)); } break; - + //If right clicked, show context menu: case 3: this.onRightClick(); break; - + //Otherwise return false; default: return false; } - + return true; } - + private void onRightClick() { var pop = new Gtk.Popover(this); @@ -423,21 +423,21 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { }); copyArticleURL_action.set_enabled(true); FeedReaderApp.get_default().add_action(copyArticleURL_action); - + var menu = new GLib.Menu(); menu.append(_("Copy URL"), "copyArticleURL"); - + pop.set_position(Gtk.PositionType.BOTTOM); pop.bind_model(menu, "app"); pop.closed.connect(() => { this.unset_state_flags(Gtk.StateFlags.PRELIGHT); }); - + pop.show(); this.set_state_flags(Gtk.StateFlags.PRELIGHT, true); } - - + + private bool unreadIconClicked(Gdk.EventButton event) { switch(event.type) @@ -451,12 +451,12 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { rowStateChanged(m_article.getUnread()); return true; } - + public ArticleStatus toggleUnread() { var view = ColumnView.get_default(); Article? selectedArticle = ColumnView.get_default().getSelectedArticle(); - + switch(m_article.getUnread()) { case ArticleStatus.READ: @@ -466,17 +466,17 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { updateUnread(ArticleStatus.READ); break; } - + if(selectedArticle != null && selectedArticle.getArticleID() == m_article.getArticleID()) { view.getHeader().setRead(m_article.getUnread()); } - + FeedReaderBackend.get_default().updateArticleRead(m_article); show_all(); return m_article.getUnread(); } - + public void updateUnread(ArticleStatus unread) { if(m_article.getUnread() != unread) @@ -506,7 +506,7 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { } } } - + private bool unreadIconEnter() { m_hovering_unread = true; @@ -521,8 +521,8 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { this.show_all(); return true; } - - + + private bool unreadIconLeave() { m_hovering_unread = false; @@ -537,7 +537,7 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { this.show_all(); return true; } - + private bool markedIconClicked(Gdk.EventButton event) { switch(event.type) @@ -551,12 +551,12 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { rowStateChanged(m_article.getMarked()); return true; } - + public ArticleStatus toggleMarked() { var view = ColumnView.get_default(); Article? selectedArticle = ColumnView.get_default().getSelectedArticle(); - + switch(m_article.getMarked()) { case ArticleStatus.MARKED: @@ -566,17 +566,17 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { updateMarked(ArticleStatus.MARKED); break; } - + if(selectedArticle != null && selectedArticle.getArticleID() == m_article.getArticleID()) { view.getHeader().setMarked(m_article.getMarked()); } - + FeedReaderBackend.get_default().updateArticleMarked(m_article); this.show_all(); return m_article.getMarked(); } - + public void updateMarked(ArticleStatus marked) { if(m_article.getMarked() != marked) @@ -587,7 +587,7 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { case ArticleStatus.MARKED: m_marked_stack.set_visible_child_name("marked"); break; - + case ArticleStatus.UNMARKED: if(m_hovering_row) { @@ -601,7 +601,7 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { } } } - + private bool markedIconEnter() { m_hovering_marked = true; @@ -616,8 +616,8 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { this.show_all(); return true; } - - + + private bool markedIconLeave() { m_hovering_marked = false; @@ -632,57 +632,57 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { this.show_all(); return true; } - + public Article getArticle() { return m_article; } - + public string getName() { return m_article.getTitle(); } - + public string getID() { return m_article.getArticleID(); } - + public GLib.DateTime getDate() { return m_article.getDate(); } - + public string getDateStr() { return m_article.getDate().format("%Y-%m-%d %H:%M:%S"); } - + public bool getUpdated() { return m_updated; } - + public void setUpdated(bool updated) { m_updated = updated; } - + public bool isHoveringUnread() { return m_hovering_unread; } - + public bool isHoveringMarked() { return m_hovering_marked; } - + public string getURL() { return m_article.getURL(); } - + public void copyArticleURL(string article_id){ /* Copy selected article url to clipboard @@ -695,12 +695,12 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { string article_url = article.getURL(); Gdk.Display display = MainWindow.get_default().get_display (); Gtk.Clipboard clipboard = Gtk.Clipboard.get_for_display (display, Gdk.SELECTION_CLIPBOARD); - + clipboard.set_text(article_url, article_url.length); } } } - + public void reveal(bool reveal, uint duration = 500) { if(!reveal) @@ -710,17 +710,17 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { m_revealer.set_transition_duration(duration); m_revealer.set_reveal_child(reveal); } - + public bool isRevealed() { return m_revealer.get_child_revealed(); } - + public bool isBeingRevealed() { return m_revealer.get_reveal_child(); } - + public bool hasTag(string tagID) { foreach(string tag in m_article.getTagIDs()) @@ -730,20 +730,20 @@ public class FeedReader.ArticleRow : Gtk.ListBoxRow { return true; } } - + return false; } - + public void removeTag(string tagID) { m_article.getTagIDs().remove(tagID); } - + public int getSortID() { return m_article.getSortID(); } - + public bool haveMedia() { return m_article.haveMedia(); diff --git a/src/Widgets/ArticleView.vala b/src/Widgets/ArticleView.vala index 57f7bc56..495e0d9b 100644 --- a/src/Widgets/ArticleView.vala +++ b/src/Widgets/ArticleView.vala @@ -22,7 +22,7 @@ interface FeedReaderWebExtension : Object } public class FeedReader.ArticleView : Gtk.Overlay { - + private Gtk.Overlay m_videoOverlay; private ArticleViewUrlOverlay m_UrlOverlay; private Gtk.Stack m_stack; @@ -53,16 +53,16 @@ public class FeedReader.ArticleView : Gtk.Overlay { private bool m_FullscreenArticle = false; private double m_FullscreenZoomLevel = 1.25; private uint m_animationDuration = 150; - - + + public ArticleView() { WebKit.WebContext.get_default().set_cache_model(WebKit.CacheModel.DOCUMENT_BROWSER); - + var emptyView = new Gtk.Label(_("No Article selected.")); emptyView.get_style_context().add_class("h2"); emptyView.get_style_context().add_class("dim-label"); - + var crashLabel = new Gtk.Label(_("WebKit has crashed")); crashLabel.get_style_context().add_class("h2"); var crashIcon = new Gtk.Image.from_icon_name("face-crying-symbolic", Gtk.IconSize.BUTTON); @@ -83,17 +83,17 @@ public class FeedReader.ArticleView : Gtk.Overlay { crashView.set_valign(Gtk.Align.CENTER); crashView.pack_start(crashLabelBox); crashView.pack_start(crashButton); - - + + m_UrlOverlay = new ArticleViewUrlOverlay(); m_stack = new Gtk.Stack(); m_stack.add_named(emptyView, "empty"); m_stack.add_named(crashView, "crash"); - + m_stack.set_visible_child_name("empty"); setTransition(Gtk.StackTransitionType.CROSSFADE, m_animationDuration); m_stack.set_size_request(450, 0); - + this.size_allocate.connect((allocation) => { if(allocation.width != m_width || allocation.height != m_height) @@ -105,18 +105,18 @@ public class FeedReader.ArticleView : Gtk.Overlay { recalculate(); } }); - + m_fsHead = new FullscreenHeader(); - + m_progress = new ArticleViewLoadProgress(); var progressOverlay = new Gtk.Overlay(); progressOverlay.add(m_stack); progressOverlay.add_overlay(m_progress); - + var fullscreenHeaderOverlay = new Gtk.Overlay(); fullscreenHeaderOverlay.add(progressOverlay); fullscreenHeaderOverlay.add_overlay(m_fsHead); - + m_prevButton = new fullscreenButton("go-previous-symbolic", Gtk.Align.START); m_prevButton.click.connect(() => { ColumnView.get_default().ArticleListPREV(); @@ -124,7 +124,7 @@ public class FeedReader.ArticleView : Gtk.Overlay { var prevOverlay = new Gtk.Overlay(); prevOverlay.add(fullscreenHeaderOverlay); prevOverlay.add_overlay(m_prevButton); - + m_nextButton = new fullscreenButton("go-next-symbolic", Gtk.Align.END); m_nextButton.click.connect(() => { ColumnView.get_default().ArticleListNEXT(); @@ -132,25 +132,25 @@ public class FeedReader.ArticleView : Gtk.Overlay { var nextOverlay = new Gtk.Overlay(); nextOverlay.add(prevOverlay); nextOverlay.add_overlay(m_nextButton); - + m_videoOverlay = new Gtk.Overlay(); m_videoOverlay.add(nextOverlay); - + this.add(m_videoOverlay); this.add_overlay(m_UrlOverlay); - + Gtk.Settings.get_default().notify["gtk-theme-name"].connect(() => { setBackgroundColor(); }); - + Gtk.Settings.get_default().notify["gtk-application-prefer-dark-theme"].connect(() => { setBackgroundColor(); }); - + Bus.watch_name(BusType.SESSION, "org.gnome.FeedReader.ArticleView", GLib.BusNameWatcherFlags.NONE, (connection, name, owner) => { on_extension_appeared(connection, name, owner); }, null); } - + private WebKit.WebView getNewView() { bool smoothScroll = Settings.tweaks().get_boolean("smooth-scrolling"); @@ -168,7 +168,7 @@ public class FeedReader.ArticleView : Gtk.Overlay { settings.set_javascript_can_open_windows_automatically(false); settings.set_media_playback_requires_user_gesture(true); settings.set_user_agent_with_application_details("FeedReader", AboutInfo.version); - + var view = new WebKit.WebView(); view.set_settings(settings); view.set_events(Gdk.EventMask.POINTER_MOTION_MASK); @@ -194,36 +194,36 @@ public class FeedReader.ArticleView : Gtk.Overlay { { view.set_background_color(m_color); } - + view.show(); return view; } - + public void fillContent(string articleID) { Logger.debug(@"ArticleView: load article $articleID"); - + if(m_busy) { Logger.debug(@"ArticleView: currently busy - next article in line is $articleID"); m_nextArticle = articleID; return; } - + m_currentArticle = articleID; - + if(m_OngoingScrollID > 0) { GLib.Source.remove(m_OngoingScrollID); m_OngoingScrollID = 0; } - + Article article = DataBase.readOnly().read_article(articleID); - + GLib.Idle.add(() => { Logger.debug("ArticleView: WebView load html"); switchViews(); - + if(m_FullscreenArticle) { m_currentView.zoom_level = m_FullscreenZoomLevel; @@ -232,15 +232,15 @@ public class FeedReader.ArticleView : Gtk.Overlay { { m_currentView.zoom_level = 1.0; } - + m_fsHead.setTitle(article.getTitle()); m_fsHead.setMarked(article.getMarked()); m_fsHead.setRead(article.getUnread()); - + m_progress.reset(); m_progress.setPercentage(0); m_progress.reveal(true); - + m_currentView.load_html( Utils.buildArticle( article.getHTML(), @@ -255,18 +255,18 @@ public class FeedReader.ArticleView : Gtk.Overlay { return false; }, GLib.Priority.HIGH_IDLE); } - + private void switchViews() { m_busy = true; string? visible = m_stack.get_visible_child_name(); - + if(visible == null) { Logger.error("ArticleView: "); return; } - + switch(visible) { case "empty": @@ -277,8 +277,8 @@ public class FeedReader.ArticleView : Gtk.Overlay { m_stack.set_visible_child_name("view1"); m_busy = false; break; - - + + case "view1": Logger.debug("ArticleView: view1 -> view2"); m_currentView = getNewView(); @@ -291,7 +291,7 @@ public class FeedReader.ArticleView : Gtk.Overlay { return false; }, GLib.Priority.HIGH); break; - + case "view2": Logger.debug("ArticleView: view2 -> view1"); m_currentView = getNewView(); @@ -305,7 +305,7 @@ public class FeedReader.ArticleView : Gtk.Overlay { }, GLib.Priority.HIGH); break; } - + if(m_FullscreenArticle) { if(ColumnView.get_default().ArticleListSelectedIsLast()) @@ -316,7 +316,7 @@ public class FeedReader.ArticleView : Gtk.Overlay { { m_prevButton.reveal(true); } - + if(ColumnView.get_default().ArticleListSelectedIsFirst()) { m_nextButton.reveal(false); @@ -327,7 +327,7 @@ public class FeedReader.ArticleView : Gtk.Overlay { } } } - + private void removeFromStack(string childName) { Gtk.Widget? widget = m_stack.get_child_by_name(childName); @@ -336,7 +336,7 @@ public class FeedReader.ArticleView : Gtk.Overlay { m_stack.remove(widget); } } - + private void checkQueue() { m_busy = false; @@ -348,7 +348,7 @@ public class FeedReader.ArticleView : Gtk.Overlay { load(id); } } - + public void clearContent() { m_busy = true; @@ -370,12 +370,12 @@ public class FeedReader.ArticleView : Gtk.Overlay { }, GLib.Priority.HIGH); m_currentArticle = ""; } - + public string getCurrentArticle() { return m_currentArticle; } - + public void open_link(WebKit.LoadEvent load_event) { switch (load_event) @@ -420,7 +420,7 @@ public class FeedReader.ArticleView : Gtk.Overlay { break; } } - + /*private bool loadFailed(WebKit.LoadEvent event, string failing_uri, void* error) { GLib.Error e = (GLib.Error)error; @@ -433,7 +433,7 @@ public class FeedReader.ArticleView : Gtk.Overlay { } return true; }*/ - + public void setScrollPos(int pos) { if(m_stack.get_visible_child_name() == "empty" @@ -442,7 +442,7 @@ public class FeedReader.ArticleView : Gtk.Overlay { { return; } - + m_busy = true; m_currentView.run_javascript.begin("window.scrollTo(0,%i);".printf(pos), null, (obj, res) => { try @@ -456,7 +456,7 @@ public class FeedReader.ArticleView : Gtk.Overlay { checkQueue(); }); } - + private int getScollUpper() { if(m_stack.get_visible_child_name() == "empty" @@ -465,7 +465,7 @@ public class FeedReader.ArticleView : Gtk.Overlay { { return 0; } - + string javascript = """ document.title = Math.max ( document.body.scrollHeight, @@ -477,7 +477,7 @@ public class FeedReader.ArticleView : Gtk.Overlay { """; int upper = -1; var loop = new MainLoop(); - + m_busy = true; m_currentView.run_javascript.begin(javascript, null, (obj, res) => { try @@ -492,11 +492,11 @@ public class FeedReader.ArticleView : Gtk.Overlay { checkQueue(); loop.quit(); }); - + loop.run(); return upper; } - + public int getScrollPos() { if(m_stack.get_visible_child_name() == "empty" @@ -505,14 +505,14 @@ public class FeedReader.ArticleView : Gtk.Overlay { { return 0; } - + // use mainloop to prevent app from shutting down before the result can be fetched // ugly but works =/ // better solution welcome - + int scrollPos = -1; var loop = new MainLoop(); - + m_busy = true; m_currentView.run_javascript.begin("document.title = window.scrollY;", null, (obj, res) => { try @@ -527,18 +527,18 @@ public class FeedReader.ArticleView : Gtk.Overlay { checkQueue(); loop.quit(); }); - + loop.run(); return scrollPos; } - - + + public void setSearchTerm(string searchTerm) { m_searchTerm = Utils.parseSearchTerm(searchTerm); } - - + + private void on_extension_appeared(GLib.DBusConnection connection, string name, string owner) { try @@ -559,7 +559,7 @@ public class FeedReader.ArticleView : Gtk.Overlay { Logger.error("ArticleView.on_extension_appeared: " + e.message); } } - + private void recalculate() { try @@ -577,7 +577,7 @@ public class FeedReader.ArticleView : Gtk.Overlay { Logger.warning("ArticleView: recalculate " + e.message); } } - + private bool onClick(Gdk.EventButton event) { if(event.button == MouseButton.MIDDLE) @@ -588,12 +588,12 @@ public class FeedReader.ArticleView : Gtk.Overlay { m_dragBuffer[i] = m_posY; } m_inDrag = true; - + var display = Gdk.Display.get_default(); var seat = display.get_default_seat(); var pointer = seat.get_pointer(); var cursor = new Gdk.Cursor.for_display(display, Gdk.CursorType.FLEUR); - + seat.grab( m_currentView.get_window(), Gdk.SeatCapabilities.POINTER, @@ -602,16 +602,16 @@ public class FeedReader.ArticleView : Gtk.Overlay { null, null ); - + Gtk.device_grab_add(this, pointer, false); GLib.Timeout.add(10, updateDragMomentum, GLib.Priority.HIGH); m_currentView.motion_notify_event.connect(updateScroll); return true; } - + return false; } - + private bool onRelease(Gdk.EventButton event) { if(event.button == MouseButton.MIDDLE) @@ -619,26 +619,26 @@ public class FeedReader.ArticleView : Gtk.Overlay { m_currentView.motion_notify_event.disconnect(updateScroll); m_inDrag = false; m_OngoingScrollID = GLib.Timeout.add(20, ScrollDragRelease, GLib.Priority.HIGH); - + var display = Gdk.Display.get_default(); var seat = display.get_default_seat(); var pointer = seat.get_pointer(); Gtk.device_grab_remove(this, pointer); seat.ungrab(); - + return true; } - + return false; } - + private bool onMouseMotion(Gdk.EventMotion event) { m_posX2 = event.x; m_posY2 = event.y; return false; } - + private bool onScroll(Gdk.EventScroll event) { if((event.state & Gdk.ModifierType.CONTROL_MASK) == Gdk.ModifierType.CONTROL_MASK) @@ -648,22 +648,22 @@ public class FeedReader.ArticleView : Gtk.Overlay { case Gdk.ScrollDirection.UP: m_currentView.zoom_level -= 0.25; break; - + case Gdk.ScrollDirection.DOWN: m_currentView.zoom_level += 0.25; break; - + case Gdk.ScrollDirection.SMOOTH: m_currentView.zoom_level -= 10 * (event.delta_y / event.y_root); break; } - + return true; } - + return false; } - + private bool onKeyPress(Gdk.EventKey event) { if((event.state & Gdk.ModifierType.CONTROL_MASK) == Gdk.ModifierType.CONTROL_MASK) @@ -680,79 +680,79 @@ public class FeedReader.ArticleView : Gtk.Overlay { m_currentView.zoom_level = 1.0; } return true; - + case Gdk.Key.KP_Add: m_currentView.zoom_level += 0.25; return true; - + case Gdk.Key.KP_Subtract: m_currentView.zoom_level -= 0.25; return true; } } - + return false; } - + private bool updateScroll(Gdk.EventMotion event) { double scroll = m_posY - event.y; m_posY = event.y; setScrollPos(getScrollPos() + (int)scroll); - + return false; } - + public void load(string? id = null) { string articleID = (id == null) ? m_currentArticle : id; fillContent(articleID); } - + private bool updateDragMomentum() { if(!m_inDrag) { return false; } - + for(int i = 9; i > 0; --i) { m_dragBuffer[i] = m_dragBuffer[i-1]; } - + m_dragBuffer[0] = m_posY; m_momentum = m_dragBuffer[9] - m_dragBuffer[0]; - + return true; } - + private bool ScrollDragRelease() { if(m_inDrag) { return true; } - + m_momentum /= 1.2; - + Gtk.Allocation allocation; m_currentView.get_allocation(out allocation); - + double pageSize = m_currentView.get_allocated_height(); double adjValue = pageSize * m_momentum / allocation.height; double oldAdj = getScrollPos(); double upper = getScollUpper() * m_currentView.zoom_level; - + if ((oldAdj + adjValue) > (upper - pageSize) || (oldAdj + adjValue) < 0) { m_momentum = 0; } - + double newScrollPos = double.min(oldAdj + adjValue, upper - pageSize); setScrollPos((int)newScrollPos); - + if (m_momentum < 1 && m_momentum > -1) { m_OngoingScrollID = 0; @@ -763,7 +763,7 @@ public class FeedReader.ArticleView : Gtk.Overlay { return true; } } - + private void setBackgroundColor() { Logger.debug("ArticleView.setBackgroundColor()"); @@ -774,7 +774,7 @@ public class FeedReader.ArticleView : Gtk.Overlay { m_color = background; } } - + private bool onContextMenu(WebKit.ContextMenu menu, Gdk.Event event, WebKit.HitTestResult hitTest) { var menuItems = menu.get_items().copy(); @@ -785,17 +785,17 @@ public class FeedReader.ArticleView : Gtk.Overlay { menu.remove(menuItem); continue; } - + if((menuItem.get_gaction().name != "context-menu-action-3") // copy link location && (menuItem.get_gaction().name != "context-menu-action-9") // copy text && (menuItem.get_gaction().name != "context-menu-action-6") // copy image && (menuItem.get_gaction().name != "context-menu-action-7")) // copy image address - + { menu.remove(menuItem); } } - + if(hitTest.context_is_image()) { var uri = hitTest.get_image_uri().substring("file://".length); @@ -805,15 +805,15 @@ public class FeedReader.ArticleView : Gtk.Overlay { }); menu.append(new WebKit.ContextMenuItem.from_gaction(action, _("Save image as"), null)); } - + if(menu.first() == null) { return true; } - + return false; } - + private void onMouseOver(WebKit.HitTestResult hitTest, uint modifiers) { if(hitTest.context_is_link()) @@ -821,12 +821,12 @@ public class FeedReader.ArticleView : Gtk.Overlay { var align = Gtk.Align.START; double relX = m_posX2/this.get_allocated_height(); double relY = m_posY2/this.get_allocated_width(); - + if(relY >= 0.85 && relX <= 0.5) { align = Gtk.Align.END; } - + m_UrlOverlay.setURL(hitTest.get_link_uri(), align); m_UrlOverlay.reveal(true); } @@ -835,7 +835,7 @@ public class FeedReader.ArticleView : Gtk.Overlay { m_UrlOverlay.reveal(false); } } - + private bool leaveFullscreenVideo() { Logger.debug("ArticleView: leave fullscreen Video"); @@ -844,22 +844,22 @@ public class FeedReader.ArticleView : Gtk.Overlay { ColumnView.get_default().showPane(); return false; } - + private bool enterFullscreenVideo() { Logger.debug("ArticleView: enter fullscreen Video"); m_FullscreenVideo = true; - + // don't try to recalculate imagesizes when playing fullscreen video m_connected = false; - + ColumnView.get_default().hidePane(); m_fsHead.hide(); m_prevButton.reveal(false); m_nextButton.reveal(false); return false; } - + public void exitFullscreenVideo() { if(m_currentView != null) @@ -867,35 +867,35 @@ public class FeedReader.ArticleView : Gtk.Overlay { m_currentView.leave_fullscreen(); } } - + public bool fullscreenVideo() { return m_FullscreenVideo; } - + public bool fullscreenArticle() { return m_FullscreenArticle; } - + public void enterFullscreenArticle() { Logger.debug("ArticleView: enter fullscreen Article"); m_FullscreenArticle = true; m_fsHead.show(); m_currentView.zoom_level = m_FullscreenZoomLevel; - + if(!ColumnView.get_default().ArticleListSelectedIsFirst()) { m_nextButton.reveal(true); } - + if(!ColumnView.get_default().ArticleListSelectedIsLast()) { m_prevButton.reveal(true); } } - + public void leaveFullscreenArticle() { Logger.debug("ArticleView: enter fullscreen Article"); @@ -906,47 +906,47 @@ public class FeedReader.ArticleView : Gtk.Overlay { m_prevButton.reveal(false); m_nextButton.reveal(false); } - + public void setTransition(Gtk.StackTransitionType trans, uint time) { m_stack.set_transition_type(trans); m_stack.set_transition_duration(time); m_animationDuration = time; } - + private void printProgress() { double progress = m_currentView.estimated_load_progress; Logger.debug("ArticleView: loading %u %%".printf((uint)(progress*100))); - + m_progress.setPercentageF(progress); - + if(progress == 1.0) { m_progress.reveal(false); } } - + public void setMarked(ArticleStatus marked) { m_fsHead.setMarked(marked); } - + public void setRead(ArticleStatus read) { m_fsHead.setRead(read); } - + public void nextButtonVisible(bool vis) { m_nextButton.reveal(vis); } - + public void prevButtonVisible(bool vis) { m_prevButton.reveal(vis); } - + private void onCrash(WebKit.WebProcessTerminationReason reason) { m_busy = true; @@ -972,14 +972,14 @@ public class FeedReader.ArticleView : Gtk.Overlay { uint micro = WebKit.get_micro_version(); Logger.debug(@"Running WebKit $major.$minor.$micro"); } - + public void addMedia(MediaPlayer media) { killMedia(); m_videoOverlay.add_overlay(media); m_currentMedia = media; } - + public void killMedia() { if(m_currentMedia != null) @@ -987,51 +987,51 @@ public class FeedReader.ArticleView : Gtk.Overlay { m_currentMedia.kill(); } } - + public bool playingMedia() { if(m_currentMedia == null) { return false; } - + return true; } - + public void print() { if(m_currentView == null) { return; } - + string articleName = DataBase.readOnly().read_article(m_currentArticle).getTitle() + ".pdf"; - + var settings = new Gtk.PrintSettings(); settings.set_printer("Print to File"); settings.set("output-file-format", "pdf"); settings.set("output-uri", articleName); - + var setup = new Gtk.PageSetup(); setup.set_left_margin(0, Gtk.Unit.MM); setup.set_right_margin(0, Gtk.Unit.MM); - + var op = new WebKit.PrintOperation(m_currentView); op.set_print_settings(settings); op.set_page_setup(setup); - + op.failed.connect((error) => { Logger.debug("ArticleView: print failed: "+ error.message); }); - + op.finished.connect(() => { Logger.debug("ArticleView: print finished"); }); - + //op.print(); op.run_dialog(MainWindow.get_default()); } - + private bool decidePolicy(WebKit.PolicyDecision decision, WebKit.PolicyDecisionType type) { Logger.debug("ArticleView: Policy decision"); @@ -1054,18 +1054,18 @@ public class FeedReader.ArticleView : Gtk.Overlay { return true; } } - + return false; } - + public void showMediaButton(bool show) { m_fsHead.showMediaButton(show); } - + public void sendEvent(Gdk.EventKey event) { m_currentView.key_press_event(event); } - + } diff --git a/src/Widgets/ArticleViewHeader.vala b/src/Widgets/ArticleViewHeader.vala index 3f73d69f..efbbbc97 100644 --- a/src/Widgets/ArticleViewHeader.vala +++ b/src/Widgets/ArticleViewHeader.vala @@ -15,7 +15,7 @@ public class FeedReader.ArticleViewHeader : Gtk.HeaderBar { - + private Gtk.Button m_share_button; private Gtk.Button m_tag_button; private Gtk.Button m_print_button; @@ -29,7 +29,7 @@ public class FeedReader.ArticleViewHeader : Gtk.HeaderBar { public signal void fsClick(); public signal void popClosed(); public signal void popOpened(); - + public ArticleViewHeader(string fsIcon, string fsTooltip) { var share_icon = Utils.checkIcon("emblem-shared-symbolic", "feed-share-symbolic", Gtk.IconSize.SMALL_TOOLBAR); @@ -39,7 +39,7 @@ public class FeedReader.ArticleViewHeader : Gtk.HeaderBar { var read_icon = new Gtk.Image.from_icon_name("feed-read-symbolic", Gtk.IconSize.SMALL_TOOLBAR); var unread_icon = new Gtk.Image.from_icon_name("feed-unread-symbolic", Gtk.IconSize.SMALL_TOOLBAR); var fs_icon = new Gtk.Image.from_icon_name(fsIcon, Gtk.IconSize.SMALL_TOOLBAR); - + m_mark_button = new HoverButton(unmarked_icon, marked_icon, false); m_mark_button.sensitive = false; m_mark_button.clicked.connect(() => { @@ -50,7 +50,7 @@ public class FeedReader.ArticleViewHeader : Gtk.HeaderBar { m_read_button.clicked.connect(() => { toggledRead(); }); - + m_fullscreen_button = new Gtk.Button(); m_fullscreen_button.add(fs_icon); m_fullscreen_button.set_relief(Gtk.ReliefStyle.NONE); @@ -60,7 +60,7 @@ public class FeedReader.ArticleViewHeader : Gtk.HeaderBar { m_fullscreen_button.clicked.connect(() => { fsClick(); }); - + m_tag_button = new Gtk.Button(); m_tag_button.add(tag_icon); m_tag_button.set_relief(Gtk.ReliefStyle.NONE); @@ -74,8 +74,8 @@ public class FeedReader.ArticleViewHeader : Gtk.HeaderBar { popClosed(); }); }); - - + + m_print_button = new Gtk.Button.from_icon_name("printer-symbolic"); m_print_button.set_relief(Gtk.ReliefStyle.NONE); m_print_button.set_focus_on_click(false); @@ -84,15 +84,15 @@ public class FeedReader.ArticleViewHeader : Gtk.HeaderBar { m_print_button.clicked.connect(() => { ColumnView.get_default().print(); }); - - + + m_share_button = new Gtk.Button(); m_share_button.add(share_icon); m_share_button.set_relief(Gtk.ReliefStyle.NONE); m_share_button.set_focus_on_click(false); m_share_button.set_tooltip_text(_("Share article")); m_share_button.sensitive = false; - + var shareSpinner = new Gtk.Spinner(); var shareStack = new Gtk.Stack(); shareStack.set_transition_type(Gtk.StackTransitionType.CROSSFADE); @@ -100,7 +100,7 @@ public class FeedReader.ArticleViewHeader : Gtk.HeaderBar { shareStack.add_named(m_share_button, "button"); shareStack.add_named(shareSpinner, "spinner"); shareStack.set_visible_child_name("button"); - + m_share_button.clicked.connect(() => { popOpened(); m_sharePopover = new SharePopover(m_share_button); @@ -117,7 +117,7 @@ public class FeedReader.ArticleViewHeader : Gtk.HeaderBar { popClosed(); }); }); - + m_media_button = new AttachedMediaButton(); m_media_button.popOpened.connect(() => { popOpened(); @@ -125,7 +125,7 @@ public class FeedReader.ArticleViewHeader : Gtk.HeaderBar { m_media_button.popClosed.connect(() => { popClosed(); }); - + this.pack_start(m_fullscreen_button); this.pack_start(m_mark_button); this.pack_start(m_read_button); @@ -134,7 +134,7 @@ public class FeedReader.ArticleViewHeader : Gtk.HeaderBar { this.pack_end(m_print_button); this.pack_end(m_media_button); } - + public void showArticleButtons(bool show) { Logger.debug("HeaderBar: showArticleButtons %s".printf(sensitive ? "true" : "false")); @@ -143,14 +143,14 @@ public class FeedReader.ArticleViewHeader : Gtk.HeaderBar { m_fullscreen_button.sensitive = show; m_share_button.sensitive = (show && FeedReaderApp.get_default().isOnline()); m_print_button.sensitive = show; - + if(FeedReaderBackend.get_default().supportTags() && Utils.canManipulateContent()) { m_tag_button.sensitive = (show && FeedReaderApp.get_default().isOnline()); } } - + public void setMarked(ArticleStatus marked) { switch(marked) @@ -164,12 +164,12 @@ public class FeedReader.ArticleViewHeader : Gtk.HeaderBar { break; } } - + public void toggleMarked() { m_mark_button.toggle(); } - + public void setRead(ArticleStatus read) { switch(read) @@ -183,12 +183,12 @@ public class FeedReader.ArticleViewHeader : Gtk.HeaderBar { break; } } - + public void toggleRead() { m_read_button.toggle(); } - + public void setOffline() { m_share_button.sensitive = false; @@ -198,7 +198,7 @@ public class FeedReader.ArticleViewHeader : Gtk.HeaderBar { m_tag_button.sensitive = false; } } - + public void setOnline() { if(m_mark_button.sensitive) @@ -211,21 +211,21 @@ public class FeedReader.ArticleViewHeader : Gtk.HeaderBar { } } } - + public void showMediaButton(bool show) { m_media_button.update(); m_media_button.visible = show; } - + public void refreshSahrePopover() { if(m_sharePopover == null) { return; } - + m_sharePopover.refreshList(); } - + } diff --git a/src/Widgets/ArticleViewLoadProgress.vala b/src/Widgets/ArticleViewLoadProgress.vala index 69a6b816..7f0bd068 100644 --- a/src/Widgets/ArticleViewLoadProgress.vala +++ b/src/Widgets/ArticleViewLoadProgress.vala @@ -14,10 +14,10 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.ArticleViewLoadProgress : Gtk.Revealer { - + private Gtk.ProgressBar m_progress; private uint m_timeout_source_id = 0; - + public ArticleViewLoadProgress() { m_progress = new Gtk.ProgressBar(); @@ -28,17 +28,17 @@ public class FeedReader.ArticleViewLoadProgress : Gtk.Revealer { this.no_show_all = true; this.add(m_progress); } - + public void setPercentage(uint percentage) { m_progress.set_fraction(percentage); } - + public void setPercentageF(double percentage) { m_progress.set_fraction(percentage); } - + public void reveal(bool show) { if(m_timeout_source_id > 0) @@ -46,7 +46,7 @@ public class FeedReader.ArticleViewLoadProgress : Gtk.Revealer { GLib.Source.remove(m_timeout_source_id); m_timeout_source_id = 0; } - + if(show) { this.visible = true; @@ -62,11 +62,11 @@ public class FeedReader.ArticleViewLoadProgress : Gtk.Revealer { this.set_reveal_child(false); } } - + public void reset() { reveal(false); } - - + + } diff --git a/src/Widgets/ArticleViewUrlOverlay.vala b/src/Widgets/ArticleViewUrlOverlay.vala index 11ee534b..18fa68fd 100644 --- a/src/Widgets/ArticleViewUrlOverlay.vala +++ b/src/Widgets/ArticleViewUrlOverlay.vala @@ -14,15 +14,15 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.ArticleViewUrlOverlay : Gtk.Revealer { - + private Gtk.Label m_label; - + public ArticleViewUrlOverlay() { m_label = new Gtk.Label("dummy"); m_label.get_style_context().add_class("osd"); m_label.height_request = 30; - + this.valign = Gtk.Align.END; this.halign = Gtk.Align.START; this.margin = 10; @@ -31,7 +31,7 @@ public class FeedReader.ArticleViewUrlOverlay : Gtk.Revealer { this.no_show_all = true; this.add(m_label); } - + public void setURL(string uri, Gtk.Align align) { int length = 45; @@ -44,7 +44,7 @@ public class FeedReader.ArticleViewUrlOverlay : Gtk.Revealer { m_label.width_chars = url.length; this.halign = align; } - + public void reveal(bool show) { if(show) @@ -52,7 +52,7 @@ public class FeedReader.ArticleViewUrlOverlay : Gtk.Revealer { this.visible = true; m_label.show(); } - + this.set_reveal_child(show); } } diff --git a/src/Widgets/BackendInfoPopover.vala b/src/Widgets/BackendInfoPopover.vala index ea8ad9bd..7a5811a2 100644 --- a/src/Widgets/BackendInfoPopover.vala +++ b/src/Widgets/BackendInfoPopover.vala @@ -14,33 +14,33 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.BackendInfoPopover : Gtk.Popover { - + private BackendInfo m_info; - + public BackendInfoPopover(Gtk.Widget widget, BackendInfo info) { m_info = info; - + int space = 25; - + var typeLabel = new Gtk.Label("Type:"); typeLabel.hexpand = true; typeLabel.get_style_context().add_class("h3"); typeLabel.set_alignment(0.0f, 0.5f); - - + + var licenseLabel = new Gtk.Label("License:"); licenseLabel.hexpand = true; licenseLabel.get_style_context().add_class("h3"); licenseLabel.set_alignment(0.0f, 0.5f); - - + + var priceLabel = new Gtk.Label("Price:"); priceLabel.hexpand = true; priceLabel.get_style_context().add_class("h3"); priceLabel.set_alignment(0.0f, 0.5f); - - + + var grid = new Gtk.Grid(); grid.set_column_spacing(20); grid.set_row_spacing(5); @@ -48,9 +48,9 @@ public class FeedReader.BackendInfoPopover : Gtk.Popover { grid.attach(typeLabel, 0, 0, 1, 1); grid.attach(licenseLabel, 0, 1, 1, 1); grid.attach(priceLabel, 0, 2, 1, 1); - - - + + + if(BackendFlags.LOCAL in m_info.flags) { var icon = getIcon("feed-local-symbolic", "Local Files only"); @@ -72,8 +72,8 @@ public class FeedReader.BackendInfoPopover : Gtk.Popover { space = 50; } } - - + + if(BackendFlags.FREE_SOFTWARE in m_info.flags) { var icon = getIcon("feed-gpl-symbolic", "Free Software"); @@ -84,7 +84,7 @@ public class FeedReader.BackendInfoPopover : Gtk.Popover { var icon = getIcon("feed-copyright-symbolic", "Proprietary Software"); grid.attach(icon, 1, 1, 1, 1); } - + if(BackendFlags.FREE in m_info.flags) { var icon = getIcon("feed-free-symbolic", "Free Service"); @@ -100,15 +100,15 @@ public class FeedReader.BackendInfoPopover : Gtk.Popover { var icon = getIcon("feed-nonfree-symbolic", "Free basic usage with paid Premium"); grid.attach(icon, 1, 2, 1, 1); } - - + + var nameLabel = new Gtk.Label(m_info.name); nameLabel.get_style_context().add_class("h2"); nameLabel.set_alignment(0.0f, 0.5f); - + var eventbox = new Gtk.EventBox(); eventbox.set_events(Gdk.EventMask.BUTTON_PRESS_MASK); - + eventbox.button_press_event.connect(websiteClicked); eventbox.add(getIcon("feed-website-symbolic", m_info.website)); var nameBox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, space); @@ -116,24 +116,24 @@ public class FeedReader.BackendInfoPopover : Gtk.Popover { nameBox.pack_end(eventbox, false, false, 0); nameBox.margin = 10; nameBox.margin_bottom = 5; - + var separator = new Gtk.Separator(Gtk.Orientation.HORIZONTAL); - + var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); box.pack_start(nameBox, false, false, 0); box.pack_start(separator, false, false, 0); box.pack_start(grid, true, true, 0); - - + + this.add(box); this.set_relative_to(widget); this.set_position(Gtk.PositionType.BOTTOM); this.show_all(); - + var cursor = new Gdk.Cursor.for_display(Gdk.Display.get_default(), Gdk.CursorType.HAND1); eventbox.get_window().set_cursor(cursor); } - + private bool websiteClicked(Gdk.EventButton event) { // only accept left mouse button @@ -141,7 +141,7 @@ public class FeedReader.BackendInfoPopover : Gtk.Popover { { return false; } - + switch(event.type) { case Gdk.EventType.BUTTON_RELEASE: @@ -149,7 +149,7 @@ public class FeedReader.BackendInfoPopover : Gtk.Popover { case Gdk.EventType.@3BUTTON_PRESS: return false; } - + try { Gtk.show_uri_on_window(MainWindow.get_default(), m_info.website, Gdk.CURRENT_TIME); @@ -160,7 +160,7 @@ public class FeedReader.BackendInfoPopover : Gtk.Popover { } return true; } - + private Gtk.Image getIcon(string name, string tooltip) { var icon = new Gtk.Image.from_icon_name(name, Gtk.IconSize.MENU); @@ -168,6 +168,6 @@ public class FeedReader.BackendInfoPopover : Gtk.Popover { icon.set_tooltip_text(tooltip); return icon; } - - + + } diff --git a/src/Widgets/CategorieRow.vala b/src/Widgets/CategorieRow.vala index c1f0dbe9..05259ee2 100644 --- a/src/Widgets/CategorieRow.vala +++ b/src/Widgets/CategorieRow.vala @@ -14,7 +14,7 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.CategoryRow : Gtk.ListBoxRow { - + private Gtk.Box m_box; private string m_name; private Gtk.Label m_label; @@ -42,7 +42,7 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { public signal void moveUP(); public signal void deselectRow(); public signal void removeRow(); - + public CategoryRow(string name, string categorieID, int orderID, uint unread_count, string parentID, int level, bool expanded) { this.get_style_context().add_class("fr-sidebar-row"); @@ -55,24 +55,24 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { m_unread_count = unread_count; var rowhight = 30; m_box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0); - - + + m_icon_collapsed = new Gtk.Image.from_icon_name("feed-sidebar-arrow-side-symbolic", Gtk.IconSize.SMALL_TOOLBAR); m_icon_collapsed.get_style_context().add_class("fr-sidebar-symbolic"); m_icon_collapsed.opacity = m_opacity; - + m_icon_expanded = new Gtk.Image.from_icon_name("feed-sidebar-arrow-down-symbolic", Gtk.IconSize.SMALL_TOOLBAR); m_icon_expanded.get_style_context().add_class("fr-sidebar-symbolic"); m_icon_expanded.opacity = m_opacity; - - - + + + m_stack = new Gtk.Stack(); m_stack.set_transition_type(Gtk.StackTransitionType.NONE); m_stack.set_transition_duration(0); m_stack.add_named(m_icon_expanded, "expanded"); m_stack.add_named(m_icon_collapsed, "collapsed"); - + m_expandBox = new Gtk.EventBox(); m_expandBox.set_events(Gdk.EventMask.BUTTON_PRESS_MASK); m_expandBox.set_events(Gdk.EventMask.ENTER_NOTIFY_MASK); @@ -83,17 +83,17 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { m_expandBox.button_press_event.connect(onExpandClick); m_expandBox.enter_notify_event.connect(onExpandEnter); m_expandBox.leave_notify_event.connect(onExpandLeave); - + m_label = new Gtk.Label(m_name); m_label.set_size_request (0, rowhight); m_label.set_ellipsize (Pango.EllipsizeMode.END); m_label.set_alignment(0, 0.5f); - - + + m_unread = new Gtk.Label(""); m_unread.set_size_request (0, rowhight); m_unread.set_alignment(0.8f, 0.5f); - + m_unreadStack = new Gtk.Stack(); m_unreadStack.set_transition_type(Gtk.StackTransitionType.NONE); m_unreadStack.set_transition_duration(0); @@ -102,18 +102,18 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { var markIcon = new Gtk.Image.from_icon_name("feed-mark-read-symbolic", Gtk.IconSize.LARGE_TOOLBAR); markIcon.get_style_context().add_class("fr-sidebar-symbolic"); m_unreadStack.add_named(markIcon, "mark"); - + m_unreadBox = new Gtk.EventBox(); m_unreadBox.set_events(Gdk.EventMask.BUTTON_PRESS_MASK); m_unreadBox.set_events(Gdk.EventMask.ENTER_NOTIFY_MASK); m_unreadBox.set_events(Gdk.EventMask.LEAVE_NOTIFY_MASK); m_unreadBox.add(m_unreadStack); activateUnreadEventbox(true); - + m_box.pack_start(m_expandBox, false, false, 0); m_box.pack_start(m_label, true, true, 0); m_box.pack_end(m_unreadBox, false, false, 8); - + m_eventBox = new Gtk.EventBox(); if(m_categorieID != CategoryID.MASTER.to_string() && m_categorieID != CategoryID.TAGS.to_string()) @@ -122,16 +122,16 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { m_eventBox.button_press_event.connect(onClick); } m_eventBox.add(m_box); - + m_revealer = new Gtk.Revealer(); m_revealer.set_transition_type(Gtk.RevealerTransitionType.SLIDE_DOWN); m_revealer.add(m_eventBox); m_revealer.set_reveal_child(false); this.add(m_revealer); this.show_all(); - + set_unread_count(m_unread_count); - + if(m_collapsed) { m_stack.set_visible_child_name("collapsed"); @@ -140,7 +140,7 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { { m_stack.set_visible_child_name("expanded"); } - + if(Utils.canManipulateContent()) { if(m_categorieID != CategoryID.MASTER.to_string() @@ -150,32 +150,32 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { { "text/plain", 0, DragTarget.FEED }, { "STRING", 0, DragTarget.CAT } }; - + Gtk.drag_dest_set ( this, Gtk.DestDefaults.MOTION, accepted_targets, Gdk.DragAction.MOVE ); - + this.drag_motion.connect(onDragMotion); this.drag_leave.connect(onDragLeave); this.drag_drop.connect(onDragDrop); this.drag_data_received.connect(onDragDataReceived); - + if(FeedReaderBackend.get_default().supportMultiLevelCategories()) { const Gtk.TargetEntry[] provided_targets = { { "STRING", 0, DragTarget.CAT } }; - + Gtk.drag_source_set ( this, Gdk.ModifierType.BUTTON1_MASK, provided_targets, Gdk.DragAction.MOVE ); - + this.drag_begin.connect(onDragBegin); this.drag_data_get.connect(onDragDataGet); } @@ -185,14 +185,14 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { const Gtk.TargetEntry[] accepted_targets = { { "STRING", 0, DragTarget.CAT } }; - + Gtk.drag_dest_set ( this, Gtk.DestDefaults.MOTION, accepted_targets, Gdk.DragAction.MOVE ); - + this.drag_motion.connect(onDragMotion); this.drag_leave.connect(onDragLeave); this.drag_drop.connect(onDragDrop); @@ -200,89 +200,89 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { } } } - + ~CategoryRow() { activateUnreadEventbox(false); - + m_expandBox.button_press_event.disconnect(onExpandClick); m_expandBox.enter_notify_event.disconnect(onExpandEnter); m_expandBox.leave_notify_event.disconnect(onExpandLeave); - + m_eventBox.button_press_event.disconnect(onClick); - + this.drag_begin.disconnect(onDragBegin); this.drag_data_get.disconnect(onDragDataGet); - + this.drag_motion.disconnect(onDragMotion); this.drag_leave.disconnect(onDragLeave); this.drag_drop.disconnect(onDragDrop); this.drag_data_received.disconnect(onDragDataReceived); - + this.drag_motion.disconnect(onDragMotion); this.drag_leave.disconnect(onDragLeave); this.drag_drop.disconnect(onDragDrop); this.drag_data_received.disconnect(onDragDataReceived); } - + //------------- Drag Source Functions ---------------------------------------------- - + private void onDragBegin(Gtk.Widget widget, Gdk.DragContext context) { Logger.debug("categoryRow: onDragBegin"); Gtk.drag_set_icon_widget(context, getDragWindow(), 0, 0); - + } - + public void onDragDataGet(Gtk.Widget widget, Gdk.DragContext context, Gtk.SelectionData selection_data, uint target_type, uint time) { Logger.debug("categoryRow: onDragDataGet"); - + if(target_type == DragTarget.CAT) { selection_data.set_text(m_categorieID, -1); } } - - + + //------------- Drag Target Functions ---------------------------------------------- - + private bool onDragMotion(Gtk.Widget widget, Gdk.DragContext context, int x, int y, uint time) { this.set_state_flags(Gtk.StateFlags.PRELIGHT, false); return true; } - + private void onDragLeave(Gtk.Widget widget, Gdk.DragContext context, uint time) { this.unset_state_flags(Gtk.StateFlags.PRELIGHT); } - - + + private bool onDragDrop(Gtk.Widget widget, Gdk.DragContext context, int x, int y, uint time) { Logger.debug("categoryRow: onDragDrop"); - + // If the source offers a target if(context.list_targets() != null) { var target_type = (Gdk.Atom)context.list_targets().nth_data(0); - + // Request the data from the source. Gtk.drag_get_data(widget, context, target_type, time); return true; } - + return false; } - + private void onDragDataReceived(Gtk.Widget widget, Gdk.DragContext context, int x, int y, Gtk.SelectionData selection_data, uint target_type, uint time) { Logger.debug("categoryRow: onDragDataReceived"); - + var dataString = selection_data.get_text(); - + if(dataString != null && selection_data.get_length() >= 0) { @@ -293,7 +293,7 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { string[] data = dataString.split(","); string feedID = data[0]; string currentCat = data[1]; - + showRenamePopover(context, time, feedID, currentCat); } else if(target_type == DragTarget.CAT) @@ -309,7 +309,7 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { string feedID = data[0]; string currentCat = data[1]; Logger.debug("drag feedID: " + feedID + " currentCat: " + currentCat); - + if(currentCat == m_categorieID) { Logger.debug("categoryRow: drag current parent -> drag_failed"); @@ -320,13 +320,13 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { { FeedReaderBackend.get_default().moveFeed(feedID, currentCat, m_categorieID); } - + Gtk.drag_finish(context, true, false, time); } else if(target_type == DragTarget.CAT) { Logger.debug("drag catID: " + dataString); - + if(dataString == m_categorieID) { Logger.debug("categoryRow: drag on self -> drag_failed"); @@ -337,14 +337,14 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { { FeedReaderBackend.get_default().moveCategory(dataString, m_categorieID); } - + Gtk.drag_finish(context, true, false, time); } } } - + } - + private Gtk.Window getDragWindow() { var window = new Gtk.Window(Gtk.WindowType.POPUP); @@ -359,7 +359,7 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { window.show_all(); return window; } - + private bool onClick(Gdk.EventButton event) { // only right click allowed @@ -367,13 +367,13 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { { return false; } - + if(!Utils.canManipulateContent()) { return false; } - - + + switch(event.type) { case Gdk.EventType.BUTTON_RELEASE: @@ -381,25 +381,25 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { case Gdk.EventType.@3BUTTON_PRESS: return false; } - + var remove_action = new GLib.SimpleAction("deleteCat", null); remove_action.activate.connect(() => { bool wasExpanded = false; - + if(!m_collapsed) { wasExpanded = true; expand_collapse(); } - + if(this.is_selected()) { moveUP(); } - + uint time = 300; this.reveal(false, time); - + string text = _("Category \"%s\" removed").printf(m_name); var notification = MainWindow.get_default().showNotification(text); ulong eventID = notification.dismissed.connect(() => { @@ -421,12 +421,12 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { { expand_collapse(); } - + if(this.is_selected()) { moveUP(); } - + uint time = 300; this.reveal(false, time); GLib.Timeout.add(time, () => { @@ -450,19 +450,19 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { rename_action.activate.connect(() => { showRenamePopover(); }); - + var app = FeedReaderApp.get_default(); app.add_action(markAsRead_action); app.add_action(rename_action); app.add_action(remove_action); app.add_action(removeWithChildren_action); - + var menu = new GLib.Menu(); menu.append(_("Mark as read"), "markCatAsRead"); menu.append(_("Rename"), "renameCat"); menu.append(_("Remove"), "deleteCat"); menu.append(_("Remove (with Feeds)"), "deleteAllCat"); - + var pop = new Gtk.Popover(this); pop.set_position(Gtk.PositionType.BOTTOM); pop.bind_model(menu, "app"); @@ -471,10 +471,10 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { }); pop.show(); this.set_state_flags(Gtk.StateFlags.PRELIGHT, false); - + return true; } - + private void showRenamePopover(Gdk.DragContext? context = null, uint time = 0, string? id1 = null, string? id2 = null) { var popRename = new Gtk.Popover(this); @@ -486,7 +486,7 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { this.drag_failed(context, Gtk.DragResult.NO_TARGET); } }); - + var renameEntry = new Gtk.Entry(); renameEntry.set_text(m_name); renameEntry.activate.connect(() => { @@ -498,46 +498,46 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { { Logger.debug("categoryRow: create new Category " + renameEntry.get_text()); m_categorieID = FeedReaderBackend.get_default().addCategory(renameEntry.get_text(), "", true); - + if(id2 == null) // move feed - + { FeedReaderBackend.get_default().moveCategory(id1, m_categorieID); } else // move category - + { FeedReaderBackend.get_default().moveFeed(id1, id2, m_categorieID); } - + Gtk.drag_finish(context, true, false, time); } - + popRename.hide(); }); - + string label = _("rename"); if(m_categorieID == CategoryID.NEW.to_string() && context != null) { label = _("add"); } - + var renameButton = new Gtk.Button.with_label(label); renameButton.get_style_context().add_class("suggested-action"); renameButton.clicked.connect(() => { renameEntry.activate(); }); - + var renameBox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 5); renameBox.margin = 5; renameBox.pack_start(renameEntry, true, true, 0); renameBox.pack_start(renameButton, false, false, 0); - + popRename.add(renameBox); popRename.show_all(); this.set_state_flags(Gtk.StateFlags.PRELIGHT, false); } - + private bool onUnreadClick(Gdk.EventButton event) { if(m_unreadHovered && m_unread_count > 0) @@ -546,7 +546,7 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { } return true; } - + private bool onUnreadEnter(Gdk.EventCrossing event) { m_unreadHovered = true; @@ -556,7 +556,7 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { } return true; } - + private bool onUnreadLeave(Gdk.EventCrossing event) { m_unreadHovered = false; @@ -570,7 +570,7 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { } return true; } - + private bool onExpandClick(Gdk.EventButton event) { // only accept left mouse button @@ -578,7 +578,7 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { { return false; } - + switch(event.type) { case Gdk.EventType.BUTTON_RELEASE: @@ -589,7 +589,7 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { expand_collapse(); return true; } - + public bool expand_collapse(bool selectParent = true) { if(m_collapsed) @@ -602,11 +602,11 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { m_collapsed = true; m_stack.set_visible_child_name("collapsed"); } - + collapse(m_collapsed, m_categorieID, selectParent); return true; } - + private bool onExpandEnter(Gdk.EventCrossing event) { m_hovered = true; @@ -614,7 +614,7 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { m_icon_collapsed.opacity = 1.0; return true; } - + private bool onExpandLeave(Gdk.EventCrossing event) { if(event.detail != Gdk.NotifyType.VIRTUAL @@ -622,18 +622,18 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { { return false; } - + m_hovered = false; - + m_icon_expanded.opacity = m_opacity; m_icon_collapsed.opacity = m_opacity; return true; } - + public void set_unread_count(uint unread_count) { m_unread_count = unread_count; - + if(m_unread_count > 0 && !m_unreadHovered) { m_unreadStack.set_visible_child_name("unreadCount"); @@ -648,12 +648,12 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { m_unreadStack.set_visible_child_name("mark"); } } - + public void upUnread() { set_unread_count(m_unread_count+1); } - + public void downUnread() { if(m_unread_count > 0) @@ -661,68 +661,68 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { set_unread_count(m_unread_count-1); } } - + public string getID() { return m_categorieID; } - + public string getName() { return m_name; } - + public string getParent() { return m_parentID; } - + public int getOrder() { return m_orderID; } - + public int getLevel() { return m_level; } - + public void setExist(bool exists) { m_exists = exists; } - + public bool doesExist() { return m_exists; } - + public bool isExpanded() { return !m_collapsed; } - + public uint getUnreadCount() { return m_unread_count; } - + public bool isRevealed() { return m_revealer.get_reveal_child(); } - + public void reveal(bool reveal, uint duration = 500) { if(!reveal && this.is_selected()) { deselectRow(); } - + m_revealer.set_transition_duration(duration); m_revealer.set_reveal_child(reveal); } - + public void activateUnreadEventbox(bool activate) { if(activate) @@ -738,5 +738,5 @@ public class FeedReader.CategoryRow : Gtk.ListBoxRow { m_unreadBox.leave_notify_event.disconnect(onUnreadLeave); } } - + } diff --git a/src/Widgets/ColorCircle.vala b/src/Widgets/ColorCircle.vala index f1ebb712..024acf08 100644 --- a/src/Widgets/ColorCircle.vala +++ b/src/Widgets/ColorCircle.vala @@ -14,42 +14,42 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.ColorCircle : Gtk.EventBox { - + private Gtk.Image m_icon; private Gtk.Image m_icon_light; private int m_color; public signal void clicked(int color); - + public ColorCircle(int color, bool clickable = true) { m_color = color; m_icon = new Gtk.Image.from_surface(drawIcon()); m_icon_light = new Gtk.Image.from_surface(drawIcon(true)); - + this.set_events(Gdk.EventMask.BUTTON_PRESS_MASK); this.set_events(Gdk.EventMask.ENTER_NOTIFY_MASK); this.set_events(Gdk.EventMask.LEAVE_NOTIFY_MASK); this.set_size_request(16, 16); - + if(clickable) { this.enter_notify_event.connect(IconEnter); this.leave_notify_event.connect(IconLeave); this.button_press_event.connect(IconClicked); } - + this.add(m_icon); this.show_all(); } - + public void newColor(int color) { m_color = color; m_icon.set_from_surface(drawIcon()); m_icon_light.set_from_surface(drawIcon(true)); } - - + + private bool IconEnter() { this.remove(m_icon); @@ -57,7 +57,7 @@ public class FeedReader.ColorCircle : Gtk.EventBox { this.show_all(); return true; } - + private bool IconLeave() { this.remove(m_icon_light); @@ -65,14 +65,14 @@ public class FeedReader.ColorCircle : Gtk.EventBox { this.show_all(); return true; } - + private bool IconClicked(Gdk.EventButton event) { if(event.button != 1) { return false; } - + switch(event.type) { case Gdk.EventType.BUTTON_RELEASE: @@ -80,13 +80,13 @@ public class FeedReader.ColorCircle : Gtk.EventBox { case Gdk.EventType.@3BUTTON_PRESS: return false; } - + Logger.debug("ColorCircle: click"); clicked(m_color); return true; } - - + + private Cairo.Surface drawIcon(bool light = false) { int scaleFactor = this.get_scale_factor(); @@ -98,22 +98,22 @@ public class FeedReader.ColorCircle : Gtk.EventBox { { lighten = 0.7; } - + var surface = this.get_window().create_similar_image_surface(0, size, size, 0); Cairo.Context context = new Cairo.Context(surface); - + context.set_line_width(2); context.set_fill_rule(Cairo.FillRule.EVEN_ODD); - + double half = size/(2*scaleFactor); context.set_source_rgba(color.red, color.green, color.blue, 0.6*lighten); context.arc(half, half, half, 0, 2*Math.PI); context.fill_preserve(); - + context.arc(half, half, half-(half/4), 0, 2*Math.PI); context.set_source_rgba(color.red, color.green, color.blue, 0.6*lighten); context.fill_preserve(); - + return surface; } } diff --git a/src/Widgets/ColorPopover.vala b/src/Widgets/ColorPopover.vala index 695ff34c..a4ed993b 100644 --- a/src/Widgets/ColorPopover.vala +++ b/src/Widgets/ColorPopover.vala @@ -16,8 +16,8 @@ public class FeedReader.ColorPopover : Gtk.Popover { private Gtk.Grid m_grid; public signal void newColorSelected(int color); - - + + public ColorPopover(Gtk.Widget widget) { m_grid = new Gtk.Grid(); @@ -32,7 +32,7 @@ public class FeedReader.ColorPopover : Gtk.Popover { int rows = Constants.COLORS.length/4; int color = 0; ColorCircle tmpCircle; - + for(int i = 0; i < rows; ++i) { for(int j = 0; j < columns; ++j) @@ -46,7 +46,7 @@ public class FeedReader.ColorPopover : Gtk.Popover { ++color; } } - + m_grid.show_all(); this.add(m_grid); this.set_modal(true); diff --git a/src/Widgets/ColumnView.vala b/src/Widgets/ColumnView.vala index b6c812af..58e6c142 100644 --- a/src/Widgets/ColumnView.vala +++ b/src/Widgets/ColumnView.vala @@ -14,27 +14,27 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.ColumnView : Gtk.Paned { - + private Gtk.Paned m_pane; private ArticleView m_article_view; private ArticleList m_articleList; private feedList m_feedList; private FeedListFooter m_footer; private ColumnViewHeader m_headerbar; - + private static ColumnView? m_columnView = null; - + public static ColumnView get_default() { if(m_columnView == null) { m_columnView = new ColumnView(); } - + return m_columnView; } - - + + private ColumnView() { Logger.debug("ColumnView: setup"); @@ -43,16 +43,16 @@ public class FeedReader.ColumnView : Gtk.Paned { var feedListBox = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); feedListBox.pack_start(m_feedList); feedListBox.pack_end(m_footer, false, false); - + m_pane = new Gtk.Paned(Gtk.Orientation.HORIZONTAL); m_pane.set_size_request(0, 300); m_pane.set_position(Settings.state().get_int("feed-row-width")); m_pane.pack1(feedListBox, false, false); - + m_feedList.clearSelected.connect(() => { m_footer.setRemoveButtonSensitive(false); }); - + m_feedList.newFeedSelected.connect((feedID) => { Logger.debug("ContentPage: new Feed selected"); m_articleList.setSelectedType(FeedListType.FEED); @@ -61,7 +61,7 @@ public class FeedReader.ColumnView : Gtk.Paned { m_headerbar.clearTitle(); m_articleList.setSelectedFeed(feedID); newArticleList(); - + if(feedID == FeedID.ALL.to_string()) { m_footer.setRemoveButtonSensitive(false); @@ -72,7 +72,7 @@ public class FeedReader.ColumnView : Gtk.Paned { m_footer.setSelectedRow(FeedListType.FEED, feedID); } }); - + m_feedList.newTagSelected.connect((tagID) => { Logger.debug("ContentPage: new Tag selected"); m_articleList.setSelectedType(FeedListType.TAG); @@ -84,7 +84,7 @@ public class FeedReader.ColumnView : Gtk.Paned { m_footer.setRemoveButtonSensitive(true); m_footer.setSelectedRow(FeedListType.TAG, tagID); }); - + m_feedList.newCategorieSelected.connect((categorieID) => { Logger.debug("ContentPage: new Category selected"); m_articleList.setSelectedType(FeedListType.CATEGORY); @@ -93,7 +93,7 @@ public class FeedReader.ColumnView : Gtk.Paned { m_headerbar.clearTitle(); m_articleList.setSelectedFeed(categorieID); newArticleList(); - + if(categorieID != CategoryID.MASTER.to_string() && categorieID != CategoryID.TAGS.to_string()) { @@ -105,10 +105,10 @@ public class FeedReader.ColumnView : Gtk.Paned { m_footer.setRemoveButtonSensitive(false); } }); - + m_feedList.markAllArticlesAsRead.connect(markAllArticlesAsRead); - - + + m_articleList = new ArticleList(); m_articleList.drag_begin.connect((context) => { if(DataBase.readOnly().read_tags().is_empty) @@ -136,10 +136,10 @@ public class FeedReader.ColumnView : Gtk.Paned { return false; }); setArticleListState((ArticleListState)Settings.state().get_enum("show-articles")); - + m_pane.pack2(m_articleList, false, false); - - + + m_articleList.row_activated.connect((row) => { if(m_article_view.getCurrentArticle() != row.getID()) { @@ -153,10 +153,10 @@ public class FeedReader.ColumnView : Gtk.Paned { m_article_view.showMediaButton(row.haveMedia()); } }); - + m_article_view = new ArticleView(); - - + + this.orientation = Gtk.Orientation.HORIZONTAL; this.set_position(Settings.state().get_int("feeds-and-articles-width")); this.pack1(m_pane, false, false); @@ -164,84 +164,84 @@ public class FeedReader.ColumnView : Gtk.Paned { this.notify["position"].connect(() => { m_headerbar.set_position(this.get_position()); }); - + m_headerbar = new ColumnViewHeader(); m_headerbar.refresh.connect(() => { syncStarted(); var app = FeedReaderApp.get_default(); app.sync(); }); - + m_headerbar.cancel.connect(() => { FeedReaderApp.get_default().cancelSync(); }); - + m_headerbar.change_state.connect((state, transition) => { setArticleListState(state); clearArticleView(); newArticleList(transition); }); - + m_headerbar.search_term.connect((searchTerm) => { Logger.debug("MainWindow: new search term"); setSearchTerm(searchTerm); clearArticleView(); newArticleList(); }); - + m_headerbar.notify["position"].connect(() => { this.set_position(m_headerbar.get_position()); }); - + m_headerbar.toggledMarked.connect(() => { toggleMarkedSelectedArticle(); }); - + m_headerbar.toggledRead.connect(() => { toggleReadSelectedArticle(); }); } - + public void hidePane() { m_pane.set_visible(false); } - + public void showPane() { m_pane.set_visible(true); } - + public int ArticleListNEXT() { if(m_article_view.fullscreenArticle()) { m_article_view.setTransition(Gtk.StackTransitionType.SLIDE_LEFT, 500); } - + return m_articleList.move(false); } - + public int ArticleListPREV() { if(m_article_view.fullscreenArticle()) { m_article_view.setTransition(Gtk.StackTransitionType.SLIDE_RIGHT, 500); } - + return m_articleList.move(true); } - + public void FeedListNEXT() { m_feedList.move(false); } - + public void FeedListPREV() { m_feedList.move(true); } - + public void newArticleList(Gtk.StackTransitionType transition = Gtk.StackTransitionType.CROSSFADE) { Logger.debug("ContentPage.newArticleList"); @@ -260,140 +260,140 @@ public class FeedReader.ColumnView : Gtk.Paned { m_articleList.newList(transition); } } - + public void newFeedList(bool defaultSettings = false) { m_feedList.newFeedlist(m_articleList.getState(), defaultSettings); } - + public void refreshFeedListCounter() { m_feedList.refreshCounters(m_articleList.getState()); } - + public void reloadArticleView() { m_article_view.load(); } - + public void updateArticleList() { m_articleList.updateArticleList(); } - + private void setArticleListState(ArticleListState state) { var oldState = m_articleList.getState(); m_articleList.setState(state); - + if(oldState == ArticleListState.MARKED || state == ArticleListState.MARKED) { m_feedList.refreshCounters(state); } } - + private void setSearchTerm(string searchTerm) { m_articleList.setSearchTerm(searchTerm); m_article_view.setSearchTerm(searchTerm); } - + private void clearArticleView() { m_headerbar.showArticleButtons(false); m_headerbar.clearTitle(); m_article_view.clearContent(); } - + public string getSelectedFeedListRow() { return m_feedList.getSelectedRow(); } - + public Article getSelectedArticle() { return m_articleList.getSelectedArticle(); } - + public void markAllArticlesAsRead() { m_headerbar.setRead(ArticleStatus.READ); m_articleList.markAllAsRead(); } - + public void toggleReadSelectedArticle() { m_headerbar.toggleRead(); m_article_view.setRead(m_articleList.toggleReadSelected()); } - + public void toggleMarkedSelectedArticle() { m_headerbar.toggleMarked(); m_article_view.setMarked(m_articleList.toggleMarkedSelected()); } - + public void openSelectedArticle() { m_articleList.openSelected(); } - + public void centerSelectedRow() { m_articleList.centerSelectedRow(); } - + public void removeTagFromSelectedRow(string tagID) { m_articleList.removeTagFromSelectedRow(tagID); } - + public void syncStarted() { m_articleList.syncStarted(); } - + public void syncFinished() { m_articleList.syncFinished(); } - + public Gdk.RGBA getBackgroundColor() { return m_articleList.getBackgroundColor(); } - + public void showArticleListOverlay() { m_articleList.showOverlay(); } - + public void setOffline() { m_headerbar.setOffline(); m_feedList.setOffline(); - + if(!Utils.canManipulateContent(false)) { m_footer.setAddButtonSensitive(false); m_feedList.newFeedlist(m_articleList.getState(), false); } } - + public void setOnline() { m_headerbar.setOnline(); m_feedList.setOnline(); - + if(Utils.canManipulateContent(true)) { m_footer.setAddButtonSensitive(true); m_feedList.newFeedlist(m_articleList.getState(), false); - + var selected_row = m_feedList.getSelectedRow(); string[] selected = selected_row.split(" "); - + if((selected[0] == "feed" && selected[1] == FeedID.ALL.to_string()) || (selected[0] == "cat" && (selected[1] == CategoryID.MASTER.to_string() || selected[1] == CategoryID.TAGS.to_string()))) { @@ -401,93 +401,93 @@ public class FeedReader.ColumnView : Gtk.Paned { } } } - + public void footerSetBusy() { m_footer.setBusy(); } - + public void footerSetReady() { m_footer.setReady(); } - + public void footerShowError(string errmsg) { m_footer.showError(errmsg); } - + public feedList getFeedList() { return m_feedList; } - + public void enterFullscreenArticle() { m_article_view.enterFullscreenArticle(); } - + public void leaveFullscreenArticle() { m_article_view.leaveFullscreenArticle(); } - + public bool isFullscreen() { return m_article_view.fullscreenArticle(); } - + public void exitFullscreenVideo() { m_article_view.exitFullscreenVideo(); } - + public bool isFullscreenVideo() { return m_article_view.fullscreenVideo(); } - + public bool ArticleListSelectedIsFirst() { return m_articleList.selectedIsFirst(); } - + public bool ArticleListSelectedIsLast() { return m_articleList.selectedIsLast(); } - + public void ArticleViewAddMedia(MediaPlayer media) { m_article_view.addMedia(media); } - + public void articleViewKillMedia() { m_article_view.killMedia(); } - + public void print() { m_article_view.print(); } - + public bool playingMedia() { return m_article_view.playingMedia(); } - + public string? displayedArticle() { return m_article_view.getCurrentArticle(); } - + public void saveState(ref InterfaceState state) { int offset = 0; double scrollPos = 0.0; m_articleList.getSavedState(out scrollPos, out offset); - + state.setArticleListScrollPos(scrollPos); state.setArticleListRowOffset(offset); state.setFeedListSelectedRow(m_feedList.getSelectedRow()); @@ -502,25 +502,25 @@ public class FeedReader.ColumnView : Gtk.Paned { state.setArticleListSelectedRow(selectedArticle.getArticleID()); } state.setArticleListTopRow(m_articleList.getFirstArticle()); - + m_headerbar.saveState(ref state); } - + public bool searchFocused() { return m_headerbar.searchFocused(); } - + public ColumnViewHeader getHeader() { return m_headerbar; } - + public void ArticleViewSendEvent(Gdk.EventKey event) { m_article_view.sendEvent(event); } - + public void clear() { m_articleList.clear(); diff --git a/src/Widgets/ColumnViewHeader.vala b/src/Widgets/ColumnViewHeader.vala index f1b2b391..4e6aa43f 100644 --- a/src/Widgets/ColumnViewHeader.vala +++ b/src/Widgets/ColumnViewHeader.vala @@ -14,7 +14,7 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.ColumnViewHeader : Gtk.Paned { - + private ModeButton m_modeButton; private UpdateButton m_refresh_button; private Gtk.SearchEntry m_search; @@ -27,18 +27,18 @@ public class FeedReader.ColumnViewHeader : Gtk.Paned { public signal void search_term(string searchTerm); public signal void toggledMarked(); public signal void toggledRead(); - - + + public ColumnViewHeader() { m_state = (ArticleListState)Settings.state().get_enum("show-articles"); - + m_modeButton = new ModeButton(); m_modeButton.append_text(_("All"), _("Show all articles")); m_modeButton.append_text(_("Unread"), _("Show only unread articles")); m_modeButton.append_text(_("Starred"), _("Show only starred articles")); m_modeButton.set_active(m_state, true); - + m_modeButton.mode_changed.connect(() => { var transition = Gtk.StackTransitionType.CROSSFADE; if(m_state == ArticleListState.ALL @@ -51,11 +51,11 @@ public class FeedReader.ColumnViewHeader : Gtk.Paned { { transition = Gtk.StackTransitionType.SLIDE_RIGHT; } - + m_state = (ArticleListState)m_modeButton.selected; change_state(m_state, transition); }); - + bool updating = Settings.state().get_boolean("currently-updating"); m_refresh_button = new UpdateButton.from_icon_name("feed-refresh-symbolic", _("Update feeds"), true, true); m_refresh_button.updating(updating); @@ -70,16 +70,16 @@ public class FeedReader.ColumnViewHeader : Gtk.Paned { m_refresh_button.setSensitive(false); } }); - - - + + + m_search = new Gtk.SearchEntry(); m_search.placeholder_text = _("Search Articles"); if(Settings.tweaks().get_boolean("restore-searchterm")) { m_search.text = Settings.state().get_string("search-term"); } - + // connect after 160ms because Gtk.SearchEntry fires search_changed with 150ms delay // with the timeout the signal should not trigger a newList() when restoring the state at startup GLib.Timeout.add(160, () => { @@ -88,23 +88,23 @@ public class FeedReader.ColumnViewHeader : Gtk.Paned { }); return false; }); - + m_header_left = new Gtk.HeaderBar(); m_header_left.show_close_button = true; m_header_left.get_style_context().add_class("header_right"); m_header_left.get_style_context().add_class("titlebar"); m_header_left.set_size_request(500, 0); - + var menubutton = new Gtk.MenuButton(); menubutton.image = new Gtk.Image.from_icon_name("open-menu-symbolic", Gtk.IconSize.MENU); menubutton.set_use_popover(true); menubutton.set_menu_model(Utils.getMenu()); m_header_left.pack_end(menubutton); - + m_header_left.pack_end(m_search); m_header_left.pack_start(m_modeButton); m_header_left.pack_start(m_refresh_button); - + m_header_right = new ArticleViewHeader("view-fullscreen-symbolic", _("Read article fullscreen")); m_header_right.show_close_button = true; m_header_right.get_style_context().add_class("header_left"); @@ -122,18 +122,18 @@ public class FeedReader.ColumnViewHeader : Gtk.Paned { ColumnView.get_default().enterFullscreenArticle(); MainWindow.get_default().fullscreen(); }); - + Gtk.Settings.get_default().notify["gtk-decoration-layout"].connect(set_window_buttons); realize.connect(set_window_buttons); set_window_buttons(); - - + + this.pack1(m_header_left, true, false); this.pack2(m_header_right, true, false); this.get_style_context().add_class("headerbar_pane"); this.set_position(Settings.state().get_int("feeds-and-articles-width")); } - + private void set_window_buttons() { string[] buttons = Gtk.Settings.get_default().gtk_decoration_layout.split(":"); @@ -142,16 +142,16 @@ public class FeedReader.ColumnViewHeader : Gtk.Paned { buttons = {buttons[0], ""}; Logger.warning("gtk_decoration_layout in unexpected format"); } - + m_header_left.set_decoration_layout(buttons[0] + ":"); m_header_right.set_decoration_layout(":" + buttons[1]); } - + public void setRefreshButton(bool status) { m_refresh_button.updating(status, false); } - + public void setButtonsSensitive(bool sensitive) { Logger.debug("HeaderBar: setButtonsSensitive %s".printf(sensitive ? "true" : "false")); @@ -159,67 +159,67 @@ public class FeedReader.ColumnViewHeader : Gtk.Paned { m_refresh_button.setSensitive(sensitive); m_search.sensitive = sensitive; } - + public void showArticleButtons(bool show) { m_header_right.showArticleButtons(show); } - + public bool searchFocused() { return m_search.has_focus; } - + public void setMarked(ArticleStatus marked) { m_header_right.setMarked(marked); } - + public void toggleMarked() { m_header_right.toggleMarked(); } - + public void setRead(ArticleStatus read) { m_header_right.setRead(read); } - + public void toggleRead() { m_header_right.toggleRead(); } - + public void focusSearch() { m_search.grab_focus(); } - + public void setOffline() { m_header_right.setOffline(); } - + public void setOnline() { m_header_right.setOnline(); } - + public void showMediaButton(bool show) { m_header_right.showMediaButton(show); } - + public void updateSyncProgress(string progress) { m_refresh_button.setProgress(progress); } - + public void refreshSahrePopover() { m_header_right.refreshSahrePopover(); } - + public void saveState(ref InterfaceState state) { state.setSearchTerm(m_search.text); @@ -233,5 +233,5 @@ public class FeedReader.ColumnViewHeader : Gtk.Paned { { m_header_right.set_title("FeedReader"); } - + } diff --git a/src/Widgets/FeedList.vala b/src/Widgets/FeedList.vala index be2d6192..9d814805 100644 --- a/src/Widgets/FeedList.vala +++ b/src/Widgets/FeedList.vala @@ -14,7 +14,7 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.feedList : Gtk.ScrolledWindow { - + private Gtk.ListBox m_list; private string? m_selectedID = null; private FeedListType m_selectedType = FeedListType.ALL_FEEDS; @@ -29,7 +29,7 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { public signal void newCategorieSelected(string categorieID); public signal void markAllArticlesAsRead(); public signal void clearSelected(); - + public feedList () { m_spinner = new Gtk.Spinner(); m_list = new Gtk.ListBox(); @@ -39,10 +39,10 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { var feedlist_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); feedlist_box.pack_start(m_branding, false, false, 0); feedlist_box.pack_start(m_list); - + this.set_size_request(100, 0); this.add(feedlist_box); - + m_list.row_activated.connect(() => { FeedRow selected_feed = m_list.get_selected_row() as FeedRow; if(selected_feed != null) @@ -52,13 +52,13 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { { return; } - + m_selectedID = selected_feed.getID(); m_selectedType = FeedListType.FEED; newFeedSelected(selected_feed.getID()); return; } - + CategoryRow selected_categorie = m_list.get_selected_row() as CategoryRow; if(selected_categorie != null) { @@ -67,13 +67,13 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { { return; } - + m_selectedID = selected_categorie.getID(); m_selectedType = FeedListType.CATEGORY; newCategorieSelected(selected_categorie.getID()); return; } - + TagRow selected_tag = m_list.get_selected_row() as TagRow; if(selected_tag != null) { @@ -82,15 +82,15 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { { return; } - + m_selectedID = selected_tag.getTag().getTagID(); m_selectedType = FeedListType.TAG; newTagSelected(selected_tag.getTag().getTagID()); return; } }); - - + + m_list.key_press_event.connect((event) => { if(event.keyval == Gdk.Key.Down) { @@ -107,12 +107,12 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { { selected_categorie.expand_collapse(); } - + } return true; }); } - + public void collapseSelectedCat() { CategoryRow selected_categorie = m_list.get_selected_row() as CategoryRow; @@ -121,21 +121,21 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { selected_categorie.expand_collapse(); } } - + public void move(bool down) { FeedRow selected_feed = m_list.get_selected_row() as FeedRow; CategoryRow selected_categorie = m_list.get_selected_row() as CategoryRow; TagRow selected_tag = m_list.get_selected_row() as TagRow; - + var FeedListChildren = m_list.get_children(); - + if(!down) { FeedListChildren.reverse(); } - - + + int current = -1; if(selected_feed != null) { @@ -149,14 +149,14 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { { current = FeedListChildren.index(selected_tag); } - + current++; while(current < FeedListChildren.length()) { FeedRow current_feed = FeedListChildren.nth_data(current) as FeedRow; CategoryRow current_categorie = FeedListChildren.nth_data(current) as CategoryRow; TagRow current_tag = FeedListChildren.nth_data(current) as TagRow; - + if(current_feed != null) { if(current_feed.getID() != FeedID.SEPARATOR.to_string() && current_feed.isRevealed() && current_feed.getName() != "") @@ -166,7 +166,7 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { break; } } - + if(current_categorie != null) { if(current_categorie.isRevealed()) @@ -176,7 +176,7 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { break; } } - + if(current_tag != null) { if(current_tag.isRevealed()) @@ -186,25 +186,25 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { break; } } - + current++; } } - - + + public void newFeedlist(ArticleListState state, bool defaultSettings, bool masterCat = false) { Logger.debug("FeedList: new FeedList"); - + if(m_busy) { Logger.debug("FeedList: busy"); return; } - + m_busy = true; m_branding.refresh(); - + if(!isEmpty()) { if(!defaultSettings) @@ -212,15 +212,15 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { Settings.state().set_strv("expanded-categories", getExpandedCategories()); Settings.state().set_string("feedlist-selected-row", getSelectedRow()); } - + Settings.state().set_double("feed-row-scrollpos", vadjustment.value); } - + clear(); createFeedlist(state, defaultSettings, masterCat); m_busy = false; } - + public void clear() { var FeedChildList = m_list.get_children(); @@ -230,7 +230,7 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { row.destroy(); } } - + private bool isEmpty() { var FeedChildList = m_list.get_children(); @@ -238,15 +238,15 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { { return true; } - + return false; } - - + + private void createFeedlist(ArticleListState state, bool defaultSettings, bool masterCat) { var separatorFeed = new Feed(FeedID.SEPARATOR.to_string(), "", "", 0); - + uint unread = 0; if(state == ArticleListState.MARKED) { @@ -257,7 +257,7 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { unread = DataBase.readOnly().get_unread_total(); } var allFeed = new Feed(FeedID.ALL.to_string(), _("All Articles"), "", unread); - + var row_separator1 = new FeedRow(separatorFeed, "-1", 0); var separator1 = new Gtk.Separator(Gtk.Orientation.HORIZONTAL); separator1.get_style_context().add_class("fr-sidebar-separator"); @@ -265,8 +265,8 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { row_separator1.add(separator1); row_separator1.sensitive = false; m_list.add(row_separator1); - - + + var row_all = new FeedRow(allFeed, "-1", 0); row_all.margin_top = 8; row_all.margin_bottom = 8; @@ -274,7 +274,7 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { row_all.activateUnreadEventbox((state == ArticleListState.MARKED) ? false : true); row_all.setAsRead.connect(markSelectedRead); row_all.reveal(true, 0); - + var row_separator = new FeedRow(separatorFeed, "-1", 0); var separator = new Gtk.Separator(Gtk.Orientation.HORIZONTAL); separator.get_style_context().add_class("fr-sidebar-separator"); @@ -282,17 +282,17 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { row_separator.add(separator); row_separator.sensitive = false; m_list.add(row_separator); - + //------------------------------------------------------------------- - + var feeds = DataBase.readOnly().read_feeds((state == ArticleListState.MARKED) ? true : false); - + if(!Utils.onlyShowFeeds()) { createCategories(ref feeds, masterCat, state); createTags(); } - + foreach(var item in feeds) { if(!Utils.onlyShowFeeds()) @@ -303,7 +303,7 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { { pos++; var tmpRow = row as CategoryRow; - + if(tmpRow != null) { if(item.getCatIDs().contains(tmpRow.getID()) @@ -351,20 +351,20 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { feedrow.activateUnreadEventbox((state == ArticleListState.MARKED) ? false : true); } } - + initCollapseCategories(); restoreSelectedRow(); this.show_all(); vadjustment.notify["upper"].connect(restoreScrollPos); } - + private void restoreSelectedRow() { Logger.debug("FeedList.restoreSelectedRow: " + Settings.state().get_string("feedlist-selected-row")); string[] selectedRow = Settings.state().get_string("feedlist-selected-row").split(" ", 2); - + var FeedChildList = m_list.get_children(); - + if(selectedRow[0] == "feed") { foreach(Gtk.Widget row in FeedChildList) @@ -381,7 +381,7 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { } } } - + if(selectedRow[0] == "cat") { foreach(Gtk.Widget row in FeedChildList) @@ -398,7 +398,7 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { } } } - + if(selectedRow[0] == "tag") { foreach(Gtk.Widget row in FeedChildList) @@ -415,8 +415,8 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { } } } - - + + // row not found: default select "ALL ARTICLES" Logger.debug("FeedList: restoreSelectedRow: no selected row found, selectin default"); foreach(Gtk.Widget row in FeedChildList) @@ -431,14 +431,14 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { } } } - - + + void restoreScrollPos(Object sender, ParamSpec property) { vadjustment.notify["upper"].disconnect(restoreScrollPos); vadjustment.set_value(Settings.state().get_double("feed-row-scrollpos")); } - + private void addMasterCategory(int length, string name) { var CategoryRow = new CategoryRow( @@ -467,7 +467,7 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { CategoryRow.moveUP.connect(moveUP); CategoryRow.reveal(true, 0); } - + private void addTagCategory(int length) { var tagrow = new CategoryRow( @@ -496,8 +496,8 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { tagrow.reveal(true, 0); m_TagsDisplayed = true; } - - + + private void createCategories(ref Gee.List<Feed> feeds, bool masterCat, ArticleListState state) { var db = DataBase.readOnly(); @@ -507,12 +507,12 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { bool supportCategories = true; bool supportMultiLevelCategories = false; string uncategorizedID = ""; - + supportTags = FeedReaderBackend.get_default().supportTags(); supportCategories = FeedReaderBackend.get_default().supportCategories(); uncategorizedID = FeedReaderBackend.get_default().uncategorizedID(); supportMultiLevelCategories = FeedReaderBackend.get_default().supportMultiLevelCategories(); - + if((supportTags && !db.isTableEmpty("tags")) || masterCat) @@ -530,11 +530,11 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { m_TagsDisplayed = false; Logger.debug("FeedList: no tags"); } - + for(int i = 1; i <= maxCatLevel; i++) { var categories = db.read_categories_level(i, feeds); - + if(db.haveFeedsWithoutCat()) { categories.insert( @@ -549,7 +549,7 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { ) ); } - + foreach(var item in categories) { var FeedChildList = m_list.get_children(); @@ -572,7 +572,7 @@ public class FeedReader.feedList : Gtk.ScrolledWindow { } level++; } - + var CategoryRow = new CategoryRow( item.getTitle(), item.getCatID(), @@ -642,7 +642,7 @@ public void refreshCounters(ArticleListState state) uint allCount = (state == ArticleListState.MARKED) ? db.get_marked_total() : db.get_unread_total(); var feeds = db.read_feeds((state == ArticleListState.MARKED) ? true : false); var categories = db.read_categories(feeds); - + // double-check uint feedCount = 0; foreach(var feed in feeds) @@ -653,9 +653,9 @@ public void refreshCounters(ArticleListState state) { Logger.warning(@"FeedList.refreshCounters: counts don't match - allCount: $allCount - feedCount: $feedCount"); } - + var FeedChildList = m_list.get_children(); - + // update "All Articles" foreach(Gtk.Widget row in FeedChildList) { @@ -667,7 +667,7 @@ public void refreshCounters(ArticleListState state) break; } } - + // update feeds foreach(Gtk.Widget row in FeedChildList) { @@ -691,14 +691,14 @@ public void refreshCounters(ArticleListState state) tmpFeedRow.reveal(true); } } - - + + break; } } } } - + // update categories foreach(Gtk.Widget row in FeedChildList) { @@ -722,13 +722,13 @@ public void refreshCounters(ArticleListState state) tmpCatRow.reveal(true); } } - + break; } } } } - + if(db.haveFeedsWithoutCat()) { foreach(Gtk.Widget row in FeedChildList) @@ -750,7 +750,7 @@ public void refreshCounters(ArticleListState state) { tmpCatRow.reveal(true); } - + break; } } @@ -778,7 +778,7 @@ private void collapseCategorieInternal(string catID, bool selectParent, bool ani { var FeedChildList = m_list.get_children(); var selected_row = m_list.get_selected_row(); - + foreach(Gtk.Widget row in FeedChildList) { var tmpFeedRow = row as FeedRow; @@ -799,13 +799,13 @@ private void collapseCategorieInternal(string catID, bool selectParent, bool ani tmpTagRow.reveal(false, animationTime); } } - + if(selectParent) { var selected_feed = selected_row as FeedRow; var selected_cat = selected_row as CategoryRow; var selected_tag = selected_row as TagRow; - + if((selected_feed != null && !selected_feed.isRevealed()) || (selected_cat != null && !selected_cat.isRevealed()) || (selected_tag != null && !selected_tag.isRevealed())) @@ -829,7 +829,7 @@ private void collapseCategorieInternal(string catID, bool selectParent, bool ani private void expandCategorieInternal(string catID) { var FeedChildList = m_list.get_children(); - + foreach(Gtk.Widget row in FeedChildList) { var tmpFeedRow = row as FeedRow; @@ -881,7 +881,7 @@ public void expand_collapse_category(string catID, bool expand = true) private bool isCategorieExpanded(string catID) { var FeedChildList = m_list.get_children(); - + foreach(Gtk.Widget row in FeedChildList) { var tmpCatRow = row as CategoryRow; @@ -890,7 +890,7 @@ private bool isCategorieExpanded(string catID) return true; } } - + return false; } @@ -902,7 +902,7 @@ public string getSelectedFeed() { return selected_row.getID(); } - + return ""; } @@ -911,7 +911,7 @@ public string[] getExpandedCategories() { var FeedChildList = m_list.get_children(); string[] e = {}; - + foreach(Gtk.Widget row in FeedChildList) { var tmpCatRow = row as CategoryRow; @@ -932,7 +932,7 @@ public string getSelectedRow() var feedrow = m_list.get_selected_row() as FeedRow; var catrow = m_list.get_selected_row() as CategoryRow; var tagrow = m_list.get_selected_row() as TagRow; - + if(feedrow != null) { return "feed " + feedrow.getID(); @@ -945,7 +945,7 @@ public string getSelectedRow() { return "tag " + tagrow.getTag().getTagID(); } - + return ""; } @@ -953,7 +953,7 @@ public string getSelectedRow() private void markSelectedRead(FeedListType type, string id) { Logger.debug("FeedList: mark all articles as read"); - + if(type == FeedListType.FEED) { if(id == FeedID.ALL.to_string()) @@ -986,7 +986,7 @@ private void markSelectedRead(FeedListType type, string id) private bool getCatState(string id) { string[] list = Settings.state().get_strv("expanded-categories"); - + foreach(string str in list) { if(id == str) @@ -994,7 +994,7 @@ private bool getCatState(string id) return true; } } - + return false; } @@ -1029,7 +1029,7 @@ public void copySelectedFeedURL(string feed_id){ { Gdk.Display display = MainWindow.get_default().get_display(); Gtk.Clipboard clipboard = Gtk.Clipboard.get_for_display(display, Gdk.SELECTION_CLIPBOARD); - + clipboard.set_text(feed_url, feed_url.length); } } @@ -1044,7 +1044,7 @@ public void moveUP() public void revealRow(string id, FeedListType type, bool reveal, uint time) { var FeedChildList = m_list.get_children(); - + switch(type) { case FeedListType.CATEGORY: @@ -1099,13 +1099,13 @@ public void addEmptyTagRow() public void removeEmptyTagRow() { Logger.debug("removeEmptyTagRow"); - + if(m_busy) { Logger.debug("FeedList: busy"); return; } - + if(m_emptyTagRow != null) { removeRow(m_emptyTagRow, 250); @@ -1126,7 +1126,7 @@ private void removeRow(Gtk.Widget? row, int duration = 700) var tagRow = row as TagRow; var catRow = row as CategoryRow; var feedRow = row as FeedRow; - + if(tagRow != null) { tagRow.reveal(false, duration); @@ -1157,10 +1157,10 @@ private void removeRow(Gtk.Widget? row, int duration = 700) private void onDragBegin(Gdk.DragContext context) { Logger.debug("FeedList: onDragBegin"); - + // save current state MainWindow.get_default().writeInterfaceState(); - + // collapse all feeds and show all Categories var FeedChildList = m_list.get_children(); foreach(Gtk.Widget row in FeedChildList) @@ -1168,7 +1168,7 @@ private void onDragBegin(Gdk.DragContext context) var tmpCat = row as CategoryRow; var tmpFeed = row as FeedRow; var tmpTag = row as TagRow; - + if(tmpCat != null) { tmpCat.reveal(true); @@ -1192,7 +1192,7 @@ private void showNewCategory() { int level = 1; int pos = -1; - + if(FeedReaderBackend.get_default().supportTags()) { var FeedChildList = m_list.get_children(); @@ -1211,7 +1211,7 @@ private void showNewCategory() { level = 1; } - + var newRow = new CategoryRow(_("New Category"), CategoryID.NEW.to_string(), 99, 0, CategoryID.MASTER.to_string(), level, false); newRow.drag_failed.connect(onDragEnd); m_list.insert(newRow, pos); @@ -1228,7 +1228,7 @@ private bool onDragEnd(Gdk.DragContext context, Gtk.DragResult result) var tmpCat = row as CategoryRow; var tmpFeed = row as FeedRow; var tmpTag = row as TagRow; - + if(tmpCat != null) { if(tmpCat.getID() == CategoryID.NEW.to_string()) @@ -1236,7 +1236,7 @@ private bool onDragEnd(Gdk.DragContext context, Gtk.DragResult result) removeRow(tmpCat, 250); continue; } - + if(tmpCat.getID() != CategoryID.MASTER.to_string() && tmpCat.getID() != CategoryID.TAGS.to_string() && tmpCat.getParent() != CategoryID.MASTER.to_string() @@ -1261,7 +1261,7 @@ private bool onDragEnd(Gdk.DragContext context, Gtk.DragResult result) tmpTag.reveal(true); } } - + return false; } } diff --git a/src/Widgets/FeedListFooter.vala b/src/Widgets/FeedListFooter.vala index 2edcf863..cd85c7f2 100644 --- a/src/Widgets/FeedListFooter.vala +++ b/src/Widgets/FeedListFooter.vala @@ -14,13 +14,13 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.FeedListFooter : Gtk.Box { - + private Gtk.Box m_box; private Gtk.Stack m_addStack; private Gtk.Spinner m_addSpinner; private AddButton m_addButton; private RemoveButton m_removeButton; - + public FeedListFooter() { this.orientation = Gtk.Orientation.VERTICAL; @@ -47,27 +47,27 @@ public class FeedReader.FeedListFooter : Gtk.Box { m_box.pack_start(m_removeButton); this.pack_start(sep2, false, false); this.pack_start(m_box); - + if(!FeedReaderBackend.get_default().supportFeedManipulation()) { m_addButton.set_sensitive(false); m_removeButton.set_sensitive(false); } } - + public void setBusy() { m_addStack.set_visible_child_name("spinner"); m_addStack.show_all(); } - + public void setReady() { m_addStack.set_visible_child_name("button"); m_addSpinner.start(); m_addStack.show_all(); } - + public void setRemoveButtonSensitive(bool sensitive) { if(FeedReaderApp.get_default().isOnline() && FeedReaderBackend.get_default().supportFeedManipulation()) @@ -75,12 +75,12 @@ public class FeedReader.FeedListFooter : Gtk.Box { m_removeButton.set_sensitive(sensitive); } } - + public void setSelectedRow(FeedListType type, string id) { m_removeButton.setSelectedRow(type, id); } - + public void setAddButtonSensitive(bool active) { if(FeedReaderBackend.get_default().supportFeedManipulation()) @@ -89,12 +89,12 @@ public class FeedReader.FeedListFooter : Gtk.Box { m_removeButton.set_sensitive(active); } } - + public void showError(string errmsg) { var label = new Gtk.Label(errmsg); label.margin = 20; - + var pop = new Gtk.Popover(m_addStack); pop.add(label); pop.show_all(); @@ -114,7 +114,7 @@ public class FeedReader.AddButton : Gtk.Button { this.relief = Gtk.ReliefStyle.NONE; this.set_tooltip_text(_("Add feed")); } - + public void onClick() { this.get_style_context().add_class("footer-popover"); @@ -131,7 +131,7 @@ public class FeedReader.AddButton : Gtk.Button { public class FeedReader.RemoveButton : Gtk.Button { private FeedListType m_type; private string m_id; - + public RemoveButton() { var image = new Gtk.Image.from_icon_name("feed-remove-symbolic", Gtk.IconSize.SMALL_TOOLBAR); @@ -143,7 +143,7 @@ public class FeedReader.RemoveButton : Gtk.Button { this.relief = Gtk.ReliefStyle.NONE; this.set_tooltip_text(_("Remove feed")); } - + public void onClick() { this.get_style_context().add_class("footer-popover"); diff --git a/src/Widgets/FeedRow.vala b/src/Widgets/FeedRow.vala index 2cc86000..e730e016 100644 --- a/src/Widgets/FeedRow.vala +++ b/src/Widgets/FeedRow.vala @@ -14,7 +14,7 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.FeedRow : Gtk.ListBoxRow { - + private Feed m_feed; private string m_parentCatID; private Gtk.Box m_box; @@ -32,29 +32,29 @@ public class FeedReader.FeedRow : Gtk.ListBoxRow { public signal void copyFeedURL(string id); public signal void moveUP(); public signal void deselectRow(); - + public FeedRow(Feed feed, string parentCat, int level) { m_subscribed = true; m_parentCatID = parentCat; m_feed = feed; - + if(m_feed.getFeedID() != FeedID.SEPARATOR.to_string()) { var rowhight = 30; m_box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0); m_icon = createFavIcon(); m_icon.margin_start = level * 24; - + m_label = new Gtk.Label(m_feed.getTitle()); m_label.set_size_request (0, rowhight); m_label.set_ellipsize (Pango.EllipsizeMode.END); m_label.set_alignment(0, 0.5f); - + m_unread = new Gtk.Label(null); m_unread.set_size_request (0, rowhight); m_unread.set_alignment(0.8f, 0.5f); - + m_unreadStack = new Gtk.Stack(); m_unreadStack.set_transition_type(Gtk.StackTransitionType.NONE); m_unreadStack.set_transition_duration(0); @@ -63,15 +63,15 @@ public class FeedReader.FeedRow : Gtk.ListBoxRow { var markIcon = new Gtk.Image.from_icon_name("feed-mark-read-symbolic", Gtk.IconSize.LARGE_TOOLBAR); markIcon.get_style_context().add_class("fr-sidebar-symbolic"); m_unreadStack.add_named(markIcon, "mark"); - + m_unreadBox = new Gtk.EventBox(); m_unreadBox.set_events(Gdk.EventMask.BUTTON_PRESS_MASK); m_unreadBox.set_events(Gdk.EventMask.ENTER_NOTIFY_MASK); m_unreadBox.set_events(Gdk.EventMask.LEAVE_NOTIFY_MASK); m_unreadBox.add(m_unreadStack); activateUnreadEventbox(true); - - + + if(!Utils.onlyShowFeeds() && m_feed.getFeedID() != FeedID.ALL.to_string()) { this.get_style_context().add_class("fr-sidebar-feed"); @@ -80,11 +80,11 @@ public class FeedReader.FeedRow : Gtk.ListBoxRow { { this.get_style_context().add_class("fr-sidebar-row"); } - + m_box.pack_start(m_icon, false, false, 8); m_box.pack_start(m_label, true, true, 0); m_box.pack_end (m_unreadBox, false, false, 8); - + m_eventBox = new Gtk.EventBox(); if(m_feed.getFeedID() != FeedID.ALL.to_string()) { @@ -92,7 +92,7 @@ public class FeedReader.FeedRow : Gtk.ListBoxRow { m_eventBox.button_press_event.connect(onClick); } m_eventBox.add(m_box); - + m_revealer = new Gtk.Revealer(); m_revealer.set_transition_type(Gtk.RevealerTransitionType.SLIDE_DOWN); m_revealer.add(m_eventBox); @@ -100,9 +100,9 @@ public class FeedReader.FeedRow : Gtk.ListBoxRow { this.add(m_revealer); this.no_show_all = true;; m_revealer.show_all(); - + set_unread_count(m_feed.getUnread()); - + if(m_feed.getFeedID() != FeedID.ALL.to_string() && !Settings.general().get_boolean("only-feeds") && Utils.canManipulateContent() @@ -111,20 +111,20 @@ public class FeedReader.FeedRow : Gtk.ListBoxRow { const Gtk.TargetEntry[] provided_targets = { { "text/plain", 0, DragTarget.FEED } }; - + Gtk.drag_source_set ( this, Gdk.ModifierType.BUTTON1_MASK, provided_targets, Gdk.DragAction.MOVE ); - + this.drag_begin.connect(onDragBegin); this.drag_data_get.connect(onDragDataGet); } } } - + ~FeedRow() { activateUnreadEventbox(false); @@ -135,29 +135,29 @@ public class FeedReader.FeedRow : Gtk.ListBoxRow { this.drag_begin.disconnect(onDragBegin); this.drag_data_get.disconnect(onDragDataGet); } - + private void onDragBegin(Gtk.Widget widget, Gdk.DragContext context) { Logger.debug("FeedRow: onDragBegin"); Gtk.drag_set_icon_widget(context, getFeedIconWindow(), 0, 0); - + } - + public void onDragDataGet(Gtk.Widget widget, Gdk.DragContext context, Gtk.SelectionData selection_data, uint target_type, uint time) { Logger.debug("FeedRow: onDragDataGet"); - + if(target_type == DragTarget.FEED) { selection_data.set_text(m_feed.getFeedID() + "," + m_parentCatID, -1); } } - + public Gtk.Image createFavIcon() { var icon = new Gtk.Image.from_icon_name("feed-rss-symbolic", Gtk.IconSize.LARGE_TOOLBAR); icon.get_style_context().add_class("fr-sidebar-symbolic"); - + var favicon = FavIcon.for_feed(m_feed); favicon.get_surface.begin((obj, res) => { var surface = favicon.get_surface.end(res); @@ -174,10 +174,10 @@ public class FeedReader.FeedRow : Gtk.ListBoxRow { icon.destroy.connect(() => { favicon.disconnect(handler_id); }); - + return icon; } - + private Gtk.Window getFeedIconWindow() { var icon = createFavIcon(); @@ -189,7 +189,7 @@ public class FeedReader.FeedRow : Gtk.ListBoxRow { window.show_all(); return window; } - + private bool onClick(Gdk.EventButton event) { // only right click allowed @@ -197,12 +197,12 @@ public class FeedReader.FeedRow : Gtk.ListBoxRow { { return false; } - + if(!Utils.canManipulateContent()) { return false; } - + switch(event.type) { case Gdk.EventType.BUTTON_RELEASE: @@ -210,20 +210,20 @@ public class FeedReader.FeedRow : Gtk.ListBoxRow { case Gdk.EventType.@3BUTTON_PRESS: return false; } - + var remove_action = new GLib.SimpleAction("deleteFeed", null); remove_action.activate.connect(RemoveThisFeed); - + var markAsRead_action = new GLib.SimpleAction("markFeedAsRead", null); markAsRead_action.activate.connect(() => { setAsRead(FeedListType.FEED, m_feed.getFeedID()); }); - + var copyFeedURL_action = new GLib.SimpleAction("copyFeedURL", null); copyFeedURL_action.activate.connect(() => { copyFeedURL(m_feed.getFeedID()); }); - + if(m_feed.getUnread() != 0) { markAsRead_action.set_enabled(true); @@ -232,17 +232,17 @@ public class FeedReader.FeedRow : Gtk.ListBoxRow { { markAsRead_action.set_enabled(false); } - + var rename_action = new GLib.SimpleAction("renameFeed", null); rename_action.activate.connect(showRenamePopover); - + FeedReaderApp.get_default().add_action(markAsRead_action); FeedReaderApp.get_default().add_action(copyFeedURL_action); FeedReaderApp.get_default().add_action(rename_action); FeedReaderApp.get_default().add_action(remove_action); - - - + + + var menu = new GLib.Menu(); menu.append(_("Mark as read"), "markFeedAsRead"); menu.append(_("Copy URL"), "copyFeedURL"); @@ -253,19 +253,19 @@ public class FeedReader.FeedRow : Gtk.ListBoxRow { menu.append(_("Remove only from %s").printf(cat.getTitle()), "deleteFeed"); } menu.append(_("Remove"), "deleteFeed"); - + var pop = new Gtk.Popover(this); pop.set_position(Gtk.PositionType.BOTTOM); pop.bind_model(menu, "app"); pop.closed.connect(() => { this.unset_state_flags(Gtk.StateFlags.PRELIGHT); }); - + pop.show(); this.set_state_flags(Gtk.StateFlags.PRELIGHT, false); return true; } - + private void showRenamePopover() { var popRename = new Gtk.Popover(this); @@ -273,34 +273,34 @@ public class FeedReader.FeedRow : Gtk.ListBoxRow { popRename.closed.connect(() => { this.unset_state_flags(Gtk.StateFlags.PRELIGHT); }); - + var renameEntry = new Gtk.Entry(); renameEntry.set_text(m_feed.getTitle()); renameEntry.activate.connect(() => { popRename.hide(); FeedReaderBackend.get_default().renameFeed(m_feed.getFeedID(), renameEntry.get_text()); }); - + var renameButton = new Gtk.Button.with_label(_("rename")); renameButton.get_style_context().add_class("suggested-action"); renameButton.clicked.connect(() => { renameEntry.activate(); }); - + var renameBox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 5); renameBox.margin = 5; renameBox.pack_start(renameEntry, true, true, 0); renameBox.pack_start(renameButton, false, false, 0); - + popRename.add(renameBox); popRename.show_all(); this.set_state_flags(Gtk.StateFlags.PRELIGHT, false); } - + public void set_unread_count(uint unread_count) { m_feed.setUnread(unread_count); - + if(m_feed.getUnread() > 0 && !m_unreadHovered) { m_unreadStack.set_visible_child_name("unreadCount"); @@ -315,7 +315,7 @@ public class FeedReader.FeedRow : Gtk.ListBoxRow { m_unreadStack.set_visible_child_name("mark"); } } - + private bool onUnreadClick(Gdk.EventButton event) { if(m_unreadHovered && m_feed.getUnread() > 0) @@ -324,7 +324,7 @@ public class FeedReader.FeedRow : Gtk.ListBoxRow { } return true; } - + private bool onUnreadEnter(Gdk.EventCrossing event) { m_unreadHovered = true; @@ -334,7 +334,7 @@ public class FeedReader.FeedRow : Gtk.ListBoxRow { } return true; } - + private bool onUnreadLeave(Gdk.EventCrossing event) { m_unreadHovered = false; @@ -348,12 +348,12 @@ public class FeedReader.FeedRow : Gtk.ListBoxRow { } return true; } - + public void upUnread() { set_unread_count(m_feed.getUnread() + 1); } - + public void downUnread() { if(m_feed.getUnread() > 0) @@ -361,48 +361,48 @@ public class FeedReader.FeedRow : Gtk.ListBoxRow { set_unread_count(m_feed.getUnread() - 1); } } - + public void update(string text, uint unread_count) { m_label.set_text(text.replace("&","&")); set_unread_count(unread_count); } - + public void setSubscribed(bool subscribed) { m_subscribed = subscribed; } - + public string getCatID() { return m_parentCatID; } - + public string getID() { return m_feed.getFeedID(); } - + public string getName() { return m_feed.getTitle(); } - + public bool isSubscribed() { return m_subscribed; } - + public uint getUnreadCount() { return m_feed.getUnread(); } - + public bool isRevealed() { return m_revealer.get_reveal_child(); } - + public void reveal(bool reveal, uint duration = 500) { if(m_timeout_source_id > 0) @@ -410,12 +410,12 @@ public class FeedReader.FeedRow : Gtk.ListBoxRow { GLib.Source.remove(m_timeout_source_id); m_timeout_source_id = 0; } - + if(reveal) { this.show(); } - + m_revealer.set_transition_duration(duration); m_revealer.set_reveal_child(reveal); if(!reveal) @@ -424,7 +424,7 @@ public class FeedReader.FeedRow : Gtk.ListBoxRow { { deselectRow(); } - + m_timeout_source_id = GLib.Timeout.add(duration, () => { this.hide(); m_timeout_source_id = 0; @@ -432,14 +432,14 @@ public class FeedReader.FeedRow : Gtk.ListBoxRow { }); } } - + public void activateUnreadEventbox(bool activate) { if(m_unreadBox == null) { return; } - + if(activate) { m_unreadBox.button_press_event.connect(onUnreadClick); @@ -453,17 +453,17 @@ public class FeedReader.FeedRow : Gtk.ListBoxRow { m_unreadBox.leave_notify_event.disconnect(onUnreadLeave); } } - + private void RemoveThisFeed(Variant? parameter) { if(this.is_selected()) { moveUP(); } - + uint time = 300; this.reveal(false, time); - + var notification = MainWindow.get_default().showNotification(_("Feed removed: %s").printf(m_feed.getTitle())); ulong eventID = notification.dismissed.connect(() => { FeedReaderBackend.get_default().removeFeed(m_feed.getFeedID()); diff --git a/src/Widgets/FullscreenButton.vala b/src/Widgets/FullscreenButton.vala index fb9f7447..c5eb60ea 100644 --- a/src/Widgets/FullscreenButton.vala +++ b/src/Widgets/FullscreenButton.vala @@ -14,10 +14,10 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.fullscreenButton : Gtk.Revealer { - + private Gtk.Button m_button; public signal void click(); - + public fullscreenButton(string iconName, Gtk.Align align) { this.valign = Gtk.Align.CENTER; @@ -27,7 +27,7 @@ public class FeedReader.fullscreenButton : Gtk.Revealer { this.no_show_all = true; this.set_transition_type(Gtk.RevealerTransitionType.CROSSFADE); this.set_transition_duration(300); - + m_button = new Gtk.Button.from_icon_name(iconName, Gtk.IconSize.DIALOG); m_button.clicked.connect(() => { click(); @@ -35,7 +35,7 @@ public class FeedReader.fullscreenButton : Gtk.Revealer { m_button.margin = 20; this.add(m_button); } - + public void reveal(bool show) { if(show) @@ -43,8 +43,8 @@ public class FeedReader.fullscreenButton : Gtk.Revealer { this.visible = true; m_button.show(); } - + this.set_reveal_child(show); } - + } diff --git a/src/Widgets/FullscreenHeader.vala b/src/Widgets/FullscreenHeader.vala index abd52ee4..2215e9ae 100644 --- a/src/Widgets/FullscreenHeader.vala +++ b/src/Widgets/FullscreenHeader.vala @@ -14,13 +14,13 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.FullscreenHeader : Gtk.EventBox { - + private Gtk.Revealer m_revealer; private ArticleViewHeader m_header; private bool m_hover = false; private bool m_popover = false; private uint m_timeout_source_id = 0; - + public FullscreenHeader() { m_header = new ArticleViewHeader("view-restore-symbolic", _("Leave fullscreen mode")); @@ -54,7 +54,7 @@ public class FeedReader.FullscreenHeader : Gtk.EventBox { m_revealer.set_transition_duration(300); m_revealer.valign = Gtk.Align.START; m_revealer.add(m_header); - + this.set_size_request(0, 80); this.no_show_all = true; this.enter_notify_event.connect((event) => { @@ -70,20 +70,20 @@ public class FeedReader.FullscreenHeader : Gtk.EventBox { { return false; } - + if(event.detail == Gdk.NotifyType.NONLINEAR_VIRTUAL) { return false; } - + m_hover = false; - + if(m_popover) { return false; } - - + + removeTimeout(); m_timeout_source_id = GLib.Timeout.add(500, () => { m_revealer.set_transition_duration(800); @@ -91,28 +91,28 @@ public class FeedReader.FullscreenHeader : Gtk.EventBox { m_timeout_source_id = 0; return false; }); - + return true; }); this.add(m_revealer); this.valign = Gtk.Align.START; } - + public void setTitle(string title) { m_header.set_title(title); } - + public void setMarked(ArticleStatus marked) { m_header.setMarked(marked); } - + public void setRead(ArticleStatus read) { m_header.setRead(read); } - + private void removeTimeout() { if(m_timeout_source_id > 0) @@ -121,7 +121,7 @@ public class FeedReader.FullscreenHeader : Gtk.EventBox { m_timeout_source_id = 0; } } - + public void showMediaButton(bool show) { m_header.showMediaButton(show); diff --git a/src/Widgets/HoverButton.vala b/src/Widgets/HoverButton.vala index 97352a55..f2ad10e4 100644 --- a/src/Widgets/HoverButton.vala +++ b/src/Widgets/HoverButton.vala @@ -14,14 +14,14 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.HoverButton : Gtk.EventBox { - + private Gtk.Button m_button; private Gtk.Stack m_stack; private Gtk.Image m_inactive; private Gtk.Image m_active; private bool m_isActive; public signal void clicked(bool active); - + public HoverButton(Gtk.Image inactive, Gtk.Image active, bool isActive) { m_inactive = inactive; @@ -35,11 +35,11 @@ public class FeedReader.HoverButton : Gtk.EventBox { toggle(); clicked(m_isActive); }); - + m_stack.add_named(inactive, "inactive"); m_stack.add_named(active, "active"); m_button.add(m_stack); - + if(isActive) { m_stack.set_visible_child_name("active"); @@ -48,39 +48,39 @@ public class FeedReader.HoverButton : Gtk.EventBox { { m_stack.set_visible_child_name("inactive"); } - - - + + + this.set_events(Gdk.EventMask.ENTER_NOTIFY_MASK); this.set_events(Gdk.EventMask.LEAVE_NOTIFY_MASK); this.set_size_request(16, 16); this.add(m_button); - + this.enter_notify_event.connect(onEnter); this.leave_notify_event.connect(onLeave); } - + private void setActiveIcon() { m_stack.set_visible_child_name("active"); m_active.show(); } - + private void setInactiveIcon() { m_stack.set_visible_child_name("inactive"); m_inactive.show(); } - + public void toggle() { setActive(!m_isActive); } - + public void setActive(bool active) { m_isActive = active; - + if(m_isActive) { setActiveIcon(); @@ -90,8 +90,8 @@ public class FeedReader.HoverButton : Gtk.EventBox { setInactiveIcon(); } } - - + + private bool onEnter(Gdk.EventCrossing event) { if(m_isActive) @@ -104,14 +104,14 @@ public class FeedReader.HoverButton : Gtk.EventBox { } return true; } - + private bool onLeave(Gdk.EventCrossing event) { if(event.detail == Gdk.NotifyType.INFERIOR) { return false; } - + if(m_isActive) { setActiveIcon(); @@ -120,9 +120,9 @@ public class FeedReader.HoverButton : Gtk.EventBox { { setInactiveIcon(); } - + return true; } - - + + } diff --git a/src/Widgets/ImagePopup.vala b/src/Widgets/ImagePopup.vala index 64fbe714..bf05cd93 100644 --- a/src/Widgets/ImagePopup.vala +++ b/src/Widgets/ImagePopup.vala @@ -14,7 +14,7 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.imagePopup : Gtk.Window { - + private Gtk.ScrolledWindow m_scroll; private string m_imagePath; private Gtk.ImageView m_image; @@ -43,7 +43,7 @@ public class FeedReader.imagePopup : Gtk.Window { private double m_maxZoom = 5.0; private double m_minZoom = 0.2; private double m_initZoom = 1.0; - + public imagePopup(string imagePath, string? url, Gtk.Window parent, double img_height, double img_width) { this.title = ""; @@ -61,14 +61,14 @@ public class FeedReader.imagePopup : Gtk.Window { } return false; }); - + m_imagePath = imagePath; - + var file = GLib.File.new_for_path(m_imagePath); m_image = new Gtk.ImageView(); m_image.zoomable = true; m_image.load_from_file_async.begin(file, 0); - + m_scale = new Gtk.Scale.with_range(Gtk.Orientation.HORIZONTAL, m_minZoom, m_maxZoom, 0.2); m_scale.draw_value = false; m_scale.set_size_request(200, 0); @@ -76,22 +76,22 @@ public class FeedReader.imagePopup : Gtk.Window { m_scale.value_changed.connect(() => { m_image.scale = m_scale.get_value(); }); - + m_scaleRevealer = new Gtk.Revealer(); m_scaleRevealer.valign = Gtk.Align.FILL; m_scaleRevealer.set_transition_type(Gtk.RevealerTransitionType.SLIDE_RIGHT); m_scaleRevealer.add(m_scale); - + var geo = Gdk.Display.get_default().get_monitor_at_window(this.get_root_window()).get_geometry(); double win_width = (int)(geo.width*0.8); double win_height = (int)(geo.height*0.8); double min_height = 300; double min_widht = 500; - + m_scroll = new Gtk.ScrolledWindow(null, null); m_scroll.add(m_image); - - + + if(img_width <= win_width) { if(img_width < min_widht) @@ -108,7 +108,7 @@ public class FeedReader.imagePopup : Gtk.Window { m_initZoom = win_width/img_width; m_image.scale = m_initZoom; } - + if(img_height * m_initZoom <= win_height) { if(img_height < min_height) @@ -120,12 +120,12 @@ public class FeedReader.imagePopup : Gtk.Window { win_height = img_height * m_initZoom; } } - + m_saveButton = new Gtk.Button.from_icon_name("document-save-symbolic", Gtk.IconSize.BUTTON); m_saveButton.clicked.connect(() => { Utils.saveImageDialog(m_imagePath); }); - + m_image.notify["scale"].connect(onImageScrolled); m_zoomButton = new Gtk.ToggleButton(); m_zoomButton.add(new Gtk.Image.from_icon_name("zoom-in-symbolic", Gtk.IconSize.BUTTON)); @@ -144,7 +144,7 @@ public class FeedReader.imagePopup : Gtk.Window { m_image.scale = m_initZoom; m_scaleRevealer.set_reveal_child(false); } - + if(!m_zoomButton.get_active()) { GLib.Timeout.add(150, () => { @@ -153,7 +153,7 @@ public class FeedReader.imagePopup : Gtk.Window { }); } }); - + var header = new Gtk.HeaderBar (); header.show_close_button = true; header.set_size_request(0, 30); @@ -174,12 +174,12 @@ public class FeedReader.imagePopup : Gtk.Window { { return false; } - + m_hoverHeader = false; return false; }); headerEvents.add(header); - + if(url != null && url != "") { var urlButton = new Gtk.Button.with_label("Open URL"); @@ -195,17 +195,17 @@ public class FeedReader.imagePopup : Gtk.Window { }); header.pack_start(urlButton); } - + m_revealer = new Gtk.Revealer(); m_revealer.valign = Gtk.Align.START; m_revealer.set_transition_type(Gtk.RevealerTransitionType.SLIDE_DOWN); m_revealer.add(headerEvents); - + m_overlay = new Gtk.Overlay(); m_overlay.add(m_scroll); m_overlay.add_overlay(m_revealer); - - + + m_eventBox = new Gtk.EventBox(); m_eventBox.button_press_event.connect(eventButtonPressed); m_eventBox.button_release_event.connect(eventButtonReleased); @@ -213,12 +213,12 @@ public class FeedReader.imagePopup : Gtk.Window { m_eventBox.leave_notify_event.connect(onLeave); m_eventBox.key_press_event.connect(keyPressed); m_eventBox.add(m_overlay); - + this.add(m_eventBox); this.set_size_request((int)win_width, (int)win_height); this.show_all(); } - + public void onImageScrolled() { if(m_image.scale > m_maxZoom) @@ -226,18 +226,18 @@ public class FeedReader.imagePopup : Gtk.Window { m_image.scale = m_maxZoom; return; } - + if(m_image.scale < m_minZoom) { m_image.scale = m_minZoom; return; } - + m_zoomButton.set_active(true); m_scaleRevealer.set_reveal_child(true); m_scale.set_value(m_image.scale); } - + private bool headerButtonPressed(Gdk.EventButton evt) { if(evt.button == MouseButton.LEFT) @@ -248,7 +248,7 @@ public class FeedReader.imagePopup : Gtk.Window { } return false; } - + private bool motionNotify(Gdk.EventMotion evt) { if((evt.state & Gdk.ModifierType.MODIFIER_MASK) >= Gdk.ModifierType.BUTTON2_MASK) @@ -263,7 +263,7 @@ public class FeedReader.imagePopup : Gtk.Window { } return false; } - + private bool eventButtonPressed(Gdk.EventButton evt) { if(!m_hoverHeader) @@ -287,7 +287,7 @@ public class FeedReader.imagePopup : Gtk.Window { var seat = display.get_default_seat(); var pointer = seat.get_pointer(); var cursor = new Gdk.Cursor.for_display(display, Gdk.CursorType.FLEUR); - + seat.grab( this.get_window(), Gdk.SeatCapabilities.POINTER, @@ -296,9 +296,9 @@ public class FeedReader.imagePopup : Gtk.Window { null, null ); - + Gtk.device_grab_add(m_eventBox, pointer, false); - + m_dndX = evt.x; m_dndY = evt.y; m_adjX = m_scroll.hadjustment.value; @@ -314,7 +314,7 @@ public class FeedReader.imagePopup : Gtk.Window { } return false; } - + private bool eventButtonReleased(Gdk.EventButton evt) { if(evt.button == MouseButton.MIDDLE) @@ -333,8 +333,8 @@ public class FeedReader.imagePopup : Gtk.Window { } return false; } - - + + private bool keyPressed(Gdk.EventKey evt) { if(evt.keyval == Gdk.Key.Escape) @@ -343,7 +343,7 @@ public class FeedReader.imagePopup : Gtk.Window { } return false; } - + private bool onEnter(Gdk.EventCrossing event) { m_hoverImage = true; @@ -351,55 +351,55 @@ public class FeedReader.imagePopup : Gtk.Window { m_revealer.show(); return true; } - + private bool onLeave(Gdk.EventCrossing event) { if(event.detail != Gdk.NotifyType.VIRTUAL && event.mode != Gdk.CrossingMode.NORMAL) { return false; } - + if(m_dragWindow) { return false; } - + m_hoverImage = false; m_revealer.set_reveal_child(false); return true; } - + private bool updateDragMomentum() { if(!m_inDrag) { return false; } - + for(int i = 9; i > 0; --i) { m_dragBufferX[i] = m_dragBufferX[i-1]; m_dragBufferY[i] = m_dragBufferY[i-1]; } - + m_dragBufferX[0] = m_posX; m_dragBufferY[0] = m_posY; m_momentumX = (m_dragBufferX[9] - m_dragBufferX[0])/2; m_momentumY = (m_dragBufferY[9] - m_dragBufferY[0])/2; - + return true; } - + private bool ScrollDragRelease() { if(m_inDrag) { return true; } - + Gtk.Allocation allocation; this.get_allocation(out allocation); - + if(m_momentumX != 0) { m_momentumX /= 1.2; @@ -415,7 +415,7 @@ public class FeedReader.imagePopup : Gtk.Window { double newXScrollPos = double.min(oldhAdj + adjhValue, upperH - pageWidth); m_scroll.hadjustment.value = (int)newXScrollPos; } - + if(m_momentumY != 0) { m_momentumY /= 1.2; @@ -431,7 +431,7 @@ public class FeedReader.imagePopup : Gtk.Window { double newYScrollPos = double.min(oldvAdj + adjvValue, upperV - pageHeight); m_scroll.vadjustment.value = (int)newYScrollPos; } - + if((m_momentumX < 1 && m_momentumX > -1) && (m_momentumY < 1 && m_momentumY > -1)) { m_OngoingScrollID = 0; @@ -442,7 +442,7 @@ public class FeedReader.imagePopup : Gtk.Window { return true; } } - + private void closeWindow() { if(m_OngoingScrollID != 0) diff --git a/src/Widgets/InAppNotification.vala b/src/Widgets/InAppNotification.vala index cb665199..b72fe73c 100644 --- a/src/Widgets/InAppNotification.vala +++ b/src/Widgets/InAppNotification.vala @@ -14,31 +14,31 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.InAppNotification : Gd.Notification { - + private Gtk.Box m_box; private Gtk.Button m_Button; public signal void action(); - + public InAppNotification(string message, string buttonText, string? tooltip = null, int timeout = 5) { m_Button = new Gtk.Button.with_label(buttonText); setup(message, tooltip); } - + public InAppNotification.withIcon(string message, string icon, string? tooltip = null, int timeout = 5) { m_Button = new Gtk.Button.from_icon_name(icon, Gtk.IconSize.BUTTON); setup(message, tooltip); } - + public InAppNotification.withIcon_from_resource(string message, string icon, string? tooltip = null, int timeout = 5) { m_Button = new Gtk.Button(); m_Button.set_image(new Gtk.Image.from_resource(icon)); setup(message, tooltip); } - - + + private void setup(string message, string? tooltip) { m_Button.set_tooltip_text(tooltip); @@ -48,15 +48,15 @@ public class FeedReader.InAppNotification : Gd.Notification { this.set_timeout(5); this.set_show_close_button(false); this.add(m_box); - + this.unrealize.connect(() => { Logger.debug("InAppNotification: destroy"); dismissed(); }); - + m_Button.clicked.connect(() => { action(); }); } - + } diff --git a/src/Widgets/InfoBar.vala b/src/Widgets/InfoBar.vala index c8e6b03c..3ec55414 100644 --- a/src/Widgets/InfoBar.vala +++ b/src/Widgets/InfoBar.vala @@ -14,16 +14,16 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.InfoBar : Gtk.Revealer { - + // THIS IS BASICALLY A WORKAROUND FOR THIS GTK+ BUG: // https://bugzilla.gnome.org/show_bug.cgi?id=710888 - + private Gtk.Label m_Label; - + public InfoBar(string text) { m_Label = new Gtk.Label(text); - + var bar = new Gtk.InfoBar(); bar.valign = Gtk.Align.START; bar.get_content_area().add(m_Label); @@ -35,22 +35,22 @@ public class FeedReader.InfoBar : Gtk.Revealer { this.set_reveal_child(false); } }); - + this.set_transition_duration(200); this.set_transition_type(Gtk.RevealerTransitionType.SLIDE_DOWN); this.valign = Gtk.Align.START; this.add(bar); } - + public void reveal() { this.show_all(); this.set_reveal_child(true); } - + public void setText(string text) { m_Label.set_text(text); } - + } diff --git a/src/Widgets/LoginPage.vala b/src/Widgets/LoginPage.vala index d9853f51..67800fe9 100644 --- a/src/Widgets/LoginPage.vala +++ b/src/Widgets/LoginPage.vala @@ -14,56 +14,56 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.LoginPage : Gtk.Stack { - + private Gtk.Box m_layout; private Gtk.ListBox m_accountList; private WebLoginPage m_page; private Gtk.Box? m_activeWidget = null; public signal void submit_data(); public signal void loginError(LoginResponse errorCode); - - + + public LoginPage() { FeedReaderBackend.get_default().tryLogin.connect(() => { writeLoginData(); }); - + m_layout = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); m_layout.set_size_request(700, 410); m_layout.set_halign(Gtk.Align.CENTER); m_layout.margin = 20; m_layout.margin_bottom = 50; m_layout.margin_top = 50; - + var welcomeText = new Gtk.Label(_("Where are your feeds?")); welcomeText.get_style_context().add_class("h1"); welcomeText.set_justify(Gtk.Justification.CENTER); - + var welcomeText2 = new Gtk.Label(_("Please select the RSS service you are using and log in to get going.")); welcomeText2.get_style_context().add_class("h2"); welcomeText2.set_justify(Gtk.Justification.CENTER); welcomeText2.set_lines(3); - - + + m_accountList = new Gtk.ListBox(); m_accountList.set_selection_mode(Gtk.SelectionMode.NONE); m_accountList.row_activated.connect(serviceSelected); - + RefreshPlugins(); FeedServer.get_default().PluginsChanedEvent.connect(RefreshPlugins); - + var scroll = new Gtk.ScrolledWindow(null, null); scroll.set_size_request(450, 0); scroll.set_halign(Gtk.Align.CENTER); scroll.get_style_context().add_class(Gtk.STYLE_CLASS_FRAME); scroll.add(m_accountList); - - + + m_layout.pack_start(welcomeText, false, true, 0); m_layout.pack_start(welcomeText2, false, true, 2); m_layout.pack_start(scroll, true, true, 20); - + m_page = new WebLoginPage(); this.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT); this.add_named(m_page, "web"); @@ -71,7 +71,7 @@ public class FeedReader.LoginPage : Gtk.Stack { this.show_all(); reset(); } - + private void RefreshPlugins() { var children = m_accountList.get_children(); @@ -80,7 +80,7 @@ public class FeedReader.LoginPage : Gtk.Stack { m_accountList.remove(row); row.destroy(); } - + FeedServer.get_default().getPlugins().foreach((extSet, info, ext) => { var plug = ext as FeedServerInterface; if(plug != null) @@ -97,15 +97,15 @@ public class FeedReader.LoginPage : Gtk.Stack { m_accountList.add(new LoginRow(pluginfo)); } }); - + m_accountList.show_all(); } - + public void reset() { var visible = this.get_visible_child_name(); this.set_visible_child_name("selectScreen"); - + if(visible == "loginWidget" && m_activeWidget != null) { @@ -117,17 +117,17 @@ public class FeedReader.LoginPage : Gtk.Stack { m_page.reset(); } } - + private void serviceSelected(Gtk.ListBoxRow row) { var serviceRow = (row as LoginRow); Logger.debug("serviceSelected: %s".printf(serviceRow.getInfo().name)); - + var window = MainWindow.get_default(); window.getSimpleHeader().showBackButton(true); FeedServer.get_default().setActivePlugin(serviceRow.getInfo().ID); FeedServerInterface? plug = FeedServer.get_default().getActivePlugin(); - + if(plug != null) { if(plug.needWebLogin()) @@ -139,7 +139,7 @@ public class FeedReader.LoginPage : Gtk.Stack { login(plug.getID()); }); this.set_visible_child_name("web"); - + window.getSimpleHeader().back.connect(() => { this.set_visible_child_full("selectScreen", Gtk.StackTransitionType.SLIDE_RIGHT); window.getSimpleHeader().showBackButton(false); @@ -150,10 +150,10 @@ public class FeedReader.LoginPage : Gtk.Stack { { m_activeWidget = plug.getWidget(); m_activeWidget.show_all(); - + this.add_named(m_activeWidget, "loginWidget"); this.set_visible_child_name("loginWidget"); - + window.getSimpleHeader().back.connect(() => { this.set_visible_child_full("selectScreen", Gtk.StackTransitionType.SLIDE_RIGHT); window.getSimpleHeader().showBackButton(false); @@ -166,13 +166,13 @@ public class FeedReader.LoginPage : Gtk.Stack { } } } - - + + public void showHtAccess() { FeedServer.get_default().getActivePlugin().showHtAccess(); } - + public void writeLoginData() { Logger.debug("write login data"); @@ -183,7 +183,7 @@ public class FeedReader.LoginPage : Gtk.Stack { login(ext.getID()); } } - + private void login(string id) { LoginResponse status = FeedReaderBackend.get_default().login(id); @@ -201,7 +201,7 @@ public class FeedReader.LoginPage : Gtk.Stack { } return; } - + loginError(status); } } diff --git a/src/Widgets/LoginRow.vala b/src/Widgets/LoginRow.vala index 3460f5d3..f7248161 100644 --- a/src/Widgets/LoginRow.vala +++ b/src/Widgets/LoginRow.vala @@ -14,21 +14,21 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.LoginRow : Gtk.ListBoxRow { - + private BackendInfo m_info; private Gtk.Stack m_infoStack; private bool m_hovered = false; - + public LoginRow(BackendInfo info) { m_info = info; - + var icon = new Gtk.Image.from_icon_name(info.iconName, Gtk.IconSize.MENU); icon.margin_start = 10; var label = new Gtk.Label(info.name); label.set_alignment(0.0f, 0.5f); label.get_style_context().add_class("h3"); - + var infoIcon = new Gtk.Image.from_icon_name("feed-backend-info", Gtk.IconSize.LARGE_TOOLBAR); var infoButton = new Gtk.Button(); infoButton.set_image(infoIcon); @@ -36,7 +36,7 @@ public class FeedReader.LoginRow : Gtk.ListBoxRow { infoButton.set_focus_on_click(false); infoButton.valign = Gtk.Align.CENTER; infoButton.clicked.connect(infoClicked); - + m_infoStack = new Gtk.Stack(); m_infoStack.set_transition_type(Gtk.StackTransitionType.CROSSFADE); m_infoStack.set_transition_duration(50); @@ -44,7 +44,7 @@ public class FeedReader.LoginRow : Gtk.ListBoxRow { m_infoStack.add_named(new Gtk.Label(""), "empty"); m_infoStack.add_named(infoButton, "button"); m_infoStack.set_visible_child_name("empty"); - + var box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 15); box.margin_top = 2; box.margin_bottom = 2; @@ -54,34 +54,34 @@ public class FeedReader.LoginRow : Gtk.ListBoxRow { var box2 = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); box2.pack_start(box); box2.pack_start(new Gtk.Separator(Gtk.Orientation.HORIZONTAL)); - + var eventbox = new Gtk.EventBox(); eventbox.set_events(Gdk.EventMask.ENTER_NOTIFY_MASK); eventbox.set_events(Gdk.EventMask.LEAVE_NOTIFY_MASK); eventbox.enter_notify_event.connect(rowEnter); eventbox.leave_notify_event.connect(rowLeave); eventbox.add(box2); - + this.add(eventbox); } - + public BackendInfo getInfo() { return m_info; } - + private bool rowEnter(Gdk.EventCrossing event) { if(event.detail == Gdk.NotifyType.INFERIOR) { return true; } - + m_hovered = true; m_infoStack.set_visible_child_name("button"); return true; } - + private bool rowLeave(Gdk.EventCrossing event) { if(event.detail == Gdk.NotifyType.INFERIOR @@ -93,13 +93,13 @@ public class FeedReader.LoginRow : Gtk.ListBoxRow { } return true; } - - + + m_hovered = false; m_infoStack.set_visible_child_name("empty"); return true; } - + private void infoClicked() { var pop = new BackendInfoPopover(m_infoStack, m_info); diff --git a/src/Widgets/MainWindow.vala b/src/Widgets/MainWindow.vala index 6c01e8e9..487ddd78 100644 --- a/src/Widgets/MainWindow.vala +++ b/src/Widgets/MainWindow.vala @@ -28,42 +28,42 @@ public class FeedReader.MainWindow : Gtk.ApplicationWindow private SpringCleanPage m_SpringClean; private Gtk.CssProvider m_cssProvider; private uint m_stackTransitionTime = 100; - + private static MainWindow? m_window = null; - + public static MainWindow get_default() { if(m_window == null) { m_window = new MainWindow(); } - + return m_window; } - + private MainWindow() { Object(application: FeedReaderApp.get_default(), title: _("FeedReader"), show_menubar: false); this.window_position = WindowPosition.CENTER; - + m_stack = new Gtk.Stack(); m_stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE); m_stack.set_transition_duration(m_stackTransitionTime); - + m_overlay = new Gtk.Overlay(); m_overlay.add(m_stack); - + setupCSS(); setupLoginPage(); setupResetPage(); setupContentPage(); setupSpringCleanPage(); - + var shortcutsAction = new SimpleAction("shortcuts", null); shortcutsAction.activate.connect(showShortcutWindow); this.add_action(shortcutsAction); shortcutsAction.set_enabled(true); - + var reportBugAction = new SimpleAction("bugs", null); reportBugAction.activate.connect(() => { try{ @@ -75,7 +75,7 @@ public class FeedReader.MainWindow : Gtk.ApplicationWindow }); this.add_action(reportBugAction); reportBugAction.set_enabled(true); - + var bountyAction = new SimpleAction("bounty", null); bountyAction.activate.connect(() => { try{ @@ -87,21 +87,21 @@ public class FeedReader.MainWindow : Gtk.ApplicationWindow }); this.add_action(bountyAction); bountyAction.set_enabled(true); - + var settingsAction = new SimpleAction("settings", null); settingsAction.activate.connect(() => { SettingsDialog.get_default().showDialog("ui"); }); this.add_action(settingsAction); settingsAction.set_enabled(true); - + var login_action = new SimpleAction("reset", null); login_action.activate.connect(() => { showReset(Gtk.StackTransitionType.SLIDE_RIGHT); }); this.add_action(login_action); login_action.set_enabled(true); - + var about_action = new SimpleAction("about", null); about_action.activate.connect(() => { Gtk.AboutDialog dialog = new Gtk.AboutDialog(); @@ -111,7 +111,7 @@ public class FeedReader.MainWindow : Gtk.ApplicationWindow dialog.authors = AboutInfo.authors; dialog.documenters = null; dialog.translator_credits = AboutInfo.translators; - + dialog.program_name = AboutInfo.programmName; dialog.comments = AboutInfo.comments; dialog.copyright = AboutInfo.copyright; @@ -120,27 +120,27 @@ public class FeedReader.MainWindow : Gtk.ApplicationWindow dialog.license_type = Gtk.License.GPL_3_0; dialog.wrap_license = true; dialog.website = AboutInfo.website; - + dialog.response.connect((response_id) => { if (response_id == Gtk.ResponseType.CANCEL || response_id == Gtk.ResponseType.DELETE_EVENT) { dialog.hide_on_delete(); } }); - + dialog.present(); }); this.add_action(about_action); about_action.set_enabled(true); - + m_simpleHeader = new SimpleHeader(); - + if(Settings.state().get_boolean("window-maximized")) { Logger.debug("MainWindow: maximize"); this.maximize(); } - + this.window_state_event.connect(onStateEvent); this.key_press_event.connect(shortcuts); this.add(m_overlay); @@ -149,7 +149,7 @@ public class FeedReader.MainWindow : Gtk.ApplicationWindow this.set_title("FeedReader"); this.set_default_size(Settings.state().get_int("window-width"), Settings.state().get_int("window-height")); this.show_all(); - + Logger.debug("MainWindow: determining state"); if(FeedReaderBackend.get_default().isOnline()) { @@ -164,13 +164,13 @@ public class FeedReader.MainWindow : Gtk.ApplicationWindow showLogin(); } } - + public override bool delete_event(Gdk.EventAny event) { this.hide(); return true; } - + private bool onStateEvent(Gdk.EventWindowState event) { if(event.type == Gdk.EventType.WINDOW_STATE) @@ -182,19 +182,19 @@ public class FeedReader.MainWindow : Gtk.ApplicationWindow { return true; } - + if(ColumnView.get_default().isFullscreenVideo()) { if((event.new_window_state & Gdk.WindowState.FULLSCREEN) != Gdk.WindowState.FULLSCREEN) { ColumnView.get_default().exitFullscreenVideo(); } - + base.window_state_event(event); return true; } - - + + if((event.new_window_state & Gdk.WindowState.FULLSCREEN) == Gdk.WindowState.FULLSCREEN) { Logger.debug("MainWindow: fullscreen event"); @@ -208,17 +208,17 @@ public class FeedReader.MainWindow : Gtk.ApplicationWindow } } } - + base.window_state_event(event); return false; } - + public void showOfflineContent() { showContent(); ColumnView.get_default().setOffline(); } - + public void showContent(Gtk.StackTransitionType transition = Gtk.StackTransitionType.CROSSFADE, bool noNewFeedList = false) { Logger.debug("MainWindow: show content"); @@ -228,14 +228,14 @@ public class FeedReader.MainWindow : Gtk.ApplicationWindow } m_stack.set_visible_child_full("content", transition); ColumnView.get_default().getHeader().setButtonsSensitive(true); - + if(!ColumnView.get_default().isFullscreen()) { ColumnView.get_default().getHeader().show_all(); this.set_titlebar(ColumnView.get_default().getHeader()); } } - + private void showLogin(Gtk.StackTransitionType transition = Gtk.StackTransitionType.CROSSFADE) { Logger.debug("MainWindow: show login"); @@ -245,19 +245,19 @@ public class FeedReader.MainWindow : Gtk.ApplicationWindow ColumnView.get_default().getHeader().setButtonsSensitive(false); this.set_titlebar(m_simpleHeader); } - + private void showReset(Gtk.StackTransitionType transition = Gtk.StackTransitionType.CROSSFADE) { Logger.debug("MainWindow: show reset"); - + // kill playing media ColumnView.get_default().articleViewKillMedia(); - + m_stack.set_visible_child_full("reset", transition); ColumnView.get_default().getHeader().setButtonsSensitive(false); this.set_titlebar(m_simpleHeader); } - + public void showSpringClean(Gtk.StackTransitionType transition = Gtk.StackTransitionType.CROSSFADE) { Logger.debug("MainWindow: show springClean"); @@ -265,57 +265,57 @@ public class FeedReader.MainWindow : Gtk.ApplicationWindow ColumnView.get_default().getHeader().setButtonsSensitive(false); this.set_titlebar(m_simpleHeader); } - + public InterfaceState getInterfaceState() { int windowWidth = 0; int windowHeight = 0; this.get_size(out windowWidth, out windowHeight); - + var state = new InterfaceState(); state.setWindowSize(windowHeight, windowWidth); state.setWindowMaximized(this.is_maximized); ColumnView.get_default().saveState(ref state); return state; } - + public void writeInterfaceState(bool shutdown = false) { getInterfaceState().write(shutdown); } - + public void reloadCSS() { Logger.debug("MainWindow: reloadCSS"); removeProvider(m_cssProvider); setupCSS(); } - + private void setupCSS() { Logger.debug("MainWindow: setupCSS"); string path = "/org/gnome/FeedReader/gtk-css/"; - + addProvider(path + "basics.css"); - + FeedListTheme theme = (FeedListTheme)Settings.general().get_enum("feedlist-theme"); - + switch(theme) { case FeedListTheme.GTK: m_cssProvider = addProvider(path + "gtk.css"); break; - + case FeedListTheme.DARK: m_cssProvider = addProvider(path + "dark.css"); break; - + case FeedListTheme.ELEMENTARY: m_cssProvider = addProvider(path + "elementary.css"); break; } } - + private Gtk.CssProvider? addProvider(string path) { Gtk.CssProvider provider = new Gtk.CssProvider(); @@ -325,14 +325,14 @@ public class FeedReader.MainWindow : Gtk.ApplicationWindow Gtk.StyleContext.add_provider_for_screen(screen, provider, Gtk.STYLE_PROVIDER_PRIORITY_USER); return provider; } - + private void removeProvider(Gtk.CssProvider provider) { weak Gdk.Display display = Gdk.Display.get_default(); weak Gdk.Screen screen = display.get_default_screen(); Gtk.StyleContext.remove_provider_for_screen(screen, provider); } - + private void setupLoginPage() { m_error_bar = new Gtk.InfoBar(); @@ -342,12 +342,12 @@ public class FeedReader.MainWindow : Gtk.ApplicationWindow error_content.add(m_ErrorMessage); m_error_bar.set_message_type(Gtk.MessageType.WARNING); m_error_bar.set_show_close_button(true); - + m_ignore_tls_errors = m_error_bar.add_button("Ignore", Gtk.ResponseType.APPLY); m_ignore_tls_errors.get_style_context().add_class(Gtk.STYLE_CLASS_DESTRUCTIVE_ACTION); m_ignore_tls_errors.set_tooltip_text(_("Ignore all TLS errors from now on")); m_ignore_tls_errors.set_visible(false); - + m_error_bar.response.connect((response_id) => { switch(response_id) { @@ -362,14 +362,14 @@ public class FeedReader.MainWindow : Gtk.ApplicationWindow break; } }); - + m_login = new LoginPage(); - + var loginBox = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); - + loginBox.pack_start(m_error_bar, false, false, 0); loginBox.pack_start(m_login, true, true, 0); - + m_login.submit_data.connect(() => { Settings.state().set_strv("expanded-categories", Utils.getDefaultExpandedCategories()); Settings.state().set_string("feedlist-selected-row", "feed -4"); @@ -382,7 +382,7 @@ public class FeedReader.MainWindow : Gtk.ApplicationWindow m_stack.add_named(loginBox, "login"); m_error_bar.set_visible(false); } - + private void setupResetPage() { var reset = new ResetPage(); @@ -394,18 +394,18 @@ public class FeedReader.MainWindow : Gtk.ApplicationWindow showLogin(Gtk.StackTransitionType.SLIDE_LEFT); }); } - + private void setupSpringCleanPage() { m_SpringClean = new SpringCleanPage(); m_stack.add_named(m_SpringClean, "springClean"); } - + private void setupContentPage() { m_stack.add_named(ColumnView.get_default(), "content"); } - + private void showErrorBar(int ErrorCode) { Logger.debug("MainWindow: show error bar - errorCode = " + ErrorCode.to_string()); @@ -461,12 +461,12 @@ public class FeedReader.MainWindow : Gtk.ApplicationWindow m_error_bar.set_visible(false); return; } - + Logger.debug("MainWindow: show error bar"); m_error_bar.set_visible(true); m_ErrorMessage.show(); } - + private void loadContent() { Logger.debug("MainWindow: load content"); @@ -474,12 +474,12 @@ public class FeedReader.MainWindow : Gtk.ApplicationWindow showContent(Gtk.StackTransitionType.NONE); m_stack.set_transition_duration(m_stackTransitionTime); } - + private void markSelectedRead() { ColumnView.get_default().markAllArticlesAsRead(); string[] selectedRow = ColumnView.get_default().getSelectedFeedListRow().split(" ", 2); - + if(selectedRow[0] == "feed") { if(selectedRow[1] == FeedID.ALL.to_string()) @@ -491,7 +491,7 @@ public class FeedReader.MainWindow : Gtk.ApplicationWindow FeedReaderBackend.get_default().markFeedAsRead(cat.getCatID(), true); Logger.debug("MainWindow: mark all articles as read cat: %s".printf(cat.getTitle())); } - + var feeds = db.read_feeds_without_cat(); foreach(Feed feed in feeds) { @@ -509,15 +509,15 @@ public class FeedReader.MainWindow : Gtk.ApplicationWindow FeedReaderBackend.get_default().markFeedAsRead(selectedRow[1], true); } } - - + + private bool checkShortcut(Gdk.EventKey event, string gsettingKey) { uint? key; Gdk.ModifierType? mod; string setting = Settings.keybindings().get_string(gsettingKey); Gtk.accelerator_parse(setting, out key, out mod); - + if(key != null && Gdk.keyval_to_lower(event.keyval) == key) { if(mod == null || mod == 0) @@ -533,50 +533,50 @@ public class FeedReader.MainWindow : Gtk.ApplicationWindow return true; } } - + return false; } - + private bool shortcuts(Gdk.EventKey event) { if(m_stack.get_visible_child_name() != "content") { return false; } - + if(ColumnView.get_default().searchFocused()) { return false; } - + if(checkShortcut(event, "articlelist-prev")) { Logger.debug("shortcut: articlelist prev"); ColumnView.get_default().ArticleListPREV(); return true; } - + if(checkShortcut(event, "articlelist-next")) { Logger.debug("shortcut: articlelist next"); ColumnView.get_default().ArticleListNEXT(); return true; } - + if(checkShortcut(event, "feedlist-prev")) { Logger.debug("shortcut: feedlist prev"); ColumnView.get_default().FeedListPREV(); return true; } - + if(checkShortcut(event, "feedlist-next")) { Logger.debug("shortcut: feedlist next"); ColumnView.get_default().FeedListNEXT(); return true; } - + if(event.keyval == Gdk.Key.Left || event.keyval == Gdk.Key.Right) { if(ColumnView.get_default().isFullscreen()) @@ -589,7 +589,7 @@ public class FeedReader.MainWindow : Gtk.ApplicationWindow { ColumnView.get_default().ArticleListNEXT(); } - + return true; } else @@ -597,49 +597,49 @@ public class FeedReader.MainWindow : Gtk.ApplicationWindow return false; } } - + if(checkShortcut(event, "articleview-up")) { event.keyval = Gdk.Key.Up; ColumnView.get_default().ArticleViewSendEvent(event); return true; } - + if(checkShortcut(event, "articleview-down")) { event.keyval = Gdk.Key.Down; ColumnView.get_default().ArticleViewSendEvent(event); return true; } - + if(checkShortcut(event, "articlelist-toggle-read")) { Logger.debug("shortcut: toggle read"); ColumnView.get_default().toggleReadSelectedArticle(); return true; } - + if(checkShortcut(event, "articlelist-toggle-marked")) { Logger.debug("shortcut: toggle marked"); ColumnView.get_default().toggleMarkedSelectedArticle(); return true; } - + if(checkShortcut(event, "articlelist-open-url")) { Logger.debug("shortcut: open in browser"); ColumnView.get_default().openSelectedArticle(); return true; } - + if(checkShortcut(event, "feedlist-mark-read")) { Logger.debug("shortcut: mark all as read"); markSelectedRead(); return true; } - + if(checkShortcut(event, "global-sync")) { Logger.debug("shortcut: sync"); @@ -647,28 +647,28 @@ public class FeedReader.MainWindow : Gtk.ApplicationWindow app.sync(); return true; } - + if(checkShortcut(event, "articlelist-center-selected")) { Logger.debug("shortcut: scroll to selcted row"); ColumnView.get_default().centerSelectedRow(); return true; } - + if(checkShortcut(event, "global-search")) { Logger.debug("shortcut: focus search"); ColumnView.get_default().getHeader().focusSearch(); return true; } - + if(checkShortcut(event, "global-quit")) { Logger.debug("shortcut: quit"); FeedReaderApp.get_default().activate_action("quit", null); return true; } - + if(event.keyval == Gdk.Key.Escape && ColumnView.get_default().isFullscreen()) { this.unfullscreen(); @@ -676,27 +676,27 @@ public class FeedReader.MainWindow : Gtk.ApplicationWindow ColumnView.get_default().leaveFullscreenArticle(); return true; } - + if(checkShortcut(event, "global-help")) { Logger.debug("shortcut: showShortcutWindow"); showShortcutWindow(); return true; } - + return false; } - + private void showShortcutWindow() { new ShortcutsWindow(this); } - + public SimpleHeader getSimpleHeader() { return m_simpleHeader; } - + public InAppNotification showNotification(string message, string buttonText = "undo") { var notification = new InAppNotification(message, buttonText); @@ -704,5 +704,5 @@ public class FeedReader.MainWindow : Gtk.ApplicationWindow this.show_all(); return notification; } - + } diff --git a/src/Widgets/MediaButton.vala b/src/Widgets/MediaButton.vala index aeb3fd2d..2508a5a2 100644 --- a/src/Widgets/MediaButton.vala +++ b/src/Widgets/MediaButton.vala @@ -14,7 +14,7 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.AttachedMediaButton : Gtk.Button { - + private Gtk.ListBox m_list; private Gtk.Image m_filesIcon; private Gtk.Spinner m_spinner; @@ -25,13 +25,13 @@ public class FeedReader.AttachedMediaButton : Gtk.Button { public signal void play(string url); public signal void popClosed(); public signal void popOpened(); - + public AttachedMediaButton() { m_filesIcon = new Gtk.Image.from_icon_name("mail-attachment-symbolic", Gtk.IconSize.SMALL_TOOLBAR); m_spinner = new Gtk.Spinner(); m_spinner.set_size_request(16,16); - + m_stack = new Gtk.Stack(); m_stack.set_transition_duration(100); m_stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE); @@ -41,7 +41,7 @@ public class FeedReader.AttachedMediaButton : Gtk.Button { this.set_relief(Gtk.ReliefStyle.NONE); this.set_events(Gdk.EventMask.ENTER_NOTIFY_MASK); this.set_focus_on_click(false); - + m_list = new Gtk.ListBox(); m_list.margin = 10; m_list.set_selection_mode(Gtk.SelectionMode.NONE); @@ -58,7 +58,7 @@ public class FeedReader.AttachedMediaButton : Gtk.Button { Logger.error("MediaPopover: invalid row clicked"); } }); - + m_pop = new Gtk.Popover(this); m_pop.add(m_list); m_pop.set_modal(true); @@ -67,7 +67,7 @@ public class FeedReader.AttachedMediaButton : Gtk.Button { popClosed(); }); } - + public void update() { m_enclosures = new Gee.ArrayList<Enclosure>(); @@ -76,13 +76,13 @@ public class FeedReader.AttachedMediaButton : Gtk.Button { { m_enclosures = selectedArticle.getEnclosures(); } - + if(m_signalID != 0) { this.disconnect(m_signalID); m_signalID = 0; } - + if(m_enclosures.size != 0) { m_stack.set_visible_child_name("files"); @@ -106,7 +106,7 @@ public class FeedReader.AttachedMediaButton : Gtk.Button { // no media } } - + private void playMedia(string url) { Logger.debug(@"MediaButton.playMedia: $url"); diff --git a/src/Widgets/MediaPlayer.vala b/src/Widgets/MediaPlayer.vala index dca881aa..df5fd596 100644 --- a/src/Widgets/MediaPlayer.vala +++ b/src/Widgets/MediaPlayer.vala @@ -14,7 +14,7 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.MediaPlayer : Gtk.Box { - + private dynamic Gst.Element m_player; private dynamic Gtk.Widget m_videoWidget; private Gtk.Stack m_playStack; @@ -38,24 +38,24 @@ public class FeedReader.MediaPlayer : Gtk.Box { private string m_URL; private DisplayPosition m_display = DisplayPosition.ALL; public signal void loaded(); - + public MediaPlayer(string url) { m_type = MediaType.AUDIO; m_URL = url; - + inspectMedia(); buildUI(); loaded(); } - + private void inspectMedia() { try { var discoverer = new Gst.PbUtils.Discoverer((Gst.ClockTime)(10*Gst.SECOND)); var info = discoverer.discover_uri(m_URL); - + foreach(Gst.PbUtils.DiscovererStreamInfo i in info.get_stream_list()) { if(i is Gst.PbUtils.DiscovererVideoInfo) @@ -71,7 +71,7 @@ public class FeedReader.MediaPlayer : Gtk.Box { Logger.error("Unable discover_uri: " + e.message); } } - + private void buildUI() { var gtksink = Gst.ElementFactory.make("gtksink", "sink"); @@ -80,15 +80,15 @@ public class FeedReader.MediaPlayer : Gtk.Box { m_videoWidget.margin_end = m_margin; m_videoWidget.margin_top = m_margin; m_videoWidget.size_allocate.connect(onAllocation); - + m_player = Gst.ElementFactory.make("playbin", "player"); m_player["video_sink"] = gtksink; m_player["volume"] = 1.0; m_player["uri"] = m_URL; - + Gst.Bus bus = m_player.get_bus(); bus.add_watch(GLib.Priority.LOW, busCallback); - + GLib.Timeout.add(500, () => { Gst.State state; Gst.State pending; @@ -110,13 +110,13 @@ public class FeedReader.MediaPlayer : Gtk.Box { } return true; }, GLib.Priority.LOW); - + m_playIcon = new Gtk.Image.from_icon_name("media-playback-start-symbolic", Gtk.IconSize.LARGE_TOOLBAR); m_pauseIcon = new Gtk.Image.from_icon_name("media-playback-pause-symbolic", Gtk.IconSize.LARGE_TOOLBAR); m_muteIcon = new Gtk.Image.from_icon_name("audio-volume-muted-symbolic", Gtk.IconSize.DND); m_noiseIcon = new Gtk.Image.from_icon_name("audio-volume-high-symbolic", Gtk.IconSize.DND); m_closeIcon = new Gtk.Image.from_icon_name("window-close-symbolic", Gtk.IconSize.SMALL_TOOLBAR); - + m_playButton = new Gtk.Button(); m_playButton.set_image(m_playIcon); m_playButton.clicked.connect(togglePause); @@ -124,13 +124,13 @@ public class FeedReader.MediaPlayer : Gtk.Box { m_playButton.valign = Gtk.Align.CENTER; m_playButton.halign = Gtk.Align.CENTER; m_playButton.set_size_request(48, 48); - + m_playSpinner = new Gtk.Spinner(); m_playStack = new Gtk.Stack(); m_playStack.add_named(m_playButton, "button"); m_playStack.add_named(m_playSpinner, "spinner"); m_playStack.set_visible_child_name("button"); - + m_muteButton = new Gtk.Button(); m_muteButton.set_image(m_noiseIcon); m_muteButton.set_tooltip_text(MediaButton.MUTE); @@ -138,7 +138,7 @@ public class FeedReader.MediaPlayer : Gtk.Box { m_muteButton.valign = Gtk.Align.CENTER; m_muteButton.halign = Gtk.Align.CENTER; m_muteButton.set_size_request(48, 48); - + m_closeButton = new Gtk.Button(); m_closeButton.set_image(m_closeIcon); m_closeButton.clicked.connect(() => { @@ -148,12 +148,12 @@ public class FeedReader.MediaPlayer : Gtk.Box { m_closeButton.valign = Gtk.Align.CENTER; m_closeButton.halign = Gtk.Align.CENTER; m_closeButton.set_size_request(48, 48); - + m_scale = new Gtk.Scale.with_range(Gtk.Orientation.HORIZONTAL, 0.0, 100.0, 5.0); m_scale.draw_value = false; m_scale.set_size_request(200, 48); m_scale.change_value.connect(valueChanged); - + m_labelButton = new Gtk.Button.with_label("00:00"); m_labelButton.set_relief(Gtk.ReliefStyle.NONE); m_labelButton.get_style_context().add_class("h3"); @@ -161,7 +161,7 @@ public class FeedReader.MediaPlayer : Gtk.Box { m_labelButton.valign = Gtk.Align.CENTER; m_labelButton.halign = Gtk.Align.CENTER; m_labelButton.set_size_request(48, 48); - + var hBox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 10); hBox.margin = m_margin; hBox.pack_start(m_playStack, false, false); @@ -169,17 +169,17 @@ public class FeedReader.MediaPlayer : Gtk.Box { hBox.pack_start(m_scale); hBox.pack_start(m_labelButton, false, false); hBox.pack_start(m_closeButton, false, false); - + m_bufferLabel = new Gtk.Label("0%"); m_bufferLabel.valign = Gtk.Align.CENTER; m_bufferLabel.halign = Gtk.Align.CENTER; m_bufferLabel.set_size_request(48, 48); m_bufferLabel.no_show_all = true; - + var bufferOverlay = new Gtk.Overlay(); bufferOverlay.add(m_videoWidget); bufferOverlay.add_overlay(m_bufferLabel); - + this.orientation = Gtk.Orientation.VERTICAL; this.get_style_context().add_class("osd"); this.margin = 40; @@ -191,13 +191,13 @@ public class FeedReader.MediaPlayer : Gtk.Box { this.valign = Gtk.Align.END; this.show_all(); } - + private void togglePause() { Gst.State state; Gst.State pending; m_player.get_state(out state, out pending, 1000); - + switch(state) { case Gst.State.PLAYING: @@ -205,7 +205,7 @@ public class FeedReader.MediaPlayer : Gtk.Box { m_playButton.set_tooltip_text(MediaButton.PLAY); m_player.set_state(Gst.State.PAUSED); break; - + case Gst.State.PAUSED: case Gst.State.READY: default: @@ -214,7 +214,7 @@ public class FeedReader.MediaPlayer : Gtk.Box { m_player.set_state(Gst.State.PLAYING); break; } - + if(m_muted) { m_player["volume"] = 0.0; @@ -224,7 +224,7 @@ public class FeedReader.MediaPlayer : Gtk.Box { m_player["volume"] = 1.0; } } - + private void switchDisplay() { switch(m_display) @@ -232,18 +232,18 @@ public class FeedReader.MediaPlayer : Gtk.Box { case DisplayPosition.ALL: m_display = DisplayPosition.POS; break; - + case DisplayPosition.POS: m_display = DisplayPosition.LEFT; break; - + case DisplayPosition.LEFT: m_display = DisplayPosition.ALL; break; } calcTime(); } - + private void calcTime() { int64 pos; @@ -252,106 +252,106 @@ public class FeedReader.MediaPlayer : Gtk.Box { m_player.query_duration(Gst.Format.TIME, out dur); double position = (int)((double)pos/1000000000); double duration = (int)((double)dur/1000000000); - - - + + + double? sd = duration; double? md = null; double? hd = null; - + if( ((int)sd) >= 60) { md = (int)(sd/60); sd = sd - (md*60); - + if( ((int)md) >= 60) { hd = (int)(md/60); md = md - (hd*60); } } - + double? sp = position; double? mp = null; double? hp = null; - + if( ((int)sp) >= 60) { mp = (int)(sp/60); sp = sp - (mp*60); - + if( ((int)mp) >= 60) { hp = (int)(mp/60); mp = mp - (hp*60); } } - + double? sr = duration - position; double? mr = null; double? hr = null; - + if( ((int)sr) >= 60) { mr = (int)(sr/60); sr = sr - (mr*60); - + if( ((int)mr) >= 60) { hr = (int)(mr/60); mr = mr - (hr*60); } } - + var pLabel = ""; (hp != null) ? pLabel += "%02.0f:".printf(hp) : null; (mp != null) ? pLabel += "%02.0f".printf(mp) : null; (mp != null) ? pLabel += ":" : pLabel += "0:"; pLabel += "%02.0f".printf(sp); - + var dLabel = ""; (hd != null) ? dLabel += "%02.0f:".printf(hd) : null; (md != null) ? dLabel += "%02.0f".printf(md) : null; (md != null) ? dLabel += ":" : dLabel += "0:"; dLabel += "%02.0f".printf(sd); - + var rLabel = "-"; (hr != null) ? rLabel += "%02.0f:".printf(hr) : null; (mr != null) ? rLabel += "%02.0f".printf(mr) : null; (mr != null) ? rLabel += ":" : rLabel += "0:"; rLabel += "%02.0f".printf(sr); - + if(dur == -1) { m_labelButton.set_label(pLabel); return; } - - + + switch(m_display) { case DisplayPosition.ALL: m_labelButton.set_label(pLabel + " / " + dLabel); break; - + case DisplayPosition.POS: m_labelButton.set_label(pLabel); break; - + case DisplayPosition.LEFT: m_labelButton.set_label(rLabel); break; } } - + private bool valueChanged(Gtk.ScrollType scroll, double new_value) { m_scale.set_value(new_value); - + if (m_seek_source_id == 0) { double startValue = new_value; - + m_seek_source_id = GLib.Timeout.add_full(GLib.Priority.DEFAULT, 500, () => { if(m_scale.get_value() != startValue) { @@ -368,7 +368,7 @@ public class FeedReader.MediaPlayer : Gtk.Box { } return true; } - + private void seek(double new_value) { int64 dur; @@ -380,7 +380,7 @@ public class FeedReader.MediaPlayer : Gtk.Box { m_playButton.set_image(m_pauseIcon); m_player.set_state(Gst.State.PLAYING); } - + private void toggleMute() { if(m_muted) @@ -398,7 +398,7 @@ public class FeedReader.MediaPlayer : Gtk.Box { m_muted = true; } } - + private bool busCallback(Gst.Bus bus, Gst.Message message) { switch (message.type) @@ -410,19 +410,19 @@ public class FeedReader.MediaPlayer : Gtk.Box { Logger.error("MediaPlayer: " + err.message); m_player.set_state(Gst.State.NULL); break; - + case Gst.MessageType.EOS: m_player.set_state(Gst.State.READY); m_playButton.set_image(m_playIcon); break; - + case Gst.MessageType.BUFFERING: int percent = 0; message.parse_buffering(out percent); if(percent < 100) { m_player.set_state(Gst.State.PAUSED); - + if(m_type == MediaType.VIDEO) { m_bufferLabel.set_text(percent.to_string() + "%"); @@ -447,7 +447,7 @@ public class FeedReader.MediaPlayer : Gtk.Box { } } break; - + case Gst.MessageType.STATE_CHANGED: Gst.State oldstate; Gst.State newstate; @@ -457,7 +457,7 @@ public class FeedReader.MediaPlayer : Gtk.Box { } return true; } - + private void onAllocation(Gtk.Allocation allocation) { if(m_aspectRatio != 0) @@ -467,11 +467,11 @@ public class FeedReader.MediaPlayer : Gtk.Box { m_videoWidget.set_size_request(-1, height); } } - + public void kill() { m_player.set_state(Gst.State.NULL); this.destroy(); } - + } diff --git a/src/Widgets/MediaRow.vala b/src/Widgets/MediaRow.vala index 352ab2ba..11466044 100644 --- a/src/Widgets/MediaRow.vala +++ b/src/Widgets/MediaRow.vala @@ -14,52 +14,52 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.mediaRow : Gtk.ListBoxRow { - + private Enclosure m_enc; - + public mediaRow(Enclosure enc) { m_enc = enc; - + int lastSlash = m_enc.get_url().last_index_of_char('/'); string fileName = m_enc.get_url().substring(lastSlash + 1); string icon_name = "image-x-generic-symbolic"; - + switch(enc.get_enclosure_type()) { case EnclosureType.IMAGE: icon_name = "image-x-generic-symbolic"; break; - + case EnclosureType.AUDIO: icon_name = "audio-speakers-symbolic"; break; - + case EnclosureType.VIDEO: icon_name = "media-playback-start-symbolic"; break; } var icon = new Gtk.Image.from_icon_name(icon_name, Gtk.IconSize.SMALL_TOOLBAR); - + var label = new Gtk.Label(GLib.Uri.unescape_string(fileName)); label.set_line_wrap_mode(Pango.WrapMode.WORD); label.set_ellipsize(Pango.EllipsizeMode.END); label.set_alignment(0.0f, 0.5f); label.get_style_context().add_class("h4"); - + var box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 3); box.margin = 3; box.pack_start(icon, false, false, 8); box.pack_start(label, true, true, 0); - + this.add(box); this.margin = 2; this.show_all(); } - + public string getURL() { return m_enc.get_url(); } - + } diff --git a/src/Widgets/ModeButton.vala b/src/Widgets/ModeButton.vala index f3b6903f..9517fd8d 100644 --- a/src/Widgets/ModeButton.vala +++ b/src/Widgets/ModeButton.vala @@ -21,14 +21,14 @@ */ namespace FeedReader { - + /** * This widget is a multiple option modal switch * * {{../../doc/images/ModeButton.png}} */ public class ModeButton : Gtk.Box { - + private class Item : Gtk.ToggleButton { public int index { get; construct; } public Item (int index) { @@ -37,11 +37,11 @@ namespace FeedReader { add_events (Gdk.EventMask.SCROLL_MASK); } } - + public signal void mode_added (int index, Gtk.Widget widget); public signal void mode_removed (int index, Gtk.Widget widget); public signal void mode_changed (Gtk.Widget widget); - + /** * Index of currently selected item. */ @@ -49,18 +49,18 @@ namespace FeedReader { get { return _selected; } set { set_active (value); } } - + /** * Read-only length of current ModeButton */ public uint n_items { get { return item_map.size; } } - + private int _selected = -1; private Gee.HashMap<int, Item> item_map; private uint m_timeout_source_id = 0; - + /** * Makes new ModeButton */ @@ -68,14 +68,14 @@ namespace FeedReader { homogeneous = true; spacing = 0; can_focus = false; - + item_map = new Gee.HashMap<int, Item> (); - + var style = get_style_context (); style.add_class (Gtk.STYLE_CLASS_LINKED); style.add_class ("raised"); // needed for toolbars } - + /** * Appends Pixbuf to ModeButton * @@ -84,7 +84,7 @@ namespace FeedReader { public int append_pixbuf (Gdk.Pixbuf pixbuf, string tooltip = "") { return append (new Gtk.Image.from_pixbuf (pixbuf), tooltip); } - + /** * Appends text to ModeButton * @@ -94,7 +94,7 @@ namespace FeedReader { public int append_text (string text, string tooltip = "") { return append (new Gtk.Label(text), tooltip); } - + /** * Appends icon to ModeButton * @@ -105,7 +105,7 @@ namespace FeedReader { public int append_icon (string icon_name, Gtk.IconSize size, string tooltip = "") { return append (new Gtk.Image.from_icon_name (icon_name, size), tooltip); } - + /** * Appends given widget to ModeButton * @@ -116,28 +116,28 @@ namespace FeedReader { int index; for (index = item_map.size; item_map.has_key (index); index++); assert (item_map[index] == null); - + w.set_tooltip_text(tooltip); - + var item = new Item (index); item.scroll_event.connect (on_scroll_event); item.add (w); - + item.button_press_event.connect (() => { set_active (item.index); return true; }); - + item_map[index] = item; - + add (item); item.show_all (); - + mode_added (index, w); - + return index; } - + /** * Sets item of given index's activity * @@ -146,26 +146,26 @@ namespace FeedReader { public void set_active (int new_active_index, bool initSet = false) { return_if_fail (item_map.has_key (new_active_index)); var new_item = item_map[new_active_index] as Item; - + if (new_item != null) { assert (new_item.index == new_active_index); new_item.set_active (true); - + if (_selected == new_active_index) { return; } - + // Unselect the previous item var old_item = item_map[_selected] as Item; if (old_item != null) { old_item.set_active (false); } - + _selected = new_active_index; - + if(!initSet) { if(m_timeout_source_id > 0) @@ -173,7 +173,7 @@ namespace FeedReader { GLib.Source.remove(m_timeout_source_id); m_timeout_source_id = 0; } - + m_timeout_source_id = GLib.Timeout.add(50, () => { mode_changed(new_item.get_child()); m_timeout_source_id = 0; @@ -182,7 +182,7 @@ namespace FeedReader { } } } - + /** * Changes visibility of item of given index * @@ -192,7 +192,7 @@ namespace FeedReader { public void set_item_visible (int index, bool val) { return_if_fail (item_map.has_key (index)); var item = item_map[index] as Item; - + if (item != null) { assert (item.index == index); @@ -200,7 +200,7 @@ namespace FeedReader { item.visible = val; } } - + /** * Removes item at given index * @@ -209,7 +209,7 @@ namespace FeedReader { public new void remove (int index) { return_if_fail (item_map.has_key (index)); var item = item_map[index] as Item; - + if (item != null) { assert (item.index == index); @@ -218,7 +218,7 @@ namespace FeedReader { item.destroy (); } } - + /** * Clears all children */ @@ -230,15 +230,15 @@ namespace FeedReader { base.remove (button); } } - + item_map.clear (); - + _selected = -1; } - + private bool on_scroll_event (Gtk.Widget widget, Gdk.EventScroll ev) { int offset; - + switch (ev.direction) { case Gdk.ScrollDirection.DOWN: case Gdk.ScrollDirection.RIGHT: @@ -251,36 +251,36 @@ namespace FeedReader { default: return false; } - + // Try to find a valid item, since there could be invisible items in // the middle and those shouldn't be selected. We use the children list // instead of item_map because order matters here. var children = get_children (); uint n_children = children.length (); - + var selected_item = item_map[selected]; if (selected_item == null) { return false; } - + int new_item = children.index (selected_item); if (new_item < 0) { return false; } - + do { new_item += offset; var item = children.nth_data (new_item) as Item; - + if (item != null && item.visible && item.sensitive) { selected = item.index; break; } } while (new_item >= 0 && new_item < n_children); - + return false; } } diff --git a/src/Widgets/RemovePopover.vala b/src/Widgets/RemovePopover.vala index 4b3d8cc3..b4450bf2 100644 --- a/src/Widgets/RemovePopover.vala +++ b/src/Widgets/RemovePopover.vala @@ -14,36 +14,36 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.RemovePopover : Gtk.Popover { - + private string m_id; private FeedListType m_type; private feedList m_feedlist; private uint m_time = 300; private string m_name; - + public RemovePopover(Gtk.Widget parent, FeedListType type, string id) { this.relative_to = parent; this.position = Gtk.PositionType.TOP; m_type = type; m_id = id; - + switch(m_type) { case FeedListType.TAG: m_name = DataBase.readOnly().getTagName(m_id); break; - + case FeedListType.FEED: var feed = DataBase.readOnly().read_feed(m_id); m_name = feed != null ? feed.getTitle() : ""; break; - + case FeedListType.CATEGORY: m_name = DataBase.readOnly().getCategoryName(m_id); break; } - + var removeButton = new Gtk.Button.with_label(_("Remove \"%s\"").printf(m_name)); removeButton.get_style_context().add_class(Gtk.STYLE_CLASS_DESTRUCTIVE_ACTION); removeButton.clicked.connect(removeX); @@ -51,36 +51,36 @@ public class FeedReader.RemovePopover : Gtk.Popover { this.add(removeButton); this.show_all(); } - + public void removeX() { m_feedlist = ColumnView.get_default().getFeedList(); m_feedlist.moveUP(); m_feedlist.revealRow(m_id, m_type, false, m_time); - + switch(m_type) { case FeedListType.TAG: removeTag(); break; - + case FeedListType.FEED: removeFeed(); break; - + case FeedListType.CATEGORY: removeCategory(); break; } - + this.hide(); } - + private void removeTag() { string text = _("Tag \"%s\" removed").printf(m_name); var notification = MainWindow.get_default().showNotification(text); - + ulong eventID = notification.dismissed.connect(() => { var tag = DataBase.readOnly().read_tag(m_id); if(tag != null) @@ -94,12 +94,12 @@ public class FeedReader.RemovePopover : Gtk.Popover { notification.dismiss(); }); } - + private void removeFeed() { string text = _("Feed \"%s\" removed").printf(m_name); var notification = MainWindow.get_default().showNotification(text); - + ulong eventID = notification.dismissed.connect(() => { FeedReaderBackend.get_default().removeFeed(m_id); }); @@ -109,13 +109,13 @@ public class FeedReader.RemovePopover : Gtk.Popover { notification.dismiss(); }); } - + private void removeCategory() { m_feedlist.expand_collapse_category(m_id, false); string text = _("Category \"%s\" removed").printf(m_name); var notification = MainWindow.get_default().showNotification(text); - + ulong eventID = notification.dismissed.connect(() => { FeedReaderBackend.get_default().removeCategory(m_id); }); diff --git a/src/Widgets/ResetPage.vala b/src/Widgets/ResetPage.vala index 5daab6c4..29b2422d 100644 --- a/src/Widgets/ResetPage.vala +++ b/src/Widgets/ResetPage.vala @@ -14,7 +14,7 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.ResetPage : Gtk.Bin { - + private Gtk.Box m_layout; private Gtk.Button m_newAccountButton; private Gtk.Label m_deleteLabel; @@ -23,28 +23,28 @@ public class FeedReader.ResetPage : Gtk.Bin { private Gtk.Spinner m_spinner; public signal void cancel(); public signal void reset(); - + public ResetPage() { m_reset = false; m_layout = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); m_layout.set_size_request(700, 410); - + var titleText = new Gtk.Label(_("Change Account?")); titleText.get_style_context().add_class("h1"); titleText.set_justify(Gtk.Justification.CENTER); - + var describtionText = new Gtk.Label(_("You are about to change the account you want FeedReader to use.\n This means deleting all local data of your old account.")); describtionText.get_style_context().add_class("h2"); describtionText.set_justify(Gtk.Justification.CENTER); - + m_deleteLabel = new Gtk.Label(_("New account")); m_waitingBox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 2); m_spinner = new Gtk.Spinner(); var waitingLabel = new Gtk.Label(_("Waiting for current sync to finish")); m_waitingBox.pack_start(m_spinner, false, false, 0); m_waitingBox.pack_start(waitingLabel, false, true, 0); - + var buttonBox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0); m_newAccountButton = new Gtk.Button(); m_newAccountButton.add(m_deleteLabel); @@ -56,20 +56,20 @@ public class FeedReader.ResetPage : Gtk.Bin { cancelButton.clicked.connect(abortReset); buttonBox.pack_start(cancelButton, false, false, 0); buttonBox.pack_end(m_newAccountButton, false, false, 0); - + m_layout.pack_start(titleText, false, true, 0); m_layout.pack_start(describtionText, true, true, 0); m_layout.pack_end(buttonBox, false, true, 0); - - + + this.set_halign(Gtk.Align.CENTER); this.set_valign(Gtk.Align.CENTER); this.margin = 20; this.add(m_layout); this.show_all(); } - - + + private void resetAllData() { if(Settings.state().get_boolean("currently-updating")) @@ -81,40 +81,40 @@ public class FeedReader.ResetPage : Gtk.Bin { m_spinner.start(); m_newAccountButton.set_sensitive(false); FeedReaderBackend.get_default().cancelSync(); - + while(Settings.state().get_boolean("currently-updating")) { Gtk.main_iteration(); } - + if(!m_reset) { return; } } - + // set "currently-updating" ourself to prevent the backend to start sync Settings.state().set_boolean("currently-updating", true); - + // clear all data from UI ColumnView.get_default().clear(); - + Settings.general().reset("plugin"); Utils.resetSettings(Settings.state()); FeedReaderBackend.get_default().resetDB(); FeedReaderBackend.get_default().resetAccount(); - + Utils.remove_directory(GLib.Environment.get_user_data_dir() + "/feedreader/data/images/"); - + Settings.state().set_boolean("currently-updating", false); FeedReaderBackend.get_default().login("none"); - + // Load all available plugins, to present them on the login page FeedServer.get_default().LoadAllPlugins(); - + reset(); } - + private void abortReset() { m_reset = false; diff --git a/src/Widgets/ServiceInfo.vala b/src/Widgets/ServiceInfo.vala index 886171fb..1a0de37e 100644 --- a/src/Widgets/ServiceInfo.vala +++ b/src/Widgets/ServiceInfo.vala @@ -20,7 +20,7 @@ public class FeedReader.ServiceInfo : Gtk.Overlay { private Gtk.Label m_label; private Gtk.Label m_offline; private Gtk.Box m_box; - + public ServiceInfo() { m_logo = new Gtk.Image(); @@ -30,22 +30,22 @@ public class FeedReader.ServiceInfo : Gtk.Overlay { m_label.margin_start = 10; m_label.margin_end = 10; m_label.set_ellipsize(Pango.EllipsizeMode.END); - + m_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); m_box.pack_start(m_logo, false, false, 0); m_box.pack_start(m_label, false, false, 5); m_box.margin_top = 20; m_box.margin_bottom = 5; - + m_spinner = new Gtk.Spinner(); m_spinner.set_size_request(32,32); - + m_stack = new Gtk.Stack(); m_stack.add_named(m_box, "info"); m_stack.add_named(m_spinner, "spinner"); m_stack.get_style_context().add_class("fr-sidebar"); this.add(m_stack); - + m_offline = new Gtk.Label("OFFLINE"); m_offline.margin_start = 40; m_offline.margin_end = 40; @@ -55,13 +55,13 @@ public class FeedReader.ServiceInfo : Gtk.Overlay { m_offline.no_show_all = true; this.add_overlay(m_offline); } - + public void refresh() { string? service_icon = FeedReaderBackend.get_default().symbolicIcon(); string? user_name = FeedReaderBackend.get_default().accountName(); string? server = FeedReaderBackend.get_default().getServerURL(); - + if(this.is_visible()) { if(user_name == "none" || service_icon == "none") @@ -81,15 +81,15 @@ public class FeedReader.ServiceInfo : Gtk.Overlay { } } } - + show_all(); } - + public void setOffline() { m_offline.show(); } - + public void setOnline() { m_offline.hide(); diff --git a/src/Widgets/ServiceSettingsPopover.vala b/src/Widgets/ServiceSettingsPopover.vala index c8dcefea..ed65c4bf 100644 --- a/src/Widgets/ServiceSettingsPopover.vala +++ b/src/Widgets/ServiceSettingsPopover.vala @@ -14,10 +14,10 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.ServiceSettingsPopover : Gtk.Popover { - + public signal void newAccount(string type); - - + + public ServiceSettingsPopover(Gtk.Widget widget) { var list = new Gtk.ListBox(); @@ -27,61 +27,61 @@ public class FeedReader.ServiceSettingsPopover : Gtk.Popover { newAccount(((ServiceSettingsPopoverRow)row).getType()); this.hide(); }); - + foreach(var account in Share.get_default().getAccountTypes()) { var row = new ServiceSettingsPopoverRow(account.getAccountName(), account.getType(), account.getIconName()); list.add(row); } - + this.add(list); this.set_modal(true); this.set_relative_to(widget); this.set_position(Gtk.PositionType.BOTTOM); this.show_all(); } - + } public class FeedReader.ServiceSettingsPopoverRow : Gtk.ListBoxRow { - + private string m_name; private Gtk.Label m_label; private Gtk.Box m_box; private string m_type; - + public ServiceSettingsPopoverRow(string serviceName, string type, string iconName) { m_type = type; m_name = serviceName; m_box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 3); m_box.margin = 3; - + var icon = new Gtk.Image.from_icon_name(iconName, Gtk.IconSize.DND); - + m_label = new Gtk.Label(serviceName); m_label.set_line_wrap_mode(Pango.WrapMode.WORD); m_label.set_ellipsize(Pango.EllipsizeMode.END); m_label.set_alignment(0.5f, 0.5f); m_label.set_justify(Gtk.Justification.LEFT); m_label.set_halign(Gtk.Align.START); - + m_box.pack_start(icon, false, false, 8); m_box.pack_start(m_label, true, true, 0); - + this.add(m_box); this.show_all(); } - + public string getType() { return m_type; } - + public string getName() { return m_name; } - + } diff --git a/src/Widgets/Setting.vala b/src/Widgets/Setting.vala index 34f5d33e..45801cb5 100644 --- a/src/Widgets/Setting.vala +++ b/src/Widgets/Setting.vala @@ -14,30 +14,30 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.Setting : Gtk.Box { - + public signal void changed(); private Gtk.Label m_label; - - + + public Setting(string name, string? tooltip = null) { this.orientation = Gtk.Orientation.HORIZONTAL; this.spacing = 0; - + m_label = new Gtk.Label(name); m_label.set_alignment(0, 0.5f); m_label.margin_start = 15; m_label.set_tooltip_text(tooltip); - + this.pack_start(m_label, true, true, 0); } - - + + } public class FeedReader.SettingFont : FeedReader.Setting { - + public SettingFont(string name, GLib.Settings settings, string key){ base(name, null); var current_font = settings.get_value(key).get_maybe(); @@ -46,33 +46,33 @@ public class FeedReader.SettingFont : FeedReader.Setting { { font_button.font = current_font.get_string(); } - + font_button.set_use_size(false); font_button.set_show_size(true); font_button.font_set.connect(() => { var new_font = new Variant.string(font_button.get_font_name()); settings.set_value(key, new Variant.maybe(VariantType.STRING, new_font)); }); - + this.pack_end(font_button, false, false, 0); } - + } public class FeedReader.SettingDropbox : FeedReader.Setting { - + public SettingDropbox(string name, GLib.Settings settings, string key, string[] values, string? tooltip = null) { base(name, tooltip); var liststore = new Gtk.ListStore(1, typeof(string)); - + foreach(string val in values) { Gtk.TreeIter iter; liststore.append(out iter); liststore.set(iter, 0, val); } - + var dropbox = new Gtk.ComboBox.with_model(liststore); var renderer = new Gtk.CellRendererText(); dropbox.pack_start(renderer, false); @@ -82,45 +82,45 @@ public class FeedReader.SettingDropbox : FeedReader.Setting { settings.set_enum(key, dropbox.get_active()); changed(); }); - + this.pack_end(dropbox, false, false, 0); } } public class FeedReader.SettingSwitch : FeedReader.Setting { - + public SettingSwitch(string name, GLib.Settings settings, string key, string? tooltip = null) { base(name, tooltip); - + var Switch = new Gtk.Switch(); Switch.active = settings.get_boolean(key); - + Switch.notify["active"].connect(() => { settings.set_boolean(key, Switch.active); changed(); }); - + this.pack_end(Switch, false, false, 0); } } public class FeedReader.SettingSpin : FeedReader.Setting { - + public SettingSpin(string name, GLib.Settings settings, string key, int min, int max, int step, string? tooltip = null) { base(name, tooltip); - + var spin = new Gtk.SpinButton.with_range(min, max, step); spin.set_value(settings.get_int(key)); - + spin.value_changed.connect(() => { settings.set_int(key, spin.get_value_as_int()); changed(); }); - + this.pack_end(spin, false, false, 0); } } diff --git a/src/Widgets/SettingsDialog.vala b/src/Widgets/SettingsDialog.vala index a00ff6d5..726c8aff 100644 --- a/src/Widgets/SettingsDialog.vala +++ b/src/Widgets/SettingsDialog.vala @@ -14,23 +14,23 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.SettingsDialog : Gtk.Dialog { - + private Gtk.ListBox m_serviceList; private Gtk.Stack m_stack; private InfoBar m_errorBar; private Gtk.HeaderBar m_headerbar; private static SettingsDialog? m_dialog = null; - + public static SettingsDialog get_default() { if(m_dialog == null) { m_dialog = new SettingsDialog(); } - + return m_dialog; } - + private SettingsDialog() { this.border_width = 20; @@ -38,11 +38,11 @@ public class FeedReader.SettingsDialog : Gtk.Dialog { this.set_modal(true); this.delete_event.connect(hide_on_delete); set_default_size(550, 450); - + m_headerbar = new Gtk.HeaderBar(); m_headerbar.set_show_close_button(true); set_titlebar(m_headerbar); - + m_stack = new Gtk.Stack(); m_stack.set_transition_duration(50); m_stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE); @@ -50,76 +50,76 @@ public class FeedReader.SettingsDialog : Gtk.Dialog { m_stack.add_titled(setup_UI(), "ui", _("Interface")); m_stack.add_titled(setup_Internal(), "internal", _("Internals")); m_stack.add_titled(setup_Service(), "service", _("Share")); - + Gtk.StackSwitcher switcher = new Gtk.StackSwitcher(); switcher.set_halign(Gtk.Align.CENTER); switcher.set_valign(Gtk.Align.CENTER); switcher.set_stack(m_stack); - + m_headerbar.set_custom_title(switcher); - + var content = get_content_area() as Gtk.Box; content.add(m_stack); } - + public void showDialog(string panel) { this.show_all(); m_stack.set_visible_child_name(panel); } - + private Gtk.Box setup_UI() { var feed_settings = headline(_("Feed List:")); - + var only_feeds = new SettingSwitch(_("Only show feeds"), Settings.general(), "only-feeds"); only_feeds.changed.connect(() => { Settings.state().set_strv("expanded-categories", Utils.getDefaultExpandedCategories()); Settings.state().set_string("feedlist-selected-row", "feed -4"); ColumnView.get_default().newFeedList(true); }); - + var only_unread = new SettingSwitch(_("Only show unread"), Settings.general(), "feedlist-only-show-unread"); only_unread.changed.connect(() => { ColumnView.get_default().newFeedList(); }); - + var feedlist_sort = new SettingDropbox(_("Sort Feed List by"), Settings.general(), "feedlist-sort-by", {_("Received"), _("Alphabetically")}); feedlist_sort.changed.connect(() => { ColumnView.get_default().newFeedList(); }); - + var feedlist_theme = new SettingDropbox(_("Theme"), Settings.general(), "feedlist-theme", {_("Gtk+"), _("Dark"), _("elementary")}); feedlist_theme.changed.connect(() => { MainWindow.get_default().reloadCSS(); }); - + var article_settings = headline(_("Article List:")); - + var article_sort = new SettingDropbox(_("Sort articles by"), Settings.general(), "articlelist-sort-by", {_("Received"), _("Date")}); article_sort.changed.connect(() => { ColumnView.get_default().newArticleList(); }); - + var newest_first = new SettingSwitch(_("Oldest first"), Settings.general(), "articlelist-oldest-first", _("Only affects \"Unread\" column")); newest_first.changed.connect(() => { ColumnView.get_default().newArticleList(); }); - + var scroll_marked = new SettingSwitch(_("Mark read by scrolling past"), Settings.general(), "articlelist-mark-scrolling"); - + var articleview_settings = headline(_("Article View:")); - + var article_theme = new SettingDropbox(_("Theme"), Settings.general(), "article-theme", {_("Default"), _("Spring"), _("Midnight"), _("Parchment")}); article_theme.changed.connect(() => { ColumnView.get_default().reloadArticleView(); }); - + var fontfamilly = new SettingFont(_("Font Familly"), Settings.general(), "font"); fontfamilly.changed.connect(() => { ColumnView.get_default().reloadArticleView(); }); - + var uiBox = new Gtk.Box(Gtk.Orientation.VERTICAL, 5); uiBox.expand = true; uiBox.pack_start(feed_settings, false, true, 0); @@ -136,33 +136,33 @@ public class FeedReader.SettingsDialog : Gtk.Dialog { uiBox.pack_start(fontfamilly, false, true, 0); return uiBox; } - - + + private Gtk.Box setup_Internal() { var sync_settings = headline(_("Sync:")); - + var sync_count = new SettingSpin(_("Number of articles"), Settings.general(), "max-articles", 10, 5000, 10); - + var sync_time = new SettingSpin(_("Interval in Minutes (0 = OFF)"), Settings.general(), "sync", 0, 600, 5); sync_time.changed.connect(() => { FeedReaderBackend.get_default().scheduleSync(Settings.general().get_int("sync")); }); - + var db_settings = headline(_("Database:")); - + var drop_articles = new SettingDropbox(_("Delete articles after"), Settings.general(), "drop-articles-after", {_("Never"), _("1 Week"), _("1 Month"), _("6 Months")}); - + var service_settings = headline(_("Additional Functionality:")); - + var grabber = new SettingSwitch(_("Content Grabber"), Settings.general(),"content-grabber"); - + var images = new SettingSwitch(_("Download Images"), Settings.general(),"download-images"); - + var mediaplayer = new SettingSwitch(_("Internal Media Player"), Settings.general(),"mediaplayer"); - - + + var internalsBox = new Gtk.Box(Gtk.Orientation.VERTICAL, 5); internalsBox.expand = true; internalsBox.pack_start(sync_settings, false, true, 0); @@ -177,42 +177,42 @@ public class FeedReader.SettingsDialog : Gtk.Dialog { internalsBox.pack_start(grabber, false, true, 0); internalsBox.pack_start(images, false, true, 0); internalsBox.pack_start(mediaplayer, false, true, 0); - + return internalsBox; } - - + + private Gtk.Box setup_Service() { m_serviceList = new Gtk.ListBox(); m_serviceList.set_selection_mode(Gtk.SelectionMode.NONE); m_serviceList.set_sort_func(sortFunc); - + m_errorBar = new InfoBar(""); - + var service_scroll = new Gtk.ScrolledWindow(null, null); service_scroll.expand = true; - + var overlay = new Gtk.Overlay(); overlay.add(service_scroll); overlay.add_overlay(m_errorBar); overlay.margin_top = 10; overlay.margin_bottom = 10; - + var viewport = new Gtk.Viewport(null, null); viewport.get_style_context().add_class("servicebox"); viewport.add(m_serviceList); service_scroll.add(viewport); - + refreshAccounts(); - + var serviceBox = new Gtk.Box(Gtk.Orientation.VERTICAL, 5); serviceBox.expand = true; serviceBox.pack_start(overlay, false, true, 0); - + return serviceBox; } - + public void refreshAccounts() { m_serviceList.set_header_func(null); @@ -222,9 +222,9 @@ public class FeedReader.SettingsDialog : Gtk.Dialog { m_serviceList.remove(row); row.destroy(); } - + var list = Share.get_default().getAccounts(); - + foreach(var account in list) { if(account.isSystemAccount()) @@ -243,14 +243,14 @@ public class FeedReader.SettingsDialog : Gtk.Dialog { row.reveal(false); } } - + var addAccount = new Gtk.Button.from_icon_name("list-add-symbolic", Gtk.IconSize.DND); addAccount.set_relief(Gtk.ReliefStyle.NONE); addAccount.get_style_context().add_class("addServiceButton"); addAccount.set_size_request(0, 48); addAccount.show(); m_serviceList.add(addAccount); - + addAccount.clicked.connect(() => { children = m_serviceList.get_children(); foreach(Gtk.Widget row in children) @@ -262,7 +262,7 @@ public class FeedReader.SettingsDialog : Gtk.Dialog { removeRow(tmpRow, m_serviceList); } } - + var popover = new ServiceSettingsPopover(addAccount); popover.newAccount.connect((type) => { ServiceSetup row = Share.get_default().newSetup(type); @@ -277,10 +277,10 @@ public class FeedReader.SettingsDialog : Gtk.Dialog { row.reveal(); }); }); - + m_serviceList.set_header_func(headerFunc); } - + public void removeRow(ServiceSetup row, Gtk.ListBox list) { row.unreveal(); @@ -289,12 +289,12 @@ public class FeedReader.SettingsDialog : Gtk.Dialog { return false; }); } - + private int sortFunc(Gtk.ListBoxRow row1, Gtk.ListBoxRow row2) { var r1 = row1 as ServiceSetup; var r2 = row2 as ServiceSetup; - + if(r1 == null && r2 == null) { return 0; @@ -307,7 +307,7 @@ public class FeedReader.SettingsDialog : Gtk.Dialog { { return -1; } - + if(r1.getUserName() == "" && r2.getUserName() == "") { @@ -321,10 +321,10 @@ public class FeedReader.SettingsDialog : Gtk.Dialog { { return -1; } - + bool sys1 = r1.isSystemAccount(); bool sys2 = r2.isSystemAccount(); - + if(sys1 && sys2) { return 0; @@ -333,32 +333,32 @@ public class FeedReader.SettingsDialog : Gtk.Dialog { { return -1; } - + return 1; } - + private void headerFunc(Gtk.ListBoxRow row, Gtk.ListBoxRow? before) { var label = new Gtk.Label(_("System Accounts")); label.get_style_context().add_class("bold"); label.margin_top = 20; label.margin_bottom = 5; - + var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); box.pack_start(label, true, true, 0); box.pack_end(new Gtk.Separator(Gtk.Orientation.HORIZONTAL), false, false, 0); box.show_all(); - + var r1 = row as ServiceSetup; - + // this is the plus-button if(r1 == null) { return; } - + bool sys1 = r1.isSystemAccount(); - + if(before == null) { if(sys1) @@ -373,11 +373,11 @@ public class FeedReader.SettingsDialog : Gtk.Dialog { return; } } - - + + var r2 = before as ServiceSetup; bool sys2 = r2.isSystemAccount(); - + if(r1 != null && r2 != null) { if(!sys1 && sys2) @@ -387,7 +387,7 @@ public class FeedReader.SettingsDialog : Gtk.Dialog { } } } - + private Gtk.Label headline(string name) { var headline = new Gtk.Label(name); diff --git a/src/Widgets/SharePopover.vala b/src/Widgets/SharePopover.vala index b8357f4b..0998f92a 100644 --- a/src/Widgets/SharePopover.vala +++ b/src/Widgets/SharePopover.vala @@ -14,12 +14,12 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.SharePopover : Gtk.Popover { - + private Gtk.ListBox m_list; private Gtk.Stack m_stack; public signal void startShare(); public signal void shareDone(); - + public SharePopover(Gtk.Widget widget) { m_list = new Gtk.ListBox(); @@ -31,14 +31,14 @@ public class FeedReader.SharePopover : Gtk.Popover { m_stack.set_transition_duration(150); m_stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT); m_stack.add_named(m_list, "list"); - + this.add(m_stack); this.set_modal(true); this.set_relative_to(widget); this.set_position(Gtk.PositionType.BOTTOM); this.show_all(); } - + public void refreshList() { var children = m_list.get_children(); @@ -47,29 +47,29 @@ public class FeedReader.SharePopover : Gtk.Popover { m_list.remove(row); row.destroy(); } - + var list = Share.get_default().getAccounts(); - + foreach(var account in list) { m_list.add(new ShareRow(account.getType(), account.getID(), account.getUsername(), account.getIconName())); } - + var addRow = new Gtk.ListBoxRow(); addRow.margin = 2; - + var addIcon = new Gtk.Image.from_icon_name("list-add-symbolic", Gtk.IconSize.DND); var addLabel = new Gtk.Label(_("Add accounts")); addLabel.set_line_wrap_mode(Pango.WrapMode.WORD); addLabel.set_ellipsize(Pango.EllipsizeMode.END); addLabel.set_alignment(0.0f, 0.5f); addLabel.get_style_context().add_class("h4"); - + var addBox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 5); addBox.margin = 3; addBox.pack_start(addIcon, false, false, 8); addBox.pack_start(addLabel, true, true, 0); - + if(list.size > 0) { var seperatorBox = new Gtk.Box(Gtk.Orientation.VERTICAL, 5); @@ -81,15 +81,15 @@ public class FeedReader.SharePopover : Gtk.Popover { { addRow.add(addBox); } - + addRow.show_all(); m_list.add(addRow); } - + private void clicked(Gtk.ListBoxRow row) { ShareRow? shareRow = row as ShareRow; - + if(shareRow == null) { this.hide(); @@ -97,10 +97,10 @@ public class FeedReader.SharePopover : Gtk.Popover { Logger.debug("SharePopover: open Settings"); return; } - + string id = shareRow.getID(); Article? selectedArticle = ColumnView.get_default().getSelectedArticle(); - + if(selectedArticle != null) { var widget = Share.get_default().shareWidget(shareRow.getType(), selectedArticle.getURL()); @@ -121,14 +121,14 @@ public class FeedReader.SharePopover : Gtk.Popover { }); } } - + } - + private void shareInternal(string id, string url) { Share.get_default().addBookmark(id, url); } - + private void shareURL(string id, string url) { this.hide(); @@ -140,13 +140,13 @@ public class FeedReader.SharePopover : Gtk.Popover { } public class FeedReader.ShareForm : Gtk.Box { - + public signal void share(); public signal void goBack(); - + public ShareForm() { - + } - + } diff --git a/src/Widgets/ShareRow.vala b/src/Widgets/ShareRow.vala index 3a0bd686..b900f0f2 100644 --- a/src/Widgets/ShareRow.vala +++ b/src/Widgets/ShareRow.vala @@ -14,10 +14,10 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.ShareRow : Gtk.ListBoxRow { - + private string m_id; private string m_type; - + public ShareRow(string type, string id, string username, string iconName) { m_id = id; @@ -29,25 +29,25 @@ public class FeedReader.ShareRow : Gtk.ListBoxRow { serviceLabel.set_ellipsize(Pango.EllipsizeMode.END); serviceLabel.set_alignment(0.0f, 0.5f); serviceLabel.get_style_context().add_class("h4"); - + var box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 3); box.margin = 3; box.pack_start(icon, false, false, 8); box.pack_start(serviceLabel, true, true, 0); - + this.add(box); this.margin = 2; this.show_all(); } - + public string getID() { return m_id; } - + public string getType() { return m_type; } - + } diff --git a/src/Widgets/ShortcutsWindow.vala b/src/Widgets/ShortcutsWindow.vala index 3b5e88fb..a74e61aa 100644 --- a/src/Widgets/ShortcutsWindow.vala +++ b/src/Widgets/ShortcutsWindow.vala @@ -14,10 +14,10 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.ShortcutsWindow : Gtk.ShortcutsWindow { - + public ShortcutsWindow(Gtk.Window parent) { - + //-------------------------------------------------- var general = newGroup(_("General")); //-------------------------------------------------- @@ -31,8 +31,8 @@ public class FeedReader.ShortcutsWindow : Gtk.ShortcutsWindow { general.add(search); general.add(quit); //-------------------------------------------------- - - + + //-------------------------------------------------- var feedList = newGroup(_("Feed List")); //-------------------------------------------------- @@ -48,8 +48,8 @@ public class FeedReader.ShortcutsWindow : Gtk.ShortcutsWindow { feedList.add(expCol); feedList.add(flmark); //-------------------------------------------------- - - + + //-------------------------------------------------- var articleList = newGroup(_("Article List")); //-------------------------------------------------- @@ -73,8 +73,8 @@ public class FeedReader.ShortcutsWindow : Gtk.ShortcutsWindow { articleList.add(upDown); articleList.add(centerSelected); //-------------------------------------------------- - - + + //-------------------------------------------------- var articleView = newGroup(_("Article View")); //-------------------------------------------------- @@ -84,8 +84,8 @@ public class FeedReader.ShortcutsWindow : Gtk.ShortcutsWindow { var AVupdown = newShortcut(_("Scroll up/down"), AVupDown); articleView.add(AVupdown); //-------------------------------------------------- - - + + //-------------------------------------------------- var section = newSection("test", "section", 10); //-------------------------------------------------- @@ -94,33 +94,33 @@ public class FeedReader.ShortcutsWindow : Gtk.ShortcutsWindow { section.add(articleList); section.add(articleView); //-------------------------------------------------- - - + + this.add(section); this.set_transient_for(parent); this.set_modal(true); this.show_all(); } - + private Gtk.ShortcutsSection newSection(string title, string section_name, int maxHeight) { var section = (Gtk.ShortcutsSection)Object.new(typeof(Gtk.ShortcutsSection), title : title, section_name : section_name, max_height : maxHeight); section.show(); return section; } - + private Gtk.ShortcutsGroup newGroup(string title) { var group = (Gtk.ShortcutsGroup)Object.new(typeof(Gtk.ShortcutsGroup), title : title); group.show(); return group; } - + private Gtk.ShortcutsShortcut newShortcut(string title, string key) { var shortcut = (Gtk.ShortcutsShortcut)Object.new(typeof(Gtk.ShortcutsShortcut), title : title, accelerator : key); shortcut.show(); return shortcut; } - + } diff --git a/src/Widgets/SimpleHeader.vala b/src/Widgets/SimpleHeader.vala index 78277584..9dac6efc 100644 --- a/src/Widgets/SimpleHeader.vala +++ b/src/Widgets/SimpleHeader.vala @@ -16,9 +16,9 @@ public class FeedReader.SimpleHeader : Gtk.HeaderBar { private Gtk.Button m_backButton; - + public signal void back(); - + public SimpleHeader() { m_backButton = new Gtk.Button.from_icon_name("go-previous-symbolic"); @@ -26,15 +26,15 @@ public class FeedReader.SimpleHeader : Gtk.HeaderBar m_backButton.clicked.connect(() => { back(); }); - + this.pack_start(m_backButton); this.show_close_button = true; this.set_title("FeedReader"); } - + public void showBackButton(bool show) { m_backButton.visible = show; } - + } diff --git a/src/Widgets/SpringCleanPage.vala b/src/Widgets/SpringCleanPage.vala index cb69d173..bfcd3ce6 100644 --- a/src/Widgets/SpringCleanPage.vala +++ b/src/Widgets/SpringCleanPage.vala @@ -14,17 +14,17 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.SpringCleanPage : Gtk.Bin { - + private Gtk.Spinner m_spinner; private Gtk.Box m_spinnerBox; - + public SpringCleanPage() { m_spinnerBox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 20); - + m_spinner = new Gtk.Spinner(); m_spinner.set_size_request(40, 40); m_spinner.start(); - + var label = new Gtk.Label(_("FeedReader is cleaning the database.\nThis shouldn't take too long.")); label.get_style_context().add_class("h2"); label.set_alignment(0, 0.5f); @@ -32,10 +32,10 @@ public class FeedReader.SpringCleanPage : Gtk.Bin { label.set_line_wrap_mode(Pango.WrapMode.WORD_CHAR); label.set_line_wrap(true); label.set_lines(2); - + m_spinnerBox.pack_start(m_spinner, false, false, 0); m_spinnerBox.pack_end(label, false, false, 0); - + this.set_halign(Gtk.Align.CENTER); this.set_valign(Gtk.Align.CENTER); this.margin = 20; diff --git a/src/Widgets/TagPopover.vala b/src/Widgets/TagPopover.vala index 82f5ffe8..ccb31c17 100644 --- a/src/Widgets/TagPopover.vala +++ b/src/Widgets/TagPopover.vala @@ -14,7 +14,7 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.TagPopover : Gtk.Popover { - + private Gtk.ListBox m_list; private Gtk.Box m_box; private Gtk.Viewport m_viewport; @@ -23,7 +23,7 @@ public class FeedReader.TagPopover : Gtk.Popover { private Gee.List<Tag> m_tags; private Gtk.EntryCompletion m_complete; private Gee.List<Tag> m_availableTags; - + public TagPopover(Gtk.Widget widget) { m_availableTags = new Gee.ArrayList<Tag>(); @@ -45,23 +45,23 @@ public class FeedReader.TagPopover : Gtk.Popover { } } } - + m_stack = new Gtk.Stack(); m_stack.set_transition_type(Gtk.StackTransitionType.NONE); m_stack.set_transition_duration(0); - + var empty_label = new Gtk.Label(_("Add Tag:")); empty_label.get_style_context().add_class("h4"); empty_label.set_alignment(0, 0.5f); m_stack.add_named(empty_label, "empty"); - + m_list = new Gtk.ListBox(); m_list.margin = 2; m_list.set_size_request(150, 0); m_list.set_selection_mode(Gtk.SelectionMode.NONE); m_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); m_box.margin = 10; - + var tag_label = new Gtk.Label(_("Tags:")); tag_label.get_style_context().add_class("h4"); tag_label.set_alignment(0, 0.5f); @@ -73,20 +73,20 @@ public class FeedReader.TagPopover : Gtk.Popover { box.pack_start(tag_label); box.pack_start(m_viewport); m_stack.add_named(box, "tags"); - - + + setupEntry(); populateList(); - - + + m_box.pack_start(m_stack); m_box.pack_start(m_entry); - + this.add(m_box); this.set_relative_to(widget); this.set_position(Gtk.PositionType.BOTTOM); this.show_all(); - + if(m_tags.size == 0) { m_stack.set_visible_child_name("empty"); @@ -96,8 +96,8 @@ public class FeedReader.TagPopover : Gtk.Popover { m_stack.set_visible_child_name("tags"); } } - - + + private void populateList() { foreach(Tag t in m_tags) @@ -107,23 +107,23 @@ public class FeedReader.TagPopover : Gtk.Popover { m_list.add(row); } } - + private void prepareCompletion() { m_complete = new Gtk.EntryCompletion(); m_entry.set_completion(m_complete); - + Gtk.ListStore list_store = new Gtk.ListStore(1, typeof (string)); m_complete.set_model(list_store); m_complete.set_text_column(0); Gtk.TreeIter iter; - + var tags = DataBase.readOnly().read_tags(); - + foreach(Tag tag in tags) { bool alreadyHasTag = false; - + foreach(Tag tag2 in m_tags) { if(tag2.getTitle() == tag.getTitle()) @@ -131,7 +131,7 @@ public class FeedReader.TagPopover : Gtk.Popover { alreadyHasTag = true; } } - + if(!alreadyHasTag) { list_store.append(out iter); @@ -140,7 +140,7 @@ public class FeedReader.TagPopover : Gtk.Popover { } } } - + private void setupEntry() { m_entry = new Gtk.Entry(); @@ -161,7 +161,7 @@ public class FeedReader.TagPopover : Gtk.Popover { } bool available = false; Tag? selectedTag = null; - + foreach(Tag tag in m_tags) { if(str == tag.getTitle()) @@ -171,7 +171,7 @@ public class FeedReader.TagPopover : Gtk.Popover { return; } } - + foreach(Tag tag in m_availableTags) { if(str == tag.getTitle()) @@ -182,30 +182,30 @@ public class FeedReader.TagPopover : Gtk.Popover { break; } } - + if(!available) { selectedTag = FeedReaderBackend.get_default().createTag(str); Logger.debug("TagPopover: %s created with id %s".printf(str, selectedTag.getTagID())); } FeedReaderBackend.get_default().tagArticle(getActiveArticleID(), selectedTag, true); - - + + var row = new TagPopoverRow(selectedTag); row.remove_tag.connect(removeTag); m_list.add(row); m_stack.set_visible_child_name("tags"); m_entry.set_text(""); }); - + prepareCompletion(); } - + private void removeTag(TagPopoverRow row) { FeedReaderBackend.get_default().tagArticle(getActiveArticleID(), row.getTag(), false); m_list.remove(row); - + foreach(Tag tag in m_tags) { if(tag.getTagID() == row.getTagID()) @@ -214,24 +214,24 @@ public class FeedReader.TagPopover : Gtk.Popover { break; } } - + if(m_list.get_children().length() == 0) { m_stack.set_visible_child_name("empty"); this.show_all(); } - + ColumnView.get_default().removeTagFromSelectedRow(row.getTagID()); } - + private Article getActiveArticleID() { return ColumnView.get_default().getSelectedArticle(); } - + public bool entryFocused() { return m_entry.has_focus; } - + } diff --git a/src/Widgets/TagPopoverRow.vala b/src/Widgets/TagPopoverRow.vala index b41bd006..4b659f8d 100644 --- a/src/Widgets/TagPopoverRow.vala +++ b/src/Widgets/TagPopoverRow.vala @@ -14,14 +14,14 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.TagPopoverRow : Gtk.ListBoxRow { - + private Gtk.Revealer m_revealer; private Gtk.Box m_box; private Tag m_tag; private Gtk.Image m_clear; private Gtk.EventBox m_eventbox; public signal void remove_tag(TagPopoverRow row); - + public TagPopoverRow(Tag tag) { m_tag = tag; @@ -34,7 +34,7 @@ public class FeedReader.TagPopoverRow : Gtk.ListBoxRow { m_clear = new Gtk.Image.from_icon_name("edit-clear-symbolic", Gtk.IconSize.MENU); m_clear.margin_end = 5; m_clear.opacity = 0.7; - + m_eventbox = new Gtk.EventBox(); m_eventbox.set_events(Gdk.EventMask.ENTER_NOTIFY_MASK); m_eventbox.set_events(Gdk.EventMask.LEAVE_NOTIFY_MASK); @@ -43,47 +43,47 @@ public class FeedReader.TagPopoverRow : Gtk.ListBoxRow { m_eventbox.leave_notify_event.connect(onLeave); m_eventbox.button_press_event.connect(onClick); m_eventbox.add(m_clear); - + m_box.pack_start(circle, false, false, 0); m_box.pack_start(label, true, true, 0); m_box.pack_end(m_eventbox, false, false, 0); - + m_revealer = new Gtk.Revealer(); m_revealer.set_transition_type(Gtk.RevealerTransitionType.SLIDE_DOWN); m_revealer.set_transition_duration(150); m_revealer.add(m_box); m_revealer.set_reveal_child(true); - + this.add(m_revealer); this.margin_top = 1; this.margin_bottom = 1; this.show_all(); } - + private bool onEnter() { m_clear.opacity = 1.0; return false; } - + private bool onLeave() { m_clear.opacity = 0.7; return false; } - + private bool onClick() { m_revealer.set_reveal_child(false); remove_tag(this); return false; } - + public string getTagID() { return m_tag.getTagID(); } - + public Tag getTag() { return m_tag; diff --git a/src/Widgets/TagRow.vala b/src/Widgets/TagRow.vala index d3ed59d1..4a823f11 100644 --- a/src/Widgets/TagRow.vala +++ b/src/Widgets/TagRow.vala @@ -14,7 +14,7 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.TagRow : Gtk.ListBoxRow { - + private Gtk.Box m_box; private Gtk.Label m_label; private bool m_exits; @@ -27,7 +27,7 @@ public class FeedReader.TagRow : Gtk.ListBoxRow { public Tag m_tag; public signal void moveUP(); public signal void removeRow(); - + public TagRow (Tag tag) { m_tag = tag; @@ -35,92 +35,92 @@ public class FeedReader.TagRow : Gtk.ListBoxRow { m_exits = true; m_name = m_tag.getTitle().replace("&","&"); m_catID = CategoryID.TAGS.to_string(); - + var rowhight = 30; m_box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0); - + m_circle = new ColorCircle(m_tag.getColor()); m_circle.margin_start = 24; m_pop = new ColorPopover(m_circle); - + m_circle.clicked.connect((color) => { m_pop.show_all(); }); - + m_pop.newColorSelected.connect((color) => { m_circle.newColor(color); m_tag.setColor(color); FeedReaderBackend.get_default().updateTagColor(m_tag); }); - + m_label = new Gtk.Label(m_name); m_label.set_use_markup (true); m_label.set_size_request (0, rowhight); m_label.set_ellipsize (Pango.EllipsizeMode.END); m_label.set_alignment(0, 0.5f); - + m_box.pack_start(m_circle, false, false, 8); m_box.pack_start(m_label, true, true, 0); - + m_revealer = new Gtk.Revealer(); m_revealer.set_transition_type(Gtk.RevealerTransitionType.SLIDE_DOWN); m_revealer.add(m_box); m_revealer.set_reveal_child(false); - + m_eventBox = new Gtk.EventBox(); m_eventBox.set_events(Gdk.EventMask.BUTTON_PRESS_MASK); m_eventBox.button_press_event.connect(onClick); m_eventBox.add(m_revealer); - + this.add(m_eventBox); this.show_all(); - + if(Utils.canManipulateContent()) { const Gtk.TargetEntry[] accepted_targets = { { "STRING", 0, DragTarget.TAG } }; - + Gtk.drag_dest_set ( this, Gtk.DestDefaults.MOTION, accepted_targets, Gdk.DragAction.COPY ); - + this.drag_motion.connect(onDragMotion); this.drag_leave.connect(onDragLeave); this.drag_drop.connect(onDragDrop); this.drag_data_received.connect(onDragDataReceived); } } - + private bool onDragMotion(Gtk.Widget widget, Gdk.DragContext context, int x, int y, uint time) { this.set_state_flags(Gtk.StateFlags.PRELIGHT, false); return false; } - + private void onDragLeave(Gtk.Widget widget, Gdk.DragContext context, uint time) { this.unset_state_flags(Gtk.StateFlags.PRELIGHT); } - + private bool onDragDrop(Gtk.Widget widget, Gdk.DragContext context, int x, int y, uint time) { // If the source offers a target if(context.list_targets() != null) { var target_type = (Gdk.Atom)context.list_targets().nth_data(0); - + // Request the data from the source. Gtk.drag_get_data(widget, context, target_type, time); return true; } - + return false; } - + private void onDragDataReceived(Gtk.Widget widget, Gdk.DragContext context, int x, int y, Gtk.SelectionData selection_data, uint target_type, uint time) { @@ -131,7 +131,7 @@ public class FeedReader.TagRow : Gtk.ListBoxRow { string articleID = (string)selection_data.get_data(); Article article = DataBase.readOnly().read_article(articleID); Logger.debug(@"drag articleID: $articleID"); - + if(m_tag.getTagID() != TagID.NEW) { FeedReaderBackend.get_default().tagArticle(article, m_tag, true); @@ -143,7 +143,7 @@ public class FeedReader.TagRow : Gtk.ListBoxRow { } } } - + private bool onClick(Gdk.EventButton event) { // only right click allowed @@ -151,12 +151,12 @@ public class FeedReader.TagRow : Gtk.ListBoxRow { { return false; } - + if(!Utils.canManipulateContent()) { return false; } - + switch(event.type) { case Gdk.EventType.BUTTON_RELEASE: @@ -164,17 +164,17 @@ public class FeedReader.TagRow : Gtk.ListBoxRow { case Gdk.EventType.@3BUTTON_PRESS: return false; } - + var remove_action = new GLib.SimpleAction("deleteTag", null); remove_action.activate.connect(() => { if(this.is_selected()) { moveUP(); } - + uint time = 300; this.reveal(false, time); - + string text = _("Tag \"%s\" removed").printf(m_name); var notification = MainWindow.get_default().showNotification(text); ulong eventID = notification.dismissed.connect(() => { @@ -191,15 +191,15 @@ public class FeedReader.TagRow : Gtk.ListBoxRow { rename_action.activate.connect(() => { showRenamePopover(); }); - + var app = FeedReaderApp.get_default(); app.add_action(rename_action); app.add_action(remove_action); - + var menu = new GLib.Menu(); menu.append(_("Rename"), "renameTag"); menu.append(_("Remove"), "deleteTag"); - + var pop = new Gtk.Popover(this); pop.set_position(Gtk.PositionType.BOTTOM); pop.bind_model(menu, "app"); @@ -208,42 +208,42 @@ public class FeedReader.TagRow : Gtk.ListBoxRow { }); pop.show(); this.set_state_flags(Gtk.StateFlags.PRELIGHT, false); - + return true; } - + public void update(string name) { m_label.set_text(name.replace("&","&")); m_label.set_use_markup (true); } - + public Tag getTag() { return m_tag; } - + public void setExits(bool subscribed) { m_exits = subscribed; } - + public bool stillExits() { return m_exits; } - + public bool isRevealed() { return m_revealer.get_reveal_child(); } - + public void reveal(bool reveal, uint duration = 500) { m_revealer.set_transition_duration(duration); m_revealer.set_reveal_child(reveal); } - + private void showRenamePopover(Gdk.DragContext? context = null, uint time = 0, Article? article = null) { var popRename = new Gtk.Popover(this); @@ -256,7 +256,7 @@ public class FeedReader.TagRow : Gtk.ListBoxRow { Gtk.drag_finish(context, false, false, time); } }); - + var renameEntry = new Gtk.Entry(); renameEntry.set_text(m_name); renameEntry.activate.connect(() => { @@ -273,28 +273,28 @@ public class FeedReader.TagRow : Gtk.ListBoxRow { Gtk.drag_finish(context, true, false, time); } }); - - + + string label = _("rename"); if(m_tag.getTagID() == TagID.NEW && context != null) { label = _("add"); } - + var renameButton = new Gtk.Button.with_label(label); renameButton.get_style_context().add_class("suggested-action"); renameButton.clicked.connect(() => { renameEntry.activate(); }); - + var renameBox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 5); renameBox.margin = 5; renameBox.pack_start(renameEntry, true, true, 0); renameBox.pack_start(renameButton, false, false, 0); - + popRename.add(renameBox); popRename.show_all(); this.set_state_flags(Gtk.StateFlags.PRELIGHT, false); } - + } diff --git a/src/Widgets/UpdateButton.vala b/src/Widgets/UpdateButton.vala index 3f93d58f..1b40c35f 100644 --- a/src/Widgets/UpdateButton.vala +++ b/src/Widgets/UpdateButton.vala @@ -14,7 +14,7 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.UpdateButton : Gtk.Button { - + private Gtk.Image m_icon; private Gtk.Spinner m_spinner; private bool m_status; @@ -24,19 +24,19 @@ public class FeedReader.UpdateButton : Gtk.Button { private bool m_isCancellable; private Gtk.Popover m_Popover; private string m_tooltip; - + public UpdateButton.from_icon_name(string iconname, string tooltip, bool progressPopup = false, bool cancellable = false) { m_icon = new Gtk.Image.from_icon_name(iconname, Gtk.IconSize.SMALL_TOOLBAR); setup(tooltip, cancellable, progressPopup); } - + public UpdateButton.from_resource(string iconname, string tooltip, bool progressPopup = false, bool cancellable = false) { m_icon = new Gtk.Image.from_resource(iconname); setup(tooltip, cancellable, progressPopup); } - + private void setup(string tooltip, bool progressPopup, bool cancellable) { m_hasPopup = progressPopup; @@ -44,13 +44,13 @@ public class FeedReader.UpdateButton : Gtk.Button { m_tooltip = tooltip; m_spinner = new Gtk.Spinner(); m_spinner.set_size_request(16,16); - + m_stack = new Gtk.Stack(); m_stack.set_transition_duration(100); m_stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE); m_stack.add_named(m_spinner, "spinner"); m_stack.add_named(m_icon, "icon"); - + if(m_hasPopup) { m_ProgressText = new Gtk.Label(Settings.state().get_string("sync-status")); @@ -59,7 +59,7 @@ public class FeedReader.UpdateButton : Gtk.Button { m_Popover.add(m_ProgressText); this.button_press_event.connect(onClick); } - + this.add(m_stack); this.set_relief(Gtk.ReliefStyle.NONE); this.set_events(Gdk.EventMask.ENTER_NOTIFY_MASK); @@ -67,7 +67,7 @@ public class FeedReader.UpdateButton : Gtk.Button { this.set_tooltip_text(tooltip); this.show_all(); } - + public void updating(bool status, bool insensitive = true) { Logger.debug("UpdateButton: update status"); @@ -90,19 +90,19 @@ public class FeedReader.UpdateButton : Gtk.Button { m_spinner.stop(); } } - + public bool getStatus() { return m_status; } - + public void setSensitive(bool sensitive) { // FIXME: dont set sensitive if canceling Logger.debug("UpdateButton: setSensitive %s".printf(sensitive ? "true" : "false")); this.sensitive = sensitive; } - + public void setProgress(string text) { if(m_hasPopup) @@ -110,26 +110,26 @@ public class FeedReader.UpdateButton : Gtk.Button { m_ProgressText.set_text(text); } } - + private bool onClick(Gdk.EventButton event) { if(event.button != 3) { return false; } - + if(m_status && !m_Popover.get_visible()) { m_Popover.show_all(); return true; } - + return false; } - + public void setIcon(Gtk.Image icon) { m_icon = icon; } - + } diff --git a/src/Widgets/WebLoginPage.vala b/src/Widgets/WebLoginPage.vala index f5cb8055..d0068938 100644 --- a/src/Widgets/WebLoginPage.vala +++ b/src/Widgets/WebLoginPage.vala @@ -14,13 +14,13 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.WebLoginPage : Gtk.Bin { - + private WebKit.WebView m_view; private bool m_success = false; public signal bool getApiCode(string url); public signal void success(); - - + + public WebLoginPage() { var settings = new WebKit.Settings(); @@ -32,14 +32,14 @@ public class FeedReader.WebLoginPage : Gtk.Bin { this.add(m_view); this.show_all(); } - - + + public void loadPage(string url) { Logger.debug("WebLoginPage: load URL: " + url); m_view.load_uri(url); } - + public void redirection(WebKit.LoadEvent load_event) { switch(load_event) @@ -56,7 +56,7 @@ public class FeedReader.WebLoginPage : Gtk.Bin { break; } } - + private void check() { if(m_success) @@ -64,18 +64,18 @@ public class FeedReader.WebLoginPage : Gtk.Bin { // code already successfully extracted return; } - + string url = m_view.get_uri(); - + if(getApiCode(url)) { m_view.stop_loading(); m_success = true; success(); - + } } - + public void reset() { m_view.load_uri("about:blank"); |