Cheat Sheets

Recent Posts

Writing Liferea Plugins Tutorial Part 5

This tutorial part is about how to localize core Python plugins so contributors can translate the user interface.

Enable gettext for Python Plugins

For a good plugin example have a look at the Liferea plugins headerbar.py where Robbie Cooper added gettext support.

To do so add this code in global scope:
# Initialize translations for tooltips
# Fallback to English if gettext module can't find the translations
# (That's possible if they are installed in a nontraditional dir)
import gettext
_ = lambda x: x
try:
    t = gettext.translation("liferea")
except FileNotFoundError:
    pass
else:
    _ = t.gettext
And now everywhere you need it you can use translated literals, e.g.
button.set_tooltip_text(_("Previous Item"))
Note the _() style which is the gettext wrapper to replace text with translations.

Related

Also check out the previous plugin tutorial posts

Liferea 1.12.6 Released

This is another bug fix release which resolves several usability problems.

Changes

    * #658: Add confirmation dialog when adding duplicate subscription
      (dymoksc)

* Fixes #689: When resuming from sleep feeds were being fetched before Wifi came up (ghost) * Fixes #685: Headerbar plugin "Mark All Read" button is not feed-specific (Robbie Cooper) * Fixes #549: Scrollbars not always present in the headline area (Leiaz) * Fixes #543: Next update overrides HTML5 enrichment (Lars Windolf) * Fixes overly wide label in enclosure preferences dialog (Lars Windolf) * Dropped unencrypted warning from auth dialog (not true anymore) (Lars Windolf)

* #692: Update of Czech translation (RadimNo) * #688: Adding translatable tooltips for the headerbar plugin (Robbie Cooper)

Download

You can download the release tarball at github.

Liferea 1.12.5 Released

1.12.5 is a bugfix release. It includes an important bug fix that solves a possible endless loop on using "Next Unread" which several users experienced as endless high CPU usage. If you are affected by this: please upgrade!

This release also switches the keyring integration from GnomeKeyring to libsecret and introduces a flatpak JSON.

Changes

    * #665: Webkit browser now supplies 'Liferea' component in user agent
    * #664: Added "Mark All As Read" button to headerbar plugin
    * #620: Added flatpak JSON
      (glitsj16)
    * #579: Added item list column drag and drop reordering
      (Yanko Kaneti)
    * #436, #662: Move from GnomeKeyring to libsecret
      (bgermann)

* Fixes #663: Correct instapaper sharing link (Daniel Alexandersen) * Fixes #661: Update sharing links (Daniel Alexandersen) * Fixes #271: Consistent over usage of CPU (trigger by "Next Unread" loop) (reported by GreenLunar)

* #472, #632: Dropping Inoreader support (API broke)

Download

You can download the release tarball at github.

Liferea 1.12.4 Released

Release 1.12.4 brings small bug and security fixes.

Changes

    * Fixes #660: Added installable plugin to change accels
      (Lars Windolf)
    * Fixes #654: Segfault on date out of range
      (Leaiz)
    * Fixes #651: Fixes Free Music Archive link in default OPMLs
      (reported by benjbrandall)
    * Fixes #649: Switch from persistent to session-only cookies
      (Daniel Alexandersen)
    * Fixes #645, #646: unread count of vfolder
      (Leaiz)
    * Fixes #637: Extra keywords in .desktop file (syndication; rss; atom)
      (Daniel Alexandersen)
    * Fixes #557: Updating counters for remote sources
      (Leiaz)

* Updated cookie usage hint in FAQ

Download

You can download the release tarball at github.

Writing Liferea Plugins Tutorial Part 2

Let's continue the plugin tutorial. The last installement was on how plugins work and how to create the boilerplate for a new plugin. Now let's look into how to access Liferea UI elements and how to modify them.

Accessing UI elements

Using the plugin boilerplate for a Liferea.ShellActivatable (a plugin that activates once the Liferea shell, which as an object comprises the entire main window UI, has been setup) we get a member variable named "shell"
    shell = GObject.property (type=Liferea.Shell)
which can be used to look up GTK objects by name using
    shell.lookup(<some name>)
