gtkplug.c 30.2 KB
Newer Older
1 2 3 4
/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6 7 8 9 10 11
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15 16 17 18 19 20
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/* By Owen Taylor <otaylor@gtk.org>              98/4/4 */

21
/*
22
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
23 24 25 26 27
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

28
#include "gtkmain.h"
29
#include "gtkmarshalers.h"
30
#include "gtkplug.h"
31
#include "gtkprivate.h"
32

33 34
#include "gdk/gdkkeysyms.h"
#include "x11/gdkx.h"
35

36
#include "gtkxembed.h"
37 38 39

static void            gtk_plug_class_init            (GtkPlugClass     *klass);
static void            gtk_plug_init                  (GtkPlug          *plug);
40
static void            gtk_plug_finalize              (GObject          *object);
41 42
static void            gtk_plug_realize               (GtkWidget        *widget);
static void            gtk_plug_unrealize             (GtkWidget        *widget);
43 44 45 46 47 48
static void            gtk_plug_show                  (GtkWidget        *widget);
static void            gtk_plug_hide                  (GtkWidget        *widget);
static void            gtk_plug_map                   (GtkWidget        *widget);
static void            gtk_plug_unmap                 (GtkWidget        *widget);
static void            gtk_plug_size_allocate         (GtkWidget        *widget,
						       GtkAllocation    *allocation);
49 50
static gboolean        gtk_plug_key_press_event       (GtkWidget        *widget,
						       GdkEventKey      *event);
51 52
static gboolean        gtk_plug_focus_event           (GtkWidget        *widget,
						       GdkEventFocus    *event);
53 54
static void            gtk_plug_set_focus             (GtkWindow        *window,
						       GtkWidget        *focus);
55
static gboolean        gtk_plug_focus                 (GtkWidget        *widget,
56
						       GtkDirectionType  direction);
57
static void            gtk_plug_check_resize          (GtkContainer     *container);
58
static void            gtk_plug_keys_changed          (GtkWindow        *window);
59 60 61 62
static GdkFilterReturn gtk_plug_filter_func           (GdkXEvent        *gdk_xevent,
						       GdkEvent         *event,
						       gpointer          data);

63 64 65
static void handle_modality_off        (GtkPlug       *plug);
static void xembed_set_info            (GdkWindow     *window,
					unsigned long  flags);
66 67 68

/* From Tk */
#define EMBEDDED_APP_WANTS_FOCUS NotifyNormal+20
69 70
  
static GtkWindowClass *parent_class = NULL;
71
static GtkBinClass *bin_class = NULL;
72

73 74 75 76 77 78 79
enum {
  EMBEDDED,
  LAST_SIGNAL
}; 

static guint plug_signals[LAST_SIGNAL] = { 0 };

Manish Singh's avatar
Manish Singh committed
80
GType
81 82
gtk_plug_get_type ()
{
Manish Singh's avatar
Manish Singh committed
83
  static GType plug_type = 0;
84 85 86

  if (!plug_type)
    {
87
      static const GTypeInfo plug_info =
88 89
      {
	sizeof (GtkPlugClass),
90 91 92 93 94 95 96 97
	NULL,           /* base_init */
	NULL,           /* base_finalize */
	(GClassInitFunc) gtk_plug_class_init,
	NULL,           /* class_finalize */
	NULL,           /* class_data */
	sizeof (GtkPlug),
	16,             /* n_preallocs */
	(GInstanceInitFunc) gtk_plug_init,
98 99
      };

Manish Singh's avatar
Manish Singh committed
100 101
      plug_type = g_type_register_static (GTK_TYPE_WINDOW, "GtkPlug",
					  &plug_info, 0);
102 103 104 105 106 107 108 109
    }

  return plug_type;
}

static void
gtk_plug_class_init (GtkPlugClass *class)
{
110
  GObjectClass *gobject_class = (GObjectClass *)class;
111 112
  GtkWidgetClass *widget_class = (GtkWidgetClass *)class;
  GtkWindowClass *window_class = (GtkWindowClass *)class;
113
  GtkContainerClass *container_class = (GtkContainerClass *)class;
114

Manish Singh's avatar
Manish Singh committed
115 116
  parent_class = g_type_class_peek_parent (class);
  bin_class = g_type_class_peek (GTK_TYPE_BIN);
117

118 119
  gobject_class->finalize = gtk_plug_finalize;
  
120
  widget_class->realize = gtk_plug_realize;
121
  widget_class->unrealize = gtk_plug_unrealize;
122
  widget_class->key_press_event = gtk_plug_key_press_event;
123 124
  widget_class->focus_in_event = gtk_plug_focus_event;
  widget_class->focus_out_event = gtk_plug_focus_event;
125

126 127 128 129 130 131 132 133 134
  widget_class->show = gtk_plug_show;
  widget_class->hide = gtk_plug_hide;
  widget_class->map = gtk_plug_map;
  widget_class->unmap = gtk_plug_unmap;
  widget_class->size_allocate = gtk_plug_size_allocate;

  widget_class->focus = gtk_plug_focus;

  container_class->check_resize = gtk_plug_check_resize;
135 136

  window_class->set_focus = gtk_plug_set_focus;
137
  window_class->keys_changed = gtk_plug_keys_changed;
138 139 140 141 142 143 144

  plug_signals[EMBEDDED] =
    g_signal_new ("embedded",
		  G_OBJECT_CLASS_TYPE (class),
		  G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (GtkPlugClass, embedded),
		  NULL, NULL,
145
		  _gtk_marshal_VOID__VOID,
Manish Singh's avatar
Manish Singh committed
146
		  G_TYPE_NONE, 0);
147 148 149 150 151 152 153 154 155 156 157
}

