1 /* -*- Mode: C; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 3 -*- */
2 
3 /*
4  * GImageView
5  * Copyright (C) 2001 Takuro Ashie
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  *
21  * $Id: gimv_vpaned.c,v 1.3 2004/03/07 11:53:31 makeinu Exp $
22  */
23 
24 /*
25  * These codes are taken from gThumb.
26  * gThumb code Copyright (C) 2001 The Free Software Foundation, Inc.
27  * gThumb author: Paolo Bacchilega
28  */
29 
30 #ifdef HAVE_CONFIG_H
31 #  include "config.h"
32 #endif
33 
34 #ifndef USE_NORMAL_PANED
35 
36 #include "gimv_vpaned.h"
37 #include <gtk/gtkmain.h>
38 #include <gtk/gtksignal.h>
39 
40 static void gimv_vpaned_class_init       (GimvVPanedClass *klass);
41 static void gimv_vpaned_init             (GimvVPaned      *vpaned);
42 static void gimv_vpaned_size_request     (GtkWidget      *widget,
43                                           GtkRequisition *requisition);
44 static void gimv_vpaned_size_allocate    (GtkWidget      *widget,
45                                           GtkAllocation  *allocation);
46 static void gimv_vpaned_draw             (GtkWidget    *widget,
47                                           GdkRectangle *area);
48 static void gimv_vpaned_xor_line         (GimvPaned *paned);
49 static gint gimv_vpaned_button_press     (GtkWidget *widget,
50                                           GdkEventButton *event);
51 static gint gimv_vpaned_button_release   (GtkWidget *widget,
52                                           GdkEventButton *event);
53 
54 GtkType
gimv_vpaned_get_type(void)55 gimv_vpaned_get_type (void)
56 {
57    static GtkType vpaned_type = 0;
58 
59    if (!vpaned_type) {
60       static const GtkTypeInfo vpaned_info = {
61          "GimvVPaned",
62          sizeof (GimvVPaned),
63          sizeof (GimvVPanedClass),
64          (GtkClassInitFunc) gimv_vpaned_class_init,
65          (GtkObjectInitFunc) gimv_vpaned_init,
66          /* reserved_1 */ NULL,
67          /* reserved_2 */ NULL,
68          (GtkClassInitFunc) NULL,
69       };
70       vpaned_type = gtk_type_unique (gimv_paned_get_type (),
71                                      &vpaned_info);
72    }
73    return vpaned_type;
74 }
75 
76 
77 static void
gimv_vpaned_class_init(GimvVPanedClass * class)78 gimv_vpaned_class_init (GimvVPanedClass *class)
79 {
80    GtkWidgetClass *widget_class;
81    GimvPanedClass *paned_class;
82 
83    widget_class = (GtkWidgetClass*) class;
84    paned_class = (GimvPanedClass*) class;
85 
86    widget_class->size_request = gimv_vpaned_size_request;
87    widget_class->size_allocate = gimv_vpaned_size_allocate;
88 #ifndef USE_GTK2
89    widget_class->draw = gimv_vpaned_draw;
90 #endif
91    widget_class->button_press_event = gimv_vpaned_button_press;
92    widget_class->button_release_event = gimv_vpaned_button_release;
93 
94    paned_class->xor_line = gimv_vpaned_xor_line;
95 }
96 
97 
98 static void
gimv_vpaned_init(GimvVPaned * vpaned)99 gimv_vpaned_init (GimvVPaned *vpaned)
100 {
101    GIMV_PANED (vpaned)->horizontal = FALSE;
102 }
103 
104 
105 GtkWidget*
gimv_vpaned_new(void)106 gimv_vpaned_new (void)
107 {
108    GimvVPaned *vpaned;
109 
110    vpaned = gtk_type_new (gimv_vpaned_get_type ());
111 
112    return GTK_WIDGET (vpaned);
113 }
114 
115 
116 static void
gimv_vpaned_size_request(GtkWidget * widget,GtkRequisition * requisition)117 gimv_vpaned_size_request (GtkWidget      *widget,
118                           GtkRequisition *requisition)
119 {
120    GimvPaned *paned;
121    GtkRequisition child_requisition;
122 
123    g_return_if_fail (widget != NULL);
124    g_return_if_fail (GIMV_IS_VPANED (widget));
125    g_return_if_fail (requisition != NULL);
126 
127    paned = GIMV_PANED (widget);
128    requisition->width = 0;
129    requisition->height = 0;
130 
131    if (paned->child1 && GTK_WIDGET_VISIBLE (paned->child1)) {
132       gtk_widget_size_request (paned->child1, &child_requisition);
133 
134       requisition->height = child_requisition.height;
135       requisition->width = child_requisition.width;
136    }
137 
138    if (paned->child2 && GTK_WIDGET_VISIBLE (paned->child2)) {
139       gtk_widget_size_request (paned->child2, &child_requisition);
140 
141       requisition->width = MAX (requisition->width,
142                                 child_requisition.width);
143       requisition->height += child_requisition.height;
144    }
145 
146    requisition->height += (GTK_CONTAINER (paned)->border_width * 2
147                            + paned->gutter_size);
148    requisition->width += GTK_CONTAINER (paned)->border_width * 2;
149 }
150 
151 
152 static void
gimv_vpaned_size_allocate(GtkWidget * widget,GtkAllocation * allocation)153 gimv_vpaned_size_allocate (GtkWidget     *widget,
154                            GtkAllocation *allocation)
155 {
156    GimvPaned *paned;
157    GtkRequisition child1_requisition;
158    GtkRequisition child2_requisition;
159    GtkAllocation child1_allocation;
160    GtkAllocation child2_allocation;
161    gint border_width, gutter_size;
162 
163    g_return_if_fail (widget != NULL);
164    g_return_if_fail (GIMV_IS_VPANED (widget));
165    g_return_if_fail (allocation != NULL);
166 
167    widget->allocation = *allocation;
168    paned = GIMV_PANED (widget);
169    border_width = GTK_CONTAINER (widget)->border_width;
170    gutter_size = paned->gutter_size;
171 
172    if (paned->child1)
173       gtk_widget_get_child_requisition (paned->child1,
174                                         &child1_requisition);
175    else
176       child1_requisition.height = 0;
177 
178    if (paned->child2)
179       gtk_widget_get_child_requisition (paned->child2,
180                                         &child2_requisition);
181    else
182       child2_requisition.height = 0;
183 
184    gimv_paned_compute_position (paned,
185                                 MAX (1, (gint) widget->allocation.height
186                                      - gutter_size
187                                      - 2 * border_width),
188                                 child1_requisition.height,
189                                 child2_requisition.height);
190 
191    if (paned->child_hidden != 0) {
192       gutter_size = 0;
193       if ((paned->child_hidden == 1) && paned->child1) {
194          /* hide child1 and show child2 if it exists. */
195          gtk_widget_hide (paned->child1);
196          if (paned->child2 && !GTK_WIDGET_VISIBLE (paned->child2))
197             gtk_widget_show (paned->child2);
198       }
199       if ((paned->child_hidden == 2) && paned->child2) {
200          /* hide child2 and show child1 if it exists. */
201          gtk_widget_hide (paned->child2);
202          if (paned->child1 && !GTK_WIDGET_VISIBLE (paned->child1))
203             gtk_widget_show (paned->child1);
204       }
205    } else {
206       /* Show both children. */
207       if (paned->child1 && !GTK_WIDGET_VISIBLE (paned->child1))
208          gtk_widget_show (paned->child1);
209       if (paned->child2 && !GTK_WIDGET_VISIBLE (paned->child2))
210          gtk_widget_show (paned->child2);
211    }
212 
213    /* Move the handle before the children so we don't get extra expose
214     * events */
215 
216    paned->handle_xpos = border_width;
217    paned->handle_ypos = paned->child1_size + border_width;
218 
219    if (GTK_WIDGET_REALIZED (widget)) {
220       gdk_window_move_resize (widget->window,
221                               allocation->x, allocation->y,
222                               allocation->width, allocation->height);
223 
224       if (paned->child_hidden == 0) {
225          gdk_window_move_resize (paned->handle,
226                                  paned->handle_xpos,
227                                  paned->handle_ypos,
228                                  allocation->width,
229                                  paned->gutter_size);
230          gdk_window_show (paned->handle);
231       } else
232          gdk_window_hide (paned->handle);
233    }
234 
235    child1_allocation.width = child2_allocation.width = MAX (1, (gint) allocation->width - border_width * 2);
236    child1_allocation.height = paned->child1_size;
237    child1_allocation.x = child2_allocation.x = border_width;
238    child1_allocation.y = border_width;
239 
240    child2_allocation.y = (child1_allocation.y + child1_allocation.height + gutter_size);
241    child2_allocation.height = MAX (1, (gint) allocation->height -
242                                    child2_allocation.y - border_width);
243 
244    /* Now allocate the childen, making sure, when resizing not to
245     * overlap the windows */
246    if (GTK_WIDGET_MAPPED (widget) &&
247        paned->child1 && GTK_WIDGET_VISIBLE (paned->child1) &&
248        paned->child1->allocation.height < child1_allocation.height)
249    {
250       if (paned->child2 && GTK_WIDGET_VISIBLE (paned->child2))
251          gtk_widget_size_allocate (paned->child2,
252                                    &child2_allocation);
253       gtk_widget_size_allocate (paned->child1, &child1_allocation);
254 
255    } else {
256       if (paned->child1 && GTK_WIDGET_VISIBLE (paned->child1))
257          gtk_widget_size_allocate (paned->child1,
258                                    &child1_allocation);
259       if (paned->child2 && GTK_WIDGET_VISIBLE (paned->child2))
260          gtk_widget_size_allocate (paned->child2,
261                                    &child2_allocation);
262    }
263 }
264 
265 
266 static void
gimv_vpaned_draw(GtkWidget * widget,GdkRectangle * area)267 gimv_vpaned_draw (GtkWidget    *widget,
268                   GdkRectangle *area)
269 {
270    GimvPaned *paned;
271    GdkRectangle handle_area, child_area;
272    guint16 border_width;
273 
274    g_return_if_fail (widget != NULL);
275    g_return_if_fail (GIMV_IS_PANED (widget));
276 
277    if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget)) {
278       gint width, height;
279 
280       paned = GIMV_PANED (widget);
281       border_width = GTK_CONTAINER (paned)->border_width;
282 
283       gdk_window_clear_area (widget->window,
284                              area->x, area->y,
285                              area->width, area->height);
286 
287       gdk_window_get_size (paned->handle, &width, &height);
288 
289       handle_area.x = paned->handle_xpos;
290       handle_area.y = paned->handle_ypos;
291       handle_area.width = width;
292       handle_area.height = height;
293 
294       if (gdk_rectangle_intersect (&handle_area, area, &child_area)){
295          child_area.x -= handle_area.x;
296          child_area.y -= handle_area.y;
297 
298          gtk_paint_flat_box (widget->style, paned->handle,
299                              GTK_WIDGET_STATE (widget),
300                              GTK_SHADOW_NONE,
301                              &child_area, widget, "paned",
302                              0, 0,
303                              width, height);
304       }
305 
306       /* Redraw the children
307        */
308       if (paned->child1 &&
309           gtk_widget_intersect (paned->child1, area, &child_area))
310          gtk_widget_draw (paned->child1, &child_area);
311       if (paned->child2 &&
312           gtk_widget_intersect (paned->child2, area, &child_area))
313          gtk_widget_draw (paned->child2, &child_area);
314 
315    }
316 }
317 
318 
319 static void
gimv_vpaned_xor_line(GimvPaned * paned)320 gimv_vpaned_xor_line (GimvPaned *paned)
321 {
322    GtkWidget *widget;
323    GdkGCValues values;
324    guint16 ypos;
325 
326    widget = GTK_WIDGET(paned);
327 
328    if (!paned->xor_gc) {
329       GdkBitmap *stipple;
330 
331       stipple = gdk_bitmap_create_from_data (NULL, gray50_bits,
332                                              gray50_width,
333                                              gray50_height);
334 
335       values.function = GDK_INVERT;
336       values.subwindow_mode = GDK_INCLUDE_INFERIORS;
337       values.fill = GDK_STIPPLED;
338       values.stipple = stipple;
339 
340       paned->xor_gc = gdk_gc_new_with_values (widget->window,
341                                               &values,
342                                               GDK_GC_FUNCTION |
343                                               GDK_GC_SUBWINDOW |
344                                               GDK_GC_FILL |
345                                               GDK_GC_STIPPLE);
346       gdk_bitmap_unref (stipple);
347    }
348 
349    ypos = paned->child1_size + GTK_CONTAINER (paned)->border_width;
350 
351    gdk_draw_rectangle (widget->window, paned->xor_gc,
352                        TRUE,
353                        0,
354                        ypos,
355                        widget->allocation.width - 1,
356                        paned->gutter_size);
357 }
358 
359 
360 static gint
gimv_vpaned_button_press(GtkWidget * widget,GdkEventButton * event)361 gimv_vpaned_button_press (GtkWidget *widget, GdkEventButton *event)
362 {
363    GimvPaned *paned;
364 
365    g_return_val_if_fail (widget != NULL,FALSE);
366    g_return_val_if_fail (GIMV_IS_PANED (widget),FALSE);
367 
368    paned = GIMV_PANED (widget);
369 
370    if (!paned->in_drag &&
371        (event->window == paned->handle) && (event->button == 1))
372    {
373       paned->in_drag = TRUE;
374       /* We need a server grab here, not gtk_grab_add(), since
375        * we don't want to pass events on to the widget's children */
376       gdk_pointer_grab (paned->handle, FALSE,
377                         GDK_POINTER_MOTION_HINT_MASK
378                         | GDK_BUTTON1_MOTION_MASK
379                         | GDK_BUTTON_RELEASE_MASK,
380                         NULL, NULL, event->time);
381       paned->child1_size += event->y - paned->gutter_size / 2;
382       paned->child1_size = CLAMP (paned->child1_size,
383                                   0,
384                                   widget->allocation.height
385                                   - paned->gutter_size
386                                   - 2 * GTK_CONTAINER (paned)->border_width);
387       gimv_vpaned_xor_line (paned);
388    }
389 
390    return TRUE;
391 }
392 
393 
394 static gint
gimv_vpaned_button_release(GtkWidget * widget,GdkEventButton * event)395 gimv_vpaned_button_release (GtkWidget *widget, GdkEventButton *event)
396 {
397    GimvPaned *paned;
398 
399    g_return_val_if_fail (widget != NULL, FALSE);
400    g_return_val_if_fail (GIMV_IS_PANED (widget), FALSE);
401 
402    paned = GIMV_PANED (widget);
403 
404    if (paned->in_drag && (event->button == 1)) {
405       gimv_vpaned_xor_line (paned);
406       paned->in_drag = FALSE;
407       paned->position_set = TRUE;
408       gdk_pointer_ungrab (event->time);
409       gtk_widget_queue_resize (GTK_WIDGET (paned));
410    }
411 
412    return TRUE;
413 }
414 
415 #else
416 #endif /* USE_NORMAL_PANED */
417