Some interesting names to look up are:
Name Description
mainwindowThe main GtkWindow
leftpaneThe vertical pane containing the feed list
rightpaneThe vertical pane containing the rest
feedlistThe feed list GtkTreeView
itemlistThe item list GtkTreeView
browsertabsThe tabs notebook of the item view
statusbarThe main window status bar
This list not being exhaustive you can grep the code for more uses
rgrep liferea_shell_lookup src/
in general when you want to modify existing UI elements or add extra elements to the UI above list should be a good start.

Example: Modifying the feed list

Here is a simple example to hide the 2nd column of the feed list GtkTreeView. To do this we use the "shell" member to look up the "feedlist" GtkTreeView and ask it for the 2nd column which we then hide:
from gi.repository import GObject, Peas, PeasGtk, Gtk, Liferea, Gdk

class NrColumnHidePlugin (GObject.Object, Liferea.ShellActivatable): __gtype_name__ = 'NrColumnHidePlugin'

object = GObject.property (type=GObject.Object) shell = GObject.property (type=Liferea.Shell)

def do_activate (self): treeview = self.shell.lookup ("feedlist") column = Gtk.TreeView.get_column (treeview, 1) Gtk.TreeViewColumn.set_visible (column, 0);

def do_deactivate (self): return
This is all done on activate, nothing needs to be done on deactivation.

Related

Also check out the other plugin tutorial posts

Writing Liferea Plugins Tutorial Part 1

Some time ago a fellow Liferea user asked about documentation on writing Liferea plugins. I see the need and the benefit and want to start doing so with a series of blog posts that later can be compiled into a tutorial to be included on the website/sources.

Plugins with GObject Introspection

First it is important to know that Liferea 1.10+ uses GObject Introspection (GI) and libpeas to allow implementing plugins. This quote from the GNOME wiki explain how GI works:

GObject introspection is a middleware layer between C libraries (using GObject) and language bindings. The C library can be scanned at compile time and generate a metadata file, in addition to the actual native C library. Then at runtime, language bindings can read this metadata and automatically provide bindings to call into the C library.

The important point is: by Liferea using GI (as all GNOME applications and many other GTK applications do now) plugins can be written in practically any scripting language. Most users seem to favour Python and all current plugins included with the Liferea sources are in Python. Note that this tutorial will also focus on Python only.

How are plugins triggered from within Liferea?

Ok, I can write a script in Python! How will Liferea run it and when?

This is where libpeas comes in, which is a basic library to implement a plugin system. If you click the preferences dialog and switch to the "Plugins" button you see a dialog provided by the PeasGtkPluginsManager class of libpeas. Detection, activation and configuration of plugins is handled by libpeas.



Now for the "When?" question: To properly allow applications to hook plugins into different parts of the applications libpeas allow an application to define one or more so called "Activatable" interfaces.

For simplicity for Liferea I decided to only support a LifereaShellActivatable interface. This means all plugins are activated together with the LifereaShell instance (src/ui/liferea_shell.c). This class represents the main application window holding all widgets. So when your plugin gets activated all widgets exist and you can access everything like extending or modifying the GUI, changing settings, everything you can think of.

Note: in the code there are two more interfaces: that are used to implement two important features (GNOME keyring support and a simple media player). Feel free to use those two, but be aware that they work differently and activate at other times and not just once as the LifereaShellActivatable.

Using LifereaShellActivatable

If you script in Python using LifereaShellActivatable means simply deriving a new class from it. For example:
from gi.repository import GObject, Peas, PeasGtk, Gtk, Liferea, Gdk


class ExamplePlugin (GObject.Object, Liferea.ShellActivatable): __gtype_name__ = 'ExamplePlugin'

object = GObject.property (type=GObject.Object) shell = GObject.property (type=Liferea.Shell)

def do_activate (self): # Do something here...

def do_deactivate (self): # Maybe do somethin here too...
The activate() and deactivate() methods are required by libpeas and provide you with the starting points to do stuff. By fetching the "Liferea.Shell" instance you gain access to the main window. Using this you can both lookup widgets or other Liferea classes like the Liferea.FeedList to perform actions against business objects of Liferea.

Providing a plugin configuration

