gtkplug.c 30.4 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
#include "gtkalias.h"

40 41
static void            gtk_plug_class_init            (GtkPlugClass     *klass);
static void            gtk_plug_init                  (GtkPlug          *plug);
42
static void            gtk_plug_finalize              (GObject          *object);
43 44
static void            gtk_plug_realize               (GtkWidget        *widget);
static void            gtk_plug_unrealize             (GtkWidget        *widget);
45 46 47 48 49 50
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);
51 52
static gboolean        gtk_plug_key_press_event       (GtkWidget        *widget,
						       GdkEventKey      *event);
53 54
static gboolean        gtk_plug_focus_event           (GtkWidget        *widget,
						       GdkEventFocus    *event);
55 56
static void            gtk_plug_set_focus             (GtkWindow        *window,
						       GtkWidget        *focus);
57
static gboolean        gtk_plug_focus                 (GtkWidget        *widget,
58
						       GtkDirectionType  direction);
59
static void            gtk_plug_check_resize          (GtkContainer     *container);
60
static void            gtk_plug_keys_changed          (GtkWindow        *window);
61 62 63 64
static GdkFilterReturn gtk_plug_filter_func           (GdkXEvent        *gdk_xevent,
						       GdkEvent         *event,
						       gpointer          data);

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

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

75 76 77 78 79 80 81
enum {
  EMBEDDED,
  LAST_SIGNAL
}; 

static guint plug_signals[LAST_SIGNAL] = { 0 };

Manish Singh's avatar
Manish Singh committed
82
GType
Matthias Clasen's avatar
Matthias Clasen committed
83
gtk_plug_get_type (void)
84
{
Manish Singh's avatar
Manish Singh committed
85
  static GType plug_type = 0;
86 87 88

  if (!plug_type)
    {
89
      static const GTypeInfo plug_info =
90 91
      {
	sizeof (GtkPlugClass),
92 93 94 95 96 97 98 99
	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,
100 101
      };

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

  return plug_type;
}

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

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

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

128 129 130 131 132 133 134 135 136
  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;
137 138

  window_class->set_focus = gtk_plug_set_focus;
139
  window_class->keys_changed = gtk_plug_keys_changed;
140 141 142 143 144 145 146

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

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

  window = GTK_WINDOW (plug);

  window->type = GTK_WINDOW_TOPLEVEL;
}
160

161 162 163 164
static void
gtk_plug_set_is_child (GtkPlug  *plug,
		       gboolean  is_child)
{
165 166
  g_assert (!GTK_WIDGET (plug)->parent);
      
167 168
  if (is_child)
    {
169 170 171 172 173 174 175 176 177 178
      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;
	}
      
179 180 181 182 183 184 185 186
      /* 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));
      
187 188
      GTK_WIDGET_UNSET_FLAGS (plug, GTK_TOPLEVEL);
      gtk_container_set_resize_mode (GTK_CONTAINER (plug), GTK_RESIZE_PARENT);
189 190 191 192 193

      _gtk_widget_propagate_hierarchy_changed (GTK_WIDGET (plug), GTK_WIDGET (plug));
    }
  else
    {
194 195 196 197 198
      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);
	  
199 200 201 202 203 204 205
      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);
206 207 208 209 210 211
    }
}

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

227 228
  widget = GTK_WIDGET (plug);

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

234 235
  plug->socket_window = GTK_WIDGET (socket)->window;

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

242
  gtk_widget_set_parent (widget, GTK_WIDGET (socket));
243

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

247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
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);
}

265
/**
266
 * _gtk_plug_remove_from_socket:
267
 * @plug: a #GtkPlug
268
 * @socket_: a #GtkSocket
269
 * 
270
 * Removes a plug from a socket within the same application.
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
 **/
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);
292 293 294
  gdk_window_reparent (widget->window,
		       gtk_widget_get_root_window (widget),
		       0, 0);
295 296 297 298 299 300

  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;
301 302 303 304 305
  if (socket->plug_window != NULL)
    {
      g_object_unref (socket->plug_window);
      socket->plug_window = NULL;
    }
