gtkplug.c 23.9 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 "gtkplug.h"
30
#include "gtkprivate.h"
31

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

35 36 37 38 39 40
#include "xembed.h"

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

61 62 63 64 65 66 67 68 69 70
static void gtk_plug_free_grabbed_keys (GHashTable    *key_table);
static void handle_modality_off        (GtkPlug       *plug);
static void send_xembed_message        (GtkPlug       *plug,
					glong          message,
					glong          detail,
					glong          data1,
					glong          data2,
					guint32        time);
static void xembed_set_info            (GdkWindow     *window,
					unsigned long  flags);
71 72 73

/* From Tk */
#define EMBEDDED_APP_WANTS_FOCUS NotifyNormal+20
74 75
  
static GtkWindowClass *parent_class = NULL;
76
static GtkBinClass *bin_class = NULL;
77

Tor Lillqvist's avatar
Tor Lillqvist committed
78
GtkType
79 80
gtk_plug_get_type ()
{
Tor Lillqvist's avatar
Tor Lillqvist committed
81
  static GtkType plug_type = 0;
82 83 84

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

98
      plug_type = g_type_register_static (GTK_TYPE_WINDOW, "GtkPlug", &plug_info, 0);
99 100 101 102 103 104 105 106
    }

  return plug_type;
}

static void
gtk_plug_class_init (GtkPlugClass *class)
{
107 108
  GtkWidgetClass *widget_class = (GtkWidgetClass *)class;
  GtkWindowClass *window_class = (GtkWindowClass *)class;
109
  GtkContainerClass *container_class = (GtkContainerClass *)class;
110

Tim Janik's avatar
Tim Janik committed
111
  parent_class = gtk_type_class (GTK_TYPE_WINDOW);
112
  bin_class = gtk_type_class (GTK_TYPE_BIN);
113

114
  widget_class->realize = gtk_plug_realize;
115
  widget_class->unrealize = gtk_plug_unrealize;
116
  widget_class->key_press_event = gtk_plug_key_press_event;
117

118 119 120 121 122 123 124 125 126
  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;
127 128

  window_class->set_focus = gtk_plug_set_focus;
129 130 131
#if 0  
  window_class->accel_entries_changed = gtk_plug_accel_entries_changed;
#endif
132 133 134 135 136 137 138 139 140 141 142 143
}

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

  window = GTK_WINDOW (plug);

  window->type = GTK_WINDOW_TOPLEVEL;
  window->auto_shrink = TRUE;
}
144

145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
static void
gtk_plug_set_is_child (GtkPlug  *plug,
		       gboolean  is_child)
{
  if (is_child)
    {
      GTK_WIDGET_UNSET_FLAGS (plug, GTK_TOPLEVEL);
      GTK_PRIVATE_UNSET_FLAG (plug, GTK_ANCHORED);
      gtk_container_set_resize_mode (GTK_CONTAINER (plug), GTK_RESIZE_PARENT);
    }
}

/**
 * _gtk_plug_add_to_socket:
 * @plug: a #GtkPlug
 * @socket: a #GtkSocket
 * 
 * Add a plug to a socket within the same application.
 **/
164
void
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
_gtk_plug_add_to_socket (GtkPlug   *plug,
			 GtkSocket *socket)
{
  GtkWidget *widget = GTK_WIDGET (plug);
  
  g_return_if_fail (GTK_IS_PLUG (plug));
  g_return_if_fail (GTK_IS_SOCKET (socket));
  g_return_if_fail (GTK_WIDGET_REALIZED (socket));

  gtk_plug_set_is_child (plug, TRUE);
  plug->same_app = TRUE;
  socket->plug_widget = widget;

  gtk_widget_set_parent (widget, GTK_WIDGET (socket));

  if (GTK_WIDGET_REALIZED (widget))
    gdk_window_reparent (widget->window, plug->socket_window, 0, 0);
  else
    gtk_widget_realize (widget);

  if (GTK_WIDGET_VISIBLE (socket) && GTK_WIDGET_VISIBLE (widget))
    {
      if (GTK_WIDGET_MAPPED (socket))
	gtk_widget_map (widget);

      gtk_widget_queue_resize (widget);
    }
}

void
gtk_plug_construct (GtkPlug         *plug,
		    GdkNativeWindow  socket_id)
