gtkwin32embedwidget.c 12.2 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
1
/* GTK - The GIMP Toolkit
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 * Modified by the GTK+ Team and others 1997-2006.  See the AUTHORS
 * 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/. 
 */

26
#include "config.h"
27 28 29

#include "gtkmain.h"
#include "gtkmarshalers.h"
Tor Lillqvist's avatar
Tor Lillqvist committed
30
#include "gtksizerequest.h"
31 32 33
#include "gtkwin32embedwidget.h"
#include "gtkintl.h"
#include "gtkprivate.h"
34
#include "gtkwindowprivate.h"
35 36
#include "gtkwidgetprivate.h"
#include "gtkcontainerprivate.h"
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88


static void            gtk_win32_embed_widget_realize               (GtkWidget        *widget);
static void            gtk_win32_embed_widget_unrealize             (GtkWidget        *widget);
static void            gtk_win32_embed_widget_show                  (GtkWidget        *widget);
static void            gtk_win32_embed_widget_hide                  (GtkWidget        *widget);
static void            gtk_win32_embed_widget_map                   (GtkWidget        *widget);
static void            gtk_win32_embed_widget_unmap                 (GtkWidget        *widget);
static void            gtk_win32_embed_widget_size_allocate         (GtkWidget        *widget,
								     GtkAllocation    *allocation);
static void            gtk_win32_embed_widget_set_focus             (GtkWindow        *window,
								     GtkWidget        *focus);
static gboolean        gtk_win32_embed_widget_focus                 (GtkWidget        *widget,
								     GtkDirectionType  direction);
static void            gtk_win32_embed_widget_check_resize          (GtkContainer     *container);

static GtkBinClass *bin_class = NULL;

G_DEFINE_TYPE (GtkWin32EmbedWidget, gtk_win32_embed_widget, GTK_TYPE_WINDOW)

static void
gtk_win32_embed_widget_class_init (GtkWin32EmbedWidgetClass *class)
{
  GtkWidgetClass *widget_class = (GtkWidgetClass *)class;
  GtkWindowClass *window_class = (GtkWindowClass *)class;
  GtkContainerClass *container_class = (GtkContainerClass *)class;

  bin_class = g_type_class_peek (GTK_TYPE_BIN);

  widget_class->realize = gtk_win32_embed_widget_realize;
  widget_class->unrealize = gtk_win32_embed_widget_unrealize;

  widget_class->show = gtk_win32_embed_widget_show;
  widget_class->hide = gtk_win32_embed_widget_hide;
  widget_class->map = gtk_win32_embed_widget_map;
  widget_class->unmap = gtk_win32_embed_widget_unmap;
  widget_class->size_allocate = gtk_win32_embed_widget_size_allocate;

  widget_class->focus = gtk_win32_embed_widget_focus;
  
  container_class->check_resize = gtk_win32_embed_widget_check_resize;

  window_class->set_focus = gtk_win32_embed_widget_set_focus;
}

static void
gtk_win32_embed_widget_init (GtkWin32EmbedWidget *embed_widget)
{
  GtkWindow *window;

  window = GTK_WINDOW (embed_widget);

89
  _gtk_widget_set_is_toplevel (GTK_WIDGET (embed_widget), TRUE);
90 91 92 93
  gtk_container_set_resize_mode (GTK_CONTAINER (embed_widget), GTK_RESIZE_QUEUE);
}

GtkWidget*
94
_gtk_win32_embed_widget_new (HWND parent)
95 96 97 98 99 100
{
  GtkWin32EmbedWidget *embed_widget;

  embed_widget = g_object_new (GTK_TYPE_WIN32_EMBED_WIDGET, NULL);
  
  embed_widget->parent_window =
101
    gdk_win32_window_lookup_for_display (gdk_display_get_default (),
102
					 parent);
103 104 105
  
  if (!embed_widget->parent_window)
    embed_widget->parent_window =
106
      gdk_win32_window_foreign_new_for_display (gdk_display_get_default (),
107
					  parent);
108 109 110 111 112 113 114 115
  
  return GTK_WIDGET (embed_widget);
}

BOOL
_gtk_win32_embed_widget_dialog_procedure (GtkWin32EmbedWidget *embed_widget,
					  HWND wnd, UINT message, WPARAM wparam, LPARAM lparam)
{
116
  GtkAllocation allocation;
117 118 119 120
  GtkWidget *widget = GTK_WIDGET (embed_widget);
  
 if (message == WM_SIZE)
   {
121 122 123 124
     allocation.width = LOWORD(lparam);
     allocation.height = HIWORD(lparam);
     gtk_widget_set_allocation (widget, &allocation);

125 126 127 128 129 130 131 132 133
     gtk_widget_queue_resize (widget);
   }
        
 return 0;
}

