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