Home | History | Annotate | Download | only in gtk
      1 /*
      2  * This file is part of the popup menu implementation for <select> elements in WebCore.
      3  *
      4  * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
      5  * Copyright (C) 2006 Michael Emmel mike.emmel (at) gmail.com
      6  * Copyright (C) 2008 Collabora Ltd.
      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., 51 Franklin Street, Fifth Floor,
     21  * Boston, MA 02110-1301, USA.
     22  *
     23  */
     24 
     25 #include "config.h"
     26 #include "PopupMenu.h"
     27 
     28 #include "CString.h"
     29 #include "FrameView.h"
     30 #include "HostWindow.h"
     31 #include "PlatformString.h"
     32 #include <gtk/gtk.h>
     33 
     34 namespace WebCore {
     35 
     36 PopupMenu::PopupMenu(PopupMenuClient* client)
     37     : m_popupClient(client)
     38 {
     39 }
     40 
     41 PopupMenu::~PopupMenu()
     42 {
     43     if (m_popup) {
     44         g_signal_handlers_disconnect_matched(m_popup.get(), G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
     45         hide();
     46     }
     47 }
     48 
     49 void PopupMenu::show(const IntRect& rect, FrameView* view, int index)
     50 {
     51     ASSERT(client());
     52 
     53     if (!m_popup) {
     54         m_popup = GTK_MENU(gtk_menu_new());
     55         g_signal_connect(m_popup.get(), "unmap", G_CALLBACK(menuUnmapped), this);
     56     } else
     57         gtk_container_foreach(GTK_CONTAINER(m_popup.get()), reinterpret_cast<GtkCallback>(menuRemoveItem), this);
     58 
     59     int x, y;
     60     gdk_window_get_origin(GTK_WIDGET(view->hostWindow()->platformPageClient())->window, &x, &y);
     61     m_menuPosition = view->contentsToWindow(rect.location());
     62     m_menuPosition = IntPoint(m_menuPosition.x() + x, m_menuPosition.y() + y + rect.height());
     63     m_indexMap.clear();
     64 
     65     const int size = client()->listSize();
     66     for (int i = 0; i < size; ++i) {
     67         GtkWidget* item;
     68         if (client()->itemIsSeparator(i))
     69             item = gtk_separator_menu_item_new();
     70         else
     71             item = gtk_menu_item_new_with_label(client()->itemText(i).utf8().data());
     72 
     73         m_indexMap.add(item, i);
     74         g_signal_connect(item, "activate", G_CALLBACK(menuItemActivated), this);
     75 
     76         // FIXME: Apply the PopupMenuStyle from client()->itemStyle(i)
     77         gtk_widget_set_sensitive(item, client()->itemIsEnabled(i));
     78         gtk_menu_shell_append(GTK_MENU_SHELL(m_popup.get()), item);
     79         gtk_widget_show(item);
     80     }
     81 
     82     gtk_menu_set_active(m_popup.get(), index);
     83 
     84 
     85     // The size calls are directly copied from gtkcombobox.c which is LGPL
     86     GtkRequisition requisition;
     87     gtk_widget_set_size_request(GTK_WIDGET(m_popup.get()), -1, -1);
     88     gtk_widget_size_request(GTK_WIDGET(m_popup.get()), &requisition);
     89     gtk_widget_set_size_request(GTK_WIDGET(m_popup.get()), std::max(rect.width(), requisition.width), -1);
     90 
     91     GList* children = GTK_MENU_SHELL(m_popup.get())->children;
     92     if (size)
     93         for (int i = 0; i < size; i++) {
     94             if (i > index)
     95               break;
     96 
     97             GtkWidget* item = reinterpret_cast<GtkWidget*>(children->data);
     98             GtkRequisition itemRequisition;
     99             gtk_widget_get_child_requisition(item, &itemRequisition);
    100             m_menuPosition.setY(m_menuPosition.y() - itemRequisition.height);
    101 
    102             children = g_list_next(children);
    103         } else
    104             // Center vertically the empty popup in the combo box area
    105             m_menuPosition.setY(m_menuPosition.y() - rect.height() / 2);
    106 
    107     gtk_menu_popup(m_popup.get(), 0, 0, reinterpret_cast<GtkMenuPositionFunc>(menuPositionFunction), this, 0, gtk_get_current_event_time());
    108 }
    109 
    110 void PopupMenu::hide()
    111 {
    112     ASSERT(m_popup);
    113     gtk_menu_popdown(m_popup.get());
    114 }
    115 
    116 void PopupMenu::updateFromElement()
    117 {
    118     client()->setTextFromItem(client()->selectedIndex());
    119 }
    120 
    121 bool PopupMenu::itemWritingDirectionIsNatural()
    122 {
    123     return true;
    124 }
    125 
    126 void PopupMenu::menuItemActivated(GtkMenuItem* item, PopupMenu* that)
    127 {
    128     ASSERT(that->client());
    129     ASSERT(that->m_indexMap.contains(GTK_WIDGET(item)));
    130     that->client()->valueChanged(that->m_indexMap.get(GTK_WIDGET(item)));
    131 }
    132 
    133 void PopupMenu::menuUnmapped(GtkWidget*, PopupMenu* that)
    134 {
    135     ASSERT(that->client());
    136     that->client()->popupDidHide();
    137 }
    138 
    139 void PopupMenu::menuPositionFunction(GtkMenu*, gint* x, gint* y, gboolean* pushIn, PopupMenu* that)
    140 {
    141     *x = that->m_menuPosition.x();
    142     *y = that->m_menuPosition.y();
    143     *pushIn = true;
    144 }
    145 
    146 void PopupMenu::menuRemoveItem(GtkWidget* widget, PopupMenu* that)
    147 {
    148     ASSERT(that->m_popup);
    149     gtk_container_remove(GTK_CONTAINER(that->m_popup.get()), widget);
    150 }
    151 
    152 }
    153 
    154