blob: ff9aee8f0541309a58b83613dd189cd796fc6717 [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"
Bram Moolenaar30613902019-12-01 22:11:18 +010029#include <gtk/gtk.h> // without this it compiles, but gives errors at
30 // runtime!
Bram Moolenaar071d4272004-06-13 20:20:40 +000031#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;
Bram Moolenaar30613902019-12-01 22:11:18 +010047 gint x; // relative subwidget x position
48 gint y; // relative subwidget y position
Bram Moolenaar071d4272004-06-13 20:20:40 +000049 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
Bram Moolenaar30613902019-12-01 22:11:18 +0100104// Public interface
Bram Moolenaar071d4272004-06-13 20:20:40 +0000105
106 GtkWidget *
107gtk_form_new(void)
108{
109 GtkForm *form;
110
Bram Moolenaar98921892016-02-23 17:14:37 +0100111#if GTK_CHECK_VERSION(3,0,0)
112 form = g_object_new(GTK_TYPE_FORM, NULL);
113#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000114 form = gtk_type_new(gtk_form_get_type());
Bram Moolenaar98921892016-02-23 17:14:37 +0100115#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000116
117 return GTK_WIDGET(form);
118}
119
120 void
121gtk_form_put(GtkForm *form,
122 GtkWidget *child_widget,
123 gint x,
124 gint y)
125{
126 GtkFormChild *child;
127
128 g_return_if_fail(GTK_IS_FORM(form));
129
Bram Moolenaar30613902019-12-01 22:11:18 +0100130 // LINTED: avoid warning: conversion to 'unsigned long'
Bram Moolenaar071d4272004-06-13 20:20:40 +0000131 child = g_new(GtkFormChild, 1);
Bram Moolenaar6ee96582019-04-27 22:06:37 +0200132 if (child == NULL)
133 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000134
135 child->widget = child_widget;
136 child->window = NULL;
137 child->x = x;
138 child->y = y;
Bram Moolenaar98921892016-02-23 17:14:37 +0100139#if GTK_CHECK_VERSION(3,0,0)
140 gtk_widget_set_size_request(child->widget, -1, -1);
141#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000142 child->widget->requisition.width = 0;
143 child->widget->requisition.height = 0;
Bram Moolenaar98921892016-02-23 17:14:37 +0100144#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000145 child->mapped = FALSE;
146
147 form->children = g_list_append(form->children, child);
148
Bram Moolenaar30613902019-12-01 22:11:18 +0100149 // child->window must be created and attached to the widget _before_
150 // it has been realized, or else things will break with GTK2. Note
151 // that gtk_widget_set_parent() realizes the widget if it's visible
152 // and its parent is mapped.
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
Bram Moolenaar30613902019-12-01 22:11:18 +0100213// Basic Object handling procedures
Bram Moolenaar98921892016-02-23 17:14:37 +0100214#if GTK_CHECK_VERSION(3,0,0)
215G_DEFINE_TYPE(GtkForm, gtk_form, GTK_TYPE_CONTAINER)
216#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000217 GtkType
218gtk_form_get_type(void)
219{
220 static GtkType form_type = 0;
221
222 if (!form_type)
223 {
Bram Moolenaarb85cb212009-05-17 14:24:23 +0000224 GtkTypeInfo form_info;
225
Bram Moolenaara9d45512009-05-17 21:25:42 +0000226 vim_memset(&form_info, 0, sizeof(form_info));
Bram Moolenaarb85cb212009-05-17 14:24:23 +0000227 form_info.type_name = "GtkForm";
228 form_info.object_size = sizeof(GtkForm);
229 form_info.class_size = sizeof(GtkFormClass);
230 form_info.class_init_func = (GtkClassInitFunc)gtk_form_class_init;
231 form_info.object_init_func = (GtkObjectInitFunc)gtk_form_init;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000232
233 form_type = gtk_type_unique(GTK_TYPE_CONTAINER, &form_info);
234 }
235 return form_type;
236}
Bram Moolenaar30613902019-12-01 22:11:18 +0100237#endif // !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000238
239 static void
240gtk_form_class_init(GtkFormClass *klass)
241{
242 GtkWidgetClass *widget_class;
243 GtkContainerClass *container_class;
244
245 widget_class = (GtkWidgetClass *) klass;
246 container_class = (GtkContainerClass *) klass;
247
Bram Moolenaar98921892016-02-23 17:14:37 +0100248#if !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000249 parent_class = gtk_type_class(gtk_container_get_type());
Bram Moolenaar98921892016-02-23 17:14:37 +0100250#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000251
252 widget_class->realize = gtk_form_realize;
253 widget_class->unrealize = gtk_form_unrealize;
254 widget_class->map = gtk_form_map;
Bram Moolenaar98921892016-02-23 17:14:37 +0100255#if GTK_CHECK_VERSION(3,0,0)
256 widget_class->get_preferred_width = gtk_form_get_preferred_width;
257 widget_class->get_preferred_height = gtk_form_get_preferred_height;
258#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000259 widget_class->size_request = gtk_form_size_request;
Bram Moolenaar98921892016-02-23 17:14:37 +0100260#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000261 widget_class->size_allocate = gtk_form_size_allocate;
Bram Moolenaar98921892016-02-23 17:14:37 +0100262#if GTK_CHECK_VERSION(3,0,0)
263 widget_class->draw = gtk_form_draw;
264#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000265 widget_class->expose_event = gtk_form_expose;
Bram Moolenaar98921892016-02-23 17:14:37 +0100266#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000267
268 container_class->remove = gtk_form_remove;
269 container_class->forall = gtk_form_forall;
270}
271
272 static void
273gtk_form_init(GtkForm *form)
274{
Bram Moolenaar98921892016-02-23 17:14:37 +0100275#if GTK_CHECK_VERSION(3,0,0)
276 gtk_widget_set_has_window(GTK_WIDGET(form), TRUE);
277#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000278 form->children = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000279 form->bin_window = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000280 form->freeze_count = 0;
281}
282
283/*
284 * Widget methods
285 */
286
287 static void
288gtk_form_realize(GtkWidget *widget)
289{
290 GList *tmp_list;
291 GtkForm *form;
292 GdkWindowAttr attributes;
293 gint attributes_mask;
Bram Moolenaar664323e2018-09-18 22:30:07 +0200294 GtkAllocation allocation;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000295
296 g_return_if_fail(GTK_IS_FORM(widget));
297
298 form = GTK_FORM(widget);
Bram Moolenaar98921892016-02-23 17:14:37 +0100299 gtk_widget_set_realized(widget, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000300
Bram Moolenaar664323e2018-09-18 22:30:07 +0200301 gtk_widget_get_allocation(widget, &allocation);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000302 attributes.window_type = GDK_WINDOW_CHILD;
Bram Moolenaar664323e2018-09-18 22:30:07 +0200303 attributes.x = allocation.x;
304 attributes.y = allocation.y;
305 attributes.width = allocation.width;
306 attributes.height = allocation.height;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000307 attributes.wclass = GDK_INPUT_OUTPUT;
308 attributes.visual = gtk_widget_get_visual(widget);
Bram Moolenaar98921892016-02-23 17:14:37 +0100309#if GTK_CHECK_VERSION(3,0,0)
310 attributes.event_mask = GDK_EXPOSURE_MASK;
311#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000312 attributes.colormap = gtk_widget_get_colormap(widget);
313 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
Bram Moolenaar98921892016-02-23 17:14:37 +0100314#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000315
Bram Moolenaar98921892016-02-23 17:14:37 +0100316#if GTK_CHECK_VERSION(3,0,0)
317 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
318#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000319 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
Bram Moolenaar98921892016-02-23 17:14:37 +0100320#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000321
Bram Moolenaar98921892016-02-23 17:14:37 +0100322 gtk_widget_set_window(widget,
323 gdk_window_new(gtk_widget_get_parent_window(widget),
324 &attributes, attributes_mask));
Bram Moolenaar25328e32018-09-11 21:30:09 +0200325 gdk_window_set_user_data(gtk_widget_get_window(widget), widget);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000326
327 attributes.x = 0;
328 attributes.y = 0;
329 attributes.event_mask = gtk_widget_get_events(widget);
330
Bram Moolenaar98921892016-02-23 17:14:37 +0100331 form->bin_window = gdk_window_new(gtk_widget_get_window(widget),
332 &attributes, attributes_mask);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000333 gdk_window_set_user_data(form->bin_window, widget);
334
Bram Moolenaar98921892016-02-23 17:14:37 +0100335#if GTK_CHECK_VERSION(3,0,0)
336 {
337 GtkStyleContext * const sctx = gtk_widget_get_style_context(widget);
338
339 gtk_style_context_add_class(sctx, "gtk-form");
340 gtk_style_context_set_state(sctx, GTK_STATE_FLAG_NORMAL);
341# if !GTK_CHECK_VERSION(3,18,0)
342 gtk_style_context_set_background(sctx, gtk_widget_get_window(widget));
343 gtk_style_context_set_background(sctx, form->bin_window);
344# endif
345 }
346#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000347 widget->style = gtk_style_attach(widget->style, widget->window);
348 gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);
349 gtk_style_set_background(widget->style, form->bin_window, GTK_STATE_NORMAL);
Bram Moolenaar98921892016-02-23 17:14:37 +0100350#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000351
Bram Moolenaar071d4272004-06-13 20:20:40 +0000352 for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next)
353 {
354 GtkFormChild *child = tmp_list->data;
355
356 gtk_form_attach_child_window(form, child);
357
Bram Moolenaar98921892016-02-23 17:14:37 +0100358 if (gtk_widget_get_visible(child->widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000359 gtk_form_realize_child(form, child);
360 }
361}
362
363
Bram Moolenaar30613902019-12-01 22:11:18 +0100364// After reading the documentation at
365// http://developer.gnome.org/doc/API/2.0/gtk/gtk-changes-2-0.html
366// I think it should be possible to remove this function when compiling
367// against gtk-2.0. It doesn't seem to cause problems, though.
368//
369// Well, I reckon at least the gdk_window_show(form->bin_window)
370// is necessary. GtkForm is anything but a usual container widget.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000371 static void
372gtk_form_map(GtkWidget *widget)
373{
374 GList *tmp_list;
375 GtkForm *form;
376
377 g_return_if_fail(GTK_IS_FORM(widget));
378
379 form = GTK_FORM(widget);
380
Bram Moolenaar98921892016-02-23 17:14:37 +0100381 gtk_widget_set_mapped(widget, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000382
Bram Moolenaar98921892016-02-23 17:14:37 +0100383 gdk_window_show(gtk_widget_get_window(widget));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000384 gdk_window_show(form->bin_window);
385
386 for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next)
387 {
388 GtkFormChild *child = tmp_list->data;
389
Bram Moolenaar98921892016-02-23 17:14:37 +0100390 if (gtk_widget_get_visible(child->widget)
391 && !gtk_widget_get_mapped(child->widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000392 gtk_widget_map(child->widget);
393 }
394}
395
396 static void
397gtk_form_unrealize(GtkWidget *widget)
398{
399 GList *tmp_list;
400 GtkForm *form;
401
402 g_return_if_fail(GTK_IS_FORM(widget));
403
404 form = GTK_FORM(widget);
405
406 tmp_list = form->children;
407
408 gdk_window_set_user_data(form->bin_window, NULL);
409 gdk_window_destroy(form->bin_window);
410 form->bin_window = NULL;
411
412 while (tmp_list)
413 {
414 GtkFormChild *child = tmp_list->data;
415
416 if (child->window != NULL)
417 {
Bram Moolenaar98921892016-02-23 17:14:37 +0100418 g_signal_handlers_disconnect_by_func(G_OBJECT(child->widget),
Bram Moolenaard47d8372016-09-09 22:13:24 +0200419 FUNC2GENERIC(gtk_form_child_map),
Bram Moolenaar98921892016-02-23 17:14:37 +0100420 child);
421 g_signal_handlers_disconnect_by_func(G_OBJECT(child->widget),
Bram Moolenaard47d8372016-09-09 22:13:24 +0200422 FUNC2GENERIC(gtk_form_child_unmap),
Bram Moolenaar98921892016-02-23 17:14:37 +0100423 child);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000424
425 gdk_window_set_user_data(child->window, NULL);
426 gdk_window_destroy(child->window);
427
428 child->window = NULL;
429 }
430
431 tmp_list = tmp_list->next;
432 }
433
Bram Moolenaar98921892016-02-23 17:14:37 +0100434#if GTK_CHECK_VERSION(3,0,0)
435 if (GTK_WIDGET_CLASS (gtk_form_parent_class)->unrealize)
436 (* GTK_WIDGET_CLASS (gtk_form_parent_class)->unrealize) (widget);
437#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000438 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
439 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
Bram Moolenaar98921892016-02-23 17:14:37 +0100440#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000441}
442
Bram Moolenaar071d4272004-06-13 20:20:40 +0000443 static void
444gtk_form_size_request(GtkWidget *widget, GtkRequisition *requisition)
445{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000446 g_return_if_fail(GTK_IS_FORM(widget));
Bram Moolenaar99a6e8d2017-03-29 18:07:40 +0200447 g_return_if_fail(requisition != NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000448
Bram Moolenaar98921892016-02-23 17:14:37 +0100449 requisition->width = 1;
450 requisition->height = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000451}
452
Bram Moolenaar98921892016-02-23 17:14:37 +0100453#if GTK_CHECK_VERSION(3,0,0)
454 static void
455gtk_form_get_preferred_width(GtkWidget *widget,
456 gint *minimal_width,
457 gint *natural_width)
458{
459 GtkRequisition requisition;
460
461 gtk_form_size_request(widget, &requisition);
462
463 *minimal_width = requisition.width;
464 *natural_width = requisition.width;
465}
466
467 static void
468gtk_form_get_preferred_height(GtkWidget *widget,
469 gint *minimal_height,
470 gint *natural_height)
471{
472 GtkRequisition requisition;
473
474 gtk_form_size_request(widget, &requisition);
475
476 *minimal_height = requisition.height;
477 *natural_height = requisition.height;
478}
Bram Moolenaar30613902019-12-01 22:11:18 +0100479#endif // GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar98921892016-02-23 17:14:37 +0100480
Bram Moolenaar071d4272004-06-13 20:20:40 +0000481 static void
482gtk_form_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
483{
484 GList *tmp_list;
485 GtkForm *form;
486 gboolean need_reposition;
Bram Moolenaar98921892016-02-23 17:14:37 +0100487 GtkAllocation cur_alloc;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000488
489 g_return_if_fail(GTK_IS_FORM(widget));
490
Bram Moolenaar98921892016-02-23 17:14:37 +0100491 gtk_widget_get_allocation(widget, &cur_alloc);
492
493 if (cur_alloc.x == allocation->x
494 && cur_alloc.y == allocation->y
495 && cur_alloc.width == allocation->width
496 && cur_alloc.height == allocation->height)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000497 return;
498
Bram Moolenaar98921892016-02-23 17:14:37 +0100499 need_reposition = cur_alloc.width != allocation->width
500 || cur_alloc.height != allocation->height;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000501 form = GTK_FORM(widget);
502
503 if (need_reposition)
504 {
505 tmp_list = form->children;
506
507 while (tmp_list)
508 {
509 GtkFormChild *child = tmp_list->data;
510 gtk_form_position_child(form, child, TRUE);
511
512 tmp_list = tmp_list->next;
513 }
514 }
515
Bram Moolenaar98921892016-02-23 17:14:37 +0100516 if (gtk_widget_get_realized(widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000517 {
Bram Moolenaar98921892016-02-23 17:14:37 +0100518 gdk_window_move_resize(gtk_widget_get_window(widget),
519 allocation->x, allocation->y,
520 allocation->width, allocation->height);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000521 gdk_window_move_resize(GTK_FORM(widget)->bin_window,
522 0, 0,
523 allocation->width, allocation->height);
524 }
Bram Moolenaar98921892016-02-23 17:14:37 +0100525 gtk_widget_set_allocation(widget, allocation);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000526 if (need_reposition)
527 gtk_form_send_configure(form);
528}
529
Bram Moolenaar98921892016-02-23 17:14:37 +0100530#if GTK_CHECK_VERSION(3,0,0)
531 static void
532gtk_form_render_background(GtkWidget *widget, cairo_t *cr)
533{
534 gtk_render_background(gtk_widget_get_style_context(widget), cr,
535 0, 0,
536 gtk_widget_get_allocated_width(widget),
537 gtk_widget_get_allocated_height(widget));
538}
539
540 static gboolean
541gtk_form_draw(GtkWidget *widget, cairo_t *cr)
542{
543 GList *tmp_list = NULL;
544 GtkForm *form = NULL;
545
546 g_return_val_if_fail(GTK_IS_FORM(widget), FALSE);
547
548 gtk_form_render_background(widget, cr);
549
550 form = GTK_FORM(widget);
551 for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next)
552 {
553 GtkFormChild * const formchild = tmp_list->data;
554
555 if (!gtk_widget_get_has_window(formchild->widget) &&
556 gtk_cairo_should_draw_window(cr, formchild->window))
557 {
Bram Moolenaar30613902019-12-01 22:11:18 +0100558 // To get gtk_widget_draw() to work, it is required to call
559 // gtk_widget_size_allocate() in advance with a well-posed
560 // allocation for a given child widget in order to set a
561 // certain private GtkWidget variable, called
562 // widget->priv->alloc_need, to the proper value; otherwise,
563 // gtk_widget_draw() fails and the relevant scrollbar won't
564 // appear on the screen.
565 //
566 // Calling gtk_form_position_child() like this is one of ways
567 // to make sure of that.
Bram Moolenaar98921892016-02-23 17:14:37 +0100568 gtk_form_position_child(form, formchild, TRUE);
569
570 gtk_form_render_background(formchild->widget, cr);
571 }
572 }
573
574 return GTK_WIDGET_CLASS(gtk_form_parent_class)->draw(widget, cr);
575}
Bram Moolenaar30613902019-12-01 22:11:18 +0100576#else // !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000577 static gint
578gtk_form_expose(GtkWidget *widget, GdkEventExpose *event)
579{
580 GList *tmp_list;
581 GtkForm *form;
582
583 g_return_val_if_fail(GTK_IS_FORM(widget), FALSE);
584
585 form = GTK_FORM(widget);
586
587 if (event->window == form->bin_window)
588 return FALSE;
589
590 for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next)
Bram Moolenaar99a6e8d2017-03-29 18:07:40 +0200591 gtk_container_propagate_expose(GTK_CONTAINER(widget),
592 GTK_WIDGET(((GtkFormChild *)tmp_list->data)->widget),
593 event);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000594
595 return FALSE;
596}
Bram Moolenaar30613902019-12-01 22:11:18 +0100597#endif // !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000598
Bram Moolenaar30613902019-12-01 22:11:18 +0100599// Container method
Bram Moolenaar071d4272004-06-13 20:20:40 +0000600 static void
601gtk_form_remove(GtkContainer *container, GtkWidget *widget)
602{
603 GList *tmp_list;
604 GtkForm *form;
Bram Moolenaar30613902019-12-01 22:11:18 +0100605 GtkFormChild *child = NULL; // init for gcc
Bram Moolenaar071d4272004-06-13 20:20:40 +0000606
607 g_return_if_fail(GTK_IS_FORM(container));
608
609 form = GTK_FORM(container);
610
611 tmp_list = form->children;
612 while (tmp_list)
613 {
614 child = tmp_list->data;
615 if (child->widget == widget)
616 break;
617 tmp_list = tmp_list->next;
618 }
619
620 if (tmp_list)
621 {
Bram Moolenaar98921892016-02-23 17:14:37 +0100622#if GTK_CHECK_VERSION(3,0,0)
623 const gboolean was_visible = gtk_widget_get_visible(widget);
624#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000625 if (child->window)
626 {
Bram Moolenaar98921892016-02-23 17:14:37 +0100627 g_signal_handlers_disconnect_by_func(G_OBJECT(child->widget),
Bram Moolenaard47d8372016-09-09 22:13:24 +0200628 FUNC2GENERIC(&gtk_form_child_map), child);
Bram Moolenaar98921892016-02-23 17:14:37 +0100629 g_signal_handlers_disconnect_by_func(G_OBJECT(child->widget),
Bram Moolenaard47d8372016-09-09 22:13:24 +0200630 FUNC2GENERIC(&gtk_form_child_unmap), child);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000631
Bram Moolenaar30613902019-12-01 22:11:18 +0100632 // FIXME: This will cause problems for reparenting NO_WINDOW
633 // widgets out of a GtkForm
Bram Moolenaar071d4272004-06-13 20:20:40 +0000634 gdk_window_set_user_data(child->window, NULL);
635 gdk_window_destroy(child->window);
636 }
637 gtk_widget_unparent(widget);
Bram Moolenaar98921892016-02-23 17:14:37 +0100638#if GTK_CHECK_VERSION(3,0,0)
639 if (was_visible)
640 gtk_widget_queue_resize(GTK_WIDGET(container));
641#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000642 form->children = g_list_remove_link(form->children, tmp_list);
643 g_list_free_1(tmp_list);
644 g_free(child);
645 }
646}
647
Bram Moolenaar071d4272004-06-13 20:20:40 +0000648 static void
649gtk_form_forall(GtkContainer *container,
Bram Moolenaarb85cb212009-05-17 14:24:23 +0000650 gboolean include_internals UNUSED,
Bram Moolenaar071d4272004-06-13 20:20:40 +0000651 GtkCallback callback,
652 gpointer callback_data)
653{
654 GtkForm *form;
655 GtkFormChild *child;
656 GList *tmp_list;
657
658 g_return_if_fail(GTK_IS_FORM(container));
659 g_return_if_fail(callback != NULL);
660
661 form = GTK_FORM(container);
662
663 tmp_list = form->children;
664 while (tmp_list)
665 {
666 child = tmp_list->data;
667 tmp_list = tmp_list->next;
668
669 (*callback) (child->widget, callback_data);
670 }
671}
672
Bram Moolenaar30613902019-12-01 22:11:18 +0100673// Operations on children
Bram Moolenaar071d4272004-06-13 20:20:40 +0000674
675 static void
676gtk_form_attach_child_window(GtkForm *form, GtkFormChild *child)
677{
678 if (child->window != NULL)
Bram Moolenaar30613902019-12-01 22:11:18 +0100679 return; // been there, done that
Bram Moolenaar071d4272004-06-13 20:20:40 +0000680
Bram Moolenaar98921892016-02-23 17:14:37 +0100681 if (!gtk_widget_get_has_window(child->widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000682 {
683 GtkWidget *widget;
684 GdkWindowAttr attributes;
685 gint attributes_mask;
Bram Moolenaar664323e2018-09-18 22:30:07 +0200686 GtkRequisition requisition;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000687
688 widget = GTK_WIDGET(form);
689
Bram Moolenaar664323e2018-09-18 22:30:07 +0200690#if GTK_CHECK_VERSION(3,0,0)
691 gtk_widget_get_preferred_size(child->widget, &requisition, NULL);
692#else
693 requisition = child->widget->requisition;
694#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000695 attributes.window_type = GDK_WINDOW_CHILD;
696 attributes.x = child->x;
697 attributes.y = child->y;
Bram Moolenaar664323e2018-09-18 22:30:07 +0200698 attributes.width = requisition.width;
699 attributes.height = requisition.height;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000700 attributes.wclass = GDK_INPUT_OUTPUT;
701 attributes.visual = gtk_widget_get_visual(widget);
Bram Moolenaar98921892016-02-23 17:14:37 +0100702#if !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000703 attributes.colormap = gtk_widget_get_colormap(widget);
Bram Moolenaar98921892016-02-23 17:14:37 +0100704#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000705 attributes.event_mask = GDK_EXPOSURE_MASK;
706
Bram Moolenaar98921892016-02-23 17:14:37 +0100707#if GTK_CHECK_VERSION(3,0,0)
708 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
709#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000710 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
Bram Moolenaar98921892016-02-23 17:14:37 +0100711#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000712 child->window = gdk_window_new(form->bin_window,
713 &attributes, attributes_mask);
714 gdk_window_set_user_data(child->window, widget);
715
Bram Moolenaar98921892016-02-23 17:14:37 +0100716#if GTK_CHECK_VERSION(3,0,0)
717 {
718 GtkStyleContext * const sctx = gtk_widget_get_style_context(widget);
719
720 gtk_style_context_set_state(sctx, GTK_STATE_FLAG_NORMAL);
721# if !GTK_CHECK_VERSION(3,18,0)
722 gtk_style_context_set_background(sctx, child->window);
723# endif
724 }
725#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000726 gtk_style_set_background(widget->style,
727 child->window,
728 GTK_STATE_NORMAL);
Bram Moolenaar98921892016-02-23 17:14:37 +0100729#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000730
731 gtk_widget_set_parent_window(child->widget, child->window);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000732 /*
733 * Install signal handlers to map/unmap child->window
734 * alongside with the actual widget.
735 */
Bram Moolenaar98921892016-02-23 17:14:37 +0100736 g_signal_connect(G_OBJECT(child->widget), "map",
737 G_CALLBACK(&gtk_form_child_map), child);
738 g_signal_connect(G_OBJECT(child->widget), "unmap",
739 G_CALLBACK(&gtk_form_child_unmap), child);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000740 }
Bram Moolenaar98921892016-02-23 17:14:37 +0100741 else if (!gtk_widget_get_realized(child->widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000742 {
743 gtk_widget_set_parent_window(child->widget, form->bin_window);
744 }
745}
746
747 static void
748gtk_form_realize_child(GtkForm *form, GtkFormChild *child)
749{
750 gtk_form_attach_child_window(form, child);
751 gtk_widget_realize(child->widget);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000752}
753
754 static void
755gtk_form_position_child(GtkForm *form, GtkFormChild *child,
756 gboolean force_allocate)
757{
758 gint x;
759 gint y;
760
761 x = child->x;
762 y = child->y;
763
764 if ((x >= G_MINSHORT) && (x <= G_MAXSHORT) &&
765 (y >= G_MINSHORT) && (y <= G_MAXSHORT))
766 {
767 if (!child->mapped)
768 {
Bram Moolenaar98921892016-02-23 17:14:37 +0100769 if (gtk_widget_get_mapped(GTK_WIDGET(form))
770 && gtk_widget_get_visible(child->widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000771 {
Bram Moolenaar98921892016-02-23 17:14:37 +0100772 if (!gtk_widget_get_mapped(child->widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000773 gtk_widget_map(child->widget);
774
775 child->mapped = TRUE;
776 force_allocate = TRUE;
777 }
778 }
779
780 if (force_allocate)
781 {
782 GtkAllocation allocation;
Bram Moolenaar98921892016-02-23 17:14:37 +0100783 GtkRequisition requisition;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000784
Bram Moolenaar664323e2018-09-18 22:30:07 +0200785#if GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar98921892016-02-23 17:14:37 +0100786 gtk_widget_get_preferred_size(child->widget, &requisition, NULL);
Bram Moolenaar664323e2018-09-18 22:30:07 +0200787#else
788 requisition = child->widget->requisition;
Bram Moolenaar98921892016-02-23 17:14:37 +0100789#endif
790
Bram Moolenaar98921892016-02-23 17:14:37 +0100791 if (!gtk_widget_get_has_window(child->widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000792 {
793 if (child->window)
794 {
Bram Moolenaar98921892016-02-23 17:14:37 +0100795 gdk_window_move_resize(child->window,
796 x, y,
797 requisition.width,
798 requisition.height);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000799 }
800
801 allocation.x = 0;
802 allocation.y = 0;
803 }
804 else
805 {
806 allocation.x = x;
807 allocation.y = y;
808 }
809
Bram Moolenaar98921892016-02-23 17:14:37 +0100810 allocation.width = requisition.width;
811 allocation.height = requisition.height;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000812
813 gtk_widget_size_allocate(child->widget, &allocation);
814 }
815 }
816 else
817 {
818 if (child->mapped)
819 {
820 child->mapped = FALSE;
821
Bram Moolenaar98921892016-02-23 17:14:37 +0100822 if (gtk_widget_get_mapped(child->widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000823 gtk_widget_unmap(child->widget);
824 }
825 }
826}
827
828 static void
829gtk_form_position_children(GtkForm *form)
830{
831 GList *tmp_list;
832
833 for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next)
834 gtk_form_position_child(form, tmp_list->data, FALSE);
835}
836
Bram Moolenaar071d4272004-06-13 20:20:40 +0000837 void
838gtk_form_move_resize(GtkForm *form, GtkWidget *widget,
839 gint x, gint y, gint w, gint h)
840{
Bram Moolenaar98921892016-02-23 17:14:37 +0100841#if GTK_CHECK_VERSION(3,0,0)
842 gtk_widget_set_size_request(widget, w, h);
843#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000844 widget->requisition.width = w;
845 widget->requisition.height = h;
Bram Moolenaar98921892016-02-23 17:14:37 +0100846#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000847
848 gtk_form_move(form, widget, x, y);
849}
850
851 static void
852gtk_form_send_configure(GtkForm *form)
853{
854 GtkWidget *widget;
855 GdkEventConfigure event;
Bram Moolenaar664323e2018-09-18 22:30:07 +0200856 GtkAllocation allocation;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000857
858 widget = GTK_WIDGET(form);
859
Bram Moolenaar664323e2018-09-18 22:30:07 +0200860 gtk_widget_get_allocation(widget, &allocation);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000861 event.type = GDK_CONFIGURE;
Bram Moolenaar98921892016-02-23 17:14:37 +0100862 event.window = gtk_widget_get_window(widget);
Bram Moolenaar664323e2018-09-18 22:30:07 +0200863 event.x = allocation.x;
864 event.y = allocation.y;
865 event.width = allocation.width;
866 event.height = allocation.height;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000867
Bram Moolenaar071d4272004-06-13 20:20:40 +0000868 gtk_main_do_event((GdkEvent*)&event);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000869}
870
Bram Moolenaar071d4272004-06-13 20:20:40 +0000871 static void
Bram Moolenaarb85cb212009-05-17 14:24:23 +0000872gtk_form_child_map(GtkWidget *widget UNUSED, gpointer user_data)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000873{
874 GtkFormChild *child;
875
876 child = (GtkFormChild *)user_data;
877
878 child->mapped = TRUE;
879 gdk_window_show(child->window);
880}
881
Bram Moolenaar071d4272004-06-13 20:20:40 +0000882 static void
Bram Moolenaarb85cb212009-05-17 14:24:23 +0000883gtk_form_child_unmap(GtkWidget *widget UNUSED, gpointer user_data)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000884{
885 GtkFormChild *child;
886
887 child = (GtkFormChild *)user_data;
888
889 child->mapped = FALSE;
890 gdk_window_hide(child->window);
891}