1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20 #include <svsys.h>
21
22 #include <vcl/menu.hxx>
23 #include <vcl/sysdata.hxx>
24 #include <o3tl/char16_t2wchar_t.hxx>
25 #include <o3tl/safeint.hxx>
26
27 #include <win/wincomp.hxx>
28 #include <win/saldata.hxx>
29 #include <win/salinst.h>
30 #include <win/salframe.h>
31 #include <win/salmenu.h>
32
33 #include <salgdi.hxx>
34
35 static DWORD myerr=0;
36
IsKnownMenuHandle(HMENU hMenu)37 bool SalData::IsKnownMenuHandle( HMENU hMenu )
38 {
39 if( mhMenuSet.find( hMenu ) == mhMenuSet.end() )
40 return false;
41 else
42 return true;
43 }
44
45 // WinSalInst factory methods
46
CreateMenu(bool bMenuBar,Menu *)47 std::unique_ptr<SalMenu> WinSalInstance::CreateMenu( bool bMenuBar, Menu* )
48 {
49 WinSalMenu *pSalMenu = new WinSalMenu();
50
51 pSalMenu->mbMenuBar = bMenuBar;
52 pSalMenu->mhWnd = nullptr;
53 if( bMenuBar )
54 pSalMenu->mhMenu = ::CreateMenu();
55 else
56 pSalMenu->mhMenu = ::CreatePopupMenu();
57
58 if( pSalMenu->mhMenu )
59 GetSalData()->mhMenuSet.insert( pSalMenu->mhMenu );
60
61 return std::unique_ptr<SalMenu>(pSalMenu);
62 }
63
CreateMenuItem(const SalItemParams & rItemData)64 std::unique_ptr<SalMenuItem> WinSalInstance::CreateMenuItem( const SalItemParams & rItemData )
65 {
66 WinSalMenuItem *pSalMenuItem = new WinSalMenuItem();
67 memset( &pSalMenuItem->mInfo, 0, sizeof( MENUITEMINFOW ) );
68 pSalMenuItem->mInfo.cbSize = sizeof( MENUITEMINFOW );
69
70 if( rItemData.eType == MenuItemType::SEPARATOR )
71 {
72 // separator
73 pSalMenuItem->mInfo.fMask = MIIM_TYPE;
74 pSalMenuItem->mInfo.fType = MFT_SEPARATOR;
75 }
76 else
77 {
78 // item
79 pSalMenuItem->mText = rItemData.aText;
80 pSalMenuItem->mpMenu = rItemData.pMenu;
81 pSalMenuItem->maBitmap= !!rItemData.aImage ? rItemData.aImage.GetBitmapEx().GetBitmap() : Bitmap();
82 pSalMenuItem->mnId = rItemData.nId;
83
84 // 'translate' mnemonics
85 pSalMenuItem->mText = pSalMenuItem->mText.replaceAll( "~", "&" );
86
87 pSalMenuItem->mInfo.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID | MIIM_DATA;
88 pSalMenuItem->mInfo.fType = MFT_STRING;
89 pSalMenuItem->mInfo.dwTypeData = o3tl::toW(const_cast<sal_Unicode *>(pSalMenuItem->mText.getStr()));
90 pSalMenuItem->mInfo.cch = pSalMenuItem->mText.getLength();
91
92 pSalMenuItem->mInfo.wID = rItemData.nId;
93 pSalMenuItem->mInfo.dwItemData = reinterpret_cast<ULONG_PTR>(pSalMenuItem); // user data
94 }
95
96 return std::unique_ptr<SalMenuItem>(pSalMenuItem);
97 }
98
ImplDrawMenuBar(SalMenu * pMenu)99 static void ImplDrawMenuBar( SalMenu *pMenu )
100 {
101 if( pMenu->VisibleMenuBar() )
102 {
103 // redrawing the menubar all the time actually seems to be unnecessary (it just flickers)
104 /*
105 WinSalMenu *pMenuBar = ImplFindMenuBar( pMenu );
106 if( pMenuBar && pMenuBar->mhWnd )
107 ::DrawMenuBar( pMenuBar->mhWnd );
108 */
109 }
110 }
111
112 /*
113 * WinSalMenu
114 */
115
WinSalMenu()116 WinSalMenu::WinSalMenu()
117 {
118 mhMenu = nullptr;
119 mbMenuBar = false;
120 mhWnd = nullptr;
121 mpParentMenu = nullptr;
122 }
123
~WinSalMenu()124 WinSalMenu::~WinSalMenu()
125 {
126 // only required if not associated to a window...
127 GetSalData()->mhMenuSet.erase( mhMenu );
128 ::DestroyMenu( mhMenu );
129 }
130
VisibleMenuBar()131 bool WinSalMenu::VisibleMenuBar()
132 {
133 // The Win32 implementation never shows a native
134 // menubar. Thus, native menus are only visible
135 // when the menu is merged with an OLE container.
136 // The reason are missing tooltips, ownerdraw
137 // issues and accessibility which are better supported
138 // by VCL menus.
139 // Nevertheless, the native menus are always created
140 // and the application will properly react to all native
141 // menu messages.
142
143 return false;
144 }
145
SetFrame(const SalFrame * pFrame)146 void WinSalMenu::SetFrame( const SalFrame *pFrame )
147 {
148 if( pFrame )
149 mhWnd = static_cast<const WinSalFrame*>(pFrame)->mhWnd;
150 else
151 mhWnd = nullptr;
152 }
153
InsertItem(SalMenuItem * pSalMenuItem,unsigned nPos)154 void WinSalMenu::InsertItem( SalMenuItem* pSalMenuItem, unsigned nPos )
155 {
156 if( pSalMenuItem )
157 {
158 WinSalMenuItem* pWItem = static_cast<WinSalMenuItem*>(pSalMenuItem);
159 if( nPos == MENU_APPEND )
160 {
161 nPos = ::GetMenuItemCount( mhMenu );
162 if( nPos == static_cast<unsigned>( -1 ) )
163 return;
164 }
165
166 if(!::InsertMenuItemW( mhMenu, nPos, TRUE, &pWItem->mInfo ))
167 myerr = GetLastError();
168 else
169 {
170 pWItem->mpSalMenu = this;
171 ImplDrawMenuBar( this );
172 }
173 }
174 }
175
RemoveItem(unsigned nPos)176 void WinSalMenu::RemoveItem( unsigned nPos )
177 {
178 int num = ::GetMenuItemCount( mhMenu );
179 if( num != -1 && nPos < o3tl::make_unsigned(num) )
180 {
181 WinSalMenuItem *pSalMenuItem = nullptr;
182
183 MENUITEMINFOW mi = {};
184 mi.cbSize = sizeof( mi );
185 mi.fMask = MIIM_DATA;
186 if( !GetMenuItemInfoW( mhMenu, nPos, TRUE, &mi) )
187 myerr = GetLastError();
188 else
189 pSalMenuItem = reinterpret_cast<WinSalMenuItem *>(mi.dwItemData);
190
191 if( !::RemoveMenu( mhMenu, nPos, MF_BYPOSITION ) )
192 myerr = GetLastError();
193 else
194 {
195 if( pSalMenuItem )
196 pSalMenuItem->mpSalMenu = nullptr;
197 ImplDrawMenuBar( this );
198 }
199 }
200 }
201
ImplRemoveItemById(WinSalMenu * pSalMenu,unsigned nItemId)202 static void ImplRemoveItemById( WinSalMenu *pSalMenu, unsigned nItemId )
203 {
204 if( !pSalMenu )
205 return;
206
207 WinSalMenuItem *pSalMenuItem = nullptr;
208
209 MENUITEMINFOW mi = {};
210 mi.cbSize = sizeof( mi );
211 mi.fMask = MIIM_DATA;
212 if( !GetMenuItemInfoW( pSalMenu->mhMenu, nItemId, FALSE, &mi) )
213 myerr = GetLastError();
214 else
215 pSalMenuItem = reinterpret_cast<WinSalMenuItem *>(mi.dwItemData);
216
217 if( !::RemoveMenu( pSalMenu->mhMenu, nItemId, MF_BYCOMMAND ) )
218 myerr = GetLastError();
219 else
220 {
221 if( pSalMenuItem )
222 pSalMenuItem->mpSalMenu = nullptr;
223 ImplDrawMenuBar( pSalMenu );
224 }
225 }
226
SetSubMenu(SalMenuItem * pSalMenuItem,SalMenu * pSubMenu,unsigned nPos)227 void WinSalMenu::SetSubMenu( SalMenuItem* pSalMenuItem, SalMenu* pSubMenu, unsigned nPos )
228 {
229 if( pSalMenuItem )
230 {
231 WinSalMenuItem* pWMenuItem = static_cast<WinSalMenuItem*>(pSalMenuItem);
232 WinSalMenu* pWSubMenu = static_cast<WinSalMenu*>(pSubMenu);
233 if( pWMenuItem->mInfo.hSubMenu )
234 {
235 GetSalData()->mhMenuSet.erase( pWMenuItem->mInfo.hSubMenu );
236 ::DestroyMenu( pWMenuItem->mInfo.hSubMenu );
237 }
238
239 pWMenuItem->mInfo.fMask |= MIIM_SUBMENU;
240 if( !pSubMenu )
241 pWMenuItem->mInfo.hSubMenu = nullptr;
242 else
243 {
244 pWMenuItem->mInfo.hSubMenu = pWSubMenu->mhMenu;
245 pWSubMenu->mpParentMenu = this;
246 }
247
248 if(!::SetMenuItemInfoW( mhMenu, nPos, TRUE, &pWMenuItem->mInfo ) )
249 myerr = GetLastError();
250 else
251 ImplDrawMenuBar( this );
252 }
253 }
254
CheckItem(unsigned nPos,bool bCheck)255 void WinSalMenu::CheckItem( unsigned nPos, bool bCheck )
256 {
257 if( static_cast<unsigned>( -1 ) != ::CheckMenuItem( mhMenu, nPos, MF_BYPOSITION|(bCheck ? MF_CHECKED : MF_UNCHECKED) ) )
258 ImplDrawMenuBar( this );
259 }
260
EnableItem(unsigned nPos,bool bEnable)261 void WinSalMenu::EnableItem( unsigned nPos, bool bEnable )
262 {
263 if( -1 != ::EnableMenuItem( mhMenu, nPos, MF_BYPOSITION|(bEnable ? MF_ENABLED : (MF_DISABLED|MF_GRAYED) ) ) )
264 ImplDrawMenuBar( this );
265 }
266
SetItemImage(unsigned,SalMenuItem * pSalMenuItem,const Image & rImage)267 void WinSalMenu::SetItemImage( unsigned /*nPos*/, SalMenuItem* pSalMenuItem, const Image& rImage )
268 {
269 if( pSalMenuItem )
270 {
271 WinSalMenuItem* pWItem = static_cast<WinSalMenuItem*>(pSalMenuItem);
272 if( !!rImage )
273 pWItem->maBitmap = rImage.GetBitmapEx().GetBitmap();
274 else
275 pWItem->maBitmap = Bitmap();
276 }
277 }
278
SetItemText(unsigned nPos,SalMenuItem * pSalMenuItem,const OUString & rText)279 void WinSalMenu::SetItemText( unsigned nPos, SalMenuItem* pSalMenuItem, const OUString& rText )
280 {
281 if( pSalMenuItem )
282 {
283 WinSalMenuItem* pWItem = static_cast<WinSalMenuItem*>(pSalMenuItem);
284 pWItem->mText = rText;
285 // 'translate' mnemonics
286 pWItem->mText = pWItem->mText.replaceAll( "~", "&" );
287 pWItem->mInfo.fMask = MIIM_TYPE | MIIM_DATA;
288 pWItem->mInfo.fType = MFT_STRING;
289
290 // combine text and accelerator text
291 OUString aStr( pWItem->mText );
292 if( pWItem->mAccelText.getLength() )
293 {
294 aStr += "\t" + pWItem->mAccelText;
295 }
296 pWItem->mInfo.dwTypeData = o3tl::toW(const_cast<sal_Unicode *>(aStr.getStr()));
297 pWItem->mInfo.cch = aStr.getLength();
298
299 if(!::SetMenuItemInfoW( mhMenu, nPos, TRUE, &pWItem->mInfo ))
300 myerr = GetLastError();
301 else
302 ImplDrawMenuBar( this );
303 }
304 }
305
SetAccelerator(unsigned nPos,SalMenuItem * pSalMenuItem,const vcl::KeyCode &,const OUString & rKeyName)306 void WinSalMenu::SetAccelerator( unsigned nPos, SalMenuItem* pSalMenuItem, const vcl::KeyCode&, const OUString& rKeyName )
307 {
308 if( pSalMenuItem )
309 {
310 WinSalMenuItem* pWItem = static_cast<WinSalMenuItem*>(pSalMenuItem);
311 pWItem->mAccelText = rKeyName;
312 pWItem->mInfo.fMask = MIIM_TYPE | MIIM_DATA;
313 pWItem->mInfo.fType = MFT_STRING;
314
315 // combine text and accelerator text
316 OUString aStr( pWItem->mText );
317 if( pWItem->mAccelText.getLength() )
318 {
319 aStr += "\t" + pWItem->mAccelText;
320 }
321 pWItem->mInfo.dwTypeData = o3tl::toW(const_cast<sal_Unicode *>(aStr.getStr()));
322 pWItem->mInfo.cch = aStr.getLength();
323
324 if(!::SetMenuItemInfoW( mhMenu, nPos, TRUE, &pWItem->mInfo ))
325 myerr = GetLastError();
326 else
327 ImplDrawMenuBar( this );
328 }
329 }
330
GetSystemMenuData(SystemMenuData * pData)331 void WinSalMenu::GetSystemMenuData( SystemMenuData* pData )
332 {
333 if( pData )
334 pData->hMenu = mhMenu;
335 }
336
337 /*
338 * SalMenuItem
339 */
340
WinSalMenuItem()341 WinSalMenuItem::WinSalMenuItem()
342 {
343 memset( &mInfo, 0, sizeof( MENUITEMINFOW ) );
344 mpMenu = nullptr;
345 mnId = 0xFFFF;
346 mpSalMenu = nullptr;
347 }
348
~WinSalMenuItem()349 WinSalMenuItem::~WinSalMenuItem()
350 {
351 if( mpSalMenu )
352 ImplRemoveItemById( mpSalMenu, mnId );
353 }
354
355 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
356