menu.cpp

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

CPP
2,300
字号
            // close just this menu
            Dismiss();
            HandleDismiss(false);
            break;

        case WXK_RETURN:
            processed = ActivateItem(item);
            break;

        case WXK_HOME:
            ChangeCurrent(m_menu->GetMenuItems().GetFirst());
            break;

        case WXK_END:
            ChangeCurrent(m_menu->GetMenuItems().GetLast());
            break;

        case WXK_UP:
        case WXK_DOWN:
            {
                bool up = key == WXK_UP;

                wxMenuItemList::compatibility_iterator nodeStart = up ? GetPrevNode()
                                                     : GetNextNode(),
                                     node = nodeStart;
                while ( node && node->GetData()->IsSeparator() )
                {
                    node = up ? GetPrevNode(node) : GetNextNode(node);

                    if ( node == nodeStart )
                    {
                        // nothing but separators and disabled items in this
                        // menu, break out
#if wxUSE_STL
                        node = wxMenuItemList::compatibility_iterator();
#else
                        node = NULL;
#endif
                    }
                }

                if ( node )
                {
                    ChangeCurrent(node);
                }
                else
                {
                    processed = false;
                }
            }
            break;

        case WXK_RIGHT:
            // don't try to reopen an already opened menu
            if ( !HasOpenSubmenu() && CanOpen(item) )
            {
                OpenSubmenu(item);
            }
            else
            {
                processed = false;
            }
            break;

        default:
            // look for the menu item starting with this letter
            if ( wxIsalnum((wxChar)key) )
            {
                // we want to start from the item after this one because
                // if we're already on the item with the given accel we want to
                // go to the next one, not to stay in place
                wxMenuItemList::compatibility_iterator nodeStart = GetNextNode();

                // do we have more than one item with this accel?
                bool notUnique = false;

                // translate everything to lower case before comparing
                wxChar chAccel = (wxChar)wxTolower(key);

                // loop through all items searching for the item with this
                // accel
                wxMenuItemList::compatibility_iterator node = nodeStart,
#if wxUSE_STL
                                                       nodeFound = wxMenuItemList::compatibility_iterator();
#else
                                                       nodeFound = NULL;
#endif
                for ( ;; )
                {
                    item = node->GetData();

                    int idxAccel = item->GetAccelIndex();
                    if ( idxAccel != -1 &&
                         wxTolower(item->GetLabel()[(size_t)idxAccel])
                            == chAccel )
                    {
                        // ok, found an item with this accel
                        if ( !nodeFound )
                        {
                            // store it but continue searching as we need to
                            // know if it's the only item with this accel or if
                            // there are more
                            nodeFound = node;
                        }
                        else // we already had found such item
                        {
                            notUnique = true;

                            // no need to continue further, we won't find
                            // anything we don't already know
                            break;
                        }
                    }

                    // we want to iterate over all items wrapping around if
                    // necessary
                    node = GetNextNode(node);
                    if ( node == nodeStart )
                    {
                        // we've seen all nodes
                        break;
                    }
                }

                if ( nodeFound )
                {
                    item = nodeFound->GetData();

                    // go to this item anyhow
                    ChangeCurrent(nodeFound);

                    if ( !notUnique && item->IsEnabled() )
                    {
                        // unique item with this accel - activate it
                        processed = ActivateItem(item);
                    }
                    //else: just select it but don't activate as the user might
                    //      have wanted to activate another item

                    // skip "processed = false" below
                    break;
                }
            }

            processed = false;
    }

    return processed;
}

// ----------------------------------------------------------------------------
// wxMenu
// ----------------------------------------------------------------------------

void wxMenu::Init()
{
    m_geometry = NULL;

    m_popupMenu = NULL;

    m_startRadioGroup = -1;
}

wxMenu::~wxMenu()
{
    delete m_geometry;
    delete m_popupMenu;
}

// ----------------------------------------------------------------------------
// wxMenu and wxMenuGeometryInfo
// ----------------------------------------------------------------------------

wxMenuGeometryInfo::~wxMenuGeometryInfo()
{
}

const wxMenuGeometryInfo& wxMenu::GetGeometryInfo() const
{
    if ( !m_geometry )
    {
        if ( m_popupMenu )
        {
            wxConstCast(this, wxMenu)->m_geometry =
                m_popupMenu->GetRenderer()->GetMenuGeometry(m_popupMenu, *this);
        }
        else
        {
            wxFAIL_MSG( _T("can't get geometry without window") );
        }
    }

    return *m_geometry;
}

void wxMenu::InvalidateGeometryInfo()
{
    if ( m_geometry )
    {
        delete m_geometry;
        m_geometry = NULL;
    }
}

// ----------------------------------------------------------------------------
// wxMenu adding/removing items
// ----------------------------------------------------------------------------

void wxMenu::OnItemAdded(wxMenuItem *item)
{
    InvalidateGeometryInfo();

#if wxUSE_ACCEL
    AddAccelFor(item);
#endif // wxUSE_ACCEL

    // the submenus of a popup menu should have the same invoking window as it
    // has
    if ( m_invokingWindow && item->IsSubMenu() )
    {
        item->GetSubMenu()->SetInvokingWindow(m_invokingWindow);
    }
}

void wxMenu::EndRadioGroup()
{
    // we're not inside a radio group any longer
    m_startRadioGroup = -1;
}