197
{
198
  if (socket_id)
199
    {
200 201
      gpointer user_data = NULL;

202 203
      plug->socket_window = gdk_window_lookup (socket_id);

204 205 206 207 208 209
      if (plug->socket_window)
	gdk_window_get_user_data (plug->socket_window, &user_data);
      else
	plug->socket_window = gdk_window_foreign_new (socket_id);

      if (user_data)
210
	{
211 212 213 214 215 216 217
	  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;
	    }
218
	}
219 220
    }
}
221

222
GtkWidget*
Elliot Lee's avatar
Elliot Lee committed
223
gtk_plug_new (GdkNativeWindow socket_id)
224 225 226
{
  GtkPlug *plug;

Tim Janik's avatar
Tim Janik committed
227
  plug = GTK_PLUG (gtk_type_new (GTK_TYPE_PLUG));
228 229 230 231
  gtk_plug_construct (plug, socket_id);
  return GTK_WIDGET (plug);
}

232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
static void
gtk_plug_unrealize (GtkWidget *widget)
{
  GtkPlug *plug;

  g_return_if_fail (widget != NULL);
  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);
      gdk_window_unref (plug->socket_window);
      plug->socket_window = NULL;
    }

249 250 251 252
  if (!plug->same_app)
    {
      if (plug->modality_window)
	handle_modality_off (plug);
253

254 255 256
      gtk_window_group_remove_window (plug->modality_group, GTK_WINDOW (plug));
      g_object_unref (plug->modality_group);
    }
257
  
258 259 260 261
  if (GTK_WIDGET_CLASS (parent_class)->unrealize)
    (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
}

262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
static void
gtk_plug_realize (GtkWidget *widget)
{
  GtkWindow *window;
  GtkPlug *plug;
  GdkWindowAttr attributes;
  gint attributes_mask;

  g_return_if_fail (widget != NULL);
  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 |
			    GDK_ENTER_NOTIFY_MASK |
			    GDK_LEAVE_NOTIFY_MASK |
			    GDK_FOCUS_CHANGE_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);

301
  if (GTK_WIDGET_TOPLEVEL (widget))
302 303
    {
      gdk_error_trap_push ();
304 305
      widget->window = gdk_window_new (plug->socket_window, 
				       &attributes, attributes_mask);
306
      gdk_flush ();
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
      if (gdk_error_trap_pop ()) /* Uh-oh */
	{
	  gdk_error_trap_push ();
	  gdk_window_destroy (widget->window);
	  gdk_flush ();
	  gdk_error_trap_pop ();
	  widget->window = gdk_window_new (NULL, &attributes, attributes_mask);
	}

      GDK_WINDOW_TYPE (widget->window) = GDK_WINDOW_TOPLEVEL;
      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);
323
    }
324 325
  else
    widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);      
326
  
327 328 329 330
  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);
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
}

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

398 399 400 401 402 403 404 405 406 407 408 409 410 411
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);

      if (GTK_WIDGET_REALIZED (widget))
	gdk_window_move_resize (widget->window,
				allocation->x, allocation->y,
				allocation->width, allocation->height);
412

413 414 415 416 417 418 419 420 421 422 423 424 425 426
      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);
	}
      
    }
427 428
}

429
static gboolean
430 431 432
gtk_plug_key_press_event (GtkWidget   *widget,
			  GdkEventKey *event)
{
433
  if (!GTK_WINDOW (widget)->has_focus)
434
    {
435
      gtk_plug_forward_key_press (GTK_PLUG (widget), event);
436 437
      return TRUE;
    }
438 439
  else
    return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
}

static void
gtk_plug_forward_key_press (GtkPlug *plug, GdkEventKey *event)
{
  XEvent xevent;
  
  xevent.xkey.type = KeyPress;
  xevent.xkey.display = GDK_WINDOW_XDISPLAY (GTK_WIDGET(plug)->window);
  xevent.xkey.window = GDK_WINDOW_XWINDOW (plug->socket_window);
  xevent.xkey.root = GDK_ROOT_WINDOW (); /* FIXME */
  xevent.xkey.time = event->time;
  /* FIXME, the following might cause big problems for
   * non-GTK apps */
  xevent.xkey.x = 0;
  xevent.xkey.y = 0;
  xevent.xkey.x_root = 0;
  xevent.xkey.y_root = 0;
  xevent.xkey.state = event->state;
  xevent.xkey.keycode =  XKeysymToKeycode(GDK_DISPLAY(), 
					  event->keyval);
  xevent.xkey.same_screen = TRUE; /* FIXME ? */
462 463

  gdk_error_trap_push ();
464 465 466
  XSendEvent (gdk_display,
	      GDK_WINDOW_XWINDOW (plug->socket_window),
	      False, NoEventMask, &xevent);
467 468
  gdk_flush ();
  gdk_error_trap_pop ();
469 470
}

