gtkwin32embedwidget.c 11.9 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


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)
{
84
  _gtk_widget_set_is_toplevel (GTK_WIDGET (embed_widget), TRUE);
85 86 87 88
  gtk_container_set_resize_mode (GTK_CONTAINER (embed_widget), GTK_RESIZE_QUEUE);
}

GtkWidget*
89
_gtk_win32_embed_widget_new (HWND parent)
90 91 92 93 94 95
{
  GtkWin32EmbedWidget *embed_widget;

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

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

120 121 122 123 124 125 126 127 128
     gtk_widget_queue_resize (widget);
   }
        
 return 0;
}

static void
gtk_win32_embed_widget_unrealize (GtkWidget *widget)
{
129
  GtkWin32EmbedWidget *embed_widget = GTK_WIN32_EMBED_WIDGET (widget);
130 131 132

  embed_widget->old_window_procedure = NULL;
  
133
  g_clear_object (&embed_widget->parent_window);
134

135
  GTK_WIDGET_CLASS (gtk_win32_embed_widget_parent_class)->unrealize (widget);
136 137 138 139 140 141 142 143 144
}

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;

145
  window = gdk_win32_window_lookup_for_display (gdk_display_get_default (), hwnd);
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
  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)
{
167 168
  GtkWindow *window = GTK_WINDOW (widget);
  GtkWin32EmbedWidget *embed_widget = GTK_WIN32_EMBED_WIDGET (widget);
169 170
  GtkAllocation allocation;
  GdkWindow *gdk_window;
171 172 173 174
  GdkWindowAttr attributes;
  gint attributes_mask;
  LONG_PTR styles;

175 176
  gtk_widget_get_allocation (widget, &allocation);

177
  /* ensure widget tree is properly size allocated */
178 179
  if (allocation.x == -1 && allocation.y == -1 &&
      allocation.width == 1 && allocation.height == 1)
180 181 182 183
    {
      GtkRequisition requisition;
      GtkAllocation allocation = { 0, 0, 200, 200 };

184
      gtk_widget_get_preferred_size (widget, &requisition, NULL);
185 186 187 188 189 190 191 192 193 194
      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));

195
      g_return_if_fail (!gtk_widget_get_realized (widget));
196 197
    }

198
  gtk_widget_set_realized (widget, TRUE);
199

200 201
  gtk_widget_get_allocation (widget, &allocation);

202
  attributes.window_type = GDK_WINDOW_CHILD;
LRN's avatar
LRN committed
203
  attributes.title = (gchar *) gtk_window_get_title (window);
Tor Lillqvist's avatar
Tor Lillqvist committed
204
  _gtk_window_get_wmclass (window, &attributes.wmclass_name, &attributes.wmclass_class);
205 206
  attributes.width = allocation.width;
  attributes.height = allocation.height;
207 208 209 210 211 212 213 214 215 216 217 218 219 220
  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);

221
  attributes_mask = GDK_WA_VISUAL;
LRN's avatar
LRN committed
222
  attributes_mask |= (attributes.title ? GDK_WA_TITLE : 0);
Tor Lillqvist's avatar
Tor Lillqvist committed
223
  attributes_mask |= (attributes.wmclass_name ? GDK_WA_WMCLASS : 0);
224

225 226 227
  gdk_window = gdk_window_new (embed_widget->parent_window,
                               &attributes, attributes_mask);
  gtk_widget_set_window (widget, gdk_window);
228
  gtk_widget_register_window (widget, gdk_window);
229 230

  embed_widget->old_window_procedure = (gpointer)
231
    SetWindowLongPtrW(GDK_WINDOW_HWND (gdk_window),
232 233 234 235
		      GWLP_WNDPROC,
		      (LONG_PTR)gtk_win32_embed_widget_window_process);

  /* Enable tab to focus the widget */
