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 #include <string.h>
22
23 #include "definitions_and_enumerations.h"
24 #include "adding_and_deleting.h"
25
26 static void hide_or_deactivate_widgets (void);
27 void one_of_the_change_values_buttons_pressed (gchar *origin);
28 static GtkTreeIter add_new_execution (gchar *new_menu_element, gboolean same_level);
29 void add_new (gchar *new_menu_element_plus_opt_suppl);
30 static void create_combo_box (void);
31 void option_list_with_headlines (G_GNUC_UNUSED GtkCellLayout *cell_layout,
32 GtkCellRenderer *action_option_combo_box_renderer,
33 GtkTreeModel *action_option_combo_box_model,
34 GtkTreeIter *action_option_combo_box_iter,
35 G_GNUC_UNUSED gpointer data);
36 static guint8 generate_action_option_combo_box_items_for_Exe_and_snotify_opts (gchar *options_type);
37 void generate_items_for_action_option_combo_box (gchar *preset_choice);
38 void show_action_options (void);
39 void show_or_hide_startupnotify_options (void);
40 void single_field_entry (void);
41 void hide_action_option_grid (gchar *origin);
42 static void clear_entries (void);
43 void action_option_insert (gchar *origin);
44 static gboolean check_for_menus ( GtkTreeModel *filter_model,
45 G_GNUC_UNUSED GtkTreePath *filter_path,
46 GtkTreeIter *filter_iter);
47 void remove_menu_id (gchar *menu_id);
48 void remove_all_children (void);
49 void remove_rows (gchar *origin);
50
51 /*
52
53 Deactivate or hide those widgets whose actions could interfere during the entry of new values.
54
55 */
56
hide_or_deactivate_widgets(void)57 static void hide_or_deactivate_widgets (void)
58 {
59 guint8 tb_cnt;
60
61 gtk_widget_set_sensitive (ks.mb_edit, FALSE);
62 gtk_widget_set_sensitive (ks.mb_search, FALSE);
63 gtk_widget_set_sensitive (ks.mb_options, FALSE);
64 for (tb_cnt = TB_MOVE_UP; tb_cnt <= TB_FIND; tb_cnt++) {
65 gtk_widget_set_sensitive ((GtkWidget *) ks.tb[tb_cnt], FALSE);
66 }
67 gtk_widget_hide (ks.button_grid);
68 gtk_widget_hide (ks.find_grid);
69 }
70
71 /*
72
73 Execute the actions that can be initiated from the "change values" input screen.
74
75 */
76
one_of_the_change_values_buttons_pressed(gchar * origin)77 void one_of_the_change_values_buttons_pressed (gchar *origin)
78 {
79 GSList *change_values_user_settings_loop;
80 // AD = adding and deleting, indicating that the enums are only used here
81 enum { AD_FIELD, AD_VALUE };
82 gchar **parameter;
83
84 gboolean including_action_check_button_active =
85 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ks.new_action_option_widgets[INCLUDING_ACTION_CHECK_BUTTON]));
86 gchar *new_menu_element = NULL; // Initialization avoids compiler warning.
87 gboolean same_level = TRUE; // default
88
89 for (change_values_user_settings_loop = ks.change_values_user_settings;
90 change_values_user_settings_loop;
91 change_values_user_settings_loop = change_values_user_settings_loop->next) {
92 parameter = g_strsplit (change_values_user_settings_loop->data, ":", -1);
93 if (STREQ (origin, "done") && STREQ (parameter[AD_FIELD], "new menu element")) {
94 // "value" exists only temporary
95 new_menu_element = g_strdup (parameter[AD_VALUE]);
96 }
97 else if (STREQ (origin, "reset")) {
98 if (STREQ (parameter[AD_FIELD], "new menu element")) {
99 gchar *default_label_txt = g_strdup_printf ("New %s", parameter[AD_VALUE]);
100 gtk_entry_set_text (GTK_ENTRY (ks.entry_fields[MENU_ELEMENT_OR_VALUE_ENTRY]), default_label_txt);
101 // Cleanup
102 g_free (default_label_txt);
103
104 // To keep the code simple, the rest of this if-clause resets fields even if they are currently not visible.
105 gtk_style_context_remove_class (gtk_widget_get_style_context (ks.entry_fields[MENU_ELEMENT_OR_VALUE_ENTRY]),
106 "mandatory_missing");
107 gtk_style_context_remove_class (gtk_widget_get_style_context (ks.entry_fields[EXECUTE_ENTRY]),
108 "mandatory_missing");
109
110 gtk_entry_set_text (GTK_ENTRY (ks.entry_fields[ICON_PATH_ENTRY]), "");
111 gtk_entry_set_text (GTK_ENTRY (ks.entry_fields[EXECUTE_ENTRY]), "");
112 }
113 else if (STREQ (parameter[AD_FIELD], "menu ID field")) {
114 gtk_entry_set_text (GTK_ENTRY (ks.entry_fields[MENU_ID_ENTRY]), parameter[AD_VALUE]);
115 }
116 else if (STREQ (parameter[AD_FIELD], "inside menu")) {
117 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ks.new_action_option_widgets[INSIDE_MENU_CHECK_BUTTON]),
118 STREQ (parameter[AD_VALUE], "true"));
119 }
120 else if (STREQ (parameter[AD_FIELD], "incl. action")) {
121 including_action_check_button_active = (STREQ (parameter[AD_VALUE], "true"));
122 // Prevent recursion
123 g_signal_handler_block (ks.new_action_option_widgets[INCLUDING_ACTION_CHECK_BUTTON],
124 ks.handler_id_including_action_check_button);
125 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ks.new_action_option_widgets[INCLUDING_ACTION_CHECK_BUTTON]),
126 including_action_check_button_active);
127 g_signal_handler_unblock (ks.new_action_option_widgets[INCLUDING_ACTION_CHECK_BUTTON],
128 ks.handler_id_including_action_check_button);
129 gtk_widget_set_visible (ks.new_action_option_widgets[NEW_ACTION_OPTION_COMBO_BOX], including_action_check_button_active);
130 gtk_widget_set_visible (ks.options_grid, including_action_check_button_active);
131 }
132 else { // action
133 gtk_combo_box_set_active_id (GTK_COMBO_BOX (ks.new_action_option_widgets[NEW_ACTION_OPTION_COMBO_BOX]), parameter[AD_VALUE]);
134 if (streq_any (parameter[AD_VALUE], "Execute", "Restart", NULL)) {
135 clear_entries ();
136 }
137 else if (streq_any (parameter[AD_VALUE], "Exit", "SessionLogout", NULL)) {
138 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ks.options_fields[SN_OR_PROMPT]), TRUE);
139 }
140 }
141 }
142 else if (STREQ (origin, "incl. action") && STREQ (parameter[AD_FIELD], "action")) {
143 gtk_combo_box_set_active_id (GTK_COMBO_BOX (ks.new_action_option_widgets[NEW_ACTION_OPTION_COMBO_BOX]), parameter[AD_VALUE]);
144 if (!including_action_check_button_active) {
145 clear_entries ();
146 }
147 else if (streq_any (parameter[AD_VALUE], "Exit", "SessionLogout", NULL)) {
148 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ks.options_fields[SN_OR_PROMPT]), TRUE);
149 }
150 gtk_widget_set_visible (ks.new_action_option_widgets[NEW_ACTION_OPTION_COMBO_BOX], including_action_check_button_active);
151 gtk_widget_set_visible (ks.options_grid, including_action_check_button_active);
152 }
153
154 // Cleanup
155 g_strfreev (parameter);
156 }
157
158 if (STREQ (origin, "done")) {
159 gboolean add_action = (ks.new_action_option_widgets[NEW_ACTION_OPTION_COMBO_BOX] &&
160 gtk_widget_get_visible (ks.new_action_option_widgets[NEW_ACTION_OPTION_COMBO_BOX]));
161 gchar *menu_id = NULL; // Initialization avoids compiler warning.
162 gboolean missing_entry_or_error = FALSE; // default
163
164 // Missing label for new menu element.
165 if (!(*((gchar *) gtk_entry_get_text (GTK_ENTRY (ks.entry_fields[MENU_ELEMENT_OR_VALUE_ENTRY]))))) {
166 wrong_or_missing (ks.entry_fields[MENU_ELEMENT_OR_VALUE_ENTRY], ks.menu_element_or_value_entry_css_provider);
167 missing_entry_or_error = TRUE;
168 }
169 else {
170 gtk_style_context_remove_class (gtk_widget_get_style_context (ks.entry_fields[MENU_ELEMENT_OR_VALUE_ENTRY]),
171 "mandatory_missing");
172 }
173
174 // "Execute" missing of new pipe menu.
175 if (STREQ (new_menu_element, "pipe menu") &&
176 !(*((gchar *) gtk_entry_get_text (GTK_ENTRY (ks.entry_fields[EXECUTE_ENTRY]))))) {
177 wrong_or_missing (ks.entry_fields[EXECUTE_ENTRY], ks.execute_entry_css_provider);
178 missing_entry_or_error = TRUE;
179 }
180 else {
181 gtk_style_context_remove_class (gtk_widget_get_style_context (ks.entry_fields[EXECUTE_ENTRY]),
182 "mandatory_missing");
183 }
184
185 // Prevent empty command field for "Execute".
186 if (add_action &&
187 STREQ ((gchar *) gtk_combo_box_get_active_id (GTK_COMBO_BOX (ks.new_action_option_widgets[NEW_ACTION_OPTION_COMBO_BOX])),
188 "Execute") &&
189 !(*((gchar *) gtk_entry_get_text (GTK_ENTRY (ks.options_fields[COMMAND]))))) {
190 wrong_or_missing (ks.options_fields[COMMAND], ks.execute_options_css_providers[COMMAND]);
191 missing_entry_or_error = TRUE;
192 }
193 else {
194 gtk_style_context_remove_class (gtk_widget_get_style_context (ks.options_fields[COMMAND]), "mandatory_missing");
195 }
196
197 // Invalid icon file or path
198 const gchar *icon_path = gtk_entry_get_text (GTK_ENTRY (ks.entry_fields[ICON_PATH_ENTRY]));
199 if (*icon_path && !set_icon (&ks.iter, (gchar *) icon_path, TRUE)) { // TRUE = display error message if error occurs.
200 missing_entry_or_error = TRUE;
201 }
202
203 // Non-unique menu ID
204 if (!STREQ (new_menu_element, "item")) { // menu or pipe menu
205 menu_id = (gchar *) gtk_entry_get_text (GTK_ENTRY (ks.entry_fields[MENU_ID_ENTRY]));
206 if (G_UNLIKELY (g_slist_find_custom (ks.menu_ids, menu_id, (GCompareFunc) strcmp))) {
207 show_errmsg ("The chosen menu ID already exists. Please choose another one.");
208 missing_entry_or_error = TRUE;
209 }
210 }
211
212 if (missing_entry_or_error) {
213 // Cleanup
214 g_free (new_menu_element);
215
216 return;
217 }
218
219 if (!STREQ (new_menu_element, "item")) {
220 ks.menu_ids = g_slist_prepend (ks.menu_ids, g_strdup (menu_id));
221 }
222
223 if (gtk_widget_get_visible (ks.new_action_option_widgets[INSIDE_MENU_CHECK_BUTTON])) {
224 same_level = !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ks.new_action_option_widgets[INSIDE_MENU_CHECK_BUTTON]));
225 }
226
227 GtkTreeIter new_menu_element_iter;
228 ks.iter = new_menu_element_iter = add_new_execution (new_menu_element, same_level);
229
230 if (add_action) {
231 repopulate_txt_fields_array (); // This is usually called by row_selected () and needed by the following function.
232 action_option_insert ("by combo box");
233 }
234
235 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ks.treeview));
236 // This triggers row_selected, resulting in a complete reset.
237 gtk_tree_selection_select_iter (selection, &new_menu_element_iter);
238
239 // Cleanup
240 g_free (new_menu_element);
241 }
242 }
243
244 /*
245
246 Adds a new menu element.
247
248 */
249
add_new_execution(gchar * new_menu_element,gboolean same_level)250 static GtkTreeIter add_new_execution (gchar *new_menu_element, gboolean same_level)
251 {
252 gpointer new_ts_fields[NUMBER_OF_TS_ELEMENTS] = { NULL }; // Defaults
253
254 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ks.treeview));
255
256 GtkTreePath *path = NULL; // Default
257 GtkTreeIter new_iter;
258
259 gboolean path_is_on_toplevel = TRUE; // Default
260
261 guint8 ts_fields_cnt;
262
263 if (streq_any (new_menu_element, "menu", "pipe menu", "item", NULL)) {
264 new_ts_fields[TS_TYPE] = new_menu_element;
265 new_ts_fields[TS_MENU_ELEMENT] = (gchar *) gtk_entry_get_text (GTK_ENTRY (ks.entry_fields[MENU_ELEMENT_OR_VALUE_ENTRY]));
266
267 if (*(gtk_entry_get_text (GTK_ENTRY (ks.entry_fields[ICON_PATH_ENTRY])))) {
268 GdkPixbuf *icon_in_original_size;
269
270 new_ts_fields[TS_ICON_PATH] = (gchar *) gtk_entry_get_text (GTK_ENTRY (ks.entry_fields[ICON_PATH_ENTRY]));
271 icon_in_original_size = gdk_pixbuf_new_from_file (new_ts_fields[TS_ICON_PATH], NULL);
272 new_ts_fields[TS_ICON_IMG] = gdk_pixbuf_scale_simple (icon_in_original_size,
273 ks.font_size + 10, ks.font_size + 10,
274 GDK_INTERP_BILINEAR);
275 new_ts_fields[TS_ICON_MODIFICATION_TIME] = get_modification_time_for_icon (new_ts_fields[TS_ICON_PATH]);
276
277 // Cleanup
278 g_object_unref (icon_in_original_size);
279 }
280 if (streq_any (new_menu_element, "menu", "pipe menu", NULL)) {
281 new_ts_fields[TS_MENU_ID] = (gchar *) gtk_entry_get_text (GTK_ENTRY (ks.entry_fields[MENU_ID_ENTRY]));
282 if (STREQ (new_menu_element, "pipe menu") && *(gtk_entry_get_text (GTK_ENTRY (ks.entry_fields[EXECUTE_ENTRY])))) {
283 new_ts_fields[TS_EXECUTE] = (gchar *) gtk_entry_get_text (GTK_ENTRY (ks.entry_fields[EXECUTE_ENTRY]));
284 }
285 }
286 }
287 else if (STREQ (new_menu_element, "separator")) {
288 new_ts_fields[TS_TYPE] = "separator";
289 }
290 else {
291 new_ts_fields[TS_TYPE] = "option";
292 new_ts_fields[TS_MENU_ELEMENT] = new_menu_element;
293
294 // --- Assigning predefinied values for options. ---
295
296 if (STREQ (new_menu_element, "enabled") ||
297 streq_any (ks.txt_fields[MENU_ELEMENT_TXT], "Exit", "SessionLogout", NULL)) { // new_menu_element == "prompt"
298 new_ts_fields[TS_VALUE] = "yes";
299 }
300 }
301
302
303 if (gtk_tree_selection_count_selected_rows (selection) &&
304 gtk_tree_path_get_depth (path = gtk_tree_model_get_path (ks.model, &ks.iter)) > 1) {
305 path_is_on_toplevel = FALSE;
306 }
307
308 // Adding options inside an action or an option block.
309 if (STREQ (ks.txt_fields[TYPE_TXT], "action") ||
310 (STREQ (ks.txt_fields[TYPE_TXT], "option block") && !streq_any (new_menu_element, "prompt", "command", NULL))) {
311 same_level = FALSE;
312 }
313
314
315 // --- Add the new row. ---
316
317
318 // Prevents that the default check for change of selection(s) gets in the way.
319 g_signal_handler_block (selection, ks.handler_id_row_selected);
320
321 gtk_tree_selection_unselect_all (selection); // The old selection will be replaced by the one of the new row.
322
323 if (!path || !same_level) {
324 gtk_tree_store_append (ks.treestore, &new_iter, (!path) ? NULL : &ks.iter);
325
326 if (path) {
327 gtk_tree_view_expand_row (GTK_TREE_VIEW (ks.treeview), path, FALSE); // FALSE == just expand immediate children.
328 }
329 }
330 else {
331 GtkTreeIter parent;
332
333 if (!path_is_on_toplevel) {
334 gtk_tree_model_iter_parent (ks.model, &parent, &ks.iter);
335 }
336 gtk_tree_store_insert_after (ks.treestore, &new_iter, (path_is_on_toplevel) ? NULL : &parent, &ks.iter);
337 }
338
339 // Set element visibility of menus, pipe menus, items and separators.
340 if (g_regex_match_simple ("menu|pipe menu|item.*|separator", new_menu_element, 0, 0)) {
341 new_ts_fields[TS_ELEMENT_VISIBILITY] = "visible"; // Default
342 /*
343 path == NULL means that the new menu element has been added at toplevel;
344 such a menu element is always visible after its addition, since it can't have an invisible ancestor.
345 */
346 if (path) {
347 GtkTreePath *new_path = gtk_tree_model_get_path (ks.model, &new_iter);
348 guint8 invisible_ancestor;
349
350 if ((invisible_ancestor = check_if_invisible_ancestor_exists (ks.model, new_path))) { // Parentheses avoid gcc warning.
351 new_ts_fields[TS_ELEMENT_VISIBILITY] = (invisible_ancestor == INVISIBLE_ANCESTOR) ?
352 "invisible dsct. of invisible menu" : "invisible dsct. of invisible orphaned menu";
353 }
354 // Cleanup
355 gtk_tree_path_free (new_path);
356 }
357 }
358
359 for (ts_fields_cnt = 0; ts_fields_cnt < NUMBER_OF_TS_ELEMENTS; ts_fields_cnt++) {
360 gtk_tree_store_set (ks.treestore, &new_iter, ts_fields_cnt, new_ts_fields[ts_fields_cnt], -1);
361 }
362
363 // Cleanup
364 gtk_tree_path_free (path);
365 if (new_ts_fields[TS_ICON_IMG]) {
366 g_object_unref (new_ts_fields[TS_ICON_IMG]);
367 }
368 g_free (new_ts_fields[TS_ICON_MODIFICATION_TIME]);
369
370 if (!gtk_widget_get_visible (ks.change_values_label)) {
371 gtk_tree_selection_select_iter (selection, &new_iter);
372 }
373
374 /*
375 In case that autosorting is activated:
376 If the type of the new row is...
377 ...a prompt or command option of Execute, ... OR:
378 ...an option of startupnotify, ...
379 ...sort all options of the parent row and move the selection to the new option.
380 */
381 if (ks.autosort_options && STREQ (new_ts_fields[TS_TYPE], "option")) {
382 GtkTreeIter parent;
383
384 gtk_tree_model_iter_parent (ks.model, &parent, &new_iter);
385 if (gtk_tree_model_iter_n_children (ks.model, &parent) > 1) {
386 sort_execute_or_startupnotify_options_after_insertion (selection, &parent,
387 (streq_any (new_menu_element, "prompt", "command", NULL)) ?
388 "Execute" : "startupnotify",
389 new_menu_element);
390 }
391 }
392
393 g_signal_handler_unblock (selection, ks.handler_id_row_selected);
394
395 activate_change_done ();
396
397 return new_iter;
398 }
399
400 /*
401
402 Preperations for the addition of a new menu element and display of the "change values" screen.
403
404 */
405
add_new(gchar * new_menu_element_plus_opt_suppl)406 void add_new (gchar *new_menu_element_plus_opt_suppl)
407 {
408 gboolean same_level = TRUE; // Default
409 gchar *new_menu_element = (!g_regex_match_simple ("item.*", new_menu_element_plus_opt_suppl, 0, 0)) ?
410 new_menu_element_plus_opt_suppl : "item";
411
412 /*
413 If the selection currently points to a menu a decision has to be made to
414 either put the new element inside that menu or next to it on the same level.
415 */
416 if (STREQ (ks.txt_fields[TYPE_TXT], "menu")) {
417 GtkWidget *dialog;
418 gchar *label_txt;
419
420 gint result;
421
422 enum { INSIDE_MENU = 2, CANCEL };
423
424 label_txt = g_strdup_printf ("Insert new %s on the same level or into the currently selected menu?",
425 new_menu_element);
426
427 create_dialog (&dialog, "Same level or inside menu?", "dialog-question", label_txt,
428 "_Same level", "_Inside menu", "_Cancel", TRUE); // TRUE == dialog is shown immediately.
429
430 // Cleanup
431 g_free (label_txt);
432
433 result = gtk_dialog_run (GTK_DIALOG (dialog));
434 gtk_widget_destroy (dialog);
435 switch (result) {
436 case INSIDE_MENU:
437 same_level = FALSE;
438 break;
439 case CANCEL:
440 case GTK_RESPONSE_DELETE_EVENT:
441 return;
442 }
443 }
444
445 if (!streq_any (new_menu_element, "menu", "pipe menu", "item", NULL)) {
446 add_new_execution (new_menu_element, same_level);
447 row_selected ();
448 }
449 else {
450 gchar *field_value_pair;
451
452 gchar *change_values_markup, *default_label_txt;
453
454 gboolean menu_or_pipe_menu = (!STREQ (new_menu_element, "item"));
455
456 guint8 entry_cnt;
457
458 gtk_box_reorder_child (GTK_BOX (ks.main_box), ks.entry_grid, 0);
459
460 if (STREQ (ks.txt_fields[TYPE_TXT], "menu")) {
461 gtk_widget_show (ks.action_option_grid);
462 gtk_widget_hide (ks.options_grid);
463 gtk_widget_show (ks.new_action_option_widgets[INSIDE_MENU_LABEL]);
464 gtk_widget_show (ks.new_action_option_widgets[INSIDE_MENU_CHECK_BUTTON]);
465 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ks.new_action_option_widgets[INSIDE_MENU_CHECK_BUTTON]), !same_level);
466 field_value_pair = g_strdup_printf ("inside menu:%s", (same_level) ? "false" : "true");
467 ks.change_values_user_settings = g_slist_prepend (ks.change_values_user_settings, field_value_pair);
468 }
469 if (STREQ (new_menu_element, "item")) {
470 GtkTreeIter action_option_combo_box_iter;
471
472 gboolean incl_action;
473
474 guint8 action_cnt;
475
476
477 gtk_widget_show (ks.new_action_option_widgets[INCLUDING_ACTION_LABEL]);
478 gtk_widget_show (ks.new_action_option_widgets[INCLUDING_ACTION_CHECK_BUTTON]);
479 gtk_widget_show (ks.action_option_grid);
480 incl_action = !STREQ (new_menu_element_plus_opt_suppl, "item w/o action");
481 // Prevent callback
482 g_signal_handler_block (ks.new_action_option_widgets[INCLUDING_ACTION_CHECK_BUTTON],
483 ks.handler_id_including_action_check_button);
484 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ks.new_action_option_widgets [INCLUDING_ACTION_CHECK_BUTTON]), incl_action);
485 g_signal_handler_unblock (ks.new_action_option_widgets[INCLUDING_ACTION_CHECK_BUTTON],
486 ks.handler_id_including_action_check_button);
487 field_value_pair = g_strdup_printf ("incl. action:%s", (incl_action) ? "true" : "false");
488 ks.change_values_user_settings = g_slist_prepend (ks.change_values_user_settings, field_value_pair);
489
490 create_combo_box ();
491
492 for (action_cnt = 0; action_cnt < NUMBER_OF_ACTIONS; action_cnt++) {
493 gtk_list_store_insert_with_values (ks.action_option_combo_box_liststore, &action_option_combo_box_iter, -1,
494 ACTION_OPTION_COMBO_ITEM, ks.actions[action_cnt], -1);
495 }
496
497 if (g_regex_match_simple ("item\\+.*", new_menu_element_plus_opt_suppl, 0, 0)) {
498 gchar *action = extract_substring_via_regex (new_menu_element_plus_opt_suppl, "(?<=\\+).*");
499 gtk_combo_box_set_active_id (GTK_COMBO_BOX (ks.new_action_option_widgets[NEW_ACTION_OPTION_COMBO_BOX]), action);
500 field_value_pair = g_strdup_printf ("action:%s", action);
501 // Cleanup
502 g_free (action);
503 ks.change_values_user_settings = g_slist_prepend (ks.change_values_user_settings, field_value_pair);
504 }
505 else { // "item" or "item w/o action". The action_option combo box is set just in case the user changes his/her mind.
506 ks.change_values_user_settings = g_slist_prepend (ks.change_values_user_settings, g_strdup ("action:Execute"));
507 gtk_combo_box_set_active_id (GTK_COMBO_BOX (ks.new_action_option_widgets[NEW_ACTION_OPTION_COMBO_BOX]), "Execute");
508 if (!STREQ (new_menu_element_plus_opt_suppl, "item")) {
509 gtk_widget_hide (ks.new_action_option_widgets[NEW_ACTION_OPTION_COMBO_BOX]);
510 }
511 }
512 // Has to be after a possible setting of the action_option combo box, because this triggers showing the grid.
513 gtk_widget_set_visible (ks.options_grid, incl_action);
514 }
515 else { // menu or pipe menu
516 gtk_widget_hide (ks.options_grid);
517 }
518
519 hide_or_deactivate_widgets ();
520
521 change_values_markup = g_strdup_printf ("<span font_desc='%i'>Change values for new %s?</span>",
522 ks.font_size + 2, new_menu_element);
523 gtk_label_set_markup (GTK_LABEL (ks.change_values_label), change_values_markup);
524
525 // Cleanup
526 g_free (change_values_markup);
527
528 // Check for change of font size, so that the label's font size can be adjusted in that case.
529 if (!ks.rows_with_icons) {
530 g_timeout_add (1000, (GSourceFunc) check_for_external_file_and_settings_changes, "timeout");
531 }
532
533 gtk_widget_show (ks.change_values_label);
534 gtk_widget_hide (ks.new_action_option_widgets[ACTION_OPTION_DONE]);
535 gtk_widget_hide (ks.new_action_option_widgets[ACTION_OPTION_CANCEL]);
536 gtk_widget_show (ks.mandatory);
537 if (!STREQ (new_menu_element, "item") && !STREQ (ks.txt_fields[TYPE_TXT], "menu")) {
538 gtk_widget_set_margin_top (ks.mandatory, 5);
539 }
540 gtk_widget_show (ks.separator);
541 gtk_widget_show (ks.change_values_buttons_grid);
542 for (entry_cnt = 0; entry_cnt < NUMBER_OF_ENTRY_FIELDS; entry_cnt++) {
543 g_signal_handler_disconnect (ks.entry_fields[entry_cnt], ks.handler_id_entry_fields[entry_cnt]);
544 }
545 gtk_label_set_markup (GTK_LABEL (ks.entry_labels[MENU_ELEMENT_OR_VALUE_ENTRY]),
546 " Label (<span foreground='darkred'>*</span>): ");
547 gtk_label_set_markup (GTK_LABEL (ks.entry_labels[EXECUTE_ENTRY]),
548 " Execute (<span foreground='darkred'>*</span>): ");
549 gtk_widget_show (ks.icon_chooser);
550 gtk_widget_hide (ks.remove_icon);
551 gtk_widget_show (ks.entry_labels[ICON_PATH_ENTRY]);
552 gtk_widget_show (ks.entry_fields[ICON_PATH_ENTRY]);
553 // In case that currently a (pipe) menu or item with an incorrect iconpath is selected.
554 gtk_style_context_remove_class (gtk_widget_get_style_context (ks.entry_fields[ICON_PATH_ENTRY]), "mandatory_missing");
555 gtk_widget_set_visible (ks.entry_labels[MENU_ID_ENTRY], menu_or_pipe_menu);
556 gtk_widget_set_visible (ks.entry_fields[MENU_ID_ENTRY], menu_or_pipe_menu);
557 gtk_widget_set_visible (ks.entry_labels[EXECUTE_ENTRY], STREQ (new_menu_element, "pipe menu"));
558 gtk_widget_set_visible (ks.entry_fields[EXECUTE_ENTRY], STREQ (new_menu_element, "pipe menu"));
559
560 field_value_pair = g_strdup_printf ("new menu element:%s", new_menu_element);
561 ks.change_values_user_settings = g_slist_prepend (ks.change_values_user_settings, field_value_pair);
562 default_label_txt = g_strconcat ("New ", new_menu_element, NULL);
563 gtk_entry_set_text (GTK_ENTRY (ks.entry_fields[MENU_ELEMENT_OR_VALUE_ENTRY]), default_label_txt);
564 // In case that currently a (pipe) menu or item without label is selected.
565 gtk_widget_set_sensitive (ks.entry_fields[MENU_ELEMENT_OR_VALUE_ENTRY], TRUE);
566 // Cleanup
567 g_free (default_label_txt);
568
569 gtk_entry_set_text (GTK_ENTRY (ks.entry_fields[ICON_PATH_ENTRY]), "");
570
571 if (menu_or_pipe_menu) {
572 gchar *menu_ID;
573 guint menu_id_index = 1;
574
575 // Menu IDs have to be unique, so the list of menu IDs has to be checked for existing values.
576 while (g_slist_find_custom (ks.menu_ids, menu_ID = g_strdup_printf ("New menu %i", menu_id_index++),
577 (GCompareFunc) strcmp)) {
578 g_free (menu_ID);
579 }
580
581 field_value_pair = g_strdup_printf ("menu ID field:%s", menu_ID);
582 ks.change_values_user_settings = g_slist_prepend (ks.change_values_user_settings, field_value_pair);
583
584 gtk_entry_set_text (GTK_ENTRY (ks.entry_fields[MENU_ID_ENTRY]), menu_ID);
585 gtk_entry_set_text (GTK_ENTRY (ks.entry_fields[EXECUTE_ENTRY]), "");
586
587 // Cleanup
588 g_free (menu_ID);
589 }
590
591 gtk_widget_grab_focus (ks.entry_fields[MENU_ELEMENT_OR_VALUE_ENTRY]);
592
593 gtk_widget_show (ks.entry_grid);
594 }
595 }
596
597 /*
598
599 Creates the action/option combo box. This is done every time from scratch, since a combo box that contains new shorter items
600 doesn't shrink down in size, resulting in a bulky look of the combo box if the size change of the items is considerable.
601
602 */
603
create_combo_box(void)604 static void create_combo_box (void)
605 {
606 GtkCellRenderer *action_option_combo_box_renderer;
607
608 ks.new_action_option_widgets[NEW_ACTION_OPTION_COMBO_BOX] = gtk_combo_box_new_with_model (ks.action_option_combo_box_model);
609 action_option_combo_box_renderer = gtk_cell_renderer_text_new ();
610 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (ks.new_action_option_widgets[NEW_ACTION_OPTION_COMBO_BOX]),
611 action_option_combo_box_renderer, TRUE);
612 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (ks.new_action_option_widgets[NEW_ACTION_OPTION_COMBO_BOX]),
613 action_option_combo_box_renderer, "text", 0, NULL);
614
615 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (ks.new_action_option_widgets[NEW_ACTION_OPTION_COMBO_BOX]),
616 action_option_combo_box_renderer, option_list_with_headlines,
617 NULL, NULL); // No user data, no destroy notify for user data.*/
618
619 gtk_combo_box_set_id_column (GTK_COMBO_BOX (ks.new_action_option_widgets[NEW_ACTION_OPTION_COMBO_BOX]), 0);
620
621
622 gtk_grid_attach (GTK_GRID (ks.new_action_option_grid), ks.new_action_option_widgets[NEW_ACTION_OPTION_COMBO_BOX],
623 4, 0, 1, 1);
624
625 gtk_widget_show (ks.new_action_option_widgets[NEW_ACTION_OPTION_COMBO_BOX]);
626
627 ks.handler_id_action_option_combo_box = g_signal_connect (ks.new_action_option_widgets[NEW_ACTION_OPTION_COMBO_BOX], "changed",
628 G_CALLBACK (show_action_options), NULL);
629 }
630
631 /*
632
633 Sets up a cell layout that includes headlines that are highlighted in colour.
634
635 */
636
option_list_with_headlines(G_GNUC_UNUSED GtkCellLayout * cell_layout,GtkCellRenderer * action_option_combo_box_renderer,GtkTreeModel * action_option_combo_box_model,GtkTreeIter * action_option_combo_box_iter,G_GNUC_UNUSED gpointer data)637 void option_list_with_headlines (G_GNUC_UNUSED GtkCellLayout *cell_layout,
638 GtkCellRenderer *action_option_combo_box_renderer,
639 GtkTreeModel *action_option_combo_box_model,
640 GtkTreeIter *action_option_combo_box_iter,
641 G_GNUC_UNUSED gpointer data)
642 {
643 gchar *action_option_combo_item;
644
645 gboolean headline;
646
647 gtk_tree_model_get (action_option_combo_box_model, action_option_combo_box_iter,
648 ACTION_OPTION_COMBO_ITEM, &action_option_combo_item,
649 -1);
650
651 headline = g_regex_match_simple ("Add|Choose", action_option_combo_item, G_REGEX_ANCHORED, 0);
652
653 g_object_set (action_option_combo_box_renderer,
654 "foreground-rgba", (headline) ? &((GdkRGBA) { 1.0, 1.0, 1.0, 1.0 }) : NULL,
655 "background-rgba", (g_str_has_prefix (action_option_combo_item, "Choose")) ?
656 &((GdkRGBA) { 0.31, 0.31, 0.79, 1.0 }) :
657 ((g_str_has_prefix (action_option_combo_item, "Add")) ?
658 &((GdkRGBA) { 0.0, 0.0, 0.0, 1.0 }) : NULL),
659 "sensitive", !headline,
660 NULL);
661
662 // Cleanup
663 g_free (action_option_combo_item);
664 }
665
666 /*
667
668 Appends possible options for Execute or startupnotify to combobox.
669
670 */
671
generate_action_option_combo_box_items_for_Exe_and_snotify_opts(gchar * options_type)672 static guint8 generate_action_option_combo_box_items_for_Exe_and_snotify_opts (gchar *options_type)
673 {
674 gboolean execute = STREQ (options_type, "Execute");
675 GtkTreeIter parent, action_option_combo_box_new_iter;
676 const guint8 number_of_opts = (execute) ? NUMBER_OF_EXECUTE_OPTS : NUMBER_OF_STARTUPNOTIFY_OPTS;
677 const guint allocated_size = sizeof (gboolean) * number_of_opts;
678 gboolean *opts_exist = g_slice_alloc0 (allocated_size); // Initialise all elements to FALSE.
679
680 guint8 number_of_added_opts = 0; // Start value
681 guint8 opts_cnt;
682
683 if (STREQ (ks.txt_fields[MENU_ELEMENT_TXT], options_type)) { // "Execute" or "startupnotify"
684 parent = ks.iter;
685 }
686 else {
687 gtk_tree_model_iter_parent (ks.model, &parent, &ks.iter);
688 }
689
690 check_for_existing_options (&parent, number_of_opts, (execute) ? ks.execute_options : ks.startupnotify_options, opts_exist);
691
692 for (opts_cnt = 0; opts_cnt < number_of_opts; opts_cnt++) {
693 if (!opts_exist[opts_cnt]) {
694 gtk_list_store_insert_with_values (ks.action_option_combo_box_liststore, &action_option_combo_box_new_iter, -1,
695 ACTION_OPTION_COMBO_ITEM, (execute) ?
696 ks.execute_displayed_txts[opts_cnt] : ks.startupnotify_displayed_txts[opts_cnt],
697 -1);
698 number_of_added_opts++;
699 }
700 }
701
702 // Cleanup
703 g_slice_free1 (allocated_size, opts_exist);
704
705 return number_of_added_opts;
706 }
707
708 /*
709
710 Shows the action combo box, to which a dynamically generated list of options has been added.
711
712 */
713
generate_items_for_action_option_combo_box(gchar * preset_choice)714 void generate_items_for_action_option_combo_box (gchar *preset_choice)
715 {
716 GtkTreeIter action_option_combo_box_iter;
717
718 const gchar *bt_add_action_option_label_txt;
719
720 gchar *add_inside_txt = NULL; // Initialization avoids compiler warning.
721
722 guint8 number_of_added_actions = 0, number_of_added_action_opts = 0, number_of_added_snotify_opts = 0; // Defaults
723
724
725 // --- Creating combo box and building combo list. ---
726
727
728 create_combo_box ();
729
730 // Generate "Choose..." headline according to the text of bt_add_action_option_label.
731 bt_add_action_option_label_txt = gtk_label_get_text (GTK_LABEL (ks.bt_add_action_option_label));
732 gtk_list_store_insert_with_values (ks.action_option_combo_box_liststore, &action_option_combo_box_iter, -1,
733 ACTION_OPTION_COMBO_ITEM,
734 (STREQ (bt_add_action_option_label_txt, "Action")) ? "Choose an action" :
735 (STREQ (bt_add_action_option_label_txt, "Option")) ?
736 "Choose an option" : "Choose an action/option",
737 -1);
738
739 // Startupnotify options: enabled, name, wmclass, icon
740 if (streq_any (ks.txt_fields[TYPE_TXT], "option", "option block", NULL) &&
741 streq_any (ks.txt_fields[MENU_ELEMENT_TXT], "startupnotify", "enabled", "name", "wmclass", "icon", NULL)) {
742 number_of_added_snotify_opts = generate_action_option_combo_box_items_for_Exe_and_snotify_opts ("startupnotify");
743 }
744
745 // Execute options: prompt, command and startupnotify
746 if ((STREQ (ks.txt_fields[TYPE_TXT], "action") && STREQ (ks.txt_fields[MENU_ELEMENT_TXT], "Execute")) ||
747 (streq_any (ks.txt_fields[TYPE_TXT], "option", "option block", NULL) &&
748 streq_any (ks.txt_fields[MENU_ELEMENT_TXT], "prompt", "command", "startupnotify", NULL))) {
749 number_of_added_action_opts = generate_action_option_combo_box_items_for_Exe_and_snotify_opts ("Execute");
750 }
751
752 // Exit and SessionLogout option: prompt, Restart option: command
753 if (STREQ (ks.txt_fields[TYPE_TXT], "action") &&
754 streq_any (ks.txt_fields[MENU_ELEMENT_TXT], "Exit", "Restart", "SessionLogout", NULL) &&
755 !gtk_tree_model_iter_has_child (ks.model, &ks.iter)) {
756 gtk_list_store_insert_with_values (ks.action_option_combo_box_liststore, &action_option_combo_box_iter, -1,
757 ACTION_OPTION_COMBO_ITEM,
758 (STREQ (ks.txt_fields[MENU_ELEMENT_TXT], "Restart")) ? "Command" : "Prompt",
759 -1);
760 number_of_added_action_opts = 1;
761 }
762
763 // Actions
764 if (streq_any (ks.txt_fields[TYPE_TXT], "item", "action", NULL)) {
765 for (; number_of_added_actions < NUMBER_OF_ACTIONS; number_of_added_actions++) {
766 gtk_list_store_insert_with_values (ks.action_option_combo_box_liststore, &action_option_combo_box_iter, -1,
767 ACTION_OPTION_COMBO_ITEM, ks.actions[number_of_added_actions],
768 -1);
769 }
770 }
771
772 /*
773 Check if there are two kinds of addable actions/options: the first one inside the current selection,
774 the second one next to the current selection. If so, add headlines to the list to separate them from each other.
775 */
776 if (number_of_added_snotify_opts && number_of_added_action_opts) {
777 add_inside_txt = "Add inside startupnotify";
778 }
779 else if (number_of_added_action_opts && number_of_added_actions) {
780 add_inside_txt = g_strdup_printf ("Add inside currently selected %s action", ks.txt_fields[MENU_ELEMENT_TXT]);
781 }
782
783 if (add_inside_txt) {
784 gtk_list_store_insert_with_values (ks.action_option_combo_box_liststore, &action_option_combo_box_iter, 1,
785 ACTION_OPTION_COMBO_ITEM, add_inside_txt,
786 -1);
787
788 /*
789 Position of "Add additional action" headline:
790 "Add inside currently selected xxx action" + number of added action options + "Add additional action" ==
791 1 + number_of_added_action_opts + 1 -> number_of_added_action_opts + 2
792 Position of "Add next to startupnotify" headline:
793 "Add inside startupnotify" + number of added startupnotify options + "Add next to startupnotify" ==
794 1 + number_of_added_snotify_opts + 1 -> number_of_added_snotify_opts + 2
795 */
796 gtk_list_store_insert_with_values (ks.action_option_combo_box_liststore, &action_option_combo_box_iter,
797 ((number_of_added_actions) ?
798 number_of_added_action_opts : number_of_added_snotify_opts) + 2,
799 ACTION_OPTION_COMBO_ITEM, (number_of_added_actions) ?
800 "Add additional action" : "Add next to startupnotify",
801 -1);
802
803 // Cleanup
804 if (number_of_added_actions) {
805 g_free (add_inside_txt);
806 }
807 }
808
809 // Show the action combo box and deactivate or hide all events and widgets that could interfere.
810 hide_or_deactivate_widgets ();
811
812 gtk_widget_show (ks.action_option_grid);
813 gtk_widget_hide (ks.options_grid);
814
815 /*
816 If this function was called by the context menu or a "Startupnotify" button,
817 preselect the appropriate combo box item.
818 */
819 if (preset_choice) {
820 gtk_combo_box_set_active_id (GTK_COMBO_BOX (ks.new_action_option_widgets[NEW_ACTION_OPTION_COMBO_BOX]), preset_choice);
821 // If there is only one choice, there is nothing to choose from the combo box.
822 if (gtk_tree_model_iter_n_children (ks.action_option_combo_box_model, NULL) == 2) {
823 gtk_widget_set_sensitive (ks.new_action_option_widgets[NEW_ACTION_OPTION_COMBO_BOX], FALSE);
824 }
825 }
826 else {
827 gtk_combo_box_set_active (GTK_COMBO_BOX (ks.new_action_option_widgets[NEW_ACTION_OPTION_COMBO_BOX]), 0);
828 gtk_widget_hide (ks.new_action_option_widgets[ACTION_OPTION_DONE]);
829 }
830
831 if (!gtk_widget_get_visible (ks.change_values_label)) {
832 gtk_widget_set_margin_top (ks.new_action_option_grid, 0);
833 }
834
835 gtk_widget_queue_draw (GTK_WIDGET (ks.treeview)); // Force redrawing of treeview (if a search was active).
836 }
837
838 /*
839
840 Shows the fields that may be changed according to the chosen action/option.
841
842 */
843
show_action_options(void)844 void show_action_options (void)
845 {
846 const gchar *combo_choice = gtk_combo_box_get_active_id (GTK_COMBO_BOX (ks.new_action_option_widgets[NEW_ACTION_OPTION_COMBO_BOX]));
847 gboolean with_new_item = gtk_widget_get_visible (ks.new_action_option_widgets[INCLUDING_ACTION_CHECK_BUTTON]);
848
849 guint8 options_cnt, snotify_opts_cnt;
850
851 clear_entries ();
852
853 // Defaults
854 gtk_widget_set_margin_bottom (ks.action_option_grid, 5);
855 gtk_widget_set_visible (ks.options_grid, !STREQ (combo_choice, "Reconfigure"));
856 for (options_cnt = 0; options_cnt < NUMBER_OF_EXECUTE_OPTS; options_cnt++) {
857 gtk_widget_hide (ks.options_labels[options_cnt]);
858 gtk_widget_hide (ks.options_fields[options_cnt]);
859 }
860 gtk_widget_hide (ks.suboptions_grid);
861 for (snotify_opts_cnt = 0; snotify_opts_cnt < NUMBER_OF_STARTUPNOTIFY_OPTS; snotify_opts_cnt++) {
862 gtk_widget_show (ks.suboptions_labels[snotify_opts_cnt]);
863 gtk_widget_show (ks.suboptions_fields[snotify_opts_cnt]);
864 }
865 gtk_label_set_text (GTK_LABEL (ks.options_labels[PROMPT]), " Prompt: ");
866 if (!with_new_item) {
867 gtk_widget_show (ks.new_action_option_widgets[ACTION_OPTION_DONE]);
868 gtk_widget_hide (ks.mandatory);
869 }
870 gtk_widget_set_margin_bottom (ks.mandatory, (with_new_item) ? 0 : 5);
871 gtk_widget_grab_focus ((with_new_item) ? ks.entry_fields[MENU_ELEMENT_OR_VALUE_ENTRY] : ks.options_fields[PROMPT]);
872
873 if (streq_any (combo_choice, "Execute", "Startupnotify", "Enabled", "Name", "WM_CLASS", "Icon", NULL)) {
874 gchar *suboptions_label_txt;
875
876 for (snotify_opts_cnt = 0; snotify_opts_cnt < NUMBER_OF_STARTUPNOTIFY_OPTS; snotify_opts_cnt++) {
877 suboptions_label_txt = g_strconcat (ks.startupnotify_displayed_txts[snotify_opts_cnt],
878 streq_any (combo_choice, "Execute", "Startupnotify", "Enabled", NULL) ? ": " :
879 " (<span foreground='darkred'>*</span>): ",
880 NULL);
881 gtk_label_set_markup (GTK_LABEL (ks.suboptions_labels[snotify_opts_cnt]), suboptions_label_txt);
882
883 // Cleanup
884 g_free (suboptions_label_txt);
885 }
886 }
887
888 // "Execute" action
889 if (STREQ (combo_choice, "Execute")) {
890 for (options_cnt = 0; options_cnt < NUMBER_OF_EXECUTE_OPTS; options_cnt++) {
891 gtk_label_set_markup (GTK_LABEL (ks.options_labels[options_cnt]), (options_cnt != COMMAND) ?
892 ks.options_label_txts[options_cnt] :
893 " Command (<span foreground='darkred'>*</span>): ");
894 gtk_widget_show (ks.options_labels[options_cnt]);
895 gtk_widget_show (ks.options_fields[options_cnt]);
896 }
897 gtk_widget_show (ks.mandatory);
898 if (!g_signal_handler_is_connected (ks.options_fields[SN_OR_PROMPT], ks.handler_id_show_or_hide_startupnotify_options)) {
899 ks.handler_id_show_or_hide_startupnotify_options = g_signal_connect (ks.options_fields[SN_OR_PROMPT], "toggled",
900 G_CALLBACK (show_or_hide_startupnotify_options), NULL);
901 }
902 }
903
904 // "Restart" action or "command" option (the latter for "Restart" and "Execute" actions)
905 if (streq_any (combo_choice, "Restart", "Command", NULL)) {
906 gtk_label_set_markup (GTK_LABEL (ks.options_labels[COMMAND]), (STREQ (combo_choice, "Restart")) ?
907 " Command: " :
908 " Command (<span foreground='darkred'>*</span>): ");
909 gtk_widget_show (ks.options_labels[COMMAND]);
910 gtk_widget_show (ks.options_fields[COMMAND]);
911 if (STREQ (combo_choice, "Command")) {
912 gtk_widget_show (ks.mandatory);
913 }
914 gtk_widget_grab_focus (ks.options_fields[COMMAND]);
915 }
916
917 // "Exit" or "SessionLogout" action or "prompt" option (the latter for "Execute", "Exit" and "SessionLogout" actions)
918 if (streq_any (combo_choice, "Exit", "Prompt", "SessionLogout", NULL)) {
919 if (!STREQ (combo_choice, "Prompt") ||
920 (STREQ (combo_choice, "Prompt") && streq_any (ks.txt_fields[MENU_ELEMENT_TXT], "Exit", "SessionLogout", NULL))) {
921 gtk_label_set_text (GTK_LABEL (ks.options_labels[SN_OR_PROMPT]), " Prompt: ");
922 gtk_widget_show (ks.options_labels[SN_OR_PROMPT]);
923 if (g_signal_handler_is_connected (ks.options_fields[SN_OR_PROMPT], ks.handler_id_show_or_hide_startupnotify_options)) {
924 g_signal_handler_disconnect (ks.options_fields[SN_OR_PROMPT], ks.handler_id_show_or_hide_startupnotify_options);
925 }
926 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ks.options_fields[SN_OR_PROMPT]), TRUE);
927 gtk_widget_show (ks.options_fields[SN_OR_PROMPT]);
928 gtk_widget_grab_focus (ks.options_fields[SN_OR_PROMPT]);
929 }
930 else { // Option of "Execute" action.
931 gtk_widget_show (ks.options_labels[PROMPT]);
932 gtk_label_set_markup (GTK_LABEL (ks.options_labels[PROMPT]), " Prompt (<span foreground='darkred'>*</span>): ");
933 gtk_widget_show (ks.options_fields[PROMPT]);
934 gtk_widget_show (ks.mandatory);
935
936 }
937 }
938
939 // "startupnotify" option and its suboptions
940 if (streq_any (combo_choice, "Startupnotify", "Enabled", "Name", "WM_CLASS", "Icon", NULL)) {
941 gtk_widget_show (ks.suboptions_grid);
942
943 if (STREQ (combo_choice, "Startupnotify")) {
944 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ks.suboptions_fields[ENABLED]), TRUE);
945 gtk_widget_grab_focus (ks.suboptions_fields[ENABLED]);
946 }
947 else { // Enabled, Name, WM_CLASS & Icon
948 for (snotify_opts_cnt = 0; snotify_opts_cnt < NUMBER_OF_STARTUPNOTIFY_OPTS; snotify_opts_cnt++) {
949 if (!STREQ (combo_choice, ks.startupnotify_displayed_txts[snotify_opts_cnt])) {
950 gtk_widget_hide (ks.suboptions_labels[snotify_opts_cnt]);
951 gtk_widget_hide (ks.suboptions_fields[snotify_opts_cnt]);
952 }
953 else {
954 gtk_widget_grab_focus (ks.suboptions_fields[snotify_opts_cnt]);
955 }
956 }
957 if (!STREQ (combo_choice, "Enabled")) {
958 gtk_widget_show (ks.mandatory);
959 }
960 }
961 }
962 }
963
964 /*
965
966 Shows the options for startupnotify after the corresponding check box has been clicked.
967
968 */
969
show_or_hide_startupnotify_options(void)970 void show_or_hide_startupnotify_options (void)
971 {
972 gboolean show_startupnotify_options = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ks.options_fields[SN_OR_PROMPT]));
973
974 gtk_widget_set_visible (ks.suboptions_grid, show_startupnotify_options);
975 if (!show_startupnotify_options) {
976 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ks.suboptions_fields[ENABLED]), TRUE);
977 gtk_entry_set_text (GTK_ENTRY (ks.suboptions_fields[NAME]), "");
978 gtk_entry_set_text (GTK_ENTRY (ks.suboptions_fields[WM_CLASS]), "");
979 gtk_entry_set_text (GTK_ENTRY (ks.suboptions_fields[ICON]), "");
980 }
981 }
982
983 /*
984
985 If only a single entry field is shown, "Enter" is enough, clicking on "Done" is not necessary.
986
987 */
988
single_field_entry(void)989 void single_field_entry (void)
990 {
991 /*
992 Applies to
993 - the _single_ "Execute" options "command" and "prompt"
994 - the _single_ "startupnotify" options "name", "wmclass" and "icon"
995 - the "Restart" action and its option "command".
996
997 If action fields are shown in conjunctin if item fields, single field entry is not executed.
998 */
999 if (!gtk_widget_get_visible (ks.new_action_option_widgets[INCLUDING_ACTION_CHECK_BUTTON]) &&
1000 !streq_any (gtk_combo_box_get_active_id (GTK_COMBO_BOX (ks.new_action_option_widgets[NEW_ACTION_OPTION_COMBO_BOX])),
1001 "Execute", "Startupnotify", NULL)) {
1002 action_option_insert ("by combo box");
1003 }
1004 }
1005
1006 /*
1007
1008 Resets everything after the cancel button has been clicked or an insertion has been done.
1009
1010 */
1011
hide_action_option_grid(gchar * origin)1012 void hide_action_option_grid (gchar *origin)
1013 {
1014 clear_entries ();
1015 gtk_widget_set_sensitive (ks.mb_options, TRUE);
1016 gtk_widget_hide (ks.action_option_grid);
1017 gtk_widget_set_margin_bottom (ks.action_option_grid, 0);
1018 gtk_widget_set_margin_top (ks.new_action_option_grid, 5);
1019
1020 gtk_widget_show (ks.button_grid);
1021 if (*ks.search_term->str) {
1022 gtk_widget_show (ks.find_grid);
1023 }
1024 gtk_widget_hide (ks.mandatory);
1025 gtk_widget_set_margin_bottom (ks.mandatory, 0);
1026
1027 if (STREQ (origin, "cancel button")) {
1028 row_selected (); // Reset visibility of menu bar and toolbar items.
1029 }
1030 }
1031
1032 /*
1033
1034 Clears all entries for actions/options.
1035
1036 */
1037
clear_entries(void)1038 static void clear_entries (void)
1039 {
1040 guint8 options_cnt, snotify_opts_cnt;
1041
1042 for (options_cnt = PROMPT; options_cnt <= COMMAND; options_cnt++) {
1043 gtk_entry_set_text (GTK_ENTRY (ks.options_fields[options_cnt]), "");
1044 gtk_style_context_remove_class (gtk_widget_get_style_context (ks.options_fields[options_cnt]), "mandatory_missing");
1045 }
1046 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ks.options_fields[SN_OR_PROMPT]), FALSE);
1047 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ks.suboptions_fields[ENABLED]), TRUE);
1048 for (snotify_opts_cnt = NAME; snotify_opts_cnt < NUMBER_OF_STARTUPNOTIFY_OPTS; snotify_opts_cnt++) {
1049 gtk_entry_set_text (GTK_ENTRY (ks.suboptions_fields[snotify_opts_cnt]), "");
1050 gtk_style_context_remove_class (gtk_widget_get_style_context (ks.suboptions_fields[snotify_opts_cnt]),
1051 "mandatory_missing");
1052 }
1053 }
1054
1055 /*
1056
1057 Inserts an action and/or its option(s) with the entered values.
1058
1059 */
1060
action_option_insert(gchar * origin)1061 void action_option_insert (gchar *origin)
1062 {
1063 const gchar *combo_choice = (STREQ (origin, "by combo box")) ?
1064 gtk_combo_box_get_active_id (GTK_COMBO_BOX (ks.new_action_option_widgets[NEW_ACTION_OPTION_COMBO_BOX])) :
1065 "Reconfigure";
1066 gboolean options_check_button_state = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ks.options_fields[SN_OR_PROMPT]));
1067 gboolean enabled_check_button_state = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (ks.suboptions_fields[ENABLED]));
1068 const gchar *options_entries[] = { gtk_entry_get_text (GTK_ENTRY (ks.options_fields[PROMPT])),
1069 gtk_entry_get_text (GTK_ENTRY (ks.options_fields[COMMAND])) };
1070 const gchar *suboption_entry = NULL; // Initialization avoids compiler warning.
1071
1072 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ks.treeview));
1073 GtkTreeIter new_iter, new_iter2, parent;
1074 GtkTreeIter execute_parent = ks.iter; // Initialization avoids compiler warning.
1075 GtkTreeIter execute_iter;
1076
1077 gint insertion_position = -1; // Default
1078
1079 // Defaults
1080 gboolean action = FALSE;
1081 gboolean execute_done = FALSE;
1082 gboolean startupnotify_done = FALSE;
1083 gchar *option_of_execute = NULL, *option_of_startupnotify = NULL;
1084
1085 // Defaults
1086 gboolean used_Execute_opts[NUMBER_OF_EXECUTE_OPTS] = { FALSE };
1087 guint8 snotify_start = NAME;
1088
1089 guint8 execute_opts_cnt, snotify_opts_cnt;
1090
1091
1092 // If only one entry or a mandatory field was displayed, check if it has been filled out.
1093 if (gtk_widget_get_visible (ks.action_option_grid)) {
1094 if (streq_any (combo_choice, "Execute", "Command", NULL) && !(*options_entries[COMMAND])) {
1095 wrong_or_missing (ks.options_fields[COMMAND], ks.execute_options_css_providers[COMMAND]);
1096 return;
1097 }
1098 else if (STREQ (combo_choice, "Prompt") && // "Execute" action or option of it currently selected.
1099 gtk_widget_get_visible (ks.options_fields[PROMPT]) && !(*options_entries[PROMPT])) {
1100 wrong_or_missing (ks.options_fields[PROMPT], ks.execute_options_css_providers[PROMPT]);
1101 return;
1102 }
1103 else {
1104 for (snotify_opts_cnt = NAME; snotify_opts_cnt < NUMBER_OF_STARTUPNOTIFY_OPTS; snotify_opts_cnt++) {
1105 if (STREQ (combo_choice, ks.startupnotify_displayed_txts[snotify_opts_cnt]) &&
1106 !(*gtk_entry_get_text (GTK_ENTRY (ks.suboptions_fields[snotify_opts_cnt])))) {
1107 // "Enabled" is not part of the css_providers, thus - 1.
1108 wrong_or_missing (ks.suboptions_fields[snotify_opts_cnt], ks.suboptions_fields_css_providers[snotify_opts_cnt - 1]);
1109 return;
1110 }
1111 }
1112 }
1113 }
1114
1115 gtk_widget_hide (ks.mandatory);
1116 gtk_widget_set_margin_bottom (ks.mandatory, 0);
1117
1118 /*
1119 Check if the insertion will be done at the current level and before the last child of the current parent.
1120 If so, set the insertion position.
1121 */
1122 if (!(STREQ (ks.txt_fields[TYPE_TXT], "item") ||
1123 (STREQ (ks.txt_fields[TYPE_TXT], "action") &&
1124 streq_any (combo_choice, "Prompt", "Command", "Startupnotify", NULL)) ||
1125 (STREQ (ks.txt_fields[TYPE_TXT], "option block") &&
1126 !streq_any (combo_choice, "Prompt", "Command", NULL)))) {
1127 GtkTreePath *path = gtk_tree_model_get_path (ks.model, &ks.iter);
1128 gint last_path_index = gtk_tree_path_get_indices (path)[gtk_tree_path_get_depth (path) - 1];
1129
1130 gtk_tree_model_iter_parent (ks.model, &parent, &ks.iter);
1131 if (gtk_tree_model_iter_n_children (ks.model, &parent) > 1 &&
1132 last_path_index < gtk_tree_model_iter_n_children (ks.model, &parent) - 1) {
1133 insertion_position = last_path_index + 1;
1134 }
1135
1136 // Cleanup
1137 gtk_tree_path_free (path);
1138 }
1139
1140
1141 // --- Create the new row(s). ---
1142
1143
1144 // Prevents that the default check for change of selection(s) gets in the way.
1145 g_signal_handler_block (selection, ks.handler_id_row_selected);
1146 gtk_tree_selection_unselect_all (selection); // The old selection will be replaced by the one of the new row.
1147
1148 // Execute
1149 if (STREQ (combo_choice, "Execute")) {
1150 if (STREQ (ks.txt_fields[TYPE_TXT], "action")) {
1151 gtk_tree_model_iter_parent (ks.model, &parent, &ks.iter);
1152 ks.iter = parent; // Move up one level and start from there.
1153 }
1154
1155 gtk_tree_store_insert_with_values (ks.treestore, &new_iter, &ks.iter, insertion_position,
1156 TS_MENU_ELEMENT, "Execute",
1157 TS_TYPE, "action",
1158 -1);
1159
1160 execute_parent = ks.iter;
1161 execute_iter = new_iter;
1162 ks.iter = new_iter; // New base level
1163
1164 action = TRUE;
1165 execute_done = TRUE;
1166 insertion_position = -1; // Reset for the options, since they should be always appended.
1167 }
1168
1169 // Prompt - only if it is an Execute option and not empty.
1170 if ((execute_done || STREQ (combo_choice, "Prompt")) && *options_entries[PROMPT]) {
1171 used_Execute_opts[PROMPT] = TRUE;
1172 }
1173
1174 // Command - only if it is an Execute option.
1175 if ((execute_done ||
1176 (STREQ (combo_choice, "Command") && !STREQ (ks.txt_fields[MENU_ELEMENT_TXT], "Restart")))) {
1177 used_Execute_opts[COMMAND] = TRUE;
1178 }
1179
1180 // Startupnotify
1181 if ((execute_done && options_check_button_state) || STREQ (combo_choice, "Startupnotify")) {
1182 used_Execute_opts[STARTUPNOTIFY] = TRUE;
1183 }
1184
1185 // Insert Execute option, if used.
1186 for (execute_opts_cnt = 0; execute_opts_cnt < NUMBER_OF_EXECUTE_OPTS; execute_opts_cnt++) {
1187 if (used_Execute_opts[execute_opts_cnt]) {
1188 if (streq_any (ks.txt_fields[TYPE_TXT], "option", "option block", NULL)) {
1189 gtk_tree_model_iter_parent (ks.model, &parent, &ks.iter);
1190 ks.iter = parent; // Move up one level and start from there.
1191 }
1192 gtk_tree_store_insert_with_values (ks.treestore, &new_iter, &ks.iter, insertion_position,
1193 TS_MENU_ELEMENT, ks.execute_options[execute_opts_cnt],
1194 TS_TYPE, (execute_opts_cnt != STARTUPNOTIFY) ? "option" : "option block",
1195 TS_VALUE, (execute_opts_cnt != STARTUPNOTIFY) ?
1196 options_entries[execute_opts_cnt] : NULL,
1197 -1);
1198
1199 if (!execute_done) { // Single option, not via an insertion of an "Execute" action.
1200 option_of_execute = ks.execute_options[execute_opts_cnt];
1201 expand_row_from_iter (&ks.iter);
1202 gtk_tree_selection_select_iter (selection, &new_iter);
1203 if (execute_opts_cnt == STARTUPNOTIFY) {
1204 startupnotify_done = TRUE;
1205 }
1206 }
1207 }
1208 }
1209
1210 // Setting parent iterator for startupnotify options, if they are added as single elements.
1211 if (streq_any (ks.txt_fields[TYPE_TXT], "option", "option block", NULL) &&
1212 !streq_any (ks.txt_fields[MENU_ELEMENT_TXT], "prompt", "command", NULL)) {
1213 if (STREQ (ks.txt_fields[MENU_ELEMENT_TXT], "startupnotify")) {
1214 new_iter = ks.iter;
1215 }
1216 else {
1217 gtk_tree_model_iter_parent (ks.model, &new_iter, &ks.iter);
1218 }
1219 }
1220
1221 // Enabled
1222 if ((execute_done && options_check_button_state) || streq_any (combo_choice, "Startupnotify", "Enabled", NULL)) {
1223 snotify_start = ENABLED;
1224 }
1225
1226 // Insert startupnotify option, if used.
1227 for (snotify_opts_cnt = snotify_start; snotify_opts_cnt < NUMBER_OF_STARTUPNOTIFY_OPTS; snotify_opts_cnt++) {
1228 if (snotify_opts_cnt == ENABLED ||
1229 *(suboption_entry = gtk_entry_get_text (GTK_ENTRY (ks.suboptions_fields[snotify_opts_cnt])))) {
1230 gtk_tree_store_insert_with_values (ks.treestore, &new_iter2, &new_iter, insertion_position,
1231 TS_MENU_ELEMENT, ks.startupnotify_options[snotify_opts_cnt],
1232 TS_TYPE, "option",
1233 TS_VALUE, (snotify_opts_cnt == ENABLED) ?
1234 ((enabled_check_button_state) ? "yes" : "no") :
1235 suboption_entry,
1236 -1);
1237
1238 expand_row_from_iter (&new_iter);
1239 if (!execute_done && !startupnotify_done) { // Single option.
1240 option_of_startupnotify = ks.startupnotify_options[snotify_opts_cnt];
1241 gtk_tree_selection_select_iter (selection, &new_iter2);
1242 }
1243 }
1244 }
1245
1246 // Action other than Execute
1247 if (streq_any (combo_choice, "Exit", "Reconfigure", "Restart", "SessionLogout", NULL)) {
1248 if (STREQ (ks.txt_fields[TYPE_TXT], "action")) {
1249 gtk_tree_model_iter_parent (ks.model, &parent, &ks.iter);
1250 ks.iter = parent; // Move up one level and start from there.
1251 }
1252
1253 gtk_tree_store_insert_with_values (ks.treestore, &new_iter, &ks.iter, insertion_position,
1254 TS_MENU_ELEMENT, combo_choice,
1255 TS_TYPE, "action",
1256 -1);
1257
1258 if (!STREQ (combo_choice, "Reconfigure") && !(STREQ (combo_choice, "Restart") && !(*options_entries[COMMAND]))) {
1259 gtk_tree_store_insert_with_values (ks.treestore, &new_iter2, &new_iter, -1,
1260 TS_MENU_ELEMENT, (STREQ (combo_choice, "Restart")) ? "command" : "prompt",
1261 TS_TYPE, "option",
1262 TS_VALUE, (STREQ (combo_choice, "Restart")) ? options_entries[COMMAND] :
1263 ((options_check_button_state) ? "yes" : "no"),
1264 -1);
1265 }
1266
1267 action = TRUE;
1268 }
1269
1270 // Exit & SessionLogout option (prompt) or Restart option (command)
1271 if ((STREQ (combo_choice, "Prompt") && gtk_widget_get_visible (ks.options_fields[SN_OR_PROMPT])) ||
1272 (STREQ (combo_choice, "Command") && STREQ (ks.txt_fields [MENU_ELEMENT_TXT], "Restart"))) {
1273 gtk_tree_store_insert_with_values (ks.treestore, &new_iter, &ks.iter, -1,
1274 TS_MENU_ELEMENT, (STREQ (combo_choice, "Prompt")) ? "prompt" : "command",
1275 TS_TYPE, "option",
1276 TS_VALUE, (STREQ (combo_choice, "Command")) ? options_entries[COMMAND] :
1277 ((options_check_button_state) ? "yes" : "no"),
1278 -1);
1279
1280 expand_row_from_iter (&ks.iter);
1281 gtk_tree_selection_select_iter (selection, &new_iter);
1282 }
1283
1284 /*
1285 Show all children of the new action. If the parent row had not been expanded, expand it is well,
1286 but leave all preceding nodes (if any) of the new action collapsed.
1287 */
1288 if (action) {
1289 if (execute_done) {
1290 ks.iter = execute_parent;
1291 }
1292 GtkTreePath *parent_path = gtk_tree_model_get_path (ks.model, &ks.iter);
1293 GtkTreePath *path = gtk_tree_model_get_path (ks.model, (execute_done) ? &execute_iter : &new_iter);
1294
1295 if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (ks.treeview), parent_path)) {
1296 gtk_tree_view_expand_row (GTK_TREE_VIEW (ks.treeview), parent_path, FALSE);
1297 }
1298 // TRUE == expand recursively, this is for a new Execute action with startupnotify options, so the latter are shown.
1299 gtk_tree_view_expand_row (GTK_TREE_VIEW (ks.treeview), path, TRUE);
1300
1301 if (!gtk_widget_get_visible (ks.change_values_label)) {
1302 gtk_tree_selection_select_path (selection, path);
1303 }
1304
1305 // Cleanup
1306 gtk_tree_path_free (parent_path);
1307 gtk_tree_path_free (path);
1308 }
1309
1310 if (ks.autosort_options) {
1311 // If the new row is an option of Execute, sort all options of the latter and move the selection to the new option.
1312 if (option_of_execute && gtk_tree_model_iter_n_children (ks.model, &ks.iter) > 1) {
1313 sort_execute_or_startupnotify_options_after_insertion (selection, &ks.iter, "Execute", option_of_execute);
1314 }
1315
1316 // The same for startupnotify. new_iter always points to an "option block" == startupnotify.
1317 if (option_of_startupnotify && gtk_tree_model_iter_n_children (ks.model, &new_iter) > 1) {
1318 sort_execute_or_startupnotify_options_after_insertion (selection, &new_iter,
1319 "startupnotify", option_of_startupnotify);
1320 }
1321 }
1322
1323 hide_action_option_grid ("action option insert");
1324
1325 g_signal_handler_unblock (selection, ks.handler_id_row_selected);
1326 activate_change_done ();
1327 if (!gtk_widget_get_visible (ks.change_values_label)) {
1328 row_selected ();
1329 }
1330 }
1331
1332 /*
1333
1334 Checks for menus or pipe menus that are descendants of the currently processed row.
1335 If found, their menu ID is passed to a function that removes this ID from the list of menu IDs.
1336
1337 */
1338
check_for_menus(GtkTreeModel * filter_model,G_GNUC_UNUSED GtkTreePath * filter_path,GtkTreeIter * filter_iter)1339 static gboolean check_for_menus ( GtkTreeModel *filter_model,
1340 G_GNUC_UNUSED GtkTreePath *filter_path,
1341 GtkTreeIter *filter_iter)
1342 {
1343 gchar *type_txt_filter, *menu_id_txt_filter;
1344
1345 gtk_tree_model_get (filter_model, filter_iter, TS_TYPE, &type_txt_filter, -1);
1346
1347 if (streq_any (type_txt_filter, "menu", "pipe menu", NULL)) {
1348 gtk_tree_model_get (filter_model, filter_iter, TS_MENU_ID, &menu_id_txt_filter, -1);
1349
1350 remove_menu_id (menu_id_txt_filter);
1351
1352 // Cleanup
1353 g_free (menu_id_txt_filter);
1354 }
1355
1356 // Cleanup
1357 g_free (type_txt_filter);
1358
1359 return FALSE;
1360 }
1361
1362 /*
1363
1364 Removes a menu ID from the list of menu IDs.
1365
1366 */
1367
remove_menu_id(gchar * menu_id)1368 void remove_menu_id (gchar *menu_id)
1369 {
1370 GSList *to_be_removed_element = g_slist_find_custom (ks.menu_ids, menu_id, (GCompareFunc) strcmp);
1371
1372 g_free (to_be_removed_element->data);
1373 ks.menu_ids = g_slist_remove (ks.menu_ids, to_be_removed_element->data);
1374 }
1375
1376 /*
1377
1378 Removes all children of a node.
1379
1380 Children are removed from bottom to top so the paths of the other children and nodes are kept.
1381 After the removal of the children the function checks for the existence of each parent
1382 before it reselects them, because if subnodes of the selected nodes had also been selected,
1383 they can't be reselected again since they got removed.
1384
1385 */
1386
remove_all_children(void)1387 void remove_all_children (void)
1388 {
1389 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ks.treeview));
1390 /*
1391 The list of rows is reverted, since if a top to bottom direction was kept and
1392 children of selected nodes was also selected, these children would be selected for removal first,
1393 but since their own children are also intended for removal, they are unselected again.
1394
1395 Example:
1396
1397 Selection done by user:
1398
1399 -menu ::: selected for removal of children
1400 -menu
1401 -menu ::: selected for removal of child
1402 -menu
1403 -menu
1404
1405 First selection loop run:
1406
1407 -menu 1. element whose children are selected
1408 -menu ::: selected
1409 -menu ::: selected
1410 -menu
1411 -menu ::: selected
1412
1413 Second selection loop run:
1414
1415 -menu
1416 -menu ::: selected
1417 -menu -> 2. element whose children are selected, unintended unselection
1418 -menu ::: selected
1419 -menu ::: selected
1420 */
1421 GList *selected_rows = g_list_reverse (gtk_tree_selection_get_selected_rows (selection, &ks.model));
1422
1423 GList *selected_rows_loop;
1424 GtkTreePath *path_loop;
1425 GtkTreeIter iter_loop;
1426
1427 // Prevents that the default check for change of selection(s) gets in the way.
1428 g_signal_handler_block (selection, ks.handler_id_row_selected);
1429
1430 for (selected_rows_loop = selected_rows; selected_rows_loop; selected_rows_loop = selected_rows_loop->next) {
1431 path_loop = selected_rows_loop->data;
1432 // (Note: Rows are not selected if they are not visible.)
1433 gtk_tree_view_expand_row (GTK_TREE_VIEW (ks.treeview), path_loop, FALSE);
1434 gtk_tree_selection_unselect_path (selection, path_loop);
1435 gtk_tree_path_down (path_loop);
1436 gtk_tree_model_get_iter (ks.model, &iter_loop, path_loop);
1437 do {
1438 gtk_tree_selection_select_iter (selection, &iter_loop);
1439 } while (gtk_tree_model_iter_next (ks.model, &iter_loop));
1440 gtk_tree_path_up (path_loop);
1441 }
1442
1443 remove_rows ("rm. all chldn.");
1444
1445 g_signal_handler_unblock (selection, ks.handler_id_row_selected);
1446
1447 for (selected_rows_loop = selected_rows; selected_rows_loop; selected_rows_loop = selected_rows_loop->next) {
1448 // Might be a former subnode that was selected and got removed.
1449 if (gtk_tree_model_get_iter (ks.model, &iter_loop, selected_rows_loop->data)) {
1450 gtk_tree_selection_select_iter (selection, &iter_loop);
1451 }
1452 }
1453
1454 // Cleanup
1455 g_list_free_full (selected_rows, (GDestroyNotify) gtk_tree_path_free);
1456 }
1457
1458 /*
1459
1460 Removes all currently selected rows.
1461
1462 */
1463
remove_rows(gchar * origin)1464 void remove_rows (gchar *origin)
1465 {
1466 GtkTreeModel *filter_model;
1467 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ks.treeview));
1468 // The list of rows is reverted, since only with a bottom to top direction the paths will stay the same.
1469 GList *selected_rows = g_list_reverse (gtk_tree_selection_get_selected_rows (selection, &ks.model));
1470
1471 GList *selected_rows_loop;
1472 GtkTreePath *path_loop;
1473 GtkTreeIter iter_loop;
1474
1475 gchar *type_txt_loop, *menu_id_txt_loop;
1476
1477 // Prevents that the default check for change of selection(s) gets in the way.
1478 g_signal_handler_block (selection, ks.handler_id_row_selected);
1479
1480 for (selected_rows_loop = selected_rows; selected_rows_loop; selected_rows_loop = selected_rows_loop->next) {
1481 path_loop = selected_rows_loop->data;
1482 gtk_tree_model_get_iter (ks.model, &iter_loop, path_loop);
1483
1484 if (!STREQ (origin, "dnd")) { // A drag & drop just moves rows; there are no new, deleted or changed menu IDs in this case.
1485 gtk_tree_model_get (ks.model, &iter_loop, TS_TYPE, &type_txt_loop, -1);
1486
1487 if (streq_any (type_txt_loop, "menu", "pipe menu", NULL)) {
1488 // Keep menu IDs in the GSList equal to the menu IDs of the treestore.
1489 gtk_tree_model_get (ks.model, &iter_loop, TS_MENU_ID, &menu_id_txt_loop, -1);
1490 remove_menu_id (menu_id_txt_loop);
1491
1492 /*
1493 If the to be deleted row is a menu that contains other menus as children,
1494 their menu IDs have to be deleted, too.
1495 */
1496 if (gtk_tree_model_iter_has_child (ks.model, &iter_loop)) { // Has to be a menu, because pipe menus don't have children.
1497 filter_model = gtk_tree_model_filter_new (ks.model, path_loop);
1498 gtk_tree_model_foreach (filter_model, (GtkTreeModelForeachFunc) check_for_menus, NULL);
1499
1500 // Cleanup
1501 g_object_unref (filter_model);
1502 }
1503 // Cleanup
1504 g_free (menu_id_txt_loop);
1505 }
1506 // Cleanup
1507 g_free (type_txt_loop);
1508 }
1509
1510 gtk_tree_store_remove (GTK_TREE_STORE (ks.model), &iter_loop);
1511 }
1512
1513 // If all rows have been deleted and the search functionality had been activated before, deactivate the latter.
1514 if (!gtk_tree_model_get_iter_first (ks.model, &iter_loop) && gtk_widget_get_visible (ks.find_grid)) {
1515 show_or_hide_find_grid ();
1516 }
1517
1518 g_signal_handler_unblock (selection, ks.handler_id_row_selected);
1519
1520 // Cleanup
1521 g_list_free_full (selected_rows, (GDestroyNotify) gtk_tree_path_free);
1522
1523 if (!streq_any (origin, "dnd", "load menu", NULL)) { // There might be further changes during Drag & Drop / loading a menu.
1524 activate_change_done ();
1525 row_selected ();
1526 }
1527 }
1528