471 472 473
static void
gtk_plug_set_focus (GtkWindow *window,
		    GtkWidget *focus)
474
{
475
  GtkPlug *plug = GTK_PLUG (window);
476

477 478 479
  GTK_WINDOW_CLASS (parent_class)->set_focus (window, focus);
  
  /* Ask for focus from embedder
480
   */
481 482

  if (focus && !window->has_focus)
483
    {
484 485
#if 0      
      XEvent xevent;
486

487 488 489 490 491
      xevent.xfocus.type = FocusIn;
      xevent.xfocus.display = GDK_WINDOW_XDISPLAY (GTK_WIDGET(plug)->window);
      xevent.xfocus.window = GDK_WINDOW_XWINDOW (plug->socket_window);
      xevent.xfocus.mode = EMBEDDED_APP_WANTS_FOCUS;
      xevent.xfocus.detail = FALSE; /* Don't force */
492

493 494 495 496 497 498 499 500 501 502 503
      gdk_error_trap_push ();
      XSendEvent (gdk_display,
		  GDK_WINDOW_XWINDOW (plug->socket_window),
		  False, NoEventMask, &xevent);
      gdk_flush ();
      gdk_error_trap_pop ();
#endif

      send_xembed_message (plug, XEMBED_REQUEST_FOCUS, 0, 0, 0,
			   gtk_get_current_event_time ());
    }
504 505
}

506 507 508
#if 0

typedef struct
509
{
510 511 512
  guint			 accelerator_key;
  GdkModifierType	 accelerator_mods;
} GrabbedKey;
513

514 515 516 517 518 519 520 521 522
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;
523

524 525
  return h;
}
526

527 528 529 530 531
static gboolean
grabbed_key_equal (gconstpointer a, gconstpointer b)
{
  const GrabbedKey *keya = a;
  const GrabbedKey *keyb = b;
532

533 534 535
  return (keya->accelerator_key == keyb->accelerator_key &&
	  keya->accelerator_mods == keyb->accelerator_mods);
}
536

537 538 539 540 541 542 543 544 545 546 547 548
static void
add_grabbed_keys (gpointer key, gpointer val, gpointer data)
{
  GrabbedKey *grabbed_key = key;
  GtkPlug *plug = data;

  if (!plug->grabbed_keys ||
      !g_hash_table_lookup (plug->grabbed_keys, grabbed_key))
    {
      send_xembed_message (plug, XEMBED_GRAB_KEY, 0, 
			   grabbed_key->accelerator_key, grabbed_key->accelerator_mods,
			   gtk_get_current_event_time ());
549
    }
550
}
551

552 553 554 555 556 557 558 559 560 561 562 563 564
static void
remove_grabbed_keys (gpointer key, gpointer val, gpointer data)
{
  GrabbedKey *grabbed_key = key;
  GtkPlug *plug = data;

  if (!plug->grabbed_keys ||
      !g_hash_table_lookup (plug->grabbed_keys, grabbed_key))
    {
      send_xembed_message (plug, XEMBED_UNGRAB_KEY, 0, 
			   grabbed_key->accelerator_key, grabbed_key->accelerator_mods,
			   gtk_get_current_event_time ());
    }
565 566 567
}

static void
568
gtk_plug_free_grabbed_keys (GHashTable *key_table)
569
{
570 571 572
  g_hash_table_foreach (key_table, (GHFunc)g_free, NULL);
  g_hash_table_destroy (key_table);
}
573

