1/*
2 *  Copyright (C) 2011 Igalia S.L.
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 "GtkAdjustmentWatcher.h"
21
22#include "Frame.h"
23#include "FrameView.h"
24#include "Page.h"
25#include "Scrollbar.h"
26#include "webkitwebviewprivate.h"
27#include <gtk/gtk.h>
28
29using namespace WebCore;
30
31namespace WebKit {
32
33GtkAdjustmentWatcher::GtkAdjustmentWatcher(WebKitWebView* webView)
34    : m_webView(webView)
35    , m_scrollbarsDisabled(false)
36    , m_handlingGtkAdjustmentChange(false)
37    , m_updateAdjustmentCallbackId(0)
38{
39}
40
41GtkAdjustmentWatcher::~GtkAdjustmentWatcher()
42{
43    if (m_updateAdjustmentCallbackId)
44        g_source_remove(m_updateAdjustmentCallbackId);
45}
46
47static void updateAdjustmentFromScrollbar(GtkAdjustment* adjustment, Scrollbar* scrollbar)
48{
49    if (!adjustment)
50        return;
51    if (!scrollbar) {
52        gtk_adjustment_configure(adjustment, 0, 0, 0, 0, 0, 0); // These are the settings which remove the scrollbar.
53        return;
54    }
55    gtk_adjustment_configure(adjustment, scrollbar->value(), 0, scrollbar->totalSize(),
56                             scrollbar->lineStep(), scrollbar->pageStep(), scrollbar->visibleSize());
57}
58
59void GtkAdjustmentWatcher::updateAdjustmentsFromScrollbars()
60{
61    if (m_scrollbarsDisabled)
62        return;
63    if (m_handlingGtkAdjustmentChange)
64        return;
65    if (!core(m_webView) || !core(m_webView)->mainFrame())
66        return;
67
68    FrameView* frameView = core(m_webView)->mainFrame()->view();
69    updateAdjustmentFromScrollbar(m_horizontalAdjustment.get(), frameView->horizontalScrollbar());
70    updateAdjustmentFromScrollbar(m_verticalAdjustment.get(), frameView->verticalScrollbar());
71    m_updateAdjustmentCallbackId = 0;
72}
73
74static gboolean updateAdjustmentCallback(GtkAdjustmentWatcher* watcher)
75{
76    watcher->updateAdjustmentsFromScrollbars();
77    return FALSE;
78}
79
80void GtkAdjustmentWatcher::updateAdjustmentsFromScrollbarsLater() const
81{
82    // We've already scheduled an update. No need to schedule another.
83    if (m_updateAdjustmentCallbackId || m_scrollbarsDisabled)
84        return;
85
86    // The fact that this method was called means that we need to update the scrollbars, but at the
87    // time of invocation they are not updated to reflect the scroll yet. We set a short timeout
88    // here, which means that they will be updated as soon as WebKit returns to the main loop.
89    m_updateAdjustmentCallbackId = g_timeout_add(0, reinterpret_cast<GSourceFunc>(updateAdjustmentCallback),
90                                                 const_cast<void*>(static_cast<const void*>(this)));
91}
92
93static void adjustmentValueChangedCallback(GtkAdjustment* adjustment, GtkAdjustmentWatcher* watcher)
94{
95    watcher->adjustmentValueChanged(adjustment);
96}
97
98static void setAdjustment(GtkAdjustmentWatcher* watcher, GRefPtr<GtkAdjustment>& adjustmentMember, GtkAdjustment* newAdjustment)
99{
100    if (adjustmentMember) {
101        g_signal_handlers_disconnect_by_func(adjustmentMember.get(),
102                                             reinterpret_cast<void*>(adjustmentValueChangedCallback), watcher);
103    }
104
105    adjustmentMember = newAdjustment;
106    if (newAdjustment)
107        g_signal_connect(newAdjustment, "value-changed", G_CALLBACK(adjustmentValueChangedCallback), watcher);
108}
109
110void GtkAdjustmentWatcher::setHorizontalAdjustment(GtkAdjustment* newAdjustment)
111{
112    setAdjustment(this, m_horizontalAdjustment, newAdjustment);
113}
114
115void GtkAdjustmentWatcher::setVerticalAdjustment(GtkAdjustment* newAdjustment)
116{
117    setAdjustment(this, m_verticalAdjustment, newAdjustment);
118}
119
120void GtkAdjustmentWatcher::adjustmentValueChanged(GtkAdjustment* adjustment)
121{
122    FrameView* frameView = core(m_webView)->mainFrame()->view();
123    Scrollbar* scrollbar = (adjustment == m_horizontalAdjustment.get()) ?
124        frameView->horizontalScrollbar() : frameView->verticalScrollbar();
125    if (!scrollbar)
126        return;
127
128    int newValue = static_cast<int>(gtk_adjustment_get_value(adjustment));
129    if (newValue != scrollbar->value()) {
130        m_handlingGtkAdjustmentChange = true;
131        frameView->scrollToOffsetWithoutAnimation(scrollbar->orientation(), newValue);
132        m_handlingGtkAdjustmentChange = false;
133    }
134}
135
136void GtkAdjustmentWatcher::disableAllScrollbars()
137{
138    updateAdjustmentFromScrollbar(m_horizontalAdjustment.get(), 0);
139    updateAdjustmentFromScrollbar(m_verticalAdjustment.get(), 0);
140    m_scrollbarsDisabled = true;
141}
142
143void GtkAdjustmentWatcher::enableAllScrollbars()
144{
145    m_scrollbarsDisabled = false;
146    updateAdjustmentsFromScrollbars();
147}
148
149} // namespace WebKit
150
151