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", &current_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