static void
gtk_plug_init (GtkPlug *plug)
{
  GtkWindow *window;

  window = GTK_WINDOW (plug);

  window->type = GTK_WINDOW_TOPLEVEL;
}
158

159 160 161 162
static void
gtk_plug_set_is_child (GtkPlug  *plug,
		       gboolean  is_child)
{
163 164
  g_assert (!GTK_WIDGET (plug)->parent);
      
165 166
  if (is_child)
    {
167 168 169 170 171 172 173 174 175 176
      if (plug->modality_window)
	handle_modality_off (plug);

      if (plug->modality_group)
	{
	  gtk_window_group_remove_window (plug->modality_group, GTK_WINDOW (plug));
	  g_object_unref (plug->modality_group);
	  plug->modality_group = NULL;
	}
      
177 178 179 180 181 182 183 184
      /* As a toplevel, the MAPPED flag doesn't correspond
       * to whether the widget->window is mapped; we unmap
       * here, but don't bother remapping -- we will get mapped
       * by gtk_widget_set_parent ().
       */
      if (GTK_WIDGET_MAPPED (plug))
	gtk_widget_unmap (GTK_WIDGET (plug));
      
185 186
      GTK_WIDGET_UNSET_FLAGS (plug, GTK_TOPLEVEL);
      gtk_container_set_resize_mode (GTK_CONTAINER (plug), GTK_RESIZE_PARENT);
187 188 189 190 191

      _gtk_widget_propagate_hierarchy_changed (GTK_WIDGET (plug), GTK_WIDGET (plug));
    }
  else
    {
192 193 194 195 196
      if (GTK_WINDOW (plug)->focus_widget)
	gtk_window_set_focus (GTK_WINDOW (plug), NULL);
      if (GTK_WINDOW (plug)->default_widget)
	gtk_window_set_default (GTK_WINDOW (plug), NULL);
	  
197 198 199 200 201 202 203
      plug->modality_group = gtk_window_group_new ();
      gtk_window_group_add_window (plug->modality_group, GTK_WINDOW (plug));
      
      GTK_WIDGET_SET_FLAGS (plug, GTK_TOPLEVEL);
      gtk_container_set_resize_mode (GTK_CONTAINER (plug), GTK_RESIZE_QUEUE);

      _gtk_widget_propagate_hierarchy_changed (GTK_WIDGET (plug), NULL);
204 205 206 207 208 209
    }
}

/**
 * _gtk_plug_add_to_socket:
 * @plug: a #GtkPlug
210
 * @socket_: a #GtkSocket
211
 * 
212
 * Adds a plug to a socket within the same application.
213
 **/
214
void
215 216 217
_gtk_plug_add_to_socket (GtkPlug   *plug,
			 GtkSocket *socket)
{
218
  GtkWidget *widget;
219
  gint w, h;
220 221 222 223 224
  
  g_return_if_fail (GTK_IS_PLUG (plug));
  g_return_if_fail (GTK_IS_SOCKET (socket));
  g_return_if_fail (GTK_WIDGET_REALIZED (socket));

225 226
  widget = GTK_WIDGET (plug);

227 228
  gtk_plug_set_is_child (plug, TRUE);
  plug->same_app = TRUE;
229
  socket->same_app = TRUE;
230 231
  socket->plug_widget = widget;

232 233
  plug->socket_window = GTK_WIDGET (socket)->window;

234
  if (GTK_WIDGET_REALIZED (widget))
235 236 237 238
    {
      gdk_drawable_get_size (GDK_DRAWABLE (widget->window), &w, &h);
      gdk_window_reparent (widget->window, plug->socket_window, -w, -h);
    }
239

240
  gtk_widget_set_parent (widget, GTK_WIDGET (socket));
241

Manish Singh's avatar
Manish Singh committed
242
  g_signal_emit_by_name (socket, "plug_added", 0);
243 244
}

245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
static void
send_delete_event (GtkWidget *widget)
{
  GdkEvent *event = gdk_event_new (GDK_DELETE);
  
  event->any.window = g_object_ref (widget->window);
  event->any.send_event = FALSE;

  gtk_widget_ref (widget);
  
  if (!gtk_widget_event (widget, event))
    gtk_widget_destroy (widget);
  
  gtk_widget_unref (widget);

  gdk_event_free (event);
}

263
/**
264
 * _gtk_plug_remove_from_socket:
265
 * @plug: a #GtkPlug
266
 * @socket_: a #GtkSocket
267
 * 
268
 * Removes a plug from a socket within the same application.
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
 **/
