gtkwin32embedwidget.c 12.1 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
 * 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
Javier Jardón's avatar
Javier Jardón committed
15
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.Free
16 17 18 19 20 21 22 23 24
 */

/*
 * 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/. 
 */

25
#include "config.h"
26 27 28

#include "gtkmain.h"
#include "gtkmarshalers.h"
Tor Lillqvist's avatar
Tor Lillqvist committed
29
#include "gtksizerequest.h"
30 31 32
#include "gtkwin32embedwidget.h"
#include "gtkintl.h"
#include "gtkprivate.h"
33
#include "gtkwindowprivate.h"
34 35
#include "gtkwidgetprivate.h"
#include "gtkcontainerprivate.h"
36 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


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

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

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

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

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

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

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

  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;
    }
143

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

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;

154
  window = gdk_win32_window_lookup_for_display (gdk_display_get_default (), hwnd);
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
  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)
{
176 177
  GtkWindow *window = GTK_WINDOW (widget);
  GtkWin32EmbedWidget *embed_widget = GTK_WIN32_EMBED_WIDGET (widget);
178 179
  GtkAllocation allocation;
  GdkWindow *gdk_window;
180 181 182 183
  GdkWindowAttr attributes;
  gint attributes_mask;
  LONG_PTR styles;

184 185
  gtk_widget_get_allocation (widget, &allocation);

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

193
      gtk_widget_get_preferred_size (widget, &requisition, NULL);
194 195 196 197 198 199 200 201 202 203
      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));

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

207
  gtk_widget_set_realized (widget, TRUE);
208

209 210
  gtk_widget_get_allocation (widget, &allocation);

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

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

234 235 236 237
  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);
238 239

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

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

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

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

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

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

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

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

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

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

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

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

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);
337
  GtkWidget *old_focus_child = gtk_container_get_focus_child (container);
338
  GtkWidget *parent;
339
  GtkWidget *child;
340 341 342 343 344 345 346 347

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

369
  if (!gtk_container_get_focus_child (GTK_CONTAINER (window)))
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
    {
      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);

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