source: trunk/third/gtk/gtk/gtkpaned.c @ 15781

Revision 15781, 17.4 KB checked in by ghudson, 24 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r15780, which included commits to RCS files with non-trunk default branches.
Line 
1/* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 */
19
20/*
21 * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
22 * file for a list of people on the GTK+ Team.  See the ChangeLog
23 * files for a list of changes.  These files are distributed with
24 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
25 */
26
27#include "gtkpaned.h"
28#include "gtkhpaned.h"
29
30enum {
31  ARG_0,
32  ARG_HANDLE_SIZE,
33  ARG_GUTTER_SIZE
34};
35
36static void gtk_paned_class_init (GtkPanedClass    *klass);
37static void gtk_paned_init       (GtkPaned         *paned);
38static void gtk_paned_set_arg    (GtkObject        *object,
39                                  GtkArg           *arg,
40                                  guint             arg_id);
41static void gtk_paned_get_arg    (GtkObject        *object,
42                                  GtkArg           *arg,
43                                  guint             arg_id);
44static void gtk_paned_realize    (GtkWidget *widget);
45static void gtk_paned_map        (GtkWidget      *widget);
46static void gtk_paned_unmap      (GtkWidget      *widget);
47static void gtk_paned_unrealize  (GtkWidget *widget);
48static gint gtk_paned_expose     (GtkWidget      *widget,
49                                  GdkEventExpose *event);
50static void gtk_paned_add        (GtkContainer   *container,
51                                  GtkWidget      *widget);
52static void gtk_paned_remove     (GtkContainer   *container,
53                                  GtkWidget      *widget);
54static void gtk_paned_forall     (GtkContainer   *container,
55                                  gboolean        include_internals,
56                                  GtkCallback     callback,
57                                  gpointer        callback_data);
58static GtkType gtk_paned_child_type (GtkContainer *container);
59
60
61static GtkContainerClass *parent_class = NULL;
62
63
64GtkType
65gtk_paned_get_type (void)
66{
67  static GtkType paned_type = 0;
68 
69  if (!paned_type)
70    {
71      static const GtkTypeInfo paned_info =
72      {
73        "GtkPaned",
74        sizeof (GtkPaned),
75        sizeof (GtkPanedClass),
76        (GtkClassInitFunc) gtk_paned_class_init,
77        (GtkObjectInitFunc) gtk_paned_init,
78        /* reserved_1 */ NULL,
79        /* reserved_2 */ NULL,
80        (GtkClassInitFunc) NULL,
81      };
82     
83      paned_type = gtk_type_unique (GTK_TYPE_CONTAINER, &paned_info);
84    }
85 
86  return paned_type;
87}
88
89static void
90gtk_paned_class_init (GtkPanedClass *class)
91{
92  GtkObjectClass *object_class;
93  GtkWidgetClass *widget_class;
94  GtkContainerClass *container_class;
95 
96  object_class = (GtkObjectClass*) class;
97  widget_class = (GtkWidgetClass*) class;
98  container_class = (GtkContainerClass*) class;
99 
100  parent_class = gtk_type_class (GTK_TYPE_CONTAINER);
101 
102  object_class->set_arg = gtk_paned_set_arg;
103  object_class->get_arg = gtk_paned_get_arg;
104
105  widget_class->realize = gtk_paned_realize;
106  widget_class->map = gtk_paned_map;
107  widget_class->unmap = gtk_paned_unmap;
108  widget_class->unrealize = gtk_paned_unrealize;
109  widget_class->expose_event = gtk_paned_expose;
110 
111  container_class->add = gtk_paned_add;
112  container_class->remove = gtk_paned_remove;
113  container_class->forall = gtk_paned_forall;
114  container_class->child_type = gtk_paned_child_type;
115
116  gtk_object_add_arg_type ("GtkPaned::handle_size", GTK_TYPE_UINT,
117                           GTK_ARG_READWRITE, ARG_HANDLE_SIZE);
118  gtk_object_add_arg_type ("GtkPaned::gutter_size", GTK_TYPE_UINT,
119                           GTK_ARG_READWRITE, ARG_GUTTER_SIZE);
120}
121
122static GtkType
123gtk_paned_child_type (GtkContainer *container)
124{
125  if (!GTK_PANED (container)->child1 || !GTK_PANED (container)->child2)
126    return GTK_TYPE_WIDGET;
127  else
128    return GTK_TYPE_NONE;
129}
130
131static void
132gtk_paned_init (GtkPaned *paned)
133{
134  GTK_WIDGET_UNSET_FLAGS (paned, GTK_NO_WINDOW);
135 
136  paned->child1 = NULL;
137  paned->child2 = NULL;
138  paned->handle = NULL;
139  paned->xor_gc = NULL;
140 
141  paned->handle_size = 10;
142  paned->gutter_size = 6;
143  paned->position_set = FALSE;
144  paned->last_allocation = -1;
145  paned->in_drag = FALSE;
146 
147  paned->handle_xpos = -1;
148  paned->handle_ypos = -1;
149}
150
151static void
152gtk_paned_set_arg (GtkObject *object,
153                   GtkArg    *arg,
154                   guint      arg_id)
155{
156  GtkPaned *paned = GTK_PANED (object);
157 
158  switch (arg_id)
159    {
160    case ARG_HANDLE_SIZE:
161      gtk_paned_set_handle_size (paned, GTK_VALUE_UINT (*arg));
162      break;
163    case ARG_GUTTER_SIZE:
164      gtk_paned_set_gutter_size (paned, GTK_VALUE_UINT (*arg));
165      break;
166    }
167}
168
169static void
170gtk_paned_get_arg (GtkObject *object,
171                   GtkArg    *arg,
172                   guint      arg_id)
173{
174  GtkPaned *paned = GTK_PANED (object);
175 
176  switch (arg_id)
177    {
178    case ARG_HANDLE_SIZE:
179      GTK_VALUE_UINT (*arg) = paned->handle_size;
180      break;
181    case ARG_GUTTER_SIZE:
182      GTK_VALUE_UINT (*arg) = paned->gutter_size;
183      break;
184    default:
185      arg->type = GTK_TYPE_INVALID;
186      break;
187    }
188}
189
190static void
191gtk_paned_realize (GtkWidget *widget)
192{
193  GtkPaned *paned = GTK_PANED (widget);
194  GdkWindowAttr attributes;
195  gint attributes_mask;
196  gboolean handle_full_size = _gtk_paned_is_handle_full_size (paned);
197 
198  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
199 
200  attributes.x = widget->allocation.x;
201  attributes.y = widget->allocation.y;
202  attributes.width = widget->allocation.width;
203  attributes.height = widget->allocation.height;
204  attributes.window_type = GDK_WINDOW_CHILD;
205  attributes.wclass = GDK_INPUT_OUTPUT;
206  attributes.visual = gtk_widget_get_visual (widget);
207  attributes.colormap = gtk_widget_get_colormap (widget);
208  attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
209  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
210 
211  widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
212                                   &attributes, attributes_mask);
213  gdk_window_set_user_data (widget->window, paned);
214
215  if (handle_full_size)
216    {
217      GdkRectangle rect;
218
219      _gtk_paned_get_handle_rect (paned, &rect);
220
221      attributes.x = rect.x;
222      attributes.y = rect.y;
223      attributes.width = rect.width;
224      attributes.height = rect.height;
225    }
226  else
227    {
228      attributes.x = paned->handle_xpos;
229      attributes.y = paned->handle_ypos;
230      attributes.width = paned->handle_size;
231      attributes.height = paned->handle_size;
232    }
233
234  attributes.cursor = gdk_cursor_new (GTK_IS_HPANED (paned) ?
235                                      GDK_SB_H_DOUBLE_ARROW :
236                                      GDK_SB_V_DOUBLE_ARROW);
237                                     
238  attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
239                            GDK_BUTTON_RELEASE_MASK |
240                            GDK_POINTER_MOTION_MASK |
241                            GDK_POINTER_MOTION_HINT_MASK);
242  attributes_mask |= GDK_WA_CURSOR;
243 
244  paned->handle = gdk_window_new (widget->window,
245                                  &attributes, attributes_mask);
246  gdk_window_set_user_data (paned->handle, paned);
247  gdk_cursor_destroy (attributes.cursor);
248 
249  widget->style = gtk_style_attach (widget->style, widget->window);
250 
251  gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
252  gtk_style_set_background (widget->style, paned->handle, GTK_STATE_NORMAL);
253
254  gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
255 
256  gdk_window_show (paned->handle);
257}
258
259static void
260gtk_paned_map (GtkWidget *widget)
261{
262  GtkPaned *paned;
263 
264  g_return_if_fail (widget != NULL);
265  g_return_if_fail (GTK_IS_PANED (widget));
266 
267  GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
268  paned = GTK_PANED (widget);
269 
270  if (paned->child1 &&
271      GTK_WIDGET_VISIBLE (paned->child1) &&
272      !GTK_WIDGET_MAPPED (paned->child1))
273    gtk_widget_map (paned->child1);
274  if (paned->child2 &&
275      GTK_WIDGET_VISIBLE (paned->child2) &&
276      !GTK_WIDGET_MAPPED (paned->child2))
277    gtk_widget_map (paned->child2);
278
279  gdk_window_show (widget->window);
280}
281
282static void
283gtk_paned_unmap (GtkWidget *widget)
284{
285  g_return_if_fail (widget != NULL);
286  g_return_if_fail (GTK_IS_PANED (widget));
287 
288  GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
289 
290  gdk_window_hide (widget->window);
291}
292
293static void
294gtk_paned_unrealize (GtkWidget *widget)
295{
296  GtkPaned *paned;
297 
298  g_return_if_fail (widget != NULL);
299  g_return_if_fail (GTK_IS_PANED (widget));
300 
301  paned = GTK_PANED (widget);
302 
303  if (paned->xor_gc)
304    {
305      gdk_gc_destroy (paned->xor_gc);
306      paned->xor_gc = NULL;
307    }
308 
309  if (paned->handle)
310    {
311      gdk_window_set_user_data (paned->handle, NULL);
312      gdk_window_destroy (paned->handle);
313      paned->handle = NULL;
314    }
315 
316  if (GTK_WIDGET_CLASS (parent_class)->unrealize)
317    (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
318}
319
320static gint
321gtk_paned_expose (GtkWidget      *widget,
322                  GdkEventExpose *event)
323{
324  GtkPaned *paned = GTK_PANED (widget);
325  gboolean handle_full_size = _gtk_paned_is_handle_full_size (paned);
326  GdkEventExpose child_event;
327 
328  if (GTK_WIDGET_DRAWABLE (widget))
329    {
330      /* An expose event for the handle */
331      if (event->window == paned->handle)
332        {
333          gint width, height;
334          gchar *detail;
335         
336          gdk_window_get_size (paned->handle, &width, &height);
337
338          if (handle_full_size)
339            detail = GTK_IS_HPANED (widget) ? "hpaned" : "vpaned";
340          else
341            detail = "paned";
342         
343          gtk_paint_box (widget->style, paned->handle,
344                         GTK_WIDGET_STATE(widget),
345                         GTK_SHADOW_OUT,
346                         &event->area, widget, detail,
347                         0, 0,
348                         width, height);
349        }
350      else
351        {
352          child_event = *event;
353          if (paned->child1 &&
354              GTK_WIDGET_NO_WINDOW (paned->child1) &&
355              gtk_widget_intersect (paned->child1, &event->area, &child_event.area))
356            gtk_widget_event (paned->child1, (GdkEvent*) &child_event);
357         
358          if (paned->child2 &&
359              GTK_WIDGET_NO_WINDOW (paned->child2) &&
360              gtk_widget_intersect (paned->child2, &event->area, &child_event.area))
361            gtk_widget_event (paned->child2, (GdkEvent*) &child_event);
362         
363          /* redraw the groove if necessary */
364          if (gdk_rectangle_intersect (&paned->groove_rectangle,
365                                       &event->area,
366                                       &child_event.area))
367            gtk_widget_draw (widget, &child_event.area);
368        }
369    }
370  return FALSE;
371}
372
373void
374gtk_paned_add1 (GtkPaned     *paned,
375                GtkWidget    *widget)
376{
377  gtk_paned_pack1 (paned, widget, FALSE, TRUE);
378}
379
380void
381gtk_paned_add2 (GtkPaned  *paned,
382                GtkWidget *widget)
383{
384  gtk_paned_pack2 (paned, widget, TRUE, TRUE);
385}
386
387void
388gtk_paned_pack1 (GtkPaned     *paned,
389                 GtkWidget    *child,
390                 gboolean      resize,
391                 gboolean      shrink)
392{
393  g_return_if_fail (paned != NULL);
394  g_return_if_fail (GTK_IS_PANED (paned));
395  g_return_if_fail (GTK_IS_WIDGET (child));
396 
397  if (!paned->child1)
398    {
399      paned->child1 = child;
400      paned->child1_resize = resize;
401      paned->child1_shrink = shrink;
402
403      gtk_widget_set_parent (child, GTK_WIDGET (paned));
404
405      if (GTK_WIDGET_REALIZED (child->parent))
406        gtk_widget_realize (child);
407
408      if (GTK_WIDGET_VISIBLE (child->parent) && GTK_WIDGET_VISIBLE (child))
409        {
410          if (GTK_WIDGET_MAPPED (child->parent))
411            gtk_widget_map (child);
412
413          gtk_widget_queue_resize (child);
414        }
415    }
416}
417
418void
419gtk_paned_pack2 (GtkPaned  *paned,
420                 GtkWidget *child,
421                 gboolean   resize,
422                 gboolean   shrink)
423{
424  g_return_if_fail (paned != NULL);
425  g_return_if_fail (GTK_IS_PANED (paned));
426  g_return_if_fail (GTK_IS_WIDGET (child));
427 
428  if (!paned->child2)
429    {
430      paned->child2 = child;
431      paned->child2_resize = resize;
432      paned->child2_shrink = shrink;
433     
434      gtk_widget_set_parent (child, GTK_WIDGET (paned));
435
436      if (GTK_WIDGET_REALIZED (child->parent))
437        gtk_widget_realize (child);
438
439      if (GTK_WIDGET_VISIBLE (child->parent) && GTK_WIDGET_VISIBLE (child))
440        {
441          if (GTK_WIDGET_MAPPED (child->parent))
442            gtk_widget_map (child);
443
444          gtk_widget_queue_resize (child);
445        }
446    }
447}
448
449static void
450gtk_paned_add (GtkContainer *container,
451               GtkWidget    *widget)
452{
453  GtkPaned *paned;
454 
455  g_return_if_fail (container != NULL);
456  g_return_if_fail (GTK_IS_PANED (container));
457  g_return_if_fail (widget != NULL);
458 
459  paned = GTK_PANED (container);
460 
461  if (!paned->child1)
462    gtk_paned_add1 (GTK_PANED (container),widget);
463  else if (!paned->child2)
464    gtk_paned_add2 (GTK_PANED (container),widget);
465}
466
467static void
468gtk_paned_remove (GtkContainer *container,
469                  GtkWidget    *widget)
470{
471  GtkPaned *paned;
472  gboolean was_visible;
473 
474  g_return_if_fail (container != NULL);
475  g_return_if_fail (GTK_IS_PANED (container));
476  g_return_if_fail (widget != NULL);
477 
478  paned = GTK_PANED (container);
479  was_visible = GTK_WIDGET_VISIBLE (widget);
480 
481  if (paned->child1 == widget)
482    {
483      gtk_widget_unparent (widget);
484     
485      paned->child1 = NULL;
486     
487      if (was_visible && GTK_WIDGET_VISIBLE (container))
488        gtk_widget_queue_resize (GTK_WIDGET (container));
489    }
490  else if (paned->child2 == widget)
491    {
492      gtk_widget_unparent (widget);
493     
494      paned->child2 = NULL;
495     
496      if (was_visible && GTK_WIDGET_VISIBLE (container))
497        gtk_widget_queue_resize (GTK_WIDGET (container));
498    }
499}
500
501static void
502gtk_paned_forall (GtkContainer *container,
503                  gboolean      include_internals,
504                  GtkCallback   callback,
505                  gpointer      callback_data)
506{
507  GtkPaned *paned;
508 
509  g_return_if_fail (container != NULL);
510  g_return_if_fail (GTK_IS_PANED (container));
511  g_return_if_fail (callback != NULL);
512 
513  paned = GTK_PANED (container);
514 
515  if (paned->child1)
516    (* callback) (paned->child1, callback_data);
517  if (paned->child2)
518    (* callback) (paned->child2, callback_data);
519}
520
521void
522gtk_paned_set_position    (GtkPaned  *paned,
523                           gint       position)
524{
525  g_return_if_fail (paned != NULL);
526  g_return_if_fail (GTK_IS_PANED (paned));
527
528  if (position >= 0)
529    {
530      /* We don't clamp here - the assumption is that
531       * if the total allocation changes at the same time
532       * as the position, the position set is with reference
533       * to the new total size. If only the position changes,
534       * then clamping will occur in gtk_paned_compute_position()
535       */
536      paned->child1_size = position;
537      paned->position_set = TRUE;
538    }
539  else
540    paned->position_set = FALSE;
541
542  gtk_widget_queue_resize (GTK_WIDGET (paned));
543}
544
545void
546gtk_paned_set_handle_size (GtkPaned *paned,
547                           guint16   size)
548{
549  gint x,y;
550 
551  g_return_if_fail (paned != NULL);
552  g_return_if_fail (GTK_IS_PANED (paned));
553
554  if (paned->handle)
555    {
556      gdk_window_get_geometry (paned->handle, &x, &y, NULL, NULL, NULL);
557      gdk_window_move_resize (paned->handle,
558                              x + paned->handle_size / 2 - size / 2,
559                              y + paned->handle_size / 2 - size / 2,
560                              size, size);
561    }
562  paned->handle_size = size;
563}
564
565void
566gtk_paned_set_gutter_size (GtkPaned *paned,
567                           guint16   size)
568{
569  g_return_if_fail (paned != NULL);
570  g_return_if_fail (GTK_IS_PANED (paned));
571
572  paned->gutter_size = size;
573 
574  if (GTK_WIDGET_VISIBLE (GTK_WIDGET (paned)))
575    gtk_widget_queue_resize (GTK_WIDGET (paned));
576}
577
578void
579gtk_paned_compute_position (GtkPaned *paned,
580                            gint      allocation,
581                            gint      child1_req,
582                            gint      child2_req)
583{
584  g_return_if_fail (paned != NULL);
585  g_return_if_fail (GTK_IS_PANED (paned));
586
587  paned->min_position = paned->child1_shrink ? 0 : child1_req;
588
589  paned->max_position = allocation;
590  if (!paned->child2_shrink)
591    paned->max_position = MAX (1, paned->max_position - child2_req);
592
593  if (!paned->position_set)
594    {
595      if (paned->child1_resize && !paned->child2_resize)
596        paned->child1_size = MAX (1, allocation - child2_req);
597      else if (!paned->child1_resize && paned->child2_resize)
598        paned->child1_size = child1_req;
599      else if (child1_req + child2_req != 0)
600        paned->child1_size = allocation * ((gdouble)child1_req / (child1_req + child2_req));
601      else
602        paned->child1_size = allocation * 0.5;
603    }
604  else
605    {
606      /* If the position was set before the initial allocation.
607       * (paned->last_allocation <= 0) just clamp it and leave it.
608       */
609      if (paned->last_allocation > 0)
610        {
611          if (paned->child1_resize && !paned->child2_resize)
612            paned->child1_size += (allocation - paned->last_allocation);
613          else if (!(!paned->child1_resize && paned->child2_resize))
614            paned->child1_size = allocation * ((gdouble)paned->child1_size / (paned->last_allocation));
615        }
616    }
617
618  paned->child1_size = CLAMP (paned->child1_size,
619                              paned->min_position,
620                              paned->max_position);
621
622  paned->last_allocation = allocation;
623}
624
625gboolean
626_gtk_paned_is_handle_full_size (GtkPaned *paned)
627{
628  return gtk_style_get_prop_experimental (GTK_WIDGET (paned)->style,
629                                          "GtkPaned::handle_full_size",
630                                          FALSE);
631}
632
633void
634_gtk_paned_get_handle_rect (GtkPaned     *paned,
635                            GdkRectangle *rect)
636{
637  gint border_width = GTK_CONTAINER (paned)->border_width;
638  GtkAllocation *allocation = &GTK_WIDGET (paned)->allocation;
639  gint gutter_size = _gtk_paned_get_gutter_size (paned);
640     
641  if (GTK_IS_HPANED (paned))
642    {
643      rect->x = border_width + paned->child1_size;
644      rect->y = border_width;
645      rect->width = gutter_size;
646      rect->height = MAX (1, (gint)allocation->height - 2 * border_width);
647    }
648  else
649    {
650      rect->x = border_width;
651      rect->y = border_width + paned->child1_size;
652      rect->width = MAX (1, (gint)allocation->width - 2 * border_width);
653      rect->height = gutter_size;
654    }
655}
656
657gint
658_gtk_paned_get_gutter_size (GtkPaned *paned)
659{
660  gint default_size = _gtk_paned_is_handle_full_size (paned) ? 5 : paned->gutter_size;
661
662  return gtk_style_get_prop_experimental (GTK_WIDGET (paned)->style,
663                                          "GtkPaned::handle_width",
664                                          default_size);
665}
Note: See TracBrowser for help on using the repository browser.