306
  
307 308 309
  socket->same_app = FALSE;

  plug->same_app = FALSE;
310
  plug->socket_window = NULL;
311 312 313

  gtk_plug_set_is_child (plug, FALSE);
		    
Manish Singh's avatar
Manish Singh committed
314
  g_signal_emit_by_name (socket, "plug_removed", &result);
315 316 317
  if (!result)
    gtk_widget_destroy (GTK_WIDGET (socket));

318 319
  send_delete_event (widget);

320 321 322 323 324 325
  g_object_unref (plug);

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

  g_object_unref (socket);
326 327
}

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

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

365 366
      plug->socket_window = gdk_window_lookup_for_display (display, socket_id);
      
367 368 369
      if (plug->socket_window)
	gdk_window_get_user_data (plug->socket_window, &user_data);
      else
370 371
	plug->socket_window = gdk_window_foreign_new_for_display (display, socket_id);
	  
372
      if (user_data)
373
	{
374 375 376 377 378 379 380
	  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;
	    }
381
	}
382 383

      if (plug->socket_window)
Manish Singh's avatar
Manish Singh committed
384
	g_signal_emit (plug, plug_signals[EMBEDDED], 0);
385 386
    }
}
387

388 389 390 391 392 393 394 395 396 397
/**
 * 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.
 **/
398
GtkWidget*
399
gtk_plug_new (GdkNativeWindow socket_id)
400
{
Owen Taylor's avatar
Owen Taylor committed
401
  return gtk_plug_new_for_display (gdk_display_get_default (), socket_id);
402 403 404 405 406 407 408
}

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

Manish Singh's avatar
Manish Singh committed
421
  plug = g_object_new (GTK_TYPE_PLUG, NULL);
422
  gtk_plug_construct_for_display (plug, display, socket_id);
423 424 425
  return GTK_WIDGET (plug);
}

426 427 428 429 430 431
/**
 * 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
432
 * instance with gtk_socket_add_id().
433 434 435 436 437 438 439 440 441 442 443 444 445 446
 * 
 * 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);
}

447 448 449 450 451 452 453 454 455 456 457 458 459 460
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);
}

461 462 463 464 465 466 467 468 469 470 471 472
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
473
      g_object_unref (plug->socket_window);
474 475 476
      plug->socket_window = NULL;
    }

477 478 479 480
  if (!plug->same_app)
    {
      if (plug->modality_window)
	handle_modality_off (plug);
481

482 483 484
      gtk_window_group_remove_window (plug->modality_group, GTK_WINDOW (plug));
      g_object_unref (plug->modality_group);
    }
485
  
486 487 488 489
  if (GTK_WIDGET_CLASS (parent_class)->unrealize)
    (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
}

490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518
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 |
519
			    GDK_KEY_RELEASE_MASK |
520 521 522 523 524 525 526 527
			    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);

528
  if (GTK_WIDGET_TOPLEVEL (widget))
529
    {
530 531
      attributes.window_type = GDK_WINDOW_TOPLEVEL;

532
      gdk_error_trap_push ();
533 534 535 536 537 538 539 540
      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));
541 542 543 544 545 546
      if (gdk_error_trap_pop ()) /* Uh-oh */
	{
	  gdk_error_trap_push ();
	  gdk_window_destroy (widget->window);
	  gdk_flush ();
	  gdk_error_trap_pop ();
547 548
	  widget->window = gdk_window_new (gtk_widget_get_root_window (widget),
					   &attributes, attributes_mask);
549
	}
550
      
551 552 553 554 555 556
      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);
557
    }
558
  else
559 560
    widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), 
				     &attributes, attributes_mask);      
561
  
562 563 564 565
  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);
566 567

  gdk_window_enable_synchronized_configure (widget->window);
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 627 628 629 630 631 632 633
}

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);
}
634

635 636 637 638 639 640 641 642 643 644
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);

645 646
      widget->allocation = *allocation;

