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