gtkwin32embedwidget.c 12 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
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
86
  gtk_container_set_resize_mode (GTK_CONTAINER (embed_widget), GTK_RESIZE_QUEUE);
87
G_GNUC_END_IGNORE_DEPRECATIONS;
88 89 90
}

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

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

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

122 123 124 125 126 127 128 129 130
     gtk_widget_queue_resize (widget);
   }
        
 return 0;
}

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

  embed_widget->old_window_procedure = NULL;
  
135
  g_clear_object (&embed_widget->parent_window);
136

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

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;

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

177 178
  gtk_widget_get_allocation (widget, &allocation);

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

186
      gtk_widget_get_preferred_size (widget, &requisition, NULL);
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);
      
195
      gtk_widget_queue_resize (widget);
196

197
      g_return_if_fail (!gtk_widget_get_realized (widget));
198 199
    }

200
  gtk_widget_set_realized (widget, TRUE);
201

202 203
  gtk_widget_get_allocation (widget, &allocation);

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

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

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

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

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

241
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
242 243
  gtk_style_context_set_background (gtk_widget_get_style_context (widget),
                                    gdk_window);
244
G_GNUC_END_IGNORE_DEPRECATIONS;
245 246 247 248 249
}

static void
gtk_win32_embed_widget_show (GtkWidget *widget)
{
250
  _gtk_widget_set_visible_flag (widget, TRUE);
251 252 253 254 255 256 257 258 259
  
  gtk_widget_realize (widget);
  gtk_container_check_resize (GTK_CONTAINER (widget));
  gtk_widget_map (widget);
}

static void
gtk_win32_embed_widget_hide (GtkWidget *widget)
{
260
  _gtk_widget_set_visible_flag (widget, FALSE);
261 262 263 264 265 266
  gtk_widget_unmap (widget);
}

static void
gtk_win32_embed_widget_map (GtkWidget *widget)
{
267 268 269
  GtkBin    *bin = GTK_BIN (widget);
  GtkWidget *child;

270
  gtk_widget_set_mapped (widget, TRUE);
271 272 273 274 275 276

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

278
  gdk_window_show (gtk_widget_get_window (widget));
279 280 281 282 283
}

static void
gtk_win32_embed_widget_unmap (GtkWidget *widget)
{
284
  gtk_widget_set_mapped (widget, FALSE);
285
  gdk_window_hide (gtk_widget_get_window (widget));
286 287 288 289 290 291
}

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

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

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);
332
  GtkWidget *old_focus_child = gtk_container_get_focus_child (container);
333
  GtkWidget *parent;
334
  GtkWidget *child;
335 336 337 338 339 340 341 342

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

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

386
  gdk_window_focus (gtk_widget_get_window (GTK_WIDGET(window)), 0);
387
}