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