1 /*
2  * appmenu-gtk-module
3  * Copyright 2012 Canonical Ltd.
4  * Copyright (C) 2015-2017 Konstantin Pugin <ria.freelander@gmail.com>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 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 Lesser General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  * Authors: Ryan Lortie <desrt@desrt.ca>
20  *          William Hua <william.hua@canonical.com>
21  *          Konstantin Pugin <ria.freelander@gmail.com>
22  *          Lester Carballo Perez <lestcape@gmail.com>
23  */
24 
25 #include "datastructs.h"
26 #include "datastructs-private.h"
27 #include "platform.h"
28 
29 #include <appmenu-gtk-menu-shell.h>
30 
31 G_GNUC_INTERNAL G_DEFINE_QUARK(window_data, window_data);
32 G_DEFINE_BOXED_TYPE(WindowData, window_data, (GBoxedCopyFunc)window_data_copy,
33                     (GBoxedFreeFunc)window_data_free);
34 G_GNUC_INTERNAL G_DEFINE_QUARK(menu_shell_data, menu_shell_data);
35 G_DEFINE_BOXED_TYPE(MenuShellData, menu_shell_data, (GBoxedCopyFunc)menu_shell_data_copy,
36                     (GBoxedFreeFunc)menu_shell_data_free);
37 
window_data_new(void)38 G_GNUC_INTERNAL WindowData *window_data_new(void)
39 {
40 	return g_slice_new0(WindowData);
41 }
42 
window_data_free(gpointer data)43 G_GNUC_INTERNAL void window_data_free(gpointer data)
44 {
45 	WindowData *window_data = data;
46 
47 	if (window_data != NULL)
48 	{
49 		GDBusConnection *session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
50 
51 		if (window_data->action_group_export_id)
52 			g_dbus_connection_unexport_action_group(session,
53 			                                        window_data
54 			                                            ->action_group_export_id);
55 
56 		if (window_data->menu_model_export_id)
57 			g_dbus_connection_unexport_menu_model(session,
58 			                                      window_data->menu_model_export_id);
59 
60 		if (window_data->action_group != NULL)
61 			g_object_unref(window_data->action_group);
62 
63 		if (window_data->menu_model != NULL)
64 			g_object_unref(window_data->menu_model);
65 
66 		if (window_data->old_model != NULL)
67 			g_object_unref(window_data->old_model);
68 
69 		if (window_data->menus != NULL)
70 			g_slist_free_full(window_data->menus, g_object_unref);
71 
72 		g_slice_free(WindowData, window_data);
73 	}
74 }
75 
window_data_copy(WindowData * source)76 G_GNUC_INTERNAL WindowData *window_data_copy(WindowData *source)
77 {
78 	WindowData *ret             = window_data_new();
79 	ret->action_group_export_id = source->action_group_export_id;
80 	ret->menu_model_export_id   = source->menu_model_export_id;
81 	if (source->action_group != NULL)
82 		ret->action_group = g_object_ref(source->action_group);
83 
84 	if (source->menu_model != NULL)
85 		ret->menu_model = g_object_ref(source->menu_model);
86 
87 	if (source->old_model != NULL)
88 		ret->old_model = g_object_ref(source->old_model);
89 
90 	if (source->menus != NULL)
91 		ret->menus = g_slist_copy_deep(source->menus, (GCopyFunc)g_object_ref, NULL);
92 
93 	return ret;
94 }
95 
menu_shell_data_new(void)96 G_GNUC_INTERNAL MenuShellData *menu_shell_data_new(void)
97 {
98 	return g_slice_new0(MenuShellData);
99 }
100 
menu_shell_data_free(gpointer data)101 G_GNUC_INTERNAL void menu_shell_data_free(gpointer data)
102 {
103 	if (data != NULL)
104 		g_slice_free(MenuShellData, data);
105 }
106 
menu_shell_data_copy(MenuShellData * source)107 G_GNUC_INTERNAL MenuShellData *menu_shell_data_copy(MenuShellData *source)
108 {
109 	MenuShellData *ret = menu_shell_data_new();
110 	ret->window        = source->window;
111 	return ret;
112 }
113 
menu_shell_data_has_window(MenuShellData * source)114 G_GNUC_INTERNAL bool menu_shell_data_has_window(MenuShellData *source)
115 {
116 	return source->window != NULL;
117 }
118 
menu_shell_data_get_window(MenuShellData * source)119 G_GNUC_INTERNAL GtkWindow *menu_shell_data_get_window(MenuShellData *source)
120 {
121 	return source->window;
122 }
123 
gtk_menu_shell_get_menu_shell_data(GtkMenuShell * menu_shell)124 G_GNUC_INTERNAL MenuShellData *gtk_menu_shell_get_menu_shell_data(GtkMenuShell *menu_shell)
125 {
126 	MenuShellData *menu_shell_data;
127 
128 	g_return_val_if_fail(GTK_IS_MENU_SHELL(menu_shell), NULL);
129 
130 	menu_shell_data = g_object_get_qdata(G_OBJECT(menu_shell), menu_shell_data_quark());
131 
132 	if (menu_shell_data == NULL)
133 	{
134 		menu_shell_data = menu_shell_data_new();
135 
136 		g_object_set_qdata_full(G_OBJECT(menu_shell),
137 		                        menu_shell_data_quark(),
138 		                        menu_shell_data,
139 		                        menu_shell_data_free);
140 	}
141 
142 	return menu_shell_data;
143 }
144 
gtk_window_get_window_data(GtkWindow * window)145 G_GNUC_INTERNAL WindowData *gtk_window_get_window_data(GtkWindow *window)
146 {
147 	WindowData *window_data = NULL;
148 
149 	g_return_val_if_fail(GTK_IS_WINDOW(window), NULL);
150 
151 #if (defined(GDK_WINDOWING_WAYLAND))
152 	if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default()))
153 		window_data = gtk_wayland_window_get_window_data(window);
154 #endif
155 #if (defined(GDK_WINDOWING_X11))
156 #if GTK_MAJOR_VERSION == 3
157 	if (GDK_IS_X11_DISPLAY(gdk_display_get_default()))
158 #endif
159 		window_data = gtk_x11_window_get_window_data(window);
160 #endif
161 	return window_data;
162 }
163 
gtk_window_disconnect_menu_shell(GtkWindow * window,GtkMenuShell * menu_shell)164 G_GNUC_INTERNAL void gtk_window_disconnect_menu_shell(GtkWindow *window, GtkMenuShell *menu_shell)
165 {
166 	WindowData *window_data;
167 	MenuShellData *menu_shell_data;
168 
169 	g_return_if_fail(GTK_IS_WINDOW(window));
170 	g_return_if_fail(GTK_IS_MENU_SHELL(menu_shell));
171 
172 	menu_shell_data = gtk_menu_shell_get_menu_shell_data(menu_shell);
173 
174 	g_warn_if_fail(window == menu_shell_data->window);
175 
176 	window_data = gtk_window_get_window_data(menu_shell_data->window);
177 
178 	if (window_data != NULL)
179 	{
180 		GSList *iter;
181 		guint i = 0;
182 
183 		if (window_data->old_model != NULL)
184 			i++;
185 
186 		for (iter = window_data->menus; iter != NULL; iter = g_slist_next(iter), i++)
187 			if (UNITY_GTK_MENU_SHELL(iter->data)->menu_shell == menu_shell)
188 				break;
189 
190 		if (iter != NULL)
191 		{
192 			g_menu_remove(window_data->menu_model, i);
193 
194 			unity_gtk_action_group_disconnect_shell(window_data->action_group,
195 			                                        iter->data);
196 
197 			g_object_unref(iter->data);
198 
199 			window_data->menus = g_slist_delete_link(window_data->menus, iter);
200 		}
201 
202 		menu_shell_data->window = NULL;
203 	}
204 }
205 
gtk_window_connect_menu_shell(GtkWindow * window,GtkMenuShell * menu_shell)206 G_GNUC_INTERNAL void gtk_window_connect_menu_shell(GtkWindow *window, GtkMenuShell *menu_shell)
207 {
208 	MenuShellData *menu_shell_data;
209 
210 	g_return_if_fail(GTK_IS_WINDOW(window));
211 	g_return_if_fail(GTK_IS_MENU_SHELL(menu_shell));
212 
213 	menu_shell_data = gtk_menu_shell_get_menu_shell_data(menu_shell);
214 
215 	if (window != menu_shell_data->window)
216 	{
217 		WindowData *window_data;
218 
219 		if (menu_shell_data->window != NULL)
220 			gtk_window_disconnect_menu_shell(menu_shell_data->window, menu_shell);
221 
222 		window_data = gtk_window_get_window_data(window);
223 
224 		if (window_data != NULL)
225 		{
226 			GSList *iter;
227 
228 			for (iter = window_data->menus; iter != NULL; iter = g_slist_next(iter))
229 				if (UNITY_GTK_MENU_SHELL(iter->data)->menu_shell == menu_shell)
230 					break;
231 
232 			if (iter == NULL)
233 			{
234 				UnityGtkMenuShell *shell = unity_gtk_menu_shell_new(menu_shell);
235 
236 				unity_gtk_action_group_connect_shell(window_data->action_group,
237 				                                     shell);
238 
239 				g_menu_append_section(window_data->menu_model,
240 				                      NULL,
241 				                      G_MENU_MODEL(shell));
242 
243 				window_data->menus = g_slist_append(window_data->menus, shell);
244 			}
245 		}
246 
247 		menu_shell_data->window = window;
248 	}
249 }
250