236 237 238
  styles = GetWindowLongPtr(GDK_WINDOW_HWND (gdk_window), GWL_STYLE);
  SetWindowLongPtrW(GDK_WINDOW_HWND (gdk_window), GWL_STYLE, styles | WS_TABSTOP);

239 240
  gtk_style_context_set_background (gtk_widget_get_style_context (widget),
                                    gdk_window);
241 242 243 244 245
}

static void
gtk_win32_embed_widget_show (GtkWidget *widget)
{
246
  _gtk_widget_set_visible_flag (widget, TRUE);
247 248 249 250 251 252 253 254 255
  
  gtk_widget_realize (widget);
  gtk_container_check_resize (GTK_CONTAINER (widget));
  gtk_widget_map (widget);
}

static void
gtk_win32_embed_widget_hide (GtkWidget *widget)
{
256
  _gtk_widget_set_visible_flag (widget, FALSE);
257 258 259 260 261 262
  gtk_widget_unmap (widget);
}

static void
gtk_win32_embed_widget_map (GtkWidget *widget)
{
263 264 265
  GtkBin    *bin = GTK_BIN (widget);
  GtkWidget *child;

266
  gtk_widget_set_mapped (widget, TRUE);
267 268 269 270 271 272

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

274
  gdk_window_show (gtk_widget_get_window (widget));
275 276 277 278 279
}

static void
gtk_win32_embed_widget_unmap (GtkWidget *widget)
{
280
  gtk_widget_set_mapped (widget, FALSE);
281
  gdk_window_hide (gtk_widget_get_window (widget));
282 283 284 285 286 287
}

static void
gtk_win32_embed_widget_size_allocate (GtkWidget     *widget,
				      GtkAllocation *allocation)
{
288 289
  GtkBin    *bin = GTK_BIN (widget);
  GtkWidget *child;
290
  
291
  gtk_widget_set_allocation (widget, allocation);
292
  
293
  if (gtk_widget_get_realized (widget))
294
    gdk_window_move_resize (gtk_widget_get_window (widget),
295 296
			    allocation->x, allocation->y,
			    allocation->width, allocation->height);
297 298 299

  child = gtk_bin_get_child (bin);
  if (child && gtk_widget_get_visible (child))
300 301 302
    {
      GtkAllocation child_allocation;
      
303 304
      child_allocation.x = gtk_container_get_border_width (GTK_CONTAINER (widget));
      child_allocation.y = child_allocation.x;
305 306 307 308 309
      child_allocation.width =
	MAX (1, (gint)allocation->width - child_allocation.x * 2);
      child_allocation.height =
	MAX (1, (gint)allocation->height - child_allocation.y * 2);
      
310
      gtk_widget_size_allocate (child, &child_allocation);
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327
    }
}

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);
328
  GtkWidget *old_focus_child = gtk_container_get_focus_child (container);
329
  GtkWidget *parent;
330
  GtkWidget *child;
331 332 333 334 335 336 337 338

  /* 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
339
      if (gtk_window_get_focus (window))
340 341
	{
	  /* Wrapped off the end, clear the focus setting for the toplevel */
Tor Lillqvist's avatar
Tor Lillqvist committed
342
	  parent = gtk_widget_get_parent (gtk_window_get_focus (window));
343 344 345
	  while (parent)
	    {
	      gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL);
346
	      parent = gtk_widget_get_parent (GTK_WIDGET (parent));
347 348 349 350 351 352 353 354
	    }
	  
	  gtk_window_set_focus (GTK_WINDOW (container), NULL);
	}
    }
  else
    {
      /* Try to focus the first widget in the window */
355 356
      child = gtk_bin_get_child (bin);
      if (child && gtk_widget_child_focus (child, direction))
357 358 359
        return TRUE;
    }

360
  if (!gtk_container_get_focus_child (GTK_CONTAINER (window)))
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
    {
      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);

382
  gdk_window_focus (gtk_widget_get_window (GTK_WIDGET(window)), 0);
383
}