menu.cpp

来自「A*算法 A*算法 A*算法 A*算法A*算法A*算法」· C++ 代码 · 共 2,300 行 · 第 1/5 页

CPP
2,300
字号
/////////////////////////////////////////////////////////////////////////////
// Name:        univ/menu.cpp
// Purpose:     wxMenuItem, wxMenu and wxMenuBar implementation
// Author:      Vadim Zeitlin
// Modified by:
// Created:     25.08.00
// RCS-ID:      $Id: menu.cpp,v 1.57 2005/05/18 21:36:13 MW Exp $
// Copyright:   (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

// ============================================================================
// declarations
// ============================================================================

// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------

#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
    #pragma implementation "univmenuitem.h"
    #pragma implementation "univmenu.h"
#endif

#include "wx/wxprec.h"

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

#ifndef WX_PRECOMP
    #include "wx/dynarray.h"
    #include "wx/control.h"      // for FindAccelIndex()
    #include "wx/menu.h"
    #include "wx/settings.h"
    #include "wx/accel.h"
    #include "wx/log.h"
#endif // WX_PRECOMP

#if wxUSE_MENUS

#include "wx/popupwin.h"
#include "wx/evtloop.h"
#include "wx/dcclient.h"
#include "wx/frame.h"

#include "wx/univ/renderer.h"

#ifdef __WXMSW__
    #include "wx/msw/private.h"
#endif // __WXMSW__

// ----------------------------------------------------------------------------
// wxMenuInfo contains all extra information about top level menus we need
// ----------------------------------------------------------------------------

class WXDLLEXPORT wxMenuInfo
{
public:
    // ctor
    wxMenuInfo(const wxString& text)
    {
        SetLabel(text);
        SetEnabled();
    }

    // modifiers

    void SetLabel(const wxString& text)
    {
        // remember the accel char (may be -1 if none)
        m_indexAccel = wxControl::FindAccelIndex(text, &m_label);

        // calculate the width later, after the menu bar is created
        m_width = 0;
    }

    void SetEnabled(bool enabled = true) { m_isEnabled = enabled; }

    // accessors

    const wxString& GetLabel() const { return m_label; }
    bool IsEnabled() const { return m_isEnabled; }
    wxCoord GetWidth(wxMenuBar *menubar) const
    {
        if ( !m_width )
        {
            wxConstCast(this, wxMenuInfo)->CalcWidth(menubar);
        }

        return m_width;
    }

    int GetAccelIndex() const { return m_indexAccel; }

private:
    void CalcWidth(wxMenuBar *menubar)
    {
        wxSize size;
        wxClientDC dc(menubar);
        dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
        dc.GetTextExtent(m_label, &size.x, &size.y);

        // adjust for the renderer we use and store the width
        m_width = menubar->GetRenderer()->GetMenuBarItemSize(size).x;
    }

    wxString m_label;
    wxCoord m_width;
    int m_indexAccel;
    bool m_isEnabled;
};

#include "wx/arrimpl.cpp"

WX_DEFINE_OBJARRAY(wxMenuInfoArray);

// ----------------------------------------------------------------------------
// wxPopupMenuWindow: a popup window showing a menu
// ----------------------------------------------------------------------------

class wxPopupMenuWindow : public wxPopupTransientWindow
{
public:
    wxPopupMenuWindow(wxWindow *parent, wxMenu *menu);

    ~wxPopupMenuWindow();

    // override the base class version to select the first item initially
    virtual void Popup(wxWindow *focus = NULL);

    // override the base class version to dismiss any open submenus
    virtual void Dismiss();

    // notify the menu when the window disappears from screen
    virtual void OnDismiss();

    // called when a submenu is dismissed
    void OnSubmenuDismiss(bool dismissParent);

    // the default wxMSW wxPopupTransientWindow::OnIdle disables the capture
    // when the cursor is inside the popup, which dsables the menu tracking
    // so override it to do nothing
#ifdef __WXMSW__
    void OnIdle(wxIdleEvent& WXUNUSED(event)) { }
#endif

    // get the currently selected item (may be NULL)
    wxMenuItem *GetCurrentItem() const
    {
        return m_nodeCurrent ? m_nodeCurrent->GetData() : NULL;
    }

    // find the menu item at given position
    wxMenuItemList::compatibility_iterator GetMenuItemFromPoint(const wxPoint& pt) const;

    // refresh the given item
    void RefreshItem(wxMenuItem *item);

    // preselect the first item
    void SelectFirst() { SetCurrent(m_menu->GetMenuItems().GetFirst()); }

    // process the key event, return true if done
    bool ProcessKeyDown(int key);

    // process mouse move event
    void ProcessMouseMove(const wxPoint& pt);