574 575 576 577 578 579
static void
gtk_plug_accel_entries_changed (GtkWindow *window)
{
  GHashTable *new_grabbed_keys, *old_grabbed_keys;
  GSList *accel_groups, *tmp_list;
  GtkPlug *plug = GTK_PLUG (window);
580

581
  new_grabbed_keys = g_hash_table_new (grabbed_key_hash, grabbed_key_equal);
582

583 584 585
  accel_groups = gtk_accel_groups_from_object (GTK_OBJECT (window));
  
  tmp_list = accel_groups;
586

587
  while (tmp_list)
588
    {
589 590 591 592 593
      GtkAccelGroup *accel_group = tmp_list->data;
      gint i, n_entries;
      GtkAccelEntry *entries;

      gtk_accel_group_get_entries (accel_group, &entries, &n_entries);
594

595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610
      for (i = 0; i < n_entries; i++)
	{
	  GdkKeymapKey *keys;
	  gint n_keys;
	  
	  if (gdk_keymap_get_entries_for_keyval (NULL, entries[i].accelerator_key, &keys, &n_keys))
	    {
	      GrabbedKey *key = g_new (GrabbedKey, 1);
	      
	      key->accelerator_key = keys[0].keycode;
	      key->accelerator_mods = entries[i].accelerator_mods;
	      
	      g_hash_table_insert (new_grabbed_keys, key, key);

	      g_free (keys);
	    }
611
	}
612 613 614 615 616
      
      tmp_list = tmp_list->next;
    }

  g_hash_table_foreach (new_grabbed_keys, add_grabbed_keys, plug);
617

618 619 620 621 622 623 624 625 626 627 628 629 630
  old_grabbed_keys = plug->grabbed_keys;
  plug->grabbed_keys = new_grabbed_keys;

  if (old_grabbed_keys)
    {
      g_hash_table_foreach (old_grabbed_keys, remove_grabbed_keys, plug);
      gtk_plug_free_grabbed_keys (old_grabbed_keys);
    }

}
#endif

static gboolean
631
gtk_plug_focus (GtkWidget        *widget,
632 633
		GtkDirectionType  direction)
{
634 635 636 637
  GtkBin *bin = GTK_BIN (widget);
  GtkPlug *plug = GTK_PLUG (widget);
  GtkWindow *window = GTK_WINDOW (widget);
  GtkContainer *container = GTK_CONTAINER (widget);
638 639 640 641 642 643 644
  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)
    {
645
      if (gtk_widget_child_focus (old_focus_child, direction))
646
	return TRUE;
647 648 649

      if (window->focus_widget)
	{
650 651 652 653 654 655 656 657 658
	  /* 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);
659

660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700
	  if (!GTK_CONTAINER (window)->focus_child)
	    {
	      gint message = -1;

	      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;
		}
	      
	      send_xembed_message (plug, message, 0, 0, 0,
				   gtk_get_current_event_time ());
	      
#if 0	      
	      gtk_window_set_focus (GTK_WINDOW (widget), NULL);

	      gdk_error_trap_push ();
	      XSetInputFocus (GDK_DISPLAY (),
			      GDK_WINDOW_XWINDOW (plug->socket_window),
			      RevertToParent, event->time);
	      gdk_flush ();
	      gdk_error_trap_pop ();

	      gtk_plug_forward_key_press (plug, event);
#endif	      
	    }
	}

      return FALSE;
    }
  else
    {
      /* Try to focus the first widget in the window */
701 702 703
      
      if (gtk_widget_child_focus (bin->child, direction))
        return TRUE;
704 705
    }

706 707
  return FALSE;
}
708

709 710 711 712 713 714 715 716 717
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);
}

718 719 720 721 722 723 724 725 726
static void
send_xembed_message (GtkPlug *plug,
		     glong      message,
		     glong      detail,
		     glong      data1,
		     glong      data2,
		     guint32    time)
{
  if (plug->socket_window)
727 728 729
    {
      XEvent xevent;

730 731 732 733 734 735 736 737 738
      xevent.xclient.window = GDK_WINDOW_XWINDOW (plug->socket_window);
      xevent.xclient.type = ClientMessage;
      xevent.xclient.message_type = gdk_atom_intern ("_XEMBED", FALSE);
      xevent.xclient.format = 32;
      xevent.xclient.data.l[0] = time;
      xevent.xclient.data.l[1] = message;
      xevent.xclient.data.l[2] = detail;
      xevent.xclient.data.l[3] = data1;
      xevent.xclient.data.l[4] = data2;
739

740
      gdk_error_trap_push ();
741 742 743
      XSendEvent (gdk_display,
		  GDK_WINDOW_XWINDOW (plug->socket_window),
		  False, NoEventMask, &xevent);
744 745
      gdk_flush ();
      gdk_error_trap_pop ();
746 747
    }
}
748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767