void
_gtk_plug_remove_from_socket (GtkPlug   *plug,
			      GtkSocket *socket)
{
  GtkWidget *widget;
  gboolean result;
  gboolean widget_was_visible;

  g_return_if_fail (GTK_IS_PLUG (plug));
  g_return_if_fail (GTK_IS_SOCKET (socket));
  g_return_if_fail (GTK_WIDGET_REALIZED (plug));

  widget = GTK_WIDGET (plug);

  g_object_ref (plug);
  g_object_ref (socket);

  widget_was_visible = GTK_WIDGET_VISIBLE (plug);
  
  gdk_window_hide (widget->window);
290 291 292
  gdk_window_reparent (widget->window,
		       gtk_widget_get_root_window (widget),
		       0, 0);
293 294 295 296 297 298

  GTK_PRIVATE_SET_FLAG (plug, GTK_IN_REPARENT);
  gtk_widget_unparent (GTK_WIDGET (plug));
  GTK_PRIVATE_UNSET_FLAG (plug, GTK_IN_REPARENT);
  
  socket->plug_widget = NULL;
299
  g_object_unref (socket->plug_window);
300
  socket->plug_window = NULL;
301
  
302 303 304
  socket->same_app = FALSE;

  plug->same_app = FALSE;
305
  plug->socket_window = NULL;
306 307 308

  gtk_plug_set_is_child (plug, FALSE);
		    
Manish Singh's avatar
Manish Singh committed
309
  g_signal_emit_by_name (socket, "plug_removed", &result);
310 311 312
  if (!result)
    gtk_widget_destroy (GTK_WIDGET (socket));

313 314
  send_delete_event (widget);

315 316 317 318 319 320
  g_object_unref (plug);

  if (widget_was_visible && GTK_WIDGET_VISIBLE (socket))
    gtk_widget_queue_resize (GTK_WIDGET (socket));

  g_object_unref (socket);
321 322
}

323 324 325 326 327
/**
 * gtk_plug_construct:
 * @plug: a #GtkPlug.
 * @socket_id: the XID of the socket's window.
 *
328
 * Finish the initialization of @plug for a given #GtkSocket identified by
329 330
 * @socket_id. This function will generally only be used by classes deriving from #GtkPlug.
 **/
331 332 333
void
gtk_plug_construct (GtkPlug         *plug,
		    GdkNativeWindow  socket_id)
334
{
Owen Taylor's avatar
Owen Taylor committed
335
  gtk_plug_construct_for_display (plug, gdk_display_get_default (), socket_id);
336 337 338 339
}

/**
 * gtk_plug_construct_for_display:
340
 * @plug: a #GtkPlug.
341 342 343 344
 * @display: the #GdkDisplay associated with @socket_id's 
 *	     #GtkSocket.
 * @socket_id: the XID of the socket's window.
 *
345
 * Finish the initialization of @plug for a given #GtkSocket identified by
346 347
 * @socket_id which is currently displayed on @display.
 * This function will generally only be used by classes deriving from #GtkPlug.
348 349
 *
 * Since: 2.2
350 351
 **/
void
352 353
gtk_plug_construct_for_display (GtkPlug         *plug,
				GdkDisplay	*display,
354
				GdkNativeWindow  socket_id)
355
{
356
  if (socket_id)
357
    {
358 359
      gpointer user_data = NULL;

360 361
      plug->socket_window = gdk_window_lookup_for_display (display, socket_id);
      
362 363 364
      if (plug->socket_window)
	gdk_window_get_user_data (plug->socket_window, &user_data);
      else
365 366
	plug->socket_window = gdk_window_foreign_new_for_display (display, socket_id);
	  
367
      if (user_data)
368
	{
369 370 371 372 373 374 375
	  if (GTK_IS_SOCKET (user_data))
	    _gtk_plug_add_to_socket (plug, user_data);
	  else
	    {
	      g_warning (G_STRLOC "Can't create GtkPlug as child of non-GtkSocket");
	      plug->socket_window = NULL;
	    }
376
	}
377 378

      if (plug->socket_window)
Manish Singh's avatar
Manish Singh committed
379
	g_signal_emit (plug, plug_signals[EMBEDDED], 0);
380 381
    }
}
382

383 384 385 386 387 388 389 390 391 392
/**
 * gtk_plug_new:
 * @socket_id:  the window ID of the socket, or 0.
 * 
 * Creates a new plug widget inside the #GtkSocket identified
 * by @socket_id. If @socket_id is 0, the plug is left "unplugged" and
 * can later be plugged into a #GtkSocket by  gtk_socket_add_id().
 * 
 * Return value: the new #GtkPlug widget.
 **/
393
GtkWidget*
394
gtk_plug_new (GdkNativeWindow socket_id)
395
{
Owen Taylor's avatar
Owen Taylor committed
396
  return gtk_plug_new_for_display (gdk_display_get_default (), socket_id);
397 398 399 400 401 402 403
}

/**
 * gtk_plug_new_for_display:
 * @display : the #GdkDisplay on which @socket_id is displayed
 * @socket_id: the XID of the socket's window.
 * 
404
 * Create a new plug widget inside the #GtkSocket identified by socket_id.
405 406
 *
 * Return value: the new #GtkPlug widget.
407 408
 *
 * Since: 2.2
409 410 411 412
 */
