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