1 /*
2 * Copyright 2010 John Mark Bell <jmb@netsurf-browser.org>
3 * Copyright 2016 Vincent Sanders <vince@netsurf-browser.org>
4 *
5 * This file is part of NetSurf, http://www.netsurf-browser.org/
6 *
7 * NetSurf is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 * NetSurf 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 General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /**
21 * \file
22 * Implementation of GTK global history manager.
23 */
24
25 #include <stdint.h>
26 #include <stdlib.h>
27 #include <gtk/gtk.h>
28
29 #include "utils/log.h"
30 #include "netsurf/keypress.h"
31 #include "netsurf/plotters.h"
32 #include "desktop/global_history.h"
33
34 #include "gtk/compat.h"
35 #include "gtk/plotters.h"
36 #include "gtk/resources.h"
37 #include "gtk/corewindow.h"
38 #include "gtk/global_history.h"
39
40 struct nsgtk_global_history_window {
41 struct nsgtk_corewindow core;
42 GtkBuilder *builder;
43 GtkWindow *wnd;
44 };
45
46 static struct nsgtk_global_history_window *global_history_window = NULL;
47
48 #define MENUPROTO(x) static gboolean nsgtk_on_##x##_activate( \
49 GtkMenuItem *widget, gpointer g)
50 #define MENUEVENT(x) { #x, G_CALLBACK(nsgtk_on_##x##_activate) }
51 #define MENUHANDLER(x) gboolean nsgtk_on_##x##_activate(GtkMenuItem *widget, \
52 gpointer g)
53
54 struct menu_events {
55 const char *widget;
56 GCallback handler;
57 };
58
59 /* file menu*/
60 MENUPROTO(export);
61
62 /* edit menu */
63 MENUPROTO(delete_selected);
64 MENUPROTO(delete_all);
65 MENUPROTO(select_all);
66 MENUPROTO(clear_selection);
67
68 /* view menu*/
69 MENUPROTO(expand_all);
70 MENUPROTO(expand_directories);
71 MENUPROTO(expand_addresses);
72 MENUPROTO(collapse_all);
73 MENUPROTO(collapse_directories);
74 MENUPROTO(collapse_addresses);
75
76 MENUPROTO(launch);
77
78 static struct menu_events menu_events[] = {
79
80 /* file menu*/
81 MENUEVENT(export),
82
83 /* edit menu */
84 MENUEVENT(delete_selected),
85 MENUEVENT(delete_all),
86 MENUEVENT(select_all),
87 MENUEVENT(clear_selection),
88
89 /* view menu*/
90 MENUEVENT(expand_all),
91 MENUEVENT(expand_directories),
92 MENUEVENT(expand_addresses),
93 MENUEVENT(collapse_all),
94 MENUEVENT(collapse_directories),
95 MENUEVENT(collapse_addresses),
96
97 MENUEVENT(launch),
98 {NULL, NULL}
99 };
100
101 /* edit menu */
MENUHANDLER(delete_selected)102 MENUHANDLER(delete_selected)
103 {
104 global_history_keypress(NS_KEY_DELETE_LEFT);
105 return TRUE;
106 }
107
MENUHANDLER(delete_all)108 MENUHANDLER(delete_all)
109 {
110 global_history_keypress(NS_KEY_ESCAPE);
111 global_history_keypress(NS_KEY_ESCAPE);
112 global_history_keypress(NS_KEY_SELECT_ALL);
113 global_history_keypress(NS_KEY_DELETE_LEFT);
114 return TRUE;
115 }
116
MENUHANDLER(select_all)117 MENUHANDLER(select_all)
118 {
119 global_history_keypress(NS_KEY_ESCAPE);
120 global_history_keypress(NS_KEY_ESCAPE);
121 global_history_keypress(NS_KEY_SELECT_ALL);
122 return TRUE;
123 }
124
MENUHANDLER(clear_selection)125 MENUHANDLER(clear_selection)
126 {
127 global_history_keypress(NS_KEY_ESCAPE);
128 global_history_keypress(NS_KEY_ESCAPE);
129 global_history_keypress(NS_KEY_CLEAR_SELECTION);
130 return TRUE;
131 }
132
133 /* view menu*/
MENUHANDLER(expand_all)134 MENUHANDLER(expand_all)
135 {
136 global_history_expand(false);
137 return TRUE;
138 }
139
MENUHANDLER(expand_directories)140 MENUHANDLER(expand_directories)
141 {
142 global_history_expand(true);
143 return TRUE;
144 }
145
MENUHANDLER(expand_addresses)146 MENUHANDLER(expand_addresses)
147 {
148 global_history_expand(false);
149 return TRUE;
150 }
151
MENUHANDLER(collapse_all)152 MENUHANDLER(collapse_all)
153 {
154 global_history_contract(true);
155 return TRUE;
156 }
157
MENUHANDLER(collapse_directories)158 MENUHANDLER(collapse_directories)
159 {
160 global_history_contract(true);
161 return TRUE;
162 }
163
MENUHANDLER(collapse_addresses)164 MENUHANDLER(collapse_addresses)
165 {
166 global_history_contract(false);
167 return TRUE;
168 }
169
MENUHANDLER(launch)170 MENUHANDLER(launch)
171 {
172 global_history_keypress(NS_KEY_CR);
173 return TRUE;
174 }
175
176 /* file menu */
MENUHANDLER(export)177 MENUHANDLER(export)
178 {
179 struct nsgtk_global_history_window *ghwin;
180 GtkWidget *save_dialog;
181
182 ghwin = (struct nsgtk_global_history_window *)g;
183
184 save_dialog = gtk_file_chooser_dialog_new("Save File",
185 ghwin->wnd,
186 GTK_FILE_CHOOSER_ACTION_SAVE,
187 NSGTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
188 NSGTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
189 NULL);
190
191 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(save_dialog),
192 getenv("HOME") ? getenv("HOME") : "/");
193
194 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(save_dialog),
195 "history.html");
196
197 if (gtk_dialog_run(GTK_DIALOG(save_dialog)) == GTK_RESPONSE_ACCEPT) {
198 gchar *filename = gtk_file_chooser_get_filename(
199 GTK_FILE_CHOOSER(save_dialog));
200
201 global_history_export(filename, NULL);
202 g_free(filename);
203 }
204
205 gtk_widget_destroy(save_dialog);
206
207 return TRUE;
208 }
209
210 /**
211 * Connects menu events in the global history window.
212 */
213 static void
nsgtk_global_history_init_menu(struct nsgtk_global_history_window * ghwin)214 nsgtk_global_history_init_menu(struct nsgtk_global_history_window *ghwin)
215 {
216 struct menu_events *event = menu_events;
217 GtkWidget *w;
218
219 while (event->widget != NULL) {
220 w = GTK_WIDGET(gtk_builder_get_object(ghwin->builder,
221 event->widget));
222 if (w == NULL) {
223 NSLOG(netsurf, INFO,
224 "Unable to connect menu widget ""%s""",
225 event->widget);
226 } else {
227 g_signal_connect(G_OBJECT(w),
228 "activate",
229 event->handler,
230 ghwin);
231 }
232 event++;
233 }
234 }
235
236
237 /**
238 * callback for mouse action on global history window
239 *
240 * \param nsgtk_cw The nsgtk core window structure.
241 * \param mouse_state netsurf mouse state on event
242 * \param x location of event
243 * \param y location of event
244 * \return NSERROR_OK on success otherwise apropriate error code
245 */
246 static nserror
nsgtk_global_history_mouse(struct nsgtk_corewindow * nsgtk_cw,browser_mouse_state mouse_state,int x,int y)247 nsgtk_global_history_mouse(struct nsgtk_corewindow *nsgtk_cw,
248 browser_mouse_state mouse_state,
249 int x, int y)
250 {
251 global_history_mouse_action(mouse_state, x, y);
252
253 return NSERROR_OK;
254 }
255
256
257 /**
258 * callback for keypress on global history window
259 *
260 * \param nsgtk_cw The nsgtk core window structure.
261 * \param nskey The netsurf key code
262 * \return NSERROR_OK on success otherwise apropriate error code
263 */
264 static nserror
nsgtk_global_history_key(struct nsgtk_corewindow * nsgtk_cw,uint32_t nskey)265 nsgtk_global_history_key(struct nsgtk_corewindow *nsgtk_cw, uint32_t nskey)
266 {
267 if (global_history_keypress(nskey)) {
268 return NSERROR_OK;
269 }
270 return NSERROR_NOT_IMPLEMENTED;
271 }
272
273
274 /**
275 * callback on draw event for global history window
276 *
277 * \param nsgtk_cw The nsgtk core window structure.
278 * \param r The rectangle of the window that needs updating.
279 * \return NSERROR_OK on success otherwise apropriate error code
280 */
281 static nserror
nsgtk_global_history_draw(struct nsgtk_corewindow * nsgtk_cw,struct rect * r)282 nsgtk_global_history_draw(struct nsgtk_corewindow *nsgtk_cw, struct rect *r)
283 {
284 struct redraw_context ctx = {
285 .interactive = true,
286 .background_images = true,
287 .plot = &nsgtk_plotters
288 };
289
290 global_history_redraw(0, 0, r, &ctx);
291
292 return NSERROR_OK;
293 }
294
295 /**
296 * Creates the window for the global history tree.
297 *
298 * \return NSERROR_OK on success else appropriate error code on faliure.
299 */
nsgtk_global_history_init(void)300 static nserror nsgtk_global_history_init(void)
301 {
302 struct nsgtk_global_history_window *ncwin;
303 nserror res;
304
305 if (global_history_window != NULL) {
306 return NSERROR_OK;
307 }
308
309 ncwin = calloc(1, sizeof(*ncwin));
310 if (ncwin == NULL) {
311 return NSERROR_NOMEM;
312 }
313
314 res = nsgtk_builder_new_from_resname("globalhistory", &ncwin->builder);
315 if (res != NSERROR_OK) {
316 NSLOG(netsurf, INFO, "History UI builder init failed");
317 free(ncwin);
318 return res;
319 }
320
321 gtk_builder_connect_signals(ncwin->builder, NULL);
322
323 ncwin->wnd = GTK_WINDOW(gtk_builder_get_object(ncwin->builder,
324 "wndHistory"));
325
326 ncwin->core.scrolled = GTK_SCROLLED_WINDOW(
327 gtk_builder_get_object(ncwin->builder,
328 "globalHistoryScrolled"));
329
330 ncwin->core.drawing_area = GTK_DRAWING_AREA(
331 gtk_builder_get_object(ncwin->builder,
332 "globalHistoryDrawingArea"));
333
334 /* make the delete event hide the window */
335 g_signal_connect(G_OBJECT(ncwin->wnd),
336 "delete_event",
337 G_CALLBACK(gtk_widget_hide_on_delete),
338 NULL);
339
340 nsgtk_global_history_init_menu(ncwin);
341
342 ncwin->core.draw = nsgtk_global_history_draw;
343 ncwin->core.key = nsgtk_global_history_key;
344 ncwin->core.mouse = nsgtk_global_history_mouse;
345
346 res = nsgtk_corewindow_init(&ncwin->core);
347 if (res != NSERROR_OK) {
348 free(ncwin);
349 return res;
350 }
351
352 res = global_history_init(ncwin->core.cb_table,
353 (struct core_window *)ncwin);
354 if (res != NSERROR_OK) {
355 free(ncwin);
356 return res;
357 }
358
359 /* memoise window so it can be represented when necessary
360 * instead of recreating every time.
361 */
362 global_history_window = ncwin;
363
364 return NSERROR_OK;
365 }
366
367
368 /* exported function documented gtk/history.h */
nsgtk_global_history_present(void)369 nserror nsgtk_global_history_present(void)
370 {
371 nserror res;
372
373 res = nsgtk_global_history_init();
374 if (res == NSERROR_OK) {
375 gtk_window_present(global_history_window->wnd);
376 }
377 return res;
378 }
379
380
381 /* exported function documented gtk/history.h */
nsgtk_global_history_destroy(void)382 nserror nsgtk_global_history_destroy(void)
383 {
384 nserror res;
385
386 if (global_history_window == NULL) {
387 return NSERROR_OK;
388 }
389
390 res = global_history_fini();
391 if (res == NSERROR_OK) {
392 res = nsgtk_corewindow_fini(&global_history_window->core);
393 gtk_widget_destroy(GTK_WIDGET(global_history_window->wnd));
394 g_object_unref(G_OBJECT(global_history_window->builder));
395 free(global_history_window);
396 global_history_window = NULL;
397 }
398
399 return res;
400
401 }
402
403
404
405