static void
gtk_win32_embed_widget_unrealize (GtkWidget *widget)
{
134
  GtkWin32EmbedWidget *embed_widget = GTK_WIN32_EMBED_WIDGET (widget);
135 136 137 138 139 140 141 142 143

  embed_widget->old_window_procedure = NULL;
  
  if (embed_widget->parent_window != NULL)
    {
      gdk_window_set_user_data (embed_widget->parent_window, NULL);
      g_object_unref (embed_widget->parent_window);
      embed_widget->parent_window = NULL;
    }
144

145
  GTK_WIDGET_CLASS (gtk_win32_embed_widget_parent_class)->unrealize (widget);
146 147 148 149 150 151 152 153 154
}

static LRESULT CALLBACK
gtk_win32_embed_widget_window_process (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
  GdkWindow *window;
  GtkWin32EmbedWidget *embed_widget;
  gpointer user_data;

155
  window = gdk_win32_window_lookup_for_display (gdk_display_get_default (), hwnd);
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
  if (window == NULL) {
    g_warning ("No such window!");
    return 0;
  }
  gdk_window_get_user_data (window, &user_data);
  embed_widget = GTK_WIN32_EMBED_WIDGET (user_data);

  if (msg == WM_GETDLGCODE) {
    return DLGC_WANTALLKEYS;
  }

  if (embed_widget && embed_widget->old_window_procedure)
    return CallWindowProc(embed_widget->old_window_procedure,
			  hwnd, msg, wparam, lparam);
  else
    return 0;
}

static void
gtk_win32_embed_widget_realize (GtkWidget *widget)
{
177 178
  GtkWindow *window = GTK_WINDOW (widget);
  GtkWin32EmbedWidget *embed_widget = GTK_WIN32_EMBED_WIDGET (widget);
179 180
  GtkAllocation allocation;
  GdkWindow *gdk_window;
181 182 183 184
  GdkWindowAttr attributes;
  gint attributes_mask;
  LONG_PTR styles;

185 186
  gtk_widget_get_allocation (widget, &allocation);

187
  /* ensure widget tree is properly size allocated */
188 189
  if (allocation.x == -1 && allocation.y == -1 &&
      allocation.width == 1 && allocation.height == 1)
190 191 192 193
    {
      GtkRequisition requisition;
      GtkAllocation allocation = { 0, 0, 200, 200 };

194
      gtk_widget_get_preferred_size (widget, &requisition, NULL);
195 196 197 198 199 200 201 202 203 204
      if (requisition.width || requisition.height)
	{
	  /* non-empty window */
	  allocation.width = requisition.width;
	  allocation.height = requisition.height;
	}
      gtk_widget_size_allocate (widget, &allocation);
      
      _gtk_container_queue_resize (GTK_CONTAINER (widget));

205
      g_return_if_fail (!gtk_widget_get_realized (widget));
206 207
    }

208
  gtk_widget_set_realized (widget, TRUE);
209

210 211
  gtk_widget_get_allocation (widget, &allocation);

212
  attributes.window_type = GDK_WINDOW_CHILD;
Tor Lillqvist's avatar
Tor Lillqvist committed
213 214
  attributes.title = gtk_window_get_title (window);
  _gtk_window_get_wmclass (window, &attributes.wmclass_name, &attributes.wmclass_class);
215 216
  attributes.width = allocation.width;
  attributes.height = allocation.height;
217 218 219 220 221 222 223 224 225 226 227 228 229 230
  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.event_mask = gtk_widget_get_events (widget);
  attributes.event_mask |= (GDK_EXPOSURE_MASK |
			    GDK_KEY_PRESS_MASK |
			    GDK_KEY_RELEASE_MASK |
			    GDK_ENTER_NOTIFY_MASK |
			    GDK_LEAVE_NOTIFY_MASK |
			    GDK_STRUCTURE_MASK |
			    GDK_FOCUS_CHANGE_MASK);

231
  attributes_mask = GDK_WA_VISUAL;
Tor Lillqvist's avatar
Tor Lillqvist committed
232 233
  attributes_mask |= (gtk_window_get_title (window) ? GDK_WA_TITLE : 0);
  attributes_mask |= (attributes.wmclass_name ? GDK_WA_WMCLASS : 0);
234

235 236 237 238
  gdk_window = gdk_window_new (embed_widget->parent_window,
                               &attributes, attributes_mask);
  gtk_widget_set_window (widget, gdk_window);
  gdk_window_set_user_data (gdk_window, window);
239 240

  embed_widget->old_window_procedure = (gpointer)
241
    SetWindowLongPtrW(GDK_WINDOW_HWND (gdk_window),
242 243 244 245
		      GWLP_WNDPROC,
		      (LONG_PTR)gtk_win32_embed_widget_window_process);

  /* Enable tab to focus the widget */
246 247 248
  styles = GetWindowLongPtr(GDK_WINDOW_HWND (gdk_window), GWL_STYLE);
  SetWindowLongPtrW(GDK_WINDOW_HWND (gdk_window), GWL_STYLE, styles | WS_TABSTOP);

249 250
  gtk_style_context_set_background (gtk_widget_get_style_context (widget),
                                    gdk_window);
251 252 253 254 255
}

