1/*
2 *  Copyright (C) 2007 Holger Hans Peter Freyther
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 "ContextMenu.h"
21#include "ContextMenuItem.h"
22#include "CString.h"
23#include "NotImplemented.h"
24
25#include <gtk/gtk.h>
26
27#define WEBKIT_CONTEXT_MENU_ACTION "webkit-context-menu"
28
29namespace WebCore {
30
31static const char* gtkStockIDFromContextMenuAction(const ContextMenuAction& action)
32{
33    switch (action) {
34    case ContextMenuItemTagCopyLinkToClipboard:
35    case ContextMenuItemTagCopyImageToClipboard:
36    case ContextMenuItemTagCopy:
37        return GTK_STOCK_COPY;
38    case ContextMenuItemTagOpenLinkInNewWindow:
39    case ContextMenuItemTagOpenImageInNewWindow:
40    case ContextMenuItemTagOpenFrameInNewWindow:
41        return GTK_STOCK_OPEN;
42    case ContextMenuItemTagDownloadLinkToDisk:
43    case ContextMenuItemTagDownloadImageToDisk:
44        return GTK_STOCK_SAVE;
45    case ContextMenuItemTagGoBack:
46        return GTK_STOCK_GO_BACK;
47    case ContextMenuItemTagGoForward:
48        return GTK_STOCK_GO_FORWARD;
49    case ContextMenuItemTagStop:
50        return GTK_STOCK_STOP;
51    case ContextMenuItemTagReload:
52        return GTK_STOCK_REFRESH;
53    case ContextMenuItemTagCut:
54        return GTK_STOCK_CUT;
55    case ContextMenuItemTagPaste:
56        return GTK_STOCK_PASTE;
57    case ContextMenuItemTagDelete:
58        return GTK_STOCK_DELETE;
59    case ContextMenuItemTagSelectAll:
60        return GTK_STOCK_SELECT_ALL;
61    case ContextMenuItemTagSpellingGuess:
62        return NULL;
63    case ContextMenuItemTagIgnoreSpelling:
64        return GTK_STOCK_NO;
65    case ContextMenuItemTagLearnSpelling:
66        return GTK_STOCK_OK;
67    case ContextMenuItemTagOther:
68        return GTK_STOCK_MISSING_IMAGE;
69    case ContextMenuItemTagSearchInSpotlight:
70        return GTK_STOCK_FIND;
71    case ContextMenuItemTagSearchWeb:
72        return GTK_STOCK_FIND;
73    case ContextMenuItemTagOpenWithDefaultApplication:
74        return GTK_STOCK_OPEN;
75    case ContextMenuItemPDFZoomIn:
76        return GTK_STOCK_ZOOM_IN;
77    case ContextMenuItemPDFZoomOut:
78        return GTK_STOCK_ZOOM_OUT;
79    case ContextMenuItemPDFAutoSize:
80        return GTK_STOCK_ZOOM_FIT;
81    case ContextMenuItemPDFNextPage:
82        return GTK_STOCK_GO_FORWARD;
83    case ContextMenuItemPDFPreviousPage:
84        return GTK_STOCK_GO_BACK;
85    // New tags, not part of API
86    case ContextMenuItemTagOpenLink:
87        return GTK_STOCK_OPEN;
88    case ContextMenuItemTagCheckSpelling:
89        return GTK_STOCK_SPELL_CHECK;
90    case ContextMenuItemTagFontMenu:
91        return GTK_STOCK_SELECT_FONT;
92    case ContextMenuItemTagShowFonts:
93        return GTK_STOCK_SELECT_FONT;
94    case ContextMenuItemTagBold:
95        return GTK_STOCK_BOLD;
96    case ContextMenuItemTagItalic:
97        return GTK_STOCK_ITALIC;
98    case ContextMenuItemTagUnderline:
99        return GTK_STOCK_UNDERLINE;
100    case ContextMenuItemTagShowColors:
101        return GTK_STOCK_SELECT_COLOR;
102    default:
103        return NULL;
104    }
105}
106
107// Extract the ActionType from the menu item
108ContextMenuItem::ContextMenuItem(GtkMenuItem* item)
109    : m_platformDescription()
110{
111    if (GTK_IS_SEPARATOR_MENU_ITEM(item))
112        m_platformDescription.type = SeparatorType;
113    else if (gtk_menu_item_get_submenu(item))
114        m_platformDescription.type = SubmenuType;
115    else if (GTK_IS_CHECK_MENU_ITEM(item)) {
116        m_platformDescription.type = CheckableActionType;
117        m_platformDescription.checked = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item));
118    } else
119        m_platformDescription.type = ActionType;
120#if GTK_CHECK_VERSION (2, 16, 0)
121    m_platformDescription.title = String::fromUTF8(gtk_menu_item_get_label(GTK_MENU_ITEM(item)));
122#else
123    GtkWidget* label = gtk_bin_get_child(GTK_BIN(item));
124    m_platformDescription.title = String::fromUTF8(gtk_label_get_label(GTK_LABEL(label)));
125#endif
126
127    m_platformDescription.action = *static_cast<ContextMenuAction*>(g_object_get_data(G_OBJECT(item), WEBKIT_CONTEXT_MENU_ACTION));
128
129    m_platformDescription.subMenu = GTK_MENU(gtk_menu_item_get_submenu(item));
130    if (m_platformDescription.subMenu)
131        g_object_ref(m_platformDescription.subMenu);
132}
133
134ContextMenuItem::ContextMenuItem(ContextMenu*)
135{
136    notImplemented();
137}
138
139ContextMenuItem::ContextMenuItem(ContextMenuItemType type, ContextMenuAction action, const String& title, ContextMenu* subMenu)
140{
141    m_platformDescription.type = type;
142    m_platformDescription.action = action;
143    m_platformDescription.title = title;
144
145    setSubMenu(subMenu);
146}
147
148ContextMenuItem::~ContextMenuItem()
149{
150    if (m_platformDescription.subMenu)
151        g_object_unref(m_platformDescription.subMenu);
152}
153
154GtkMenuItem* ContextMenuItem::createNativeMenuItem(const PlatformMenuItemDescription& menu)
155{
156    GtkMenuItem* item = 0;
157    if (menu.type == SeparatorType)
158        item = GTK_MENU_ITEM(gtk_separator_menu_item_new());
159    else {
160        if (menu.type == CheckableActionType) {
161            item = GTK_MENU_ITEM(gtk_check_menu_item_new_with_mnemonic(menu.title.utf8().data()));
162            gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), menu.checked);
163        } else {
164            if (const gchar* stockID = gtkStockIDFromContextMenuAction(menu.action)) {
165                item = GTK_MENU_ITEM(gtk_image_menu_item_new_with_mnemonic(menu.title.utf8().data()));
166                GtkWidget* image = gtk_image_new_from_stock(stockID, GTK_ICON_SIZE_MENU);
167                gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
168            } else
169                item = GTK_MENU_ITEM(gtk_menu_item_new_with_mnemonic(menu.title.utf8().data()));
170        }
171
172        ContextMenuAction* menuAction = static_cast<ContextMenuAction*>(malloc(sizeof(ContextMenuAction*)));
173        *menuAction = menu.action;
174        g_object_set_data(G_OBJECT(item), WEBKIT_CONTEXT_MENU_ACTION, menuAction);
175
176        gtk_widget_set_sensitive(GTK_WIDGET(item), menu.enabled);
177
178        if (menu.subMenu)
179            gtk_menu_item_set_submenu(item, GTK_WIDGET(menu.subMenu));
180    }
181
182    return item;
183}
184
185PlatformMenuItemDescription ContextMenuItem::releasePlatformDescription()
186{
187    PlatformMenuItemDescription description = m_platformDescription;
188    m_platformDescription = PlatformMenuItemDescription();
189    return description;
190}
191
192ContextMenuItemType ContextMenuItem::type() const
193{
194    return m_platformDescription.type;
195}
196
197void ContextMenuItem::setType(ContextMenuItemType type)
198{
199    m_platformDescription.type = type;
200}
201
202ContextMenuAction ContextMenuItem::action() const
203{
204    return m_platformDescription.action;
205}
206
207void ContextMenuItem::setAction(ContextMenuAction action)
208{
209    m_platformDescription.action = action;
210}
211
212String ContextMenuItem::title() const
213{
214    return m_platformDescription.title;
215}
216
217void ContextMenuItem::setTitle(const String& title)
218{
219    m_platformDescription.title = title;
220}
221
222PlatformMenuDescription ContextMenuItem::platformSubMenu() const
223{
224    return m_platformDescription.subMenu;
225}
226
227void ContextMenuItem::setSubMenu(ContextMenu* menu)
228{
229    if (m_platformDescription.subMenu)
230        g_object_unref(m_platformDescription.subMenu);
231
232    if (!menu)
233        return;
234
235    m_platformDescription.subMenu = menu->releasePlatformDescription();
236    m_platformDescription.type = SubmenuType;
237
238    g_object_ref_sink(G_OBJECT(m_platformDescription.subMenu));
239}
240
241void ContextMenuItem::setChecked(bool shouldCheck)
242{
243    m_platformDescription.checked = shouldCheck;
244}
245
246void ContextMenuItem::setEnabled(bool shouldEnable)
247{
248    m_platformDescription.enabled = shouldEnable;
249}
250
251}
252