]> code.delx.au - gnu-emacs/blob - src/xwidget.c
Use eldoc-documentation-functions
[gnu-emacs] / src / xwidget.c
1 /* Support for embedding graphical components in a buffer.
2
3 Copyright (C) 2011-2016 Free Software Foundation, Inc.
4
5 This file is part of GNU Emacs.
6
7 GNU Emacs 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, either version 3 of the License, or (at
10 your option) any later version.
11
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
19
20 #include <config.h>
21
22 #include "xwidget.h"
23
24 #include "lisp.h"
25 #include "blockinput.h"
26 #include "frame.h"
27 #include "keyboard.h"
28 #include "gtkutil.h"
29
30 #include <webkit/webkitwebview.h>
31 #include <webkit/webkitwebnavigationaction.h>
32 #include <webkit/webkitdownload.h>
33 #include <webkit/webkitwebpolicydecision.h>
34
35 static struct xwidget *
36 allocate_xwidget (void)
37 {
38 return ALLOCATE_PSEUDOVECTOR (struct xwidget, height, PVEC_XWIDGET);
39 }
40
41 static struct xwidget_view *
42 allocate_xwidget_view (void)
43 {
44 return ALLOCATE_PSEUDOVECTOR (struct xwidget_view, redisplayed,
45 PVEC_XWIDGET_VIEW);
46 }
47
48 #define XSETXWIDGET(a, b) XSETPSEUDOVECTOR (a, b, PVEC_XWIDGET)
49 #define XSETXWIDGET_VIEW(a, b) XSETPSEUDOVECTOR (a, b, PVEC_XWIDGET_VIEW)
50
51 static struct xwidget_view *xwidget_view_lookup (struct xwidget *,
52 struct window *);
53 static void webkit_document_load_finished_cb (WebKitWebView *, WebKitWebFrame *,
54 gpointer);
55 static gboolean webkit_download_cb (WebKitWebView *, WebKitDownload *, gpointer);
56
57 static gboolean
58 webkit_mime_type_policy_typedecision_requested_cb (WebKitWebView *,
59 WebKitWebFrame *,
60 WebKitNetworkRequest *,
61 gchar *,
62 WebKitWebPolicyDecision *,
63 gpointer);
64
65 static gboolean
66 webkit_new_window_policy_decision_requested_cb (WebKitWebView *,
67 WebKitWebFrame *,
68 WebKitNetworkRequest *,
69 WebKitWebNavigationAction *,
70 WebKitWebPolicyDecision *,
71 gpointer);
72
73 static gboolean
74 webkit_navigation_policy_decision_requested_cb (WebKitWebView *,
75 WebKitWebFrame *,
76 WebKitNetworkRequest *,
77 WebKitWebNavigationAction *,
78 WebKitWebPolicyDecision *,
79 gpointer);
80
81
82
83 DEFUN ("make-xwidget",
84 Fmake_xwidget, Smake_xwidget,
85 5, 6, 0,
86 doc: /* Make an xwidget of TYPE.
87 If BUFFER is nil, use the current buffer.
88 If BUFFER is a string and no such buffer exists, create it.
89 TYPE is a symbol which can take one of the following values:
90
91 - webkit
92
93 Returns the newly constructed xwidget, or nil if construction fails. */)
94 (Lisp_Object type,
95 Lisp_Object title, Lisp_Object width, Lisp_Object height,
96 Lisp_Object arguments, Lisp_Object buffer)
97 {
98 CHECK_SYMBOL (type);
99 CHECK_NATNUM (width);
100 CHECK_NATNUM (height);
101
102 struct xwidget *xw = allocate_xwidget ();
103 Lisp_Object val;
104 xw->type = type;
105 xw->title = title;
106 xw->buffer = NILP (buffer) ? Fcurrent_buffer () : Fget_buffer_create (buffer);
107 xw->height = XFASTINT (height);
108 xw->width = XFASTINT (width);
109 xw->kill_without_query = false;
110 XSETXWIDGET (val, xw);
111 Vxwidget_list = Fcons (val, Vxwidget_list);
112 xw->widgetwindow_osr = NULL;
113 xw->widget_osr = NULL;
114 xw->plist = Qnil;
115
116 if (EQ (xw->type, Qwebkit))
117 {
118 block_input ();
119 xw->widgetwindow_osr = gtk_offscreen_window_new ();
120 gtk_window_resize (GTK_WINDOW (xw->widgetwindow_osr), xw->width,
121 xw->height);
122
123 /* WebKit OSR is the only scrolled component at the moment. */
124 xw->widgetscrolledwindow_osr = NULL;
125
126 if (EQ (xw->type, Qwebkit))
127 {
128 xw->widgetscrolledwindow_osr = gtk_scrolled_window_new (NULL, NULL);
129 gtk_scrolled_window_set_min_content_height
130 (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr),
131 xw->height);
132 gtk_scrolled_window_set_min_content_width
133 (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr),
134 xw->width);
135 gtk_scrolled_window_set_policy
136 (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr),
137 GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS);
138
139 xw->widget_osr = webkit_web_view_new ();
140 gtk_container_add (GTK_CONTAINER (xw->widgetscrolledwindow_osr),
141 GTK_WIDGET (WEBKIT_WEB_VIEW (xw->widget_osr)));
142 }
143
144 gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr), xw->width,
145 xw->height);
146
147 if (EQ (xw->type, Qwebkit))
148 {
149 gtk_container_add (GTK_CONTAINER (xw->widgetwindow_osr),
150 xw->widgetscrolledwindow_osr);
151 }
152 else
153 {
154 gtk_container_add (GTK_CONTAINER (xw->widgetwindow_osr),
155 xw->widget_osr);
156 }
157
158 gtk_widget_show (xw->widget_osr);
159 gtk_widget_show (xw->widgetwindow_osr);
160 gtk_widget_show (xw->widgetscrolledwindow_osr);
161
162 /* Store some xwidget data in the gtk widgets for convenient
163 retrieval in the event handlers. */
164 g_object_set_data (G_OBJECT (xw->widget_osr), XG_XWIDGET, xw);
165 g_object_set_data (G_OBJECT (xw->widgetwindow_osr), XG_XWIDGET, xw);
166
167 /* signals */
168 if (EQ (xw->type, Qwebkit))
169 {
170 g_signal_connect (G_OBJECT (xw->widget_osr),
171 "document-load-finished",
172 G_CALLBACK (webkit_document_load_finished_cb), xw);
173
174 g_signal_connect (G_OBJECT (xw->widget_osr),
175 "download-requested",
176 G_CALLBACK (webkit_download_cb), xw);
177
178 g_signal_connect (G_OBJECT (xw->widget_osr),
179 "mime-type-policy-decision-requested",
180 G_CALLBACK
181 (webkit_mime_type_policy_typedecision_requested_cb),
182 xw);
183
184 g_signal_connect (G_OBJECT (xw->widget_osr),
185 "new-window-policy-decision-requested",
186 G_CALLBACK
187 (webkit_new_window_policy_decision_requested_cb),
188 xw);
189
190 g_signal_connect (G_OBJECT (xw->widget_osr),
191 "navigation-policy-decision-requested",
192 G_CALLBACK
193 (webkit_navigation_policy_decision_requested_cb),
194 xw);
195 }
196
197 unblock_input ();
198 }
199
200 return val;
201 }
202
203 DEFUN ("get-buffer-xwidgets", Fget_buffer_xwidgets, Sget_buffer_xwidgets,
204 1, 1, 0,
205 doc: /* Return a list of xwidgets associated with BUFFER.
206 BUFFER may be a buffer or the name of one. */)
207 (Lisp_Object buffer)
208 {
209 Lisp_Object xw, tail, xw_list;
210
211 if (NILP (buffer))
212 return Qnil;
213 buffer = Fget_buffer (buffer);
214 if (NILP (buffer))
215 return Qnil;
216
217 xw_list = Qnil;
218
219 for (tail = Vxwidget_list; CONSP (tail); tail = XCDR (tail))
220 {
221 xw = XCAR (tail);
222 if (XWIDGETP (xw) && EQ (Fxwidget_buffer (xw), buffer))
223 xw_list = Fcons (xw, xw_list);
224 }
225 return xw_list;
226 }
227
228 static bool
229 xwidget_hidden (struct xwidget_view *xv)
230 {
231 return xv->hidden;
232 }
233
234 static void
235 xwidget_show_view (struct xwidget_view *xv)
236 {
237 xv->hidden = false;
238 gtk_widget_show (xv->widgetwindow);
239 gtk_fixed_move (GTK_FIXED (xv->emacswindow),
240 xv->widgetwindow,
241 xv->x + xv->clip_left,
242 xv->y + xv->clip_top);
243 }
244
245 /* Hide an xwidget view. */
246 static void
247 xwidget_hide_view (struct xwidget_view *xv)
248 {
249 xv->hidden = true;
250 gtk_fixed_move (GTK_FIXED (xv->emacswindow), xv->widgetwindow,
251 10000, 10000);
252 }
253
254 /* When the off-screen webkit master view changes this signal is called.
255 It copies the bitmap from the off-screen instance. */
256 static gboolean
257 offscreen_damage_event (GtkWidget *widget, GdkEvent *event,
258 gpointer xv_widget)
259 {
260 /* Queue a redraw of onscreen widget.
261 There is a guard against receiving an invalid widget,
262 which should only happen if we failed to remove the
263 specific signal handler for the damage event. */
264 if (GTK_IS_WIDGET (xv_widget))
265 gtk_widget_queue_draw (GTK_WIDGET (xv_widget));
266 else
267 printf ("Warning, offscreen_damage_event received invalid xv pointer:%p\n",
268 xv_widget);
269
270 return FALSE;
271 }
272
273 static void
274 store_xwidget_event_string (struct xwidget *xw, const char *eventname,
275 const char *eventstr)
276 {
277 struct input_event event;
278 Lisp_Object xwl;
279 XSETXWIDGET (xwl, xw);
280 EVENT_INIT (event);
281 event.kind = XWIDGET_EVENT;
282 event.frame_or_window = Qnil;
283 event.arg = list3 (intern (eventname), xwl, build_string (eventstr));
284 kbd_buffer_store_event (&event);
285 }
286
287 /* TODO deprecated, use load-status. */
288 void
289 webkit_document_load_finished_cb (WebKitWebView *webkitwebview,
290 WebKitWebFrame *arg1,
291 gpointer data)
292 {
293 struct xwidget *xw = g_object_get_data (G_OBJECT (webkitwebview),
294 XG_XWIDGET);
295
296 store_xwidget_event_string (xw, "document-load-finished", "");
297 }
298
299 gboolean
300 webkit_download_cb (WebKitWebView *webkitwebview,
301 WebKitDownload *arg1,
302 gpointer data)
303 {
304 struct xwidget *xw = g_object_get_data (G_OBJECT (webkitwebview),
305 XG_XWIDGET);
306 store_xwidget_event_string (xw, "download-requested",
307 webkit_download_get_uri (arg1));
308 return FALSE;
309 }
310
311 static gboolean
312 webkit_mime_type_policy_typedecision_requested_cb (WebKitWebView *webView,
313 WebKitWebFrame *frame,
314 WebKitNetworkRequest *request,
315 gchar *mimetype,
316 WebKitWebPolicyDecision *policy_decision,
317 gpointer user_data)
318 {
319 /* This function makes webkit send a download signal for all unknown
320 mime types. TODO: Defer the decision to Lisp, so that it's
321 possible to make Emacs handle mime text for instance. */
322 if (!webkit_web_view_can_show_mime_type (webView, mimetype))
323 {
324 webkit_web_policy_decision_download (policy_decision);
325 return TRUE;
326 }
327 else
328 return FALSE;
329 }
330
331 static gboolean
332 webkit_new_window_policy_decision_requested_cb (WebKitWebView *webView,
333 WebKitWebFrame *frame,
334 WebKitNetworkRequest *request,
335 WebKitWebNavigationAction *navigation_action,
336 WebKitWebPolicyDecision *policy_decision,
337 gpointer user_data)
338 {
339 struct xwidget *xw = g_object_get_data (G_OBJECT (webView), XG_XWIDGET);
340 webkit_web_navigation_action_get_original_uri (navigation_action);
341
342 store_xwidget_event_string (xw, "new-window-policy-decision-requested",
343 webkit_web_navigation_action_get_original_uri
344 (navigation_action));
345 return FALSE;
346 }
347
348 static gboolean
349 webkit_navigation_policy_decision_requested_cb (WebKitWebView *webView,
350 WebKitWebFrame *frame,
351 WebKitNetworkRequest *request,
352 WebKitWebNavigationAction *navigation_action,
353 WebKitWebPolicyDecision *policy_decision,
354 gpointer user_data)
355 {
356 struct xwidget *xw = g_object_get_data (G_OBJECT (webView), XG_XWIDGET);
357 store_xwidget_event_string (xw, "navigation-policy-decision-requested",
358 webkit_web_navigation_action_get_original_uri
359 (navigation_action));
360 return FALSE;
361 }
362
363 /* For gtk3 offscreen rendered widgets. */
364 static gboolean
365 xwidget_osr_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer data)
366 {
367 struct xwidget *xw = g_object_get_data (G_OBJECT (widget), XG_XWIDGET);
368 struct xwidget_view *xv = g_object_get_data (G_OBJECT (widget),
369 XG_XWIDGET_VIEW);
370
371 cairo_rectangle (cr, 0, 0, xv->clip_right, xv->clip_bottom);
372 cairo_clip (cr);
373
374 if (xw->widgetscrolledwindow_osr != NULL)
375 gtk_widget_draw (xw->widgetscrolledwindow_osr, cr);
376 else
377 gtk_widget_draw (xw->widget_osr, cr);
378 return FALSE;
379 }
380
381 static gboolean
382 xwidget_osr_event_forward (GtkWidget *widget, GdkEvent *event,
383 gpointer user_data)
384 {
385 /* Copy events that arrive at the outer widget to the offscreen widget. */
386 struct xwidget *xw = g_object_get_data (G_OBJECT (widget), XG_XWIDGET);
387 GdkEvent *eventcopy = gdk_event_copy (event);
388 eventcopy->any.window = gtk_widget_get_window (xw->widget_osr);
389
390 /* TODO: This might leak events. They should be deallocated later,
391 perhaps in xwgir_event_cb. */
392 gtk_main_do_event (eventcopy);
393
394 /* Don't propagate this event further. */
395 return TRUE;
396 }
397
398 static gboolean
399 xwidget_osr_event_set_embedder (GtkWidget *widget, GdkEvent *event,
400 gpointer data)
401 {
402 struct xwidget_view *xv = data;
403 struct xwidget *xww = XXWIDGET (xv->model);
404 gdk_offscreen_window_set_embedder (gtk_widget_get_window
405 (xww->widgetwindow_osr),
406 gtk_widget_get_window (xv->widget));
407 return FALSE;
408 }
409
410
411 /* Initializes and does initial placement of an xwidget view on screen. */
412 static struct xwidget_view *
413 xwidget_init_view (struct xwidget *xww,
414 struct glyph_string *s,
415 int x, int y)
416 {
417 struct xwidget_view *xv = allocate_xwidget_view ();
418 Lisp_Object val;
419
420 XSETXWIDGET_VIEW (val, xv);
421 Vxwidget_view_list = Fcons (val, Vxwidget_view_list);
422
423 XSETWINDOW (xv->w, s->w);
424 XSETXWIDGET (xv->model, xww);
425
426 if (EQ (xww->type, Qwebkit))
427 {
428 xv->widget = gtk_drawing_area_new ();
429 /* Expose event handling. */
430 gtk_widget_set_app_paintable (xv->widget, TRUE);
431 gtk_widget_add_events (xv->widget, GDK_ALL_EVENTS_MASK);
432
433 /* Draw the view on damage-event. */
434 g_signal_connect (G_OBJECT (xww->widgetwindow_osr), "damage-event",
435 G_CALLBACK (offscreen_damage_event), xv->widget);
436
437 if (EQ (xww->type, Qwebkit))
438 {
439 g_signal_connect (G_OBJECT (xv->widget), "button-press-event",
440 G_CALLBACK (xwidget_osr_event_forward), NULL);
441 g_signal_connect (G_OBJECT (xv->widget), "button-release-event",
442 G_CALLBACK (xwidget_osr_event_forward), NULL);
443 g_signal_connect (G_OBJECT (xv->widget), "motion-notify-event",
444 G_CALLBACK (xwidget_osr_event_forward), NULL);
445 }
446 else
447 {
448 /* xwgir debug, orthogonal to forwarding. */
449 g_signal_connect (G_OBJECT (xv->widget), "enter-notify-event",
450 G_CALLBACK (xwidget_osr_event_set_embedder), xv);
451 }
452 g_signal_connect (G_OBJECT (xv->widget), "draw",
453 G_CALLBACK (xwidget_osr_draw_cb), NULL);
454 }
455
456 /* Widget realization.
457
458 Make container widget first, and put the actual widget inside the
459 container later. Drawing should crop container window if necessary
460 to handle case where xwidget is partially obscured by other Emacs
461 windows. Other containers than gtk_fixed where explored, but
462 gtk_fixed had the most predictable behavior so far. */
463
464 xv->emacswindow = FRAME_GTK_WIDGET (s->f);
465 xv->widgetwindow = gtk_fixed_new ();
466 gtk_widget_set_has_window (xv->widgetwindow, TRUE);
467 gtk_container_add (GTK_CONTAINER (xv->widgetwindow), xv->widget);
468
469 /* Store some xwidget data in the gtk widgets. */
470 g_object_set_data (G_OBJECT (xv->widget), XG_FRAME_DATA, s->f);
471 g_object_set_data (G_OBJECT (xv->widget), XG_XWIDGET, xww);
472 g_object_set_data (G_OBJECT (xv->widget), XG_XWIDGET_VIEW, xv);
473 g_object_set_data (G_OBJECT (xv->widgetwindow), XG_XWIDGET, xww);
474 g_object_set_data (G_OBJECT (xv->widgetwindow), XG_XWIDGET_VIEW, xv);
475
476 gtk_widget_set_size_request (GTK_WIDGET (xv->widget), xww->width,
477 xww->height);
478 gtk_widget_set_size_request (xv->widgetwindow, xww->width, xww->height);
479 gtk_fixed_put (GTK_FIXED (FRAME_GTK_WIDGET (s->f)), xv->widgetwindow, x, y);
480 xv->x = x;
481 xv->y = y;
482 gtk_widget_show_all (xv->widgetwindow);
483
484 return xv;
485 }
486
487 void
488 x_draw_xwidget_glyph_string (struct glyph_string *s)
489 {
490 /* This method is called by the redisplay engine and places the
491 xwidget on screen. Moving and clipping is done here. Also view
492 initialization. */
493 struct xwidget *xww = s->xwidget;
494 struct xwidget_view *xv;
495 int clip_right;
496 int clip_bottom;
497 int clip_top;
498 int clip_left;
499
500 /* FIXME: The result of this call is discarded.
501 What if the lookup fails? */
502 xwidget_view_lookup (xww, s->w);
503
504 int x = s->x;
505 int y = s->y + (s->height / 2) - (xww->height / 2);
506
507 /* Do initialization here in the display loop because there is no
508 other time to know things like window placement etc. */
509 xv = xwidget_init_view (xww, s, x, y);
510
511 int text_area_x, text_area_y, text_area_width, text_area_height;
512
513 window_box (s->w, TEXT_AREA, &text_area_x, &text_area_y,
514 &text_area_width, &text_area_height);
515 clip_left = max (0, text_area_x - x);
516 clip_right = max (clip_left,
517 min (xww->width, text_area_x + text_area_width - x));
518 clip_top = max (0, text_area_y - y);
519 clip_bottom = max (clip_top,
520 min (xww->height, text_area_y + text_area_height - y));
521
522 /* We are concerned with movement of the onscreen area. The area
523 might sit still when the widget actually moves. This happens
524 when an Emacs window border moves across a widget window. So, if
525 any corner of the outer widget clipping window moves, that counts
526 as movement here, even if it looks like no movement happens
527 because the widget sits still inside the clipping area. The
528 widget can also move inside the clipping area, which happens
529 later. */
530 bool moved = (xv->x + xv->clip_left != x + clip_left
531 || xv->y + xv->clip_top != y + clip_top);
532 xv->x = x;
533 xv->y = y;
534
535 /* Has it moved? */
536 if (moved)
537 gtk_fixed_move (GTK_FIXED (FRAME_GTK_WIDGET (s->f)),
538 xv->widgetwindow, x + clip_left, y + clip_top);
539
540 /* Clip the widget window if some parts happen to be outside
541 drawable area. An Emacs window is not a gtk window. A gtk window
542 covers the entire frame. Clipping might have changed even if we
543 haven't actually moved; try to figure out when we need to reclip
544 for real. */
545 if (xv->clip_right != clip_right
546 || xv->clip_bottom != clip_bottom
547 || xv->clip_top != clip_top || xv->clip_left != clip_left)
548 {
549 gtk_widget_set_size_request (xv->widgetwindow, clip_right - clip_left,
550 clip_bottom - clip_top);
551 gtk_fixed_move (GTK_FIXED (xv->widgetwindow), xv->widget, -clip_left,
552 -clip_top);
553
554 xv->clip_right = clip_right;
555 xv->clip_bottom = clip_bottom;
556 xv->clip_top = clip_top;
557 xv->clip_left = clip_left;
558 }
559
560 /* If emacs wants to repaint the area where the widget lives, queue
561 a redraw. It seems its possible to get out of sync with emacs
562 redraws so emacs background sometimes shows up instead of the
563 xwidgets background. It's just a visual glitch though. */
564 if (!xwidget_hidden (xv))
565 {
566 gtk_widget_queue_draw (xv->widgetwindow);
567 gtk_widget_queue_draw (xv->widget);
568 }
569 }
570
571 /* Macro that checks WEBKIT_IS_WEB_VIEW (xw->widget_osr) first. */
572 #define WEBKIT_FN_INIT() \
573 CHECK_XWIDGET (xwidget); \
574 struct xwidget *xw = XXWIDGET (xwidget); \
575 if (!xw->widget_osr || !WEBKIT_IS_WEB_VIEW (xw->widget_osr)) \
576 { \
577 printf ("ERROR xw->widget_osr does not hold a webkit instance\n"); \
578 return Qnil; \
579 }
580
581 DEFUN ("xwidget-webkit-goto-uri",
582 Fxwidget_webkit_goto_uri, Sxwidget_webkit_goto_uri,
583 2, 2, 0,
584 doc: /* Make the xwidget webkit instance referenced by XWIDGET browse URI. */)
585 (Lisp_Object xwidget, Lisp_Object uri)
586 {
587 WEBKIT_FN_INIT ();
588 CHECK_STRING (uri);
589 webkit_web_view_load_uri (WEBKIT_WEB_VIEW (xw->widget_osr), SSDATA (uri));
590 return Qnil;
591 }
592
593
594 DEFUN ("xwidget-webkit-execute-script",
595 Fxwidget_webkit_execute_script, Sxwidget_webkit_execute_script,
596 2, 2, 0,
597 doc: /* Make the Webkit XWIDGET execute JavaScript SCRIPT. */)
598 (Lisp_Object xwidget, Lisp_Object script)
599 {
600 WEBKIT_FN_INIT ();
601 CHECK_STRING (script);
602 webkit_web_view_execute_script (WEBKIT_WEB_VIEW (xw->widget_osr),
603 SSDATA (script));
604 return Qnil;
605 }
606
607 DEFUN ("xwidget-webkit-get-title",
608 Fxwidget_webkit_get_title, Sxwidget_webkit_get_title,
609 1, 1, 0,
610 doc: /* Return the title from the Webkit instance in XWIDGET.
611 This can be used to work around the lack of a return value from the
612 exec method. */ )
613 (Lisp_Object xwidget)
614 {
615 /* TODO support multibyte strings. */
616 WEBKIT_FN_INIT ();
617 const gchar *str =
618 webkit_web_view_get_title (WEBKIT_WEB_VIEW (xw->widget_osr));
619 if (str == 0)
620 {
621 /* TODO maybe return Qnil instead. I suppose webkit returns
622 null pointer when doc is not properly loaded or something. */
623 return build_string ("");
624 }
625 return build_string (str);
626 }
627
628 DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0,
629 doc: /* Resize XWIDGET. NEW_WIDTH, NEW_HEIGHT define the new size. */ )
630 (Lisp_Object xwidget, Lisp_Object new_width, Lisp_Object new_height)
631 {
632 CHECK_XWIDGET (xwidget);
633 CHECK_NATNUM (new_width);
634 CHECK_NATNUM (new_height);
635 struct xwidget *xw = XXWIDGET (xwidget);
636 int w = XFASTINT (new_width);
637 int h = XFASTINT (new_height);
638
639 xw->width = w;
640 xw->height = h;
641
642 /* If there is an offscreen widget resize it first. */
643 if (xw->widget_osr)
644 {
645 /* Use minimum size. */
646 gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr),
647 xw->width, xw->height);
648
649 gtk_window_resize (GTK_WINDOW (xw->widgetwindow_osr), xw->width,
650 xw->height);
651 gtk_scrolled_window_set_min_content_height
652 (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr),
653 xw->height);
654 gtk_scrolled_window_set_min_content_width
655 (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr),
656 xw->width);
657
658 gtk_container_resize_children (GTK_CONTAINER (xw->widgetwindow_osr));
659
660 }
661
662 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail); tail = XCDR (tail))
663 {
664 if (XWIDGET_VIEW_P (XCAR (tail)))
665 {
666 struct xwidget_view *xv = XXWIDGET_VIEW (XCAR (tail));
667 if (XXWIDGET (xv->model) == xw)
668 gtk_widget_set_size_request (GTK_WIDGET (xv->widget), xw->width,
669 xw->height);
670 }
671 }
672
673 return Qnil;
674 }
675
676
677
678 DEFUN ("xwidget-set-adjustment",
679 Fxwidget_set_adjustment, Sxwidget_set_adjustment, 4, 4, 0,
680 doc: /* Set native scrolling for XWIDGET.
681 AXIS can be `vertical' or `horizontal'.
682 If RELATIVE is t, scroll relative, otherwise absolutely.
683 VALUE is the amount to scroll, either relatively or absolutely. */)
684 (Lisp_Object xwidget, Lisp_Object axis, Lisp_Object relative,
685 Lisp_Object value)
686 {
687 CHECK_XWIDGET (xwidget);
688 CHECK_NUMBER (value);
689 struct xwidget *xw = XXWIDGET (xwidget);
690 GtkAdjustment *adjustment
691 = ((EQ (Qhorizontal, axis)
692 ? gtk_scrolled_window_get_hadjustment
693 : gtk_scrolled_window_get_vadjustment)
694 (GTK_SCROLLED_WINDOW (xw->widgetscrolledwindow_osr)));
695 double final_value = XINT (value);
696 if (EQ (Qt, relative))
697 final_value += gtk_adjustment_get_value (adjustment);
698 gtk_adjustment_set_value (adjustment, final_value);
699 return Qnil;
700 }
701
702
703 DEFUN ("xwidget-size-request",
704 Fxwidget_size_request, Sxwidget_size_request,
705 1, 1, 0,
706 doc: /* Return the desired size of the XWIDGET.
707 This can be used to read the xwidget desired size, and resizes the
708 Emacs allocated area accordingly. */)
709 (Lisp_Object xwidget)
710 {
711 CHECK_XWIDGET (xwidget);
712 GtkRequisition requisition;
713 gtk_widget_size_request (XXWIDGET (xwidget)->widget_osr, &requisition);
714 return list2 (make_number (requisition.width),
715 make_number (requisition.height));
716 }
717
718 DEFUN ("xwidgetp",
719 Fxwidgetp, Sxwidgetp,
720 1, 1, 0,
721 doc: /* Return t if OBJECT is an xwidget. */)
722 (Lisp_Object object)
723 {
724 return XWIDGETP (object) ? Qt : Qnil;
725 }
726
727 DEFUN ("xwidget-view-p",
728 Fxwidget_view_p, Sxwidget_view_p,
729 1, 1, 0,
730 doc: /* Return t if OBJECT is an xwidget-view. */)
731 (Lisp_Object object)
732 {
733 return XWIDGET_VIEW_P (object) ? Qt : Qnil;
734 }
735
736 DEFUN ("xwidget-info",
737 Fxwidget_info, Sxwidget_info,
738 1, 1, 0,
739 doc: /* Return XWIDGET properties in a vector.
740 Currently [TYPE TITLE WIDTH HEIGHT]. */)
741 (Lisp_Object xwidget)
742 {
743 CHECK_XWIDGET (xwidget);
744 struct xwidget *xw = XXWIDGET (xwidget);
745 return CALLN (Fvector, xw->type, xw->title,
746 make_natnum (xw->width), make_natnum (xw->height));
747 }
748
749 DEFUN ("xwidget-view-info",
750 Fxwidget_view_info, Sxwidget_view_info,
751 1, 1, 0,
752 doc: /* Return properties of XWIDGET-VIEW in a vector.
753 Currently [X Y CLIP_RIGHT CLIP_BOTTOM CLIP_TOP CLIP_LEFT]. */)
754 (Lisp_Object xwidget_view)
755 {
756 CHECK_XWIDGET_VIEW (xwidget_view);
757 struct xwidget_view *xv = XXWIDGET_VIEW (xwidget_view);
758 return CALLN (Fvector, make_number (xv->x), make_number (xv->y),
759 make_number (xv->clip_right), make_number (xv->clip_bottom),
760 make_number (xv->clip_top), make_number (xv->clip_left));
761 }
762
763 DEFUN ("xwidget-view-model",
764 Fxwidget_view_model, Sxwidget_view_model,
765 1, 1, 0,
766 doc: /* Return the model associated with XWIDGET-VIEW. */)
767 (Lisp_Object xwidget_view)
768 {
769 CHECK_XWIDGET_VIEW (xwidget_view);
770 return XXWIDGET_VIEW (xwidget_view)->model;
771 }
772
773 DEFUN ("xwidget-view-window",
774 Fxwidget_view_window, Sxwidget_view_window,
775 1, 1, 0,
776 doc: /* Return the window of XWIDGET-VIEW. */)
777 (Lisp_Object xwidget_view)
778 {
779 CHECK_XWIDGET_VIEW (xwidget_view);
780 return XXWIDGET_VIEW (xwidget_view)->w;
781 }
782
783
784 DEFUN ("delete-xwidget-view",
785 Fdelete_xwidget_view, Sdelete_xwidget_view,
786 1, 1, 0,
787 doc: /* Delete the XWIDGET-VIEW. */)
788 (Lisp_Object xwidget_view)
789 {
790 CHECK_XWIDGET_VIEW (xwidget_view);
791 struct xwidget_view *xv = XXWIDGET_VIEW (xwidget_view);
792 gtk_widget_destroy (xv->widgetwindow);
793 Vxwidget_view_list = Fdelq (xwidget_view, Vxwidget_view_list);
794 /* xv->model still has signals pointing to the view. There can be
795 several views. Find the matching signals and delete them all. */
796 g_signal_handlers_disconnect_matched (XXWIDGET (xv->model)->widgetwindow_osr,
797 G_SIGNAL_MATCH_DATA,
798 0, 0, 0, 0,
799 xv->widget);
800 return Qnil;
801 }
802
803 DEFUN ("xwidget-view-lookup",
804 Fxwidget_view_lookup, Sxwidget_view_lookup,
805 1, 2, 0,
806 doc: /* Return the xwidget-view associated with XWIDGET in WINDOW.
807 If WINDOW is unspecified or nil, use the selected window.
808 Return nil if no association is found. */)
809 (Lisp_Object xwidget, Lisp_Object window)
810 {
811 CHECK_XWIDGET (xwidget);
812
813 if (NILP (window))
814 window = Fselected_window ();
815 CHECK_WINDOW (window);
816
817 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
818 tail = XCDR (tail))
819 {
820 Lisp_Object xwidget_view = XCAR (tail);
821 if (EQ (Fxwidget_view_model (xwidget_view), xwidget)
822 && EQ (Fxwidget_view_window (xwidget_view), window))
823 return xwidget_view;
824 }
825
826 return Qnil;
827 }
828
829 DEFUN ("xwidget-plist",
830 Fxwidget_plist, Sxwidget_plist,
831 1, 1, 0,
832 doc: /* Return the plist of XWIDGET. */)
833 (Lisp_Object xwidget)
834 {
835 CHECK_XWIDGET (xwidget);
836 return XXWIDGET (xwidget)->plist;
837 }
838
839 DEFUN ("xwidget-buffer",
840 Fxwidget_buffer, Sxwidget_buffer,
841 1, 1, 0,
842 doc: /* Return the buffer of XWIDGET. */)
843 (Lisp_Object xwidget)
844 {
845 CHECK_XWIDGET (xwidget);
846 return XXWIDGET (xwidget)->buffer;
847 }
848
849 DEFUN ("set-xwidget-plist",
850 Fset_xwidget_plist, Sset_xwidget_plist,
851 2, 2, 0,
852 doc: /* Replace the plist of XWIDGET with PLIST.
853 Returns PLIST. */)
854 (Lisp_Object xwidget, Lisp_Object plist)
855 {
856 CHECK_XWIDGET (xwidget);
857 CHECK_LIST (plist);
858
859 XXWIDGET (xwidget)->plist = plist;
860 return plist;
861 }
862
863 DEFUN ("set-xwidget-query-on-exit-flag",
864 Fset_xwidget_query_on_exit_flag, Sset_xwidget_query_on_exit_flag,
865 2, 2, 0,
866 doc: /* Specify if query is needed for XWIDGET when Emacs is exited.
867 If the second argument FLAG is non-nil, Emacs will query the user before
868 exiting or killing a buffer if XWIDGET is running.
869 This function returns FLAG. */)
870 (Lisp_Object xwidget, Lisp_Object flag)
871 {
872 CHECK_XWIDGET (xwidget);
873 XXWIDGET (xwidget)->kill_without_query = NILP (flag);
874 return flag;
875 }
876
877 DEFUN ("xwidget-query-on-exit-flag",
878 Fxwidget_query_on_exit_flag, Sxwidget_query_on_exit_flag,
879 1, 1, 0,
880 doc: /* Return the current value of the query-on-exit flag for XWIDGET. */)
881 (Lisp_Object xwidget)
882 {
883 CHECK_XWIDGET (xwidget);
884 return (XXWIDGET (xwidget)->kill_without_query ? Qnil : Qt);
885 }
886
887 void
888 syms_of_xwidget (void)
889 {
890 defsubr (&Smake_xwidget);
891 defsubr (&Sxwidgetp);
892 DEFSYM (Qxwidgetp, "xwidgetp");
893 defsubr (&Sxwidget_view_p);
894 DEFSYM (Qxwidget_view_p, "xwidget-view-p");
895 defsubr (&Sxwidget_info);
896 defsubr (&Sxwidget_view_info);
897 defsubr (&Sxwidget_resize);
898 defsubr (&Sget_buffer_xwidgets);
899 defsubr (&Sxwidget_view_model);
900 defsubr (&Sxwidget_view_window);
901 defsubr (&Sxwidget_view_lookup);
902 defsubr (&Sxwidget_query_on_exit_flag);
903 defsubr (&Sset_xwidget_query_on_exit_flag);
904
905 defsubr (&Sxwidget_webkit_goto_uri);
906 defsubr (&Sxwidget_webkit_execute_script);
907 defsubr (&Sxwidget_webkit_get_title);
908 DEFSYM (Qwebkit, "webkit");
909
910 defsubr (&Sxwidget_size_request);
911 defsubr (&Sdelete_xwidget_view);
912
913 defsubr (&Sxwidget_plist);
914 defsubr (&Sxwidget_buffer);
915 defsubr (&Sset_xwidget_plist);
916
917 defsubr (&Sxwidget_set_adjustment);
918
919 DEFSYM (Qxwidget, "xwidget");
920
921 DEFSYM (QCxwidget, ":xwidget");
922 DEFSYM (QCtitle, ":title");
923
924 /* Do not forget to update the docstring of make-xwidget if you add
925 new types. */
926
927 DEFSYM (Qvertical, "vertical");
928 DEFSYM (Qhorizontal, "horizontal");
929
930 DEFSYM (QCplist, ":plist");
931
932 DEFVAR_LISP ("xwidget-list", Vxwidget_list,
933 doc: /* xwidgets list. */);
934 Vxwidget_list = Qnil;
935
936 DEFVAR_LISP ("xwidget-view-list", Vxwidget_view_list,
937 doc: /* xwidget views list. */);
938 Vxwidget_view_list = Qnil;
939
940 Fprovide (intern ("xwidget-internal"), Qnil);
941 }
942
943
944 /* Value is non-zero if OBJECT is a valid Lisp xwidget specification. A
945 valid xwidget specification is a list whose car is the symbol
946 `xwidget', and whose rest is a property list. The property list must
947 contain a value for key `:type'. That value must be the name of a
948 supported xwidget type. The rest of the property list depends on the
949 xwidget type. */
950
951 bool
952 valid_xwidget_spec_p (Lisp_Object object)
953 {
954 return CONSP (object) && EQ (XCAR (object), Qxwidget);
955 }
956
957
958 /* Find a value associated with key in spec. */
959 static Lisp_Object
960 xwidget_spec_value (Lisp_Object spec, Lisp_Object key)
961 {
962 Lisp_Object tail;
963
964 eassert (valid_xwidget_spec_p (spec));
965
966 for (tail = XCDR (spec);
967 CONSP (tail) && CONSP (XCDR (tail)); tail = XCDR (XCDR (tail)))
968 {
969 if (EQ (XCAR (tail), key))
970 return XCAR (XCDR (tail));
971 }
972
973 return Qnil;
974 }
975
976
977 void
978 xwidget_view_delete_all_in_window (struct window *w)
979 {
980 struct xwidget_view *xv = NULL;
981 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
982 tail = XCDR (tail))
983 {
984 if (XWIDGET_VIEW_P (XCAR (tail)))
985 {
986 xv = XXWIDGET_VIEW (XCAR (tail));
987 if (XWINDOW (xv->w) == w)
988 {
989 Fdelete_xwidget_view (XCAR (tail));
990 }
991 }
992 }
993 }
994
995 static struct xwidget_view *
996 xwidget_view_lookup (struct xwidget *xw, struct window *w)
997 {
998 Lisp_Object xwidget, window, ret;
999 XSETXWIDGET (xwidget, xw);
1000 XSETWINDOW (window, w);
1001
1002 ret = Fxwidget_view_lookup (xwidget, window);
1003
1004 return EQ (ret, Qnil) ? NULL : XXWIDGET_VIEW (ret);
1005 }
1006
1007 struct xwidget *
1008 lookup_xwidget (Lisp_Object spec)
1009 {
1010 /* When a xwidget lisp spec is found initialize the C struct that is
1011 used in the C code. This is done by redisplay so values change
1012 if the spec changes. So, take special care of one-shot events. */
1013 Lisp_Object value;
1014 struct xwidget *xw;
1015
1016 value = xwidget_spec_value (spec, QCxwidget);
1017 xw = XXWIDGET (value);
1018
1019 return xw;
1020 }
1021
1022 /* Set up detection of touched xwidget. */
1023 static void
1024 xwidget_start_redisplay (void)
1025 {
1026 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
1027 tail = XCDR (tail))
1028 {
1029 if (XWIDGET_VIEW_P (XCAR (tail)))
1030 XXWIDGET_VIEW (XCAR (tail))->redisplayed = false;
1031 }
1032 }
1033
1034 /* The xwidget was touched during redisplay, so it isn't a candidate
1035 for hiding. */
1036 static void
1037 xwidget_touch (struct xwidget_view *xv)
1038 {
1039 xv->redisplayed = true;
1040 }
1041
1042 static bool
1043 xwidget_touched (struct xwidget_view *xv)
1044 {
1045 return xv->redisplayed;
1046 }
1047
1048 /* Redisplay has ended, now we should hide untouched xwidgets. */
1049 void
1050 xwidget_end_redisplay (struct window *w, struct glyph_matrix *matrix)
1051 {
1052 int i;
1053 int area;
1054
1055 xwidget_start_redisplay ();
1056 /* Iterate desired glyph matrix of window here, hide gtk widgets
1057 not in the desired matrix.
1058
1059 This only takes care of xwidgets in active windows. If a window
1060 goes away from the screen, xwidget views must be deleted.
1061
1062 dump_glyph_matrix (matrix, 2); */
1063 for (i = 0; i < matrix->nrows; ++i)
1064 {
1065 /* dump_glyph_row (MATRIX_ROW (matrix, i), i, glyphs); */
1066 struct glyph_row *row;
1067 row = MATRIX_ROW (matrix, i);
1068 if (row->enabled_p)
1069 for (area = LEFT_MARGIN_AREA; area < LAST_AREA; ++area)
1070 {
1071 struct glyph *glyph = row->glyphs[area];
1072 struct glyph *glyph_end = glyph + row->used[area];
1073 for (; glyph < glyph_end; ++glyph)
1074 if (glyph->type == XWIDGET_GLYPH)
1075 {
1076 /* The only call to xwidget_end_redisplay is in dispnew.
1077 xwidget_end_redisplay (w->current_matrix); */
1078 struct xwidget_view *xv
1079 = xwidget_view_lookup (glyph->u.xwidget, w);
1080 /* FIXME: Is it safe to assume xwidget_view_lookup
1081 always succeeds here? If so, this comment can be removed.
1082 If not, the code probably needs fixing. */
1083 eassume (xv);
1084 xwidget_touch (xv);
1085 }
1086 }
1087 }
1088
1089 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail);
1090 tail = XCDR (tail))
1091 {
1092 if (XWIDGET_VIEW_P (XCAR (tail)))
1093 {
1094 struct xwidget_view *xv = XXWIDGET_VIEW (XCAR (tail));
1095
1096 /* "touched" is only meaningful for the current window, so
1097 disregard other views. */
1098 if (XWINDOW (xv->w) == w)
1099 {
1100 if (xwidget_touched (xv))
1101 xwidget_show_view (xv);
1102 else
1103 xwidget_hide_view (xv);
1104 }
1105 }
1106 }
1107 }
1108
1109 /* Kill all xwidget in BUFFER. */
1110 void
1111 kill_buffer_xwidgets (Lisp_Object buffer)
1112 {
1113 Lisp_Object tail, xwidget;
1114 for (tail = Fget_buffer_xwidgets (buffer); CONSP (tail); tail = XCDR (tail))
1115 {
1116 xwidget = XCAR (tail);
1117 Vxwidget_list = Fdelq (xwidget, Vxwidget_list);
1118 /* TODO free the GTK things in xw. */
1119 {
1120 CHECK_XWIDGET (xwidget);
1121 struct xwidget *xw = XXWIDGET (xwidget);
1122 if (xw->widget_osr && xw->widgetwindow_osr)
1123 {
1124 gtk_widget_destroy (xw->widget_osr);
1125 gtk_widget_destroy (xw->widgetwindow_osr);
1126 }
1127 }
1128 }
1129 }