Along with the actual plugin code libpeas requires a plugin configuration file defining the language the plugin is implemented with and metadata (name, description, website...) for this plugin. Such a file looks like this:
[Plugin]
Module=example
Loader=python3
IAge=2
Name=Example Plugin
Description=Illustrates how to implement plugins in Liferea
Authors=Lars Windolf 
Copyright=Copyright © 2014 Lars Windolf
Website=http://lzone.de/liferea/
Help=http://lzone.de/liferea/
Most important is the "Loader" setting indicating the correct scripting language and the "Module" setting which together with the "Loader" setting as "python" indicates that or plugin script is to be named "example.py". Both the "example.py" plugin script and it's "example.plugin" config file need to be put into the Liferea plugins directory...

Where to put my plugin script?

There are two possible locations for the plugin script (and it's configuration file): Note that paths can be different with different XDG settings.

When writing and testing don't bother installing the plugin in the package directories. Just put it in ~/.config/liferea/plugins, fire up Liferea.




More about how to check for activation, debug problems and handling enabling/disabling in the next installment of this tutorial!

Related

Also check out the other plugin tutorial posts

New GTK Headerbar Plugin

Yesterdays release (1.12.3) includes a new plugin that allows you to completely switch out the toolbar/menubar window decoration for a GNOME style GTK headerbar.

How it looks with headerbar plugin enabled:



For comparison the default GTK toolbar/menubar:



If you are unsure wether you like this, just enable the plugin to have a look, disable it again and you will get back the default look.

Contribute!

The plugin is not perfect, there are many UX considerations that are probably not yet right. If you use the plugin extensively let's work together on it. Also don't be afraid of the code, for now it's just 80 lines of Python waiting for your new ideas!

Liferea 1.12.3 Released

Today sees a new 1.12 release which addresses some long-standing user concerns. One being accidentally marking all your headlines as read, we have no a confirmation dialog. Another issue addressed is that of custom download tools. The list provided is not always sufficient so we now provide a CLI command you can enter to use you favorite downloader. A lot of refactoring also allowed us to provide a GTK headerbar plugin for all fans of GNOME style window decorations.

Changes

    * #634: Added setting for custom download commands
      (Leiaz)
    * #614: GTK Headerbar support via plugin
      (Lars Windolf)
    * #608: Refactoring UI code to switch to GAction and GtkBuilder
      Note: this implies not having icons in the main menu anymore
      which were still there for all non-GNOME users (see #626).
      (Leiaz)
    * #589: Item list view column order rework as a preparation for
      possible real column drag&drop. Introduces a new DConf setting
      for the column order.
      (Yanki Kaneti)

* Fixes #280: Mark read toolbar button always disabled for search folders (Lars Windolf, reported by dvahalev) * Fixes #591: Please add a safety question when "marking all read" (Leiaz, reported by Nudin) * Fixes #625: Avoid exception in trayicon.py (Lars Windolf) * Fixes #627: GnomeKeyring plugin fails to activate when keyring doesn't exist (Lars Windolf) * Fixes #630: Fix feed list selection after DnD (Peter Zaitev) * Fixes #633: Big Memory leak in date code (Leiaz)

* Update of Turkish translation (emintufan) * Update of French translation (guilieb)

Download

You can download the release tarball at github.

Writing Liferea Plugins Tutorial Part 4

Here is another How-To on GTK/libpeas plugin development. This time it is on how to use the GTK Inspector to dive into application details.

How to enable GTK Inspector

Before we can start we have to enable the GTK inspector with
gsettings set org.gtk.Settings.Debug enable-inspector-keybinding true
Now with any GTK application you can open the GTK inspector by pressing either Ctrl-Shift-I or Ctrl-Shift-D. If this doesn't help try launching with
GTK_DEBUG=interactive liferea

Discovering widget names with GTK Inspector

When you have successfully launched the inspector you should see a window like this:



Note the second column 'Name' of the "Objects" table containing all those widget names we covered in part 2/3 of the tutorial.

Trying GTK CSS styles

If you select the 'CSS' tab you get a inline editor to try style changes in Liferea. If you make modifications successfully you can save them to disk and make them permanent.

For quick effect try entering
treeview {
   background-color:red;
}

button { font-size: 200% }
and notice the immediate changes.

To address widgets by name ensure to always prefix the widget type selector. For example to change the 'itemlist' treeview do
treeview#itemlist {
	background-color:green;
}
and not just (as one would in HTML)
#itemlist {
	background-color:green;
}
You probably also noticed that widget type names are not 'GtkTreeView' but 'treeview', not 'GtkButton' but 'button' and so on. Just lowercase and strip 'Gtk' and you have the selector name.