wxMenuItem* wxMenu::DoAppend(wxMenuItem *item)
{
    if ( item->GetKind() == wxITEM_RADIO )
    {
        int count = GetMenuItemCount();

        if ( m_startRadioGroup == -1 )
        {
            // start a new radio group
            m_startRadioGroup = count;

            // for now it has just one element
            item->SetAsRadioGroupStart();
            item->SetRadioGroupEnd(m_startRadioGroup);
        }
        else // extend the current radio group
        {
            // we need to update its end item
            item->SetRadioGroupStart(m_startRadioGroup);
            wxMenuItemList::compatibility_iterator node = GetMenuItems().Item(m_startRadioGroup);

            if ( node )
            {
                node->GetData()->SetRadioGroupEnd(count);
            }
            else
            {
                wxFAIL_MSG( _T("where is the radio group start item?") );
            }
        }
    }
    else // not a radio item
    {
        EndRadioGroup();
    }

    if ( !wxMenuBase::DoAppend(item) )
        return NULL;

    OnItemAdded(item);

    return item;
}

wxMenuItem* wxMenu::DoInsert(size_t pos, wxMenuItem *item)
{
    if ( !wxMenuBase::DoInsert(pos, item) )
        return NULL;

    OnItemAdded(item);

    return item;
}

wxMenuItem *wxMenu::DoRemove(wxMenuItem *item)
{
    wxMenuItem *itemOld = wxMenuBase::DoRemove(item);

    if ( itemOld )
    {
        InvalidateGeometryInfo();

#if wxUSE_ACCEL
        RemoveAccelFor(item);
#endif // wxUSE_ACCEL
    }

    return itemOld;
}

// ----------------------------------------------------------------------------
// wxMenu attaching/detaching
// ----------------------------------------------------------------------------

void wxMenu::Attach(wxMenuBarBase *menubar)
{
    wxMenuBase::Attach(menubar);

    wxCHECK_RET( m_menuBar, _T("menubar can't be NULL after attaching") );

    // unfortunately, we can't use m_menuBar->GetEventHandler() here because,
    // if the menubar is currently showing a menu, its event handler is a
    // temporary one installed by wxPopupWindow and so will disappear soon any
    // any attempts to use it from the newly attached menu would result in a
    // crash
    //
    // so we use the menubar itself, even if it's a pity as it means we can't
    // redirect all menu events by changing the menubar handler (FIXME)
    SetNextHandler(m_menuBar);
}

void wxMenu::Detach()
{
    wxMenuBase::Detach();
}

// ----------------------------------------------------------------------------
// wxMenu misc functions
// ----------------------------------------------------------------------------

wxWindow *wxMenu::GetRootWindow() const
{
    if ( GetMenuBar() )
    {
        // simple case - a normal menu attached to the menubar
        return GetMenuBar();
    }

    // we're a popup menu but the trouble is that only the top level popup menu
    // has a pointer to the invoking window, so we must walk up the menu chain
    // if needed
    wxWindow *win = GetInvokingWindow();
    if ( win )
    {
        // we already have it
        return win;
    }

    wxMenu *menu = GetParent();
    while ( menu )
    {
        // We are a submenu of a menu of a menubar
        if (menu->GetMenuBar())
           return menu->GetMenuBar();

        win = menu->GetInvokingWindow();
        if ( win )
            break;

        menu = menu->GetParent();
    }

    // we're probably going to crash in the caller anyhow, but try to detect
    // this error as soon as possible
    wxASSERT_MSG( win, _T("menu without any associated window?") );

    // also remember it in this menu so that we don't have to search for it the
    // next time
    wxConstCast(this, wxMenu)->m_invokingWindow = win;

    return win;
}

wxRenderer *wxMenu::GetRenderer() const
{
    // we're going to crash without renderer!
    wxCHECK_MSG( m_popupMenu, NULL, _T("neither popup nor menubar menu?") );

    return m_popupMenu->GetRenderer();
}

void wxMenu::RefreshItem(wxMenuItem *item)
{
    // the item geometry changed, so our might have changed as well
    InvalidateGeometryInfo();

    if ( IsShown() )
    {
        // this would be a bug in IsShown()
        wxCHECK_RET( m_popupMenu, _T("must have popup window if shown!") );

        // recalc geometry to update the item height and such
        (void)GetGeometryInfo();

        m_popupMenu->RefreshItem(item);
    }
}

// ----------------------------------------------------------------------------
// wxMenu showing and hiding
// ----------------------------------------------------------------------------

bool wxMenu::IsShown() const
{
    return m_popupMenu && m_popupMenu->IsShown();
}

void wxMenu::OnDismiss(bool dismissParent)
{
    if ( m_menuParent )
    {
        // always notify the parent about submenu disappearance
        wxPopupMenuWindow *win = m_menuParent->m_popupMenu;
        if ( win )
        {
            win->OnSubmenuDismiss( true );
        }
        else
        {
            wxFAIL_MSG( _T("parent menu not shown?") );
        }

        // and if we dismiss everything, propagate to parent
        if ( dismissParent )
        {
            // dismissParent is recursive
            m_menuParent->Dismiss();
            m_menuParent->OnDismiss(true);
        }
    }
    else // no parent menu
    {
        // notify the menu bar if we're a top level menu
        if ( m_menuBar )
        {
            m_menuBar->OnDismissMenu(dismissParent);
        }
        else // popup menu
        {
            wxCHECK_RET( m_invokingWindow, _T("what kind of menu is this?") );

            m_invokingWindow->DismissPopupMenu();

            // Why reset it here? We need it for sending the event to...
            // SetInvokingWindow(NULL);
        }
    }
}

void wxMenu::Popup(const wxPoint& pos, const wxSize& size, bool selectFirst)
{
    // create the popup window if not done yet
    if ( !m_popupMenu )
    {
        m_popupMenu = new wxPopupMenuWindow(GetRootWindow(), this);
    }

    // select the first item unless disabled
    if ( selectFirst )
    {

⌨️ 快捷键说明

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