1 /*
2  * Copyright 2017 Vincent Sanders <vince@netsurf-browser.org>
3  *
4  * This file is part of NetSurf, http://www.netsurf-browser.org/
5  *
6  * NetSurf 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; version 2 of the License.
9  *
10  * NetSurf is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 /**
20  * \file
21  * Implementation of RISC OS local history.
22  */
23 
24 #include <stdint.h>
25 #include <stdlib.h>
26 #include <oslib/wimp.h>
27 
28 #include "utils/nsoption.h"
29 #include "utils/messages.h"
30 #include "utils/log.h"
31 #include "utils/nsurl.h"
32 #include "netsurf/window.h"
33 #include "netsurf/plotters.h"
34 #include "netsurf/keypress.h"
35 #include "desktop/local_history.h"
36 
37 #include "riscos/dialog.h"
38 #include "riscos/gui.h"
39 #include "riscos/menus.h"
40 #include "riscos/save.h"
41 #include "riscos/toolbar.h"
42 #include "riscos/wimp.h"
43 #include "riscos/wimp_event.h"
44 #include "riscos/wimputils.h"
45 #include "riscos/corewindow.h"
46 #include "riscos/local_history.h"
47 
48 struct ro_local_history_window {
49 	struct ro_corewindow core;
50 
51 	/** local history window context */
52 	struct local_history_session *session;
53 
54 	/** tooltip previous x */
55 	int x;
56 	/** tooltip previous y */
57 	int y;
58 };
59 
60 /** local_history window is a singleton */
61 static struct ro_local_history_window *local_history_window = NULL;
62 
63 /** riscos template for local_history window */
64 static wimp_window *dialog_local_history_template;
65 
66 
67 /**
68  * callback to draw on drawable area of ro local history window
69  *
70  * \param ro_cw The riscos core window structure.
71  * \param r The rectangle of the window that needs updating.
72  * \param originx The risc os plotter x origin.
73  * \param originy The risc os plotter y origin.
74  * \return NSERROR_OK on success otherwise apropriate error code
75  */
76 static nserror
ro_local_history_draw(struct ro_corewindow * ro_cw,int originx,int originy,struct rect * r)77 ro_local_history_draw(struct ro_corewindow *ro_cw,
78 		      int originx,
79 		      int originy,
80 		      struct rect *r)
81 {
82 	struct redraw_context ctx = {
83 		.interactive = true,
84 		.background_images = true,
85 		.plot = &ro_plotters
86 	};
87 	struct ro_local_history_window *lhw;
88 
89 	lhw = (struct ro_local_history_window *)ro_cw;
90 
91 	ro_plot_origin_x = originx;
92 	ro_plot_origin_y = originy;
93 	no_font_blending = true;
94 	local_history_redraw(lhw->session, 0, 0, r, &ctx);
95 	no_font_blending = false;
96 
97 	return NSERROR_OK;
98 }
99 
100 
101 /**
102  * callback for keypress on ro coookie window
103  *
104  * \param ro_cw The ro core window structure.
105  * \param nskey The netsurf key code.
106  * \return NSERROR_OK if key processed,
107  *         NSERROR_NOT_IMPLEMENTED if key not processed
108  *         otherwise apropriate error code
109  */
110 static nserror
ro_local_history_key(struct ro_corewindow * ro_cw,uint32_t nskey)111 ro_local_history_key(struct ro_corewindow *ro_cw, uint32_t nskey)
112 {
113 	struct ro_local_history_window *lhw;
114 
115 	lhw = (struct ro_local_history_window *)ro_cw;
116 
117 	if (local_history_keypress(lhw->session, nskey)) {
118 		return NSERROR_OK;
119 	}
120 	return NSERROR_NOT_IMPLEMENTED;
121 }
122 
123 
124 /**
125  * handle hover mouse movement for tooltips
126  */
127 static nserror
ro_local_history_tooltip(struct ro_local_history_window * lhw,int x,int y)128 ro_local_history_tooltip(struct ro_local_history_window *lhw, int x, int y)
129 {
130 	int width;
131 	nsurl *url;
132 	wimp_window_state state;
133 	wimp_icon_state ic;
134 	os_box box = {0, 0, 0, 0};
135 	os_error *error;
136 	wimp_pointer pointer;
137 	nserror res;
138 
139 	/* check if tooltip are required */
140 	if (!nsoption_bool(history_tooltip)) {
141 		return NSERROR_OK;
142 	}
143 
144 	/* ensure pointer has moved */
145 	if ((lhw->x == x) && (lhw->y == y)) {
146 		return NSERROR_OK;
147 	}
148 
149 	lhw->x = x;
150 	lhw->y = y;
151 
152 	res = local_history_get_url(lhw->session, x, y, &url);
153 	if (res != NSERROR_OK) {
154 		/* not over a tree entry => close tooltip window. */
155 		error = xwimp_close_window(dialog_tooltip);
156 		if (error) {
157 			NSLOG(netsurf, INFO, "xwimp_close_window: 0x%x: %s",
158 			      error->errnum, error->errmess);
159 			ro_warn_user("WimpError", error->errmess);
160 			return NSERROR_NOMEM;
161 		}
162 		return NSERROR_OK;
163 	}
164 
165 	/* get width of string */
166 	error = xwimptextop_string_width(nsurl_access(url),
167 					 nsurl_length(url) > 256 ? 256 : nsurl_length(url),
168 					 &width);
169 	if (error) {
170 		NSLOG(netsurf, INFO, "xwimptextop_string_width: 0x%x: %s",
171 		      error->errnum, error->errmess);
172 		ro_warn_user("WimpError", error->errmess);
173 		nsurl_unref(url);
174 		return NSERROR_NOMEM;
175 	}
176 
177 	ro_gui_set_icon_string(dialog_tooltip, 0, nsurl_access(url), true);
178 	nsurl_unref(url);
179 
180 	/* resize icon appropriately */
181 	ic.w = dialog_tooltip;
182 	ic.i = 0;
183 	error = xwimp_get_icon_state(&ic);
184 	if (error) {
185 		NSLOG(netsurf, INFO, "xwimp_get_icon_state: 0x%x: %s",
186 		      error->errnum, error->errmess);
187 		ro_warn_user("WimpError", error->errmess);
188 		return NSERROR_NOMEM;
189 	}
190 	error = xwimp_resize_icon(dialog_tooltip, 0,
191 				  ic.icon.extent.x0, ic.icon.extent.y0,
192 				  width + 16, ic.icon.extent.y1);
193 	if (error) {
194 		NSLOG(netsurf, INFO, "xwimp_resize_icon: 0x%x: %s",
195 		      error->errnum, error->errmess);
196 		ro_warn_user("WimpError", error->errmess);
197 		return NSERROR_NOMEM;
198 	}
199 
200 	state.w = dialog_tooltip;
201 	error = xwimp_get_window_state(&state);
202 	if (error) {
203 		NSLOG(netsurf, INFO, "xwimp_get_window_state: 0x%x: %s",
204 		      error->errnum, error->errmess);
205 		ro_warn_user("WimpError", error->errmess);
206 		return NSERROR_NOMEM;
207 	}
208 
209 	/* update window extent */
210 	box.x1 = width + 16;
211 	box.y0 = -36;
212 	error = xwimp_set_extent(dialog_tooltip, &box);
213 	if (error) {
214 		NSLOG(netsurf, INFO, "xwimp_set_extent: 0x%x: %s",
215 		      error->errnum, error->errmess);
216 		ro_warn_user("WimpError", error->errmess);
217 		return NSERROR_NOMEM;
218 	}
219 
220 	error = xwimp_get_pointer_info(&pointer);
221 	if (error) {
222 		NSLOG(netsurf, INFO, "xwimp_get_pointer_info: 0x%x: %s",
223 		      error->errnum, error->errmess);
224 		ro_warn_user("WimpError", error->errmess);
225 		return NSERROR_NOMEM;
226 	}
227 
228 	/* set visible area */
229 	state.visible.x0 = pointer.pos.x + 24;
230 	state.visible.y0 = pointer.pos.y - 22 - 36;
231 	state.visible.x1 = pointer.pos.x + 24 + width + 16;
232 	state.visible.y1 = pointer.pos.y - 22;
233 	state.next = wimp_TOP;
234 	/* open window */
235 	error = xwimp_open_window(PTR_WIMP_OPEN(&state));
236 	if (error) {
237 		NSLOG(netsurf, INFO, "xwimp_open_window: 0x%x: %s",
238 		      error->errnum, error->errmess);
239 		ro_warn_user("WimpError", error->errmess);
240 		return NSERROR_NOMEM;
241 	}
242 	return NSERROR_OK;
243 }
244 
245 
246 /**
247  * callback for mouse event on ro local_history window
248  *
249  * \param ro_cw The ro core window structure.
250  * \param mouse_state mouse state
251  * \param x location of event
252  * \param y location of event
253  * \return NSERROR_OK on sucess otherwise apropriate error code.
254  */
255 static nserror
ro_local_history_mouse(struct ro_corewindow * ro_cw,browser_mouse_state mouse_state,int x,int y)256 ro_local_history_mouse(struct ro_corewindow *ro_cw,
257 		       browser_mouse_state mouse_state,
258 		       int x, int y)
259 {
260 	struct ro_local_history_window *lhw;
261 
262 	lhw = (struct ro_local_history_window *)ro_cw;
263 
264 	switch (mouse_state) {
265 
266 	case BROWSER_MOUSE_HOVER:
267 		ro_local_history_tooltip(lhw, x, y);
268 		break;
269 
270 	case BROWSER_MOUSE_LEAVE:
271 		ro_gui_dialog_close(dialog_tooltip);
272 		break;
273 
274 	default:
275 		local_history_mouse_action(lhw->session, mouse_state, x, y);
276 		break;
277 	}
278 
279 	return NSERROR_OK;
280 }
281 
282 
283 /**
284  * Creates the window for the local_history tree.
285  *
286  * \return NSERROR_OK on success else appropriate error code on faliure.
287  */
288 static nserror
ro_local_history_init(struct browser_window * bw,struct ro_local_history_window ** win_out)289 ro_local_history_init(struct browser_window *bw,
290 		      struct ro_local_history_window **win_out)
291 {
292 	os_error *error;
293 	struct ro_local_history_window *ncwin;
294 	nserror res;
295 
296 	/* memoise window so it can be represented when necessary
297 	 * instead of recreating every time.
298 	 */
299 	if ((*win_out) != NULL) {
300 		res = local_history_set((*win_out)->session, bw);
301 		return res;
302 	}
303 
304 	ncwin = calloc(1, sizeof(*ncwin));
305 	if (ncwin == NULL) {
306 		return NSERROR_NOMEM;
307 	}
308 
309 	/* create window from template */
310 	error = xwimp_create_window(dialog_local_history_template,
311 				    &ncwin->core.wh);
312 	if (error) {
313 		NSLOG(netsurf, INFO, "xwimp_create_window: 0x%x: %s",
314 		      error->errnum, error->errmess);
315 		ro_warn_user("WimpError", error->errmess);
316 		free(ncwin);
317 		return NSERROR_NOMEM;
318 	}
319 
320 	/* initialise callbacks */
321 	ncwin->core.draw = ro_local_history_draw;
322 	ncwin->core.key = ro_local_history_key;
323 	ncwin->core.mouse = ro_local_history_mouse;
324 
325 	/* initialise core window */
326 	res = ro_corewindow_init(&ncwin->core,
327 				 NULL,
328 				 NULL,
329 				 0,
330 				 NULL);
331 	if (res != NSERROR_OK) {
332 		free(ncwin);
333 		return res;
334 	}
335 
336 	res = local_history_init(ncwin->core.cb_table,
337 				 (struct core_window *)ncwin,
338 				 bw,
339 				 &ncwin->session);
340 	if (res != NSERROR_OK) {
341 		free(ncwin);
342 		return res;
343 	}
344 
345 	*win_out = ncwin;
346 
347 	return NSERROR_OK;
348 }
349 
350 
351 /**
352  * open RISC OS local history window at the correct size
353  */
354 static nserror
ro_local_history_open(struct ro_local_history_window * lhw,wimp_w parent)355 ro_local_history_open(struct ro_local_history_window *lhw, wimp_w parent)
356 {
357 	nserror res;
358 	int width, height;
359 	os_box box = {0, 0, 0, 0};
360 	wimp_window_state state;
361 	os_error *error;
362 
363 	res = local_history_get_size(lhw->session, &width, &height);
364 	if (res != NSERROR_OK) {
365 		return res;
366 	}
367 
368 	width *= 2;
369 	height *= 2;
370 
371 	/* set extent */
372 	box.x1 = width;
373 	box.y0 = -height;
374 	error = xwimp_set_extent(lhw->core.wh, &box);
375 	if (error) {
376 		NSLOG(netsurf, INFO, "xwimp_set_extent: 0x%x: %s",
377 		      error->errnum, error->errmess);
378 		ro_warn_user("WimpError", error->errmess);
379 		return NSERROR_NOMEM;
380 	}
381 
382 	/* open full size */
383 	state.w = lhw->core.wh;
384 	error = xwimp_get_window_state(&state);
385 	if (error) {
386 		NSLOG(netsurf, INFO, "xwimp_get_window_state: 0x%x: %s",
387 		      error->errnum, error->errmess);
388 		ro_warn_user("WimpError", error->errmess);
389 		return NSERROR_NOMEM;
390 	}
391 	state.visible.x0 = 0;
392 	state.visible.y0 = 0;
393 	state.visible.x1 = width;
394 	state.visible.y1 = height;
395 	state.next = wimp_HIDDEN;
396 	error = xwimp_open_window(PTR_WIMP_OPEN(&state));
397 	if (error) {
398 		NSLOG(netsurf, INFO, "xwimp_open_window: 0x%x: %s",
399 		      error->errnum, error->errmess);
400 		ro_warn_user("WimpError", error->errmess);
401 		return NSERROR_NOMEM;
402 	}
403 
404 	ro_gui_dialog_open_persistent(parent, lhw->core.wh, true);
405 
406 	/* Give the window focus. */
407 	error = xwimp_set_caret_position(lhw->core.wh, -1, 0, 0, -1, 0);
408 	if (error) {
409 		NSLOG(netsurf, INFO,
410 		      "xwimp_set_caret_position: 0x%x : %s",
411 		      error->errnum,
412 		      error->errmess);
413 	}
414 
415 	local_history_scroll_to_cursor(lhw->session);
416 
417 	return NSERROR_OK;
418 }
419 
420 /* exported interface documented in riscos/local_history.h */
ro_gui_local_history_present(wimp_w parent,struct browser_window * bw)421 nserror ro_gui_local_history_present(wimp_w parent, struct browser_window *bw)
422 {
423 	nserror res;
424 
425 	res = ro_local_history_init(bw, &local_history_window);
426 	if (res == NSERROR_OK) {
427 		NSLOG(netsurf, INFO, "Presenting");
428 		res = ro_local_history_open(local_history_window, parent);
429 	} else {
430 		NSLOG(netsurf, INFO, "Failed presenting error code %d", res);
431 	}
432 
433 	return res;
434 }
435 
436 
437 /* exported interface documented in riscos/local_history.h */
ro_gui_local_history_initialise(void)438 void ro_gui_local_history_initialise(void)
439 {
440 	dialog_local_history_template = ro_gui_dialog_load_template("history");
441 }
442 
443 
444 /* exported interface documented in riscos/local_history.h */
ro_gui_local_history_finalise(void)445 nserror ro_gui_local_history_finalise(void)
446 {
447 	nserror res;
448 
449 	if (local_history_window == NULL) {
450 		return NSERROR_OK;
451 	}
452 
453 	res = local_history_fini(local_history_window->session);
454 	if (res == NSERROR_OK) {
455 		res = ro_corewindow_fini(&local_history_window->core);
456 
457 		free(local_history_window);
458 		local_history_window = NULL;
459 	}
460 
461 	return res;
462 }
463