Experiment with GTK settings

Finally check out the 'Visual' tab which exposes all major GTK settings on a per application basis. Here you can try fonts, theme setting, dark variant...

Related

Also check out the previous plugin tutorial posts

Writing Liferea Plugins Tutorial Part 3

Welcome to a new installment of the plugins tutorial! Todays issue will cover menu modifications. This allows you to add new menu options for features you introduce with your plugin.

GTK menu concepts

Before diving into the example code we need to cover some theory. GTK3 provides several APIs to build menus but for Liferea plugins only one of them is exposed: the GtkUIManager.

In short the GtkUIManager allows adding UI elements (menus ,toolbars, accelerators) using XML like this:
<ui>
  <menubar>
    <menu name="FileMenu" action="FileMenuAction">
      <menuitem name="New" action="New2Action" />
    </menu>
</ui>
The above snippet defines an menu item that you can merged into the UI by calling a method like gtk_ui_manager_add_ui_from_string().

Note that above definition only says that there ought to be a menu action with internal action name "New2Action". So we still have to define this active and bind a callback.

Extending a menu in PyGI

Now how do we extend a Liferea menu? First we get the GtkUIManager:
self._ui_manager = self.shell.get_property("ui-manager")
Now we can add an XML snippet describing a menu
self._ui_manager.add_ui_from_string(
    """<ui>
         <menubar name='MainwindowMenubar'>
           <menu action='ToolsMenu'>
             <menuitem action='MyPluginMenuEntry'/>
           </menu>
        </menubar>
      </ui>"""
)
Please note that the GtkUIManager property is only exposed starting with Liferea version 1.12.2!

And as said above we need to add an action for this menu item. As actions can only be added in groups using a GtkActionGroup we need to create one first, insert the action and finally tell the GtkUIManager about the action group:
self._action = Gtk.Action('MyPluginMenuEntry', 'Awesome Function', 'Run a really awesome function', None)
self._action.connect("activate", self._do_action)

self._actiongroup = Gtk.ActionGroup("MyPluginActions") self._actiongroup.add_action(self._action)

self._ui_manager.insert_action_group(self._actiongroup)

Available Menus

You probably noticed the "ToolsMenu" and "MainwindowMenubar" ids in the XML snippet above. Here is a list of all menu ids in 1.12 which also can be found in src/ui/liferea_shell.c.
Name Description
FeedMenuAll actions regarding subscriptions and the feed list
ItemMenuAll actions regarding the item list
ViewMenuAll actions controlling reading layout
ToolsMenuThe menu to access preferences and update status
SearchMenuAll actions for finding stuff
HelpMenuThe place to go for help

Complete Solution

Everything put together in a plugin could look like this:
import gi

gi.require_version('Gtk', '3.0')

from gi.repository import GObject, Liferea, Gtk

class AppActivatable(GObject.Object, Liferea.ShellActivatable): __gtype_name__ = "MyPluginAppActivatable"

shell = GObject.property(type=Liferea.Shell)

def __init__(self): GObject.Object.__init__(self)

def do_activate(self): self._action = Gtk.Action('MyPluginMenuEntry', 'Awesome Function', 'Run a really awesome function', None) self._action.connect("activate", self._do_action)

self._actiongroup = Gtk.ActionGroup("MyPluginActions") self._actiongroup.add_action(self._action)

self._ui_manager = self.shell.get_property("ui-manager") self._ui_manager.insert_action_group(self._actiongroup) self._ui_manager.add_ui_from_string( """<ui> <menubar name='MainwindowMenubar'> <menu action='ToolsMenu'> <menuitem action='MyPluginMenuEntry'/> </menu> </menubar> </ui>""" )

def _do_action(self, action, data=None): print("Action triggered")
For a real world example have a look at https://github.com/lwindolf/liferea/blob/master/plugins/plugin-installer.py