    // don't dismiss the popup window if the parent menu was clicked
    virtual bool ProcessLeftDown(wxMouseEvent& event);

protected:
    // how did we perform this operation?
    enum InputMethod
    {
        WithKeyboard,
        WithMouse
    };

    // draw the menu inside this window
    virtual void DoDraw(wxControlRenderer *renderer);

    // event handlers
    void OnLeftUp(wxMouseEvent& event);
    void OnMouseMove(wxMouseEvent& event);
    void OnMouseLeave(wxMouseEvent& event);
    void OnKeyDown(wxKeyEvent& event);

    // reset the current item and node
    void ResetCurrent();

    // set the current node and item withotu refreshing anything
    void SetCurrent(wxMenuItemList::compatibility_iterator node);
    virtual bool SetCurrent(bool doit = true){return wxPopupTransientWindow::SetCurrent(doit);};

    // change the current item refreshing the old and new items
    void ChangeCurrent(wxMenuItemList::compatibility_iterator node);

    // activate item, i.e. call either ClickItem() or OpenSubmenu() depending
    // on what it is, return true if something was done (i.e. it's not a
    // separator...)
    bool ActivateItem(wxMenuItem *item, InputMethod how = WithKeyboard);

    // send the event about the item click
    void ClickItem(wxMenuItem *item);

    // show the submenu for this item
    void OpenSubmenu(wxMenuItem *item, InputMethod how = WithKeyboard);

    // can this tiem be opened?
    bool CanOpen(wxMenuItem *item)
    {
        return item && item->IsEnabled() && item->IsSubMenu();
    }

    // dismiss the menu and all parent menus too
    void DismissAndNotify();

    // react to dimissing this menu and also dismiss the parent if
    // dismissParent
    void HandleDismiss(bool dismissParent);

    // do we have an open submenu?
    bool HasOpenSubmenu() const { return m_hasOpenSubMenu; }

    // get previous node after the current one
    wxMenuItemList::compatibility_iterator GetPrevNode() const;

    // get previous node before the given one, wrapping if it's the first one
    wxMenuItemList::compatibility_iterator GetPrevNode(wxMenuItemList::compatibility_iterator node) const;

    // get next node after the current one
    wxMenuItemList::compatibility_iterator GetNextNode() const;

    // get next node after the given one, wrapping if it's the last one
    wxMenuItemList::compatibility_iterator GetNextNode(wxMenuItemList::compatibility_iterator node) const;

private:
    // the menu we show
    wxMenu *m_menu;

    // the menu node corresponding to the current item
    wxMenuItemList::compatibility_iterator m_nodeCurrent;

    // do we currently have an opened submenu?
    bool m_hasOpenSubMenu;

    DECLARE_EVENT_TABLE()
};

// ----------------------------------------------------------------------------
// wxMenuKbdRedirector: an event handler which redirects kbd input to wxMenu
// ----------------------------------------------------------------------------

class wxMenuKbdRedirector : public wxEvtHandler
{
public:
    wxMenuKbdRedirector(wxMenu *menu) { m_menu = menu; }

    virtual bool ProcessEvent(wxEvent& event)
    {
        if ( event.GetEventType() == wxEVT_KEY_DOWN )
        {
            return m_menu->ProcessKeyDown(((wxKeyEvent &)event).GetKeyCode());
        }
        else
        {
            // return false;

            return wxEvtHandler::ProcessEvent(event);
        }
    }

private:
    wxMenu *m_menu;
};

// ----------------------------------------------------------------------------
// wxWin macros
// ----------------------------------------------------------------------------

IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler)
IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxWindow)
IMPLEMENT_DYNAMIC_CLASS(wxMenuItem, wxObject)

BEGIN_EVENT_TABLE(wxPopupMenuWindow, wxPopupTransientWindow)
    EVT_KEY_DOWN(wxPopupMenuWindow::OnKeyDown)

    EVT_LEFT_UP(wxPopupMenuWindow::OnLeftUp)
    EVT_MOTION(wxPopupMenuWindow::OnMouseMove)
    EVT_LEAVE_WINDOW(wxPopupMenuWindow::OnMouseLeave)
#ifdef __WXMSW__
    EVT_IDLE(wxPopupMenuWindow::OnIdle)
#endif
END_EVENT_TABLE()

BEGIN_EVENT_TABLE(wxMenuBar, wxMenuBarBase)
    EVT_KILL_FOCUS(wxMenuBar::OnKillFocus)

    EVT_KEY_DOWN(wxMenuBar::OnKeyDown)

    EVT_LEFT_DOWN(wxMenuBar::OnLeftDown)
    EVT_MOTION(wxMenuBar::OnMouseMove)
END_EVENT_TABLE()

// ============================================================================
// implementation
// ============================================================================

