Home | History | Annotate | Download | only in haiku
      1 /*
      2  * This file is part of the popup menu implementation for <select> elements in WebCore.
      3  *
      4  * Copyright (C) 2010 Stephan Amus <superstippi (at) gmx.de>
      5  * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
      6  *
      7  * This library is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU Library General Public
      9  * License as published by the Free Software Foundation; either
     10  * version 2 of the License, or (at your option) any later version.
     11  *
     12  * This library is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  * Library General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU Library General Public License
     18  * along with this library; see the file COPYING.LIB.  If not, write to
     19  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
     20  * Boston, MA 02111-1307, USA.
     21  *
     22  */
     23 
     24 #include "config.h"
     25 #include "PopupMenuHaiku.h"
     26 
     27 #include "FrameView.h"
     28 
     29 #include "NotImplemented.h"
     30 #include <Application.h>
     31 #include <Handler.h>
     32 #include <MenuItem.h>
     33 #include <Message.h>
     34 #include <PopUpMenu.h>
     35 #include <String.h>
     36 #include <Window.h>
     37 #include <support/Autolock.h>
     38 #include <support/Locker.h>
     39 
     40 namespace WebCore {
     41 
     42 static const uint32 kPopupResult = 'pmrs';
     43 static const uint32 kPopupHidden = 'pmhd';
     44 
     45 // This BHandler is added to the application (main) thread, so that we
     46 // invoke methods on the PopupMenuClient within the main thread.
     47 class PopupMenuHandler : public BHandler {
     48 public:
     49     PopupMenuHandler(PopupMenuClient* popupClient)
     50         : m_popupClient(popupClient)
     51     {
     52     }
     53 
     54     virtual void MessageReceived(BMessage* message)
     55     {
     56         switch (message->what) {
     57         case kPopupResult: {
     58             int32 index = 0;
     59             message->FindInt32("index", &index);
     60             m_popupClient->valueChanged(index);
     61             break;
     62         }
     63         case kPopupHidden:
     64             m_popupClient->popupDidHide();
     65             break;
     66 
     67         default:
     68             BHandler::MessageReceived(message);
     69         }
     70     }
     71 
     72 private:
     73     PopupMenuClient* m_popupClient;
     74 };
     75 
     76 class HaikuPopup : public BPopUpMenu {
     77 public:
     78     HaikuPopup(PopupMenuClient* popupClient)
     79         : BPopUpMenu("WebCore Popup", true, false)
     80         , m_popupClient(popupClient)
     81         , m_Handler(popupClient)
     82     {
     83         if (be_app->Lock()) {
     84             be_app->AddHandler(&m_Handler);
     85             be_app->Unlock();
     86         }
     87         SetAsyncAutoDestruct(false);
     88     }
     89 
     90     virtual ~HaikuPopup()
     91     {
     92         if (be_app->Lock()) {
     93             be_app->RemoveHandler(&m_Handler);
     94             be_app->Unlock();
     95         }
     96     }
     97 
     98     void show(const IntRect& rect, FrameView* view, int index)
     99     {
    100         // Clean out the menu first
    101         for (int32 i = CountItems() - 1; i >= 0; i--)
    102             delete RemoveItem(i);
    103 
    104         // Popuplate the menu from the client
    105         int itemCount = m_popupClient->listSize();
    106         for (int i = 0; i < itemCount; i++) {
    107             if (m_popupClient->itemIsSeparator(i))
    108                 AddSeparatorItem();
    109             else {
    110                 // NOTE: WebCore distinguishes between "Group" and "Label"
    111                 // here, but both types of item (radio or check mark) currently
    112                 // look the same on Haiku.
    113                 BString label(m_popupClient->itemText(i));
    114                 BMessage* message = new BMessage(kPopupResult);
    115                 message->AddInt32("index", i);
    116                 BMenuItem* item = new BMenuItem(label.String(), message);
    117                 AddItem(item);
    118                 item->SetTarget(BMessenger(&m_Handler));
    119                 item->SetEnabled(m_popupClient->itemIsEnabled(i));
    120                 item->SetMarked(i == index);
    121             }
    122         }
    123 
    124         // We need to force a layout now, or the item frames will not be
    125         // computed yet, so we cannot move the current item under the mouse.
    126         DoLayout();
    127 
    128         // Account for frame of menu field
    129         BRect screenRect(view->contentsToScreen(rect));
    130         screenRect.OffsetBy(2, 2);
    131         // Move currently selected item under the mouse.
    132         if (BMenuItem* item = ItemAt(index))
    133             screenRect.OffsetBy(0, -item->Frame().top);
    134 
    135         BRect openRect = Bounds().OffsetToSelf(screenRect.LeftTop());
    136 
    137         Go(screenRect.LeftTop(), true, true, openRect, true);
    138     }
    139 
    140     void hide()
    141     {
    142         if (!IsHidden())
    143             Hide();
    144     }
    145 
    146 private:
    147     virtual void Hide()
    148     {
    149         BPopUpMenu::Hide();
    150         be_app->PostMessage(kPopupHidden, &m_Handler);
    151     }
    152 
    153     PopupMenuClient* m_popupClient;
    154     PopupMenuHandler m_Handler;
    155 };
    156 
    157 PopupMenuHaiku::PopupMenuHaiku(PopupMenuClient* client)
    158     : m_popupClient(client)
    159     , m_menu(new HaikuPopup(client))
    160 {
    161     // We don't need additional references to the client, since we completely
    162     // control any sub-objects we create that need it as well.
    163 }
    164 
    165 PopupMenuHaiku::~PopupMenuHaiku()
    166 {
    167     delete m_menu;
    168 }
    169 
    170 void PopupMenuHaiku::disconnectClient()
    171 {
    172     m_popupClient = 0;
    173 }
    174 
    175 void PopupMenuHaiku::show(const IntRect& rect, FrameView* view, int index)
    176 {
    177     // The menu will update itself from the PopupMenuClient before showing.
    178     m_menu->show(rect, view, index);
    179 }
    180 
    181 void PopupMenuHaiku::hide()
    182 {
    183     m_menu->hide();
    184 }
    185 
    186 void PopupMenuHaiku::updateFromElement()
    187 {
    188     client()->setTextFromItem(m_popupClient->selectedIndex());
    189 }
    190 
    191 } // namespace WebCore
    192 
    193