Skip to main content

development

Having settings in your plugin

Posted in

Basic description

Having settings in your plugin boils down to implementing the IHaveSettings interface and writing an XML file with the description of the settings. The XML file defines the settings that your plugin exposes to the user via the XmlSettingsDialog (further referred as XSD) system.

C++ side

Implementing IHaveSettings

Implementing IHaveSettings typically consists of the following steps:

  1. creating a class that is responsible for keeping settings' values, derived from BaseSettingsManager and typically called XmlSettingsManager (see the example below);
  2. instantiating a XmlSettingsDialog class;
  3. registering your settings file and settings manager in the just instantiated XmlSettingsDialog object via RegisterObject;
  4. returning the instantiated object of XmlSettingsDialog from the IHaveSettings::GetSettingsDialog () method.

XmlSettingsManager example

xmlsettingsmanager.h:

#pragma once
 
#include <xmlsettingsdialog/basesettingsmanager.h>
 
namespace LeechCraft
{
namespace LMP
{
        class XmlSettingsManager : public Util::BaseSettingsManager
        {
                Q_OBJECT
 
                XmlSettingsManager ();
        public:
                static XmlSettingsManager& Instance ();
        protected:
                virtual QSettings* BeginSettings () const;
                virtual void EndSettings (QSettings*) const;
        };
}
}

xmlsettingsmanager.h:

#include "xmlsettingsmanager.h"
#include <QCoreApplication>
 
namespace LeechCraft
{
namespace LMP
{
        XmlSettingsManager::XmlSettingsManager ()
        {
                Util::BaseSettingsManager::Init ();
        }
 
        XmlSettingsManager& XmlSettingsManager::Instance ()
        {
                static XmlSettingsManager manager;
                return manager;
        }
 
        QSettings* XmlSettingsManager::BeginSettings () const
        {
                QSettings *settings = new QSettings (QCoreApplication::organizationName (),
                                QCoreApplication::applicationName () + "_LMP");
                return settings;
        }
 
        void XmlSettingsManager::EndSettings (QSettings*) const
        {
        }
}
}

Don't forget to include the license header!

Manipulating settings values

Getting a value of a setting is a matter of calling the property () method on your settings manager object, like this:

const bool isScrobblingEnabled = XmlSettingsManager::Instance ().property ("ShouldScrobble").toBool ();
const auto& username = XmlSettingsManager::Instance ().property ("Username").toString ();

You can also update the settings from your C++ code, and the GUI will update itself, though that's rarely needed. For that, just call setProperty, for example:

if (UsernameConflict (username))
    XmlSettingsManager::Instance ().setProperty ("Username", username + "_lc");

Subscribing to property changes

One calls the BaseSettingsManager::RegisterObject method to register a metamethod to be invoked when a property changes. The metamethod must have no parameters and return nothing. Please note that it isn't invoked upon registration, so you should do that yourself if you need to.

For example:

XmlSettingsManager::Instance ().RegisterObject ("TransitionTime",
                this, "setTransitionTime");
setTransitionTime ();

and the setTransitionTime () body:
void Player::setTransitionTime ()
{
        const int time = XmlSettingsManager::Instance ()
                        .property ("TransitionTime").toInt ();
        Source_->SetTransitionTime (time);
}

XML description

XML description file defines a tree of settings items and their types, like a string, an integer, a real number, a local path or a color.

The basic element of the settings file is <item>. This is the exact element that defines a single option, its type, default value, human-readable name and sometimes a tooltip. The <item> must have three attributes:

type
Defines the type of the item, like a checkbox for boolean options, a line edit for strings, etc. The full list of available types is given in the end of this page.
property
The identifier of this option. It is used to manipulate the value of this option via the XmlSettingsManager class from the C++ side.
default
The default value of this option. The possible values depend on the option type, for example, a <item type="checkbox"> only accepts "true" or "false" values, while an <item type="lineedit"> can accept literally everything.

Some option types may have additional attributes, they are documented later on.

