blob: 6146fe6d9e2da72c56a8148d1b53c16f06f25f59 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
Bram Moolenaara0a83be2005-01-04 21:26:43 +000011 * (C) 1998,1999 by Marcin Dalecki <martin@dalecki.de>
Bram Moolenaar071d4272004-06-13 20:20:40 +000012 *
13 * Support for GTK+ 2 was added by:
14 *
15 * (C) 2002,2003 Jason Hildebrand <jason@peaceworks.ca>
16 * Daniel Elstner <daniel.elstner@gmx.net>
17 *
Bram Moolenaar933eb392007-05-10 17:52:45 +000018 * This is a special purpose container widget, which manages arbitrary
19 * children at arbitrary positions width arbitrary sizes. This finally puts
20 * an end on our resize problems with which we where struggling for such a
21 * long time.
Bram Moolenaar98921892016-02-23 17:14:37 +010022 *
23 * Support for GTK+ 3 was added by:
24 *
25 * 2016 Kazunobu Kuriyama <kazunobu.kuriyama@gmail.com>
Bram Moolenaar071d4272004-06-13 20:20:40 +000026 */
27
28#include "vim.h"
29#include <gtk/gtk.h> /* without this it compiles, but gives errors at
30 runtime! */
31#include "gui_gtk_f.h"
Bram Moolenaar98921892016-02-23 17:14:37 +010032#if !GTK_CHECK_VERSION(3,0,0)
33# include <gtk/gtksignal.h>
34#endif
Bram Moolenaar4f974752019-02-17 17:44:42 +010035#ifdef MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +000036# include <gdk/gdkwin32.h>
37#else
38# include <gdk/gdkx.h>
39#endif
40
41typedef struct _GtkFormChild GtkFormChild;
42
43struct _GtkFormChild
44{
45 GtkWidget *widget;
46 GdkWindow *window;
47 gint x; /* relative subwidget x position */
48 gint y; /* relative subwidget y position */
49 gint mapped;
50};
51
52
53static void gtk_form_class_init(GtkFormClass *klass);
54static void gtk_form_init(GtkForm *form);
55
56static void gtk_form_realize(GtkWidget *widget);
57static void gtk_form_unrealize(GtkWidget *widget);
58static void gtk_form_map(GtkWidget *widget);
59static void gtk_form_size_request(GtkWidget *widget,
60 GtkRequisition *requisition);
Bram Moolenaar98921892016-02-23 17:14:37 +010061#if GTK_CHECK_VERSION(3,0,0)
62static void gtk_form_get_preferred_width(GtkWidget *widget,
63 gint *minimal_width,
64 gint *natural_width);
65static void gtk_form_get_preferred_height(GtkWidget *widget,
66 gint *minimal_height,
67 gint *natural_height);
68#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000069static void gtk_form_size_allocate(GtkWidget *widget,
70 GtkAllocation *allocation);
Bram Moolenaar98921892016-02-23 17:14:37 +010071#if GTK_CHECK_VERSION(3,0,0)
72static gboolean gtk_form_draw(GtkWidget *widget,
73 cairo_t *cr);
74#else
Bram Moolenaar071d4272004-06-13 20:20:40 +000075static gint gtk_form_expose(GtkWidget *widget,
76 GdkEventExpose *event);
Bram Moolenaar98921892016-02-23 17:14:37 +010077#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000078
79static void gtk_form_remove(GtkContainer *container,
80 GtkWidget *widget);
81static void gtk_form_forall(GtkContainer *container,
82 gboolean include_internals,
83 GtkCallback callback,
84 gpointer callback_data);
85
86static void gtk_form_attach_child_window(GtkForm *form,
87 GtkFormChild *child);
88static void gtk_form_realize_child(GtkForm *form,
89 GtkFormChild *child);
90static void gtk_form_position_child(GtkForm *form,
91 GtkFormChild *child,
92 gboolean force_allocate);
93static void gtk_form_position_children(GtkForm *form);
94
Bram Moolenaar071d4272004-06-13 20:20:40 +000095static void gtk_form_send_configure(GtkForm *form);
96
97static void gtk_form_child_map(GtkWidget *widget, gpointer user_data);
98static void gtk_form_child_unmap(GtkWidget *widget, gpointer user_data);
99
Bram Moolenaar98921892016-02-23 17:14:37 +0100100#if !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000101static GtkWidgetClass *parent_class = NULL;
Bram Moolenaar98921892016-02-23 17:14:37 +0100102#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000103
104/* Public interface
105 */
106
107 GtkWidget *
108gtk_form_new(void)
109{
110 GtkForm *form;
111
Bram Moolenaar98921892016-02-23 17:14:37 +0100112#if GTK_CHECK_VERSION(3,0,0)
113 form = g_object_new(GTK_TYPE_FORM, NULL);
114#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000115 form = gtk_type_new(gtk_form_get_type());
Bram Moolenaar98921892016-02-23 17:14:37 +0100116#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000117
118 return GTK_WIDGET(form);
119}
120
121 void
122gtk_form_put(GtkForm *form,
123 GtkWidget *child_widget,
124 gint x,
125 gint y)
126{
127 GtkFormChild *child;
128
129 g_return_if_fail(GTK_IS_FORM(form));
130
Bram Moolenaar9d75c832005-01-25 21:57:23 +0000131 /* LINTED: avoid warning: conversion to 'unsigned long' */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000132 child = g_new(GtkFormChild, 1);
133
134 child->widget = child_widget;
135 child->window = NULL;
136 child->x = x;
137 child->y = y;
Bram Moolenaar98921892016-02-23 17:14:37 +0100138#if GTK_CHECK_VERSION(3,0,0)
139 gtk_widget_set_size_request(child->widget, -1, -1);
140#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000141 child->widget->requisition.width = 0;
142 child->widget->requisition.height = 0;
Bram Moolenaar98921892016-02-23 17:14:37 +0100143#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000144 child->mapped = FALSE;
145
146 form->children = g_list_append(form->children, child);
147
148 /* child->window must be created and attached to the widget _before_
149 * it has been realized, or else things will break with GTK2. Note
150 * that gtk_widget_set_parent() realizes the widget if it's visible
151 * and its parent is mapped.
152 */
Bram Moolenaar98921892016-02-23 17:14:37 +0100153 if (gtk_widget_get_realized(GTK_WIDGET(form)))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000154 gtk_form_attach_child_window(form, child);
155
156 gtk_widget_set_parent(child_widget, GTK_WIDGET(form));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000157
Bram Moolenaar98921892016-02-23 17:14:37 +0100158 if (gtk_widget_get_realized(GTK_WIDGET(form))
159 && !gtk_widget_get_realized(child_widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000160 gtk_form_realize_child(form, child);
161
162 gtk_form_position_child(form, child, TRUE);
163}
164
165 void
166gtk_form_move(GtkForm *form,
167 GtkWidget *child_widget,
168 gint x,
169 gint y)
170{
171 GList *tmp_list;
172 GtkFormChild *child;
173
174 g_return_if_fail(GTK_IS_FORM(form));
175
176 for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next)
177 {
178 child = tmp_list->data;
179 if (child->widget == child_widget)
180 {
181 child->x = x;
182 child->y = y;
183
184 gtk_form_position_child(form, child, TRUE);
185 return;
186 }
187 }
188}
189
Bram Moolenaar071d4272004-06-13 20:20:40 +0000190 void
191gtk_form_freeze(GtkForm *form)
192{
193 g_return_if_fail(GTK_IS_FORM(form));
194
195 ++form->freeze_count;
196}
197
198 void
199gtk_form_thaw(GtkForm *form)
200{
201 g_return_if_fail(GTK_IS_FORM(form));
202
203 if (form->freeze_count)
204 {
205 if (!(--form->freeze_count))
206 {
207 gtk_form_position_children(form);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000208 gtk_widget_queue_draw(GTK_WIDGET(form));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000209 }
210 }
211}
212
213/* Basic Object handling procedures
214 */
Bram Moolenaar98921892016-02-23 17:14:37 +0100215#if GTK_CHECK_VERSION(3,0,0)
216G_DEFINE_TYPE(GtkForm, gtk_form, GTK_TYPE_CONTAINER)
217#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000218 GtkType
219gtk_form_get_type(void)
220{
221 static GtkType form_type = 0;
222
223 if (!form_type)
224 {
Bram Moolenaarb85cb212009-05-17 14:24:23 +0000225 GtkTypeInfo form_info;
226
Bram Moolenaara9d45512009-05-17 21:25:42 +0000227 vim_memset(&form_info, 0, sizeof(form_info));
Bram Moolenaarb85cb212009-05-17 14:24:23 +0000228 form_info.type_name = "GtkForm";
229 form_info.object_size = sizeof(GtkForm);
230 form_info.class_size = sizeof(GtkFormClass);
231 form_info.class_init_func = (GtkClassInitFunc)gtk_form_class_init;
232 form_info.object_init_func = (GtkObjectInitFunc)gtk_form_init;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000233
234 form_type = gtk_type_unique(GTK_TYPE_CONTAINER, &form_info);
235 }
236 return form_type;
237}
Bram Moolenaar98921892016-02-23 17:14:37 +0100238#endif /* !GTK_CHECK_VERSION(3,0,0) */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000239
240 static void
241gtk_form_class_init(GtkFormClass *klass)
242{
243 GtkWidgetClass *widget_class;
244 GtkContainerClass *container_class;
245
246 widget_class = (GtkWidgetClass *) klass;
247 container_class = (GtkContainerClass *) klass;
248
Bram Moolenaar98921892016-02-23 17:14:37 +0100249#if !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000250 parent_class = gtk_type_class(gtk_container_get_type());
Bram Moolenaar98921892016-02-23 17:14:37 +0100251#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000252
253 widget_class->realize = gtk_form_realize;
254 widget_class->unrealize = gtk_form_unrealize;
255 widget_class->map = gtk_form_map;
Bram Moolenaar98921892016-02-23 17:14:37 +0100256#if GTK_CHECK_VERSION(3,0,0)
257 widget_class->get_preferred_width = gtk_form_get_preferred_width;
258 widget_class->get_preferred_height = gtk_form_get_preferred_height;
259#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000260 widget_class->size_request = gtk_form_size_request;
Bram Moolenaar98921892016-02-23 17:14:37 +0100261#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000262 widget_class->size_allocate = gtk_form_size_allocate;
Bram Moolenaar98921892016-02-23 17:14:37 +0100263#if GTK_CHECK_VERSION(3,0,0)
264 widget_class->draw = gtk_form_draw;
265#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000266 widget_class->expose_event = gtk_form_expose;
Bram Moolenaar98921892016-02-23 17:14:37 +0100267#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000268
269 container_class->remove = gtk_form_remove;
270 container_class->forall = gtk_form_forall;
271}
272
273 static void
274gtk_form_init(GtkForm *form)
275{
Bram Moolenaar98921892016-02-23 17:14:37 +0100276#if GTK_CHECK_VERSION(3,0,0)
277 gtk_widget_set_has_window(GTK_WIDGET(form), TRUE);
278#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000279 form->children = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000280 form->bin_window = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000281 form->freeze_count = 0;
282}
283
284/*
285 * Widget methods
286 */
287
288 static void
289gtk_form_realize(GtkWidget *widget)
290{
291 GList *tmp_list;
292 GtkForm *form;
293 GdkWindowAttr attributes;
294 gint attributes_mask;
Bram Moolenaar664323e2018-09-18 22:30:07 +0200295 GtkAllocation allocation;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000296
297 g_return_if_fail(GTK_IS_FORM(widget));
298
299 form = GTK_FORM(widget);
Bram Moolenaar98921892016-02-23 17:14:37 +0100300 gtk_widget_set_realized(widget, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000301
Bram Moolenaar664323e2018-09-18 22:30:07 +0200302 gtk_widget_get_allocation(widget, &allocation);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000303 attributes.window_type = GDK_WINDOW_CHILD;
Bram Moolenaar664323e2018-09-18 22:30:07 +0200304 attributes.x = allocation.x;
305 attributes.y = allocation.y;
306 attributes.width = allocation.width;
307 attributes.height = allocation.height;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000308 attributes.wclass = GDK_INPUT_OUTPUT;
309 attributes.visual = gtk_widget_get_visual(widget);
Bram Moolenaar98921892016-02-23 17:14:37 +0100310#if GTK_CHECK_VERSION(3,0,0)
311 attributes.event_mask = GDK_EXPOSURE_MASK;
312#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000313 attributes.colormap = gtk_widget_get_colormap(widget);
314 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
Bram Moolenaar98921892016-02-23 17:14:37 +0100315#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000316
Bram Moolenaar98921892016-02-23 17:14:37 +0100317#if GTK_CHECK_VERSION(3,0,0)
318 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
319#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000320 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
Bram Moolenaar98921892016-02-23 17:14:37 +0100321#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000322
Bram Moolenaar98921892016-02-23 17:14:37 +0100323 gtk_widget_set_window(widget,
324 gdk_window_new(gtk_widget_get_parent_window(widget),
325 &attributes, attributes_mask));
Bram Moolenaar25328e32018-09-11 21:30:09 +0200326 gdk_window_set_user_data(gtk_widget_get_window(widget), widget);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000327
328 attributes.x = 0;
329 attributes.y = 0;
330 attributes.event_mask = gtk_widget_get_events(widget);
331
Bram Moolenaar98921892016-02-23 17:14:37 +0100332 form->bin_window = gdk_window_new(gtk_widget_get_window(widget),
333 &attributes, attributes_mask);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000334 gdk_window_set_user_data(form->bin_window, widget);
335
Bram Moolenaar98921892016-02-23 17:14:37 +0100336#if GTK_CHECK_VERSION(3,0,0)
337 {
338 GtkStyleContext * const sctx = gtk_widget_get_style_context(widget);
339
340 gtk_style_context_add_class(sctx, "gtk-form");
341 gtk_style_context_set_state(sctx, GTK_STATE_FLAG_NORMAL);
342# if !GTK_CHECK_VERSION(3,18,0)
343 gtk_style_context_set_background(sctx, gtk_widget_get_window(widget));
344 gtk_style_context_set_background(sctx, form->bin_window);
345# endif
346 }
347#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000348 widget->style = gtk_style_attach(widget->style, widget->window);
349 gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);
350 gtk_style_set_background(widget->style, form->bin_window, GTK_STATE_NORMAL);
Bram Moolenaar98921892016-02-23 17:14:37 +0100351#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000352
Bram Moolenaar071d4272004-06-13 20:20:40 +0000353 for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next)
354 {
355 GtkFormChild *child = tmp_list->data;
356
357 gtk_form_attach_child_window(form, child);
358
Bram Moolenaar98921892016-02-23 17:14:37 +0100359 if (gtk_widget_get_visible(child->widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000360 gtk_form_realize_child(form, child);
361 }
362}
363
364
365/* After reading the documentation at
366 * http://developer.gnome.org/doc/API/2.0/gtk/gtk-changes-2-0.html
367 * I think it should be possible to remove this function when compiling
368 * against gtk-2.0. It doesn't seem to cause problems, though.
369 *
370 * Well, I reckon at least the gdk_window_show(form->bin_window)
371 * is necessary. GtkForm is anything but a usual container widget.
372 */
373 static void
374gtk_form_map(GtkWidget *widget)
375{
376 GList *tmp_list;
377 GtkForm *form;
378
379 g_return_if_fail(GTK_IS_FORM(widget));
380
381 form = GTK_FORM(widget);
382
Bram Moolenaar98921892016-02-23 17:14:37 +0100383 gtk_widget_set_mapped(widget, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000384
Bram Moolenaar98921892016-02-23 17:14:37 +0100385 gdk_window_show(gtk_widget_get_window(widget));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000386 gdk_window_show(form->bin_window);
387
388 for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next)
389 {
390 GtkFormChild *child = tmp_list->data;
391
Bram Moolenaar98921892016-02-23 17:14:37 +0100392 if (gtk_widget_get_visible(child->widget)
393 && !gtk_widget_get_mapped(child->widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000394 gtk_widget_map(child->widget);
395 }
396}
397
398 static void
399gtk_form_unrealize(GtkWidget *widget)
400{
401 GList *tmp_list;
402 GtkForm *form;
403
404 g_return_if_fail(GTK_IS_FORM(widget));
405
406 form = GTK_FORM(widget);
407
408 tmp_list = form->children;
409
410 gdk_window_set_user_data(form->bin_window, NULL);
411 gdk_window_destroy(form->bin_window);
412 form->bin_window = NULL;
413
414 while (tmp_list)
415 {
416 GtkFormChild *child = tmp_list->data;
417
418 if (child->window != NULL)
419 {
Bram Moolenaar98921892016-02-23 17:14:37 +0100420 g_signal_handlers_disconnect_by_func(G_OBJECT(child->widget),
Bram Moolenaard47d8372016-09-09 22:13:24 +0200421 FUNC2GENERIC(gtk_form_child_map),
Bram Moolenaar98921892016-02-23 17:14:37 +0100422 child);
423 g_signal_handlers_disconnect_by_func(G_OBJECT(child->widget),
Bram Moolenaard47d8372016-09-09 22:13:24 +0200424 FUNC2GENERIC(gtk_form_child_unmap),
Bram Moolenaar98921892016-02-23 17:14:37 +0100425 child);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000426
427 gdk_window_set_user_data(child->window, NULL);
428 gdk_window_destroy(child->window);
429
430 child->window = NULL;
431 }
432
433 tmp_list = tmp_list->next;
434 }
435
Bram Moolenaar98921892016-02-23 17:14:37 +0100436#if GTK_CHECK_VERSION(3,0,0)
437 if (GTK_WIDGET_CLASS (gtk_form_parent_class)->unrealize)
438 (* GTK_WIDGET_CLASS (gtk_form_parent_class)->unrealize) (widget);
439#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000440 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
441 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
Bram Moolenaar98921892016-02-23 17:14:37 +0100442#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000443}
444
Bram Moolenaar071d4272004-06-13 20:20:40 +0000445 static void
446gtk_form_size_request(GtkWidget *widget, GtkRequisition *requisition)
447{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000448 g_return_if_fail(GTK_IS_FORM(widget));
Bram Moolenaar99a6e8d2017-03-29 18:07:40 +0200449 g_return_if_fail(requisition != NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000450
Bram Moolenaar98921892016-02-23 17:14:37 +0100451 requisition->width = 1;
452 requisition->height = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000453}
454
Bram Moolenaar98921892016-02-23 17:14:37 +0100455#if GTK_CHECK_VERSION(3,0,0)
456 static void
457gtk_form_get_preferred_width(GtkWidget *widget,
458 gint *minimal_width,
459 gint *natural_width)
460{
461 GtkRequisition requisition;
462
463 gtk_form_size_request(widget, &requisition);
464
465 *minimal_width = requisition.width;
466 *natural_width = requisition.width;
467}
468
469 static void
470gtk_form_get_preferred_height(GtkWidget *widget,
471 gint *minimal_height,
472 gint *natural_height)
473{
474 GtkRequisition requisition;
475
476 gtk_form_size_request(widget, &requisition);
477
478 *minimal_height = requisition.height;
479 *natural_height = requisition.height;
480}
481#endif /* GTK_CHECK_VERSION(3,0,0) */
482
Bram Moolenaar071d4272004-06-13 20:20:40 +0000483 static void
484gtk_form_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
485{
486 GList *tmp_list;
487 GtkForm *form;
488 gboolean need_reposition;
Bram Moolenaar98921892016-02-23 17:14:37 +0100489 GtkAllocation cur_alloc;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000490
491 g_return_if_fail(GTK_IS_FORM(widget));
492
Bram Moolenaar98921892016-02-23 17:14:37 +0100493 gtk_widget_get_allocation(widget, &cur_alloc);
494
495 if (cur_alloc.x == allocation->x
496 && cur_alloc.y == allocation->y
497 && cur_alloc.width == allocation->width
498 && cur_alloc.height == allocation->height)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000499 return;
500
Bram Moolenaar98921892016-02-23 17:14:37 +0100501 need_reposition = cur_alloc.width != allocation->width
502 || cur_alloc.height != allocation->height;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000503 form = GTK_FORM(widget);
504
505 if (need_reposition)
506 {
507 tmp_list = form->children;
508
509 while (tmp_list)
510 {
511 GtkFormChild *child = tmp_list->data;
512 gtk_form_position_child(form, child, TRUE);
513
514 tmp_list = tmp_list->next;
515 }
516 }
517
Bram Moolenaar98921892016-02-23 17:14:37 +0100518 if (gtk_widget_get_realized(widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000519 {
Bram Moolenaar98921892016-02-23 17:14:37 +0100520 gdk_window_move_resize(gtk_widget_get_window(widget),
521 allocation->x, allocation->y,
522 allocation->width, allocation->height);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000523 gdk_window_move_resize(GTK_FORM(widget)->bin_window,
524 0, 0,
525 allocation->width, allocation->height);
526 }
Bram Moolenaar98921892016-02-23 17:14:37 +0100527 gtk_widget_set_allocation(widget, allocation);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000528 if (need_reposition)
529 gtk_form_send_configure(form);
530}
531
Bram Moolenaar98921892016-02-23 17:14:37 +0100532#if GTK_CHECK_VERSION(3,0,0)
533 static void
534gtk_form_render_background(GtkWidget *widget, cairo_t *cr)
535{
536 gtk_render_background(gtk_widget_get_style_context(widget), cr,
537 0, 0,
538 gtk_widget_get_allocated_width(widget),
539 gtk_widget_get_allocated_height(widget));
540}
541
542 static gboolean
543gtk_form_draw(GtkWidget *widget, cairo_t *cr)
544{
545 GList *tmp_list = NULL;
546 GtkForm *form = NULL;
547
548 g_return_val_if_fail(GTK_IS_FORM(widget), FALSE);
549
550 gtk_form_render_background(widget, cr);
551
552 form = GTK_FORM(widget);
553 for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next)
554 {
555 GtkFormChild * const formchild = tmp_list->data;
556
557 if (!gtk_widget_get_has_window(formchild->widget) &&
558 gtk_cairo_should_draw_window(cr, formchild->window))
559 {
560 /* To get gtk_widget_draw() to work, it is required to call
561 * gtk_widget_size_allocate() in advance with a well-posed
562 * allocation for a given child widget in order to set a
563 * certain private GtkWidget variable, called
Bram Moolenaarbdace832019-03-02 10:13:42 +0100564 * widget->priv->alloc_need, to the proper value; otherwise,
Bram Moolenaar98921892016-02-23 17:14:37 +0100565 * gtk_widget_draw() fails and the relevant scrollbar won't
566 * appear on the screen.
567 *
568 * Calling gtk_form_position_child() like this is one of ways
569 * to make sure of that. */
570 gtk_form_position_child(form, formchild, TRUE);
571
572 gtk_form_render_background(formchild->widget, cr);
573 }
574 }
575
576 return GTK_WIDGET_CLASS(gtk_form_parent_class)->draw(widget, cr);
577}
578#else /* !GTK_CHECK_VERSION(3,0,0) */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000579 static gint
580gtk_form_expose(GtkWidget *widget, GdkEventExpose *event)
581{
582 GList *tmp_list;
583 GtkForm *form;
584
585 g_return_val_if_fail(GTK_IS_FORM(widget), FALSE);
586
587 form = GTK_FORM(widget);
588
589 if (event->window == form->bin_window)
590 return FALSE;
591
592 for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next)
Bram Moolenaar99a6e8d2017-03-29 18:07:40 +0200593 gtk_container_propagate_expose(GTK_CONTAINER(widget),
594 GTK_WIDGET(((GtkFormChild *)tmp_list->data)->widget),
595 event);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000596
597 return FALSE;
598}
Bram Moolenaar98921892016-02-23 17:14:37 +0100599#endif /* !GTK_CHECK_VERSION(3,0,0) */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000600
601/* Container method
602 */
603 static void
604gtk_form_remove(GtkContainer *container, GtkWidget *widget)
605{
606 GList *tmp_list;
607 GtkForm *form;
608 GtkFormChild *child = NULL; /* init for gcc */
609
610 g_return_if_fail(GTK_IS_FORM(container));
611
612 form = GTK_FORM(container);
613
614 tmp_list = form->children;
615 while (tmp_list)
616 {
617 child = tmp_list->data;
618 if (child->widget == widget)
619 break;
620 tmp_list = tmp_list->next;
621 }
622
623 if (tmp_list)
624 {
Bram Moolenaar98921892016-02-23 17:14:37 +0100625#if GTK_CHECK_VERSION(3,0,0)
626 const gboolean was_visible = gtk_widget_get_visible(widget);
627#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000628 if (child->window)
629 {
Bram Moolenaar98921892016-02-23 17:14:37 +0100630 g_signal_handlers_disconnect_by_func(G_OBJECT(child->widget),
Bram Moolenaard47d8372016-09-09 22:13:24 +0200631 FUNC2GENERIC(&gtk_form_child_map), child);
Bram Moolenaar98921892016-02-23 17:14:37 +0100632 g_signal_handlers_disconnect_by_func(G_OBJECT(child->widget),
Bram Moolenaard47d8372016-09-09 22:13:24 +0200633 FUNC2GENERIC(&gtk_form_child_unmap), child);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000634
635 /* FIXME: This will cause problems for reparenting NO_WINDOW
636 * widgets out of a GtkForm
637 */
638 gdk_window_set_user_data(child->window, NULL);
639 gdk_window_destroy(child->window);
640 }
641 gtk_widget_unparent(widget);
Bram Moolenaar98921892016-02-23 17:14:37 +0100642#if GTK_CHECK_VERSION(3,0,0)
643 if (was_visible)
644 gtk_widget_queue_resize(GTK_WIDGET(container));
645#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000646 form->children = g_list_remove_link(form->children, tmp_list);
647 g_list_free_1(tmp_list);
648 g_free(child);
649 }
650}
651
Bram Moolenaar071d4272004-06-13 20:20:40 +0000652 static void
653gtk_form_forall(GtkContainer *container,
Bram Moolenaarb85cb212009-05-17 14:24:23 +0000654 gboolean include_internals UNUSED,
Bram Moolenaar071d4272004-06-13 20:20:40 +0000655 GtkCallback callback,
656 gpointer callback_data)
657{
658 GtkForm *form;
659 GtkFormChild *child;
660 GList *tmp_list;
661
662 g_return_if_fail(GTK_IS_FORM(container));
663 g_return_if_fail(callback != NULL);
664
665 form = GTK_FORM(container);
666
667 tmp_list = form->children;
668 while (tmp_list)
669 {
670 child = tmp_list->data;
671 tmp_list = tmp_list->next;
672
673 (*callback) (child->widget, callback_data);
674 }
675}
676
677/* Operations on children
678 */
679
680 static void
681gtk_form_attach_child_window(GtkForm *form, GtkFormChild *child)
682{
683 if (child->window != NULL)
684 return; /* been there, done that */
685
Bram Moolenaar98921892016-02-23 17:14:37 +0100686 if (!gtk_widget_get_has_window(child->widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000687 {
688 GtkWidget *widget;
689 GdkWindowAttr attributes;
690 gint attributes_mask;
Bram Moolenaar664323e2018-09-18 22:30:07 +0200691 GtkRequisition requisition;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000692
693 widget = GTK_WIDGET(form);
694
Bram Moolenaar664323e2018-09-18 22:30:07 +0200695#if GTK_CHECK_VERSION(3,0,0)
696 gtk_widget_get_preferred_size(child->widget, &requisition, NULL);
697#else
698 requisition = child->widget->requisition;
699#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000700 attributes.window_type = GDK_WINDOW_CHILD;
701 attributes.x = child->x;
702 attributes.y = child->y;
Bram Moolenaar664323e2018-09-18 22:30:07 +0200703 attributes.width = requisition.width;
704 attributes.height = requisition.height;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000705 attributes.wclass = GDK_INPUT_OUTPUT;
706 attributes.visual = gtk_widget_get_visual(widget);
Bram Moolenaar98921892016-02-23 17:14:37 +0100707#if !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000708 attributes.colormap = gtk_widget_get_colormap(widget);
Bram Moolenaar98921892016-02-23 17:14:37 +0100709#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000710 attributes.event_mask = GDK_EXPOSURE_MASK;
711
Bram Moolenaar98921892016-02-23 17:14:37 +0100712#if GTK_CHECK_VERSION(3,0,0)
713 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
714#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000715 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
Bram Moolenaar98921892016-02-23 17:14:37 +0100716#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000717 child->window = gdk_window_new(form->bin_window,
718 &attributes, attributes_mask);
719 gdk_window_set_user_data(child->window, widget);
720
Bram Moolenaar98921892016-02-23 17:14:37 +0100721#if GTK_CHECK_VERSION(3,0,0)
722 {
723 GtkStyleContext * const sctx = gtk_widget_get_style_context(widget);
724
725 gtk_style_context_set_state(sctx, GTK_STATE_FLAG_NORMAL);
726# if !GTK_CHECK_VERSION(3,18,0)
727 gtk_style_context_set_background(sctx, child->window);
728# endif
729 }
730#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000731 gtk_style_set_background(widget->style,
732 child->window,
733 GTK_STATE_NORMAL);
Bram Moolenaar98921892016-02-23 17:14:37 +0100734#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000735
736 gtk_widget_set_parent_window(child->widget, child->window);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000737 /*
738 * Install signal handlers to map/unmap child->window
739 * alongside with the actual widget.
740 */
Bram Moolenaar98921892016-02-23 17:14:37 +0100741 g_signal_connect(G_OBJECT(child->widget), "map",
742 G_CALLBACK(&gtk_form_child_map), child);
743 g_signal_connect(G_OBJECT(child->widget), "unmap",
744 G_CALLBACK(&gtk_form_child_unmap), child);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000745 }
Bram Moolenaar98921892016-02-23 17:14:37 +0100746 else if (!gtk_widget_get_realized(child->widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000747 {
748 gtk_widget_set_parent_window(child->widget, form->bin_window);
749 }
750}
751
752 static void
753gtk_form_realize_child(GtkForm *form, GtkFormChild *child)
754{
755 gtk_form_attach_child_window(form, child);
756 gtk_widget_realize(child->widget);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000757}
758
759 static void
760gtk_form_position_child(GtkForm *form, GtkFormChild *child,
761 gboolean force_allocate)
762{
763 gint x;
764 gint y;
765
766 x = child->x;
767 y = child->y;
768
769 if ((x >= G_MINSHORT) && (x <= G_MAXSHORT) &&
770 (y >= G_MINSHORT) && (y <= G_MAXSHORT))
771 {
772 if (!child->mapped)
773 {
Bram Moolenaar98921892016-02-23 17:14:37 +0100774 if (gtk_widget_get_mapped(GTK_WIDGET(form))
775 && gtk_widget_get_visible(child->widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000776 {
Bram Moolenaar98921892016-02-23 17:14:37 +0100777 if (!gtk_widget_get_mapped(child->widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000778 gtk_widget_map(child->widget);
779
780 child->mapped = TRUE;
781 force_allocate = TRUE;
782 }
783 }
784
785 if (force_allocate)
786 {
787 GtkAllocation allocation;
Bram Moolenaar98921892016-02-23 17:14:37 +0100788 GtkRequisition requisition;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000789
Bram Moolenaar664323e2018-09-18 22:30:07 +0200790#if GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar98921892016-02-23 17:14:37 +0100791 gtk_widget_get_preferred_size(child->widget, &requisition, NULL);
Bram Moolenaar664323e2018-09-18 22:30:07 +0200792#else
793 requisition = child->widget->requisition;
Bram Moolenaar98921892016-02-23 17:14:37 +0100794#endif
795
Bram Moolenaar98921892016-02-23 17:14:37 +0100796 if (!gtk_widget_get_has_window(child->widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000797 {
798 if (child->window)
799 {
Bram Moolenaar98921892016-02-23 17:14:37 +0100800 gdk_window_move_resize(child->window,
801 x, y,
802 requisition.width,
803 requisition.height);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000804 }
805
806 allocation.x = 0;
807 allocation.y = 0;
808 }
809 else
810 {
811 allocation.x = x;
812 allocation.y = y;
813 }
814
Bram Moolenaar98921892016-02-23 17:14:37 +0100815 allocation.width = requisition.width;
816 allocation.height = requisition.height;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000817
818 gtk_widget_size_allocate(child->widget, &allocation);
819 }
820 }
821 else
822 {
823 if (child->mapped)
824 {
825 child->mapped = FALSE;
826
Bram Moolenaar98921892016-02-23 17:14:37 +0100827 if (gtk_widget_get_mapped(child->widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000828 gtk_widget_unmap(child->widget);
829 }
830 }
831}
832
833 static void
834gtk_form_position_children(GtkForm *form)
835{
836 GList *tmp_list;
837
838 for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next)
839 gtk_form_position_child(form, tmp_list->data, FALSE);
840}
841
Bram Moolenaar071d4272004-06-13 20:20:40 +0000842 void
843gtk_form_move_resize(GtkForm *form, GtkWidget *widget,
844 gint x, gint y, gint w, gint h)
845{
Bram Moolenaar98921892016-02-23 17:14:37 +0100846#if GTK_CHECK_VERSION(3,0,0)
847 gtk_widget_set_size_request(widget, w, h);
848#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000849 widget->requisition.width = w;
850 widget->requisition.height = h;
Bram Moolenaar98921892016-02-23 17:14:37 +0100851#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000852
853 gtk_form_move(form, widget, x, y);
854}
855
856 static void
857gtk_form_send_configure(GtkForm *form)
858{
859 GtkWidget *widget;
860 GdkEventConfigure event;
Bram Moolenaar664323e2018-09-18 22:30:07 +0200861 GtkAllocation allocation;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000862
863 widget = GTK_WIDGET(form);
864
Bram Moolenaar664323e2018-09-18 22:30:07 +0200865 gtk_widget_get_allocation(widget, &allocation);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000866 event.type = GDK_CONFIGURE;
Bram Moolenaar98921892016-02-23 17:14:37 +0100867 event.window = gtk_widget_get_window(widget);
Bram Moolenaar664323e2018-09-18 22:30:07 +0200868 event.x = allocation.x;
869 event.y = allocation.y;
870 event.width = allocation.width;
871 event.height = allocation.height;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000872
Bram Moolenaar071d4272004-06-13 20:20:40 +0000873 gtk_main_do_event((GdkEvent*)&event);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000874}
875
Bram Moolenaar071d4272004-06-13 20:20:40 +0000876 static void
Bram Moolenaarb85cb212009-05-17 14:24:23 +0000877gtk_form_child_map(GtkWidget *widget UNUSED, gpointer user_data)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000878{
879 GtkFormChild *child;
880
881 child = (GtkFormChild *)user_data;
882
883 child->mapped = TRUE;
884 gdk_window_show(child->window);
885}
886
Bram Moolenaar071d4272004-06-13 20:20:40 +0000887 static void
Bram Moolenaarb85cb212009-05-17 14:24:23 +0000888gtk_form_child_unmap(GtkWidget *widget UNUSED, gpointer user_data)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000889{
890 GtkFormChild *child;
891
892 child = (GtkFormChild *)user_data;
893
894 child->mapped = FALSE;
895 gdk_window_hide(child->window);
896}