1 /*
2 * This file is part of brisk-menu.
3 *
4 * Copyright © 2016-2020 Brisk Menu Developers
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
12 #include "util.h"
13
14 #include <stdlib.h>
15
16 BRISK_BEGIN_PEDANTIC
17 #include "menu-private.h"
18 #include <gio/gdesktopappinfo.h>
19 #include <gtk/gtk.h>
20 #include <string.h>
21 BRISK_END_PEDANTIC
22
23 /**
24 * Update sensitivity - ignoring non checkboxes
25 */
brisk_menu_set_checks_sensitive(GtkWidget * parent,gboolean sensitive)26 static void brisk_menu_set_checks_sensitive(GtkWidget *parent, gboolean sensitive)
27 {
28 autofree(GList) *kids = NULL;
29 GList *elem = NULL;
30
31 kids = gtk_container_get_children(GTK_CONTAINER(parent));
32 for (elem = kids; elem; elem = elem->next) {
33 GtkWidget *kid = elem->data;
34 if (!GTK_IS_CHECK_BUTTON(kid)) {
35 continue;
36 }
37 gtk_widget_set_sensitive(kid, sensitive);
38 }
39 }
40
41 /**
42 * Ask all boxes to update their section children
43 */
brisk_menu_set_categories_sensitive(BriskMenuWindow * self,gboolean sensitive)44 static void brisk_menu_set_categories_sensitive(BriskMenuWindow *self, gboolean sensitive)
45 {
46 brisk_menu_set_checks_sensitive(self->section_box_holder, sensitive);
47 GHashTableIter iter;
48 __attribute__((unused)) gchar *key = NULL;
49 GtkWidget *box = NULL;
50
51 /* Update all sideboxes for search */
52 g_hash_table_iter_init(&iter, self->section_boxes);
53 while (g_hash_table_iter_next(&iter, (void **)&key, (void **)&box)) {
54 brisk_menu_set_checks_sensitive(box, sensitive);
55 }
56 }
57
58 /**
59 * brisk_menu_window_filter_section:
60 *
61 * This function will handle filtering the selection based on the active
62 * section, when no search term is applied.
63 *
64 * Returning TRUE means the item should be displayed
65 */
brisk_menu_window_filter_section(BriskMenuWindow * self,BriskItem * item)66 __brisk_pure__ static gboolean brisk_menu_window_filter_section(BriskMenuWindow *self,
67 BriskItem *item)
68 {
69 /* All visible */
70 if (!self->active_section) {
71 return TRUE;
72 }
73
74 return brisk_section_can_show_item(self->active_section, item);
75 }
76
77 /**
78 * brisk_menu_window_clear_search:
79 *
80 * Simply put, resets the active search term
81 */
brisk_menu_window_clear_search(GtkEntry * entry,GtkEntryIconPosition pos,__brisk_unused__ GdkEvent * event,__brisk_unused__ gpointer v)82 void brisk_menu_window_clear_search(GtkEntry *entry, GtkEntryIconPosition pos,
83 __brisk_unused__ GdkEvent *event, __brisk_unused__ gpointer v)
84 {
85 if (pos != GTK_ENTRY_ICON_SECONDARY) {
86 return;
87 }
88 gtk_entry_set_text(entry, "");
89 }
90
91 /**
92 * brisk_menu_window_search:
93 *
94 * Callback for the text entry changing. Set the search term and force
95 * an invalidation of our filters.
96 */
brisk_menu_window_search(BriskMenuWindow * self,GtkEntry * entry)97 void brisk_menu_window_search(BriskMenuWindow *self, GtkEntry *entry)
98 {
99 const gchar *search_term = NULL;
100
101 if (!self->filtering) {
102 return;
103 }
104
105 /* Remove old search term */
106 search_term = gtk_entry_get_text(entry);
107 g_clear_pointer(&self->search_term, g_free);
108
109 /* New search term, always lower case for simplicity */
110 self->search_term = g_strstrip(g_ascii_strdown(search_term, -1));
111
112 /* Reset our search term if it's not valid anymore, or whitespace */
113 if (strlen(self->search_term) > 0) {
114 brisk_menu_set_categories_sensitive(self, FALSE);
115 } else {
116 brisk_menu_set_categories_sensitive(self, TRUE);
117 g_clear_pointer(&self->search_term, g_free);
118 }
119
120 /* Now filter again */
121 brisk_menu_window_invalidate_filter(self, NULL);
122 }
123
brisk_menu_window_filter_apps(BriskMenuWindow * self,GtkWidget * child)124 __brisk_pure__ gboolean brisk_menu_window_filter_apps(BriskMenuWindow *self, GtkWidget *child)
125 {
126 const gchar *item_id = NULL;
127 BriskItem *item = NULL;
128 GtkWidget *compare_child = NULL;
129
130 g_object_get(child, "item", &item, NULL);
131 if (!item) {
132 return FALSE;
133 }
134
135 /* Item ID's are unique, so the last entry added for an ID is the
136 * button we want to display. Basically, a button can be duplicated and
137 * appear in multiple categories. By keeping a unique ID -> button mapping,
138 * we ensure we only ever show it once in the search function.
139 */
140 item_id = brisk_item_get_id(item);
141 if (item_id) {
142 compare_child = g_hash_table_lookup(self->item_store, item_id);
143 if (compare_child && compare_child != child) {
144 return FALSE;
145 }
146 }
147
148 /* If we have no search term, filter on the section */
149 if (!self->search_term) {
150 return brisk_menu_window_filter_section(self, item);
151 }
152
153 /* Have search term? Filter on that. */
154 return brisk_item_matches_search(item, self->search_term);
155 }
156
157 /*
158 * Editor modelines - https://www.wireshark.org/tools/modelines.html
159 *
160 * Local variables:
161 * c-basic-offset: 8
162 * tab-width: 8
163 * indent-tabs-mode: nil
164 * End:
165 *
166 * vi: set shiftwidth=8 tabstop=8 expandtab:
167 * :indentSize=8:tabSize=8:noTabs=true:
168 */
169