blob: b4689f61a1e3dd7f06a69609ddece84d556f94e2 [file] [log] [blame]
/*
* This file is part of the popup menu implementation for <select> elements in WebCore.
*
* Copyright (C) 2006, 2007 Apple Inc.
* Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include "config.h"
#include "PopupMenu.h"
#include "CString.h"
#include "FrameView.h"
#include "NotImplemented.h"
#include "PlatformString.h"
#include <gtk/gtk.h>
namespace WebCore {
PopupMenu::PopupMenu(PopupMenuClient* client)
: m_popupClient(client)
, m_popup(0)
{
}
PopupMenu::~PopupMenu()
{
if (m_popup)
g_object_unref(m_popup);
}
void PopupMenu::show(const IntRect& rect, FrameView* view, int index)
{
ASSERT(client());
if (!m_popup) {
m_popup = GTK_MENU(gtk_menu_new());
#if GLIB_CHECK_VERSION(2,10,0)
g_object_ref_sink(G_OBJECT(m_popup));
#else
g_object_ref(G_OBJECT(m_popup));
gtk_object_sink(GTK_OBJECT(m_popup));
#endif
g_signal_connect(m_popup, "unmap", G_CALLBACK(menuUnmapped), this);
} else
gtk_container_foreach(GTK_CONTAINER(m_popup), reinterpret_cast<GtkCallback>(menuRemoveItem), this);
int x, y;
gdk_window_get_origin(GTK_WIDGET(view->containingWindow())->window, &x, &y);
m_menuPosition = view->contentsToWindow(rect.location());
m_menuPosition = IntPoint(m_menuPosition.x() + x, m_menuPosition.y() + y + rect.height());
m_indexMap.clear();
const int size = client()->listSize();
for (int i = 0; i < size; ++i) {
GtkWidget* item;
if (client()->itemIsSeparator(i))
item = gtk_separator_menu_item_new();
else
item = gtk_menu_item_new_with_label(client()->itemText(i).utf8().data());
m_indexMap.add(item, i);
g_signal_connect(item, "activate", G_CALLBACK(menuItemActivated), this);
// FIXME: Apply the RenderStyle from client()->itemStyle(i)
gtk_widget_set_sensitive(item, client()->itemIsEnabled(i));
gtk_menu_shell_append(GTK_MENU_SHELL(m_popup), item);
gtk_widget_show(item);
}
gtk_menu_set_active(m_popup, index);
// The size calls are directly copied from gtkcombobox.c which is LGPL
GtkRequisition requisition;
gtk_widget_set_size_request(GTK_WIDGET(m_popup), -1, -1);
gtk_widget_size_request(GTK_WIDGET(m_popup), &requisition);
gtk_widget_set_size_request(GTK_WIDGET(m_popup), MAX(rect.width(), requisition.width), -1);
gtk_menu_popup(m_popup, NULL, NULL, reinterpret_cast<GtkMenuPositionFunc>(menuPositionFunction), this, 0, gtk_get_current_event_time());
}
void PopupMenu::hide()
{
ASSERT(m_popup);
gtk_menu_popdown(m_popup);
}
void PopupMenu::updateFromElement()
{
client()->setTextFromItem(client()->selectedIndex());
}
bool PopupMenu::itemWritingDirectionIsNatural()
{
return true;
}
void PopupMenu::menuItemActivated(GtkMenuItem* item, PopupMenu* that)
{
ASSERT(that->client());
ASSERT(that->m_indexMap.contains(GTK_WIDGET(item)));
that->client()->valueChanged(that->m_indexMap.get(GTK_WIDGET(item)));
}
void PopupMenu::menuUnmapped(GtkWidget*, PopupMenu* that)
{
ASSERT(that->client());
that->client()->hidePopup();
}
void PopupMenu::menuPositionFunction(GtkMenu*, gint* x, gint* y, gboolean* pushIn, PopupMenu* that)
{
*x = that->m_menuPosition.x();
*y = that->m_menuPosition.y();
pushIn = false;
}
void PopupMenu::menuRemoveItem(GtkWidget* widget, PopupMenu* that)
{
ASSERT(that->m_popup);
gtk_container_remove(GTK_CONTAINER(that->m_popup), widget);
}
}