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