Related

Also check out the previous plugin tutorial posts

Liferea 1.12.1 Released

This is a bugfix release for the recent 1.12.0 release. Thanks to Leiaz tackling several complicated issues. Most prominent is a hopefully solved issue with the pane sizing with maximized window. Last but not least we have Italian and Indonesian translation updates.

Changes

* Fixes #562: Lintian spelling errors
  (reported by Paul Gevers)
* Fixes #563: Appstream data has new format
  (patch by Paul Gevers)
* Fixes #572: Doesn't remember some sort orders
  (reported by geplus)
* Fixes #504: Fix assertions/crashes on changing view layouts
  (Leiaz)
* Fixes #573: Workaround to avoid GtkPaned shrinking
  (Leiaz)

* #566: Update of Italian translation (Gianvito Cavasoli) * #566: Update of Italian default feed list (Gianvito Cavasoli) * #514: Update of Indonesian translation (Samsul Ma'arif) * #514: Added Indonesian default feed list (Samsul Ma'arif) * Update of German translation

Thanks for the stars!

Finally I wanted to say thank to all of you who starred the github repo after the 1.12.0 release! It really makes a difference to know how many people are out there caring.

Download

Get a tarball or checkout the code from Github!

Liferea 1.12.0 Released

This is the first release of the new stable release line 1.12. Many thanks to all the contributors and package maintainers that made this possible!

Important changes compared to 1.10

    * Wide view is now default view
    * HTML view now has a 'View Image' context menu
    * Redesign of the wide view: large icons with teaser text for better using 16:9 screen ratios
      and making Liferea usable on touchscreen
    * Optional AMP / HTML5 rich content fetching feature
    * Upgrade to WebKit2: This is an important security improvement!
    * Added a "Do Not Track" preference (disabled per-default)
    * Reordered columns in 'Normal' email-like view to have the date column always at the end
    * Plugins switched to Python3 libpeas loader
    * Full screen support for videos
    * Simplified external browser support

* Experimental support for InoReader and Reedah online services * Added category/folder support for TheOldReader * Added folder auto-removal for TinyTinyRSS & TheOldReader

* Removed libindicate support * Removed libnotify code (re-added as a plugin) * Removed tray icon code (re-added as a plugin)

Important fixes

    * Fixes unhiding from tray icon when activated via GApplication (hidden window problem)
    * Fixes #538: toggle_visibility() does not make a minimized window visible again (trayicon problem)
    * Liferea now uses ETags and If-None-Matches on feed updates
Please help fixing any leftover bugs and UX issues!

Show Your Support

Nowadays it is hard to know how many users are left out there. If you like the release of 1.12 show it by giving the project a star at Github!!!

Download

Get a tarball or checkout the code from Github!

Liferea 1.12-rc3 Released

This is another 1.12 release candidate fixing bugs and updating a lot of translations.

    * Fixes #459: Fixes GtkDoc warnings
      (Leiaz)
    * Fixes #415: Filter commands are not asynchronous
      (Rich Coe)
    * Fixes #363: Missing space above internal browser address bar
      (reported by nekohayo, patch by Mikel Olasagasti)
    * Fixes #208: All "Unread" search folder items marked read at once
      (Leiaz)
    * Fixes #251: Liferea does not always use theme icons when it is launched
      on system startup (reported by GreenLunar, fix by Leiaz)

* Updated Finnish translation (Jorma Karvonen) * Updated Latvian translation (Rihards Prieditis) * Updated Albanian translation (Bensik Bleta) * Updated Hungarian translation (Balázs Úr) * Updated Brazlian translation (Rafael Ferreira) * Updated French translation (Guillaume Bernard)

Download

Get a tarball or checkout the code from Github!

Liferea 1.12-rc1 Released



This is a first release candidate for new stable line 1.12

Major changes since 1.10

Changes since 1.11.7

    * Github #348: Added support for downloading content that
      cannot be displayed by HTML widget (e.g. PDFs)
      (Leiaz)
    * Github #355: Migrate to Python3 libpeas loader
      (patch by picsel2)
    * Github #311: Upgrade to WebKit2
      (patch by Leiaz)
    * Github #292: Show new item count in tray icon
      (patch by mozbugbox)
    * Github #297: Minimize to systray on window close
      (patch by Hugo Arregui)
    * Github #325: Auto-fitting, translated license
      (patches by GreenLunar and Adolfo Jayme-Barrientos)