GtkWidget*
gtk_plug_new_for_display (GdkDisplay	  *display,
			  GdkNativeWindow  socket_id)
413 414 415
{
  GtkPlug *plug;

Manish Singh's avatar
Manish Singh committed
416
  plug = g_object_new (GTK_TYPE_PLUG, NULL);
417
  gtk_plug_construct_for_display (plug, display, socket_id);
418 419 420
  return GTK_WIDGET (plug);
}

421 422 423 424 425 426
/**
 * gtk_plug_get_id:
 * @plug: a #GtkPlug.
 * 
 * Gets the window ID of a #GtkPlug widget, which can then
 * be used to embed this window inside another window, for
Matthias Clasen's avatar
Matthias Clasen committed
427
 * instance with gtk_socket_add_id().
428 429 430 431 432 433 434 435 436 437 438 439 440 441
 * 
 * Return value: the window ID for the plug
 **/
GdkNativeWindow
gtk_plug_get_id (GtkPlug *plug)
{
  g_return_val_if_fail (GTK_IS_PLUG (plug), 0);

  if (!GTK_WIDGET_REALIZED (plug))
    gtk_widget_realize (GTK_WIDGET (plug));

  return GDK_WINDOW_XWINDOW (GTK_WIDGET (plug)->window);
}

442 443 444 445 446 447 448 449 450 451 452 453 454 455
static void
gtk_plug_finalize (GObject *object)
{
  GtkPlug *plug = GTK_PLUG (object);

  if (plug->grabbed_keys)
    {
      g_hash_table_destroy (plug->grabbed_keys);
      plug->grabbed_keys = NULL;
    }
  
  G_OBJECT_CLASS (parent_class)->finalize (object);
}

456 457 458 459 460 461 462 463 464 465 466 467
static void
gtk_plug_unrealize (GtkWidget *widget)
{
  GtkPlug *plug;

  g_return_if_fail (GTK_IS_PLUG (widget));

  plug = GTK_PLUG (widget);

  if (plug->socket_window != NULL)
    {
      gdk_window_set_user_data (plug->socket_window, NULL);
Manish Singh's avatar
Manish Singh committed
468
      g_object_unref (plug->socket_window);
469 470 471
      plug->socket_window = NULL;
    }

472 473 474 475
  if (!plug->same_app)
    {
      if (plug->modality_window)
	handle_modality_off (plug);
476

477 478 479
      gtk_window_group_remove_window (plug->modality_group, GTK_WINDOW (plug));
      g_object_unref (plug->modality_group);
    }
480
  
481 482 483 484
  if (GTK_WIDGET_CLASS (parent_class)->unrealize)
    (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
}

485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513
static void
gtk_plug_realize (GtkWidget *widget)
{
  GtkWindow *window;
  GtkPlug *plug;
  GdkWindowAttr attributes;
  gint attributes_mask;

  g_return_if_fail (GTK_IS_PLUG (widget));

  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
  window = GTK_WINDOW (widget);
  plug = GTK_PLUG (widget);

  attributes.window_type = GDK_WINDOW_CHILD;	/* XXX GDK_WINDOW_PLUG ? */
  attributes.title = window->title;
  attributes.wmclass_name = window->wmclass_name;
  attributes.wmclass_class = window->wmclass_class;
  attributes.width = widget->allocation.width;
  attributes.height = widget->allocation.height;
  attributes.wclass = GDK_INPUT_OUTPUT;

  /* this isn't right - we should match our parent's visual/colormap.
   * though that will require handling "foreign" colormaps */
  attributes.visual = gtk_widget_get_visual (widget);
  attributes.colormap = gtk_widget_get_colormap (widget);
  attributes.event_mask = gtk_widget_get_events (widget);
  attributes.event_mask |= (GDK_EXPOSURE_MASK |
			    GDK_KEY_PRESS_MASK |
514
			    GDK_KEY_RELEASE_MASK |
515 516 517 518 519 520 521 522
			    GDK_ENTER_NOTIFY_MASK |
			    GDK_LEAVE_NOTIFY_MASK |
			    GDK_STRUCTURE_MASK);

  attributes_mask = GDK_WA_VISUAL | GDK_WA_COLORMAP;
  attributes_mask |= (window->title ? GDK_WA_TITLE : 0);
  attributes_mask |= (window->wmclass_name ? GDK_WA_WMCLASS : 0);

523
  if (GTK_WIDGET_TOPLEVEL (widget))
524
    {
525 526
      attributes.window_type = GDK_WINDOW_TOPLEVEL;

527
      gdk_error_trap_push ();
528 529 530 531 532 533 534 535
      if (plug->socket_window)
	widget->window = gdk_window_new (plug->socket_window, 
					 &attributes, attributes_mask);
      else /* If it's a passive plug, we use the root window */
	widget->window = gdk_window_new (gtk_widget_get_root_window (widget),
					 &attributes, attributes_mask);

      gdk_display_sync (gtk_widget_get_display (widget));
536 537 538 539 540 541
      if (gdk_error_trap_pop ()) /* Uh-oh */
	{
	  gdk_error_trap_push ();
	  gdk_window_destroy (widget->window);
	  gdk_flush ();
	  gdk_error_trap_pop ();
542 543
	  widget->window = gdk_window_new (gtk_widget_get_root_window (widget),
					   &attributes, attributes_mask);
544
	}
545
      
546 547 548 549 550 551
      gdk_window_add_filter (widget->window, gtk_plug_filter_func, widget);

      plug->modality_group = gtk_window_group_new ();
      gtk_window_group_add_window (plug->modality_group, window);
      
      xembed_set_info (widget->window, 0);
552
    }
553
  else
554 555
    widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), 
				     &attributes, attributes_mask);      
