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_hpaned.c,v 1.3 2004/03/07 11:53:30 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_hpaned.h"
37 #include <gtk/gtkmain.h>
38 #include <gtk/gtksignal.h>
39 
40 static void gimv_hpaned_class_init       (GimvHPanedClass *klass);
41 static void gimv_hpaned_init             (GimvHPaned      *hpaned);
42 static void gimv_hpaned_size_request     (GtkWidget      *widget,
43                                           GtkRequisition *requisition);
44 static void gimv_hpaned_size_allocate    (GtkWidget          *widget,
45                                           GtkAllocation      *allocation);
46 static void gimv_hpaned_draw             (GtkWidget    *widget,
47                                           GdkRectangle *area);
48 static void gimv_hpaned_xor_line         (GimvPaned *paned);
49 static gint gimv_hpaned_button_press     (GtkWidget *widget,
50                                           GdkEventButton *event);
51 static gint gimv_hpaned_button_release   (GtkWidget *widget,
52                                           GdkEventButton *event);
53 
54 
55 GtkType
gimv_hpaned_get_type(void)56 gimv_hpaned_get_type (void)
57 {
58    static GtkType hpaned_type = 0;
59 
60    if (!hpaned_type) {
61       static const GtkTypeInfo hpaned_info =	{
62          "GimvHPaned",
63          sizeof (GimvHPaned),
64          sizeof (GimvHPanedClass),
65          (GtkClassInitFunc) gimv_hpaned_class_init,
66          (GtkObjectInitFunc) gimv_hpaned_init,
67          /* reserved_1 */ NULL,
68          /* reserved_2 */ NULL,
69          (GtkClassInitFunc) NULL,
70       };
71       hpaned_type = gtk_type_unique (gimv_paned_get_type (),
72                                      &hpaned_info);
73    }
74    return hpaned_type;
75 }
76 
77 
78 static void
gimv_hpaned_class_init(GimvHPanedClass * class)79 gimv_hpaned_class_init (GimvHPanedClass *class)
80 {
81    GtkWidgetClass *widget_class;
82    GimvPanedClass *paned_class;
83 
84    widget_class = (GtkWidgetClass*) class;
85    paned_class = (GimvPanedClass*) class;
86 
87    widget_class->size_request = gimv_hpaned_size_request;
88    widget_class->size_allocate = gimv_hpaned_size_allocate;
89 #ifndef USE_GTK2
90    widget_class->draw = gimv_hpaned_draw;
91 #endif
92    widget_class->button_press_event = gimv_hpaned_button_press;
93    widget_class->button_release_event = gimv_hpaned_button_release;
94 
95    paned_class->xor_line = gimv_hpaned_xor_line;
96 }
97 
98 
99 static void
gimv_hpaned_init(GimvHPaned * hpaned)100 gimv_hpaned_init (GimvHPaned *hpaned)
101 {
102    GIMV_PANED (hpaned)->horizontal = TRUE;
103 }
104 
105 
106 GtkWidget*
gimv_hpaned_new(void)107 gimv_hpaned_new (void)
108 {
109    GimvHPaned *hpaned;
110 
111    hpaned = gtk_type_new (gimv_hpaned_get_type ());
112 
113    return GTK_WIDGET (hpaned);
114 }
115 
116 
117 static void
gimv_hpaned_size_request(GtkWidget * widget,GtkRequisition * requisition)118 gimv_hpaned_size_request (GtkWidget      *widget,
119                           GtkRequisition *requisition)
120 {
121    GimvPaned *paned;
122    GtkRequisition child_requisition;
123 
124    g_return_if_fail (widget != NULL);
125    g_return_if_fail (GIMV_IS_HPANED (widget));
126    g_return_if_fail (requisition != NULL);
127 
128    paned = GIMV_PANED (widget);
129    requisition->width = 0;
130    requisition->height = 0;
131 
132    if (paned->child1 && GTK_WIDGET_VISIBLE (paned->child1)) {
133       gtk_widget_size_request (paned->child1, &child_requisition);
134 
135       requisition->height = child_requisition.height;
136       requisition->width = child_requisition.width;
137    }
138 
139    if (paned->child2 && GTK_WIDGET_VISIBLE (paned->child2)) {
140       gtk_widget_size_request (paned->child2, &child_requisition);
141 
142       requisition->height = MAX (requisition->height,
143                                  child_requisition.height);
144       requisition->width += child_requisition.width;
145    }
146 
147    requisition->width += (GTK_CONTAINER (paned)->border_width * 2
148                           + paned->gutter_size);
149    requisition->height += GTK_CONTAINER (paned)->border_width * 2;
150 }
151 
152 
153 static void
gimv_hpaned_size_allocate(GtkWidget * widget,GtkAllocation * allocation)154 gimv_hpaned_size_allocate (GtkWidget     *widget,
155                            GtkAllocation *allocation)
156 {
157    GimvPaned *paned;
158    GtkRequisition child1_requisition;
159    GtkRequisition child2_requisition;
160    GtkAllocation child1_allocation;
161    GtkAllocation child2_allocation;
162    gint border_width, gutter_size;
163 
164    g_return_if_fail (widget != NULL);
165    g_return_if_fail (GIMV_IS_HPANED (widget));
166    g_return_if_fail (allocation != NULL);
167 
168    widget->allocation = *allocation;
169    paned = GIMV_PANED (widget);
170    border_width = GTK_CONTAINER (paned)->border_width;
171    gutter_size = paned->gutter_size;
172 
173    if (paned->child1)
174       gtk_widget_get_child_requisition (paned->child1,
175                                         &child1_requisition);
176    else
177       child1_requisition.width = 0;
178 
179    if (paned->child2)
180       gtk_widget_get_child_requisition (paned->child2,
181                                         &child2_requisition);
182    else
183       child2_requisition.width = 0;
184 
185    gimv_paned_compute_position (paned,
186                                 MAX (1, (gint) widget->allocation.width
187                                      - gutter_size
188                                      - 2 * border_width),
189                                 child1_requisition.width,
190                                 child2_requisition.width);
191 
192    if (paned->child_hidden != 0) {
193       gutter_size = 0;
194       if ((paned->child_hidden == 1) && paned->child1) {
195          /* hide child1 and show child2 if it exists. */
196          gtk_widget_hide (paned->child1);
197          if (paned->child2 && !GTK_WIDGET_VISIBLE (paned->child2))
198             gtk_widget_show (paned->child2);
199       }
200       if ((paned->child_hidden == 2) && paned->child2) {
201          /* hide child2 and show child1 if it exists. */
202          gtk_widget_hide (paned->child2);
203          if (paned->child1 && !GTK_WIDGET_VISIBLE (paned->child1))
204             gtk_widget_show (paned->child1);
205       }
206    } else {
207       /* Show both children. */
208       if (paned->child1 && !GTK_WIDGET_VISIBLE (paned->child1))
209          gtk_widget_show (paned->child1);
210       if (paned->child2 && !GTK_WIDGET_VISIBLE (paned->child2))
211          gtk_widget_show (paned->child2);
212    }
213 
214    /* Move the handle before the children so we don't get extra expose
215     * events */
216 
217    paned->handle_ypos = border_width;
218    paned->handle_xpos = paned->child1_size + border_width;
219 
220    if (GTK_WIDGET_REALIZED (widget)) {
221       gdk_window_move_resize (widget->window,
222                               allocation->x, allocation->y,
223                               allocation->width, allocation->height);
224 
225       if (paned->child_hidden == 0) {
226          gdk_window_move_resize (paned->handle,
227                                  paned->handle_xpos,
228                                  paned->handle_ypos,
229                                  paned->gutter_size,
230                                  allocation->height);
231          gdk_window_show (paned->handle);
232       } else
233          gdk_window_hide (paned->handle);
234    }
235 
236    child1_allocation.height = child2_allocation.height
237       = MAX (1, (gint) allocation->height - border_width * 2);
238    child1_allocation.width = paned->child1_size;
239    child1_allocation.y = child2_allocation.y = border_width;
240    child1_allocation.x = border_width;
241 
242    child2_allocation.x
243       = child1_allocation.x + child1_allocation.width + gutter_size;
244    child2_allocation.width
245       = MAX (1, (gint) allocation->width - child2_allocation.x - border_width);
246 
247    /* Now allocate the childen, making sure, when resizing not to
248     * overlap the windows */
249    if (GTK_WIDGET_MAPPED(widget) &&
250        paned->child1 && GTK_WIDGET_VISIBLE (paned->child1) &&
251        paned->child1->allocation.width < child1_allocation.width)
252    {
253       if (paned->child2 && GTK_WIDGET_VISIBLE (paned->child2))
254          gtk_widget_size_allocate (paned->child2, &child2_allocation);
255       gtk_widget_size_allocate (paned->child1, &child1_allocation);
256    }  else {
257       if (paned->child1 && GTK_WIDGET_VISIBLE (paned->child1))
258          gtk_widget_size_allocate (paned->child1, &child1_allocation);
259       if (paned->child2 && GTK_WIDGET_VISIBLE (paned->child2))
260          gtk_widget_size_allocate (paned->child2, &child2_allocation);
261    }
262 }
263 
264 
265 static void
gimv_hpaned_draw(GtkWidget * widget,GdkRectangle * area)266 gimv_hpaned_draw (GtkWidget    *widget,
267                   GdkRectangle *area)
268 {
269    GimvPaned *paned;
270    GdkRectangle handle_area, child_area;
271    guint16 border_width;
272 
273    g_return_if_fail (widget != NULL);
274    g_return_if_fail (GIMV_IS_PANED (widget));
275 
276    if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget)) {
277       gint width, height;
278 
279       paned = GIMV_PANED (widget);
280       border_width = GTK_CONTAINER (paned)->border_width;
281 
282       gdk_window_clear_area (widget->window,
283                              area->x, area->y,
284                              area->width, area->height);
285 
286       gdk_window_get_size (paned->handle, &width, &height);
287 
288       handle_area.x = paned->handle_xpos;
289       handle_area.y = paned->handle_ypos;
290       handle_area.width = width;
291       handle_area.height = height;
292 
293       if (gdk_rectangle_intersect (&handle_area, area, &child_area)){
294          child_area.x -= handle_area.x;
295          child_area.y -= handle_area.y;
296 
297          gtk_paint_flat_box (widget->style, paned->handle,
298                              GTK_WIDGET_STATE (widget),
299                              GTK_SHADOW_NONE,
300                              &child_area, widget, "paned",
301                              0, 0,
302                              width, height);
303       }
304 
305       /* Redraw the children
306        */
307       if (paned->child1 &&
308           gtk_widget_intersect (paned->child1, area, &child_area))
309          gtk_widget_draw (paned->child1, &child_area);
310       if (paned->child2 &&
311           gtk_widget_intersect (paned->child2, area, &child_area))
312          gtk_widget_draw (paned->child2, &child_area);
313 
314    }
315 }
316 
317 
318 static void
gimv_hpaned_xor_line(GimvPaned * paned)319 gimv_hpaned_xor_line (GimvPaned *paned)
320 {
321    GtkWidget *widget;
322    GdkGCValues values;
323    guint16 xpos;
324 
325    widget = GTK_WIDGET(paned);
326 
327    if (!paned->xor_gc) {
328       GdkBitmap *stipple;
329 
330       stipple = gdk_bitmap_create_from_data (NULL, gray50_bits,
331                                              gray50_width,
332                                              gray50_height);
333 
334       values.function = GDK_INVERT;
335       values.subwindow_mode = GDK_INCLUDE_INFERIORS;
336       values.fill = GDK_STIPPLED;
337       values.stipple = stipple;
338       paned->xor_gc = gdk_gc_new_with_values (widget->window,
339                                               &values,
340                                               GDK_GC_FUNCTION |
341                                               GDK_GC_SUBWINDOW |
342                                               GDK_GC_FILL |
343                                               GDK_GC_STIPPLE);
344       gdk_bitmap_unref (stipple);
345    }
346 
347    xpos = paned->child1_size + GTK_CONTAINER (paned)->border_width;
348 
349    gdk_draw_rectangle (widget->window, paned->xor_gc,
350                        TRUE,
351                        xpos,
352                        0,
353                        paned->gutter_size,
354                        widget->allocation.height - 1);
355 }
356 
357 
358 static gint
gimv_hpaned_button_press(GtkWidget * widget,GdkEventButton * event)359 gimv_hpaned_button_press (GtkWidget *widget, GdkEventButton *event)
360 {
361    GimvPaned *paned;
362 
363    g_return_val_if_fail (widget != NULL,FALSE);
364    g_return_val_if_fail (GIMV_IS_PANED (widget),FALSE);
365 
366    paned = GIMV_PANED (widget);
367 
368    if (!paned->in_drag &&
369        (event->window == paned->handle) && (event->button == 1))
370    {
371       paned->in_drag = TRUE;
372       /* We need a server grab here, not gtk_grab_add(), since
373        * we don't want to pass events on to the widget's children */
374       gdk_pointer_grab (paned->handle, FALSE,
375                         GDK_POINTER_MOTION_HINT_MASK
376                         | GDK_BUTTON1_MOTION_MASK
377                         | GDK_BUTTON_RELEASE_MASK,
378                         NULL, NULL, event->time);
379       paned->child1_size += event->x - paned->gutter_size / 2;
380       paned->child1_size = CLAMP (paned->child1_size, 0,
381                                   widget->allocation.width
382                                   - paned->gutter_size
383                                   - 2 * GTK_CONTAINER (paned)->border_width);
384       gimv_hpaned_xor_line (paned);
385    }
386 
387    return TRUE;
388 }
389 
390 
391 static gint
gimv_hpaned_button_release(GtkWidget * widget,GdkEventButton * event)392 gimv_hpaned_button_release (GtkWidget *widget, GdkEventButton *event)
393 {
394    GimvPaned *paned;
395 
396    g_return_val_if_fail (widget != NULL,FALSE);
397    g_return_val_if_fail (GIMV_IS_PANED (widget),FALSE);
398 
399    paned = GIMV_PANED (widget);
400 
401    if (paned->in_drag && (event->button == 1)) {
402       gimv_hpaned_xor_line (paned);
403       paned->in_drag = FALSE;
404       paned->position_set = TRUE;
405       gdk_pointer_ungrab (event->time);
406       gtk_widget_queue_resize (GTK_WIDGET (paned));
407    }
408 
409    return TRUE;
410 }
411 
412 #endif /* USE_NORMAL_PANED */
413