647 648 649 650
      if (GTK_WIDGET_REALIZED (widget))
	gdk_window_move_resize (widget->window,
				allocation->x, allocation->y,
				allocation->width, allocation->height);
651

652 653 654 655 656 657 658 659 660 661 662 663 664 665
      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);
	}
      
    }
666 667
}

668
static gboolean
669 670 671
gtk_plug_key_press_event (GtkWidget   *widget,
			  GdkEventKey *event)
{
672
  if (GTK_WIDGET_TOPLEVEL (widget))
673
    return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
674
  else
675
    return FALSE;
676 677
}

678 679 680 681 682 683 684 685 686 687 688
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;
}

689 690 691
static void
gtk_plug_set_focus (GtkWindow *window,
		    GtkWidget *focus)
692
{
693
  GtkPlug *plug = GTK_PLUG (window);
694

695 696 697
  GTK_WINDOW_CLASS (parent_class)->set_focus (window, focus);
  
  /* Ask for focus from embedder
698
   */
699

700
  if (focus && !window->has_toplevel_focus)
701
    {
702 703
      _gtk_xembed_send_message (plug->socket_window,
				XEMBED_REQUEST_FOCUS, 0, 0, 0);
704
    }
705 706
}

707
typedef struct
708
{
709 710 711
  guint			 accelerator_key;
  GdkModifierType	 accelerator_mods;
} GrabbedKey;
712

713 714 715 716 717 718 719 720 721
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;
722

723 724
  return h;
}
725

726 727 728 729 730
static gboolean
grabbed_key_equal (gconstpointer a, gconstpointer b)
{
  const GrabbedKey *keya = a;
  const GrabbedKey *keyb = b;
731

732 733 734
  return (keya->accelerator_key == keyb->accelerator_key &&
	  keya->accelerator_mods == keyb->accelerator_mods);
}
735

736
static void
737
add_grabbed_key (gpointer key, gpointer val, gpointer data)
738 739 740 741 742 743 744
{
  GrabbedKey *grabbed_key = key;
  GtkPlug *plug = data;

  if (!plug->grabbed_keys ||
      !g_hash_table_lookup (plug->grabbed_keys, grabbed_key))
    {
745 746
      _gtk_xembed_send_message (plug->socket_window, XEMBED_GTK_GRAB_KEY, 0, 
				grabbed_key->accelerator_key, grabbed_key->accelerator_mods);
747
    }
748
}
749

750
static void
751 752 753 754 755
add_grabbed_key_always (gpointer key, gpointer val, gpointer data)
{
  GrabbedKey *grabbed_key = key;
  GtkPlug *plug = data;

756 757
  _gtk_xembed_send_message (plug->socket_window, XEMBED_GTK_GRAB_KEY, 0, 
			    grabbed_key->accelerator_key, grabbed_key->accelerator_mods);
758 759 760 761
}

static void
remove_grabbed_key (gpointer key, gpointer val, gpointer data)
762 763 764 765 766 767 768
{
  GrabbedKey *grabbed_key = key;
  GtkPlug *plug = data;

  if (!plug->grabbed_keys ||
      !g_hash_table_lookup (plug->grabbed_keys, grabbed_key))
    {
769 770
      _gtk_xembed_send_message (plug->socket_window, XEMBED_GTK_UNGRAB_KEY, 0, 
				grabbed_key->accelerator_key, grabbed_key->accelerator_mods);
771
    }
772 773 774
}

static void
775 776 777 778 779
keys_foreach (GtkWindow      *window,
	      guint           keyval,
	      GdkModifierType modifiers,
	      gboolean        is_mnemonic,
	      gpointer        data)
780
{
781 782 783 784 785 786 787
  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);
788
}
789

790
static void
791
gtk_plug_keys_changed (GtkWindow *window)
792 793 794
{
  GHashTable *new_grabbed_keys, *old_grabbed_keys;
  GtkPlug *plug = GTK_PLUG (window);
795

796 797
  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);
798

799 800
  if (plug->socket_window)
    g_hash_table_foreach (new_grabbed_keys, add_grabbed_key, plug);