556
  
557 558 559 560
  gdk_window_set_user_data (widget->window, window);

  widget->style = gtk_style_attach (widget->style, widget->window);
  gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626
}

static void
gtk_plug_show (GtkWidget *widget)
{
  if (GTK_WIDGET_TOPLEVEL (widget))
    GTK_WIDGET_CLASS (parent_class)->show (widget);
  else
    GTK_WIDGET_CLASS (bin_class)->show (widget);
}

static void
gtk_plug_hide (GtkWidget *widget)
{
  if (GTK_WIDGET_TOPLEVEL (widget))
    GTK_WIDGET_CLASS (parent_class)->hide (widget);
  else
    GTK_WIDGET_CLASS (bin_class)->hide (widget);
}

/* From gdkinternals.h */
void gdk_synthesize_window_state (GdkWindow     *window,
                                  GdkWindowState unset_flags,
                                  GdkWindowState set_flags);

static void
gtk_plug_map (GtkWidget *widget)
{
  if (GTK_WIDGET_TOPLEVEL (widget))
    {
      GtkBin *bin = GTK_BIN (widget);
      
      GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);

      if (bin->child &&
	  GTK_WIDGET_VISIBLE (bin->child) &&
	  !GTK_WIDGET_MAPPED (bin->child))
	gtk_widget_map (bin->child);

      xembed_set_info (widget->window, XEMBED_MAPPED);
      
      gdk_synthesize_window_state (widget->window,
				   GDK_WINDOW_STATE_WITHDRAWN,
				   0);
    }
  else
    GTK_WIDGET_CLASS (bin_class)->map (widget);
}

static void
gtk_plug_unmap (GtkWidget *widget)
{
  if (GTK_WIDGET_TOPLEVEL (widget))
    {
      GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);

      gdk_window_hide (widget->window);
      xembed_set_info (widget->window, 0);
      
      gdk_synthesize_window_state (widget->window,
				   0,
				   GDK_WINDOW_STATE_WITHDRAWN);
    }
  else
    GTK_WIDGET_CLASS (bin_class)->unmap (widget);
}
627

628 629 630 631 632 633 634 635 636 637
static void
gtk_plug_size_allocate (GtkWidget     *widget,
			GtkAllocation *allocation)
{
  if (GTK_WIDGET_TOPLEVEL (widget))
    GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
  else
    {
      GtkBin *bin = GTK_BIN (widget);

638 639
      widget->allocation = *allocation;

640 641 642 643
      if (GTK_WIDGET_REALIZED (widget))
	gdk_window_move_resize (widget->window,
				allocation->x, allocation->y,
				allocation->width, allocation->height);
644

645 646 647 648 649 650 651 652 653 654 655 656 657 658
      if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
	{
	  GtkAllocation child_allocation;
	  
	  child_allocation.x = child_allocation.y = GTK_CONTAINER (widget)->border_width;
	  child_allocation.width =
	    MAX (1, (gint)allocation->width - child_allocation.x * 2);
	  child_allocation.height =
	    MAX (1, (gint)allocation->height - child_allocation.y * 2);
	  
	  gtk_widget_size_allocate (bin->child, &child_allocation);
	}
      
    }
659 660
}

661
static gboolean
662 663 664
gtk_plug_key_press_event (GtkWidget   *widget,
			  GdkEventKey *event)
{
665
  if (GTK_WIDGET_TOPLEVEL (widget))
666
    return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
667
  else
668
    return FALSE;
669 670
}

671 672 673 674 675 676 677 678 679 680 681
static gboolean
gtk_plug_focus_event (GtkWidget      *widget,
		      GdkEventFocus  *event)
{
  /* We eat focus-in events and focus-out events, since they
   * can be generated by something like a keyboard grab on
   * a child of the plug.
   */
  return FALSE;
}

682 683 684
static void
gtk_plug_set_focus (GtkWindow *window,
		    GtkWidget *focus)
685
{
686
  GtkPlug *plug = GTK_PLUG (window);
687

688 689 690
  GTK_WINDOW_CLASS (parent_class)->set_focus (window, focus);
  
  /* Ask for focus from embedder
691
   */
692

693
  if (focus && !window->has_toplevel_focus)
694
    {
695 696
      _gtk_xembed_send_message (plug->socket_window,
				XEMBED_REQUEST_FOCUS, 0, 0, 0);
697
    }
698 699
}

700
typedef struct
701
{
702 703 704
  guint			 accelerator_key;
  GdkModifierType	 accelerator_mods;
} GrabbedKey;
705