static void
gtk_win32_embed_widget_show (GtkWidget *widget)
{
256
  gtk_widget_set_visible (widget, TRUE);
257 258 259 260 261 262 263 264 265
  
  gtk_widget_realize (widget);
  gtk_container_check_resize (GTK_CONTAINER (widget));
  gtk_widget_map (widget);
}

static void
gtk_win32_embed_widget_hide (GtkWidget *widget)
{
266
  gtk_widget_set_visible (widget, FALSE);
267 268 269 270 271 272
  gtk_widget_unmap (widget);
}

static void
gtk_win32_embed_widget_map (GtkWidget *widget)
{
273 274 275
  GtkBin    *bin = GTK_BIN (widget);
  GtkWidget *child;

276
  gtk_widget_set_mapped (widget, TRUE);
277 278 279 280 281 282

  child = gtk_bin_get_child (bin);
  if (child &&
      gtk_widget_get_visible (child) &&
      !gtk_widget_get_mapped (child))
    gtk_widget_map (child);
283

284
  gdk_window_show (gtk_widget_get_window (widget));
285 286 287 288 289
}

static void
gtk_win32_embed_widget_unmap (GtkWidget *widget)
{
290
  gtk_widget_set_mapped (widget, FALSE);
291
  gdk_window_hide (gtk_widget_get_window (widget));
292 293 294 295 296 297
}

static void
gtk_win32_embed_widget_size_allocate (GtkWidget     *widget,
				      GtkAllocation *allocation)
{
298 299
  GtkBin    *bin = GTK_BIN (widget);
  GtkWidget *child;
300
  
301
  gtk_widget_set_allocation (widget, allocation);
302
  
303
  if (gtk_widget_get_realized (widget))
304
    gdk_window_move_resize (gtk_widget_get_window (widget),
305 306
			    allocation->x, allocation->y,
			    allocation->width, allocation->height);
307 308 309

  child = gtk_bin_get_child (bin);
  if (child && gtk_widget_get_visible (child))
310 311 312
    {
      GtkAllocation child_allocation;
      
313 314
      child_allocation.x = gtk_container_get_border_width (GTK_CONTAINER (widget));
      child_allocation.y = child_allocation.x;
315 316 317 318 319
      child_allocation.width =
	MAX (1, (gint)allocation->width - child_allocation.x * 2);
      child_allocation.height =
	MAX (1, (gint)allocation->height - child_allocation.y * 2);
      
320
      gtk_widget_size_allocate (child, &child_allocation);
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
    }
}

static void
gtk_win32_embed_widget_check_resize (GtkContainer *container)
{
  GTK_CONTAINER_CLASS (bin_class)->check_resize (container);
}

static gboolean
gtk_win32_embed_widget_focus (GtkWidget        *widget,
			      GtkDirectionType  direction)
{
  GtkBin *bin = GTK_BIN (widget);
  GtkWin32EmbedWidget *embed_widget = GTK_WIN32_EMBED_WIDGET (widget);
  GtkWindow *window = GTK_WINDOW (widget);
  GtkContainer *container = GTK_CONTAINER (widget);
338
  GtkWidget *old_focus_child = gtk_container_get_focus_child (container);
339
  GtkWidget *parent;
340
  GtkWidget *child;
341 342 343 344 345 346 347 348

  /* We override GtkWindow's behavior, since we don't want wrapping here.
   */
  if (old_focus_child)
    {
      if (gtk_widget_child_focus (old_focus_child, direction))
	return TRUE;

Tor Lillqvist's avatar
Tor Lillqvist committed
349
      if (gtk_window_get_focus (window))
350 351
	{
	  /* Wrapped off the end, clear the focus setting for the toplevel */
Tor Lillqvist's avatar
Tor Lillqvist committed
352
	  parent = gtk_widget_get_parent (gtk_window_get_focus (window));
353 354 355
	  while (parent)
	    {
	      gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL);
356
	      parent = gtk_widget_get_parent (GTK_WIDGET (parent));
357 358 359 360 361 362 363 364
	    }
	  
	  gtk_window_set_focus (GTK_WINDOW (container), NULL);
	}
    }
  else
    {
      /* Try to focus the first widget in the window */
365 366
      child = gtk_bin_get_child (bin);
      if (child && gtk_widget_child_focus (child, direction))
367 368 369
        return TRUE;
    }

370
  if (!gtk_container_get_focus_child (GTK_CONTAINER (window)))
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
    {
      int backwards = FALSE;

      if (direction == GTK_DIR_TAB_BACKWARD ||
	  direction == GTK_DIR_LEFT)
	backwards = TRUE;
      
      PostMessage(GDK_WINDOW_HWND (embed_widget->parent_window),
				   WM_NEXTDLGCTL,
				   backwards, 0);
    }

  return FALSE;
}

static void
gtk_win32_embed_widget_set_focus (GtkWindow *window,
				  GtkWidget *focus)
{
  GTK_WINDOW_CLASS (gtk_win32_embed_widget_parent_class)->set_focus (window, focus);

392
  gdk_window_focus (gtk_widget_get_window (GTK_WIDGET(window)), 0);
393
}