* Fixes Github #73: Problem with favicon update (reported by asl97) * Fixes Github #177, #350: Tray icon not scaled properly (patch by mozbugbox) * Removes GeoIP rendering via OSM to avoid exposing users to remote JS library resources. (reported by Paul Gevers) * Fixes Github #337: Case sensitive sorting (reported by Pi03k) * Fixes Github #361: Show all enclosuers (Leiaz) * Fixes Github #368: Segfault on liferea-feed-add (Leiaz) * Fixes Github #382: Broken Auto-Detect/No Proxy setting (Leiaz) * Fixes Github #383: Per feed don't use proxy setting is broken (reported by Leiaz)

* Github #309: Update of Japanese translation (IWAI, Masaharu) * Github #329: Update of Hebrew translation (GreenLunar) * Github #330: Update of Spanish translation (Adolfo Jayme-Barrientos) * Update of Swedish translation (Andreas Ronnquist)

Download

Get a tarball or checkout the code from Github!

Liferea 1.12 now with Webkit2

Some minutes ago I'v added Leiaz extensive change for switching to Webkit2. This is great news because for quite some time Webkit1 has not seen any security updates anymore and many applications still on Webkit1 expose there users to quite some risk.

What's different in Webkit2?

The most important change is Webkit being multi-process. For applications using Webkit this means they have to talk to the background process to scroll, copy text, get context menues and other stuff. The Webkit2 way to do the talking is to write a Webkit extension that is loaded when the background process starts which allows us to send DBUS commands.

As for performance it seems faster. But you might want to try yourself!

Distro Support

To compile with Webkit2 you need at least the following Linux distribution versions

No Travis CI anymore

Due to the Ubuntu 16.04 not yet supported by Travis I had to disable the test builds as the fail installing the Webkit2 package :-(

Liferea Trick #9: Skimming Through the Headlines

I'm not sure about how many users are aware of the feature, but I'm certain it is worth to know about as it saves a lot of clicking and pointing with the mouse. If you are a keyboard user it's worth knowing about the hotkey to skim through headlines.

Remember the Hot Key

As this hotkey is configurable check the preference dialog ff you are not sure about the setting. The default setting is <Ctrl>-Space...

How it Works...

By using this hot key you can navigate the article pane and the item list view at the same time. As long as the article pane allow vertical scrolling it scrolls down. Once you reach bottom Liferea jumps to the next unread article.

Using the headline skimming hotkey is like a "Next Unread And Scroll Down" menu option...

Recent Liferea Tricks

Liferea Trick #8: Change Menu Accelerators

When you are not satisfied with the menu key bindings defined by Liferea do not despair it is easy to change them!

Variant #1: Edit ~/.config/liferea/accels

This variant is 100% portable and should work for everyone. Open ~/.config/liferea/accels in your favourite editor. This file is loaded upon startup by Liferea and contains lines like these:
[...]
; (gtk_accel_path "<Actions>/AddActions/NewVFolder" "")
; (gtk_accel_path "<Actions>/GeneralActions/SearchMenu" "")
; (gtk_accel_path "<Actions>/ItemActions/ToggleItemFlag" "<Primary>t")
; (gtk_accel_path "<Actions>/GeneralActions/PrevReadItem" "<Primary><Shift>n")
[...]
Note how only the "ToggleItemFlag" and the "PrevReadItem" line have defined key bindings, while "NewVFolder" and "SearchMenu" don't.

To change a key binding first remove the semicolon at the start of the line and then adapt or clear the key binding field. Choose prefixes like "<Primary>" (for Ctrl), "<Alt>", "$lt;Shift> as needed and append the key after it.

Variant #2: Enable Editable Accelerators with your Linux Distro Settings

This variant is hard to document as different distributions have different setting dialogs. Some expose a setting to enable life editing of key bindings. Once this is enabled you can open a menu hover over a menu option and press the accelerator you want to assign. It should show up instantly.


Recent Liferea Tricks