706 707 708 709 710 711 712 713 714
static guint
grabbed_key_hash (gconstpointer a)
{
  const GrabbedKey *key = a;
  guint h;
  
  h = key->accelerator_key << 16;
  h ^= key->accelerator_key >> 16;
  h ^= key->accelerator_mods;
715

716 717
  return h;
}
718

719 720 721 722 723
static gboolean
grabbed_key_equal (gconstpointer a, gconstpointer b)
{
  const GrabbedKey *keya = a;
  const GrabbedKey *keyb = b;
724

725 726 727
  return (keya->accelerator_key == keyb->accelerator_key &&
	  keya->accelerator_mods == keyb->accelerator_mods);
}
728

729
static void
730
add_grabbed_key (gpointer key, gpointer val, gpointer data)
731 732 733 734 735 736 737
{
  GrabbedKey *grabbed_key = key;
  GtkPlug *plug = data;

  if (!plug->grabbed_keys ||
      !g_hash_table_lookup (plug->grabbed_keys, grabbed_key))
    {
738 739
      _gtk_xembed_send_message (plug->socket_window, XEMBED_GTK_GRAB_KEY, 0, 
				grabbed_key->accelerator_key, grabbed_key->accelerator_mods);
740
    }
741
}
742

743
static void
744 745 746 747 748
add_grabbed_key_always (gpointer key, gpointer val, gpointer data)
{
  GrabbedKey *grabbed_key = key;
  GtkPlug *plug = data;

749 750
  _gtk_xembed_send_message (plug->socket_window, XEMBED_GTK_GRAB_KEY, 0, 
			    grabbed_key->accelerator_key, grabbed_key->accelerator_mods);
751 752 753 754
}

static void
remove_grabbed_key (gpointer key, gpointer val, gpointer data)
755 756 757 758 759 760 761
{
  GrabbedKey *grabbed_key = key;
  GtkPlug *plug = data;

  if (!plug->grabbed_keys ||
      !g_hash_table_lookup (plug->grabbed_keys, grabbed_key))
    {
762 763
      _gtk_xembed_send_message (plug->socket_window, XEMBED_GTK_UNGRAB_KEY, 0, 
				grabbed_key->accelerator_key, grabbed_key->accelerator_mods);
764
    }
765 766 767
}

static void
768 769 770 771 772
keys_foreach (GtkWindow      *window,
	      guint           keyval,
	      GdkModifierType modifiers,
	      gboolean        is_mnemonic,
	      gpointer        data)
773
{
774 775 776 777 778 779 780
  GHashTable *new_grabbed_keys = data;
  GrabbedKey *key = g_new (GrabbedKey, 1);

  key->accelerator_key = keyval;
  key->accelerator_mods = modifiers;
  
  g_hash_table_replace (new_grabbed_keys, key, key);
781
}
782

783
static void
784
gtk_plug_keys_changed (GtkWindow *window)
785 786 787
{
  GHashTable *new_grabbed_keys, *old_grabbed_keys;
  GtkPlug *plug = GTK_PLUG (window);
788

789 790
  new_grabbed_keys = g_hash_table_new_full (grabbed_key_hash, grabbed_key_equal, (GDestroyNotify)g_free, NULL);
  _gtk_window_keys_foreach (window, keys_foreach, new_grabbed_keys);
791

792 793
  if (plug->socket_window)
    g_hash_table_foreach (new_grabbed_keys, add_grabbed_key, plug);
794

795 796 797 798 799
  old_grabbed_keys = plug->grabbed_keys;
  plug->grabbed_keys = new_grabbed_keys;

  if (old_grabbed_keys)
    {
800 801 802
      if (plug->socket_window)
	g_hash_table_foreach (old_grabbed_keys, remove_grabbed_key, plug);
      g_hash_table_destroy (old_grabbed_keys);
803 804 805
    }
}

806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828
static void
focus_to_parent (GtkPlug          *plug,
		 GtkDirectionType  direction)
{
  XEmbedMessageType message = XEMBED_FOCUS_PREV; /* Quiet GCC */
  
  switch (direction)
    {
    case GTK_DIR_UP:
    case GTK_DIR_LEFT:
    case GTK_DIR_TAB_BACKWARD:
      message = XEMBED_FOCUS_PREV;
      break;
    case GTK_DIR_DOWN:
    case GTK_DIR_RIGHT:
    case GTK_DIR_TAB_FORWARD:
      message = XEMBED_FOCUS_NEXT;
      break;
    }
  
  _gtk_xembed_send_focus_message (plug->socket_window, message, 0);
}

