xref: /trunk/main/vcl/win/source/window/salmenu.cxx (revision 92968359)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_vcl.hxx"
26 
27 #include <tools/svwin.h>
28 
29 #include <vcl/menu.hxx>
30 #include <vcl/sysdata.hxx>
31 
32 #include <win/wincomp.hxx>
33 #include <win/saldata.hxx>
34 #include <win/salinst.h>
35 #include <win/salframe.h>
36 #include <win/salmenu.h>
37 
38 #include <impbmp.hxx>
39 #include <salgdi.hxx>
40 
41 // uncomment the following line to have ownerdrawn menus, i.e. with bitmaps
42 // however, this is incompatible with OLE inplace editing
43 // so it is not activated by default
44 //#define OWNERDRAW
45 
46 static DWORD myerr=0;
47 
48 // =======================================================================
49 
IsKnownMenuHandle(HMENU hMenu)50 sal_Bool SalData::IsKnownMenuHandle( HMENU hMenu )
51 {
52     if( mhMenuSet.find( hMenu ) == mhMenuSet.end() )
53         return FALSE;
54     else
55         return TRUE;
56 }
57 
58 // =======================================================================
59 
60 // WinSalInst factory methods
61 
CreateMenu(sal_Bool bMenuBar,Menu *)62 SalMenu* WinSalInstance::CreateMenu( sal_Bool bMenuBar, Menu* )
63 {
64     WinSalMenu *pSalMenu = new WinSalMenu();
65 
66     pSalMenu->mbMenuBar = bMenuBar;
67     pSalMenu->mhWnd     = NULL;
68     if( bMenuBar )
69         pSalMenu->mhMenu = ::CreateMenu();
70     else
71         pSalMenu->mhMenu = ::CreatePopupMenu();
72 
73     if( pSalMenu->mhMenu )
74         GetSalData()->mhMenuSet.insert( pSalMenu->mhMenu );
75 
76     return pSalMenu;
77 }
78 
DestroyMenu(SalMenu * pSalMenu)79 void WinSalInstance::DestroyMenu( SalMenu* pSalMenu )
80 {
81     delete pSalMenu;
82 }
83 
84 
CreateMenuItem(const SalItemParams * pItemData)85 SalMenuItem* WinSalInstance::CreateMenuItem( const SalItemParams* pItemData )
86 {
87     if( !pItemData )
88         return NULL;
89 
90     WinSalMenuItem *pSalMenuItem = new WinSalMenuItem();
91     memset( &pSalMenuItem->mInfo, 0, sizeof( MENUITEMINFOW ) );
92     pSalMenuItem->mInfo.cbSize = sizeof( MENUITEMINFOW );
93 
94     if( pItemData->eType == MENUITEM_SEPARATOR )
95     {
96         // separator
97         pSalMenuItem->mInfo.fMask = MIIM_TYPE;
98         pSalMenuItem->mInfo.fType = MFT_SEPARATOR;
99     }
100     else
101     {
102         // item
103         pSalMenuItem->mText   = pItemData->aText;
104         pSalMenuItem->mpMenu  = pItemData->pMenu;
105         pSalMenuItem->maBitmap= !!pItemData->aImage ? pItemData->aImage.GetBitmapEx().GetBitmap() : Bitmap();
106         pSalMenuItem->mnId    = pItemData->nId;
107 
108         // 'translate' mnemonics
109         pSalMenuItem->mText.SearchAndReplace( '~', '&' );
110 
111         pSalMenuItem->mInfo.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID | MIIM_DATA;
112         pSalMenuItem->mInfo.fType = MFT_STRING;
113 #ifdef OWNERDRAW
114         if( pItemData->pMenu && !pItemData->pMenu->IsMenuBar() )
115             pSalMenuItem->mInfo.fType |= MFT_OWNERDRAW;
116         pSalMenuItem->mInfo.fState = MFS_ENABLED;
117 #endif
118         pSalMenuItem->mInfo.dwTypeData = (LPWSTR) pSalMenuItem->mText.GetBuffer();
119         pSalMenuItem->mInfo.cch = pSalMenuItem->mText.Len();
120 
121         pSalMenuItem->mInfo.wID = pItemData->nId;
122         pSalMenuItem->mInfo.dwItemData = (ULONG_PTR) pSalMenuItem; // user data
123     }
124 
125     return pSalMenuItem;
126 }
127 
DestroyMenuItem(SalMenuItem * pSalMenuItem)128 void WinSalInstance::DestroyMenuItem( SalMenuItem* pSalMenuItem )
129 {
130     delete pSalMenuItem;
131 }
132 
133 
134 // =======================================================================
135 
ImplDrawMenuBar(SalMenu * pMenu)136 static void ImplDrawMenuBar( SalMenu *pMenu )
137 {
138     if( pMenu->VisibleMenuBar() )
139     {
140         // redrawing the menubar all the time actually seems to be unnecessary (it just flickers)
141         /*
142         WinSalMenu *pMenuBar = ImplFindMenuBar( pMenu );
143         if( pMenuBar && pMenuBar->mhWnd )
144             ::DrawMenuBar( pMenuBar->mhWnd );
145             */
146     }
147 }
148 
149 // =======================================================================
150 
151 
152 /*
153  * WinSalMenu
154  */
155 
WinSalMenu()156 WinSalMenu::WinSalMenu()
157 {
158     mhMenu       = NULL;
159     mbMenuBar    = FALSE;
160     mhWnd        = NULL;
161     mpParentMenu = NULL;
162 }
163 
~WinSalMenu()164 WinSalMenu::~WinSalMenu()
165 {
166     // only required if not associated to a window...
167     GetSalData()->mhMenuSet.erase( mhMenu );
168     ::DestroyMenu( mhMenu );
169 }
170 
VisibleMenuBar()171 sal_Bool WinSalMenu::VisibleMenuBar()
172 {
173     // The Win32 implementation never shows a native
174     // menubar. Thus, native menus are only visible
175     // when the menu is merged with an OLE container.
176     // The reason are missing tooltips, ownerdraw
177     // issues and accessibility which are better supported
178     // by VCL menus.
179     // Nevertheless, the native menus are always created
180     // and the application will properly react to all native
181     // menu messages.
182 
183     return FALSE;
184 }
185 
SetFrame(const SalFrame * pFrame)186 void WinSalMenu::SetFrame( const SalFrame *pFrame )
187 {
188     if( pFrame )
189         mhWnd = static_cast<const WinSalFrame*>(pFrame)->mhWnd;
190     else
191         mhWnd = NULL;
192 }
193 
InsertItem(SalMenuItem * pSalMenuItem,unsigned nPos)194 void WinSalMenu::InsertItem( SalMenuItem* pSalMenuItem, unsigned nPos )
195 {
196     if( pSalMenuItem )
197     {
198 	WinSalMenuItem* pWItem = static_cast<WinSalMenuItem*>(pSalMenuItem);
199         if( nPos == MENU_APPEND )
200         {
201             nPos = ::GetMenuItemCount( mhMenu );
202             if( nPos == -1 )
203                 return;
204         }
205 
206         if(!::InsertMenuItemW( mhMenu, nPos, TRUE, &pWItem->mInfo ))
207             myerr = GetLastError();
208         else
209         {
210             pWItem->mpSalMenu = this;
211             ImplDrawMenuBar( this );
212         }
213     }
214 }
215 
RemoveItem(unsigned nPos)216 void WinSalMenu::RemoveItem( unsigned nPos )
217 {
218     int num = ::GetMenuItemCount( mhMenu );
219     if( num != -1 && nPos < (unsigned)num )
220     {
221         WinSalMenuItem *pSalMenuItem = NULL;
222 
223         MENUITEMINFOW mi;
224         memset( &mi, 0, sizeof(mi) );
225         mi.cbSize = sizeof( mi );
226         mi.fMask = MIIM_DATA;
227         if( !GetMenuItemInfoW( mhMenu, nPos, TRUE, &mi) )
228             myerr = GetLastError();
229         else
230             pSalMenuItem = (WinSalMenuItem *) mi.dwItemData;
231 
232         if( !::RemoveMenu( mhMenu, nPos, MF_BYPOSITION ) )
233             myerr = GetLastError();
234         else
235         {
236             if( pSalMenuItem )
237                 pSalMenuItem->mpSalMenu = NULL;
238             ImplDrawMenuBar( this );
239         }
240     }
241 }
242 
ImplRemoveItemById(WinSalMenu * pSalMenu,unsigned nItemId)243 void ImplRemoveItemById( WinSalMenu *pSalMenu, unsigned nItemId )
244 {
245     if( !pSalMenu )
246         return;
247 
248     WinSalMenuItem *pSalMenuItem = NULL;
249 
250     MENUITEMINFOW mi;
251     memset( &mi, 0, sizeof(mi) );
252     mi.cbSize = sizeof( mi );
253     mi.fMask = MIIM_DATA;
254     if( !GetMenuItemInfoW( pSalMenu->mhMenu, nItemId, FALSE, &mi) )
255         myerr = GetLastError();
256     else
257         pSalMenuItem = (WinSalMenuItem *) mi.dwItemData;
258 
259     if( !::RemoveMenu( pSalMenu->mhMenu, nItemId, MF_BYCOMMAND ) )
260         myerr = GetLastError();
261     else
262     {
263         if( pSalMenuItem )
264             pSalMenuItem->mpSalMenu = NULL;
265         ImplDrawMenuBar( pSalMenu );
266     }
267 }
268 
SetSubMenu(SalMenuItem * pSalMenuItem,SalMenu * pSubMenu,unsigned nPos)269 void WinSalMenu::SetSubMenu( SalMenuItem* pSalMenuItem, SalMenu* pSubMenu, unsigned nPos )
270 {
271     if( pSalMenuItem )
272     {
273 	    WinSalMenuItem* pWMenuItem = static_cast<WinSalMenuItem*>(pSalMenuItem);
274 	    WinSalMenu* pWSubMenu = static_cast<WinSalMenu*>(pSubMenu);
275         if( pWMenuItem->mInfo.hSubMenu )
276         {
277             GetSalData()->mhMenuSet.erase( pWMenuItem->mInfo.hSubMenu );
278             ::DestroyMenu( pWMenuItem->mInfo.hSubMenu );
279         }
280 
281         pWMenuItem->mInfo.fMask |= MIIM_SUBMENU;
282         if( !pSubMenu )
283             pWMenuItem->mInfo.hSubMenu = NULL;
284         else
285         {
286             pWMenuItem->mInfo.hSubMenu = pWSubMenu->mhMenu;
287             pWSubMenu->mpParentMenu = this;
288         }
289 
290         if(!::SetMenuItemInfoW( mhMenu, nPos, TRUE, &pWMenuItem->mInfo ) )
291             myerr = GetLastError();
292         else
293             ImplDrawMenuBar( this );
294     }
295 }
296 
CheckItem(unsigned nPos,sal_Bool bCheck)297 void WinSalMenu::CheckItem( unsigned nPos, sal_Bool bCheck )
298 {
299     if( -1 != ::CheckMenuItem( mhMenu, nPos, MF_BYPOSITION|(bCheck ? MF_CHECKED : MF_UNCHECKED) ) )
300         ImplDrawMenuBar( this );
301 }
302 
EnableItem(unsigned nPos,sal_Bool bEnable)303 void WinSalMenu::EnableItem( unsigned nPos, sal_Bool bEnable )
304 {
305     if( -1 != ::EnableMenuItem( mhMenu, nPos, MF_BYPOSITION|(bEnable ? MF_ENABLED : (MF_DISABLED|MF_GRAYED) ) ) )
306         ImplDrawMenuBar( this );
307 }
308 
SetItemImage(unsigned,SalMenuItem * pSalMenuItem,const Image & rImage)309 void WinSalMenu::SetItemImage( unsigned /*nPos*/, SalMenuItem* pSalMenuItem, const Image& rImage )
310 {
311     if( pSalMenuItem )
312     {
313 	WinSalMenuItem* pWItem = static_cast<WinSalMenuItem*>(pSalMenuItem);
314         if( !!rImage )
315             pWItem->maBitmap = rImage.GetBitmapEx().GetBitmap();
316         else
317             pWItem->maBitmap = Bitmap();
318     }
319 }
320 
SetItemText(unsigned nPos,SalMenuItem * pSalMenuItem,const XubString & rText)321 void WinSalMenu::SetItemText( unsigned nPos, SalMenuItem* pSalMenuItem, const XubString& rText )
322 {
323     if( pSalMenuItem )
324     {
325 	WinSalMenuItem* pWItem = static_cast<WinSalMenuItem*>(pSalMenuItem);
326         pWItem->mText = rText;
327         // 'translate' mnemonics
328         pWItem->mText.SearchAndReplace( '~', '&' );
329         pWItem->mInfo.fMask = MIIM_TYPE | MIIM_DATA;
330         pWItem->mInfo.fType = MFT_STRING;
331 #ifdef OWNERDRAW
332         if( pWItem->mpMenu && !((Menu*) pWItem->mpMenu)->IsMenuBar() )
333             pWItem->mInfo.fType |= MFT_OWNERDRAW;
334 #endif
335 
336         // combine text and accelerator text
337         XubString aStr( pWItem->mText );
338         if( pWItem->mAccelText.Len() )
339         {
340             aStr.AppendAscii("\t");
341             aStr.Append( pWItem->mAccelText );
342         }
343         pWItem->mInfo.dwTypeData = (LPWSTR) aStr.GetBuffer();
344         pWItem->mInfo.cch = aStr.Len();
345 
346         if(!::SetMenuItemInfoW( mhMenu, nPos, TRUE, &pWItem->mInfo ))
347             myerr = GetLastError();
348         else
349             ImplDrawMenuBar( this );
350     }
351 }
352 
SetAccelerator(unsigned nPos,SalMenuItem * pSalMenuItem,const KeyCode &,const XubString & rKeyName)353 void WinSalMenu::SetAccelerator( unsigned nPos, SalMenuItem* pSalMenuItem, const KeyCode&, const XubString& rKeyName )
354 {
355     if( pSalMenuItem )
356     {
357 	WinSalMenuItem* pWItem = static_cast<WinSalMenuItem*>(pSalMenuItem);
358         pWItem->mAccelText = rKeyName;
359         pWItem->mInfo.fMask = MIIM_TYPE | MIIM_DATA;
360         pWItem->mInfo.fType = MFT_STRING;
361 #ifdef OWNERDRAW
362         if( pWItem->mpMenu && !((Menu*)pWItem->mpMenu)->IsMenuBar() )
363             pWItem->mInfo.fType |= MFT_OWNERDRAW;
364 #endif
365         // combine text and accelerator text
366         XubString aStr( pWItem->mText );
367         if( pWItem->mAccelText.Len() )
368         {
369             aStr.AppendAscii("\t");
370             aStr.Append( pWItem->mAccelText );
371         }
372         pWItem->mInfo.dwTypeData = (LPWSTR) aStr.GetBuffer();
373         pWItem->mInfo.cch = aStr.Len();
374 
375         if(!::SetMenuItemInfoW( mhMenu, nPos, TRUE, &pWItem->mInfo ))
376             myerr = GetLastError();
377         else
378             ImplDrawMenuBar( this );
379     }
380 }
381 
GetSystemMenuData(SystemMenuData * pData)382 void WinSalMenu::GetSystemMenuData( SystemMenuData* pData )
383 {
384     if( pData )
385         pData->hMenu = mhMenu;
386 }
387 
388 // =======================================================================
389 
390 /*
391  * SalMenuItem
392  */
393 
394 
WinSalMenuItem()395 WinSalMenuItem::WinSalMenuItem()
396 {
397     memset( &mInfo, 0, sizeof( MENUITEMINFOW ) );
398     mpMenu = NULL;
399     mnId  = 0xFFFF;
400     mpSalMenu = NULL;
401 }
402 
~WinSalMenuItem()403 WinSalMenuItem::~WinSalMenuItem()
404 {
405     if( mpSalMenu )
406         ImplRemoveItemById( mpSalMenu, mnId );
407 }
408 
409 // -------------------------------------------------------------------
410