1/*
2 *  Copyright (C) 2007 Holger Hans Peter Freyther zecke@selfish.org
3 *            (C) 2009 Kenneth Rohde Christiansen
4 *            (C) 2009 INdT, Instituto Nokia de Technologia
5 *            (C) 2009-2010 ProFUSION embedded systems
6 *            (C) 2009-2010 Samsung Electronics
7 *
8 *  This library is free software; you can redistribute it and/or
9 *  modify it under the terms of the GNU Lesser General Public
10 *  License as published by the Free Software Foundation; either
11 *  version 2 of the License, or (at your option) any later version.
12 *
13 *  This library is distributed in the hope that it will be useful,
14 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 *  Lesser General Public License for more details.
17 *
18 *  You should have received a copy of the GNU Lesser General Public
19 *  License along with this library; if not, write to the Free Software
20 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21 */
22
23#include "config.h"
24#include "ScrollbarEfl.h"
25
26#include "Frame.h"
27#include "FrameView.h"
28#include "GraphicsContext.h"
29#include "HostWindow.h"
30#include "IntRect.h"
31#include "Page.h"
32#include "RenderThemeEfl.h"
33#include "ScrollbarTheme.h"
34#include "Settings.h"
35
36#include <Ecore.h>
37#include <Edje.h>
38#include <Evas.h>
39#include <new>
40#include <string>
41#include <wtf/OwnArrayPtr.h>
42#include <wtf/text/CString.h>
43
44using namespace std;
45using namespace WebCore;
46
47PassRefPtr<Scrollbar> Scrollbar::createNativeScrollbar(ScrollableArea* scrollableArea, ScrollbarOrientation orientation, ScrollbarControlSize size)
48{
49    if (Settings::mockScrollbarsEnabled())
50        return adoptRef(new Scrollbar(scrollableArea, orientation, size));
51
52    return adoptRef(new ScrollbarEfl(scrollableArea, orientation, size));
53}
54
55ScrollbarEfl::ScrollbarEfl(ScrollableArea* scrollableArea, ScrollbarOrientation orientation, ScrollbarControlSize controlSize)
56    : Scrollbar(scrollableArea, orientation, controlSize)
57    , m_lastPos(0)
58    , m_lastTotalSize(0)
59    , m_lastVisibleSize(0)
60{
61}
62
63ScrollbarEfl::~ScrollbarEfl()
64{
65    if (!evasObject())
66        return;
67
68    evas_object_del(evasObject());
69    setEvasObject(0);
70}
71
72static void scrollbarEflEdjeMessage(void* data, Evas_Object*, Edje_Message_Type messageType, int id, void* message)
73{
74    if (!id) {
75        EINA_LOG_ERR("Unknown message id '%d' from scroll bar theme.", id);
76        return;
77    }
78
79    if (messageType != EDJE_MESSAGE_FLOAT) {
80        EINA_LOG_ERR("Message id '%d' of incorrect type from scroll bar theme. "
81                     "Expected '%d', got '%d'.",
82                     id, EDJE_MESSAGE_FLOAT, messageType);
83        return;
84    }
85
86    ScrollbarEfl* that = static_cast<ScrollbarEfl*>(data);
87
88    Edje_Message_Float* messageFloat = static_cast<Edje_Message_Float*>(message);
89    int value = messageFloat->val * (that->totalSize() - that->visibleSize());
90    that->scrollableArea()->scrollToOffsetWithoutAnimation(that->orientation(), value);
91}
92
93void ScrollbarEfl::show()
94{
95    if (Evas_Object* object = evasObject())
96        evas_object_show(object);
97}
98
99void ScrollbarEfl::hide()
100{
101    if (Evas_Object* object = evasObject())
102        evas_object_hide(object);
103}
104
105void ScrollbarEfl::setParent(ScrollView* view)
106{
107    Widget::setParent(view);
108
109    if (!view || !view->evasObject())
110        return;
111
112    Frame* frame = toFrameView(view)->frame();
113    if (!frame || !frame->page())
114        return;
115
116    String theme = static_cast<RenderThemeEfl*>(frame->page()->theme())->themePath();
117
118    const char* group = (orientation() == HorizontalScrollbar) ? "scrollbar.horizontal" : "scrollbar.vertical";
119    if (theme.isEmpty()) {
120        EINA_LOG_ERR("Could not load theme '%s': no theme path set.", group);
121        return;
122    }
123
124    if (!evasObject()) {
125        setEvasObject(edje_object_add(evas_object_evas_get(view->evasObject())));
126        if (!evasObject()) {
127            EINA_LOG_ERR("Could not create edje object for view=%p", view);
128            return;
129        }
130        frameRectsChanged();
131        edje_object_message_handler_set(evasObject(), scrollbarEflEdjeMessage, this);
132    }
133
134    if (!edje_object_file_set(evasObject(), theme.utf8().data(), group)) {
135        Edje_Load_Error err = edje_object_load_error_get(evasObject());
136        const char* errmessage = edje_load_error_str(err);
137        EINA_LOG_ERR("Could not load theme '%s' from file '%s': #%d '%s'",
138                     group, theme.utf8().data(), err, errmessage);
139        return;
140    }
141
142    evas_object_smart_member_add(evasObject(), view->evasObject());
143    evas_object_show(evasObject());
144}
145
146void ScrollbarEfl::updateThumbPosition()
147{
148    updateThumbPositionAndProportion();
149}
150
151void ScrollbarEfl::updateThumbProportion()
152{
153    updateThumbPositionAndProportion();
154}
155
156void ScrollbarEfl::updateThumbPositionAndProportion()
157{
158    if (!evasObject())
159        return;
160
161    int pos = currentPos();
162    int tSize = totalSize();
163    int vSize = visibleSize();
164
165    if (m_lastPos == pos
166        && m_lastTotalSize == tSize
167        && m_lastVisibleSize == vSize)
168        return;
169
170    m_lastPos = pos;
171    m_lastTotalSize = tSize;
172    m_lastVisibleSize = vSize;
173
174    OwnArrayPtr<char> buffer = adoptArrayPtr(new char[sizeof(Edje_Message_Float_Set) + sizeof(double)]);
175    Edje_Message_Float_Set* message = new(buffer.get()) Edje_Message_Float_Set;
176    message->count = 2;
177
178    if (tSize - vSize > 0)
179        message->val[0] = pos / static_cast<float>(tSize - vSize);
180    else
181        message->val[0] = 0.0;
182
183    if (tSize > 0)
184        message->val[1] = vSize / static_cast<float>(tSize);
185    else
186        message->val[1] = 0.0;
187
188    edje_object_message_send(evasObject(), EDJE_MESSAGE_FLOAT_SET, 0, message);
189}
190
191void ScrollbarEfl::setFrameRect(const IntRect& rect)
192{
193    Widget::setFrameRect(rect);
194    frameRectsChanged();
195}
196
197void ScrollbarEfl::frameRectsChanged()
198{
199    Evas_Object* object = evasObject();
200    Evas_Coord x, y;
201
202    if (!parent() || !object)
203        return;
204
205    IntRect rect = frameRect();
206    if (parent()->isScrollViewScrollbar(this))
207        rect.setLocation(parent()->convertToContainingWindow(rect.location()));
208    else
209        rect.setLocation(parent()->contentsToWindow(rect.location()));
210
211    evas_object_geometry_get(root()->evasObject(), &x, &y, 0, 0);
212    evas_object_move(object, x + rect.x(), y + rect.y());
213    evas_object_resize(object, rect.width(), rect.height());
214}
215