1 /*
2 Kickshaw - A Menu Editor for Openbox
3
4 Copyright (c) 2010–2018 Marcus Schätzle
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License along
17 with Kickshaw. If not, see http://www.gnu.org/licenses/.
18 */
19
20 #include <gtk/gtk.h>
21 #if !(GLIB_CHECK_VERSION(2,6,0))
22 #error "Kickshaw needs GLib version 2.6 or higher to compile. Please make sure to have at least GLib version 2.6 installed. Compilation aborted."
23 #endif
24 #if !(GTK_CHECK_VERSION(2,6,0))
25 #error "Kickshaw needs GLib version 2.6 or higher to compile. The first version based upon a version of GLib >= 2.6 is GTK 2.6. Please make sure to have at least GTK version 2.6 installed. Compilation aborted."
26 #endif
27
28 #include "definitions_and_enumerations.h"
29 #include "check_for_external_changes_timeout.h"
30
31 gboolean check_for_external_file_and_settings_changes (G_GNUC_UNUSED gpointer identifier);
32 void stop_timeout (void);
33
34 /*
35
36 Checks if...
37 ...a valid icon file path has become invalid; if so, changes the icon to a broken one.
38 ...an invalid icon file path has become valid; if so, replaces the broken icon with the icon image,
39 if it is a proper image file.
40 ...the font size has been changed; if so, changes the size of icons according to the new font size.
41
42 */
43
44 // The identifier is not used by the function, but to switch the timeout on and off.
check_for_external_file_and_settings_changes(G_GNUC_UNUSED gpointer identifier)45 gboolean check_for_external_file_and_settings_changes (G_GNUC_UNUSED gpointer identifier)
46 {
47 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ks.treeview));
48 gint number_of_selected_rows = gtk_tree_selection_count_selected_rows (selection);
49
50 gchar *font_desc;
51 guint font_size_updated;
52 gboolean recreate_icon_images = FALSE; // Default
53 gboolean create_new_invalid_icon_imgs = FALSE; // Default
54 gboolean replacement_done;
55
56 gchar *current_icon_theme_name;
57
58 gchar *time_stamp;
59
60 GSList *rows_with_icons_loop;
61
62 GtkTreePath *path_loop;
63 GtkTreeIter iter_loop;
64
65 guint icon_img_status_uint_loop;
66 gchar *icon_modification_time_txt_loop;
67 gchar *icon_path_txt_loop;
68
69 g_object_get (gtk_settings_get_default (), "gtk-font-name", &font_desc, NULL);
70 if (!(STREQ (font_desc, ks.font_desc))) {
71 FREE_AND_REASSIGN (ks.font_desc, font_desc);
72
73 // Check if font size has changed.
74 if ((recreate_icon_images = (G_UNLIKELY (ks.font_size != (font_size_updated = get_font_size ()))))) {
75 ks.font_size = font_size_updated;
76 create_new_invalid_icon_imgs = TRUE;
77 }
78
79 #if GTK_CHECK_VERSION(2,18,0)
80 if (gtk_widget_get_visible (ks.change_values_label)) {
81 #else
82 if (GTK_WIDGET_VISIBLE (ks.change_values_label)) {
83 #endif
84 gchar *font_name = get_font_name ();
85 const gchar *label_txt = gtk_label_get_text (GTK_LABEL (ks.change_values_label));
86
87 gchar *label_markup = g_strdup_printf ("<span font_desc='%s %i'>%s</span>", font_name, ks.font_size + 2, label_txt);
88 gtk_label_set_markup (GTK_LABEL (ks.change_values_label), label_markup);
89
90 // Cleanup
91 g_free (label_markup);
92 g_free (font_name);
93 }
94 }
95 else {
96 // Cleanup
97 g_free (font_desc);
98 }
99
100 // Check if icon theme has changed.
101 g_object_get (gtk_settings_get_default (), "gtk-icon-theme-name", ¤t_icon_theme_name, NULL);
102 if (G_UNLIKELY (!STREQ (ks.icon_theme_name, current_icon_theme_name))) {
103 FREE_AND_REASSIGN (ks.icon_theme_name, g_strdup (current_icon_theme_name));
104 create_new_invalid_icon_imgs = TRUE;
105 }
106
107 // If font size or icon theme has changed, recreate the invalid icon images.
108 if (G_UNLIKELY (create_new_invalid_icon_imgs)) {
109 create_invalid_icon_imgs ();
110 }
111
112 for (rows_with_icons_loop = ks.rows_with_icons;
113 rows_with_icons_loop;
114 rows_with_icons_loop = rows_with_icons_loop->next) {
115 path_loop = rows_with_icons_loop->data;
116 gtk_tree_model_get_iter (ks.model, &iter_loop, path_loop);
117
118 gtk_tree_model_get (ks.model, &iter_loop,
119 TS_ICON_IMG_STATUS, &icon_img_status_uint_loop,
120 TS_ICON_MODIFICATION_TIME, &icon_modification_time_txt_loop,
121 TS_ICON_PATH, &icon_path_txt_loop,
122 -1);
123
124 replacement_done = FALSE; // Default
125
126 /*
127 Case 1: Stored path no longer points to a valid icon image, so icon has to replaced with broken icon.
128 Case 2: Invalid path to an icon image has become valid.
129 Case 3: Icon image file is replaced with another one.
130 */
131 if (G_LIKELY (icon_img_status_uint_loop != INVALID_PATH) &&
132 G_UNLIKELY (!g_file_test (icon_path_txt_loop, G_FILE_TEST_EXISTS))) { // Case 1
133 gtk_tree_store_set (ks.treestore, &iter_loop,
134 TS_ICON_IMG, ks.invalid_icon_imgs[INVALID_PATH_ICON],
135 TS_ICON_IMG_STATUS, INVALID_PATH,
136 TS_ICON_MODIFICATION_TIME, NULL,
137 -1);
138
139 if (number_of_selected_rows == 1 && gtk_tree_selection_iter_is_selected (selection, &iter_loop)) {
140 highlight_border (ks.highlighted_widgets, ICON_PATH_ENTRY_HL, TRUE);
141 }
142
143 replacement_done = TRUE;
144 }
145 else if (G_LIKELY (g_file_test (icon_path_txt_loop, G_FILE_TEST_EXISTS))) {
146 time_stamp = get_modification_time_for_icon (icon_path_txt_loop);
147
148 if (G_UNLIKELY (icon_img_status_uint_loop == INVALID_PATH ||
149 !STREQ (time_stamp, icon_modification_time_txt_loop))) {
150 // Case 2 & 3, FALSE == don't show error message when error occurs.
151 if (G_UNLIKELY (!set_icon (&iter_loop, icon_path_txt_loop, FALSE))) {
152 gtk_tree_store_set (ks.treestore, &iter_loop,
153 TS_ICON_IMG, ks.invalid_icon_imgs[INVALID_FILE_ICON],
154 TS_ICON_IMG_STATUS, INVALID_FILE,
155 TS_ICON_MODIFICATION_TIME, time_stamp,
156 -1);
157 }
158
159 if (number_of_selected_rows == 1 && gtk_tree_selection_iter_is_selected (selection, &iter_loop)) {
160 highlight_border (ks.highlighted_widgets, ICON_PATH_ENTRY_HL, FALSE);
161 }
162
163 replacement_done = TRUE;
164 }
165
166 // Cleanup
167 g_free (time_stamp);
168 }
169
170 /*
171 If the font size or icon theme has changed, replace the icon images accordingly,
172 but only if the icons have not been replaced already, since the replacement icons already have the new size.
173 */
174 if (G_UNLIKELY ((recreate_icon_images || (create_new_invalid_icon_imgs && icon_img_status_uint_loop)) &&
175 !replacement_done)) {
176 if (!icon_img_status_uint_loop) { // Icon path is OK and valid icon exists = create and store larger or smaller icon
177 set_icon (&iter_loop, icon_path_txt_loop, FALSE); // FALSE == don't show error message when error occurs.
178 }
179 else { // Icon path is wrong or icon image is invalid = store broken icon with larger or smaller size
180 gtk_tree_store_set (ks.treestore, &iter_loop, TS_ICON_IMG,
181 ks.invalid_icon_imgs[(icon_img_status_uint_loop == INVALID_PATH) ?
182 INVALID_PATH_ICON : INVALID_FILE_ICON], -1);
183 }
184
185 gtk_tree_view_columns_autosize (GTK_TREE_VIEW (ks.treeview)); // In case that the font size has been reduced.
186 }
187
188 // Cleanup
189 g_free (icon_modification_time_txt_loop);
190 g_free (icon_path_txt_loop);
191 }
192
193 // Cleanup
194 g_free (current_icon_theme_name);
195
196 return TRUE; // Keep timeout function up and running.
197 }
198
199 /*
200
201 Stops the timer and erases the list of icon occourrences.
202
203 */
204
205 void stop_timeout (void)
206 {
207 g_source_remove_by_user_data ("timeout");
208 #if GLIB_CHECK_VERSION(2,28,0)
209 g_slist_free_full (ks.rows_with_icons, (GDestroyNotify) gtk_tree_path_free);
210 #else
211 g_slist_foreach (ks.rows_with_icons, (GFunc) gtk_tree_path_free, NULL);
212 g_slist_free (ks.rows_with_icons);
213 #endif
214 ks.rows_with_icons = NULL;
215 }
216