]> Pileus Git - ~andy/gtk/blob - gtk/gtkhpaned.c
Initial revision
[~andy/gtk] / gtk / gtkhpaned.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 "gtkhpaned.h"
19 #include "gtkmain.h"
20 #include "gtksignal.h"
21
22 static void gtk_hpaned_class_init       (GtkHPanedClass *klass);
23 static void gtk_hpaned_init             (GtkHPaned      *hpaned);
24 static void gtk_hpaned_size_request     (GtkWidget      *widget,
25                                          GtkRequisition *requisition);
26 static void gtk_hpaned_size_allocate    (GtkWidget          *widget,
27                                          GtkAllocation      *allocation);
28 static void gtk_hpaned_draw             (GtkWidget    *widget,
29                                          GdkRectangle *area);
30 static void gtk_hpaned_xor_line         (GtkPaned *paned);
31 static gint gtk_hpaned_button_press     (GtkWidget *widget,
32                                          GdkEventButton *event);
33 static gint gtk_hpaned_button_release   (GtkWidget *widget,
34                                          GdkEventButton *event);
35 static gint gtk_hpaned_motion           (GtkWidget *widget,
36                                          GdkEventMotion *event);
37
38 guint
39 gtk_hpaned_get_type ()
40 {
41   static guint hpaned_type = 0;
42
43   if (!hpaned_type)
44     {
45       GtkTypeInfo hpaned_info =
46       {
47         "GtkHPaned",
48         sizeof (GtkHPaned),
49         sizeof (GtkHPanedClass),
50         (GtkClassInitFunc) gtk_hpaned_class_init,
51         (GtkObjectInitFunc) gtk_hpaned_init,
52         (GtkArgFunc) NULL,
53       };
54
55       hpaned_type = gtk_type_unique (gtk_paned_get_type (), &hpaned_info);
56     }
57
58   return hpaned_type;
59 }
60
61 static void
62 gtk_hpaned_class_init (GtkHPanedClass *class)
63 {
64   GtkWidgetClass *widget_class;
65
66   widget_class = (GtkWidgetClass*) class;
67
68   widget_class->size_request = gtk_hpaned_size_request;
69   widget_class->size_allocate = gtk_hpaned_size_allocate;
70   widget_class->draw = gtk_hpaned_draw;
71   widget_class->button_press_event = gtk_hpaned_button_press;
72   widget_class->button_release_event = gtk_hpaned_button_release;
73   widget_class->motion_notify_event = gtk_hpaned_motion;
74 }
75
76 static void
77 gtk_hpaned_init (GtkHPaned *hpaned)
78 {
79 }
80
81 GtkWidget*
82 gtk_hpaned_new ()
83 {
84   GtkHPaned *hpaned;
85
86   hpaned = gtk_type_new (gtk_hpaned_get_type ());
87
88   return GTK_WIDGET (hpaned);
89 }
90
91 static void
92 gtk_hpaned_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_HPANED (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->height = MAX(requisition->height,
118                                 paned->child2->requisition.height);
119       requisition->width += paned->child2->requisition.width;
120     }
121
122   requisition->width += GTK_CONTAINER (paned)->border_width * 2 + paned->gutter_size;
123   requisition->height += GTK_CONTAINER (paned)->border_width * 2;
124 }
125
126 static void
127 gtk_hpaned_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_HPANED (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.width;
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 + paned->child1_size + border_width + paned->gutter_size / 2 - paned->handle_size / 2;
155   paned->handle_ypos = allocation->y + allocation->height - border_width - 2*paned->handle_size;
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.height = child2_allocation.height = allocation->height - border_width * 2;
173   child1_allocation.width = paned->child1_size;
174   child1_allocation.x = allocation->x + border_width;
175   child1_allocation.y = child2_allocation.y = allocation->y + border_width;
176   
177   paned->groove_rectangle.x = child1_allocation.x 
178     + child1_allocation.width + paned->gutter_size / 2 - 1;
179   paned->groove_rectangle.y = allocation->y;
180   paned->groove_rectangle.width = 2;
181   paned->groove_rectangle.height = allocation->height;
182       
183   child2_allocation.x = paned->groove_rectangle.x + paned->gutter_size / 2 + 1;
184   child2_allocation.width = allocation->x + allocation->width
185     - child2_allocation.x - 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.width < child1_allocation.width)
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_hpaned_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 + border_width + paned->child1_size + paned->gutter_size / 2 - 1,
232                      widget->allocation.y,
233                      widget->allocation.x + border_width + paned->child1_size + paned->gutter_size / 2 - 1,
234                      widget->allocation.y + widget->allocation.height - 1);
235       gdk_draw_line (widget->window,
236                      widget->style->light_gc[widget->state],
237                      widget->allocation.x + border_width + paned->child1_size + paned->gutter_size / 2,
238                      widget->allocation.y,
239                      widget->allocation.x + border_width + paned->child1_size + paned->gutter_size / 2,
240                      widget->allocation.y + widget->allocation.height - 1);
241     }
242 }
243
244 static void
245 gtk_hpaned_xor_line (GtkPaned *paned)
246 {
247   GtkWidget *widget;
248   GdkGCValues values;
249   guint16 xpos;
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   xpos = widget->allocation.x + paned->child1_size
266     + GTK_CONTAINER(paned)->border_width + paned->gutter_size / 2;
267
268   gdk_draw_line (widget->window, paned->xor_gc,
269                  xpos,
270                  widget->allocation.y,
271                  xpos,
272                  widget->allocation.y + widget->allocation.height - 1);
273 }
274
275 static gint
276 gtk_hpaned_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->x - paned->handle_size / 2;
297       paned->child1_size = CLAMP (paned->child1_size, 0,
298                                   widget->allocation.width - paned->gutter_size
299                                   - 2 * GTK_CONTAINER (paned)->border_width);
300       gtk_hpaned_xor_line (paned);
301     }
302
303   return TRUE;
304 }
305
306 static gint
307 gtk_hpaned_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_hpaned_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_hpaned_motion (GtkWidget *widget, GdkEventMotion *event)
330 {
331   GtkPaned *paned;
332   gint x;
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, &x, NULL);
339   else
340     x = event->x;
341
342   paned = GTK_PANED (widget);
343
344   if (paned->in_drag)
345     {
346       gtk_hpaned_xor_line (paned);
347       paned->child1_size = x - GTK_CONTAINER (paned)->border_width - paned->gutter_size / 2;
348       paned->child1_size = CLAMP (paned->child1_size, 0,
349                                   widget->allocation.width - paned->gutter_size
350                                   - 2 * GTK_CONTAINER (paned)->border_width);
351       gtk_hpaned_xor_line (paned);
352     }
353
354   return TRUE;
355 }