]> Pileus Git - ~andy/gtk/blob - gtk/gtkvpaned.c
Initial revision
[~andy/gtk] / gtk / gtkvpaned.c
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 Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 #include "gtkvpaned.h"
19 #include "gtkmain.h"
20 #include "gtksignal.h"
21
22 static void gtk_vpaned_class_init       (GtkVPanedClass *klass);
23 static void gtk_vpaned_init             (GtkVPaned      *vpaned);
24 static void gtk_vpaned_size_request     (GtkWidget      *widget,
25                                          GtkRequisition *requisition);
26 static void gtk_vpaned_size_allocate    (GtkWidget          *widget,
27                                          GtkAllocation      *allocation);
28 static void gtk_vpaned_draw             (GtkWidget    *widget,
29                                          GdkRectangle *area);
30 static void gtk_vpaned_xor_line         (GtkPaned *paned);
31 static gint gtk_vpaned_button_press     (GtkWidget *widget,
32                                          GdkEventButton *event);
33 static gint gtk_vpaned_button_release   (GtkWidget *widget,
34                                          GdkEventButton *event);
35 static gint gtk_vpaned_motion           (GtkWidget *widget,
36                                          GdkEventMotion *event);
37
38 guint
39 gtk_vpaned_get_type ()
40 {
41   static guint vpaned_type = 0;
42
43   if (!vpaned_type)
44     {
45       GtkTypeInfo vpaned_info =
46       {
47         "GtkVPaned",
48         sizeof (GtkVPaned),
49         sizeof (GtkVPanedClass),
50         (GtkClassInitFunc) gtk_vpaned_class_init,
51         (GtkObjectInitFunc) gtk_vpaned_init,
52         (GtkArgFunc) NULL,
53       };
54
55       vpaned_type = gtk_type_unique (gtk_paned_get_type (), &vpaned_info);
56     }
57
58   return vpaned_type;
59 }
60
61 static void
62 gtk_vpaned_class_init (GtkVPanedClass *class)
63 {
64   GtkWidgetClass *widget_class;
65
66   widget_class = (GtkWidgetClass*) class;
67
68   widget_class->size_request = gtk_vpaned_size_request;
69   widget_class->size_allocate = gtk_vpaned_size_allocate;
70   widget_class->draw = gtk_vpaned_draw;
71   widget_class->button_press_event = gtk_vpaned_button_press;
72   widget_class->button_release_event = gtk_vpaned_button_release;
73   widget_class->motion_notify_event = gtk_vpaned_motion;
74 }
75
76 static void
77 gtk_vpaned_init (GtkVPaned *vpaned)
78 {
79 }
80
81 GtkWidget*
82 gtk_vpaned_new ()
83 {
84   GtkVPaned *vpaned;
85
86   vpaned = gtk_type_new (gtk_vpaned_get_type ());
87
88   return GTK_WIDGET (vpaned);
89 }
90
91 static void
92 gtk_vpaned_size_request (GtkWidget      *widget,
93                          GtkRequisition *requisition)
94 {
95   GtkPaned *paned;
96
97   g_return_if_fail (widget != NULL);
98   g_return_if_fail (GTK_IS_VPANED (widget));
99   g_return_if_fail (requisition != NULL);
100
101   paned = GTK_PANED (widget);
102   requisition->width = 0;
103   requisition->height = 0;
104
105   if (paned->child1 && GTK_WIDGET_VISIBLE (paned->child1))
106     {
107       gtk_widget_size_request (paned->child1, &paned->child1->requisition);
108
109       requisition->height = paned->child1->requisition.height;
110       requisition->width = paned->child1->requisition.width;
111     }
112
113   if (paned->child2 && GTK_WIDGET_VISIBLE (paned->child2))
114     {
115       gtk_widget_size_request (paned->child2, &paned->child2->requisition);
116
117       requisition->width = MAX (requisition->width,
118                                 paned->child2->requisition.width);
119       requisition->height += paned->child2->requisition.height;
120     }
121
122   requisition->height += GTK_CONTAINER (paned)->border_width * 2 + paned->gutter_size;
123   requisition->width += GTK_CONTAINER (paned)->border_width * 2;
124 }
125
126 static void
127 gtk_vpaned_size_allocate (GtkWidget     *widget,
128                           GtkAllocation *allocation)
129 {
130   GtkPaned *paned;
131   GtkAllocation child1_allocation;
132   GtkAllocation child2_allocation;
133   guint16 border_width;
134
135   g_return_if_fail (widget != NULL);
136   g_return_if_fail (GTK_IS_VPANED (widget));
137   g_return_if_fail (allocation != NULL);
138
139   widget->allocation = *allocation;
140
141   paned = GTK_PANED (widget);
142   border_width = GTK_CONTAINER (paned)->border_width;
143
144   if (!paned->position_set)
145     {
146       if (paned->child1 && GTK_WIDGET_VISIBLE (paned->child1))
147         paned->child1_size = paned->child1->requisition.height;
148       else
149         paned->child1_size = 0;
150     }
151
152   /* Move the handle first so we don't get extra expose events */
153
154   paned->handle_xpos = allocation->x + allocation->width - border_width - 2 * paned->handle_size;
155   paned->handle_ypos = allocation->y + paned->child1_size + border_width + paned->gutter_size / 2 - paned->handle_size / 2;
156
157   if (GTK_WIDGET_REALIZED (widget))
158     {
159       gdk_window_move (paned->handle, paned->handle_xpos, paned->handle_ypos);
160       gdk_window_raise (paned->handle);
161     }
162
163   if (GTK_WIDGET_MAPPED (widget))
164     {
165       gdk_window_clear_area (widget->window,
166                              paned->groove_rectangle.x,
167                              paned->groove_rectangle.y,
168                              paned->groove_rectangle.width,
169                              paned->groove_rectangle.height);
170     }
171   
172   child1_allocation.width = child2_allocation.width = allocation->width - border_width * 2;
173   child1_allocation.height = paned->child1_size;
174   child1_allocation.x = child2_allocation.x = allocation->x + border_width;
175   child1_allocation.y = allocation->y + border_width;
176   
177   paned->groove_rectangle.y = child1_allocation.y 
178     + child1_allocation.height + paned->gutter_size / 2 - 1;
179   paned->groove_rectangle.x = allocation->x;
180   paned->groove_rectangle.height = 2;
181   paned->groove_rectangle.width = allocation->width;
182   
183   child2_allocation.y = paned->groove_rectangle.y + paned->gutter_size / 2 + 1;
184   child2_allocation.height = allocation->y + allocation->height 
185     - child2_allocation.y - border_width;
186   
187   /* Now allocate the childen, making sure, when resizing not to
188    * overlap the windows */
189   if (GTK_WIDGET_MAPPED(widget) &&
190       paned->child1 && GTK_WIDGET_VISIBLE (paned->child1) &&
191       paned->child1->allocation.height < child1_allocation.height)
192     {
193       if (paned->child2 && GTK_WIDGET_VISIBLE (paned->child2))
194         gtk_widget_size_allocate (paned->child2, &child2_allocation);
195       gtk_widget_size_allocate (paned->child1, &child1_allocation);      
196     }
197   else
198     {
199       if (paned->child1 && GTK_WIDGET_VISIBLE (paned->child1))
200         gtk_widget_size_allocate (paned->child1, &child1_allocation);
201       if (paned->child2 && GTK_WIDGET_VISIBLE (paned->child2))
202         gtk_widget_size_allocate (paned->child2, &child2_allocation);
203     }
204 }
205
206 static void
207 gtk_vpaned_draw (GtkWidget    *widget,
208                 GdkRectangle *area)
209 {
210   GtkPaned *paned;
211   GdkRectangle child_area;
212   guint16 border_width;
213
214   g_return_if_fail (widget != NULL);
215   g_return_if_fail (GTK_IS_PANED (widget));
216
217   if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget))
218     {
219       paned = GTK_PANED (widget);
220       border_width = GTK_CONTAINER (paned)->border_width;
221
222       if (paned->child1 &&
223           gtk_widget_intersect (paned->child1, area, &child_area))
224         gtk_widget_draw (paned->child1, &child_area);
225       if (paned->child2 &&
226           gtk_widget_intersect (paned->child2, area, &child_area))
227         gtk_widget_draw (paned->child2, &child_area);
228
229       gdk_draw_line (widget->window,
230                      widget->style->dark_gc[widget->state],
231                      widget->allocation.x,
232                      widget->allocation.y + border_width + paned->child1_size + paned->gutter_size / 2 - 1,
233                      widget->allocation.x + widget->allocation.width - 1,
234                      widget->allocation.y + border_width + paned->child1_size + paned->gutter_size / 2 - 1);
235       gdk_draw_line (widget->window,
236                      widget->style->light_gc[widget->state],
237                      widget->allocation.x,
238                      widget->allocation.y + border_width + paned->child1_size + paned->gutter_size / 2,
239                      widget->allocation.x + widget->allocation.width - 1,
240                      widget->allocation.y + border_width + paned->child1_size + paned->gutter_size / 2);
241     }
242 }
243
244 static void
245 gtk_vpaned_xor_line (GtkPaned *paned)
246 {
247   GtkWidget *widget;
248   GdkGCValues values;
249   guint16 ypos;
250
251   widget = GTK_WIDGET(paned);
252
253   if (!paned->xor_gc)
254     {
255       values.foreground = widget->style->white;
256       values.function = GDK_XOR;
257       values.subwindow_mode = GDK_INCLUDE_INFERIORS;
258       paned->xor_gc = gdk_gc_new_with_values (widget->window,
259                                               &values,
260                                               GDK_GC_FOREGROUND |
261                                               GDK_GC_FUNCTION |
262                                               GDK_GC_SUBWINDOW);
263     }
264
265   ypos = widget->allocation.y + paned->child1_size
266     + GTK_CONTAINER (paned)->border_width + paned->gutter_size / 2;
267
268   gdk_draw_line (widget->window, paned->xor_gc,
269                  widget->allocation.x,
270                  ypos,
271                  widget->allocation.x + widget->allocation.width - 1,
272                  ypos);
273 }
274
275 static gint
276 gtk_vpaned_button_press (GtkWidget *widget, GdkEventButton *event)
277 {
278   GtkPaned *paned;
279
280   g_return_val_if_fail (widget != NULL,FALSE);
281   g_return_val_if_fail (GTK_IS_PANED (widget),FALSE);
282
283   paned = GTK_PANED (widget);
284
285   if (!paned->in_drag &&
286       (event->window == paned->handle) && (event->button == 1))
287     {
288       paned->in_drag = TRUE;
289       /* We need a server grab here, not gtk_grab_add(), since
290        * we don't want to pass events on to the widget's children */
291       gdk_pointer_grab (paned->handle, FALSE,
292                         GDK_POINTER_MOTION_HINT_MASK 
293                         | GDK_BUTTON1_MOTION_MASK 
294                         | GDK_BUTTON_RELEASE_MASK,
295                         NULL, NULL, event->time);
296       paned->child1_size += event->y - paned->handle_size / 2;
297       paned->child1_size = CLAMP (paned->child1_size, 0,
298                                   widget->allocation.height - paned->gutter_size
299                                   - 2 * GTK_CONTAINER (paned)->border_width);
300       gtk_vpaned_xor_line (paned);
301     }
302
303   return TRUE;
304 }
305
306 static gint
307 gtk_vpaned_button_release (GtkWidget *widget, GdkEventButton *event)
308 {
309   GtkPaned *paned;
310
311   g_return_val_if_fail (widget != NULL, FALSE);
312   g_return_val_if_fail (GTK_IS_PANED (widget), FALSE);
313
314   paned = GTK_PANED (widget);
315
316   if (paned->in_drag && (event->button == 1))
317     {
318       gtk_vpaned_xor_line (paned);
319       paned->in_drag = FALSE;
320       paned->position_set = TRUE;
321       gdk_pointer_ungrab (event->time);
322       gtk_widget_queue_resize (GTK_WIDGET (paned));
323     }
324
325   return TRUE;
326 }
327
328 static gint
329 gtk_vpaned_motion (GtkWidget *widget, GdkEventMotion *event)
330 {
331   GtkPaned *paned;
332   gint y;
333
334   g_return_val_if_fail (widget != NULL, FALSE);
335   g_return_val_if_fail (GTK_IS_PANED (widget), FALSE);
336
337   if (event->is_hint || event->window != widget->window)
338       gtk_widget_get_pointer(widget, NULL, &y);
339   else
340       y = event->y;
341
342   paned = GTK_PANED (widget);
343
344   if (paned->in_drag)
345     {
346       gtk_vpaned_xor_line (paned);
347       paned->child1_size = y - GTK_CONTAINER (paned)->border_width -
348         paned->gutter_size/2;
349       paned->child1_size = CLAMP (paned->child1_size, 0,
350                                   widget->allocation.height - paned->gutter_size
351                                   - 2 * GTK_CONTAINER (paned)->border_width);
352       gtk_vpaned_xor_line (paned);
353     }
354
355   return TRUE;
356 }