blob: 1bff14b530a74f79ac0aed5c1d034c7be37675ea [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);
Bram Moolenaar6ee96582019-04-27 22:06:37 +0200133 if (child == NULL)
134 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000135
136 child->widget = child_widget;
137 child->window = NULL;
138 child->x = x;
139 child->y = y;
Bram Moolenaar98921892016-02-23 17:14:37 +0100140#if GTK_CHECK_VERSION(3,0,0)
141 gtk_widget_set_size_request(child->widget, -1, -1);
142#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000143 child->widget->requisition.width = 0;
144 child->widget->requisition.height = 0;
Bram Moolenaar98921892016-02-23 17:14:37 +0100145#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000146 child->mapped = FALSE;
147
148 form->children = g_list_append(form->children, child);
149
150 /* child->window must be created and attached to the widget _before_
151 * it has been realized, or else things will break with GTK2. Note
152 * that gtk_widget_set_parent() realizes the widget if it's visible
153 * and its parent is mapped.
154 */
Bram Moolenaar98921892016-02-23 17:14:37 +0100155 if (gtk_widget_get_realized(GTK_WIDGET(form)))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000156 gtk_form_attach_child_window(form, child);
157
158 gtk_widget_set_parent(child_widget, GTK_WIDGET(form));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000159
Bram Moolenaar98921892016-02-23 17:14:37 +0100160 if (gtk_widget_get_realized(GTK_WIDGET(form))
161 && !gtk_widget_get_realized(child_widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000162 gtk_form_realize_child(form, child);
163
164 gtk_form_position_child(form, child, TRUE);
165}
166
167 void
168gtk_form_move(GtkForm *form,
169 GtkWidget *child_widget,
170 gint x,
171 gint y)
172{
173 GList *tmp_list;
174 GtkFormChild *child;
175
176 g_return_if_fail(GTK_IS_FORM(form));
177
178 for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next)
179 {
180 child = tmp_list->data;
181 if (child->widget == child_widget)
182 {
183 child->x = x;
184 child->y = y;
185
186 gtk_form_position_child(form, child, TRUE);
187 return;
188 }
189 }
190}
191
Bram Moolenaar071d4272004-06-13 20:20:40 +0000192 void
193gtk_form_freeze(GtkForm *form)
194{
195 g_return_if_fail(GTK_IS_FORM(form));
196
197 ++form->freeze_count;
198}
199
200 void
201gtk_form_thaw(GtkForm *form)
202{
203 g_return_if_fail(GTK_IS_FORM(form));
204
205 if (form->freeze_count)
206 {
207 if (!(--form->freeze_count))
208 {
209 gtk_form_position_children(form);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000210 gtk_widget_queue_draw(GTK_WIDGET(form));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000211 }
212 }
213}
214
215/* Basic Object handling procedures
216 */
Bram Moolenaar98921892016-02-23 17:14:37 +0100217#if GTK_CHECK_VERSION(3,0,0)
218G_DEFINE_TYPE(GtkForm, gtk_form, GTK_TYPE_CONTAINER)
219#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000220 GtkType
221gtk_form_get_type(void)
222{
223 static GtkType form_type = 0;
224
225 if (!form_type)
226 {
Bram Moolenaarb85cb212009-05-17 14:24:23 +0000227 GtkTypeInfo form_info;
228
Bram Moolenaara9d45512009-05-17 21:25:42 +0000229 vim_memset(&form_info, 0, sizeof(form_info));
Bram Moolenaarb85cb212009-05-17 14:24:23 +0000230 form_info.type_name = "GtkForm";
231 form_info.object_size = sizeof(GtkForm);
232 form_info.class_size = sizeof(GtkFormClass);
233 form_info.class_init_func = (GtkClassInitFunc)gtk_form_class_init;
234 form_info.object_init_func = (GtkObjectInitFunc)gtk_form_init;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000235
236 form_type = gtk_type_unique(GTK_TYPE_CONTAINER, &form_info);
237 }
238 return form_type;
239}
Bram Moolenaar98921892016-02-23 17:14:37 +0100240#endif /* !GTK_CHECK_VERSION(3,0,0) */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000241
242 static void
243gtk_form_class_init(GtkFormClass *klass)
244{
245 GtkWidgetClass *widget_class;
246 GtkContainerClass *container_class;
247
248 widget_class = (GtkWidgetClass *) klass;
249 container_class = (GtkContainerClass *) klass;
250
Bram Moolenaar98921892016-02-23 17:14:37 +0100251#if !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000252 parent_class = gtk_type_class(gtk_container_get_type());
Bram Moolenaar98921892016-02-23 17:14:37 +0100253#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000254
255 widget_class->realize = gtk_form_realize;
256 widget_class->unrealize = gtk_form_unrealize;
257 widget_class->map = gtk_form_map;
Bram Moolenaar98921892016-02-23 17:14:37 +0100258#if GTK_CHECK_VERSION(3,0,0)
259 widget_class->get_preferred_width = gtk_form_get_preferred_width;
260 widget_class->get_preferred_height = gtk_form_get_preferred_height;
261#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000262 widget_class->size_request = gtk_form_size_request;
Bram Moolenaar98921892016-02-23 17:14:37 +0100263#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000264 widget_class->size_allocate = gtk_form_size_allocate;
Bram Moolenaar98921892016-02-23 17:14:37 +0100265#if GTK_CHECK_VERSION(3,0,0)
266 widget_class->draw = gtk_form_draw;
267#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000268 widget_class->expose_event = gtk_form_expose;
Bram Moolenaar98921892016-02-23 17:14:37 +0100269#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000270
271 container_class->remove = gtk_form_remove;
272 container_class->forall = gtk_form_forall;
273}
274
275 static void
276gtk_form_init(GtkForm *form)
277{
Bram Moolenaar98921892016-02-23 17:14:37 +0100278#if GTK_CHECK_VERSION(3,0,0)
279 gtk_widget_set_has_window(GTK_WIDGET(form), TRUE);
280#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000281 form->children = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000282 form->bin_window = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000283 form->freeze_count = 0;
284}
285
286/*
287 * Widget methods
288 */
289
290 static void
291gtk_form_realize(GtkWidget *widget)
292{
293 GList *tmp_list;
294 GtkForm *form;
295 GdkWindowAttr attributes;
296 gint attributes_mask;
Bram Moolenaar664323e2018-09-18 22:30:07 +0200297 GtkAllocation allocation;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000298
299 g_return_if_fail(GTK_IS_FORM(widget));
300
301 form = GTK_FORM(widget);
Bram Moolenaar98921892016-02-23 17:14:37 +0100302 gtk_widget_set_realized(widget, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000303
Bram Moolenaar664323e2018-09-18 22:30:07 +0200304 gtk_widget_get_allocation(widget, &allocation);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000305 attributes.window_type = GDK_WINDOW_CHILD;
Bram Moolenaar664323e2018-09-18 22:30:07 +0200306 attributes.x = allocation.x;
307 attributes.y = allocation.y;
308 attributes.width = allocation.width;
309 attributes.height = allocation.height;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000310 attributes.wclass = GDK_INPUT_OUTPUT;
311 attributes.visual = gtk_widget_get_visual(widget);
Bram Moolenaar98921892016-02-23 17:14:37 +0100312#if GTK_CHECK_VERSION(3,0,0)
313 attributes.event_mask = GDK_EXPOSURE_MASK;
314#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000315 attributes.colormap = gtk_widget_get_colormap(widget);
316 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
Bram Moolenaar98921892016-02-23 17:14:37 +0100317#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000318
Bram Moolenaar98921892016-02-23 17:14:37 +0100319#if GTK_CHECK_VERSION(3,0,0)
320 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
321#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000322 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
Bram Moolenaar98921892016-02-23 17:14:37 +0100323#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000324
Bram Moolenaar98921892016-02-23 17:14:37 +0100325 gtk_widget_set_window(widget,
326 gdk_window_new(gtk_widget_get_parent_window(widget),
327 &attributes, attributes_mask));
Bram Moolenaar25328e32018-09-11 21:30:09 +0200328 gdk_window_set_user_data(gtk_widget_get_window(widget), widget);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000329
330 attributes.x = 0;
331 attributes.y = 0;
332 attributes.event_mask = gtk_widget_get_events(widget);
333
Bram Moolenaar98921892016-02-23 17:14:37 +0100334 form->bin_window = gdk_window_new(gtk_widget_get_window(widget),
335 &attributes, attributes_mask);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000336 gdk_window_set_user_data(form->bin_window, widget);
337
Bram Moolenaar98921892016-02-23 17:14:37 +0100338#if GTK_CHECK_VERSION(3,0,0)
339 {
340 GtkStyleContext * const sctx = gtk_widget_get_style_context(widget);
341
342 gtk_style_context_add_class(sctx, "gtk-form");
343 gtk_style_context_set_state(sctx, GTK_STATE_FLAG_NORMAL);
344# if !GTK_CHECK_VERSION(3,18,0)
345 gtk_style_context_set_background(sctx, gtk_widget_get_window(widget));
346 gtk_style_context_set_background(sctx, form->bin_window);
347# endif
348 }
349#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000350 widget->style = gtk_style_attach(widget->style, widget->window);
351 gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);
352 gtk_style_set_background(widget->style, form->bin_window, GTK_STATE_NORMAL);
Bram Moolenaar98921892016-02-23 17:14:37 +0100353#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000354
Bram Moolenaar071d4272004-06-13 20:20:40 +0000355 for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next)
356 {
357 GtkFormChild *child = tmp_list->data;
358
359 gtk_form_attach_child_window(form, child);
360
Bram Moolenaar98921892016-02-23 17:14:37 +0100361 if (gtk_widget_get_visible(child->widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000362 gtk_form_realize_child(form, child);
363 }
364}
365
366
367/* After reading the documentation at
368 * http://developer.gnome.org/doc/API/2.0/gtk/gtk-changes-2-0.html
369 * I think it should be possible to remove this function when compiling
370 * against gtk-2.0. It doesn't seem to cause problems, though.
371 *
372 * Well, I reckon at least the gdk_window_show(form->bin_window)
373 * is necessary. GtkForm is anything but a usual container widget.
374 */
375 static void
376gtk_form_map(GtkWidget *widget)
377{
378 GList *tmp_list;
379 GtkForm *form;
380
381 g_return_if_fail(GTK_IS_FORM(widget));
382
383 form = GTK_FORM(widget);
384
Bram Moolenaar98921892016-02-23 17:14:37 +0100385 gtk_widget_set_mapped(widget, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000386
Bram Moolenaar98921892016-02-23 17:14:37 +0100387 gdk_window_show(gtk_widget_get_window(widget));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000388 gdk_window_show(form->bin_window);
389
390 for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next)
391 {
392 GtkFormChild *child = tmp_list->data;
393
Bram Moolenaar98921892016-02-23 17:14:37 +0100394 if (gtk_widget_get_visible(child->widget)
395 && !gtk_widget_get_mapped(child->widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000396 gtk_widget_map(child->widget);
397 }
398}
399
400 static void
401gtk_form_unrealize(GtkWidget *widget)
402{
403 GList *tmp_list;
404 GtkForm *form;
405
406 g_return_if_fail(GTK_IS_FORM(widget));
407
408 form = GTK_FORM(widget);
409
410 tmp_list = form->children;
411
412 gdk_window_set_user_data(form->bin_window, NULL);
413 gdk_window_destroy(form->bin_window);
414 form->bin_window = NULL;
415
416 while (tmp_list)
417 {
418 GtkFormChild *child = tmp_list->data;
419
420 if (child->window != NULL)
421 {
Bram Moolenaar98921892016-02-23 17:14:37 +0100422 g_signal_handlers_disconnect_by_func(G_OBJECT(child->widget),
Bram Moolenaard47d8372016-09-09 22:13:24 +0200423 FUNC2GENERIC(gtk_form_child_map),
Bram Moolenaar98921892016-02-23 17:14:37 +0100424 child);
425 g_signal_handlers_disconnect_by_func(G_OBJECT(child->widget),
Bram Moolenaard47d8372016-09-09 22:13:24 +0200426 FUNC2GENERIC(gtk_form_child_unmap),
Bram Moolenaar98921892016-02-23 17:14:37 +0100427 child);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000428
429 gdk_window_set_user_data(child->window, NULL);
430 gdk_window_destroy(child->window);
431
432 child->window = NULL;
433 }
434
435 tmp_list = tmp_list->next;
436 }
437
Bram Moolenaar98921892016-02-23 17:14:37 +0100438#if GTK_CHECK_VERSION(3,0,0)
439 if (GTK_WIDGET_CLASS (gtk_form_parent_class)->unrealize)
440 (* GTK_WIDGET_CLASS (gtk_form_parent_class)->unrealize) (widget);
441#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000442 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
443 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
Bram Moolenaar98921892016-02-23 17:14:37 +0100444#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000445}
446
Bram Moolenaar071d4272004-06-13 20:20:40 +0000447 static void
448gtk_form_size_request(GtkWidget *widget, GtkRequisition *requisition)
449{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000450 g_return_if_fail(GTK_IS_FORM(widget));
Bram Moolenaar99a6e8d2017-03-29 18:07:40 +0200451 g_return_if_fail(requisition != NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000452
Bram Moolenaar98921892016-02-23 17:14:37 +0100453 requisition->width = 1;
454 requisition->height = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000455}
456
Bram Moolenaar98921892016-02-23 17:14:37 +0100457#if GTK_CHECK_VERSION(3,0,0)
458 static void
459gtk_form_get_preferred_width(GtkWidget *widget,
460 gint *minimal_width,
461 gint *natural_width)
462{
463 GtkRequisition requisition;
464
465 gtk_form_size_request(widget, &requisition);
466
467 *minimal_width = requisition.width;
468 *natural_width = requisition.width;
469}
470
471 static void
472gtk_form_get_preferred_height(GtkWidget *widget,
473 gint *minimal_height,
474 gint *natural_height)
475{
476 GtkRequisition requisition;
477
478 gtk_form_size_request(widget, &requisition);
479
480 *minimal_height = requisition.height;
481 *natural_height = requisition.height;
482}
483#endif /* GTK_CHECK_VERSION(3,0,0) */
484
Bram Moolenaar071d4272004-06-13 20:20:40 +0000485 static void
486gtk_form_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
487{
488 GList *tmp_list;
489 GtkForm *form;
490 gboolean need_reposition;
Bram Moolenaar98921892016-02-23 17:14:37 +0100491 GtkAllocation cur_alloc;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000492
493 g_return_if_fail(GTK_IS_FORM(widget));
494
Bram Moolenaar98921892016-02-23 17:14:37 +0100495 gtk_widget_get_allocation(widget, &cur_alloc);
496
497 if (cur_alloc.x == allocation->x
498 && cur_alloc.y == allocation->y
499 && cur_alloc.width == allocation->width
500 && cur_alloc.height == allocation->height)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000501 return;
502
Bram Moolenaar98921892016-02-23 17:14:37 +0100503 need_reposition = cur_alloc.width != allocation->width
504 || cur_alloc.height != allocation->height;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000505 form = GTK_FORM(widget);
506
507 if (need_reposition)
508 {
509 tmp_list = form->children;
510
511 while (tmp_list)
512 {
513 GtkFormChild *child = tmp_list->data;
514 gtk_form_position_child(form, child, TRUE);
515
516 tmp_list = tmp_list->next;
517 }
518 }
519
Bram Moolenaar98921892016-02-23 17:14:37 +0100520 if (gtk_widget_get_realized(widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000521 {
Bram Moolenaar98921892016-02-23 17:14:37 +0100522 gdk_window_move_resize(gtk_widget_get_window(widget),
523 allocation->x, allocation->y,
524 allocation->width, allocation->height);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000525 gdk_window_move_resize(GTK_FORM(widget)->bin_window,
526 0, 0,
527 allocation->width, allocation->height);
528 }
Bram Moolenaar98921892016-02-23 17:14:37 +0100529 gtk_widget_set_allocation(widget, allocation);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000530 if (need_reposition)
531 gtk_form_send_configure(form);
532}
533
Bram Moolenaar98921892016-02-23 17:14:37 +0100534#if GTK_CHECK_VERSION(3,0,0)
535 static void
536gtk_form_render_background(GtkWidget *widget, cairo_t *cr)
537{
538 gtk_render_background(gtk_widget_get_style_context(widget), cr,
539 0, 0,
540 gtk_widget_get_allocated_width(widget),
541 gtk_widget_get_allocated_height(widget));
542}
543
544 static gboolean
545gtk_form_draw(GtkWidget *widget, cairo_t *cr)
546{
547 GList *tmp_list = NULL;
548 GtkForm *form = NULL;
549
550 g_return_val_if_fail(GTK_IS_FORM(widget), FALSE);
551
552 gtk_form_render_background(widget, cr);
553
554 form = GTK_FORM(widget);
555 for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next)
556 {
557 GtkFormChild * const formchild = tmp_list->data;
558
559 if (!gtk_widget_get_has_window(formchild->widget) &&
560 gtk_cairo_should_draw_window(cr, formchild->window))
561 {
562 /* To get gtk_widget_draw() to work, it is required to call
563 * gtk_widget_size_allocate() in advance with a well-posed
564 * allocation for a given child widget in order to set a
565 * certain private GtkWidget variable, called
Bram Moolenaarbdace832019-03-02 10:13:42 +0100566 * widget->priv->alloc_need, to the proper value; otherwise,
Bram Moolenaar98921892016-02-23 17:14:37 +0100567 * gtk_widget_draw() fails and the relevant scrollbar won't
568 * appear on the screen.
569 *
570 * Calling gtk_form_position_child() like this is one of ways
571 * to make sure of that. */
572 gtk_form_position_child(form, formchild, TRUE);
573
574 gtk_form_render_background(formchild->widget, cr);
575 }
576 }
577
578 return GTK_WIDGET_CLASS(gtk_form_parent_class)->draw(widget, cr);
579}
580#else /* !GTK_CHECK_VERSION(3,0,0) */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000581 static gint
582gtk_form_expose(GtkWidget *widget, GdkEventExpose *event)
583{
584 GList *tmp_list;
585 GtkForm *form;
586
587 g_return_val_if_fail(GTK_IS_FORM(widget), FALSE);
588
589 form = GTK_FORM(widget);
590
591 if (event->window == form->bin_window)
592 return FALSE;
593
594 for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next)
Bram Moolenaar99a6e8d2017-03-29 18:07:40 +0200595 gtk_container_propagate_expose(GTK_CONTAINER(widget),
596 GTK_WIDGET(((GtkFormChild *)tmp_list->data)->widget),
597 event);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000598
599 return FALSE;
600}
Bram Moolenaar98921892016-02-23 17:14:37 +0100601#endif /* !GTK_CHECK_VERSION(3,0,0) */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000602
603/* Container method
604 */
605 static void
606gtk_form_remove(GtkContainer *container, GtkWidget *widget)
607{
608 GList *tmp_list;
609 GtkForm *form;
610 GtkFormChild *child = NULL; /* init for gcc */
611
612 g_return_if_fail(GTK_IS_FORM(container));
613
614 form = GTK_FORM(container);
615
616 tmp_list = form->children;
617 while (tmp_list)
618 {
619 child = tmp_list->data;
620 if (child->widget == widget)
621 break;
622 tmp_list = tmp_list->next;
623 }
624
625 if (tmp_list)
626 {
Bram Moolenaar98921892016-02-23 17:14:37 +0100627#if GTK_CHECK_VERSION(3,0,0)
628 const gboolean was_visible = gtk_widget_get_visible(widget);
629#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000630 if (child->window)
631 {
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_map), child);
Bram Moolenaar98921892016-02-23 17:14:37 +0100634 g_signal_handlers_disconnect_by_func(G_OBJECT(child->widget),
Bram Moolenaard47d8372016-09-09 22:13:24 +0200635 FUNC2GENERIC(&gtk_form_child_unmap), child);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000636
637 /* FIXME: This will cause problems for reparenting NO_WINDOW
638 * widgets out of a GtkForm
639 */
640 gdk_window_set_user_data(child->window, NULL);
641 gdk_window_destroy(child->window);
642 }
643 gtk_widget_unparent(widget);
Bram Moolenaar98921892016-02-23 17:14:37 +0100644#if GTK_CHECK_VERSION(3,0,0)
645 if (was_visible)
646 gtk_widget_queue_resize(GTK_WIDGET(container));
647#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000648 form->children = g_list_remove_link(form->children, tmp_list);
649 g_list_free_1(tmp_list);
650 g_free(child);
651 }
652}
653
Bram Moolenaar071d4272004-06-13 20:20:40 +0000654 static void
655gtk_form_forall(GtkContainer *container,
Bram Moolenaarb85cb212009-05-17 14:24:23 +0000656 gboolean include_internals UNUSED,
Bram Moolenaar071d4272004-06-13 20:20:40 +0000657 GtkCallback callback,
658 gpointer callback_data)
659{
660 GtkForm *form;
661 GtkFormChild *child;
662 GList *tmp_list;
663
664 g_return_if_fail(GTK_IS_FORM(container));
665 g_return_if_fail(callback != NULL);
666
667 form = GTK_FORM(container);
668
669 tmp_list = form->children;
670 while (tmp_list)
671 {
672 child = tmp_list->data;
673 tmp_list = tmp_list->next;
674
675 (*callback) (child->widget, callback_data);
676 }
677}
678
679/* Operations on children
680 */
681
682 static void
683gtk_form_attach_child_window(GtkForm *form, GtkFormChild *child)
684{
685 if (child->window != NULL)
686 return; /* been there, done that */
687
Bram Moolenaar98921892016-02-23 17:14:37 +0100688 if (!gtk_widget_get_has_window(child->widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000689 {
690 GtkWidget *widget;
691 GdkWindowAttr attributes;
692 gint attributes_mask;
Bram Moolenaar664323e2018-09-18 22:30:07 +0200693 GtkRequisition requisition;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000694
695 widget = GTK_WIDGET(form);
696
Bram Moolenaar664323e2018-09-18 22:30:07 +0200697#if GTK_CHECK_VERSION(3,0,0)
698 gtk_widget_get_preferred_size(child->widget, &requisition, NULL);
699#else
700 requisition = child->widget->requisition;
701#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000702 attributes.window_type = GDK_WINDOW_CHILD;
703 attributes.x = child->x;
704 attributes.y = child->y;
Bram Moolenaar664323e2018-09-18 22:30:07 +0200705 attributes.width = requisition.width;
706 attributes.height = requisition.height;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000707 attributes.wclass = GDK_INPUT_OUTPUT;
708 attributes.visual = gtk_widget_get_visual(widget);
Bram Moolenaar98921892016-02-23 17:14:37 +0100709#if !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000710 attributes.colormap = gtk_widget_get_colormap(widget);
Bram Moolenaar98921892016-02-23 17:14:37 +0100711#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000712 attributes.event_mask = GDK_EXPOSURE_MASK;
713
Bram Moolenaar98921892016-02-23 17:14:37 +0100714#if GTK_CHECK_VERSION(3,0,0)
715 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
716#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000717 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
Bram Moolenaar98921892016-02-23 17:14:37 +0100718#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000719 child->window = gdk_window_new(form->bin_window,
720 &attributes, attributes_mask);
721 gdk_window_set_user_data(child->window, widget);
722
Bram Moolenaar98921892016-02-23 17:14:37 +0100723#if GTK_CHECK_VERSION(3,0,0)
724 {
725 GtkStyleContext * const sctx = gtk_widget_get_style_context(widget);
726
727 gtk_style_context_set_state(sctx, GTK_STATE_FLAG_NORMAL);
728# if !GTK_CHECK_VERSION(3,18,0)
729 gtk_style_context_set_background(sctx, child->window);
730# endif
731 }
732#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000733 gtk_style_set_background(widget->style,
734 child->window,
735 GTK_STATE_NORMAL);
Bram Moolenaar98921892016-02-23 17:14:37 +0100736#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000737
738 gtk_widget_set_parent_window(child->widget, child->window);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000739 /*
740 * Install signal handlers to map/unmap child->window
741 * alongside with the actual widget.
742 */
Bram Moolenaar98921892016-02-23 17:14:37 +0100743 g_signal_connect(G_OBJECT(child->widget), "map",
744 G_CALLBACK(&gtk_form_child_map), child);
745 g_signal_connect(G_OBJECT(child->widget), "unmap",
746 G_CALLBACK(&gtk_form_child_unmap), child);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000747 }
Bram Moolenaar98921892016-02-23 17:14:37 +0100748 else if (!gtk_widget_get_realized(child->widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000749 {
750 gtk_widget_set_parent_window(child->widget, form->bin_window);
751 }
752}
753
754 static void
755gtk_form_realize_child(GtkForm *form, GtkFormChild *child)
756{
757 gtk_form_attach_child_window(form, child);
758 gtk_widget_realize(child->widget);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000759}
760
761 static void
762gtk_form_position_child(GtkForm *form, GtkFormChild *child,
763 gboolean force_allocate)
764{
765 gint x;
766 gint y;
767
768 x = child->x;
769 y = child->y;
770
771 if ((x >= G_MINSHORT) && (x <= G_MAXSHORT) &&
772 (y >= G_MINSHORT) && (y <= G_MAXSHORT))
773 {
774 if (!child->mapped)
775 {
Bram Moolenaar98921892016-02-23 17:14:37 +0100776 if (gtk_widget_get_mapped(GTK_WIDGET(form))
777 && gtk_widget_get_visible(child->widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000778 {
Bram Moolenaar98921892016-02-23 17:14:37 +0100779 if (!gtk_widget_get_mapped(child->widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000780 gtk_widget_map(child->widget);
781
782 child->mapped = TRUE;
783 force_allocate = TRUE;
784 }
785 }
786
787 if (force_allocate)
788 {
789 GtkAllocation allocation;
Bram Moolenaar98921892016-02-23 17:14:37 +0100790 GtkRequisition requisition;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000791
Bram Moolenaar664323e2018-09-18 22:30:07 +0200792#if GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar98921892016-02-23 17:14:37 +0100793 gtk_widget_get_preferred_size(child->widget, &requisition, NULL);
Bram Moolenaar664323e2018-09-18 22:30:07 +0200794#else
795 requisition = child->widget->requisition;
Bram Moolenaar98921892016-02-23 17:14:37 +0100796#endif
797
Bram Moolenaar98921892016-02-23 17:14:37 +0100798 if (!gtk_widget_get_has_window(child->widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000799 {
800 if (child->window)
801 {
Bram Moolenaar98921892016-02-23 17:14:37 +0100802 gdk_window_move_resize(child->window,
803 x, y,
804 requisition.width,
805 requisition.height);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000806 }
807
808 allocation.x = 0;
809 allocation.y = 0;
810 }
811 else
812 {
813 allocation.x = x;
814 allocation.y = y;
815 }
816
Bram Moolenaar98921892016-02-23 17:14:37 +0100817 allocation.width = requisition.width;
818 allocation.height = requisition.height;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000819
820 gtk_widget_size_allocate(child->widget, &allocation);
821 }
822 }
823 else
824 {
825 if (child->mapped)
826 {
827 child->mapped = FALSE;
828
Bram Moolenaar98921892016-02-23 17:14:37 +0100829 if (gtk_widget_get_mapped(child->widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000830 gtk_widget_unmap(child->widget);
831 }
832 }
833}
834
835 static void
836gtk_form_position_children(GtkForm *form)
837{
838 GList *tmp_list;
839
840 for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next)
841 gtk_form_position_child(form, tmp_list->data, FALSE);
842}
843
Bram Moolenaar071d4272004-06-13 20:20:40 +0000844 void
845gtk_form_move_resize(GtkForm *form, GtkWidget *widget,
846 gint x, gint y, gint w, gint h)
847{
Bram Moolenaar98921892016-02-23 17:14:37 +0100848#if GTK_CHECK_VERSION(3,0,0)
849 gtk_widget_set_size_request(widget, w, h);
850#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000851 widget->requisition.width = w;
852 widget->requisition.height = h;
Bram Moolenaar98921892016-02-23 17:14:37 +0100853#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000854
855 gtk_form_move(form, widget, x, y);
856}
857
858 static void
859gtk_form_send_configure(GtkForm *form)
860{
861 GtkWidget *widget;
862 GdkEventConfigure event;
Bram Moolenaar664323e2018-09-18 22:30:07 +0200863 GtkAllocation allocation;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000864
865 widget = GTK_WIDGET(form);
866
Bram Moolenaar664323e2018-09-18 22:30:07 +0200867 gtk_widget_get_allocation(widget, &allocation);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000868 event.type = GDK_CONFIGURE;
Bram Moolenaar98921892016-02-23 17:14:37 +0100869 event.window = gtk_widget_get_window(widget);
Bram Moolenaar664323e2018-09-18 22:30:07 +0200870 event.x = allocation.x;
871 event.y = allocation.y;
872 event.width = allocation.width;
873 event.height = allocation.height;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000874
Bram Moolenaar071d4272004-06-13 20:20:40 +0000875 gtk_main_do_event((GdkEvent*)&event);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000876}
877
Bram Moolenaar071d4272004-06-13 20:20:40 +0000878 static void
Bram Moolenaarb85cb212009-05-17 14:24:23 +0000879gtk_form_child_map(GtkWidget *widget UNUSED, gpointer user_data)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000880{
881 GtkFormChild *child;
882
883 child = (GtkFormChild *)user_data;
884
885 child->mapped = TRUE;
886 gdk_window_show(child->window);
887}
888
Bram Moolenaar071d4272004-06-13 20:20:40 +0000889 static void
Bram Moolenaarb85cb212009-05-17 14:24:23 +0000890gtk_form_child_unmap(GtkWidget *widget UNUSED, gpointer user_data)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000891{
892 GtkFormChild *child;
893
894 child = (GtkFormChild *)user_data;
895
896 child->mapped = FALSE;
897 gdk_window_hide(child->window);
898}