801

802 803 804 805 806
  old_grabbed_keys = plug->grabbed_keys;
  plug->grabbed_keys = new_grabbed_keys;

  if (old_grabbed_keys)
    {
807 808 809
      if (plug->socket_window)
	g_hash_table_foreach (old_grabbed_keys, remove_grabbed_key, plug);
      g_hash_table_destroy (old_grabbed_keys);
810 811 812
    }
}

813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835
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);
}

836
static gboolean
837
gtk_plug_focus (GtkWidget        *widget,
838 839
		GtkDirectionType  direction)
{
840 841 842 843
  GtkBin *bin = GTK_BIN (widget);
  GtkPlug *plug = GTK_PLUG (widget);
  GtkWindow *window = GTK_WINDOW (widget);
  GtkContainer *container = GTK_CONTAINER (widget);
844 845 846 847 848 849 850
  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)
    {
851
      if (gtk_widget_child_focus (old_focus_child, direction))
852
	return TRUE;
853 854 855

      if (window->focus_widget)
	{
856 857 858 859 860 861 862 863 864 865 866 867 868 869
	  /* 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 */
870
      if (bin->child && gtk_widget_child_focus (bin->child, direction))
871
        return TRUE;
872 873
    }

874 875 876
  if (!GTK_CONTAINER (window)->focus_child)
    focus_to_parent (plug, direction);

877 878
  return FALSE;
}
879

880 881 882 883 884 885 886 887 888
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);
}

889 890 891 892 893 894
static void
focus_first_last (GtkPlug          *plug,
		  GtkDirectionType  direction)
{
  GtkWindow *window = GTK_WINDOW (plug);
  GtkWidget *parent;
895

896 897 898 899 900 901 902 903 904 905 906 907
  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);
    }

908
  gtk_widget_child_focus (GTK_WIDGET (plug), direction);
909 910 911 912 913 914 915 916
}

static void
handle_modality_on (GtkPlug *plug)
{
  if (!plug->modality_window)
    {
      plug->modality_window = gtk_window_new (GTK_WINDOW_POPUP);
917 918
      gtk_window_set_screen (GTK_WINDOW (plug->modality_window),
			     gtk_widget_get_screen (GTK_WIDGET (plug)));
919
      gtk_widget_realize (plug->modality_window);
920
      gtk_window_group_add_window (plug->modality_group, GTK_WINDOW (plug->modality_window));
921 922 923 924 925 926 927 928 929 930 931 932 933 934
      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;
    }
}

935
static void
936
xembed_set_info (GdkWindow     *window,
937 938
		 unsigned long  flags)
{
939
  GdkDisplay *display = gdk_drawable_get_display (window);
940
  unsigned long buffer[2];
941 942

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

944
  buffer[0] = GTK_XEMBED_PROTOCOL_VERSION;
945 946
  buffer[1] = flags;

947 948
  XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
		   GDK_WINDOW_XWINDOW (window),
949 950 951 952 953
		   xembed_info_atom, xembed_info_atom, 32,
		   PropModeReplace,
		   (unsigned char *)buffer, 2);
}

954
static void
955 956 957 958 959 960
handle_xembed_message (GtkPlug           *plug,
		       XEmbedMessageType  message,
		       glong              detail,
		       glong              data1,
		       glong              data2,
		       guint32            time)
