This is the applet interface of Cairo-Dock. It provides an easy way to write applets in any languages.
Note: There is another interface to control the dock itself, with wrappers in many languages; see this page.
This page describes how to write an applet for Cairo-Dock. However, you may also be interesting in the following ressources:
Introduction There are 2 ways to write an applet for Cairo-Dock, depending on what kind of applet you want:
- An applet that does fancy things, like some new 3D animations or OpenGL effects, or just that draws its icon dynamically, like a Clock.
- In this case, go for the C API and let your hacker heart express itself !
- An applet that just displays some values/text, responds to basic actions like click or scroll, adds entries in the menu and can pop-up dialogs, all in a convenient way.
- In this case, pick up your favorite langage (yes, literally!), write few lines, and be happy with a functionnal applet !
Write a full-featured applet in C Cairo-Dock is very modular: it comes with a core library (itself separated in several components) called libgldi (GL Desktop Interface). It exposes a clear and powerful API, that you can use to write any kind of applet.
Most of the time however, writing an applet in C is very straightforward, since a lot of convenient macros are provided.
To start: Grab the sources of cairo-dock-plug-ins (from the tarballs or from Git), and run the script generate-new-applet.sh; answer the few questions, install the applet, restart your dock, and voilà! a new applet is available in the config window of Cairo-Dock.
To continue: The best is to look at the code of some simple applets like Logout or ShowDesktop.
If you want to write a plug-in that extends the possibilities of the dock (not an applet which has an icon inside the dock), take a look at Icon-effects for instance.
The complete API is described at http://doc.glx-dock.org, or you can generate it by going into cairo-dock-core/doc and running generate-doc.sh.
Writing easily an applet in your favorite language (Python, Ruby, Vala, Mono, Bash, etc.) Whatever language you choose, the way is the same: write a class that inherits from the CDApplet class, and then override the methods you need.
Currently supported languages are Python, Ruby, Mono, Vala, and Bash. You can easily write an interface for any language that supports DBus (Java, Perl, etc).
A concrete exemple in Python is available at the end of this page. For other examples, please have a look to our demo applets (python/bash/ruby/mono/vala) : Demos applets on Github
Starting
Say the name of your applet is "demo"; make a folder named demo, where you'll put all your files, and place this folder in ~/.config/cairo-dock/third-party.
In this folder, we'll have :
- demo : the executable script, without extension (the shebang on the first line is used to launch it)
- demo.conf : the default config file (see below for the options syntax)
- auto-load.conf : the file describing our applet
- icon : the default icon of the applet (optionnal, no extension, it can be any image format)
- preview : a preview of this applet (optionnal, no extension, it can be any image format)
The auto-load.conf describes your applet (name, author, etc); here is an example:
[Register]
# Author of the applet
author = Fabounet
# A short description of the applet and how to use it.
description = This is the description of the applet\nIt can be on several lines.
# Category of the applet : 2 = files, 3 = internet, 4 = Desktop, 5 = accessory, 6 = fun
category = 5
# Version of the applet; change it everytime you change something in the config file. Don't forget to update the version both in this file and in the config file.
version = 0.0.1
# Default icon to use if no icon has been defined by the user. If not specified, or if the file is not found, the "icon" file will be used.
icon =
# Whether the applet can be instanciated several times or not.
multi-instance = true
# Whether the applet will act as a launcher or not (like Pidgin or Transmission)
act as launcher = false
The demo.conf file lists all the options of your applet (it usually has 3 groups: icon options, desklet options, and parameters, where you add your parameters).
See any applet in ~/.config/cairo-dock/current_theme/plug-ins for some examples.
See the options list for more details.
Overriding the methods you need The methods available are:
Applet definition - begin : do here what you need to do when your applet is started
- end: if you have anything to do when your applet is terminated
- reload: reload your applet with the new config parameters
- get_config: get your config parameters from the config keyfile.
Actions from the user - on_click: action to do when the user clicks on the icon
- on_middle_click: same with middle-click
- on_scroll: same with scroll up/down
- on_build_menu: same with right-click (which opens the menu)
- on_menu_select: action to do when the user selects one of the entries you added beforehand in the menu
- on_drop_data: action to do when the user drops some data on the icon
- on_answer_dialog: action to do when the user answers a dialog you raised beforehand
- on_shortkey: action to do when the user triggers a shortkey you registered beforehand
- on_change_focus: action to do when the user give or remove the focus on the window controlled by the applet
Acting on the icon The CDApplet class holds the main icon of your applet (icon), and the sub-icons (the icons in the sub-dock, optional) (sub_icons). You can act on the icon and the sub-icons with several methods (examples are given in Python, you can easily adapt):
SetQuickInfo Sets the quick-info on our icon (this is a small text displayed on the icon).E.g.:
self.icon.SetQuickInfo("123")
SetLabel Sets the label of our icon (its name), overwriting the previous one. E.g.:
self.icon.SetLabel("Monday")
SetIcon Sets the image of our icon, overwriting the previous one.
You can refer to the image by either its name if it's an image from a icon theme, or by a path. E.g.:
self.icon.SetIcon("gimp")
self.icon.SetIcon("gtk-go-up")
self.icon.SetIcon("/path/to/image")
SetEmblem Sets an emblem on our icon. The emblem is drawn directly on the icon, so if you want to remove it, you have to use SetEmblem without link image.
The image is given by its path, and the position of the emblem is fixed by one of the following integer:
0 = UPPER_LEFT, 1 = LOWER_RIGHT, 2 = LOWER_LEFT, 3 = UPPER_RIGHT, 4 = MIDDLE :
self.icon.SetEmblem("./emblem-charged.png", CDApplet.UPPER_RIGHT)
To erase the emblem you have to reset the icon, using SetIcon method with the default "icon" parameter.
Animate Animates our icon, with a given animation and for a given number of rounds. E.g.:
self.icon.Animate("default",2)
self.icon.Animate("fire",30)
DemandsAttention Like the Animate method, but will animate the icon endlessly, and the icon will be visible even if the dock is hidden. If the animation is an empty string, or "default", the animation used when an application demands the attention will be used. The first argument is True to start demanding the attention, and False to stop it. E.g.:
self.icon.DemandsAttention(True,"bounce")
self.icon.DemandsAttention(False,"")
ShowDialog Pops up a simple dialog bubble on our icon, with a given message and for a given duration (in seconds). The dialog can be closed by clicking on it. E.g.:
self.icon.ShowDialog("Cairo-Dock is great!",4)
PopupDialog Pops up a dialog bubble on our icon (since 2.2.0). The dialog can contain a message, an icon, some buttons, and a widget the user can act on.
To get the answer, you need to connect to the "on_answer_dialog" signal.
The dialog has several attributes, and the interaction widget too. Each of these are optionnal.
dialog attributes:
- message (string) : the message of the dialog (empty by default)
- icon (string) : an icon to be displayed next to the message (same icon as the applet by default)
- time-length (int) : time length of the dialog, in second (unlimited duration by default)
- force-above (bool): true to force the dialog above (use it with parcimony, false by default)
- use-markup (bool) : true to use markups (HTML)
- buttons (string) : images of the buttons, separated by comma ";". Adding buttons will trigger an on_answer_dialog signal when the user press one of them. "ok" and "cancel" are used as keywords for the default "ok" and "cancel" buttons defined by the dock.
widget attributes:
- widget-type (string) : type of the widget (can be "text-entry", "scale", "list")
text-entry attributes:
- multi-lines (bool) : true to have a multi-lines text-entry, ie a text-view (false by default)
- editable (bool) : whether the user can modify the text or not (true by default)
- visible (bool) : whether the text will be visible or not (useful to type passwords) (true by default)
- nb-chars (int) : maximum number of chars (the current number of chars will be displayed next to the entry) (infinite by default)
- initial-value (string): text initially contained in the entry (empty by default)
scale attributes:
- min-value (double) : lower value (0 by default)
- max-value (double) : upper value (100 by default)
- nb-digit (int) : number of digits after the dot (2 by default)
- initial-value (double): value initially set to the scale (0 by default)
- min-label (string) : label displayed on the left of the scale (empty by default)
- max-label (string) : label displayed on the right of the scale (empty by default)
list attributes:
- editable (bool) : true if a non-existing choice can be entered by the user (in this case, the content of the widget will be the selected text, and not the number of the selected line) (false by default)
- values (string) : a list of values, separated by comma ";", used to fill the combo list.
- initial-value (string or int, depending on the "editable" attribute) :
- The initial selection (the number of line or the initial text depending on
- The "editable" attribute) (0 or empty by default)
The following example pops up a simple message for 5 seconds:
self.icon.PopupDialog( {"message" : "Hello World!",
"time-length" : 5},
{} )
The following example pops up a dialog to select a password of 30 characters max:
self.icon.PopupDialog( {"message" : "Enter your password:",
"buttons" : "ok;cancel",
"icon" : "gtk-stock-edit"},
{"widget-type" : "text-entry",
"visible" : false,
"nb-chars" : 30} )
AddDataRenderer This method lets you add a data renderer, that is to say a gauge, graph, progressbar, etc.
You define the data renderer with a type ("gauge", "graph", "progressbar"), the number of values to render, and a theme. E.g.:
self.icon.AddDataRenderer("gauge",2,"Turbo-night-fuel")
self.icon.AddDataRenderer("progressbar",1,"")
RenderValues Renders some values on your icon. You must have added a data renderer before with the previous method.
The number of values you send to the dock is the one you defined with the previous method. Values are given between 0 and 1. E.g.:
self.icon.RenderValues([0.7, 0.2])
ControlAppli Makes your applet control the window of an external application, instead of the Taskbar (Logout, MusicPlayer and System-Monitor use this feature).
The parameter is the class of the application you wish to control (which is most of the time the name of the appli, but not always; use "xprop | grep CLASS" to find it), or "none" to stop controlling any appli. You can then override the on_change_focus method if you need to track the active state of the application. E.g.:
self.icon.ControlAppli("pidgin")
ActOnAppli Send an action on the application controlled by the icon (see ControlAppli). The parameter is the action. It can be:
- "minimize" to hide the window
- "show" to show the window and give it focus
- "toggle-visibility" to show or hide
- "maximize" to maximize the window
- "restore" to restore the window
- "toggle-size" to maximize or restore
- "close" to close the window (Note: some programs will just hide the window and stay in the systray)
- "kill" to kill the X window
E.g.:self.icon.ActOnAppli("show")
BindShortkey Bind keyboard shortcuts to your applet (since 2.2.0). You can bind several shortkey at once, since the method takes an array as input.
Then, you need to override the method on_shortkey to be notified when the shortkey is pressed. E.g.:
self.icon.BindShortkey(["<Control>F8","<Control><Shift>Z"])
AddMenuItems It allows you to add any kind of entries to the menu that is popped up on a right-click (so you can only use this method in the on_build_menu method).
It takes a an array of dicts as parameter. Each dict represents 1 menu-item.
The dict contains the properties of the menu-item in the form of variants :
- type (integer) : 0 <-> normal entry, 1 <-> sub-menu, 2 <-> separator, 3 <-> check-box, 4 <-> grouped radio-box. By default it is 0.
- label (string) : the text displayed in the menu-item.
- icon (string) : a GTK stock icon, or the path to an image. (optionnal)
- id (integer) : the ID of the menu-item, that will be passed to the on_menu_select method when it is selected by the user.
- menu (integer) : the ID of a menu where the menu-item will be placed : 0 <-> the main menu, -1 <-> the default sub-menu (it is automatically built and contains at least the applet's handbook), another integer <-> the ID of a previously added sub-menu. By default it is -1.
- state (boolean) : the state of the entry in the case of a check-box or a radio-button. By default it is False.
- tooltip (string): a tooltip (message that will appear when you let the mouse over the item). (optionnal)
When the user chooses one of these entries, you will get a "on_menu_select" signal with the ID of the selected entry.
The following exemple in Python will insert a normal entry in the main menu, followed by a separator and a sub-menu, in which there will be a check-box and an entry, and will insert a group of 3 radio-buttons in the default sub-menu, the 2nd button being active.
self.icon.AddMenuItems( {"widget-type" : 0,
"label": "this is an entry of the main menu",
"icon" : "gtk-add",
"menu" : 0,
"id" : 1,
"tooltip" : "this is the tooltip that will appear when you hover this entry"},
{"widget-type" : 2,
"menu" : 0},
{"widget-type" : 1,
"label": "this is a sub-menu",
"icon" : "/path/to/image",
"menu" : 0,
"id" : 2},
{"widget-type" : 3,
"label": "this entry can be true or false",
"icon" : "/path/to/another/image",
"menu" : 1,
"state": True,
"id" : 101},
{"widget-type" : 0,
"label": "bla bla bla",
"menu" : 1,
"id" : 102},
{"widget-type" : 4,
"label": "you can choose this entry",
"menu" : -1,
"group": 201,
"id" : 201},
{"widget-type" : 4,
"label": "or this one",
"menu" : -1,
"group": 201,
"state": True,
"id" : 202},
{"widget-type" : 4,
"label": "or even this one",
"menu" : -1,
"group": 201,
"id" : 203} )
A real example in Python
Let's say we need to create a menu ("Identi.ca"), and a sub menu ("Add") as shown on the image bellow
The following code solves the problem, I am going to explain how it works. First, the dict "menu" is going to be created, it has type MENU_SUB_MENU exactly because it is going to contain a sub menu. When you define type MENU_SUB_MENU it does not means that you are creating a sub menu, but instead that you are creating a menu that is going to show a sub menu. On the label, set the text you want to appear for the user. The value for id you have to define, choose something that is not -1, or 0, as explained before those IDs are special IDs on the CDApplet interface, and are used to another kind of menus. On this case I chosed 5000. Set the path you want for the icon. For the sub menu the same ideas are applied for type, label, id, and icon. The only difference is that you have to set a reference to the parent menu, and that is the menu value. Therefore, since "Identi.ca" menu is parent of "Add" menu, we set menu as the id of "Identi.ca" menu, i.e., the variable identica_menu_id, or 5000.
self.identica_menu_id = 5000
self.add_identica_menu_id = 5001
def build_identica_menu(self):
identica_menu = []
menu = {
'type' : CDApplet.MENU_SUB_MENU,
'label' : _("Identi.ca"),
'id' : self.identica_menu_id,
'icon' : os.path.abspath("./data/identica.png")
}
sub_menu = {
'type' : CDApplet.MENU_ENTRY,
'label' : _("Add"),
'id' : self.add_identica_menu_id,
'menu' : self.identica_menu_id
}
identica_menu.append (menu)
identica_menu.append (sub_menu)
self.icon.AddMenuItems(identica_menu)
Cairo-Dock creates the menus for you, but you have to call the method that creates them on the on_build_menu method, this method is inherited from the CDApplet, so, following this example:
def on_build_menu(self):
self.build_identica_menu()
Get Get a property of the icon of your applet. Current available properties are :
- x : x position of the icon's center on the screen (starting from 0 on the left)
- y : y position of the icon's center on the screen (starting from 0 at the top of the screen)
- width : width of the icon, in pixels (this is the maximum width, when the icon is zoomed)
- height : height of the icon, in pixels (this is the maximum height, when the icon is zoomed)
- container : type of container of the applet (DOCK, DESKLET)
- orientation : orientation of the container on the screen (BOTTOM, TOP, RIGHT, LEFT). A desklet has always an orientation of BOTTOM.
- Xid : ID of the application's window which is controlled by the applet, or 0 if none (this parameter can only be non nul if you used the method ControlAppli beforehand).
- has_focus : whether the application's window which is controlled by the applet is the current active window (it has the focus) or not. E.g.:
w = self.icon.Get(width")
GetAll Get all the available properties in a dictionnary. E.g.:
props = self.icon.GetAll()
Methods that act on the sub-icons are the same, except that they take as their last argument the ID of the icon (a unique string that you have assigned to each icon when using the AddSubIcons method).
Eg:self.sub_icons.SetQuickInfo("abc", "id1")
A concrete example in Python
from __future__ import print_function import random
from CDApplet import CDApplet
class Applet(CDApplet):
def __init__(self):
self.count = 0
CDApplet.__init__(self)
def set_counter(self,count):
self.count = count
percent = float(self.count)/self.config['iMaxValue']
self.icon.RenderValues([percent])
self.icon.SetQuickInfo(format(self.count, "d"))
def get_config(self,keyfile):
print("*** get config")
self.config['cTheme'] = keyfile.get('Configuration', 'theme')
self.config['iMaxValue'] = keyfile.getint('Configuration', 'max value')
self.config['yesno'] = keyfile.getboolean('Configuration', 'yesno')
def end(self):
print("*** end of demo_python")
def begin(self):
print("*** begin")
self.icon.ShowDialog("I'm connected to Cairo-Dock !", 4) self.icon.AddDataRenderer("gauge", 1, self.config['cTheme']) self.set_counter(0) self.sub_icons.AddSubIcons(["icon 1", "firefox-3.0", "id1", "icon 2", "trash", "id2", "icon 3", "thunderbird", "id3", "icon 4", "nautilus", "id4"]) self.sub_icons.RemoveSubIcon("id2") self.sub_icons.SetQuickInfo("1", "id1") self.sub_icons.SetQuickInfo("3", "id3")
self.sub_icons.SetQuickInfo("4", "id4")
self.icon.BindShortkey(["<Control>F9"]) def reload(self):
print("*** reload")
self.icon.AddDataRenderer("gauge", 1, self.config['cTheme'])
self.icon.RenderValues([float(self.count)/self.config['iMaxValue']])
self.sub_icons.RemoveSubIcon("any")
self.sub_icons.AddSubIcons(["icon 1", "firefox-3.0", "id1", "icon 2", "natilus", "id2", "icon 3", "thunderbird", "id3"])
def on_click(self,iState):
print("*** click")
self.set_counter(random.randint(0,self.config['iMaxValue']))
def on_middle_click(self):
print("*** middle click")
dialog_attributes = {
"icon" : "stock_properties",
"message" : "Set the value you want",
"buttons" : "ok;cancel"}
widget_attributes = {
"widget-type" : "scale",
"max-value" : self.config['iMaxValue'],
"message" : "Set the value you want"}
self.icon.PopupDialog(dialog_attributes, widget_attributes)
def on_build_menu(self):
print("*** build menu")
items = [ {
"label": "set min value",
"icon" : "gtk-zoom-out",
"id" : 1
}, {
"label": "set medium value",
"icon" : "gtk-zoom-fit",
"id" : 2
}, {
"label": "set max value",
"icon" : "gtk-zoom-in",
"id" : 3
} ]
self.icon.AddMenuItems(items)
def on_menu_select(self,iNumEntry):
print("*** choice",iNumEntry,"has been selected !")
if iNumEntry == 1:
self.set_counter(0)
elif iNumEntry == 2:
self.set_counter(self.config['iMaxValue']/2)
elif iNumEntry == 3:
self.set_counter(self.config['iMaxValue'])
def on_scroll(self,bScrollUp):
print("*** scroll !")
if bScrollUp:
count = min(self.config['iMaxValue'], self.count+1)
else:
count = max(0, self.count-1)
self.set_counter(count)
def on_drop_data(self,cReceivedData):
print("*** received",cReceivedData)
self.icon.SetLabel(cReceivedData)
def on_answer_dialog(self,button, answer):
print("*** answer dialog :",button, answer)
self.set_counter(int (answer))
def on_shortkey(self,key):
print("*** shortkey :",key)
if __name__ == '__main__':
Applet().run()
Building the Applet's Configuration Window
The configuration window of an applet is reached with a right-click on the applet icon and selecting "configure this applet" or from the main configuration panel, by clicking on the button corresponding to the applet.
The content of the configuration window of the applet is loaded from the .conf file in the ~/.config/cairo-dock/current_theme/plugins/applet-name folder, where "applet-name" is the name of the applet. The .conf file is named after the applet, using the same spelling (case sensitive). The .conf file in the above mentioned folder is the user configuration file (hereafter named as such), which will change as the user makes his/her modifications. The original configuration file (hereafter named as such) file is found at ~/.config/cairo-dock/third-party/applet-name, where "applet-name" is the name of the applet.
If there is no user configuration file, or if this file's version number is inferior to the original configuration file's, a copy of the latter is used to create the former. In order for the user configuration file to update to the original, the original configuration file's version must be increased.
The configuration file's version number should be identical to the applet's version number. Prefer the classic format x.y.z. Change it each time you modify the .conf file.
Editing the Configuration File The configuration file of an applet is a classical group/key file:
- Lines starting with a # are comments.
- Groups (or sections) are between brackets, eg. [section-name].
- Keys are of the form key-name=value
A configuration file is filled with four types of data: comments, sections, widget descriptors and key=value pairs. Each occupies one line of the configuration file.
- Comments are preceded by #, the first line of the configuration file is commented and stands for the version number of the configuration file.
- Sections are describing and organising the content of the configuration file and configuration window. Sections are written between brackets eg. [section], where section is the section name. The sections [Icon] and [Desklet] should be there by default, usually these two sections are left untouched. Typically you use a third section [Configuration] followed by the data you require. Depending on the applet you are making, you may need other sections, which you can name as you wish. Each new section in the configuration file will appear as a new tab in the configuration window.
- Widget descriptors are preceded by #. The information following # will change the type, icon and labels of the widget displayed in the configuration window. A list of these descriptors is available here below.
- Key-Value pairs are identified by the = sign following the key name. The key is the variable name you need to retrieve for your code and the value is the value of that variable. The key name need not be the same as the variable in your code, but it is a good practice to call them the same, for better reading. The key can have no value, the equal sign is then followed by nothing (or a return sign depending on your perspective). To add a value to a key in the original configuration file will make it the default value. If you use a drop-down (aka. combo) list as a widget, make sure the default value is the first of the list or it may become confusing when opening the configuration window.
Reading from the Configuration File The applet inherits a method from the CDApplet() class called get_config(self,keyfile). "keyfile" is a variable Cairo-Dock will recognise as the user configuration file object, hence there is no need to specify the path to it.
The configuration file object "keyfile" has several methods to fetch and load data to the applet:
- keyfile.getstring(section, key)
- keyfile.getint(section, key)
- keyfile.getboolean(section, key)
In the above methods, section is a section name of the configuration file, which the method will recognize because it is written in brackets ([section]) and key is a key of the configuration file, the value of which you want to retrieve and which the method will recognize be cause it is followed by an equal sign (key=value).
Usually you won't need to write to the config file, although you could do it with keyfile.setxyz
List and Syntax of Widget Descriptors NB: This list may not be exhaustive, but it strives to. Please add any if missing. You can take a look at the file cairo-dock-gui-factory.h too.
Tabs
Tabs are automatically created for each new section in the configuration file.
Tab Icons
Tab icons, may precede the section name. The icon can be one of the Gtk Stock Icons (need documentation for Gtk Stock Icons) or an absolute path to another icon.
Frames
Frames can be used to group a certain type of data. The widget descriptor contains the label and optional icon. The icon again may be from Gtk Stock or absolute. Its widget descriptor needs to be followed by a key identifying the frame name variable. The variable will not appear on the configuration screen.
Combo/Drop-down Lists (integer)
Lists provide multiple choices for a user to choose from. The first item of this list should be the default value unless you have good reasons to do otherwise. Lists return integers as values to the key that follow them.
Combo/Drop-down Lists (strings)
Idem, but the list returns strings rather than integers:
Combo/Drop-down List for Animations
Preformated list for choosing between existing animations. If no selection is made, the default animation will be used.
Numbered list
A list where the number of the line is used for the choice (so you get an integer in the key value).
Check Buttons
Check button return a boolean as the value of the key that follows it.
Tool Tips
Tooltips appear when hovering a widget with the mouse pointer. They follow on the next line the widget they should support:
Horizontal Scale (int)
A horizontal scale returns an integer between the minimum and maximum integer defined by its descriptor.
Horizontal Scale (float)
A horizontal scale returns a float between the minimum and maximum integer defined by its descriptor.
Expander
Expanders are expandable/retractable frames. They save a lot of space on the window, so don't hesitate to use theme. Their descriptor can include an icon. Just like frame they must be followed by an empty key as their variable.
Theme Chooser
Provides a list of themes from a folder, a description and a preview of the theme if any.
Colour Chooser
Color chooser returns a RGBA value for the selected colour.
If you only need an RGB value, use 'c':
Font Chooser
Font chooser returns a font name and size in a single string.
File Chooser (with button)
The file chooser has an input field for the path to file name and a button to browse and search the file instead.
Text Input
An input field for the user to fill.
Spin Box
A spin box returns an integer. |