829
static gboolean
830
gtk_plug_focus (GtkWidget        *widget,
831 832
		GtkDirectionType  direction)
{
833 834 835 836
  GtkBin *bin = GTK_BIN (widget);
  GtkPlug *plug = GTK_PLUG (widget);
  GtkWindow *window = GTK_WINDOW (widget);
  GtkContainer *container = GTK_CONTAINER (widget);
837 838 839 840 841 842 843
  GtkWidget *old_focus_child = container->focus_child;
  GtkWidget *parent;
  
  /* We override GtkWindow's behavior, since we don't want wrapping here.
   */
  if (old_focus_child)
    {
844
      if (gtk_widget_child_focus (old_focus_child, direction))
845
	return TRUE;
846 847 848

      if (window->focus_widget)
	{
849 850 851 852 853 854 855 856 857 858 859 860 861 862
	  /* Wrapped off the end, clear the focus setting for the toplevel */
	  parent = window->focus_widget->parent;
	  while (parent)
	    {
	      gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL);
	      parent = GTK_WIDGET (parent)->parent;
	    }
	  
	  gtk_window_set_focus (GTK_WINDOW (container), NULL);
	}
    }
  else
    {
      /* Try to focus the first widget in the window */
863
      if (bin->child && gtk_widget_child_focus (bin->child, direction))
864
        return TRUE;
865 866
    }

867 868 869
  if (!GTK_CONTAINER (window)->focus_child)
    focus_to_parent (plug, direction);

870 871
  return FALSE;
}
872

873 874 875 876 877 878 879 880 881
static void
gtk_plug_check_resize (GtkContainer *container)
{
  if (GTK_WIDGET_TOPLEVEL (container))
    GTK_CONTAINER_CLASS (parent_class)->check_resize (container);
  else
    GTK_CONTAINER_CLASS (bin_class)->check_resize (container);
}

882 883 884 885 886 887
static void
focus_first_last (GtkPlug          *plug,
		  GtkDirectionType  direction)
{
  GtkWindow *window = GTK_WINDOW (plug);
  GtkWidget *parent;
888

889 890 891 892 893 894 895 896 897 898 899 900
  if (window->focus_widget)
    {
      parent = window->focus_widget->parent;
      while (parent)
	{
	  gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL);
	  parent = GTK_WIDGET (parent)->parent;
	}
      
      gtk_window_set_focus (GTK_WINDOW (plug), NULL);
    }

901
  gtk_widget_child_focus (GTK_WIDGET (plug), direction);
902 903 904 905 906 907 908 909
}

static void
handle_modality_on (GtkPlug *plug)
{
  if (!plug->modality_window)
    {
      plug->modality_window = gtk_window_new (GTK_WINDOW_POPUP);
910 911
      gtk_window_set_screen (GTK_WINDOW (plug->modality_window),
			     gtk_widget_get_screen (GTK_WIDGET (plug)));
912
      gtk_widget_realize (plug->modality_window);
913
      gtk_window_group_add_window (plug->modality_group, GTK_WINDOW (plug->modality_window));
914 915 916 917 918 919 920 921 922 923 924 925 926 927
      gtk_grab_add (plug->modality_window);
    }
}

static void
handle_modality_off (GtkPlug *plug)
{
  if (plug->modality_window)
    {
      gtk_widget_destroy (plug->modality_window);
      plug->modality_window = NULL;
    }
}

928
static void
929
xembed_set_info (GdkWindow     *window,
930 931
		 unsigned long  flags)
{
932
  GdkDisplay *display = gdk_drawable_get_display (window);
933
  unsigned long buffer[2];
934 935

  Atom xembed_info_atom = gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO");
936

937
  buffer[0] = GTK_XEMBED_PROTOCOL_VERSION;
938 939
  buffer[1] = flags;

940 941
  XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
		   GDK_WINDOW_XWINDOW (window),
942 943 944 945 946
		   xembed_info_atom, xembed_info_atom, 32,
		   PropModeReplace,
		   (unsigned char *)buffer, 2);
}

947
static void
948 949 950 951 952 953
handle_xembed_message (GtkPlug           *plug,
		       XEmbedMessageType  message,
		       glong              detail,
		       glong              data1,
		       glong              data2,
		       guint32            time)
954
{
955
  GtkWindow *window = GTK_WINDOW (plug);
956

957
  GTK_NOTE (PLUGSOCKET,
958
	    g_message ("GtkPlug: Message of type %d received", message));
959 960 961 962 963 964
  
  switch (message)
    {
    case XEMBED_EMBEDDED_NOTIFY:
      break;
    case XEMBED_WINDOW_ACTIVATE:
965
      _gtk_window_set_is_active (window, TRUE);
966 967
      break;
    case XEMBED_WINDOW_DEACTIVATE:
968
      _gtk_window_set_is_active (window, FALSE);
969 970 971 972 973 974 975 976 977 978
      break;
      
    case XEMBED_MODALITY_ON:
      handle_modality_on (plug);
      break;
    case XEMBED_MODALITY_OFF:
      handle_modality_off (plug);
      break;

    case XEMBED_FOCUS_IN:
979
      _gtk_window_set_has_toplevel_focus (window, TRUE);
980 981 982 983 984 985 986 987 988
      switch (detail)
	{
	case XEMBED_FOCUS_FIRST:
	  focus_first_last (plug, GTK_DIR_TAB_FORWARD);
	  break;
	case XEMBED_FOCUS_LAST:
	  focus_first_last (plug, GTK_DIR_TAB_BACKWARD);
	  break;
	case XEMBED_FOCUS_CURRENT:
989
	  break;
990
	}
991 992
      break;

993
    case XEMBED_FOCUS_OUT:
994 995
      _gtk_window_set_has_toplevel_focus (window, FALSE);
      break;
996
      
997 998 999 1000
    case XEMBED_GRAB_KEY:
    case XEMBED_UNGRAB_KEY:
    case XEMBED_GTK_GRAB_KEY:
    case XEMBED_GTK_UNGRAB_KEY:
1001 1002 1003
    case XEMBED_REQUEST_FOCUS:
    case XEMBED_FOCUS_NEXT:
    case XEMBED_FOCUS_PREV:
1004
      g_warning ("GtkPlug: Invalid _XEMBED message of type %d received", message);
1005 1006 1007 1008
      break;
      
    default:
      GTK_NOTE(PLUGSOCKET,
1009
	       g_message ("GtkPlug: Ignoring unknown _XEMBED message of type %d", message));
1010 1011 1012 1013 1014 1015 1016
      break;
    }
}

