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