1/*
2 * This file is part of the popup menu implementation for <select> elements in WebCore.
3 *
4 * Copyright 2009, The Android Open Source Project
5 * Copyright (C) 2006 Apple Computer, Inc.
6 * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library 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 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB.  If not, write to
20 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
22 *
23 */
24
25#include "config.h"
26#include "PopupMenuAndroid.h"
27
28#include "IntRect.h"
29#include "PopupMenuClient.h"
30#include "SkTDArray.h"
31#include "WebViewCore.h"
32
33using namespace WebCore;
34
35class PopupReply : public android::WebCoreReply {
36public:
37    PopupReply(const IntRect& rect, android::WebViewCore* view, ListPopupMenuClient* client)
38        : m_rect(rect)
39        , m_viewImpl(view)
40        , m_popupClient(client)
41    {
42    }
43
44    virtual ~PopupReply() {}
45
46    virtual void replyInt(int value)
47    {
48        if (m_popupClient) {
49            m_popupClient->popupDidHide();
50            // -2 is a special value signaling that the popup was canceled.
51            if (-2 == value)
52                return;
53            m_popupClient->valueChanged(value, true);
54        }
55        if (m_viewImpl)
56            m_viewImpl->contentInvalidate(m_rect);
57    }
58
59    virtual void replyIntArray(const int* values, int count)
60    {
61        if (m_popupClient) {
62            m_popupClient->popupDidHide();
63            if (0 == count) {
64                m_popupClient->valueChanged(-1, true);
65            } else {
66                for (int i = 0; i < count; i++) {
67                    m_popupClient->listBoxSelectItem(values[i],
68                        i != 0 /* allowMultiplySelection */,
69                        false /* shift */,
70                        i == count - 1 /* fireOnChangeNow */);
71                }
72            }
73        }
74        if (m_viewImpl)
75            m_viewImpl->contentInvalidate(m_rect);
76    }
77
78    void disconnectClient()
79    {
80        m_popupClient = 0;
81        m_viewImpl = 0;
82    }
83private:
84    IntRect m_rect;
85    // FIXME: Do not need this if we handle ChromeClientAndroid::formStateDidChange
86    android::WebViewCore* m_viewImpl;
87    ListPopupMenuClient* m_popupClient;
88};
89
90namespace WebCore {
91
92PopupMenuAndroid::PopupMenuAndroid(ListPopupMenuClient* menuList)
93    : m_popupClient(menuList)
94    , m_reply(0)
95{
96}
97PopupMenuAndroid::~PopupMenuAndroid()
98{
99    disconnectClient();
100}
101
102void PopupMenuAndroid::disconnectClient()
103{
104    m_popupClient = 0;
105    if (m_reply) {
106        m_reply->disconnectClient();
107        Release(m_reply);
108        m_reply = 0;
109    }
110}
111
112// Convert a WTF::String into an array of characters where the first
113// character represents the length, for easy conversion to java.
114static uint16_t* stringConverter(const WTF::String& text)
115{
116    size_t length = text.length();
117    uint16_t* itemName = new uint16_t[length+1];
118    itemName[0] = (uint16_t)length;
119    uint16_t* firstChar = &(itemName[1]);
120    memcpy((void*)firstChar, text.characters(), sizeof(UChar)*length);
121    return itemName;
122}
123
124void PopupMenuAndroid::show(const IntRect& rect, FrameView* frameView, int)
125{
126    android::WebViewCore* viewImpl = android::WebViewCore::getWebViewCore(frameView);
127    m_reply = new PopupReply(rect, viewImpl, m_popupClient);
128    Retain(m_reply);
129
130    SkTDArray<const uint16_t*> names;
131    // Possible values for enabledArray.  Keep in Sync with values in
132    // InvokeListBox.Container in WebView.java
133    enum OptionStatus {
134        OPTGROUP = -1,
135        OPTION_DISABLED = 0,
136        OPTION_ENABLED = 1,
137    };
138    SkTDArray<int> enabledArray;
139    SkTDArray<int> selectedArray;
140    int size = m_popupClient->listSize();
141    bool multiple = m_popupClient->multiple();
142    for (int i = 0; i < size; i++) {
143        *names.append() = stringConverter(m_popupClient->itemText(i));
144        if (m_popupClient->itemIsSeparator(i)) {
145            *enabledArray.append() = OPTION_DISABLED;
146        } else if (m_popupClient->itemIsLabel(i)) {
147            *enabledArray.append() = OPTGROUP;
148        } else {
149            // Must be an Option
150            *enabledArray.append() = m_popupClient->itemIsEnabled(i)
151                    ? OPTION_ENABLED : OPTION_DISABLED;
152            if (multiple && m_popupClient->itemIsSelected(i))
153                *selectedArray.append() = i;
154        }
155    }
156
157    viewImpl->listBoxRequest(m_reply,
158                             names.begin(),
159                             size,
160                             enabledArray.begin(),
161                             enabledArray.count(),
162                             multiple,
163                             selectedArray.begin(),
164                             multiple ? selectedArray.count() : m_popupClient->selectedIndex());
165}
166
167} // namespace WebCore
168