diff --git a/CMakeLists.txt b/CMakeLists.txt
index 232cf7c..5868732 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -100,6 +100,7 @@ include_directories( ${PROJECT_BINARY_DIR}
src/tooltip
src/util
src/execplugin
+ src/button
src/freespace
src/separator
${X11_INCLUDE_DIRS}
@@ -128,6 +129,7 @@ set( SOURCES src/config.c
src/taskbar/taskbarname.c
src/tooltip/tooltip.c
src/execplugin/execplugin.c
+ src/button/button.c
src/freespace/freespace.c
src/separator/separator.c
src/tint2rc.c
diff --git a/ChangeLog b/ChangeLog
index 8f85c01..8387e83 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2017-03-25 master
+- Enhancements:
+ - New plugin: button.
+
2017-03-25 0.13.3
- Fixes:
- Fixed autohide for non-bottom panels (issue #632)
diff --git a/doc/manual.html b/doc/manual.html
index f223a9c..2e7eeff 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -229,6 +229,7 @@ It was specifically made for Openbox but it should also work with other window m
Tooltip
Battery
Executor
+Button
Separator
Example configuration
@@ -359,6 +360,7 @@ gradient_id_pressed = 2
C
shows the Clock
F
adds an extensible spacer (freespace). You can specify more than one. Has no effect if T
is also present. (since 0.12)
E
adds an executor plugin. You can specify more than one. (since 0.12.4)
+P
adds a push button. You can specify more than one. (since 0.14)
:
adds a separator. You can specify more than one. (since 0.13.0)
For example, panel_items = STC
will show the systray, the taskbar and the clock (from left to right).
@@ -650,6 +652,23 @@ execp_command = stdbuf -oL bwm-ng -o csv -t 1000 | awk -F ';' '/total/ { printf
execp_continuous = 1
execp_interval = 1
+
+
+button = new
: Begins the configuration of a new button. Multiple such plugins are supported; just use multiple P
s in panel_items
. (since 0.14)
+button_icon = text
: Name or path of icon (or empty). (since 0.14)
+button_text = text
: Text to display (or empty). (since 0.14)
+button_tooltip = text
: The tooltip (or empty). (since 0.14)
+button_font = [FAMILY-LIST] [STYLE-OPTIONS] [SIZE]
: The font used to draw the text. (since 0.14)
+button_font_color = color opacity
: The font color. (since 0.14)
+button_background_id = integer
: Which background to use. (since 0.14)
+button_centered = boolean (0 or 1)
: Whether to center the text. (since 0.14)
+button_padding = horizontal_padding vertical_padding spacing_between_icon_and_text
(since 0.14)
+button_lclick_command = text
: Command to execute on left click. If not defined, execp_command
is executed immediately, unless it is currently running. (since 0.14)
+button_mclick_command = text
: Command to execute on right click. If not defined, execp_command
is executed immediately, unless it is currently running. (since 0.14)
+button_rclick_command = text
: Command to execute on middle click. If not defined, execp_command
is executed immediately, unless it is currently running. (since 0.14)
+button_uwheel_command = text
: Command to execute on wheel scroll up. If not defined, execp_command
is executed immediately, unless it is currently running. (since 0.14)
+button_dwheel_command = text
: Command to execute on wheel scroll down. If not defined, execp_command
is executed immediately, unless it is currently running. (since 0.14)
+
Separator
separator = new
: Begins the configuration of a new separator. Multiple such plugins are supported; just use multiple :
s in panel_items
. (since 0.13.0)
@@ -659,142 +678,7 @@ execp_interval = 1
separator_size = integer
: The thickness of the separator. Does not include the border and padding. For example, if the style is line
, this is the line thickness; if the style is dots
, this is the dot's diameter. (since 0.13.0)
separator_padding = side_padding cap_padding
: The padding to add to the sides of the separator, in pixels. (since 0.13.0)
-Example configuration
#---------------------------------------------
-## TINT2 CONFIG FILE
-#---------------------------------------------
-
-#---------------------------------------------
-## BACKGROUND AND BORDER
-#---------------------------------------------
-rounded = 7
-border_width = 2
-background_color = #000000 60
-border_color = #ffffff 18
-
-rounded = 5
-border_width = 0
-background_color = #ffffff 40
-border_color = #ffffff 50
-
-rounded = 5
-border_width = 0
-background_color = #ffffff 18
-border_color = #ffffff 70
-
-#---------------------------------------------
-## PANEL
-#---------------------------------------------
-panel_monitor = all
-panel_position = bottom center
-panel_size = 94% 30
-panel_margin = 0 0
-panel_padding = 7 0
-font_shadow = 0
-panel_background_id = 1
-wm_menu = 0
-panel_dock = 0
-panel_layer = bottom
-
-#---------------------------------------------
-## TASKBAR
-#---------------------------------------------
-#taskbar_mode = multi_desktop
-taskbar_mode = single_desktop
-taskbar_padding = 2 3 2
-taskbar_background_id = 0
-#taskbar_active_background_id = 0
-
-#---------------------------------------------
-## TASKS
-#---------------------------------------------
-task_icon = 1
-task_text = 1
-task_maximum_size = 140 35
-task_centered = 1
-task_padding = 6 3
-task_font = sans 7
-task_font_color = #ffffff 70
-task_background_id = 3
-task_icon_asb = 100 0 0
-## replace STATUS by 'urgent', 'active' or 'iconified'
-#task_STATUS_background_id = 2
-#task_STATUS_font_color = #ffffff 85
-#task_STATUS_icon_asb = 100 0 0
-## example:
-task_active_background_id = 2
-task_active_font_color = #ffffff 85
-task_active_icon_asb = 100 0 0
-urgent_nb_of_blink = 8
-
-#---------------------------------------------
-## SYSTRAYBAR
-#---------------------------------------------
-systray = 1
-systray_padding = 0 4 5
-systray_background_id = 0
-systray_sort = left2right
-systray_icon_size = 0
-systray_icon_asb = 100 0 0
-
-#---------------------------------------------
-## CLOCK
-#---------------------------------------------
-time1_format = %H:%M
-time1_font = sans 8
-time2_format = %A %d %B
-time2_font = sans 6
-clock_font_color = #ffffff 76
-clock_padding = 1 0
-clock_background_id = 0
-#clock_lclick_command = xclock
-clock_rclick_command = orage
-#clock_tooltip = %A %d %B
-#time1_timezone = :US/Hawaii
-#time2_timezone = :Europe/Berlin
-#clock_tooltip_timezone = :/usr/share/zoneinfo/Europe/Paris
-
-#---------------------------------------------
-## BATTERY
-#---------------------------------------------
-battery = 0
-battery_hide = 98
-battery_low_status = 10
-battery_low_cmd = notify-send "battery low"
-bat1_font = sans 8
-bat2_font = sans 6
-battery_font_color = #ffffff 76
-battery_padding = 1 0
-battery_background_id = 0
-
-#---------------------------------------------
-## TOOLTIP
-#---------------------------------------------
-tooltip = 0
-tooltip_padding = 2 2
-tooltip_show_timeout = 0.7
-tooltip_hide_timeout = 0.3
-tooltip_background_id = 1
-tooltip_font_color = #OOOOOO 80
-tooltip_font = sans 10
-
-#---------------------------------------------
-## MOUSE ACTION ON TASK
-#---------------------------------------------
-mouse_middle = none
-mouse_right = close
-mouse_scroll_up = toggle
-mouse_scroll_down = iconify
-
-#---------------------------------------------
-## AUTOHIDE OPTIONS
-#---------------------------------------------
-autohide = 0
-autohide_show_timeout = 0.3
-autohide_hide_timeout = 2
-autohide_height = 4
-strut_policy = minimum
-
-AUTHOR
tint2 was written by Thierry Lorthiois lorthiois@bbsoft.fr.
+
Example configuration
See /etc/xdg/tint2/tint2rc.
AUTHOR
tint2 was written by Thierry Lorthiois lorthiois@bbsoft.fr.
It is based on ttm, originally written by Pål Staurland staura@gmail.com.
This manual page was originally written by Daniel Moerner dmoerner@gmail.com, for the Debian project (but may be used by others).
It was adopted from the tint2 docs.
SEE ALSO
The main website https://gitlab.com/o9000/tint2
and the wiki page at https://gitlab.com/o9000/tint2/wikis/home.
This documentation is also provided in HTML and Markdown format in the system's default location
diff --git a/doc/tint2.1 b/doc/tint2.1
index a502a54..10a6cdf 100644
--- a/doc/tint2.1
+++ b/doc/tint2.1
@@ -67,6 +67,8 @@ Battery \[la]#battery\[ra]
.IP \(bu 2
Executor \[la]#executor\[ra]
.IP \(bu 2
+Button \[la]#button\[ra]
+.IP \(bu 2
Separator \[la]#separator\[ra]
.IP \(bu 2
Example configuration \[la]#example-configuration\[ra]
@@ -294,6 +296,8 @@ gradient_id_pressed = 2
.IP \(bu 2
\fB\fCE\fR adds an executor plugin. You can specify more than one. \fI(since 0.12.4)\fP
.IP \(bu 2
+\fB\fCP\fR adds a push button. You can specify more than one. \fI(since 0.14)\fP
+.IP \(bu 2
\fB\fC:\fR adds a separator. You can specify more than one. \fI(since 0.13.0)\fP
.RE
.PP
@@ -800,6 +804,37 @@ execp_continuous = 1
execp_interval = 1
.fi
.RE
+.SS Button
+.RS
+.IP \(bu 2
+\fB\fCbutton = new\fR : Begins the configuration of a new button. Multiple such plugins are supported; just use multiple \fB\fCP\fRs in \fB\fCpanel_items\fR\&. \fI(since 0.14)\fP
+.IP \(bu 2
+\fB\fCbutton_icon = text\fR : Name or path of icon (or empty). \fI(since 0.14)\fP
+.IP \(bu 2
+\fB\fCbutton_text = text\fR : Text to display (or empty). \fI(since 0.14)\fP
+.IP \(bu 2
+\fB\fCbutton_tooltip = text\fR : The tooltip (or empty). \fI(since 0.14)\fP
+.IP \(bu 2
+\fB\fCbutton_font = [FAMILY\-LIST] [STYLE\-OPTIONS] [SIZE]\fR : The font used to draw the text. \fI(since 0.14)\fP
+.IP \(bu 2
+\fB\fCbutton_font_color = color opacity\fR : The font color. \fI(since 0.14)\fP
+.IP \(bu 2
+\fB\fCbutton_background_id = integer\fR : Which background to use. \fI(since 0.14)\fP
+.IP \(bu 2
+\fB\fCbutton_centered = boolean (0 or 1)\fR : Whether to center the text. \fI(since 0.14)\fP
+.IP \(bu 2
+\fB\fCbutton_padding = horizontal_padding vertical_padding spacing_between_icon_and_text\fR \fI(since 0.14)\fP
+.IP \(bu 2
+\fB\fCbutton_lclick_command = text\fR : Command to execute on left click. If not defined, \fB\fCexecp_command\fR is executed immediately, unless it is currently running. \fI(since 0.14)\fP
+.IP \(bu 2
+\fB\fCbutton_mclick_command = text\fR : Command to execute on right click. If not defined, \fB\fCexecp_command\fR is executed immediately, unless it is currently running. \fI(since 0.14)\fP
+.IP \(bu 2
+\fB\fCbutton_rclick_command = text\fR : Command to execute on middle click. If not defined, \fB\fCexecp_command\fR is executed immediately, unless it is currently running. \fI(since 0.14)\fP
+.IP \(bu 2
+\fB\fCbutton_uwheel_command = text\fR : Command to execute on wheel scroll up. If not defined, \fB\fCexecp_command\fR is executed immediately, unless it is currently running. \fI(since 0.14)\fP
+.IP \(bu 2
+\fB\fCbutton_dwheel_command = text\fR : Command to execute on wheel scroll down. If not defined, \fB\fCexecp_command\fR is executed immediately, unless it is currently running. \fI(since 0.14)\fP
+.RE
.SS Separator
.RS
.IP \(bu 2
@@ -817,144 +852,7 @@ execp_interval = 1
.RE
.SS Example configuration
.PP
-.RS
-.nf
-#\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
-## TINT2 CONFIG FILE
-#\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
-
-#\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
-## BACKGROUND AND BORDER
-#\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
-rounded = 7
-border_width = 2
-background_color = #000000 60
-border_color = #ffffff 18
-
-rounded = 5
-border_width = 0
-background_color = #ffffff 40
-border_color = #ffffff 50
-
-rounded = 5
-border_width = 0
-background_color = #ffffff 18
-border_color = #ffffff 70
-
-#\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
-## PANEL
-#\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
-panel_monitor = all
-panel_position = bottom center
-panel_size = 94% 30
-panel_margin = 0 0
-panel_padding = 7 0
-font_shadow = 0
-panel_background_id = 1
-wm_menu = 0
-panel_dock = 0
-panel_layer = bottom
-
-#\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
-## TASKBAR
-#\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
-#taskbar_mode = multi_desktop
-taskbar_mode = single_desktop
-taskbar_padding = 2 3 2
-taskbar_background_id = 0
-#taskbar_active_background_id = 0
-
-#\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
-## TASKS
-#\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
-task_icon = 1
-task_text = 1
-task_maximum_size = 140 35
-task_centered = 1
-task_padding = 6 3
-task_font = sans 7
-task_font_color = #ffffff 70
-task_background_id = 3
-task_icon_asb = 100 0 0
-## replace STATUS by 'urgent', 'active' or 'iconified'
-#task_STATUS_background_id = 2
-#task_STATUS_font_color = #ffffff 85
-#task_STATUS_icon_asb = 100 0 0
-## example:
-task_active_background_id = 2
-task_active_font_color = #ffffff 85
-task_active_icon_asb = 100 0 0
-urgent_nb_of_blink = 8
-
-#\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
-## SYSTRAYBAR
-#\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
-systray = 1
-systray_padding = 0 4 5
-systray_background_id = 0
-systray_sort = left2right
-systray_icon_size = 0
-systray_icon_asb = 100 0 0
-
-#\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
-## CLOCK
-#\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
-time1_format = %H:%M
-time1_font = sans 8
-time2_format = %A %d %B
-time2_font = sans 6
-clock_font_color = #ffffff 76
-clock_padding = 1 0
-clock_background_id = 0
-#clock_lclick_command = xclock
-clock_rclick_command = orage
-#clock_tooltip = %A %d %B
-#time1_timezone = :US/Hawaii
-#time2_timezone = :Europe/Berlin
-#clock_tooltip_timezone = :/usr/share/zoneinfo/Europe/Paris
-
-#\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
-## BATTERY
-#\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
-battery = 0
-battery_hide = 98
-battery_low_status = 10
-battery_low_cmd = notify\-send "battery low"
-bat1_font = sans 8
-bat2_font = sans 6
-battery_font_color = #ffffff 76
-battery_padding = 1 0
-battery_background_id = 0
-
-#\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
-## TOOLTIP
-#\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
-tooltip = 0
-tooltip_padding = 2 2
-tooltip_show_timeout = 0.7
-tooltip_hide_timeout = 0.3
-tooltip_background_id = 1
-tooltip_font_color = #OOOOOO 80
-tooltip_font = sans 10
-
-#\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
-## MOUSE ACTION ON TASK
-#\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
-mouse_middle = none
-mouse_right = close
-mouse_scroll_up = toggle
-mouse_scroll_down = iconify
-
-#\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
-## AUTOHIDE OPTIONS
-#\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
-autohide = 0
-autohide_show_timeout = 0.3
-autohide_hide_timeout = 2
-autohide_height = 4
-strut_policy = minimum
-.fi
-.RE
+See /etc/xdg/tint2/tint2rc.
.SH AUTHOR
.PP
tint2 was written by Thierry Lorthiois \[la]lorthiois@bbsoft.fr\[ra]\&.
diff --git a/doc/tint2.md b/doc/tint2.md
index 75e73d1..10b061b 100644
--- a/doc/tint2.md
+++ b/doc/tint2.md
@@ -58,6 +58,8 @@ Goals:
* [Executor](#executor)
+ * [Button](#button)
+
* [Separator](#separator)
* [Example configuration](#example-configuration)
@@ -244,6 +246,7 @@ gradient_id_pressed = 2
* `C` shows the Clock
* `F` adds an extensible spacer (freespace). You can specify more than one. Has no effect if `T` is also present. *(since 0.12)*
* `E` adds an executor plugin. You can specify more than one. *(since 0.12.4)*
+ * `P` adds a push button. You can specify more than one. *(since 0.14)*
* `:` adds a separator. You can specify more than one. *(since 0.13.0)*
For example, `panel_items = STC` will show the systray, the taskbar and the clock (from left to right).
@@ -671,6 +674,32 @@ execp_continuous = 1
execp_interval = 1
```
+### Button
+
+ * `button = new` : Begins the configuration of a new button. Multiple such plugins are supported; just use multiple `P`s in `panel_items`. *(since 0.14)*
+
+ * `button_icon = text` : Name or path of icon (or empty). *(since 0.14)*
+
+ * `button_text = text` : Text to display (or empty). *(since 0.14)*
+
+ * `button_tooltip = text` : The tooltip (or empty). *(since 0.14)*
+
+ * `button_font = [FAMILY-LIST] [STYLE-OPTIONS] [SIZE]` : The font used to draw the text. *(since 0.14)*
+
+ * `button_font_color = color opacity` : The font color. *(since 0.14)*
+
+ * `button_background_id = integer` : Which background to use. *(since 0.14)*
+
+ * `button_centered = boolean (0 or 1)` : Whether to center the text. *(since 0.14)*
+
+ * `button_padding = horizontal_padding vertical_padding spacing_between_icon_and_text` *(since 0.14)*
+
+ * `button_lclick_command = text` : Command to execute on left click. If not defined, `execp_command` is executed immediately, unless it is currently running. *(since 0.14)*
+ * `button_mclick_command = text` : Command to execute on right click. If not defined, `execp_command` is executed immediately, unless it is currently running. *(since 0.14)*
+ * `button_rclick_command = text` : Command to execute on middle click. If not defined, `execp_command` is executed immediately, unless it is currently running. *(since 0.14)*
+ * `button_uwheel_command = text` : Command to execute on wheel scroll up. If not defined, `execp_command` is executed immediately, unless it is currently running. *(since 0.14)*
+ * `button_dwheel_command = text` : Command to execute on wheel scroll down. If not defined, `execp_command` is executed immediately, unless it is currently running. *(since 0.14)*
+
### Separator
* `separator = new` : Begins the configuration of a new separator. Multiple such plugins are supported; just use multiple `:`s in `panel_items`. *(since 0.13.0)*
@@ -687,142 +716,7 @@ execp_interval = 1
### Example configuration
-```
-#---------------------------------------------
-## TINT2 CONFIG FILE
-#---------------------------------------------
-
-#---------------------------------------------
-## BACKGROUND AND BORDER
-#---------------------------------------------
-rounded = 7
-border_width = 2
-background_color = #000000 60
-border_color = #ffffff 18
-
-rounded = 5
-border_width = 0
-background_color = #ffffff 40
-border_color = #ffffff 50
-
-rounded = 5
-border_width = 0
-background_color = #ffffff 18
-border_color = #ffffff 70
-
-#---------------------------------------------
-## PANEL
-#---------------------------------------------
-panel_monitor = all
-panel_position = bottom center
-panel_size = 94% 30
-panel_margin = 0 0
-panel_padding = 7 0
-font_shadow = 0
-panel_background_id = 1
-wm_menu = 0
-panel_dock = 0
-panel_layer = bottom
-
-#---------------------------------------------
-## TASKBAR
-#---------------------------------------------
-#taskbar_mode = multi_desktop
-taskbar_mode = single_desktop
-taskbar_padding = 2 3 2
-taskbar_background_id = 0
-#taskbar_active_background_id = 0
-
-#---------------------------------------------
-## TASKS
-#---------------------------------------------
-task_icon = 1
-task_text = 1
-task_maximum_size = 140 35
-task_centered = 1
-task_padding = 6 3
-task_font = sans 7
-task_font_color = #ffffff 70
-task_background_id = 3
-task_icon_asb = 100 0 0
-## replace STATUS by 'urgent', 'active' or 'iconified'
-#task_STATUS_background_id = 2
-#task_STATUS_font_color = #ffffff 85
-#task_STATUS_icon_asb = 100 0 0
-## example:
-task_active_background_id = 2
-task_active_font_color = #ffffff 85
-task_active_icon_asb = 100 0 0
-urgent_nb_of_blink = 8
-
-#---------------------------------------------
-## SYSTRAYBAR
-#---------------------------------------------
-systray = 1
-systray_padding = 0 4 5
-systray_background_id = 0
-systray_sort = left2right
-systray_icon_size = 0
-systray_icon_asb = 100 0 0
-
-#---------------------------------------------
-## CLOCK
-#---------------------------------------------
-time1_format = %H:%M
-time1_font = sans 8
-time2_format = %A %d %B
-time2_font = sans 6
-clock_font_color = #ffffff 76
-clock_padding = 1 0
-clock_background_id = 0
-#clock_lclick_command = xclock
-clock_rclick_command = orage
-#clock_tooltip = %A %d %B
-#time1_timezone = :US/Hawaii
-#time2_timezone = :Europe/Berlin
-#clock_tooltip_timezone = :/usr/share/zoneinfo/Europe/Paris
-
-#---------------------------------------------
-## BATTERY
-#---------------------------------------------
-battery = 0
-battery_hide = 98
-battery_low_status = 10
-battery_low_cmd = notify-send "battery low"
-bat1_font = sans 8
-bat2_font = sans 6
-battery_font_color = #ffffff 76
-battery_padding = 1 0
-battery_background_id = 0
-
-#---------------------------------------------
-## TOOLTIP
-#---------------------------------------------
-tooltip = 0
-tooltip_padding = 2 2
-tooltip_show_timeout = 0.7
-tooltip_hide_timeout = 0.3
-tooltip_background_id = 1
-tooltip_font_color = #OOOOOO 80
-tooltip_font = sans 10
-
-#---------------------------------------------
-## MOUSE ACTION ON TASK
-#---------------------------------------------
-mouse_middle = none
-mouse_right = close
-mouse_scroll_up = toggle
-mouse_scroll_down = iconify
-
-#---------------------------------------------
-## AUTOHIDE OPTIONS
-#---------------------------------------------
-autohide = 0
-autohide_show_timeout = 0.3
-autohide_hide_timeout = 2
-autohide_height = 4
-strut_policy = minimum
-```
+See /etc/xdg/tint2/tint2rc.
## AUTHOR
tint2 was written by Thierry Lorthiois .
diff --git a/src/button/button.c b/src/button/button.c
new file mode 100644
index 0000000..19c28a0
--- /dev/null
+++ b/src/button/button.c
@@ -0,0 +1,543 @@
+#include "button.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "window.h"
+#include "server.h"
+#include "panel.h"
+#include "timer.h"
+#include "common.h"
+
+char *button_get_tooltip(void *obj);
+void button_init_fonts();
+int button_compute_desired_size(void *obj);
+void button_dump_geometry(void *obj, int indent);
+
+void default_button()
+{
+}
+
+Button *create_button()
+{
+ Button *button = calloc(1, sizeof(Button));
+ button->backend = calloc(1, sizeof(ButtonBackend));
+ button->backend->centered = TRUE;
+ button->backend->font_color.alpha = 0.5;
+ return button;
+}
+
+gpointer create_button_frontend(gconstpointer arg, gpointer data)
+{
+ Button *button_backend = (Button *)arg;
+
+ Button *button_frontend = calloc(1, sizeof(Button));
+ button_frontend->backend = button_backend->backend;
+ button_backend->backend->instances = g_list_append(button_backend->backend->instances, button_frontend);
+ button_frontend->frontend = calloc(1, sizeof(ButtonFrontend));
+ return button_frontend;
+}
+
+void destroy_button(void *obj)
+{
+ Button *button = (Button *)obj;
+ if (button->frontend) {
+ // This is a frontend element
+ if (button->frontend->icon) {
+ imlib_context_set_image(button->frontend->icon);
+ imlib_free_image();
+ button->frontend->icon = NULL;
+ }
+ button->backend->instances = g_list_remove_all(button->backend->instances, button);
+ free_and_null(button->frontend);
+ remove_area(&button->area);
+ free_area(&button->area);
+ free_and_null(button);
+ } else {
+ // This is a backend element
+ free_and_null(button->backend->text);
+ free_and_null(button->backend->icon_name);
+ free_and_null(button->backend->tooltip);
+
+ button->backend->bg = NULL;
+ pango_font_description_free(button->backend->font_desc);
+ button->backend->font_desc = NULL;
+ free_and_null(button->backend->lclick_command);
+ free_and_null(button->backend->mclick_command);
+ free_and_null(button->backend->rclick_command);
+ free_and_null(button->backend->dwheel_command);
+ free_and_null(button->backend->uwheel_command);
+
+ if (button->backend->instances) {
+ fprintf(stderr, "Error: Attempt to destroy backend while there are still frontend instances!\n");
+ exit(-1);
+ }
+ free(button->backend);
+ free(button);
+ }
+}
+
+void init_button()
+{
+ GList *to_remove = panel_config.button_list;
+ for (int k = 0; k < strlen(panel_items_order) && to_remove; k++) {
+ if (panel_items_order[k] == 'E') {
+ to_remove = to_remove->next;
+ }
+ }
+
+ if (to_remove) {
+ if (to_remove == panel_config.button_list) {
+ g_list_free_full(to_remove, destroy_button);
+ panel_config.button_list = NULL;
+ } else {
+ // Cut panel_config.button_list
+ if (to_remove->prev)
+ to_remove->prev->next = NULL;
+ to_remove->prev = NULL;
+ // Remove all elements of to_remove and to_remove itself
+ g_list_free_full(to_remove, destroy_button);
+ }
+ }
+
+ button_init_fonts();
+ for (GList *l = panel_config.button_list; l; l = l->next) {
+ Button *button = l->data;
+
+ // Set missing config options
+ if (!button->backend->bg)
+ button->backend->bg = &g_array_index(backgrounds, Background, 0);
+ }
+}
+
+void init_button_panel(void *p)
+{
+ Panel *panel = (Panel *)p;
+
+ // Make sure this is only done once if there are multiple items
+ if (panel->button_list && ((Button *)panel->button_list->data)->frontend)
+ return;
+
+ // panel->button_list is now a copy of the pointer panel_config.button_list
+ // We make it a deep copy
+ panel->button_list = g_list_copy_deep(panel_config.button_list, create_button_frontend, NULL);
+
+ load_icon_themes();
+
+ for (GList *l = panel->button_list; l; l = l->next) {
+ Button *button = l->data;
+ button->area.bg = button->backend->bg;
+ button->area.paddingx = button->backend->paddingx;
+ button->area.paddingy = button->backend->paddingy;
+ button->area.paddingxlr = button->backend->paddingxlr;
+ button->area.parent = panel;
+ button->area.panel = panel;
+ button->area._dump_geometry = button_dump_geometry;
+ button->area._compute_desired_size = button_compute_desired_size;
+ snprintf(button->area.name, sizeof(button->area.name), "Button");
+ button->area._draw_foreground = draw_button;
+ button->area.size_mode = LAYOUT_FIXED;
+ button->area._resize = resize_button;
+ button->area._get_tooltip_text = button_get_tooltip;
+ button->area._is_under_mouse = full_width_area_is_under_mouse;
+ button->area.has_mouse_press_effect =
+ panel_config.mouse_effects &&
+ (button->area.has_mouse_over_effect = button->backend->lclick_command || button->backend->mclick_command ||
+ button->backend->rclick_command || button->backend->uwheel_command ||
+ button->backend->dwheel_command);
+
+ button->area.resize_needed = TRUE;
+ button->area.on_screen = TRUE;
+ instantiate_area_gradients(&button->area);
+
+ button_reload_icon(button);
+ }
+}
+
+void button_init_fonts()
+{
+ for (GList *l = panel_config.button_list; l; l = l->next) {
+ Button *button = l->data;
+ if (!button->backend->font_desc)
+ button->backend->font_desc = pango_font_description_from_string(get_default_font());
+ }
+}
+
+void button_default_font_changed()
+{
+ gboolean needs_update = FALSE;
+ for (GList *l = panel_config.button_list; l; l = l->next) {
+ Button *button = l->data;
+
+ if (!button->backend->has_font) {
+ pango_font_description_free(button->backend->font_desc);
+ button->backend->font_desc = NULL;
+ needs_update = TRUE;
+ }
+ }
+ if (!needs_update)
+ return;
+
+ button_init_fonts();
+ for (int i = 0; i < num_panels; i++) {
+ for (GList *l = panels[i].button_list; l; l = l->next) {
+ Button *button = l->data;
+
+ if (!button->backend->has_font) {
+ button->area.resize_needed = TRUE;
+ schedule_redraw(&button->area);
+ }
+ }
+ }
+ schedule_panel_redraw();
+}
+
+void button_reload_icon(Button *button)
+{
+ free_icon(button->frontend->icon);
+ free_icon(button->frontend->icon_hover);
+ free_icon(button->frontend->icon_pressed);
+ button->frontend->icon = NULL;
+
+ button->frontend->icon_load_size = button->frontend->iconw;
+
+ char *new_icon_path = get_icon_path(icon_theme_wrapper, button->backend->icon_name, button->frontend->iconw, TRUE);
+ if (new_icon_path)
+ button->frontend->icon = imlib_load_image_immediately(new_icon_path);
+ free(new_icon_path);
+ // On loading error, fallback to default
+ if (!button->frontend->icon) {
+ new_icon_path = get_icon_path(icon_theme_wrapper, DEFAULT_ICON, button->frontend->iconw, TRUE);
+ if (new_icon_path)
+ button->frontend->icon = imlib_load_image_immediately(new_icon_path);
+ free(new_icon_path);
+ }
+ Imlib_Image original = button->frontend->icon;
+ button->frontend->icon = scale_icon(button->frontend->icon, button->frontend->iconw);
+ free_icon(original);
+
+ if (panel_config.mouse_effects) {
+ button->frontend->icon_hover = adjust_icon(button->frontend->icon,
+ panel_config.mouse_over_alpha,
+ panel_config.mouse_over_saturation,
+ panel_config.mouse_over_brightness);
+ button->frontend->icon_pressed = adjust_icon(button->frontend->icon,
+ panel_config.mouse_pressed_alpha,
+ panel_config.mouse_pressed_saturation,
+ panel_config.mouse_pressed_brightness);
+ }
+ schedule_redraw(&button->area);
+}
+
+void button_default_icon_theme_changed()
+{
+ for (int i = 0; i < num_panels; i++) {
+ for (GList *l = panels[i].button_list; l; l = l->next) {
+ Button *button = l->data;
+ button_reload_icon(button);
+ }
+ }
+ schedule_panel_redraw();
+}
+
+void cleanup_button()
+{
+ // Cleanup frontends
+ for (int i = 0; i < num_panels; i++) {
+ g_list_free_full(panels[i].button_list, destroy_button);
+ panels[i].button_list = NULL;
+ }
+
+ // Cleanup backends
+ g_list_free_full(panel_config.button_list, destroy_button);
+ panel_config.button_list = NULL;
+}
+
+int button_compute_desired_size(void *obj)
+{
+ Button *button = (Button *)obj;
+ Panel *panel = (Panel *)button->area.panel;
+ int horiz_padding = (panel_horizontal ? button->area.paddingxlr : button->area.paddingy);
+ int vert_padding = (panel_horizontal ? button->area.paddingy : button->area.paddingxlr);
+ int interior_padding = button->area.paddingx;
+
+ int icon_w, icon_h;
+ if (button->backend->icon_name) {
+ if (panel_horizontal)
+ icon_h = icon_w = button->area.height - top_bottom_border_width(&button->area) - 2 * vert_padding;
+ else
+ icon_h = icon_w = button->area.width - left_right_border_width(&button->area) - 2 * horiz_padding;
+ } else {
+ icon_h = icon_w = 0;
+ }
+
+ int txt_height_ink, txt_height, txt_width;
+ if (button->backend->text) {
+ if (panel_horizontal) {
+ get_text_size2(button->backend->font_desc,
+ &txt_height_ink,
+ &txt_height,
+ &txt_width,
+ panel->area.height,
+ panel->area.width,
+ button->backend->text,
+ strlen(button->backend->text),
+ PANGO_WRAP_WORD_CHAR,
+ PANGO_ELLIPSIZE_NONE,
+ FALSE);
+ } else {
+ get_text_size2(button->backend->font_desc,
+ &txt_height_ink,
+ &txt_height,
+ &txt_width,
+ panel->area.height,
+ button->area.width - icon_w - (icon_w ? interior_padding : 0) -
+ 2 * horiz_padding - left_right_border_width(&button->area),
+ button->backend->text,
+ strlen(button->backend->text),
+ PANGO_WRAP_WORD_CHAR,
+ PANGO_ELLIPSIZE_NONE,
+ FALSE);
+ }
+ } else {
+ txt_height_ink = txt_height = txt_width = 0;
+ }
+
+ if (panel_horizontal) {
+ int new_size = txt_width + icon_w + (txt_width && icon_w ? interior_padding : 0);
+ new_size += 2 * horiz_padding + left_right_border_width(&button->area);
+ return new_size;
+ } else {
+ int new_size;
+ new_size = txt_height + 2 * vert_padding + top_bottom_border_width(&button->area);
+ new_size = MAX(new_size, icon_h + 2 * vert_padding + top_bottom_border_width(&button->area));
+ return new_size;
+ }
+}
+
+gboolean resize_button(void *obj)
+{
+ Button *button = (Button *)obj;
+ Panel *panel = (Panel *)button->area.panel;
+ int horiz_padding = (panel_horizontal ? button->area.paddingxlr : button->area.paddingy);
+ int vert_padding = (panel_horizontal ? button->area.paddingy : button->area.paddingxlr);
+ int interior_padding = button->area.paddingx;
+
+ int icon_w, icon_h;
+ if (button->backend->icon_name) {
+ if (panel_horizontal)
+ icon_h = icon_w = button->area.height - top_bottom_border_width(&button->area) - 2 * vert_padding;
+ else
+ icon_h = icon_w = button->area.width - left_right_border_width(&button->area) - 2 * horiz_padding;
+ } else {
+ icon_h = icon_w = 0;
+ }
+
+ button->frontend->iconw = icon_w;
+ button->frontend->iconh = icon_h;
+ if (button->frontend->icon_load_size != button->frontend->iconw)
+ button_reload_icon(button);
+
+ int txt_height_ink, txt_height, txt_width;
+ if (button->backend->text) {
+ if (panel_horizontal) {
+ get_text_size2(button->backend->font_desc,
+ &txt_height_ink,
+ &txt_height,
+ &txt_width,
+ panel->area.height,
+ panel->area.width,
+ button->backend->text,
+ strlen(button->backend->text),
+ PANGO_WRAP_WORD_CHAR,
+ PANGO_ELLIPSIZE_NONE,
+ FALSE);
+ } else {
+ get_text_size2(button->backend->font_desc,
+ &txt_height_ink,
+ &txt_height,
+ &txt_width,
+ panel->area.height,
+ button->area.width - icon_w - (icon_w ? interior_padding : 0) -
+ 2 * horiz_padding - left_right_border_width(&button->area),
+ button->backend->text,
+ strlen(button->backend->text),
+ PANGO_WRAP_WORD_CHAR,
+ PANGO_ELLIPSIZE_NONE,
+ FALSE);
+ }
+ } else {
+ txt_height_ink = txt_height = txt_width = 0;
+ }
+
+ gboolean result = FALSE;
+ if (panel_horizontal) {
+ int new_size = txt_width + icon_w + (txt_width && icon_w ? interior_padding : 0);
+ new_size += 2 * horiz_padding + left_right_border_width(&button->area);
+ if (new_size != button->area.width) {
+ button->area.width = new_size;
+ result = TRUE;
+ }
+ } else {
+ int new_size;
+ new_size = txt_height + 2 * vert_padding + top_bottom_border_width(&button->area);
+ new_size = MAX(new_size, icon_h + 2 * vert_padding + top_bottom_border_width(&button->area));
+ if (new_size != button->area.height) {
+ button->area.height = new_size;
+ result = TRUE;
+ }
+ }
+ button->frontend->textw = txt_width;
+ button->frontend->texth = txt_height;
+ if (button->backend->centered) {
+ if (icon_w) {
+ button->frontend->icony = (button->area.height - icon_h) / 2;
+ button->frontend->iconx = (button->area.width - txt_width - (txt_width ? interior_padding : 0) - icon_w) / 2;
+ button->frontend->texty = (button->area.height - txt_height) / 2;
+ button->frontend->textx = button->frontend->iconx + icon_w + interior_padding;
+ } else {
+ button->frontend->texty = (button->area.height - txt_height) / 2;
+ button->frontend->textx = (button->area.width - txt_width) / 2;
+ }
+ } else {
+ if (icon_w) {
+ button->frontend->icony = (button->area.height - icon_h) / 2;
+ button->frontend->iconx = left_border_width(&button->area) + horiz_padding;
+ button->frontend->texty = (button->area.height - txt_height) / 2;
+ button->frontend->textx = button->frontend->iconx + icon_w + interior_padding;
+ } else {
+ button->frontend->texty = (button->area.height - txt_height) / 2;
+ button->frontend->textx = left_border_width(&button->area) + horiz_padding;
+ }
+ }
+
+ schedule_redraw(&button->area);
+
+ return result;
+}
+
+void draw_button(void *obj, cairo_t *c)
+{
+ Button *button = obj;
+
+ if (button->frontend->icon) {
+ imlib_context_set_image(button->frontend->icon);
+ // Render icon
+ render_image(button->area.pix, button->frontend->iconx, button->frontend->icony);
+ }
+
+ // Render text
+ if (button->backend->text) {
+ PangoLayout *layout = pango_cairo_create_layout(c);
+
+ pango_layout_set_font_description(layout, button->backend->font_desc);
+ pango_layout_set_width(layout, button->frontend->textw * PANGO_SCALE);
+ pango_layout_set_alignment(layout, button->backend->centered ? PANGO_ALIGN_CENTER : PANGO_ALIGN_LEFT);
+ pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
+ pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE);
+ pango_layout_set_text(layout, button->backend->text, strlen(button->backend->text));
+
+ pango_cairo_update_layout(c, layout);
+ draw_text(layout,
+ c,
+ button->frontend->textx,
+ button->frontend->texty,
+ &button->backend->font_color,
+ panel_config.font_shadow);
+
+ g_object_unref(layout);
+ }
+}
+
+void button_dump_geometry(void *obj, int indent)
+{
+ Button *button = obj;
+
+ if (button->frontend->icon) {
+ Imlib_Image tmp = imlib_context_get_image();
+ imlib_context_set_image(button->frontend->icon);
+ fprintf(stderr,
+ "%*sIcon: x = %d, y = %d, w = %d, h = %d\n",
+ indent,
+ "",
+ button->frontend->iconx,
+ button->frontend->icony,
+ imlib_image_get_width(),
+ imlib_image_get_height());
+ if (tmp)
+ imlib_context_set_image(tmp);
+ }
+ fprintf(stderr,
+ "%*sText: x = %d, y = %d, w = %d, align = %s, text = %s\n",
+ indent,
+ "",
+ button->frontend->textx,
+ button->frontend->texty,
+ button->frontend->textw,
+ button->backend->centered ? "center" : "left",
+ button->backend->text);
+}
+
+void button_action(void *obj, int mouse_button, int x, int y)
+{
+ Button *button = obj;
+ char *command = NULL;
+ switch (mouse_button) {
+ case 1:
+ command = button->backend->lclick_command;
+ break;
+ case 2:
+ command = button->backend->mclick_command;
+ break;
+ case 3:
+ command = button->backend->rclick_command;
+ break;
+ case 4:
+ command = button->backend->uwheel_command;
+ break;
+ case 5:
+ command = button->backend->dwheel_command;
+ break;
+ }
+ if (command) {
+ char *full_cmd = g_strdup_printf("export BUTTON_X=%d;"
+ "export BUTTON_Y=%d;"
+ "export BUTTON_W=%d;"
+ "export BUTTON_H=%d; %s",
+ x,
+ y,
+ button->area.width,
+ button->area.height,
+ command);
+ pid_t pid = fork();
+ if (pid < 0) {
+ fprintf(stderr, "Could not fork\n");
+ } else if (pid == 0) {
+ // Child process
+ // Allow children to exist after parent destruction
+ setsid();
+ // Run the command
+ execl("/bin/sh", "/bin/sh", "-c", full_cmd, NULL);
+ fprintf(stderr, "Failed to execlp %s\n", full_cmd);
+ exit(1);
+ }
+ }
+}
+
+char *button_get_tooltip(void *obj)
+{
+ Button *button = obj;
+
+ if (button->backend->tooltip && strlen(button->backend->tooltip) > 0)
+ return strdup(button->backend->tooltip);
+ return NULL;
+}
diff --git a/src/button/button.h b/src/button/button.h
new file mode 100644
index 0000000..0e4e058
--- /dev/null
+++ b/src/button/button.h
@@ -0,0 +1,111 @@
+#ifndef BUTTON_H
+#define BUTTON_H
+
+#include
+#include
+
+#include "area.h"
+#include "common.h"
+#include "timer.h"
+
+// Architecture:
+// Panel panel_config contains an array of Button, each storing all config options and all the state variables.
+// Only these run commands.
+//
+// Tint2 maintains an array of Panels, one for each monitor. Each stores an array of Button which was initially copied
+// from panel_config. Each works as a frontend to the corresponding Button in panel_config as backend, using the
+// backend's config and state variables.
+
+typedef struct ButtonBackend {
+ // Config:
+ char *icon_name;
+ char *text;
+ char *tooltip;
+ gboolean centered;
+ gboolean has_font;
+ PangoFontDescription *font_desc;
+ Color font_color;
+ char *lclick_command;
+ char *mclick_command;
+ char *rclick_command;
+ char *uwheel_command;
+ char *dwheel_command;
+ // paddingxlr = horizontal padding left/right
+ // paddingx = horizontal padding between childs
+ int paddingxlr, paddingx, paddingy;
+ Background *bg;
+
+ // List of Button which are frontends for this backend, one for each panel
+ GList *instances;
+} ButtonBackend;
+
+typedef struct ButtonFrontend {
+ // Frontend state:
+ Imlib_Image icon;
+ Imlib_Image icon_hover;
+ Imlib_Image icon_pressed;
+ int icon_load_size;
+ int iconx;
+ int icony;
+ int iconw;
+ int iconh;
+ int textx;
+ int texty;
+ int textw;
+ int texth;
+} ButtonFrontend;
+
+typedef struct Button {
+ Area area;
+ // All elements have the backend pointer set. However only backend elements have ownership.
+ ButtonBackend *backend;
+ // Set only for frontend Button items.
+ ButtonFrontend *frontend;
+} Button;
+
+// Called before the config is read and panel_config/panels are created.
+// Afterwards, the config parsing code creates the array of Button in panel_config and populates the configuration fields
+// in the backend.
+// Probably does nothing.
+void default_button();
+
+// Creates a new Button item with only the backend field set. The state is NOT initialized. The config is initialized to
+// the default values.
+// This will be used by the config code to populate its backedn config fields.
+Button *create_button();
+
+void destroy_button(void *obj);
+
+// Called after the config is read and panel_config is populated, but before panels are created.
+// Initializes the state of the backend items.
+// panel_config.panel_items is used to determine which backend items are enabled. The others should be destroyed and
+// removed from panel_config.button_list.
+void init_button();
+
+// Called after each on-screen panel is created, with a pointer to the panel.
+// Initializes the state of the frontend items. Also adds a pointer to it in backend->instances.
+// At this point the Area has not been added yet to the GUI tree, but it will be added right away.
+void init_button_panel(void *panel);
+
+// Called just before the panels are destroyed. Afterwards, tint2 exits or restarts and reads the config again.
+// Releases all frontends and then all the backends.
+// The frontend items are not freed by this function, only their members. The items are Areas which are freed in the
+// GUI element tree cleanup function (remove_area).
+void cleanup_button();
+
+// Called on draw, obj = pointer to the front-end Button item.
+void draw_button(void *obj, cairo_t *c);
+
+// Called on resize, obj = pointer to the front-end Button item.
+// Returns 1 if the new size is different than the previous size.
+gboolean resize_button(void *obj);
+
+// Called on mouse click event.
+void button_action(void *obj, int button, int x, int y);
+
+void button_default_font_changed();
+void button_default_icon_theme_changed();
+
+void button_reload_icon(Button *button);
+
+#endif // BUTTON_H
diff --git a/src/config.c b/src/config.c
index 65799c0..c293119 100644
--- a/src/config.c
+++ b/src/config.c
@@ -221,6 +221,15 @@ Execp *get_or_create_last_execp()
return (Execp *)g_list_last(panel_config.execp_list)->data;
}
+Button *get_or_create_last_button()
+{
+ if (!panel_config.button_list) {
+ fprintf(stderr, "Warning: button items should start with 'button = new'\n");
+ panel_config.button_list = g_list_append(panel_config.button_list, create_button());
+ }
+ return (Button *)g_list_last(panel_config.button_list)->data;
+}
+
void add_entry(char *key, char *value)
{
char *value1 = 0, *value2 = 0, *value3 = 0;
@@ -738,6 +747,78 @@ void add_entry(char *key, char *value)
execp->backend->dwheel_command = strdup(value);
}
+ /* Button */
+ else if (strcmp(key, "button") == 0) {
+ panel_config.button_list = g_list_append(panel_config.button_list, create_button());
+ } else if (strcmp(key, "button_icon") == 0) {
+ Button *button = get_or_create_last_button();
+ button->backend->icon_name = strdup(value);
+ } else if (strcmp(key, "button_text") == 0) {
+ Button *button = get_or_create_last_button();
+ free_and_null(button->backend->text);
+ button->backend->text = strdup(value);
+ } else if (strcmp(key, "button_tooltip") == 0) {
+ Button *button = get_or_create_last_button();
+ free_and_null(button->backend->tooltip);
+ button->backend->tooltip = strdup(value);
+ } else if (strcmp(key, "button_font") == 0) {
+ Button *button = get_or_create_last_button();
+ pango_font_description_free(button->backend->font_desc);
+ button->backend->font_desc = pango_font_description_from_string(value);
+ button->backend->has_font = TRUE;
+ } else if (strcmp(key, "button_font_color") == 0) {
+ Button *button = get_or_create_last_button();
+ extract_values(value, &value1, &value2, &value3);
+ get_color(value1, button->backend->font_color.rgb);
+ if (value2)
+ button->backend->font_color.alpha = atoi(value2) / 100.0;
+ else
+ button->backend->font_color.alpha = 0.5;
+ } else if (strcmp(key, "button_padding") == 0) {
+ Button *button = get_or_create_last_button();
+ extract_values(value, &value1, &value2, &value3);
+ button->backend->paddingxlr = button->backend->paddingx = atoi(value1);
+ if (value2)
+ button->backend->paddingy = atoi(value2);
+ else
+ button->backend->paddingy = 0;
+ if (value3)
+ button->backend->paddingx = atoi(value3);
+ } else if (strcmp(key, "button_background_id") == 0) {
+ Button *button = get_or_create_last_button();
+ int id = atoi(value);
+ id = (id < backgrounds->len && id >= 0) ? id : 0;
+ button->backend->bg = &g_array_index(backgrounds, Background, id);
+ } else if (strcmp(key, "button_centered") == 0) {
+ Button *button = get_or_create_last_button();
+ button->backend->centered = atoi(value);
+ } else if (strcmp(key, "button_lclick_command") == 0) {
+ Button *button = get_or_create_last_button();
+ free_and_null(button->backend->lclick_command);
+ if (strlen(value) > 0)
+ button->backend->lclick_command = strdup(value);
+ } else if (strcmp(key, "button_mclick_command") == 0) {
+ Button *button = get_or_create_last_button();
+ free_and_null(button->backend->mclick_command);
+ if (strlen(value) > 0)
+ button->backend->mclick_command = strdup(value);
+ } else if (strcmp(key, "button_rclick_command") == 0) {
+ Button *button = get_or_create_last_button();
+ free_and_null(button->backend->rclick_command);
+ if (strlen(value) > 0)
+ button->backend->rclick_command = strdup(value);
+ } else if (strcmp(key, "button_uwheel_command") == 0) {
+ Button *button = get_or_create_last_button();
+ free_and_null(button->backend->uwheel_command);
+ if (strlen(value) > 0)
+ button->backend->uwheel_command = strdup(value);
+ } else if (strcmp(key, "button_dwheel_command") == 0) {
+ Button *button = get_or_create_last_button();
+ free_and_null(button->backend->dwheel_command);
+ if (strlen(value) > 0)
+ button->backend->dwheel_command = strdup(value);
+ }
+
/* Clock */
else if (strcmp(key, "time1_format") == 0) {
if (!new_config_file) {
diff --git a/src/launcher/launcher.c b/src/launcher/launcher.c
index 8d6c23e..46c22d0 100644
--- a/src/launcher/launcher.c
+++ b/src/launcher/launcher.c
@@ -54,6 +54,8 @@ int startup_notifications;
Background *launcher_icon_bg;
GList *launcher_icon_gradients;
+IconThemeWrapper *icon_theme_wrapper;
+
Imlib_Image scale_icon(Imlib_Image original, int icon_size);
void free_icon(Imlib_Image icon);
void launcher_icon_dump_geometry(void *obj, int indent);
@@ -114,10 +116,16 @@ void init_launcher_panel(void *p)
schedule_panel_redraw();
instantiate_area_gradients(&launcher->area);
- launcher_load_themes(launcher);
+ load_icon_themes();
launcher_load_icons(launcher);
}
+void free_icon_themes()
+{
+ free_themes(icon_theme_wrapper);
+ icon_theme_wrapper = NULL;
+}
+
void cleanup_launcher()
{
for (int i = 0; i < num_panels; i++) {
@@ -160,9 +168,6 @@ void cleanup_launcher_theme(Launcher *launcher)
}
g_slist_free(launcher->list_icons);
launcher->list_icons = NULL;
-
- free_themes(launcher->icon_theme_wrapper);
- launcher->icon_theme_wrapper = NULL;
}
int launcher_compute_icon_size(Launcher *launcher)
@@ -246,7 +251,7 @@ gboolean resize_launcher(void *obj)
launcher_reload_icon_image(launcher, launcherIcon);
}
}
- save_icon_cache(launcher->icon_theme_wrapper);
+ save_icon_cache(icon_theme_wrapper);
int count = 0;
gboolean needs_repositioning = FALSE;
@@ -565,13 +570,13 @@ void launcher_reload_icon_image(Launcher *launcher, LauncherIcon *launcherIcon)
free_icon(launcherIcon->image_pressed);
launcherIcon->image = NULL;
- char *new_icon_path = get_icon_path(launcher->icon_theme_wrapper, launcherIcon->icon_name, launcherIcon->icon_size, TRUE);
+ char *new_icon_path = get_icon_path(icon_theme_wrapper, launcherIcon->icon_name, launcherIcon->icon_size, TRUE);
if (new_icon_path)
launcherIcon->image = load_image(new_icon_path, 1);
// On loading error, fallback to default
if (!launcherIcon->image) {
free(new_icon_path);
- new_icon_path = get_icon_path(launcher->icon_theme_wrapper, DEFAULT_ICON, launcherIcon->icon_size, TRUE);
+ new_icon_path = get_icon_path(icon_theme_wrapper, DEFAULT_ICON, launcherIcon->icon_size, TRUE);
if (new_icon_path)
launcherIcon->image = imlib_load_image_immediately(new_icon_path);
}
@@ -595,10 +600,11 @@ void launcher_reload_icon_image(Launcher *launcher, LauncherIcon *launcherIcon)
schedule_redraw(&launcherIcon->area);
}
-// Populates the icon_theme_wrapper list
-void launcher_load_themes(Launcher *launcher)
+void load_icon_themes()
{
- launcher->icon_theme_wrapper =
+ if (icon_theme_wrapper)
+ return;
+ icon_theme_wrapper =
load_themes(launcher_icon_theme_override
? (icon_theme_name_config ? icon_theme_name_config
: icon_theme_name_xsettings ? icon_theme_name_xsettings : "hicolor")
@@ -608,14 +614,9 @@ void launcher_load_themes(Launcher *launcher)
void launcher_default_icon_theme_changed()
{
- if (!launcher_enabled)
- return;
- if (launcher_icon_theme_override && icon_theme_name_config)
- return;
for (int i = 0; i < num_panels; i++) {
Launcher *launcher = &panels[i].launcher;
cleanup_launcher_theme(launcher);
- launcher_load_themes(launcher);
launcher_load_icons(launcher);
launcher->area.resize_needed = 1;
}
diff --git a/src/launcher/launcher.h b/src/launcher/launcher.h
index a1f3b99..0ac9f88 100644
--- a/src/launcher/launcher.h
+++ b/src/launcher/launcher.h
@@ -12,12 +12,15 @@
#include "xsettings-client.h"
#include "icon-theme-common.h"
+extern IconThemeWrapper *icon_theme_wrapper;
+void load_icon_themes();
+void free_icon_themes();
+
typedef struct Launcher {
// always start with area
Area area;
GSList *list_apps; // List of char*, each is a path to a app.desktop file
GSList *list_icons; // List of LauncherIcon*
- IconThemeWrapper *icon_theme_wrapper;
int icon_size;
} Launcher;
@@ -65,8 +68,6 @@ void launcher_default_icon_theme_changed();
// Populates the list_icons list
void launcher_load_icons(Launcher *launcher);
-// Populates the list_themes list
-void launcher_load_themes(Launcher *launcher);
void launcher_action(LauncherIcon *icon, XEvent *e);
void test_launcher_read_desktop_file();
diff --git a/src/panel.c b/src/panel.c
index 9b037b0..9170a87 100644
--- a/src/panel.c
+++ b/src/panel.c
@@ -180,6 +180,8 @@ void init_panel()
fprintf(stderr, "panel items: %s\n", panel_items_order);
+ icon_theme_wrapper = NULL;
+
init_tooltip();
init_systray();
init_launcher();
@@ -190,6 +192,7 @@ void init_panel()
init_taskbar();
init_separator();
init_execp();
+ init_button();
// number of panels (one monitor or 'all' monitors)
if (panel_config.monitor >= 0)
@@ -247,6 +250,8 @@ void init_panel()
init_separator_panel(p);
if (panel_items_order[k] == 'E')
init_execp_panel(p);
+ if (panel_items_order[k] == 'P')
+ init_button_panel(p);
}
set_panel_items_order(p);
@@ -603,6 +608,7 @@ void set_panel_items_order(Panel *p)
int i_execp = 0;
int i_separator = 0;
int i_freespace = 0;
+ int i_button = 0;
for (int k = 0; k < strlen(panel_items_order); k++) {
if (panel_items_order[k] == 'L') {
p->area.children = g_list_append(p->area.children, &p->launcher);
@@ -640,6 +646,12 @@ void set_panel_items_order(Panel *p)
if (item)
p->area.children = g_list_append(p->area.children, (Area *)item->data);
}
+ if (panel_items_order[k] == 'P') {
+ GList *item = g_list_nth(p->button_list, i_button);
+ i_button++;
+ if (item)
+ p->area.children = g_list_append(p->area.children, (Area *)item->data);
+ }
}
initialize_positions(&p->area, 0);
}
@@ -986,6 +998,16 @@ Execp *click_execp(Panel *panel, int x, int y)
return NULL;
}
+Button *click_button(Panel *panel, int x, int y)
+{
+ for (GList *l = panel->button_list; l; l = l->next) {
+ Button *button = (Button *)l->data;
+ if (area_is_under_mouse(button, x, y))
+ return button;
+ }
+ return NULL;
+}
+
void stop_autohide_timeout(Panel *p)
{
stop_timeout(p->autohide_timeout);
@@ -1082,7 +1104,16 @@ const char *get_default_font()
void default_icon_theme_changed()
{
+ if (!launcher_enabled && !panel_config.button_list)
+ return;
+ if (launcher_icon_theme_override && icon_theme_name_config)
+ return;
+
+ free_icon_themes();
+ load_icon_themes();
+
launcher_default_icon_theme_changed();
+ button_default_icon_theme_changed();
}
void default_font_changed()
@@ -1092,6 +1123,7 @@ void default_font_changed()
#endif
clock_default_font_changed();
execp_default_font_changed();
+ button_default_font_changed();
taskbar_default_font_changed();
taskbarname_default_font_changed();
tooltip_default_font_changed();
diff --git a/src/panel.h b/src/panel.h
index f5249a3..8e56716 100644
--- a/src/panel.h
+++ b/src/panel.h
@@ -23,6 +23,7 @@
#include "freespace.h"
#include "execplugin.h"
#include "separator.h"
+#include "button.h"
#ifdef ENABLE_BATTERY
#include "battery.h"
@@ -135,6 +136,7 @@ typedef struct Panel {
GList *freespace_list;
GList *separator_list;
GList *execp_list;
+ GList *button_list;
// Autohide
gboolean is_hidden;
@@ -189,6 +191,7 @@ Battery *click_battery(Panel *panel, int x, int y);
Area *click_area(Panel *panel, int x, int y);
Execp *click_execp(Panel *panel, int x, int y);
+Button *click_button(Panel *panel, int x, int y);
void autohide_show(void *p);
void autohide_hide(void *p);
@@ -200,4 +203,7 @@ const char *get_default_font();
void default_icon_theme_changed();
void default_font_changed();
+void free_icon(Imlib_Image icon);
+Imlib_Image scale_icon(Imlib_Image original, int icon_size);
+
#endif
diff --git a/src/tint.c b/src/tint.c
index cc26e0f..00ac012 100644
--- a/src/tint.c
+++ b/src/tint.c
@@ -393,6 +393,7 @@ void init(int argc, char *argv[])
default_taskbar();
default_tooltip();
default_execp();
+ default_button();
default_panel();
// Read command line arguments
@@ -612,6 +613,7 @@ void init_X11_post_config()
void cleanup()
{
+ cleanup_button();
cleanup_execp();
cleanup_systray();
cleanup_tooltip();
@@ -812,6 +814,8 @@ int tint2_handles_click(Panel *panel, XButtonEvent *e)
#endif
if (click_execp(panel, e->x, e->y))
return 1;
+ if (click_button(panel, e->x, e->y))
+ return 1;
return 0;
}
@@ -975,6 +979,15 @@ void event_button_release(XEvent *e)
return;
}
+ Button *button = click_button(panel, e->xbutton.x, e->xbutton.y);
+ if (button) {
+ button_action(button, e->xbutton.button, e->xbutton.x - button->area.posx, e->xbutton.y - button->area.posy);
+ if (panel_layer == BOTTOM_LAYER)
+ XLowerWindow(server.display, panel->main_win);
+ task_drag = 0;
+ return;
+ }
+
if (e->xbutton.button == 1 && click_launcher(panel, e->xbutton.x, e->xbutton.y)) {
LauncherIcon *icon = click_launcher_icon(panel, e->xbutton.x, e->xbutton.y);
if (icon) {
diff --git a/tint2.files b/tint2.files
index 8abbb15..5d950a1 100644
--- a/tint2.files
+++ b/tint2.files
@@ -223,3 +223,5 @@ src/tint2conf/md4.h
src/tint2conf/md4.c
src/tint2rc.c
src/tint2rc.h
+src/button/button.c
+src/button/button.h
diff --git a/tint2.includes b/tint2.includes
index 102427b..b1821df 100644
--- a/tint2.includes
+++ b/tint2.includes
@@ -24,3 +24,4 @@ src/execplugin
src/separator
themes
doc
+src/button