961
{
962
  GtkWindow *window = GTK_WINDOW (plug);
963

964
  GTK_NOTE (PLUGSOCKET,
965
	    g_message ("GtkPlug: Message of type %d received", message));
966 967 968 969 970 971
  
  switch (message)
    {
    case XEMBED_EMBEDDED_NOTIFY:
      break;
    case XEMBED_WINDOW_ACTIVATE:
972
      _gtk_window_set_is_active (window, TRUE);
973 974
      break;
    case XEMBED_WINDOW_DEACTIVATE:
975
      _gtk_window_set_is_active (window, FALSE);
976 977 978 979 980 981 982 983 984 985
      break;
      
    case XEMBED_MODALITY_ON:
      handle_modality_on (plug);
      break;
    case XEMBED_MODALITY_OFF:
      handle_modality_off (plug);
      break;

    case XEMBED_FOCUS_IN:
986
      _gtk_window_set_has_toplevel_focus (window, TRUE);
987 988 989 990 991 992 993 994 995
      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:
996
	  break;
997
	}
998 999
      break;

1000
    case XEMBED_FOCUS_OUT:
1001 1002
      _gtk_window_set_has_toplevel_focus (window, FALSE);
      break;
1003
      
1004 1005 1006 1007
    case XEMBED_GRAB_KEY:
    case XEMBED_UNGRAB_KEY:
    case XEMBED_GTK_GRAB_KEY:
    case XEMBED_GTK_UNGRAB_KEY:
1008 1009 1010
    case XEMBED_REQUEST_FOCUS:
    case XEMBED_FOCUS_NEXT:
    case XEMBED_FOCUS_PREV:
1011
      g_warning ("GtkPlug: Invalid _XEMBED message of type %d received", message);
1012 1013 1014 1015
      break;
      
    default:
      GTK_NOTE(PLUGSOCKET,
1016
	       g_message ("GtkPlug: Ignoring unknown _XEMBED message of type %d", message));
1017 1018 1019 1020 1021 1022 1023
      break;
    }
}

static GdkFilterReturn
gtk_plug_filter_func (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data)
{
1024 1025
  GdkScreen *screen = gdk_drawable_get_screen (event->any.window);
  GdkDisplay *display = gdk_screen_get_display (screen);
1026 1027 1028 1029 1030 1031 1032 1033 1034 1035
  GtkPlug *plug = GTK_PLUG (data);
  XEvent *xevent = (XEvent *)gdk_xevent;

  GdkFilterReturn return_val;
  
  return_val = GDK_FILTER_CONTINUE;

  switch (xevent->type)
    {
    case ClientMessage:
1036
      if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED"))
1037
	{
1038
	  _gtk_xembed_push_message (xevent);
1039 1040 1041 1042 1043 1044
	  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]);
1045
	  _gtk_xembed_pop_message ();
1046
				 
1047 1048
	  return GDK_FILTER_REMOVE;
	}
1049
      else if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "WM_DELETE_WINDOW"))
1050 1051 1052 1053 1054
	{
	  /* We filter these out because we take being reparented back to the
	   * root window as the reliable end of the embedding protocol
	   */

1055 1056 1057
	  return GDK_FILTER_REMOVE;
	}
      break;
1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080
    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
1081
		g_object_unref (plug->socket_window);
1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092
		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.
		 */

1093
		if (xre->parent == GDK_WINDOW_XWINDOW (gdk_screen_get_root_window (screen)))
1094
		  send_delete_event (widget);
1095 1096
	      }
	    else
1097
	      goto done;
1098 1099
	  }

1100
	if (xre->parent != GDK_WINDOW_XWINDOW (gdk_screen_get_root_window (screen)))
1101 1102 1103
	  {
	    /* Start of embedding protocol */

1104
	    plug->socket_window = gdk_window_lookup_for_display (display, xre->parent);
1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120
	    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
	      {
1121
		plug->socket_window = gdk_window_foreign_new_for_display (display, xre->parent);
1122 1123 1124 1125
		if (!plug->socket_window) /* Already gone */
		  break;
	      }

1126 1127
	    if (plug->grabbed_keys)
	      g_hash_table_foreach (plug->grabbed_keys, add_grabbed_key_always, plug);
1128 1129

	    if (!was_embedded)
Manish Singh's avatar
Manish Singh committed
1130
	      g_signal_emit (plug, plug_signals[EMBEDDED], 0);
1131
	  }
1132 1133

      done:
1134 1135 1136 1137
	g_object_unref (plug);
	
	break;
      }
1138 1139 1140 1141
    }

  return GDK_FILTER_CONTINUE;
}
1142 1143 1144

#define __GTK_PLUG_C__
#include "gtkaliasdef.c"