For example (please note it's not a full XML, just a tiny bit of it):

<item type="checkbox" property="ShowTrayIcon" default="false">
        <label value="Show tray icon" />
</item>

The items are organized in pages (defined by the <page> element), which can be further divided into tabs (defined by the <tab> element). There are also two containers, a <groupbox> element, which just visually groups its children in UI, and <item type="groupbox" checkable="true">, which is an item itself and also groups its children but also has a checkbox near its label, and the children are unavailable if the checkbox is in the disabled state.

Most items ranging from pages and tabs to every tiny spinbox out there support labels — human-readable pieces of texts that name the option for the user. Labels are defined by a <item>'s subelement <label>.

For example, KBSwitch has the following XML setting file:

<?xml version="1.0" encoding="UTF-8"?>
<settings>
        <page>
                <label value="KBSwitch" />
                <tab>
                        <label value="General settings" />
                        <item type="combobox" property="SwitchingPolicy">
                                <label value="Keboard layout switching policy (LeechCraft window):" />
                                <option name="global" default="true">
                                        <label value="Use system layout" />
                                </option>
                                <option name="plugin">
                                        <label value="Keyboard layout per plugin" />
                                </option>
                                <option name="tab">
                                        <label value="Keyboard layout per tab" />
                                </option>
                        </item>
                        <item type="groupbox" checkable="true" property="ManageSystemWide" default="false">
                                <label value="Manage keyboard layouts system-wide" />
                                <item type="combobox" property="KeyboardModel" mayHaveDataSource="true">
                                        <label value="Keyboard model:" />
                                </item>
                        </item>
                </tab>
                <tab>
                        <label value="Layouts" />
                        <item type="customwidget" name="LayoutsConfigWidget" label="own" />
                </tab>
                <tab>
                        <label value="Options" />
                        <item type="customwidget" name="OptionsConfigWidget" label="own" />
                </tab>
        </page>
</settings>

LMP module has the following:

<?xml version="1.0" encoding="UTF-8"?>
<settings>
        <page>
                <label value="Appearance" />
                <item type="checkbox" property="ShowTrayIcon" default="false">
                        <label value="Show tray icon" />
                </item>
                <item type="checkbox" property="UseNavTabBar" default="false">
                        <label value="Use tabs for switching pages" />
                </item>
                <item type="checkbox" property="AutocenterCurrentTrack" default="true">
                        <label value="Automatically center on current track" />
                </item>
                <item type="lineedit" property="SingleTrackDisplayMask" default="$artist - $album - $title">
                        <label value="Format for single tracks in playlist:" />
                        <tooltip>The following variables are allowed: &lt;em>$artist&lt;/em>, &lt;em>$year&lt;/em>, &lt;em>$album&lt;/em>, &lt;em>$trackNumber&lt;/em>, &lt;em>$title&lt;/em>.</tooltip>
                </item>
        </page>
        <page>
                <label value="Behavior" />
                <item type="checkbox" property="EnableNotifications" default="true">
                        <label value="Enable notifications" />
                </item>
                <item type="checkbox" property="EnableScrobbling" default="true">
                        <label value="Enable scrobbling" />
                        <tooltip>This option requires at least one other scrobbler plugin, like LastFMScrobble, for example.</tooltip>
                </item>
                <item type="checkbox" property="RequestLyrics" default="true">
                        <label value="Request lyrics" />
                </item>
                <item type="checkbox" property="AutoFetchAlbumArt" default="true">
                        <label value="Automatically fetch missing album art" />
                </item>
                <item type="checkbox" property="RememberUsedProviders" default="true">
                        <label value="Remember used data providers" />
                </item>
                <item type="checkbox" property="SortWithThe" default="true">
                        <label value="Take 'The' into account when sorting" />
                </item>
                <item type="checkbox" property="FollowSymLinks" default="false">
                        <label value="Follow symbolic links" />
                </item>
                <item type="checkbox" property="AutoContinuePlayback" default="false">
                        <label value="Continue playback automatically" />
                </item>
                <item type="path" property="CoversStoragePath" default="{LCDIR}/lmp/covers">
                        <label value="Album art storage path:" />
                </item>
                <item type="spinbox" property="TransitionTime" default="0" step="100" minimum="-10000" maximum="10000">
                        <label value="Transition time between tracks:" />
                        <suffix value=" ms" />
                        <tooltip value="Setting this to positive values introduces a gap between tracks. Negative values enable crossfade. Zero requests gapless playback." />
                </item>
        </page>
        <page>
                <label value="Collection" />
                <groupbox>
                        <label value="Root paths" />
                        <item type="dataview" property="RootPathsView" />
                </groupbox>
        </page>
        <page>
                <label value="Plugin communication" />
                <item type="groupbox" property="TestOnly" state="on" checkable="true">
                        <label value="Test only these extensions" />
                        <item type="lineedit" property="TestExtensions" default="avi flac flv mkv mp3 mp4 ogg">
                                <label value="Extenstions list:" />
                        </item>
                </item>
        </page>
</settings>

Item types

checkbox

<item type="checkbox"> defines a boolean option that can be toggled. It has no additional options.

Example:

<item type="checkbox" property="ShowTrayIcon" default="false">
        <label value="Show tray icon" />
</item>

combobox

<item type="combobox"> defines a combobox with a list of options, one of which can be chosen. The list may be either static or dynamic.

In case of static option lists, each option is defined by an <option> element, which should have a name="value" attribute. The value string is returned by the BaseSettingsManager::property() method.

An option can be marked as default by having the default="true" attribute.

Static options list example:

<item type="combobox" property="SelectionBehavior">
        <label value="Tab selection behavior: " />
        <option name="PreviousActive" default="true">
                <label value="Select the previously selected tab" />
        </option>
        <option name="NextIndex">
                <label value="Select the tab to the right of the one being removed" />
        </option>
        <option name="PreviousIndex">
                <label value="Select the tab to the left of the one being removed" />
        </option>
</item>

If the user selects, for example, the second option, the corresponding property ("SelectionBehavior").toString () call will return the QString ("NextIndex") value.

dataview

dataview item defines a view for a C++ model exposed from the plugin and registered via the XmlSettingsDialog::SetDataSource(const QString& propertyName, QAbstractItemModel *model) method.

<item type="dataview"> supports the following attributes:

addEnabled
If "false", adding to the model via XSD isn't allowed (for example, the Add button is inactive).
modifyEnabled
If "false", modifying model data via XSD isn't allowed (for example, the Modify button is inactive).
removeEnabled
If "false", removing data from the model via XSD isn't allowed (for example, the Remove button is inactive).

Please note that dataviews don't have any labels, thus the <label> subelement is ignored.

The exposed model must have the exact and constant number of columns. Hierarchical models aren't supported. It must also have data for the following roles on its horizontal header items:

Qt::DisplayRole
The human-readable name of the setting.
LeechCraft::DataSources::DataSourceRole::FieldType
Must contain an int value equal to a member of the LeechCraft::DataSources::DataFieldType enumeration.



LeechCraft::DataSources::DataSourceRole::FieldValues
A QVariantList of QVariantMaps with the possible values of the type corresponding to the FieldType of the item. Currently only used for DataFieldType::Enum items. The maps should contain the following keys:
  • Icon with a value of type QIcon.
  • Name with a value of type QString.
  • ID with a vlaue of any type, it will be passed back in addRequested function.

Stuff in the LeechCraft::DataSources namespace is defined in xmlsettingsdialog/datasourceroles.h header.

The model columns are expected to contain values of types corresponding to the DataSourceRole::FieldType.

Moreover, the exposed model must have a parent, and the parent must have the following methods for adding, modifying and removing of rows to work correctly:

void addRequested (const QString& propertyName, const QVariantList& datas)
  • const QString& propertyName is the value of the property attribute of the dataview item. It is used to distinguish between different dataviews having models with the same parent.
  • const QVariant& datas is the variant list with the data of the row to append. one variant per column.
void modifyRequested (const QString& propertyName, int row, const QVariantList& datas)
  • const QString& propertyName is the value of the property attribute of the dataview item. It is used to distinguish between different dataviews having models with the same parent.
  • int row is the index of the row to modify.
  • const QVariant& datas is the variant list with the data of the row to append. one variant per column.
void removeRequested (const QString& propertyName, const QModelIndexList& rows)
  • const QString& propertyName is the value of the property attribute of the dataview item. It is used to distinguish between different dataviews having models with the same parent.
  • const QModelIndexList& rows is the list of the rows to remove. The list is sorted in reverse order so you can just iterate over it and remove the indexes from the model.

Example:

<item type="dataview" property="RootPathsView" />

Integrating with other plugins

Posted in

This section describes how your plugin can be integrated with other LeechCraft plugins and facilities, from notifications to SB2.

Roadmap & wanted

Posted in

Since this page is write-protected, discussion of desired features can be found here.

Features we want

Jamendo integration in LMP.
We need to support radio streams, obtaining artists info, recommendations, album art and similar stuff as does LastFMScrobble for Last.FM.
HAL backend for Liznoo.
This will be needed for the FreeBSD port of Liznoo, since there is no UPower there.
UPower2 backend for Liznoo.
This should be pretty simple since UPower2 resembles UPower pretty much.

Plans

0.5.85

Release date: ≈15 September.

Nicer Google Drive support.
CHM support in Monocle.
Use chmlib for this.

Plugin-specific documentation

Posted in

This chapter lists information specific to plugins, like what kind of entities a plugin accepts, API for its subplugins, etc.

Packaging

Posted in

This page describes the process of packaging for LackMan.

Basics

Generally, LackMan package consists from a description of the package and a set of archives. Each archive corresponds to a separate package version, while package description describes the most recent version.

The package description file is named like the package itself, converted to lowercase and whitespaces removed. So for a package called Cool New Icons the corresponding file would be coolnewicons.xml.

Description example

The XML file for the Modern Bubbling package, which we will create in the next section, looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<package type="theme">
 <name>Modern Bubbling</name>
 <description>An Adium chat style theme from the Adiumxtras site.</description>
 <tags>
  <tag>azoth</tag>
  <tag>theme</tag>
  <tag>chat styles</tag>
  <tag>adium</tag>
 </tags>
 <versions>
  <version size="203060">1.0</version>
 </versions>
 <images>
  <icon url=""/>
  <screenshot url="http://files.leechcraft.org/modern_bubbling.png"/>
 </images>
 <long>A nice theme with lots of variants.&lt;br />&lt;br />
This package contains two variants: plain Modern Bubbling and Compact version, which allows to group consecutive messages into a single message bubble.&lt;br />&lt;br />
This theme is available at &lt;a href="http://www.adiumxtras.com/index.php?a=xtras&amp;xtra_id=3629">http://www.adiumxtras.com/index.php?a=xtras&amp;xtra_id=3629&lt;/a></long>
 <maintainer>
  <name>Georg Rudoy</name>
  <email>0xd34df00d@gmail.com</email>
 </maintainer>
 <depends/>
</package>

Creating package description

Package description is, well, just an XML file describing that package. That XML format isn't documented yet, though I hope someday it would be.

The best method of creating the description file is to use our tiny lcpackgen program. It's simple as hell and could be obtained via git: git://github.com/0xd34df00d/lcpackgen.git. It uses Qt and builds with cmake and could be built everywhere LeechCraft can run.

Here we will show by example how lcpackgen can be used. We'd make a description file for package Modern Bubbling, which is an Adium chat style for Azoth.

So, we run lcpackgen and see something like this:

We start filling different fields and noting where the label in bottom right corner becomes green, meaning that we have provided enough information. So, we fill in some fields in the Basic tab:

Please note how we use HTML in the Long description field.

Then we set what versions are available:

Our theme doesn't have any dependencies, so we leave that field blank. Also note that the label in the bottom right corner tells now that the description is valid. We can basically save the description, but since our theme is a really nice piece of artwork, we'd provide a screenshot:

In these fields, we can list URLs of screenshots and thumbnails, one URL per line.

Finally, we save the description to a directory containing all the stuff (like archives) related to our package as modernbubbling.xml.

Archives

Creating proper archives for the packages is documented on the corresponding Archives page.

Getting it into the repo

After you've created the package description and the corresponding archive (or several archives), you can either create an issue on our issue tracker with your files attached, send them into the maillist (leechcraft-users@lists.sourceforge.net), or find us in our XMPP conference, leechcraft@conference.jabber.ru.

After that, if all goes well, your package would be in the LackMan repos:

Syndicate content