1 /*
2  * This file is part of brisk-menu.
3  *
4  * Copyright © 2017-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 #define _GNU_SOURCE
13 
14 #include "util.h"
15 
16 BRISK_BEGIN_PEDANTIC
17 #include "menu-private.h"
18 BRISK_END_PEDANTIC
19 
20 G_DEFINE_TYPE(BriskMenuWindow, brisk_menu_window, GTK_TYPE_WINDOW)
21 
22 static void brisk_menu_window_set_property(GObject *object, guint id, const GValue *value,
23                                            GParamSpec *spec);
24 static void brisk_menu_window_get_property(GObject *object, guint id, GValue *value,
25                                            GParamSpec *spec);
26 enum { PROP_RELATIVE_TO = 1, N_PROPS };
27 
28 static GParamSpec *obj_properties[N_PROPS] = {
29         NULL,
30 };
31 
32 /**
33  * brisk_menu_window_dispose:
34  *
35  * Clean up a BriskMenuWindow instance
36  */
brisk_menu_window_dispose(GObject * obj)37 static void brisk_menu_window_dispose(GObject *obj)
38 {
39         BriskMenuWindow *self = BRISK_MENU_WINDOW(obj);
40 
41         g_clear_object(&self->binder);
42         g_clear_pointer(&self->shortcut, g_free);
43         g_clear_pointer(&self->search_term, g_free);
44         g_clear_object(&self->launcher);
45         g_clear_object(&self->session);
46         g_clear_object(&self->saver);
47         g_clear_object(&self->settings);
48         g_clear_pointer(&self->item_store, g_hash_table_unref);
49         g_clear_pointer(&self->section_boxes, g_hash_table_unref);
50         g_clear_pointer(&self->backends, g_hash_table_unref);
51         g_clear_pointer(&self->context_menu, gtk_widget_destroy);
52         g_clear_object(&self->context_group);
53 
54         G_OBJECT_CLASS(brisk_menu_window_parent_class)->dispose(obj);
55 }
56 
57 /**
58  * brisk_menu_window_class_init:
59  *
60  * Handle class initialisation
61  */
brisk_menu_window_class_init(BriskMenuWindowClass * klazz)62 static void brisk_menu_window_class_init(BriskMenuWindowClass *klazz)
63 {
64         GObjectClass *obj_class = G_OBJECT_CLASS(klazz);
65 
66         /* gobject vtable hookup */
67         obj_class->dispose = brisk_menu_window_dispose;
68         obj_class->set_property = brisk_menu_window_set_property;
69         obj_class->get_property = brisk_menu_window_get_property;
70 
71         /* Set up properties */
72         obj_properties[PROP_RELATIVE_TO] =
73             g_param_spec_pointer("relative-to",
74                                  "GtkWidget we appear near",
75                                  "Owning GtkWidget for this BriskMenuWindow",
76                                  G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
77         g_object_class_install_properties(obj_class, N_PROPS, obj_properties);
78 }
79 
80 /**
81  * brisk_menu_window_init:
82  *
83  * Handle construction of the BriskMenuWindow
84  */
brisk_menu_window_init(BriskMenuWindow * self)85 static void brisk_menu_window_init(BriskMenuWindow *self)
86 {
87         gtk_window_set_decorated(GTK_WINDOW(self), FALSE);
88         gtk_window_set_type_hint(GTK_WINDOW(self), GDK_WINDOW_TYPE_HINT_POPUP_MENU);
89         gtk_window_set_skip_pager_hint(GTK_WINDOW(self), TRUE);
90         gtk_window_set_skip_taskbar_hint(GTK_WINDOW(self), TRUE);
91 
92         /* Initialise main tables */
93         self->item_store = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
94         self->section_boxes = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
95         self->backends = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_object_unref);
96 
97         self->binder = brisk_key_binder_new();
98         self->launcher = brisk_menu_launcher_new();
99 
100         brisk_menu_window_init_settings(self);
101 }
102 
brisk_menu_window_set_property(GObject * object,guint id,const GValue * value,GParamSpec * spec)103 static void brisk_menu_window_set_property(GObject *object, guint id, const GValue *value,
104                                            GParamSpec *spec)
105 {
106         BriskMenuWindow *self = BRISK_MENU_WINDOW(object);
107 
108         switch (id) {
109         case PROP_RELATIVE_TO:
110                 self->relative_to = g_value_get_pointer(value);
111                 break;
112         default:
113                 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, id, spec);
114                 break;
115         }
116 }
117 
brisk_menu_window_get_property(GObject * object,guint id,GValue * value,GParamSpec * spec)118 static void brisk_menu_window_get_property(GObject *object, guint id, GValue *value,
119                                            GParamSpec *spec)
120 {
121         BriskMenuWindow *self = BRISK_MENU_WINDOW(object);
122 
123         switch (id) {
124         case PROP_RELATIVE_TO:
125                 g_value_set_pointer(value, self->relative_to);
126                 break;
127         default:
128                 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, id, spec);
129                 break;
130         }
131 }
132 
brisk_menu_window_set_parent_position(BriskMenuWindow * self,GtkPositionType position)133 void brisk_menu_window_set_parent_position(BriskMenuWindow *self, GtkPositionType position)
134 {
135         self->position = position;
136         brisk_menu_window_update_screen_position(self);
137         brisk_menu_window_update_search(self);
138 }
139 
140 /**
141  * brisk_menu_window_select_sidebar:
142  *
143  * Select the first child in the section box holder prior to any kind of display
144  */
brisk_menu_window_select_sections(BriskMenuWindow * self)145 void brisk_menu_window_select_sections(BriskMenuWindow *self)
146 {
147         GtkWidget *next_child = NULL;
148 
149         /* Activate the first child in the group after the dummy widget */
150         next_child = brisk_menu_window_find_first_visible_radio(self);
151         if (next_child) {
152                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(next_child), TRUE);
153         }
154 }
155 
brisk_menu_window_find_first_visible_radio(BriskMenuWindow * self)156 GtkWidget *brisk_menu_window_find_first_visible_radio(BriskMenuWindow *self)
157 {
158         autofree(GList) *box_kids = NULL;
159         autofree(GList) *box_kids2 = NULL;
160         GtkWidget *main_box = NULL;
161         GList *elem = NULL;
162 
163         box_kids = gtk_container_get_children(GTK_CONTAINER(self->section_box_holder));
164         for (elem = box_kids; elem; elem = elem->next) {
165                 if (!GTK_IS_BOX(elem->data)) {
166                         continue;
167                 }
168                 main_box = elem->data;
169                 break;
170         }
171         if (!main_box) {
172                 return NULL;
173         }
174         box_kids2 = gtk_container_get_children(GTK_CONTAINER(main_box));
175         return g_list_nth_data(box_kids2, 0);
176 }
177 
178 /**
179  * brisk_menu_window_get_id:
180  *
181  * Return the unique ID for the window
182  * @note This string is owned by the window and must not be freed
183  */
brisk_menu_window_get_id(BriskMenuWindow * window)184 const gchar *brisk_menu_window_get_id(BriskMenuWindow *window)
185 {
186         g_assert(window != NULL);
187         BriskMenuWindowClass *klazz = BRISK_MENU_WINDOW_GET_CLASS(window);
188         g_assert(klazz->get_id != NULL);
189         return klazz->get_id(window);
190 }
191 
192 /**
193  * brisk_menu_window_get_display_name:
194  *
195  * Return the display name for the window
196  * @note This string is owned by the window and must not be freed
197  */
brisk_menu_window_get_display_name(BriskMenuWindow * window)198 const gchar *brisk_menu_window_get_display_name(BriskMenuWindow *window)
199 {
200         g_assert(window != NULL);
201         BriskMenuWindowClass *klazz = BRISK_MENU_WINDOW_GET_CLASS(window);
202         g_assert(klazz->get_display_name != NULL);
203         return klazz->get_display_name(window);
204 }
205 
206 /**
207  * brisk_menu_window_update_screen_position:
208  *
209  * Ask that the menu window updates it's position on screen
210  */
brisk_menu_window_update_screen_position(BriskMenuWindow * window)211 void brisk_menu_window_update_screen_position(BriskMenuWindow *window)
212 {
213         g_assert(window != NULL);
214         BriskMenuWindowClass *klazz = BRISK_MENU_WINDOW_GET_CLASS(window);
215         g_assert(klazz->add_item != NULL);
216         klazz->update_screen_position(window);
217 }
218 
brisk_menu_window_update_search(BriskMenuWindow * window)219 void brisk_menu_window_update_search(BriskMenuWindow *window)
220 {
221         g_assert(window != NULL);
222         BriskMenuWindowClass *klazz = BRISK_MENU_WINDOW_GET_CLASS(window);
223         if (klazz->update_search) {
224                 klazz->update_search(window);
225         }
226 }
227 
brisk_menu_window_add_item(BriskMenuWindow * window,BriskItem * item,BriskBackend * backend)228 void brisk_menu_window_add_item(BriskMenuWindow *window, BriskItem *item, BriskBackend *backend)
229 {
230         g_assert(window != NULL);
231         BriskMenuWindowClass *klazz = BRISK_MENU_WINDOW_GET_CLASS(window);
232         g_assert(klazz->add_item != NULL);
233         klazz->add_item(window, item, backend);
234 }
235 
brisk_menu_window_add_section(BriskMenuWindow * window,BriskSection * section,BriskBackend * backend)236 void brisk_menu_window_add_section(BriskMenuWindow *window, BriskSection *section,
237                                    BriskBackend *backend)
238 {
239         g_assert(window != NULL);
240         BriskMenuWindowClass *klazz = BRISK_MENU_WINDOW_GET_CLASS(window);
241         g_assert(klazz->add_section != NULL);
242         klazz->add_section(window, section, backend);
243 }
244 
brisk_menu_window_invalidate_filter(BriskMenuWindow * window,BriskBackend * backend)245 void brisk_menu_window_invalidate_filter(BriskMenuWindow *window, BriskBackend *backend)
246 {
247         g_assert(window != NULL);
248         BriskMenuWindowClass *klazz = BRISK_MENU_WINDOW_GET_CLASS(window);
249         g_assert(klazz->invalidate_filter != NULL);
250         klazz->invalidate_filter(window, backend);
251 }
252 
brisk_menu_window_reset(BriskMenuWindow * window,BriskBackend * backend)253 void brisk_menu_window_reset(BriskMenuWindow *window, BriskBackend *backend)
254 {
255         g_assert(window != NULL);
256         BriskMenuWindowClass *klazz = BRISK_MENU_WINDOW_GET_CLASS(window);
257         g_assert(klazz->reset != NULL);
258         klazz->reset(window, backend);
259 }
260 
261 /*
262  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
263  *
264  * Local variables:
265  * c-basic-offset: 8
266  * tab-width: 8
267  * indent-tabs-mode: nil
268  * End:
269  *
270  * vi: set shiftwidth=8 tabstop=8 expandtab:
271  * :indentSize=8:tabSize=8:noTabs=true:
272  */
273