static void
focus_first_last (GtkPlug          *plug,
		  GtkDirectionType  direction)
{
  GtkWindow *window = GTK_WINDOW (plug);
  GtkWidget *parent;
  
  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);
    }

768
  gtk_widget_child_focus (GTK_WIDGET (plug), direction);
769 770 771 772 773 774 775 776
}

static void
handle_modality_on (GtkPlug *plug)
{
  if (!plug->modality_window)
    {
      plug->modality_window = gtk_window_new (GTK_WINDOW_POPUP);
777
      gtk_window_group_add_window (plug->modality_group, GTK_WINDOW (plug->modality_window));
778 779 780 781 782 783 784 785 786 787 788 789 790 791
      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;
    }
}

792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810
static void
xembed_set_info (GdkWindow     *gdk_window,
		 unsigned long  flags)
{
  Display *display = GDK_WINDOW_XDISPLAY (gdk_window);
  Window window = GDK_WINDOW_XWINDOW (gdk_window);
  unsigned long buffer[2];
  
  Atom xembed_info_atom = gdk_atom_intern ("_XEMBED_INFO", FALSE);

  buffer[1] = 0;		/* Protocol version */
  buffer[1] = flags;

  XChangeProperty (display, window,
		   xembed_info_atom, xembed_info_atom, 32,
		   PropModeReplace,
		   (unsigned char *)buffer, 2);
}

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 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913
static void
handle_xembed_message (GtkPlug   *plug,
		       glong      message,
		       glong      detail,
		       glong      data1,
		       glong      data2,
		       guint32    time)
{
  GTK_NOTE (PLUGSOCKET,
	    g_message ("Message of type %ld received", message));
  
  switch (message)
    {
    case XEMBED_EMBEDDED_NOTIFY:
      break;
    case XEMBED_WINDOW_ACTIVATE:
      GTK_NOTE(PLUGSOCKET,
	       g_message ("GtkPlug: ACTIVATE received"));
      break;
    case XEMBED_WINDOW_DEACTIVATE:
      GTK_NOTE(PLUGSOCKET,
	       g_message ("GtkPlug: DEACTIVATE received"));
      break;
      
    case XEMBED_MODALITY_ON:
      handle_modality_on (plug);
      break;
    case XEMBED_MODALITY_OFF:
      handle_modality_off (plug);
      break;

    case XEMBED_FOCUS_IN:
      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:
	  /* fall through */;
	}
      
    case XEMBED_FOCUS_OUT:
      {
	GdkEvent event;

	event.focus_change.type = GDK_FOCUS_CHANGE;
	event.focus_change.window = GTK_WIDGET (plug)->window;
	event.focus_change.send_event = TRUE;
	event.focus_change.in = (message == XEMBED_FOCUS_IN);

	gtk_widget_event (GTK_WIDGET (plug), &event);

	break;
      }
      
    case XEMBED_REQUEST_FOCUS:
    case XEMBED_FOCUS_NEXT:
    case XEMBED_FOCUS_PREV:
    case XEMBED_GRAB_KEY:
    case XEMBED_UNGRAB_KEY:
      g_warning ("GtkPlug: Invalid _XEMBED message of type %ld received", message);
      break;
      
    default:
      GTK_NOTE(PLUGSOCKET,
	       g_message ("GtkPlug: Ignoring unknown _XEMBED message of type %ld", message));
      break;
    }
}

static GdkFilterReturn
gtk_plug_filter_func (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data)
{
  GtkPlug *plug = GTK_PLUG (data);
  XEvent *xevent = (XEvent *)gdk_xevent;

  GdkFilterReturn return_val;
  
  return_val = GDK_FILTER_CONTINUE;

  switch (xevent->type)
    {
    case ClientMessage:
      if (xevent->xclient.message_type == gdk_atom_intern ("_XEMBED", FALSE))
	{
	  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]);
				 

	  return GDK_FILTER_REMOVE;
	}
      break;
    }

  return GDK_FILTER_CONTINUE;
}