// ----------------------------------------------------------------------------
// wxPopupMenuWindow
// ----------------------------------------------------------------------------

wxPopupMenuWindow::wxPopupMenuWindow(wxWindow *parent, wxMenu *menu)
{
    m_menu = menu;
    m_hasOpenSubMenu = false;

    ResetCurrent();

    (void)Create(parent, wxBORDER_RAISED);

    SetCursor(wxCURSOR_ARROW);
}

wxPopupMenuWindow::~wxPopupMenuWindow()
{
    // When m_popupMenu in wxMenu is deleted because it
    // is a child of an old menu bar being deleted (note: it does
    // not get destroyed by the wxMenu destructor, but
    // by DestroyChildren()), m_popupMenu should be reset to NULL.

    m_menu->m_popupMenu = NULL;
}

// ----------------------------------------------------------------------------
// wxPopupMenuWindow current item/node handling
// ----------------------------------------------------------------------------

void wxPopupMenuWindow::ResetCurrent()
{
#if wxUSE_STL
    SetCurrent(wxMenuItemList::compatibility_iterator());
#else
    SetCurrent((wxwxMenuItemListNode *)NULL);
#endif
}

void wxPopupMenuWindow::SetCurrent(wxMenuItemList::compatibility_iterator node)
{
    m_nodeCurrent = node;
}

void wxPopupMenuWindow::ChangeCurrent(wxMenuItemList::compatibility_iterator node)
{
    if ( node != m_nodeCurrent )
    {
        wxMenuItemList::compatibility_iterator nodeOldCurrent = m_nodeCurrent;

        m_nodeCurrent = node;

        if ( nodeOldCurrent )
        {
            wxMenuItem *item = nodeOldCurrent->GetData();
            wxCHECK_RET( item, _T("no current item?") );

            // if it was the currently opened menu, close it
            if ( item->IsSubMenu() && item->GetSubMenu()->IsShown() )
            {
                item->GetSubMenu()->Dismiss();
                OnSubmenuDismiss( false );
            }

            RefreshItem(item);
        }

        if ( m_nodeCurrent )
            RefreshItem(m_nodeCurrent->GetData());
    }
}

wxMenuItemList::compatibility_iterator wxPopupMenuWindow::GetPrevNode() const
{
    // return the last node if there had been no previously selected one
    return m_nodeCurrent ? GetPrevNode(m_nodeCurrent)
                         : m_menu->GetMenuItems().GetLast();
}

wxMenuItemList::compatibility_iterator
wxPopupMenuWindow::GetPrevNode(wxMenuItemList::compatibility_iterator node) const
{
    if ( node )
    {
        node = node->GetPrevious();
        if ( !node )
        {
            node = m_menu->GetMenuItems().GetLast();
        }
    }
    //else: the menu is empty

    return node;
}

wxMenuItemList::compatibility_iterator wxPopupMenuWindow::GetNextNode() const
{
    // return the first node if there had been no previously selected one
    return m_nodeCurrent ? GetNextNode(m_nodeCurrent)
                         : m_menu->GetMenuItems().GetFirst();
}

wxMenuItemList::compatibility_iterator
wxPopupMenuWindow::GetNextNode(wxMenuItemList::compatibility_iterator node) const
{
    if ( node )
    {
        node = node->GetNext();
        if ( !node )
        {
            node = m_menu->GetMenuItems().GetFirst();
        }
    }
    //else: the menu is empty

    return node;
}

// ----------------------------------------------------------------------------
// wxPopupMenuWindow popup/dismiss
// ----------------------------------------------------------------------------

void wxPopupMenuWindow::Popup(wxWindow *focus)
{
    // check that the current item had been properly reset before
    wxASSERT_MSG( !m_nodeCurrent ||
                  m_nodeCurrent == m_menu->GetMenuItems().GetFirst(),
                  _T("menu current item preselected incorrectly") );

    wxPopupTransientWindow::Popup(focus);

    // the base class no-longer captures the mouse automatically when Popup
    // is called, so do it here to allow the menu tracking to work
    if ( !HasCapture() )
        CaptureMouse();

#ifdef __WXMSW__
    // ensure that this window is really on top of everything: without using
    // SetWindowPos() it can be covered by its parent menu which is not
    // really what we want
    wxMenu *menuParent = m_menu->GetParent();
    if ( menuParent )
    {
        wxPopupMenuWindow *win = menuParent->m_popupMenu;

        // if we're shown, the parent menu must be also shown
        wxCHECK_RET( win, _T("parent menu is not shown?") );

        if ( !::SetWindowPos(GetHwndOf(win), GetHwnd(),
                             0, 0, 0, 0,
                             SWP_NOMOVE | SWP_NOSIZE | SWP_NOREDRAW) )

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?