]> Pileus Git - ~andy/gtk/blob - gtk/gtksocket.c
Added layout widget for scrolling arbitrarily big areas. Added plug/socket
[~andy/gtk] / gtk / gtksocket.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 /* By Owen Taylor <otaylor@gtk.org>              98/4/4 */
20
21 #include "gdk/gdkx.h"
22 #include "gdk/gdkkeysyms.h"
23 #include "gtkwindow.h"
24 #include "gtksignal.h"
25 #include "gtksocket.h"
26 #include "gtkdnd.h"
27
28 /* Forward declararations */
29
30 static void gtk_socket_class_init               (GtkSocketClass    *klass);
31 static void gtk_socket_init                     (GtkSocket         *socket);
32 static void gtk_socket_realize                  (GtkWidget        *widget);
33 static void gtk_socket_unrealize                (GtkWidget        *widget);
34 static void gtk_socket_size_request             (GtkWidget      *widget,
35                                                GtkRequisition *requisition);
36 static void gtk_socket_size_allocate            (GtkWidget     *widget,
37                                                GtkAllocation *allocation);
38 static gint gtk_socket_focus_in_event           (GtkWidget *widget, 
39                                                  GdkEventFocus *event);
40 static void gtk_socket_claim_focus              (GtkSocket *socket);
41 static gint gtk_socket_focus_out_event          (GtkWidget *widget, 
42                                                  GdkEventFocus *event);
43 static void gtk_socket_send_configure_event     (GtkSocket *socket);
44 static gint gtk_socket_focus                    (GtkContainer *container, 
45                                                  GtkDirectionType direction);
46 static GdkFilterReturn gtk_socket_filter_func   (GdkXEvent *gdk_xevent, 
47                                                  GdkEvent *event, 
48                                                  gpointer data);
49
50 #ifdef DEBUG_PLUGSOCKET
51 #define DPRINTF(arg) g_print arg
52 #else
53 #define DPRINTF(arg)
54 #endif
55
56 /* From Tk */
57 #define EMBEDDED_APP_WANTS_FOCUS NotifyNormal+20
58
59 /* Local data */
60
61 static GtkWidgetClass *parent_class = NULL;
62
63 guint
64 gtk_socket_get_type ()
65 {
66   static guint socket_type = 0;
67
68   if (!socket_type)
69     {
70       GtkTypeInfo socket_info =
71       {
72         "GtkSocket",
73         sizeof (GtkSocket),
74         sizeof (GtkSocketClass),
75         (GtkClassInitFunc) gtk_socket_class_init,
76         (GtkObjectInitFunc) gtk_socket_init,
77         (GtkArgSetFunc) NULL,
78         (GtkArgGetFunc) NULL
79       };
80
81       socket_type = gtk_type_unique (gtk_container_get_type (), &socket_info);
82     }
83
84   return socket_type;
85 }
86
87 static void
88 gtk_socket_class_init (GtkSocketClass *class)
89 {
90   GtkObjectClass *object_class;
91   GtkWidgetClass *widget_class;
92   GtkContainerClass *container_class;
93
94   object_class = (GtkObjectClass*) class;
95   widget_class = (GtkWidgetClass*) class;
96   container_class = (GtkContainerClass*) class;
97
98   parent_class = gtk_type_class (gtk_widget_get_type ());
99
100   widget_class->realize = gtk_socket_realize;
101   widget_class->unrealize = gtk_socket_unrealize;
102   widget_class->size_request = gtk_socket_size_request;
103   widget_class->size_allocate = gtk_socket_size_allocate;
104   widget_class->focus_in_event = gtk_socket_focus_in_event;
105   widget_class->focus_out_event = gtk_socket_focus_out_event;
106
107   container_class->focus = gtk_socket_focus;
108 }
109
110 static void
111 gtk_socket_init (GtkSocket *socket)
112 {
113   socket->request_width = 0;
114   socket->request_height = 0;
115   socket->current_width = 0;
116   socket->current_height = 0;
117   
118   socket->plug_window = NULL;
119   socket->same_app = FALSE;
120   socket->focus_in = FALSE;
121   socket->have_size = FALSE;
122   socket->need_map = FALSE;
123 }
124
125 GtkWidget*
126 gtk_socket_new ()
127 {
128   GtkSocket *socket;
129
130   socket = gtk_type_new (gtk_socket_get_type ());
131
132   return GTK_WIDGET (socket);
133 }
134
135 void           
136 gtk_socket_steal (GtkSocket *socket, guint32 id)
137 {
138   GtkWidget *widget;
139
140   widget = GTK_WIDGET (socket);
141   
142   socket->plug_window = gdk_window_lookup (id);
143
144   if (socket->plug_window && socket->plug_window->user_data)
145     {
146       GtkWidget *child_widget = GTK_WIDGET (socket->plug_window->user_data);
147
148       g_warning("Stealing from same app not yet implemented");
149       
150       socket->same_app = TRUE;
151     }
152   else
153     {
154       socket->plug_window = gdk_window_foreign_new (id);
155       socket->same_app = FALSE;
156       socket->have_size = FALSE;
157
158       XSelectInput (GDK_DISPLAY (),
159                     GDK_WINDOW_XWINDOW(socket->plug_window),
160                     StructureNotifyMask | PropertyChangeMask);
161
162       gtk_widget_queue_resize (widget);
163     }
164
165   gdk_window_hide (socket->plug_window);
166   gdk_window_reparent (socket->plug_window, widget->window, 0, 0);
167   socket->need_map = TRUE;
168 }
169
170 static void
171 gtk_socket_realize (GtkWidget *widget)
172 {
173   GtkSocket *socket;
174   GdkWindowAttr attributes;
175   gint attributes_mask;
176   XWindowAttributes xattrs;
177
178   g_return_if_fail (widget != NULL);
179   g_return_if_fail (GTK_IS_SOCKET (widget));
180
181   socket = GTK_SOCKET (widget);
182   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
183
184   attributes.window_type = GDK_WINDOW_CHILD;
185   attributes.x = widget->allocation.x;
186   attributes.y = widget->allocation.y;
187   attributes.width = widget->allocation.width;
188   attributes.height = widget->allocation.height;
189   attributes.wclass = GDK_INPUT_OUTPUT;
190   attributes.visual = gtk_widget_get_visual (widget);
191   attributes.colormap = gtk_widget_get_colormap (widget);
192   attributes.event_mask = GDK_FOCUS_CHANGE_MASK;
193
194   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
195
196   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), 
197                                    &attributes, attributes_mask);
198   gdk_window_set_user_data (widget->window, socket);
199
200   widget->style = gtk_style_attach (widget->style, widget->window);
201   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
202
203   XGetWindowAttributes (GDK_DISPLAY (),
204                         GDK_WINDOW_XWINDOW (widget->window),
205                         &xattrs);
206
207   XSelectInput (GDK_DISPLAY (),
208                 GDK_WINDOW_XWINDOW(widget->window), 
209                 xattrs.your_event_mask | 
210                 SubstructureNotifyMask | SubstructureRedirectMask);
211
212   gdk_window_add_filter (widget->window, gtk_socket_filter_func, widget);
213
214   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
215
216   /* We sync here so that we make sure that if the XID for
217    * our window is passed to another application, SubstructureRedirectMask
218    * will be set by the time the other app creates its window.
219    */
220   gdk_flush();
221 }
222
223 static void
224 gtk_socket_unrealize (GtkWidget *widget)
225 {
226   GtkSocket *socket;
227
228   g_return_if_fail (widget != NULL);
229   g_return_if_fail (GTK_IS_SOCKET (widget));
230
231   socket = GTK_SOCKET (widget);
232
233   if (socket->plug_window)
234     {
235       GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
236       if (toplevel && GTK_IS_WINDOW (toplevel))
237         gtk_window_remove_embedded_xid (GTK_WINDOW (toplevel), 
238                                         GDK_WINDOW_XWINDOW (socket->plug_window));
239     }
240
241   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
242     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
243 }
244   
245 static void 
246 gtk_socket_size_request (GtkWidget      *widget,
247                          GtkRequisition *requisition)
248 {
249   GtkSocket *socket;
250
251   g_return_if_fail (widget != NULL);
252   g_return_if_fail (GTK_IS_SOCKET (widget));
253   g_return_if_fail (requisition != NULL);
254   
255   socket = GTK_SOCKET (widget);
256
257   if (!socket->have_size && socket->plug_window)
258     {
259       XSizeHints hints;
260       long supplied;
261       
262       if (XGetWMNormalHints (GDK_DISPLAY(),
263                              GDK_WINDOW_XWINDOW (socket->plug_window),
264                              &hints, &supplied))
265         {
266           /* This is obsolete, according the X docs, but many programs
267            * still use it */
268           if (hints.flags & (PSize | USSize))
269             {
270               socket->request_width = hints.width;
271               socket->request_height = hints.height;
272             }
273           else if (hints.flags & PMinSize)
274             {
275               socket->request_width = hints.min_width;
276               socket->request_height = hints.min_height;
277             }
278           else if (hints.flags & PBaseSize)
279             {
280               socket->request_width = hints.base_width;
281               socket->request_height = hints.base_height;
282             }
283         }
284       socket->have_size = TRUE; /* don't check again? */
285     }
286
287   requisition->width = socket->request_width;
288   requisition->height = socket->request_height;
289 }
290
291 static void
292 gtk_socket_size_allocate (GtkWidget     *widget,
293                           GtkAllocation *allocation)
294 {
295   GtkSocket *socket;
296
297   g_return_if_fail (widget != NULL);
298   g_return_if_fail (GTK_IS_SOCKET (widget));
299   g_return_if_fail (allocation != NULL);
300
301   socket = GTK_SOCKET (widget);
302
303   widget->allocation = *allocation;
304   if (GTK_WIDGET_REALIZED (widget))
305     {
306       gdk_window_move_resize (widget->window,
307                               allocation->x, allocation->y,
308                               allocation->width, allocation->height);
309
310       if (socket->plug_window)
311         {
312           if (!socket->need_map &&
313               (allocation->width == socket->current_width) &&
314               (allocation->height == socket->current_height))
315             {
316               gtk_socket_send_configure_event (socket);
317               DPRINTF(( "No change: %d %d\n",
318                       allocation->width, allocation->height));
319             }
320           else
321             {
322               gdk_window_move_resize (socket->plug_window,
323                                       0, 0,
324                                       allocation->width, allocation->height);
325               DPRINTF(("configuring: %d %d\n",
326                       allocation->width, allocation->height));
327               socket->current_width = allocation->width;
328               socket->current_height = allocation->height;
329             }
330
331           if (socket->need_map)
332             {
333               gdk_window_show (socket->plug_window);
334               socket->need_map = FALSE;
335             }
336
337         }
338     }
339 }
340
341 static gint
342 gtk_socket_focus_in_event (GtkWidget *widget, GdkEventFocus *event)
343 {
344   GtkSocket *socket;
345   g_return_val_if_fail (GTK_IS_SOCKET (widget), FALSE);
346   socket = GTK_SOCKET (widget);
347
348   DPRINTF (( "Got focus\n"));
349
350   if (socket->focus_in && socket->plug_window)
351     XSetInputFocus (GDK_DISPLAY (),
352                     GDK_WINDOW_XWINDOW (socket->plug_window),
353                     RevertToParent, GDK_CURRENT_TIME);
354   
355   return TRUE;
356 }
357
358 static gint
359 gtk_socket_focus_out_event (GtkWidget *widget, GdkEventFocus *event)
360 {
361   GtkWidget *toplevel;
362   GtkSocket *socket;
363
364   g_return_val_if_fail (GTK_IS_SOCKET (widget), FALSE);
365   socket = GTK_SOCKET (widget);
366
367   toplevel = gtk_widget_get_ancestor (widget, gtk_window_get_type());
368   
369   if (toplevel)
370     {
371       XSetInputFocus (GDK_DISPLAY (),
372                       GDK_WINDOW_XWINDOW (toplevel->window),
373                       RevertToParent, CurrentTime); /* FIXME? */
374     }
375
376   socket->focus_in = FALSE;
377
378   return TRUE;
379 }
380
381 static void
382 gtk_socket_claim_focus (GtkSocket *socket)
383 {
384       
385   socket->focus_in = TRUE;
386   
387   /* Oh, the trickery... */
388   
389   GTK_WIDGET_SET_FLAGS (socket, GTK_CAN_FOCUS);
390   gtk_widget_grab_focus (GTK_WIDGET (socket));
391   GTK_WIDGET_UNSET_FLAGS (socket, GTK_CAN_FOCUS);
392   
393   /* FIXME: we might grab the focus even if we don't have
394    * it as an app... (and see _focus_in ()) */
395   if (socket->plug_window)
396     XSetInputFocus (GDK_DISPLAY (),
397                     GDK_WINDOW_XWINDOW (socket->plug_window),
398                     RevertToParent, GDK_CURRENT_TIME);
399 }
400
401 static gint 
402 gtk_socket_focus (GtkContainer *container, GtkDirectionType direction)
403 {
404   GtkSocket *socket;
405
406   g_return_val_if_fail (GTK_IS_SOCKET (container), FALSE);
407   
408   socket = GTK_SOCKET (container);
409
410   if (!socket->focus_in && socket->plug_window)
411     {
412       XEvent xevent;
413
414       gtk_socket_claim_focus (socket);
415       
416       xevent.xkey.type = KeyPress;
417       xevent.xkey.display = GDK_DISPLAY ();
418       xevent.xkey.window = GDK_WINDOW_XWINDOW (socket->plug_window);
419       xevent.xkey.root = GDK_ROOT_WINDOW (); /* FIXME */
420       xevent.xkey.time = GDK_CURRENT_TIME; /* FIXME */
421       /* FIXME, the following might cause big problems for
422        * non-GTK apps */
423       xevent.xkey.x = 0;
424       xevent.xkey.y = 0;
425       xevent.xkey.x_root = 0;
426       xevent.xkey.y_root = 0;
427       xevent.xkey.state = 0;
428       xevent.xkey.same_screen = TRUE; /* FIXME ? */
429
430       switch (direction)
431         {
432         case GTK_DIR_UP:
433           xevent.xkey.keycode =  XKeysymToKeycode(GDK_DISPLAY(), GDK_Up);
434           break;
435         case GTK_DIR_DOWN:
436           xevent.xkey.keycode =  XKeysymToKeycode(GDK_DISPLAY(), GDK_Down);
437           break;
438         case GTK_DIR_LEFT:
439           xevent.xkey.keycode =  XKeysymToKeycode(GDK_DISPLAY(), GDK_Left);
440           break;
441         case GTK_DIR_RIGHT:
442           xevent.xkey.keycode =  XKeysymToKeycode(GDK_DISPLAY(), GDK_Right);
443           break;
444         case GTK_DIR_TAB_FORWARD:
445           xevent.xkey.keycode =  XKeysymToKeycode(GDK_DISPLAY(), GDK_Tab);
446           break;
447         case GTK_DIR_TAB_BACKWARD:
448           xevent.xkey.keycode =  XKeysymToKeycode(GDK_DISPLAY(), GDK_Tab);
449           xevent.xkey.state = ShiftMask;
450           break;
451         }
452
453       
454       XSendEvent (gdk_display,
455                   GDK_WINDOW_XWINDOW (socket->plug_window),
456                   False, NoEventMask, &xevent);
457       return TRUE;
458     }
459   else
460     {
461       return FALSE;
462     }
463 }
464
465 static void
466 gtk_socket_send_configure_event (GtkSocket *socket)
467 {
468   XEvent event;
469
470   g_return_if_fail (socket->plug_window != NULL);
471
472   event.xconfigure.type = ConfigureNotify;
473   event.xconfigure.display = gdk_display;
474
475   event.xconfigure.event = GDK_WINDOW_XWINDOW (socket->plug_window);
476   event.xconfigure.window = GDK_WINDOW_XWINDOW (socket->plug_window);
477
478   event.xconfigure.x = 0;
479   event.xconfigure.y = 0;
480   event.xconfigure.width = GTK_WIDGET(socket)->allocation.width;
481   event.xconfigure.height = GTK_WIDGET(socket)->allocation.height;
482
483   event.xconfigure.border_width = 0;
484   event.xconfigure.above = None;
485   event.xconfigure.override_redirect = False;
486   
487   XSendEvent (gdk_display,
488               GDK_WINDOW_XWINDOW (socket->plug_window),
489               False, NoEventMask, &event);
490 }
491
492 static void
493 gtk_socket_add_window (GtkSocket *socket, guint32 xid)
494 {
495   socket->plug_window = gdk_window_lookup (xid);
496   socket->same_app = TRUE;
497
498   if (!socket->plug_window)
499     {
500       GtkWidget *toplevel;
501       GdkDragProtocol protocol;
502
503       socket->plug_window = gdk_window_foreign_new (xid);
504       socket->same_app = FALSE;
505       
506       XSelectInput (GDK_DISPLAY (),
507                     GDK_WINDOW_XWINDOW(socket->plug_window),
508                     StructureNotifyMask | PropertyChangeMask);
509
510       if (gdk_drag_get_protocol (xid, &protocol))
511         gtk_drag_dest_set_proxy (GTK_WIDGET (socket), socket->plug_window, 
512                                  protocol, TRUE);
513
514       gdk_window_add_filter (socket->plug_window, 
515                              gtk_socket_filter_func, socket);
516
517       /* Add a pointer to the socket on our toplevel window */
518
519       toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
520       if (toplevel && GTK_IS_WINDOW (toplevel))
521         {
522           gtk_window_add_embedded_xid (GTK_WINDOW (toplevel), xid);
523         }
524     }
525 }
526
527 static GdkFilterReturn
528 gtk_socket_filter_func (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data)
529 {
530   GtkSocket *socket;
531   GtkWidget *widget;
532   XEvent *xevent;
533
534   GdkFilterReturn return_val;
535   
536   socket = GTK_SOCKET (data);
537   widget = GTK_WIDGET (socket);
538   xevent = (XEvent *)gdk_xevent;
539
540   return_val = GDK_FILTER_CONTINUE;
541
542   switch (xevent->type)
543     {
544     case CreateNotify:
545       {
546         XCreateWindowEvent *xcwe = &xevent->xcreatewindow;
547
548         if (!socket->plug_window)
549           {
550             g_print("Here!\n");
551
552             gtk_socket_add_window (socket, xcwe->window);
553             
554             gdk_window_move_resize(socket->plug_window,
555                                    0, 0,
556                                    widget->allocation.width, 
557                                    widget->allocation.height);
558         
559             socket->request_width = xcwe->width;
560             socket->request_height = xcwe->height;
561             socket->have_size = TRUE;
562             
563             DPRINTF(("Window created with size: %d %d\n",
564                      socket->request_width,
565                      socket->request_height));
566             
567             gtk_widget_queue_resize (widget);
568           }
569         
570         return_val = GDK_FILTER_REMOVE;
571         
572         break;
573       }
574
575     case ConfigureRequest:
576       {
577         XConfigureRequestEvent *xcre = &xevent->xconfigurerequest;
578         
579         if (!socket->plug_window)
580           gtk_socket_add_window (socket, xcre->window);
581         
582         if (xcre->window == GDK_WINDOW_XWINDOW (socket->plug_window))
583           {
584             if (xcre->value_mask & (CWWidth | CWHeight))
585               {
586                 socket->request_width = xcre->width;
587                 socket->request_height = xcre->height;
588                 socket->have_size = TRUE;
589                 
590                 DPRINTF(("Configure request: %d %d\n",
591                         socket->request_width,
592                         socket->request_height));
593                 
594                 gtk_widget_queue_resize (widget);
595               }
596             else if (xcre->value_mask & (CWX | CWY))
597               {
598                 gtk_socket_send_configure_event (socket);
599               }
600             /* Ignore stacking requests. */
601             
602             return_val = GDK_FILTER_REMOVE;
603           }
604         break;
605       }
606
607     case DestroyNotify:
608       {
609         XDestroyWindowEvent *xdwe = &xevent->xdestroywindow;
610
611         if (socket->plug_window &&
612             (xdwe->window == GDK_WINDOW_XWINDOW (socket->plug_window)))
613           {
614             GtkWidget *toplevel;
615             
616             DPRINTF(("Destroy Notify\n"));
617             
618             toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket));
619             if (toplevel && GTK_IS_WINDOW (toplevel))
620               gtk_window_remove_embedded_xid (GTK_WINDOW (toplevel), xdwe->window);
621             gtk_widget_destroy (widget);
622             gdk_window_destroy_notify (socket->plug_window);
623
624             socket->plug_window = NULL;
625             
626             return_val = GDK_FILTER_REMOVE;
627           }
628         break;
629     }
630       
631     case FocusIn:
632       if (xevent->xfocus.mode == EMBEDDED_APP_WANTS_FOCUS)
633         {
634           gtk_socket_claim_focus (socket);
635         }
636       else if (xevent->xfocus.detail == NotifyInferior)
637         {
638 #if 0
639           GtkWidget *toplevel;
640           toplevel = gtk_widget_get_ancestor (widget, gtk_window_get_type());
641           
642           if (toplevel)
643             {
644               XSetInputFocus (GDK_DISPLAY (),
645                               GDK_WINDOW_XWINDOW (toplevel->window),
646                               RevertToParent, CurrentTime); /* FIXME? */
647             }
648 #endif    
649         }
650       return_val = GDK_FILTER_REMOVE;
651       break;
652     case FocusOut:
653       return_val = GDK_FILTER_REMOVE;
654       break;
655     case MapRequest:
656       if (!socket->plug_window)
657         gtk_socket_add_window (socket, xevent->xmaprequest.window);
658         
659       if (xevent->xmaprequest.window ==
660           GDK_WINDOW_XWINDOW (socket->plug_window))
661         {
662           DPRINTF(("Map Request\n"));
663             
664           gdk_window_show (socket->plug_window);
665
666           return_val = GDK_FILTER_REMOVE;
667         }
668       break;
669     case PropertyNotify:
670       if (xevent->xproperty.window ==
671           GDK_WINDOW_XWINDOW (socket->plug_window))
672         {
673           GdkDragProtocol protocol;
674
675           if ((xevent->xproperty.atom == gdk_atom_intern ("XdndAware", FALSE)) ||
676               (xevent->xproperty.atom == gdk_atom_intern ("_MOTIF_DRAG_RECEIVER_INFO", FALSE)))
677             {
678               if (gdk_drag_get_protocol (xevent->xproperty.window, &protocol))
679                 gtk_drag_dest_set_proxy (GTK_WIDGET (socket),
680                                          socket->plug_window,
681                                          protocol, TRUE);
682             }
683           return_val = GDK_FILTER_REMOVE;
684         }
685     }
686
687   return return_val;
688 }