1 /**************************************************************************
4 * Copyright (C) 2010 (mrovi@interfete-web-club.com)
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License version 2
8 * as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 **************************************************************************/
22 #include <cairo-xlib.h>
23 #include <pango/pangocairo.h>
27 #include <glib/gi18n.h>
41 int launcher_max_icon_size
;
42 int launcher_tooltip_enabled
;
44 int launcher_saturation
;
45 int launcher_brightness
;
46 char *icon_theme_name
;
47 XSettingsClient
*xsettings_client
;
49 #define ICON_FALLBACK "application-x-executable"
51 char *icon_path(Launcher
*launcher
, const char *icon_name
, int size
);
52 void launcher_load_themes(Launcher
*launcher
);
53 void free_desktop_entry(DesktopEntry
*entry
);
54 int launcher_read_desktop_file(const char *path
, DesktopEntry
*entry
);
55 Imlib_Image
scale_icon(Imlib_Image original
, int icon_size
);
56 void free_icon(Imlib_Image icon
);
57 void free_icon_theme(IconTheme
*theme
);
60 void default_launcher()
63 launcher_max_icon_size
= 0;
64 launcher_tooltip_enabled
= 0;
66 launcher_saturation
= 0;
67 launcher_brightness
= 0;
69 xsettings_client
= NULL
;
75 if (launcher_enabled
) {
76 // if XSETTINGS manager running, tint2 read the icon_theme_name.
77 xsettings_client
= xsettings_client_new(server
.dsp
, server
.screen
, xsettings_notify_cb
, NULL
, NULL
);
82 void init_launcher_panel(void *p
)
84 Panel
*panel
=(Panel
*)p
;
85 Launcher
*launcher
= &panel
->launcher
;
87 launcher
->area
.parent
= p
;
88 launcher
->area
.panel
= p
;
89 launcher
->area
._draw_foreground
= NULL
;
90 launcher
->area
.size_mode
= SIZE_BY_CONTENT
;
91 launcher
->area
._resize
= resize_launcher
;
92 launcher
->area
.resize
= 1;
93 launcher
->area
.redraw
= 1;
94 if (launcher
->area
.bg
== 0)
95 launcher
->area
.bg
= &g_array_index(backgrounds
, Background
, 0);
98 if (launcher
->list_apps
== NULL
)
101 launcher
->area
.on_screen
= 1;
104 launcher_load_themes(launcher
);
105 launcher_load_icons(launcher
);
109 void cleanup_launcher()
114 if (xsettings_client
)
115 xsettings_client_destroy(xsettings_client
);
116 for (i
= 0 ; i
< nb_panel
; i
++) {
117 Panel
*panel
= &panel1
[i
];
118 Launcher
*launcher
= &panel
->launcher
;
119 cleanup_launcher_theme(launcher
);
121 for (l
= panel_config
.launcher
.list_apps
; l
; l
= l
->next
) {
124 g_slist_free(panel_config
.launcher
.list_apps
);
125 panel_config
.launcher
.list_apps
= NULL
;
126 free(icon_theme_name
);
128 launcher_enabled
= 0;
132 void cleanup_launcher_theme(Launcher
*launcher
)
134 free_area(&launcher
->area
);
136 for (l
= launcher
->list_icons
; l
; l
= l
->next
) {
137 LauncherIcon
*launcherIcon
= (LauncherIcon
*)l
->data
;
139 free_icon(launcherIcon
->icon_scaled
);
140 free_icon(launcherIcon
->icon_original
);
141 free(launcherIcon
->icon_name
);
142 free(launcherIcon
->icon_path
);
143 free(launcherIcon
->cmd
);
144 free(launcherIcon
->icon_tooltip
);
148 g_slist_free(launcher
->list_icons
);
150 for (l
= launcher
->list_themes
; l
; l
= l
->next
) {
151 IconTheme
*theme
= (IconTheme
*) l
->data
;
152 free_icon_theme(theme
);
155 g_slist_free(launcher
->list_themes
);
156 launcher
->list_icons
= launcher
->list_themes
= NULL
;
160 int resize_launcher(void *obj
)
162 Launcher
*launcher
= obj
;
164 int count
, icon_size
;
165 int icons_per_column
=1, icons_per_row
=1, marging
=0;
167 if (panel_horizontal
)
168 icon_size
= launcher
->area
.height
;
170 icon_size
= launcher
->area
.width
;
171 icon_size
= icon_size
- (2 * launcher
->area
.bg
->border
.width
) - (2 * launcher
->area
.paddingy
);
172 if (launcher_max_icon_size
> 0 && icon_size
> launcher_max_icon_size
)
173 icon_size
= launcher_max_icon_size
;
175 // Resize icons if necessary
176 for (l
= launcher
->list_icons
; l
; l
= l
->next
) {
177 LauncherIcon
*launcherIcon
= (LauncherIcon
*)l
->data
;
178 if (launcherIcon
->icon_size
!= icon_size
|| !launcherIcon
->icon_original
) {
179 launcherIcon
->icon_size
= icon_size
;
180 launcherIcon
->area
.width
= launcherIcon
->icon_size
;
181 launcherIcon
->area
.height
= launcherIcon
->icon_size
;
183 // Get the path for an icon file with the new size
184 char *new_icon_path
= icon_path(launcher
, launcherIcon
->icon_name
, launcherIcon
->icon_size
);
185 if (!new_icon_path
) {
187 free_icon(launcherIcon
->icon_original
);
188 launcherIcon
->icon_original
= NULL
;
189 free_icon(launcherIcon
->icon_scaled
);
190 launcherIcon
->icon_scaled
= NULL
;
191 new_icon_path
= icon_path(launcher
, ICON_FALLBACK
, launcherIcon
->icon_size
);
193 launcherIcon
->icon_original
= imlib_load_image(new_icon_path
);
194 fprintf(stderr
, "launcher.c %d: Using icon %s\n", __LINE__
, new_icon_path
);
197 launcherIcon
->icon_scaled
= scale_icon(launcherIcon
->icon_original
, icon_size
);
200 if (launcherIcon
->icon_path
&& strcmp(new_icon_path
, launcherIcon
->icon_path
) == 0) {
201 // If it's the same file just rescale
202 free_icon(launcherIcon
->icon_scaled
);
203 launcherIcon
->icon_scaled
= scale_icon(launcherIcon
->icon_original
, icon_size
);
205 fprintf(stderr
, "launcher.c %d: Using icon %s\n", __LINE__
, launcherIcon
->icon_path
);
207 // Free the old files
208 free_icon(launcherIcon
->icon_original
);
209 free_icon(launcherIcon
->icon_scaled
);
210 // Load the new file and scale
211 launcherIcon
->icon_original
= imlib_load_image(new_icon_path
);
212 launcherIcon
->icon_scaled
= scale_icon(launcherIcon
->icon_original
, launcherIcon
->icon_size
);
213 free(launcherIcon
->icon_path
);
214 launcherIcon
->icon_path
= new_icon_path
;
215 fprintf(stderr
, "launcher.c %d: Using icon %s\n", __LINE__
, launcherIcon
->icon_path
);
220 count
= g_slist_length(launcher
->list_icons
);
222 if (panel_horizontal
) {
223 if (!count
) launcher
->area
.width
= 0;
225 int height
= launcher
->area
.height
- 2*launcher
->area
.bg
->border
.width
- 2*launcher
->area
.paddingy
;
226 // here icons_per_column always higher than 0
227 icons_per_column
= (height
+launcher
->area
.paddingx
) / (icon_size
+launcher
->area
.paddingx
);
228 marging
= height
- (icons_per_column
-1)*(icon_size
+launcher
->area
.paddingx
) - icon_size
;
229 icons_per_row
= count
/ icons_per_column
+ (count%icons_per_column
!= 0);
230 launcher
->area
.width
= (2 * launcher
->area
.bg
->border
.width
) + (2 * launcher
->area
.paddingxlr
) + (icon_size
* icons_per_row
) + ((icons_per_row
-1) * launcher
->area
.paddingx
);
234 if (!count
) launcher
->area
.height
= 0;
236 int width
= launcher
->area
.width
- 2*launcher
->area
.bg
->border
.width
- 2*launcher
->area
.paddingy
;
237 // here icons_per_row always higher than 0
238 icons_per_row
= (width
+launcher
->area
.paddingx
) / (icon_size
+launcher
->area
.paddingx
);
239 marging
= width
- (icons_per_row
-1)*(icon_size
+launcher
->area
.paddingx
) - icon_size
;
240 icons_per_column
= count
/ icons_per_row
+ (count%icons_per_row
!= 0);
241 launcher
->area
.height
= (2 * launcher
->area
.bg
->border
.width
) + (2 * launcher
->area
.paddingxlr
) + (icon_size
* icons_per_column
) + ((icons_per_column
-1) * launcher
->area
.paddingx
);
246 int start
= launcher
->area
.bg
->border
.width
+ launcher
->area
.paddingy
+ marging
/2;
247 if (panel_horizontal
) {
249 posx
= launcher
->area
.bg
->border
.width
+ launcher
->area
.paddingxlr
;
253 posy
= launcher
->area
.bg
->border
.width
+ launcher
->area
.paddingxlr
;
256 for (i
=1, l
= launcher
->list_icons
; l
; i
++, l
= l
->next
) {
257 LauncherIcon
*launcherIcon
= (LauncherIcon
*)l
->data
;
259 launcherIcon
->y
= posy
;
260 launcherIcon
->x
= posx
;
261 //printf("launcher %d : %d,%d\n", i, posx, posy);
262 if (panel_horizontal
) {
263 if (i
% icons_per_column
)
264 posy
+= icon_size
+ launcher
->area
.paddingx
;
267 posx
+= (icon_size
+ launcher
->area
.paddingx
);
271 if (i
% icons_per_row
)
272 posx
+= icon_size
+ launcher
->area
.paddingx
;
275 posy
+= (icon_size
+ launcher
->area
.paddingx
);
282 // Here we override the default layout of the icons; normally Area layouts its children
283 // in a stack; we need to layout them in a kind of table
284 void launcher_icon_on_change_layout(void *obj
)
286 LauncherIcon
*launcherIcon
= (LauncherIcon
*)obj
;
287 launcherIcon
->area
.posy
= ((Area
*)launcherIcon
->area
.parent
)->posy
+ launcherIcon
->y
;
288 launcherIcon
->area
.posx
= ((Area
*)launcherIcon
->area
.parent
)->posx
+ launcherIcon
->x
;
291 const char* launcher_icon_get_tooltip_text(void *obj
)
293 LauncherIcon
*launcherIcon
= (LauncherIcon
*)obj
;
294 return launcherIcon
->icon_tooltip
;
297 void draw_launcher_icon(void *obj
, cairo_t
*c
)
299 LauncherIcon
*launcherIcon
= (LauncherIcon
*)obj
;
300 Imlib_Image icon_scaled
= launcherIcon
->icon_scaled
;
302 imlib_context_set_image (icon_scaled
);
303 if (server
.real_transparency
) {
304 render_image(launcherIcon
->area
.pix
, 0, 0, imlib_image_get_width(), imlib_image_get_height() );
306 imlib_context_set_drawable(launcherIcon
->area
.pix
);
307 imlib_render_image_on_drawable (0, 0);
311 Imlib_Image
scale_icon(Imlib_Image original
, int icon_size
)
313 Imlib_Image icon_scaled
;
315 imlib_context_set_image (original
);
316 icon_scaled
= imlib_create_cropped_scaled_image(0, 0, imlib_image_get_width(), imlib_image_get_height(), icon_size
, icon_size
);
317 imlib_context_set_image (icon_scaled
);
318 imlib_image_set_has_alpha(1);
319 DATA32
* data
= imlib_image_get_data();
320 adjust_asb(data
, icon_size
, icon_size
, launcher_alpha
, (float)launcher_saturation
/100, (float)launcher_brightness
/100);
321 imlib_image_put_back_data(data
);
323 icon_scaled
= imlib_create_image(icon_size
, icon_size
);
324 imlib_context_set_image (icon_scaled
);
325 imlib_context_set_color(255, 255, 255, 255);
326 imlib_image_fill_rectangle(0, 0, icon_size
, icon_size
);
331 void free_icon(Imlib_Image icon
)
334 imlib_context_set_image(icon
);
339 void launcher_action(LauncherIcon
*icon
, XEvent
* evt
)
341 char *cmd
= malloc(strlen(icon
->cmd
) + 10);
342 sprintf(cmd
, "(%s&)", icon
->cmd
);
344 SnLauncherContext
* ctx
;
347 ctx
= sn_launcher_context_new(server
.sn_dsp
, server
.screen
);
348 sn_launcher_context_set_name(ctx
, icon
->icon_tooltip
);
349 sn_launcher_context_set_description(ctx
, "Application launched from tint2");
350 sn_launcher_context_set_binary_name (ctx
, icon
->cmd
);
351 // Get a timestamp from the X event
352 if (evt
->type
== ButtonPress
|| evt
->type
== ButtonRelease
) {
353 time
= evt
->xbutton
.time
;
356 fprintf(stderr
, "Unknown X event: %d\n", evt
->type
);
360 sn_launcher_context_initiate(ctx
, "tint2", icon
->cmd
, time
);
365 fprintf(stderr
, "Could not fork\n");
369 sn_launcher_context_setup_child_process (ctx
);
371 // Allow children to exist after parent destruction
374 execl("/bin/sh", "/bin/sh", "-c", icon
->cmd
, NULL
);
376 fprintf(stderr
, "Failed to execlp %s\n", icon
->cmd
);
378 sn_launcher_context_unref (ctx
);
384 g_tree_insert (server
.pids
, GINT_TO_POINTER (pid
), ctx
);
390 /***************** Freedesktop app.desktop and icon theme handling *********************/
391 /* http://standards.freedesktop.org/desktop-entry-spec/ */
392 /* http://standards.freedesktop.org/icon-theme-spec/ */
394 // Splits line at first '=' and returns 1 if successful, and parts are not empty
395 // key and value point to the parts
396 int parse_dektop_line(char *line
, char **key
, char **value
)
401 for (p
= line
; *p
; p
++) {
411 if (found
&& (strlen(*key
) == 0 || strlen(*value
) == 0))
416 int parse_theme_line(char *line
, char **key
, char **value
)
418 return parse_dektop_line(line
, key
, value
);
421 void expand_exec(DesktopEntry
*entry
, const char *path
)
428 char *exec2
= malloc(strlen(entry
->exec
) + (entry
->name
? strlen(entry
->name
) : 1) + (entry
->icon
? strlen(entry
->icon
) : 1) + 100);
430 // p will never point to an escaped char
431 for (p
= entry
->exec
, q
= exec2
; *p
; p
++, q
++) {
435 // Copy the escaped char
436 if (*p
== '%') // For % we delete the backslash, i.e. write % over it
445 if (*p
== 'i' && entry
->icon
!= NULL
) {
446 sprintf(q
, "--icon '%s'", entry
->icon
);
447 q
+= strlen("--icon ''");
448 q
+= strlen(entry
->icon
);
449 q
--; // To balance the q++ in the for
450 } else if (*p
== 'c' && entry
->name
!= NULL
) {
451 sprintf(q
, "'%s'", entry
->name
);
453 q
+= strlen(entry
->name
);
454 q
--; // To balance the q++ in the for
455 } else if (*p
== 'c') {
456 sprintf(q
, "'%s'", path
);
459 q
--; // To balance the q++ in the for
461 // We don't care about other expansions
462 q
--; // Delete the last % from q
473 int launcher_read_desktop_file(const char *path
, DesktopEntry
*entry
)
481 entry
->name
= entry
->icon
= entry
->exec
= NULL
;
483 if ((fp
= fopen(path
, "rt")) == NULL
) {
484 fprintf(stderr
, "Could not open file %s\n", path
);
488 gchar
**languages
= (gchar
**)g_get_language_names();
489 // lang_index is the index of the language for the best Name key in the language vector
490 // lang_index_default is a constant that encodes the Name key without a language
491 int lang_index
, lang_index_default
;
493 if (LANG_DBG
) printf("Languages:");
494 for (i
= 0; languages
[i
]; i
++) {
495 if (LANG_DBG
) printf(" %s", languages
[i
]);
497 if (LANG_DBG
) printf("\n");
498 lang_index_default
= i
;
499 // we currently do not know about any Name key at all, so use an invalid index
500 lang_index
= lang_index_default
+ 1;
502 int inside_desktop_entry
= 0;
503 while (getline(&line
, &line_size
, fp
) >= 0) {
504 int len
= strlen(line
);
507 line
[len
- 1] = '\0';
508 if (line
[0] == '[') {
509 inside_desktop_entry
= (strcmp(line
, "[Desktop Entry]") == 0);
511 if (inside_desktop_entry
&& parse_dektop_line(line
, &key
, &value
)) {
512 if (strstr(key
, "Name") == key
) {
513 if (strcmp(key
, "Name") == 0 && lang_index
> lang_index_default
) {
514 entry
->name
= strdup(value
);
515 lang_index
= lang_index_default
;
517 for (i
= 0; languages
[i
] && i
< lang_index
; i
++) {
518 gchar
*localized_key
= g_strdup_printf("Name[%s]", languages
[i
]);
519 if (strcmp(key
, localized_key
) == 0) {
522 entry
->name
= strdup(value
);
525 g_free(localized_key
);
528 } else if (!entry
->exec
&& strcmp(key
, "Exec") == 0) {
529 entry
->exec
= strdup(value
);
530 } else if (!entry
->icon
&& strcmp(key
, "Icon") == 0) {
531 entry
->icon
= strdup(value
);
537 // entry->name, entry->icon, entry->exec will never be empty strings (can be NULL though)
539 expand_exec(entry
, path
);
545 void free_desktop_entry(DesktopEntry
*entry
)
552 void test_launcher_read_desktop_file()
554 fprintf(stdout
, "\033[1;33m");
556 launcher_read_desktop_file("/usr/share/applications/firefox.desktop", &entry
);
557 printf("Name:%s Icon:%s Exec:%s\n", entry
.name
, entry
.icon
, entry
.exec
);
558 fprintf(stdout
, "\033[0m");
561 //TODO Use UTF8 when parsing the file
562 IconTheme
*load_theme(char *name
)
564 // Look for name/index.theme in $HOME/.icons, /usr/share/icons, /usr/share/pixmaps (stop at the first found)
565 // Parse index.theme -> list of IconThemeDir with attributes
577 file_name
= g_build_filename(g_get_home_dir(), ".icons", name
, "index.theme", NULL
);
578 if (!g_file_test(file_name
, G_FILE_TEST_EXISTS
)) {
580 file_name
= g_build_filename("/usr/share/icons", name
, "index.theme", NULL
);
581 if (!g_file_test(file_name
, G_FILE_TEST_EXISTS
)) {
583 file_name
= g_build_filename("/usr/share/pixmaps", name
, "index.theme", NULL
);
584 if (!g_file_test(file_name
, G_FILE_TEST_EXISTS
)) {
595 if ((f
= fopen(file_name
, "rt")) == NULL
) {
596 fprintf(stderr
, "Could not open theme '%s'\n", file_name
);
602 theme
= calloc(1, sizeof(IconTheme
));
603 theme
->name
= strdup(name
);
604 theme
->list_inherits
= NULL
;
605 theme
->list_directories
= NULL
;
607 IconThemeDir
*current_dir
= NULL
;
608 int inside_header
= 1;
609 while (getline(&line
, &line_size
, f
) >= 0) {
612 int line_len
= strlen(line
);
614 if (line
[line_len
- 1] == '\n') {
615 line
[line_len
- 1] = '\0';
624 if (parse_theme_line(line
, &key
, &value
)) {
625 if (strcmp(key
, "Inherits") == 0) {
626 // value is like oxygen,wood,default
628 token
= strtok(value
, ",\n");
629 while (token
!= NULL
)
631 theme
->list_inherits
= g_slist_append(theme
->list_inherits
, strdup(token
));
632 token
= strtok(NULL
, ",\n");
634 } else if (strcmp(key
, "Directories") == 0) {
635 // value is like 48x48/apps,48x48/mimetypes,32x32/apps,scalable/apps,scalable/mimetypes
637 token
= strtok(value
, ",\n");
638 while (token
!= NULL
)
640 IconThemeDir
*dir
= calloc(1, sizeof(IconThemeDir
));
641 dir
->name
= strdup(token
);
642 dir
->max_size
= dir
->min_size
= dir
->size
= -1;
643 dir
->type
= ICON_DIR_TYPE_THRESHOLD
;
645 theme
->list_directories
= g_slist_append(theme
->list_directories
, dir
);
646 token
= strtok(NULL
, ",\n");
650 } else if (current_dir
!= NULL
) {
651 if (parse_theme_line(line
, &key
, &value
)) {
652 if (strcmp(key
, "Size") == 0) {
654 sscanf(value
, "%d", ¤t_dir
->size
);
655 if (current_dir
->max_size
== -1)
656 current_dir
->max_size
= current_dir
->size
;
657 if (current_dir
->min_size
== -1)
658 current_dir
->min_size
= current_dir
->size
;
659 } else if (strcmp(key
, "MaxSize") == 0) {
661 sscanf(value
, "%d", ¤t_dir
->max_size
);
662 } else if (strcmp(key
, "MinSize") == 0) {
664 sscanf(value
, "%d", ¤t_dir
->min_size
);
665 } else if (strcmp(key
, "Threshold") == 0) {
667 sscanf(value
, "%d", ¤t_dir
->threshold
);
668 } else if (strcmp(key
, "Type") == 0) {
669 // value is Fixed, Scalable or Threshold : default to scalable for unknown Type.
670 if (strcmp(value
, "Fixed") == 0) {
671 current_dir
->type
= ICON_DIR_TYPE_FIXED
;
672 } else if (strcmp(value
, "Threshold") == 0) {
673 current_dir
->type
= ICON_DIR_TYPE_THRESHOLD
;
675 current_dir
->type
= ICON_DIR_TYPE_SCALABLE
;
677 } else if (strcmp(key
, "Context") == 0) {
678 // usual values: Actions, Applications, Devices, FileSystems, MimeTypes
679 current_dir
->context
= strdup(value
);
684 if (line
[0] == '[' && line
[line_len
- 1] == ']' && strcmp(line
, "[Icon Theme]") != 0) {
687 line
[line_len
- 1] = '\0';
688 char *dir_name
= line
+ 1;
689 GSList
* dir_item
= theme
->list_directories
;
690 while (dir_item
!= NULL
)
692 IconThemeDir
*dir
= dir_item
->data
;
693 if (strcmp(dir
->name
, dir_name
) == 0) {
697 dir_item
= g_slist_next(dir_item
);
708 void free_icon_theme(IconTheme
*theme
)
712 for (l_inherits
= theme
->list_inherits
; l_inherits
; l_inherits
= l_inherits
->next
) {
713 free(l_inherits
->data
);
716 for (l_dir
= theme
->list_directories
; l_dir
; l_dir
= l_dir
->next
) {
717 IconThemeDir
*dir
= (IconThemeDir
*)l_dir
->data
;
724 void test_launcher_read_theme_file()
726 fprintf(stdout
, "\033[1;33m");
727 IconTheme
*theme
= load_theme("oxygen");
729 printf("Could not load theme\n");
732 printf("Loaded theme: %s\n", theme
->name
);
733 GSList
* item
= theme
->list_inherits
;
736 printf("Inherits:%s\n", (char*)item
->data
);
737 item
= g_slist_next(item
);
739 item
= theme
->list_directories
;
742 IconThemeDir
*dir
= item
->data
;
743 printf("Dir:%s Size=%d MinSize=%d MaxSize=%d Threshold=%d Type=%s Context=%s\n",
744 dir
->name
, dir
->size
, dir
->min_size
, dir
->max_size
, dir
->threshold
,
745 dir
->type
== ICON_DIR_TYPE_FIXED
? "Fixed" :
746 dir
->type
== ICON_DIR_TYPE_SCALABLE
? "Scalable" :
747 dir
->type
== ICON_DIR_TYPE_THRESHOLD
? "Threshold" : "?????",
749 item
= g_slist_next(item
);
751 fprintf(stdout
, "\033[0m");
755 // Populates the list_icons list
756 void launcher_load_icons(Launcher
*launcher
)
758 // Load apps (.desktop style launcher items)
759 GSList
* app
= launcher
->list_apps
;
760 while (app
!= NULL
) {
762 launcher_read_desktop_file(app
->data
, &entry
);
764 LauncherIcon
*launcherIcon
= calloc(1, sizeof(LauncherIcon
));
765 launcherIcon
->area
.parent
= launcher
;
766 launcherIcon
->area
.panel
= launcher
->area
.panel
;
767 launcherIcon
->area
._draw_foreground
= draw_launcher_icon
;
768 launcherIcon
->area
.size_mode
= SIZE_BY_CONTENT
;
769 launcherIcon
->area
._resize
= NULL
;
770 launcherIcon
->area
.resize
= 0;
771 launcherIcon
->area
.redraw
= 1;
772 launcherIcon
->area
.bg
= &g_array_index(backgrounds
, Background
, 0);
773 launcherIcon
->area
.on_screen
= 1;
774 launcherIcon
->area
._on_change_layout
= launcher_icon_on_change_layout
;
775 if (launcher_tooltip_enabled
)
776 launcherIcon
->area
._get_tooltip_text
= launcher_icon_get_tooltip_text
;
778 launcherIcon
->area
._get_tooltip_text
= NULL
;
779 launcherIcon
->is_app_desktop
= 1;
780 launcherIcon
->cmd
= strdup(entry
.exec
);
781 launcherIcon
->icon_name
= entry
.icon
? strdup(entry
.icon
) : strdup(ICON_FALLBACK
);
782 launcherIcon
->icon_size
= 1;
783 launcherIcon
->icon_tooltip
= entry
.name
? strdup(entry
.name
) : strdup(entry
.exec
);
784 free_desktop_entry(&entry
);
785 launcher
->list_icons
= g_slist_append(launcher
->list_icons
, launcherIcon
);
786 add_area(&launcherIcon
->area
);
788 app
= g_slist_next(app
);
793 // Populates the list_themes list
794 void launcher_load_themes(Launcher
*launcher
)
796 // load the user theme, all the inherited themes recursively (DFS), and the hicolor theme
797 // avoid inheritance loops
798 if (!icon_theme_name
) {
799 fprintf(stderr
, "Missing launcher theme, default to 'hicolor'.\n");
800 icon_theme_name
= strdup("hicolor");
802 fprintf(stderr
, "Loading %s. Icon theme :", icon_theme_name
);
805 GSList
*queue
= g_slist_append(NULL
, strdup(icon_theme_name
));
806 GSList
*queued
= g_slist_append(NULL
, strdup(icon_theme_name
));
808 int hicolor_loaded
= 0;
809 while (queue
|| !hicolor_loaded
) {
811 GSList
* queued_item
= queued
;
812 while (queued_item
!= NULL
) {
813 if (strcmp(queued_item
->data
, "hicolor") == 0) {
817 queued_item
= g_slist_next(queued_item
);
821 queue
= g_slist_append(queue
, strdup("hicolor"));
822 queued
= g_slist_append(queued
, strdup("hicolor"));
825 char *name
= queue
->data
;
826 queue
= g_slist_remove(queue
, name
);
828 fprintf(stderr
, " '%s',", name
);
829 IconTheme
*theme
= load_theme(name
);
831 launcher
->list_themes
= g_slist_append(launcher
->list_themes
, theme
);
833 GSList
* item
= theme
->list_inherits
;
837 char *parent
= item
->data
;
839 GSList
* queued_item
= queued
;
840 while (queued_item
!= NULL
) {
841 if (strcmp(queued_item
->data
, parent
) == 0) {
845 queued_item
= g_slist_next(queued_item
);
848 queue
= g_slist_insert(queue
, strdup(parent
), pos
);
850 queued
= g_slist_append(queued
, strdup(parent
));
852 item
= g_slist_next(item
);
856 fprintf(stderr
, "\n");
860 for (l
= queue
; l
; l
= l
->next
)
863 for (l
= queued
; l
; l
= l
->next
)
865 g_slist_free(queued
);
868 int directory_matches_size(IconThemeDir
*dir
, int size
)
870 if (dir
->type
== ICON_DIR_TYPE_FIXED
) {
871 return dir
->size
== size
;
872 } else if (dir
->type
== ICON_DIR_TYPE_SCALABLE
) {
873 return dir
->min_size
<= size
&& size
<= dir
->max_size
;
874 } else /*if (dir->type == ICON_DIR_TYPE_THRESHOLD)*/ {
875 return dir
->size
- dir
->threshold
<= size
&& size
<= dir
->size
+ dir
->threshold
;
879 int directory_size_distance(IconThemeDir
*dir
, int size
)
881 if (dir
->type
== ICON_DIR_TYPE_FIXED
) {
882 return abs(dir
->size
- size
);
883 } else if (dir
->type
== ICON_DIR_TYPE_SCALABLE
) {
884 if (size
< dir
->min_size
) {
885 return dir
->min_size
- size
;
886 } else if (size
> dir
->max_size
) {
887 return size
- dir
->max_size
;
891 } else /*if (dir->type == ICON_DIR_TYPE_THRESHOLD)*/ {
892 if (size
< dir
->size
- dir
->threshold
) {
893 return dir
->min_size
- size
;
894 } else if (size
> dir
->size
+ dir
->threshold
) {
895 return size
- dir
->max_size
;
902 #define DEBUG_ICON_SEARCH 0
903 // Returns the full path to an icon file (or NULL) given the icon name
904 char *icon_path(Launcher
*launcher
, const char *icon_name
, int size
)
906 if (icon_name
== NULL
)
909 // If the icon_name is already a path and the file exists, return it
910 if (strstr(icon_name
, "/") == icon_name
) {
911 if (g_file_test(icon_name
, G_FILE_TEST_EXISTS
))
912 return strdup(icon_name
);
917 GSList
*basenames
= NULL
;
918 char *home_icons
= g_build_filename(g_get_home_dir(), ".icons", NULL
);
919 basenames
= g_slist_append(basenames
, home_icons
);
920 char *home_local_icons
= g_build_filename(g_get_home_dir(), ".local/share/icons", NULL
);
921 basenames
= g_slist_append(basenames
, home_local_icons
);
922 basenames
= g_slist_append(basenames
, "/usr/local/share/icons");
923 basenames
= g_slist_append(basenames
, "/usr/local/share/pixmaps");
924 basenames
= g_slist_append(basenames
, "/usr/share/icons");
925 basenames
= g_slist_append(basenames
, "/usr/share/pixmaps");
927 GSList
*extensions
= NULL
;
928 extensions
= g_slist_append(extensions
, ".png");
929 extensions
= g_slist_append(extensions
, ".xpm");
930 // if the icon name already contains one of the extensions (e.g. vlc.png instead of vlc) add a special entry
932 for (ext
= extensions
; ext
; ext
= g_slist_next(ext
)) {
933 char *extension
= (char*) ext
->data
;
934 if (strlen(icon_name
) > strlen(extension
) &&
935 strcmp(extension
, icon_name
+ strlen(icon_name
) - strlen(extension
)) == 0) {
936 extensions
= g_slist_append(extensions
, "");
942 // Stage 1: exact size match
943 // the theme must have a higher priority than having an exact size match, so we will just use
944 // the code that searches for the best size match (it will find the exact size match if one exists)
946 for (theme = launcher->list_themes; theme; theme = g_slist_next(theme)) {
948 for (dir = ((IconTheme*)theme->data)->list_directories; dir; dir = g_slist_next(dir)) {
949 if (directory_matches_size((IconThemeDir*)dir->data, size)) {
951 for (base = basenames; base; base = g_slist_next(base)) {
953 for (ext = extensions; ext; ext = g_slist_next(ext)) {
954 char *base_name = (char*) base->data;
955 char *theme_name = ((IconTheme*)theme->data)->name;
956 char *dir_name = ((IconThemeDir*)dir->data)->name;
957 char *extension = (char*) ext->data;
958 char *file_name = malloc(strlen(base_name) + strlen(theme_name) +
959 strlen(dir_name) + strlen(icon_name) + strlen(extension) + 100);
960 // filename = directory/$(themename)/subdirectory/iconname.extension
961 sprintf(file_name, "%s/%s/%s/%s%s", base_name, theme_name, dir_name, icon_name, extension);
962 //printf("found exact: %s\n", file_name);
963 //printf("checking %s\n", file_name);
964 if (g_file_test(file_name, G_FILE_TEST_EXISTS)) {
965 g_slist_free(basenames);
966 g_slist_free(extensions);
968 g_free(home_local_icons);
982 // Stage 2: best size match
983 // Contrary to the freedesktop spec, we are not choosing the closest icon in size, but the next larger icon
984 // otherwise the quality is usually crap (for size 22, if you can choose 16 or 32, you're better with 32)
985 // We do fallback to the closest size if we cannot find a larger or equal icon
987 // These 3 variables are used for keeping the closest size match
988 int minimal_size
= INT_MAX
;
989 char *best_file_name
= NULL
;
990 GSList
*best_file_theme
= NULL
;
992 // These 3 variables are used for keeping the next larger match
993 int next_larger_size
= -1;
994 char *next_larger
= NULL
;
995 GSList
*next_larger_theme
= NULL
;
997 for (theme
= launcher
->list_themes
; theme
; theme
= g_slist_next(theme
)) {
999 for (dir
= ((IconTheme
*)theme
->data
)->list_directories
; dir
; dir
= g_slist_next(dir
)) {
1001 for (base
= basenames
; base
; base
= g_slist_next(base
)) {
1003 for (ext
= extensions
; ext
; ext
= g_slist_next(ext
)) {
1004 char *base_name
= (char*) base
->data
;
1005 char *theme_name
= ((IconTheme
*)theme
->data
)->name
;
1006 char *dir_name
= ((IconThemeDir
*)dir
->data
)->name
;
1007 char *extension
= (char*) ext
->data
;
1008 char *file_name
= malloc(strlen(base_name
) + strlen(theme_name
) +
1009 strlen(dir_name
) + strlen(icon_name
) + strlen(extension
) + 100);
1010 // filename = directory/$(themename)/subdirectory/iconname.extension
1011 sprintf(file_name
, "%s/%s/%s/%s%s", base_name
, theme_name
, dir_name
, icon_name
, extension
);
1012 if (DEBUG_ICON_SEARCH
)
1013 printf("checking %s\n", file_name
);
1014 if (g_file_test(file_name
, G_FILE_TEST_EXISTS
)) {
1015 if (DEBUG_ICON_SEARCH
)
1016 printf("found: %s\n", file_name
);
1018 if (directory_size_distance((IconThemeDir
*)dir
->data
, size
) < minimal_size
&& (!best_file_theme
? 1 : theme
== best_file_theme
)) {
1019 if (best_file_name
) {
1020 free(best_file_name
);
1021 best_file_name
= NULL
;
1023 best_file_name
= strdup(file_name
);
1024 minimal_size
= directory_size_distance((IconThemeDir
*)dir
->data
, size
);
1025 best_file_theme
= theme
;
1026 if (DEBUG_ICON_SEARCH
)
1027 printf("best_file_name = %s; minimal_size = %d\n", best_file_name
, minimal_size
);
1029 // Next larger match
1030 if (((IconThemeDir
*)dir
->data
)->size
>= size
&&
1031 (next_larger_size
== -1 || ((IconThemeDir
*)dir
->data
)->size
< next_larger_size
) &&
1032 (!next_larger_theme
? 1 : theme
== next_larger_theme
)) {
1037 next_larger
= strdup(file_name
);
1038 next_larger_size
= ((IconThemeDir
*)dir
->data
)->size
;
1039 next_larger_theme
= theme
;
1040 if (DEBUG_ICON_SEARCH
)
1041 printf("next_larger = %s; next_larger_size = %d\n", next_larger
, next_larger_size
);
1050 g_slist_free(basenames
);
1051 g_slist_free(extensions
);
1052 free(best_file_name
);
1054 g_free(home_local_icons
);
1057 if (best_file_name
) {
1058 g_slist_free(basenames
);
1059 g_slist_free(extensions
);
1061 g_free(home_local_icons
);
1062 return best_file_name
;
1065 // Stage 3: look in unthemed icons
1068 for (base
= basenames
; base
; base
= g_slist_next(base
)) {
1070 for (ext
= extensions
; ext
; ext
= g_slist_next(ext
)) {
1071 char *base_name
= (char*) base
->data
;
1072 char *extension
= (char*) ext
->data
;
1073 char *file_name
= malloc(strlen(base_name
) + strlen(icon_name
) +
1074 strlen(extension
) + 100);
1075 // filename = directory/iconname.extension
1076 sprintf(file_name
, "%s/%s%s", base_name
, icon_name
, extension
);
1077 if (DEBUG_ICON_SEARCH
)
1078 printf("checking %s\n", file_name
);
1079 if (g_file_test(file_name
, G_FILE_TEST_EXISTS
)) {
1080 g_slist_free(basenames
);
1081 g_slist_free(extensions
);
1083 g_free(home_local_icons
);
1093 fprintf(stderr
, "Could not find icon %s\n", icon_name
);
1095 g_slist_free(basenames
);
1096 g_slist_free(extensions
);
1098 g_free(home_local_icons
);