1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/qt/menu.cpp
3 // Author:      Peter Most, Mariano Reingart
4 // Copyright:   (c) 2010 wxWidgets dev team
5 // Licence:     wxWindows licence
6 /////////////////////////////////////////////////////////////////////////////
7 
8 // For compilers that support precompilation, includes "wx.h".
9 #include "wx/wxprec.h"
10 
11 #include "wx/menu.h"
12 #include "wx/qt/private/utils.h"
13 #include "wx/qt/private/converter.h"
14 #include "wx/stockitem.h"
15 
16 #include <QtWidgets/QMenu>
17 #include <QtWidgets/QMenuBar>
18 
ApplyStyle(QMenu * qtMenu,long style)19 static void ApplyStyle( QMenu *qtMenu, long style )
20 {
21     if ( style & wxMENU_TEAROFF )
22         qtMenu->setTearOffEnabled( true );
23 }
24 
wxMenu(long style)25 wxMenu::wxMenu(long style)
26     : wxMenuBase( style )
27 {
28     m_qtMenu = new QMenu();
29 
30     ApplyStyle( m_qtMenu, style );
31 }
32 
wxMenu(const wxString & title,long style)33 wxMenu::wxMenu(const wxString& title, long style)
34     : wxMenuBase( title, style )
35 {
36     m_qtMenu = new QMenu( wxQtConvertString( title ));
37 
38     ApplyStyle( m_qtMenu, style );
39 }
40 
41 
42 
GetMenuItemAt(const wxMenu * menu,size_t position)43 static wxMenuItem *GetMenuItemAt( const wxMenu *menu, size_t position )
44 {
45     // FindItemByPosition() is doing the same test, but we want to prevent
46     // the warning message it prints when an illegal index is used.
47 
48     if ( position < menu->GetMenuItemCount() )
49         return menu->FindItemByPosition( position );
50     else
51         return NULL;
52 }
53 
AddItemActionToGroup(const wxMenuItem * groupItem,QAction * itemAction)54 static void AddItemActionToGroup( const wxMenuItem *groupItem, QAction *itemAction )
55 {
56     QAction *groupItemAction = groupItem->GetHandle();
57     QActionGroup *itemActionGroup = groupItemAction->actionGroup();
58     wxASSERT_MSG( itemActionGroup != NULL, "An action group should have been setup" );
59     itemActionGroup->addAction( itemAction );
60 }
61 
InsertMenuItemAction(const wxMenu * menu,const wxMenuItem * previousItem,wxMenuItem * item,const wxMenuItem * successiveItem)62 static void InsertMenuItemAction( const wxMenu *menu, const wxMenuItem *previousItem,
63     wxMenuItem *item, const wxMenuItem *successiveItem )
64 {
65     QMenu *qtMenu = menu->GetHandle();
66     QAction *itemAction = item->GetHandle();
67     switch ( item->GetKind() )
68     {
69         case wxITEM_RADIO:
70             // If a neighbouring menu item is a radio item then add this item to the
71             // same action group, otherwise start a new group:
72 
73             if ( previousItem != NULL && previousItem->GetKind() == wxITEM_RADIO )
74             {
75                 AddItemActionToGroup( previousItem, itemAction );
76             }
77             else if ( successiveItem != NULL && successiveItem->GetKind() == wxITEM_RADIO )
78             {
79                 AddItemActionToGroup( successiveItem, itemAction );
80             }
81             else
82             {
83                 QActionGroup *actionGroup = new QActionGroup( qtMenu );
84                 actionGroup->addAction( itemAction );
85                 item->Check();
86                 wxASSERT_MSG( itemAction->actionGroup() == actionGroup, "Must be the same action group" );
87             }
88             break;
89         case wxITEM_NORMAL:
90         {
91             // If the inserted action is a submenu, set the owner for the submenu.
92             if ( item->IsSubMenu() )
93             {
94                 item->GetSubMenu()->GetHandle()->setParent(qtMenu, Qt::Popup);
95             }
96 
97             wxWindowID id = item->GetId();
98             if ( wxIsStockID( id ) )
99             {
100                 itemAction->setText( wxQtConvertString( wxGetStockLabel( id ) ) );
101                 wxAcceleratorEntry accel = wxGetStockAccelerator( id );
102                 QString shortcut;
103                 if ( id == wxID_EXIT )
104                 {
105                     shortcut = QStringLiteral("Ctrl+Q");
106                 }
107                 else if ( accel.IsOk() )
108                 {
109                     shortcut = wxQtConvertString( accel.ToRawString() );
110                 }
111                 if ( !shortcut.isEmpty() )
112                 {
113                     itemAction->setShortcut( QKeySequence( shortcut ) );
114                 }
115             }
116             break;
117         }
118         default:
119             break;
120     }
121     // Insert the action into the actual menu:
122     QAction *successiveItemAction = ( successiveItem != NULL ) ? successiveItem->GetHandle() : NULL;
123     qtMenu->insertAction( successiveItemAction, itemAction );
124     // Menu items in Qt can be part of multiple menus, so a menu will not take ownership
125     // when one is added to it. Take it explicitly, otherwise it will create a memory leak.
126     itemAction->setParent(qtMenu);
127 }
128 
DoAppend(wxMenuItem * item)129 wxMenuItem *wxMenu::DoAppend(wxMenuItem *item)
130 {
131     // Get the previous/successive items *before* we call the base class methods,
132     // because afterwards it is less clear where these items end up:
133 
134     wxMenuItem *previousItem = GetMenuItemAt( this, GetMenuItemCount() - 1 );
135     wxMenuItem *successiveItem = GetMenuItemAt( this, GetMenuItemCount() );
136 
137     if ( wxMenuBase::DoAppend( item ) == NULL )
138         return NULL;
139 
140     InsertMenuItemAction( this, previousItem, item, successiveItem );
141 
142     return item;
143 }
144 
145 
DoInsert(size_t insertPosition,wxMenuItem * item)146 wxMenuItem *wxMenu::DoInsert(size_t insertPosition, wxMenuItem *item)
147 {
148     // Get the previous/successive items *before* we call the base class methods,
149     // because afterwards it is less clear where these items end up:
150 
151     wxMenuItem *previousItem = GetMenuItemAt( this, insertPosition - 1 );
152     wxMenuItem *successiveItem = GetMenuItemAt( this, insertPosition );
153 
154     if ( wxMenuBase::DoInsert( insertPosition, item ) == NULL )
155         return NULL;
156 
157     InsertMenuItemAction( this, previousItem, item, successiveItem );
158 
159     return item;
160 }
161 
162 
DoRemove(wxMenuItem * item)163 wxMenuItem *wxMenu::DoRemove(wxMenuItem *item)
164 {
165     if ( wxMenuBase::DoRemove( item ) == NULL )
166         return NULL;
167 
168     m_qtMenu->removeAction( item->GetHandle() );
169 
170     return item;
171 }
172 
173 
GetHandle() const174 QMenu *wxMenu::GetHandle() const
175 {
176     return m_qtMenu;
177 }
178 
179 
180 //##############################################################################
181 
wxMenuBar()182 wxMenuBar::wxMenuBar()
183 {
184     m_qtMenuBar  = new QMenuBar();
185     PostCreation(false);
186 }
187 
wxMenuBar(long WXUNUSED (style))188 wxMenuBar::wxMenuBar( long WXUNUSED( style ))
189 {
190     m_qtMenuBar = new QMenuBar();
191     PostCreation(false);
192 }
193 
wxMenuBar(size_t count,wxMenu * menus[],const wxString titles[],long WXUNUSED (style))194 wxMenuBar::wxMenuBar(size_t count, wxMenu *menus[], const wxString titles[], long WXUNUSED( style ))
195 {
196     m_qtMenuBar = new QMenuBar();
197 
198     for ( size_t i = 0; i < count; ++i )
199         Append( menus[ i ], titles[ i ] );
200 
201     PostCreation(false);
202 }
203 
204 
SetTitle(wxMenu * menu,const wxString & title)205 static QMenu *SetTitle( wxMenu *menu, const wxString &title )
206 {
207     menu->SetTitle(title);
208 
209     QMenu *qtMenu = menu->GetHandle();
210     qtMenu->setTitle( wxQtConvertString( title ));
211 
212     return qtMenu;
213 }
214 
215 
Append(wxMenu * menu,const wxString & title)216 bool wxMenuBar::Append( wxMenu *menu, const wxString& title )
217 {
218     if ( !wxMenuBarBase::Append( menu, title ))
219         return false;
220 
221     // Override the stored menu title with the given one:
222 
223     QMenu *qtMenu = SetTitle( menu, title );
224     m_qtMenuBar->addMenu( qtMenu );
225     // Menus in Qt can be reused as popups, so a menu bar will not take ownership when
226     // one is added to it. Take it explicitly, otherwise there will be a memory leak.
227     qtMenu->setParent(m_qtMenuBar, Qt::Popup); // must specify window type for correct display!
228 
229     return true;
230 }
231 
232 
GetActionAt(const QWidget * qtWidget,size_t pos)233 static QAction *GetActionAt( const QWidget *qtWidget, size_t pos )
234 {
235     QList< QAction * > actions = qtWidget->actions();
236     return pos < static_cast< unsigned >( actions.size() ) ? actions.at( pos ) : NULL;
237 }
238 
239 
Insert(size_t pos,wxMenu * menu,const wxString & title)240 bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
241 {
242     if ( !wxMenuBarBase::Insert( pos, menu, title ))
243         return false;
244 
245     // Override the stored menu title with the given one:
246 
247     QMenu *qtMenu = SetTitle( menu, title );
248     QAction *qtAction = GetActionAt( m_qtMenuBar, pos );
249     m_qtMenuBar->insertMenu( qtAction, qtMenu );
250     qtMenu->setParent(m_qtMenuBar, Qt::Popup); // must specify window type for correct display!
251 
252     return true;
253 }
254 
Remove(size_t pos)255 wxMenu *wxMenuBar::Remove(size_t pos)
256 {
257     wxMenu *menu;
258 
259     if (( menu = wxMenuBarBase::Remove( pos )) == NULL )
260         return NULL;
261 
262     m_qtMenuBar->removeAction( GetActionAt( m_qtMenuBar, pos ));
263     return menu;
264 }
265 
EnableTop(size_t pos,bool enable)266 void wxMenuBar::EnableTop(size_t pos, bool enable)
267 {
268     QAction *qtAction = GetActionAt( m_qtMenuBar, pos );
269     qtAction->setEnabled( enable );
270 }
271 
IsEnabledTop(size_t pos) const272 bool wxMenuBar::IsEnabledTop(size_t pos) const
273 {
274     QAction *qtAction = GetActionAt( m_qtMenuBar, pos );
275     return qtAction->isEnabled();
276 }
277 
278 
SetMenuLabel(size_t pos,const wxString & label)279 void wxMenuBar::SetMenuLabel(size_t pos, const wxString& label)
280 {
281     QAction *qtAction = GetActionAt( m_qtMenuBar, pos );
282     QMenu *qtMenu = qtAction->menu();
283     qtMenu->setTitle( wxQtConvertString( label ));
284 }
285 
GetMenuLabel(size_t pos) const286 wxString wxMenuBar::GetMenuLabel(size_t pos) const
287 {
288     QAction *qtAction = GetActionAt( m_qtMenuBar, pos );
289     QMenu *qtMenu = qtAction->menu();
290 
291     return wxQtConvertString( qtMenu->title() );
292 }
293 
Attach(wxFrame * frame)294 void wxMenuBar::Attach(wxFrame *frame)
295 {
296     // sanity check as setMenuBar takes ownership
297     wxCHECK_RET( m_qtMenuBar, "Menu bar has been previously deleted by Qt");
298     wxMenuBarBase::Attach(frame);
299 }
300 
Detach()301 void wxMenuBar::Detach()
302 {
303     // the QMenuBar probably was deleted by Qt as setMenuBar takes ownership
304     m_qtMenuBar = NULL;
305     wxMenuBarBase::Detach();
306 }
307 
GetHandle() const308 QWidget *wxMenuBar::GetHandle() const
309 {
310     return m_qtMenuBar;
311 }
312