static GdkFilterReturn
gtk_plug_filter_func (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data)
{
1017 1018
  GdkScreen *screen = gdk_drawable_get_screen (event->any.window);
  GdkDisplay *display = gdk_screen_get_display (screen);
1019 1020 1021 1022 1023 1024 1025 1026 1027 1028
  GtkPlug *plug = GTK_PLUG (data);
  XEvent *xevent = (XEvent *)gdk_xevent;

  GdkFilterReturn return_val;
  
  return_val = GDK_FILTER_CONTINUE;

  switch (xevent->type)
    {
    case ClientMessage:
1029
      if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED"))
1030
	{
1031
	  _gtk_xembed_push_message (xevent);
1032 1033 1034 1035 1036 1037
	  handle_xembed_message (plug,
				 xevent->xclient.data.l[1],
				 xevent->xclient.data.l[2],
				 xevent->xclient.data.l[3],
				 xevent->xclient.data.l[4],
				 xevent->xclient.data.l[0]);
1038
	  _gtk_xembed_pop_message ();
1039
				 
1040 1041
	  return GDK_FILTER_REMOVE;
	}
1042
      else if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "WM_DELETE_WINDOW"))
1043 1044 1045 1046 1047
	{
	  /* We filter these out because we take being reparented back to the
	   * root window as the reliable end of the embedding protocol
	   */

1048 1049 1050
	  return GDK_FILTER_REMOVE;
	}
      break;
1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073
    case ReparentNotify:
      {
	XReparentEvent *xre = &xevent->xreparent;
	gboolean was_embedded = plug->socket_window != NULL;

	return_val = GDK_FILTER_REMOVE;
	
	g_object_ref (plug);
	
	if (was_embedded)
	  {
	    /* End of embedding protocol for previous socket */
	    
	    /* FIXME: race if we remove from another socket and
	     * then add to a local window before we get notification
	     * Probably need check in _gtk_plug_add_to_socket
	     */
	    
	    if (xre->parent != GDK_WINDOW_XWINDOW (plug->socket_window))
	      {
		GtkWidget *widget = GTK_WIDGET (plug);

		gdk_window_set_user_data (plug->socket_window, NULL);
Manish Singh's avatar
Manish Singh committed
1074
		g_object_unref (plug->socket_window);
1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085
		plug->socket_window = NULL;

		/* Emit a delete window, as if the user attempted
		 * to close the toplevel. Simple as to how we
		 * handle WM_DELETE_WINDOW, if it isn't handled
		 * we destroy the widget. BUt only do this if
		 * we are being reparented to the root window.
		 * Moving from one embedder to another should
		 * be invisible to the app.
		 */

1086
		if (xre->parent == GDK_WINDOW_XWINDOW (gdk_screen_get_root_window (screen)))
1087
		  send_delete_event (widget);
1088 1089
	      }
	    else
1090
	      goto done;
1091 1092
	  }

1093
	if (xre->parent != GDK_WINDOW_XWINDOW (gdk_screen_get_root_window (screen)))
1094 1095 1096
	  {
	    /* Start of embedding protocol */

1097
	    plug->socket_window = gdk_window_lookup_for_display (display, xre->parent);
1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113
	    if (plug->socket_window)
	      {
		gpointer user_data = NULL;
		gdk_window_get_user_data (plug->socket_window, &user_data);

		if (user_data)
		  {
		    g_warning (G_STRLOC "Plug reparented unexpectedly into window in the same process");
		    plug->socket_window = NULL;
		    break;
		  }

		g_object_ref (plug->socket_window);
	      }
	    else
	      {
1114
		plug->socket_window = gdk_window_foreign_new_for_display (display, xre->parent);
1115 1116 1117 1118
		if (!plug->socket_window) /* Already gone */
		  break;
	      }

1119 1120
	    if (plug->grabbed_keys)
	      g_hash_table_foreach (plug->grabbed_keys, add_grabbed_key_always, plug);
1121 1122

	    if (!was_embedded)
Manish Singh's avatar
Manish Singh committed
1123
	      g_signal_emit (plug, plug_signals[EMBEDDED], 0);
1124
	  }
1125 1126

      done:
1127 1128 1129 1130
	g_object_unref (plug);
	
	break;
      }
1131 1132 1133 1134
    }

  return GDK_FILTER_CONTINUE;
}