2009-03-07 Xan Lopez <xan@gnome.org>
[WebKit-https.git] / WebCore / platform / gtk / ScrollbarGtk.cpp
1 /*
2  *  Copyright (C) 2007 Holger Hans Peter Freyther zecke@selfish.org
3  *
4  *  This library is free software; you can redistribute it and/or
5  *  modify it under the terms of the GNU Lesser 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  *  Lesser General Public License for more details.
13  *
14  *  You should have received a copy of the GNU Lesser General Public
15  *  License along with this library; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18
19 #include "config.h"
20 #include "ScrollbarGtk.h"
21
22 #include "IntRect.h"
23 #include "GraphicsContext.h"
24 #include "FrameView.h"
25 #include "NotImplemented.h"
26 #include "ScrollbarTheme.h"
27 #include "gtkdrawing.h"
28
29 #include <gtk/gtk.h>
30
31 using namespace WebCore;
32
33 PassRefPtr<Scrollbar> Scrollbar::createNativeScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, ScrollbarControlSize size)
34 {
35     return adoptRef(new ScrollbarGtk(client, orientation, size));
36 }
37
38 static gboolean gtkScrollEventCallback(GtkWidget* widget, GdkEventScroll* event, ScrollbarGtk*)
39 {
40     /* Scroll only if our parent rejects the scroll event. The rationale for
41      * this is that we want the main frame to scroll when we move the mouse
42      * wheel over a child scrollbar in most cases. */
43     return gtk_widget_event(gtk_widget_get_parent(widget), reinterpret_cast<GdkEvent*>(event));
44 }
45
46 ScrollbarGtk::ScrollbarGtk(ScrollbarClient* client, ScrollbarOrientation orientation,
47                            ScrollbarControlSize controlSize)
48     : Scrollbar(client, orientation, controlSize)
49     , m_adjustment(GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0)))
50 {
51     GtkWidget* scrollBar = orientation == HorizontalScrollbar ?
52                            gtk_hscrollbar_new(m_adjustment):
53                            gtk_vscrollbar_new(m_adjustment);
54     gtk_widget_show(scrollBar);
55     g_signal_connect(scrollBar, "value-changed", G_CALLBACK(ScrollbarGtk::gtkValueChanged), this);
56     g_signal_connect(scrollBar, "scroll-event", G_CALLBACK(gtkScrollEventCallback), this);
57
58     setPlatformWidget(scrollBar);
59
60     /*
61      * assign a sane default width and height to the Scrollbar, otherwise
62      * we will end up with a 0 width scrollbar.
63      */
64     resize(ScrollbarTheme::nativeTheme()->scrollbarThickness(),
65            ScrollbarTheme::nativeTheme()->scrollbarThickness());
66 }
67
68 IntPoint ScrollbarGtk::getLocationInParentWindow(const IntRect& rect)
69 {
70     IntPoint loc;
71
72     if (parent()->isScrollViewScrollbar(this))
73         loc = parent()->convertToContainingWindow(rect.location());
74     else
75         loc = parent()->contentsToWindow(rect.location());
76
77     return loc;
78 }
79
80 void ScrollbarGtk::frameRectsChanged()
81 {
82     if (!parent())
83         return;
84
85     IntPoint loc = getLocationInParentWindow(frameRect());
86
87     // Don't allow the allocation size to be negative
88     IntSize sz = frameRect().size();
89     sz.clampNegativeToZero();
90
91     GtkAllocation allocation = { loc.x(), loc.y(), sz.width(), sz.height() };
92     gtk_widget_size_allocate(platformWidget(), &allocation);
93 }
94
95 void ScrollbarGtk::updateThumbPosition()
96 {
97     if (m_adjustment->value != m_currentPos) {
98         m_adjustment->value = m_currentPos;
99         gtk_adjustment_value_changed(m_adjustment);
100     }
101 }
102
103 void ScrollbarGtk::updateThumbProportion()
104 {
105     m_adjustment->step_increment = m_lineStep;
106     m_adjustment->page_increment = m_pageStep;
107     m_adjustment->page_size = m_visibleSize;
108     m_adjustment->upper = m_totalSize;
109     gtk_adjustment_changed(m_adjustment);
110 }
111
112 void ScrollbarGtk::setFrameRect(const IntRect& rect)
113 {
114     Widget::setFrameRect(rect);
115     frameRectsChanged();
116 }
117
118 void ScrollbarGtk::gtkValueChanged(GtkAdjustment*, ScrollbarGtk* that)
119 {
120     that->setValue(static_cast<int>(gtk_adjustment_get_value(that->m_adjustment)));
121 }
122
123 void ScrollbarGtk::setEnabled(bool shouldEnable)
124 {
125     if (enabled() == shouldEnable)
126         return;
127         
128     Scrollbar::setEnabled(shouldEnable);
129     if (platformWidget()) 
130         gtk_widget_set_sensitive(platformWidget(), shouldEnable);
131 }
132
133 /*
134  * Strategy to painting a Widget:
135  *  1.) do not paint if there is no GtkWidget set
136  *  2.) We assume that GTK_NO_WINDOW is set and that frameRectsChanged positioned
137  *      the widget correctly. ATM we do not honor the GraphicsContext translation.
138  */
139 void ScrollbarGtk::paint(GraphicsContext* context, const IntRect& rect)
140 {
141     if (!platformWidget())
142         return;
143
144     if (!context->gdkExposeEvent())
145         return;
146
147     GtkWidget* widget = platformWidget();
148     ASSERT(GTK_WIDGET_NO_WINDOW(widget));
149
150     GdkEvent* event = gdk_event_new(GDK_EXPOSE);
151     event->expose = *context->gdkExposeEvent();
152     event->expose.area = static_cast<GdkRectangle>(rect);
153
154     IntPoint loc = getLocationInParentWindow(rect);
155
156     event->expose.area.x = loc.x();
157     event->expose.area.y = loc.y();
158
159     event->expose.region = gdk_region_rectangle(&event->expose.area);
160
161     /*
162      * This will be unref'ed by gdk_event_free.
163      */
164     g_object_ref(event->expose.window);
165
166     /*
167      * If we are going to paint do the translation and GtkAllocation manipulation.
168      */
169     if (!gdk_region_empty(event->expose.region))
170         gtk_widget_send_expose(widget, event);
171
172     gdk_event_free(event);
173 }