xref: /core/sfx2/source/appl/shutdowniconw32.cxx (revision 2a20616d09709427e8cf2474d4721836bce66b8e)
1 /*
2  * This file is part of the LibreOffice project.
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7  *
8  * This file incorporates work covered by the following license notice:
9  *
10  *   Licensed to the Apache Software Foundation (ASF) under one or more
11  *   contributor license agreements. See the NOTICE file distributed
12  *   with this work for additional information regarding copyright
13  *   ownership. The ASF licenses this file to you under the Apache
14  *   License, Version 2.0 (the "License"); you may not use this file
15  *   except in compliance with the License. You may obtain a copy of
16  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
17  */
18 
19 #include <sal/macros.h>
20 #include <sal/log.hxx>
21 
22 #include <unotools/moduleoptions.hxx>
23 #include <unotools/dynamicmenuoptions.hxx>
24 
25 #undef WB_LEFT
26 #undef WB_RIGHT
27 
28 #include <shutdownicon.hxx>
29 #include <sfx2/sfxresid.hxx>
30 #include <sfx2/strings.hrc>
31 #include <shlobj.h>
32 #include <objidl.h>
33 #include <osl/diagnose.h>
34 #include <osl/thread.h>
35 #include <systools/win32/comtools.hxx>
36 #include <systools/win32/extended_max_path.hxx>
37 #include <systools/win32/qswin32.h>
38 #include <comphelper/sequenceashashmap.hxx>
39 #include <comphelper/windowserrorstring.hxx>
40 #include <o3tl/char16_t2wchar_t.hxx>
41 
42 #include <set>
43 
44 
45 #define EXECUTER_WINDOWCLASS    L"SO Executer Class"
46 #define EXECUTER_WINDOWNAME     L"SO Executer Window"
47 
48 
49 #define ID_QUICKSTART               1
50 #define IDM_EXIT                    2
51 #define IDM_OPEN                    3
52 #define IDM_WRITER                  4
53 #define IDM_CALC                    5
54 #define IDM_IMPRESS                 6
55 #define IDM_DRAW                    7
56 #define IDM_BASE                    8
57 #define IDM_TEMPLATE                9
58 #define IDM_MATH                    12
59 #define IDM_INSTALL                 10
60 #define IDM_STARTCENTER             14
61 
62 
63 #define ICON_LO_DEFAULT                 1
64 #define ICON_TEXT_DOCUMENT              2
65 #define ICON_SPREADSHEET_DOCUMENT       4
66 #define ICON_DRAWING_DOCUMENT           6
67 #define ICON_PRESENTATION_DOCUMENT      8
68 #define ICON_TEMPLATE                   11
69 #define ICON_DATABASE_DOCUMENT          12
70 #define ICON_MATH_DOCUMENT              13
71 #define ICON_OPEN                       5   // See index of open folder icon in shell32.dll
72 
73 #define SFX_TASKBAR_NOTIFICATION    WM_USER+1
74 
75 static HWND  aListenerWindow = nullptr;
76 static HWND  aExecuterWindow = nullptr;
77 static HMENU popupMenu = nullptr;
78 
79 static void OnMeasureItem(HWND hwnd, LPMEASUREITEMSTRUCT lpmis);
80 static void OnDrawItem(HWND hwnd, LPDRAWITEMSTRUCT lpdis);
81 
82 namespace {
83 
84 struct MYITEM
85 {
86     OUString text;
87     OUString module;
88     UINT iconId;
89 };
90 
91 }
92 
addMenuItem(HMENU hMenu,UINT id,UINT iconId,const OUString & text,int & pos,bool bOwnerdraw,const OUString & module)93 static void addMenuItem( HMENU hMenu, UINT id, UINT iconId, const OUString& text, int& pos, bool bOwnerdraw, const OUString& module )
94 {
95     MENUITEMINFOW mi = {};
96 
97     mi.cbSize = sizeof( mi );
98     if( id == static_cast<UINT>( -1 ) )
99     {
100         mi.fMask=MIIM_FTYPE;
101         mi.fType=MFT_SEPARATOR;
102     }
103     else
104     {
105         if( bOwnerdraw )
106         {
107             mi.fMask=MIIM_FTYPE | MIIM_STATE | MIIM_ID | MIIM_DATA;
108             mi.fType=MFT_OWNERDRAW;
109 
110             MYITEM *pMyItem = new MYITEM;
111             pMyItem->text = text;
112             pMyItem->iconId = iconId;
113             pMyItem->module = module;
114             mi.dwItemData = reinterpret_cast<ULONG_PTR>(pMyItem);
115         }
116         else
117         {
118             mi.fMask=MIIM_STRING | MIIM_STATE | MIIM_ID;
119             mi.dwTypeData = o3tl::toW(
120                 const_cast<sal_Unicode *>(text.getStr()));
121             mi.cch = text.getLength();
122         }
123 
124         mi.fState = MFS_ENABLED;
125         mi.wID = id;
126         if ( IDM_TEMPLATE == id )
127             mi.fState |= MFS_DEFAULT;
128     }
129 
130     InsertMenuItemW( hMenu, pos++, TRUE, &mi );
131 }
132 
133 
createSystrayMenu()134 static HMENU createSystrayMenu( )
135 {
136     SvtModuleOptions    aModuleOptions;
137 
138     HMENU hMenu = CreatePopupMenu();
139     int pos=0;
140 
141     ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance();
142     OSL_ENSURE( pShutdownIcon, "ShutdownIcon instance empty!");
143 
144     if( !pShutdownIcon )
145         return nullptr;
146 
147     // collect the URLs of the entries in the File/New menu
148     ::std::set< OUString > aFileNewAppsAvailable;
149     std::vector< SvtDynMenuEntry > const aNewMenu = SvtDynamicMenuOptions::GetMenu( EDynamicMenuType::NewMenu );
150     for ( SvtDynMenuEntry const & newMenuProp : aNewMenu )
151     {
152         if ( !newMenuProp.sURL.isEmpty() )
153             aFileNewAppsAvailable.insert( newMenuProp.sURL );
154     }
155 
156     // describe the menu entries for launching the applications
157     struct MenuEntryDescriptor
158     {
159         SvtModuleOptions::EModule   eModuleIdentifier;
160         UINT                        nMenuItemID;
161         UINT                        nMenuIconID;
162         OUString                    sURLDescription;
163     } static constexpr aMenuItems[] =
164     {
165         { SvtModuleOptions::EModule::WRITER,    IDM_WRITER, ICON_TEXT_DOCUMENT,         WRITER_URL },
166         { SvtModuleOptions::EModule::CALC,      IDM_CALC,   ICON_SPREADSHEET_DOCUMENT,  CALC_URL },
167         { SvtModuleOptions::EModule::IMPRESS,   IDM_IMPRESS,ICON_PRESENTATION_DOCUMENT, IMPRESS_WIZARD_URL },
168         { SvtModuleOptions::EModule::DRAW,      IDM_DRAW,   ICON_DRAWING_DOCUMENT,      DRAW_URL },
169         { SvtModuleOptions::EModule::DATABASE,  IDM_BASE,   ICON_DATABASE_DOCUMENT,     BASE_URL },
170         { SvtModuleOptions::EModule::MATH,      IDM_MATH,   ICON_MATH_DOCUMENT,         MATH_URL },
171     };
172 
173     // insert the menu entries for launching the applications
174     for (const auto& [eModuleIdentifier, nMenuItemID, nMenuIconID, sURL] : aMenuItems)
175     {
176         if ( !aModuleOptions.IsModuleInstalled( eModuleIdentifier ) )
177             // the complete application is not even installed
178             continue;
179 
180         if ( aFileNewAppsAvailable.find( sURL ) == aFileNewAppsAvailable.end() )
181             // the application is installed, but the entry has been configured to *not* appear in the File/New
182             // menu => also let not appear it in the quickstarter
183             continue;
184 
185         addMenuItem( hMenu, nMenuItemID, nMenuIconID,
186             ShutdownIcon::GetUrlDescription( sURL ), pos, true, "" );
187     }
188 
189 
190     // insert the remaining menu entries
191     addMenuItem( hMenu, IDM_TEMPLATE, ICON_TEMPLATE,
192         SfxResId( STR_QUICKSTART_FROMTEMPLATE ), pos, true, "");
193     addMenuItem( hMenu, static_cast< UINT >( -1 ), 0, OUString(), pos, false, "" );
194     addMenuItem( hMenu, IDM_OPEN,   ICON_OPEN, SfxResId(STR_QUICKSTART_FILEOPEN), pos, true, "SHELL32");
195     addMenuItem( hMenu, static_cast< UINT >( -1 ), 0, OUString(), pos, false, "" );
196     addMenuItem( hMenu, IDM_INSTALL,0, SfxResId(STR_QUICKSTART_PRELAUNCH), pos, false, "" );
197     addMenuItem( hMenu, static_cast< UINT >( -1 ), 0, OUString(), pos, false, "" );
198     addMenuItem( hMenu, IDM_EXIT,   0, SfxResId(STR_QUICKSTART_EXIT), pos, false, "" );
199 
200     // indicate status of autostart folder
201     CheckMenuItem( hMenu, IDM_INSTALL, MF_BYCOMMAND | (ShutdownIcon::GetAutostart() ? MF_CHECKED : MF_UNCHECKED) );
202 
203     return hMenu;
204 }
205 
206 
deleteSystrayMenu(HMENU hMenu)207 static void deleteSystrayMenu( HMENU hMenu )
208 {
209     if( !hMenu || !IsMenu( hMenu ))
210         return;
211 
212     MENUITEMINFOW mi = {};
213     mi.cbSize = sizeof( mi );
214     mi.fMask = MIIM_DATA;
215 
216     for (UINT pos = 0; GetMenuItemInfoW(hMenu, pos, true, &mi); ++pos)
217     {
218         if (MYITEM* pMyItem = reinterpret_cast<MYITEM*>(mi.dwItemData))
219             delete pMyItem;
220         mi.fMask = MIIM_DATA;
221     }
222 }
223 
224 
addTaskbarIcon(HWND hWnd)225 static void addTaskbarIcon( HWND hWnd )
226 {
227     OUString strTip = SfxResId(STR_QUICKSTART_TIP);
228 
229     // add taskbar icon
230     NOTIFYICONDATAW nid;
231     nid.hIcon = static_cast<HICON>(LoadImageW( GetModuleHandleW( nullptr ), MAKEINTRESOURCEW( ICON_LO_DEFAULT ),
232         IMAGE_ICON, GetSystemMetrics( SM_CXSMICON ), GetSystemMetrics( SM_CYSMICON ),
233         LR_DEFAULTCOLOR | LR_SHARED ));
234 
235     wcsncpy( nid.szTip, o3tl::toW(strTip.getStr()), 64 );
236 
237     nid.cbSize              = sizeof(nid);
238     nid.hWnd                = hWnd;
239     nid.uID                 = ID_QUICKSTART;
240     nid.uCallbackMessage    = SFX_TASKBAR_NOTIFICATION;
241     nid.uFlags              = NIF_MESSAGE|NIF_TIP|NIF_ICON;
242 
243     Shell_NotifyIconW(NIM_ADD, &nid);
244 }
245 
246 
listenerWndProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)247 static LRESULT CALLBACK listenerWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
248 {
249     static UINT s_uTaskbarRestart = 0;
250     static UINT s_uMsgKillTray = 0;
251 
252     switch (uMsg)
253     {
254         case WM_NCCREATE:
255             return TRUE;
256         case WM_CREATE:
257             {
258                 // request notification when taskbar is recreated
259                 // we then have to add our icon again
260                 s_uTaskbarRestart = RegisterWindowMessageW(L"TaskbarCreated");
261                 s_uMsgKillTray = RegisterWindowMessageW( SHUTDOWN_QUICKSTART_MESSAGE );
262 
263                 // create the menu
264                 if( !popupMenu )
265                     if( (popupMenu = createSystrayMenu( )) == nullptr )
266                         return -1;
267 
268                 // and the icon
269                 addTaskbarIcon( hWnd );
270 
271                 // disable shutdown
272                 ShutdownIcon::getInstance()->SetVeto( true );
273                 ShutdownIcon::addTerminateListener();
274             }
275             return 0;
276 
277         case WM_MEASUREITEM:
278             OnMeasureItem(hWnd, reinterpret_cast<LPMEASUREITEMSTRUCT>(lParam));
279             return TRUE;
280 
281         case WM_DRAWITEM:
282             OnDrawItem(hWnd, reinterpret_cast<LPDRAWITEMSTRUCT>(lParam));
283             return TRUE;
284 
285         case SFX_TASKBAR_NOTIFICATION:
286             switch( lParam )
287             {
288                 case WM_LBUTTONDOWN:
289                 {
290                     bool const ret = PostMessageW(aExecuterWindow, WM_COMMAND, IDM_STARTCENTER, reinterpret_cast<LPARAM>(hWnd));
291                     SAL_WARN_IF(!ret, "sfx.appl", "ERROR: PostMessage() failed!");
292                     break;
293                 }
294 
295                 case WM_RBUTTONDOWN:
296                 {
297                     POINT pt;
298                     GetCursorPos(&pt);
299                     SetForegroundWindow( hWnd );
300 
301                     // update status before showing menu, could have been changed from option page
302                     CheckMenuItem( popupMenu, IDM_INSTALL, MF_BYCOMMAND| (ShutdownIcon::GetAutostart() ? MF_CHECKED : MF_UNCHECKED) );
303 
304                     EnableMenuItem( popupMenu, IDM_EXIT, MF_BYCOMMAND | (ShutdownIcon::bModalMode ? MF_GRAYED : MF_ENABLED) );
305                     EnableMenuItem( popupMenu, IDM_OPEN, MF_BYCOMMAND | (ShutdownIcon::bModalMode ? MF_GRAYED : MF_ENABLED) );
306                     EnableMenuItem( popupMenu, IDM_TEMPLATE, MF_BYCOMMAND | (ShutdownIcon::bModalMode ? MF_GRAYED : MF_ENABLED) );
307                     int m = TrackPopupMenuEx( popupMenu, TPM_RETURNCMD|TPM_LEFTALIGN|TPM_RIGHTBUTTON,
308                                               pt.x, pt.y, hWnd, nullptr );
309                     bool const ret = PostMessageW( hWnd, 0, 0, 0 );
310                     SAL_WARN_IF(!ret, "sfx.appl", "ERROR: PostMessage() failed!");
311                     switch( m )
312                     {
313                         case IDM_OPEN:
314                         case IDM_WRITER:
315                         case IDM_CALC:
316                         case IDM_IMPRESS:
317                         case IDM_DRAW:
318                         case IDM_TEMPLATE:
319                         case IDM_BASE:
320                         case IDM_MATH:
321                             break;
322                         case IDM_INSTALL:
323                             CheckMenuItem( popupMenu, IDM_INSTALL, MF_BYCOMMAND| (ShutdownIcon::GetAutostart() ? MF_CHECKED : MF_UNCHECKED) );
324                             break;
325                         case IDM_EXIT:
326                             // delete taskbar icon
327                             NOTIFYICONDATAW nid;
328                             nid.cbSize=sizeof(nid);
329                             nid.hWnd = hWnd;
330                             nid.uID = ID_QUICKSTART;
331                             Shell_NotifyIconW(NIM_DELETE, &nid);
332                             break;
333                     }
334 
335                     bool const ret2 = PostMessageW(aExecuterWindow, WM_COMMAND, m, reinterpret_cast<LPARAM>(hWnd));
336                     SAL_WARN_IF(!ret2, "sfx.appl", "ERROR: PostMessage() failed!");
337                 }
338                 break;
339             }
340             break;
341         case WM_DESTROY:
342             deleteSystrayMenu( popupMenu );
343             // We don't need the Systray Thread anymore
344             PostQuitMessage( 0 );
345             return DefWindowProcW(hWnd, uMsg, wParam, lParam);
346         default:
347             if( uMsg == s_uTaskbarRestart )
348             {
349                 // re-create taskbar icon
350                 addTaskbarIcon( hWnd );
351             }
352             else if ( uMsg == s_uMsgKillTray )
353             {
354                 // delete taskbar icon
355                 NOTIFYICONDATAW nid;
356                 nid.cbSize=sizeof(nid);
357                 nid.hWnd = hWnd;
358                 nid.uID = ID_QUICKSTART;
359                 Shell_NotifyIconW(NIM_DELETE, &nid);
360 
361                 bool const ret = PostMessageW(aExecuterWindow, WM_COMMAND, IDM_EXIT, reinterpret_cast<LPARAM>(hWnd));
362                 SAL_WARN_IF(!ret, "sfx.appl", "ERROR: PostMessage() failed!");
363             }
364             else
365                 return DefWindowProcW(hWnd, uMsg, wParam, lParam);
366     }
367     return 0;
368 }
369 
370 
executerWndProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)371 static LRESULT CALLBACK executerWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
372 {
373     switch (uMsg)
374     {
375         case WM_NCCREATE:
376             return TRUE;
377         case WM_CREATE:
378             return 0;
379 
380         case WM_COMMAND:
381             switch( LOWORD(wParam) )
382             {
383                 case IDM_OPEN:
384                     if ( !ShutdownIcon::bModalMode )
385                         ShutdownIcon::FileOpen();
386                 break;
387                 case IDM_WRITER:
388                     ShutdownIcon::OpenURL( WRITER_URL, "_default" );
389                 break;
390                 case IDM_CALC:
391                     ShutdownIcon::OpenURL( CALC_URL, "_default" );
392                 break;
393                 case IDM_IMPRESS:
394                     ShutdownIcon::OpenURL( IMPRESS_WIZARD_URL, "_default" );
395                 break;
396                 case IDM_DRAW:
397                     ShutdownIcon::OpenURL( DRAW_URL, "_default" );
398                 break;
399                 case IDM_BASE:
400                     ShutdownIcon::OpenURL( BASE_URL, "_default" );
401                 break;
402                 case IDM_MATH:
403                     ShutdownIcon::OpenURL( MATH_URL, "_default" );
404                 break;
405                 case IDM_STARTCENTER:
406                     ShutdownIcon::OpenURL( STARTMODULE_URL, "_default" );
407                 break;
408                 case IDM_TEMPLATE:
409                     if ( !ShutdownIcon::bModalMode )
410                         ShutdownIcon::FromTemplate();
411                 break;
412                 case IDM_INSTALL:
413                     ShutdownIcon::SetAutostart( !ShutdownIcon::GetAutostart() );
414                     break;
415                 case IDM_EXIT:
416                     // remove listener and
417                     //  terminate office if running in background
418                     if ( !ShutdownIcon::bModalMode )
419                         ShutdownIcon::terminateDesktop();
420                     break;
421             }
422             break;
423         case WM_DESTROY:
424         default:
425             return DefWindowProcW(hWnd, uMsg, wParam, lParam);
426     }
427     return 0;
428 }
429 
430 
SystrayThread(void *)431 static unsigned __stdcall SystrayThread(void* /*lpParam*/)
432 {
433     osl_setThreadName("SystrayThread");
434 
435     aListenerWindow = CreateWindowExW(0,
436         QUICKSTART_CLASSNAME,        // registered class name
437         QUICKSTART_WINDOWNAME,       // window name
438         0,                           // window style
439         CW_USEDEFAULT,               // horizontal position of window
440         CW_USEDEFAULT,               // vertical position of window
441         CW_USEDEFAULT,               // window width
442         CW_USEDEFAULT,               // window height
443         nullptr,                     // handle to parent or owner window
444         nullptr,                     // menu handle or child identifier
445         GetModuleHandleW( nullptr ), // handle to application instance
446         nullptr                      // window-creation data
447         );
448 
449     MSG msg;
450 
451     for (;;)
452     {
453         int const bRet = GetMessageW(&msg, nullptr, 0, 0);
454         if (bRet == 0)
455         {
456             break;
457         }
458         if (-1 == bRet)
459         {
460             SAL_WARN("sfx.appl", "GetMessageW failed: " << comphelper::WindowsErrorString(GetLastError()));
461             return 1;
462         }
463         TranslateMessage( &msg );
464         DispatchMessageW( &msg );
465     }
466 
467     return msg.wParam; // Exit code of WM_QUIT
468 }
469 
470 
win32_init_sys_tray()471 void win32_init_sys_tray()
472 {
473     if ( ShutdownIcon::IsQuickstarterInstalled() )
474     {
475         WNDCLASSEXW listenerClass;
476         listenerClass.cbSize        = sizeof(listenerClass);
477         listenerClass.style         = 0;
478         listenerClass.lpfnWndProc   = listenerWndProc;
479         listenerClass.cbClsExtra    = 0;
480         listenerClass.cbWndExtra    = 0;
481         listenerClass.hInstance     = GetModuleHandleW( nullptr );
482         listenerClass.hIcon         = nullptr;
483         listenerClass.hCursor       = nullptr;
484         listenerClass.hbrBackground = nullptr;
485         listenerClass.lpszMenuName  = nullptr;
486         listenerClass.lpszClassName = QUICKSTART_CLASSNAME;
487         listenerClass.hIconSm       = nullptr;
488 
489         RegisterClassExW(&listenerClass);
490 
491         WNDCLASSEXW executerClass;
492         executerClass.cbSize        = sizeof(executerClass);
493         executerClass.style         = 0;
494         executerClass.lpfnWndProc   = executerWndProc;
495         executerClass.cbClsExtra    = 0;
496         executerClass.cbWndExtra    = 0;
497         executerClass.hInstance     = GetModuleHandleW( nullptr );
498         executerClass.hIcon         = nullptr;
499         executerClass.hCursor       = nullptr;
500         executerClass.hbrBackground = nullptr;
501         executerClass.lpszMenuName  = nullptr;
502         executerClass.lpszClassName = EXECUTER_WINDOWCLASS;
503         executerClass.hIconSm       = nullptr;
504 
505         RegisterClassExW( &executerClass );
506 
507         aExecuterWindow = CreateWindowExW(0,
508             EXECUTER_WINDOWCLASS,        // registered class name
509             EXECUTER_WINDOWNAME,         // window name
510             0,                           // window style
511             CW_USEDEFAULT,               // horizontal position of window
512             CW_USEDEFAULT,               // vertical position of window
513             CW_USEDEFAULT,               // window width
514             CW_USEDEFAULT,               // window height
515             nullptr,                     // handle to parent or owner window
516             nullptr,                     // menu handle or child identifier
517             GetModuleHandleW( nullptr ), // handle to application instance
518             nullptr                      // window-creation data
519             );
520 
521         CloseHandle(reinterpret_cast<HANDLE>(
522             _beginthreadex(nullptr, 0, SystrayThread, nullptr, 0, nullptr)));
523     }
524 }
525 
526 
win32_shutdown_sys_tray()527 void win32_shutdown_sys_tray()
528 {
529     if ( ShutdownIcon::IsQuickstarterInstalled() )
530     {
531         if( IsWindow( aListenerWindow ) )
532         {
533             DestroyWindow( aListenerWindow );
534             aListenerWindow = nullptr;
535             DestroyWindow( aExecuterWindow );
536             aExecuterWindow = nullptr;
537         }
538         UnregisterClassW( QUICKSTART_CLASSNAME, GetModuleHandleW( nullptr ) );
539         UnregisterClassW( EXECUTER_WINDOWCLASS, GetModuleHandleW( nullptr ) );
540     }
541 }
542 
543 
OnMeasureItem(HWND hwnd,LPMEASUREITEMSTRUCT lpmis)544 void OnMeasureItem(HWND hwnd, LPMEASUREITEMSTRUCT lpmis)
545 {
546     MYITEM *pMyItem = reinterpret_cast<MYITEM *>(lpmis->itemData);
547     HDC hdc = GetDC(hwnd);
548     SIZE size;
549 
550     NONCLIENTMETRICSW ncm = {};
551     ncm.cbSize = sizeof(ncm);
552 
553     SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
554 
555     // Assume every menu item can be default and printed bold
556     ncm.lfMenuFont.lfWeight = FW_BOLD;
557 
558     HFONT hfntOld = static_cast<HFONT>(SelectObject(hdc, CreateFontIndirectW( &ncm.lfMenuFont )));
559 
560     GetTextExtentPoint32W(hdc, o3tl::toW(pMyItem->text.getStr()),
561             pMyItem->text.getLength(), &size);
562 
563     lpmis->itemWidth = size.cx + 4 + GetSystemMetrics( SM_CXSMICON );
564     lpmis->itemHeight = std::max<int>(size.cy, GetSystemMetrics( SM_CYSMICON ));
565     lpmis->itemHeight += 4;
566 
567     DeleteObject( SelectObject(hdc, hfntOld) );
568     ReleaseDC(hwnd, hdc);
569 }
570 
OnDrawItem(HWND,LPDRAWITEMSTRUCT lpdis)571 void OnDrawItem(HWND /*hwnd*/, LPDRAWITEMSTRUCT lpdis)
572 {
573     MYITEM *pMyItem = reinterpret_cast<MYITEM *>(lpdis->itemData);
574     COLORREF clrPrevText, clrPrevBkgnd;
575     HFONT hfntOld;
576     HBRUSH hbrOld;
577     int x, y;
578     bool    fSelected = lpdis->itemState & ODS_SELECTED;
579     bool    fDisabled = lpdis->itemState & (ODS_DISABLED | ODS_GRAYED);
580 
581     // Set the appropriate foreground and background colors.
582 
583     RECT aRect = lpdis->rcItem;
584 
585     clrPrevBkgnd = SetBkColor( lpdis->hDC, GetSysColor(COLOR_MENU) );
586 
587     if ( fDisabled )
588         clrPrevText = SetTextColor( lpdis->hDC, GetSysColor( COLOR_GRAYTEXT ) );
589     else
590         clrPrevText = SetTextColor( lpdis->hDC, GetSysColor( fSelected ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT ) );
591 
592     if ( fSelected )
593         clrPrevBkgnd = SetBkColor( lpdis->hDC, GetSysColor(COLOR_HIGHLIGHT) );
594     else
595         clrPrevBkgnd = SetBkColor( lpdis->hDC, GetSysColor(COLOR_MENU) );
596 
597     hbrOld = static_cast<HBRUSH>(SelectObject( lpdis->hDC, CreateSolidBrush( GetBkColor( lpdis->hDC ) ) ));
598 
599     // Fill background
600     PatBlt(lpdis->hDC, aRect.left, aRect.top, aRect.right-aRect.left, aRect.bottom-aRect.top, PATCOPY);
601 
602     int height = aRect.bottom-aRect.top;
603 
604     x = aRect.left;
605     y = aRect.top;
606 
607     int     cx = GetSystemMetrics( SM_CXSMICON );
608     int     cy = GetSystemMetrics( SM_CYSMICON );
609     HICON   hIcon( nullptr );
610     HMODULE hModule( GetModuleHandleW( nullptr ) );
611 
612     if ( pMyItem->module.getLength() > 0 )
613     {
614         LPCWSTR pModuleName = o3tl::toW( pMyItem->module.getStr() );
615         hModule = GetModuleHandleW( pModuleName );
616         if ( hModule == nullptr )
617         {
618             hModule = LoadLibraryW(pModuleName);
619         }
620     }
621 
622     hIcon = static_cast<HICON>(LoadImageW( hModule, MAKEINTRESOURCEW( pMyItem->iconId ),
623                                 IMAGE_ICON, cx, cy,
624                                 LR_DEFAULTCOLOR | LR_SHARED ));
625 
626 
627     HBRUSH hbrIcon = CreateSolidBrush( GetSysColor( COLOR_GRAYTEXT ) );
628 
629     DrawStateW( lpdis->hDC, hbrIcon, nullptr, reinterpret_cast<LPARAM>(hIcon), WPARAM(0), x, y+(height-cy)/2, 0, 0, DST_ICON | (fDisabled ? (fSelected ? DSS_MONO : DSS_DISABLED) : DSS_NORMAL) );
630 
631     DeleteObject( hbrIcon );
632 
633     x += cx + 4;    // space for icon
634     aRect.left = x;
635 
636     NONCLIENTMETRICSW ncm = {};
637     ncm.cbSize = sizeof(ncm);
638 
639     SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
640 
641     // Print default menu entry with bold font
642     if ( lpdis->itemState & ODS_DEFAULT )
643         ncm.lfMenuFont.lfWeight = FW_BOLD;
644 
645     hfntOld = static_cast<HFONT>(SelectObject(lpdis->hDC, CreateFontIndirectW( &ncm.lfMenuFont )));
646 
647 
648     SIZE    size;
649     GetTextExtentPointW( lpdis->hDC, o3tl::toW(pMyItem->text.getStr()), pMyItem->text.getLength(), &size );
650 
651     DrawStateW( lpdis->hDC, nullptr, nullptr, reinterpret_cast<LPARAM>(pMyItem->text.getStr()), WPARAM(0), aRect.left, aRect.top + (height - size.cy)/2, 0, 0, DST_TEXT | (fDisabled && !fSelected ? DSS_DISABLED : DSS_NORMAL) );
652 
653     // Restore the original font and colors.
654     DeleteObject( SelectObject( lpdis->hDC, hbrOld ) );
655     DeleteObject( SelectObject( lpdis->hDC, hfntOld) );
656     SetTextColor(lpdis->hDC, clrPrevText);
657     SetBkColor(lpdis->hDC, clrPrevBkgnd);
658 }
659 
660 
661 // code from setup2 project
662 
SHGetSpecialFolder(int nFolderID)663 static OUString SHGetSpecialFolder( int nFolderID )
664 {
665 
666     LPITEMIDLIST    pidl;
667     HRESULT         hHdl = SHGetSpecialFolderLocation( nullptr, nFolderID, &pidl );
668     OUString        aFolder;
669 
670     if( hHdl == NOERROR )
671     {
672         auto xFolder = std::make_unique<WCHAR[]>(16000);
673         SHGetPathFromIDListW(pidl, xFolder.get());
674         aFolder = o3tl::toU(xFolder.get());
675     }
676 
677     return aFolder;
678 }
679 
GetAutostartFolderNameW32()680 OUString ShutdownIcon::GetAutostartFolderNameW32()
681 {
682     return SHGetSpecialFolder(CSIDL_STARTUP);
683 }
684 
SHCoCreateInstance(LPVOID lpszReserved,REFCLSID clsid,LPUNKNOWN pUnkUnknown,REFIID iid,LPVOID * ppv)685 static HRESULT WINAPI SHCoCreateInstance( LPVOID lpszReserved, REFCLSID clsid, LPUNKNOWN pUnkUnknown, REFIID iid, LPVOID *ppv )
686 {
687     HRESULT hResult = E_NOTIMPL;
688     HMODULE hModShell = GetModuleHandleW( L"SHELL32" );
689 
690     if ( hModShell != nullptr )
691     {
692         typedef HRESULT (WINAPI *SHCoCreateInstance_PROC)( LPVOID lpszReserved, REFCLSID clsid, LPUNKNOWN pUnkUnknown, REFIID iid, LPVOID *ppv );
693 
694         SHCoCreateInstance_PROC lpfnSHCoCreateInstance = reinterpret_cast<SHCoCreateInstance_PROC>(GetProcAddress( hModShell, MAKEINTRESOURCEA(102) ));
695 
696         if ( lpfnSHCoCreateInstance )
697             hResult = lpfnSHCoCreateInstance( lpszReserved, clsid, pUnkUnknown, iid, ppv );
698     }
699     return hResult;
700 }
701 
CreateShortcut(const OUString & rAbsObject,const OUString & rAbsObjectPath,const OUString & rAbsShortcut,const OUString & rDescription,const OUString & rParameter)702 static bool CreateShortcut( const OUString& rAbsObject, const OUString& rAbsObjectPath,
703     const OUString& rAbsShortcut, const OUString& rDescription, const OUString& rParameter )
704 {
705     try
706     {
707         sal::systools::COMReference<IShellLinkW> psl(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER);
708         psl->SetPath( o3tl::toW(rAbsObject.getStr()) );
709         psl->SetWorkingDirectory( o3tl::toW(rAbsObjectPath.getStr()) );
710         psl->SetDescription( o3tl::toW(rDescription.getStr()) );
711         if (!rParameter.isEmpty())
712             psl->SetArguments( o3tl::toW(rParameter.getStr()) );
713 
714         sal::systools::COMReference<IPersistFile> ppf(psl, sal::systools::COM_QUERY_THROW);
715         return SUCCEEDED(ppf->Save(o3tl::toW(rAbsShortcut.getStr()), TRUE));
716     }
717     catch (const sal::systools::ComError&)
718     {
719         return false;
720     }
721 }
722 
723 
724 // install/uninstall
725 
FileExistsW(LPCWSTR lpPath)726 static bool FileExistsW( LPCWSTR lpPath )
727 {
728     bool    bExists = false;
729     WIN32_FIND_DATAW    aFindData;
730 
731     HANDLE  hFind = FindFirstFileW( lpPath, &aFindData );
732 
733     if ( INVALID_HANDLE_VALUE != hFind )
734     {
735         bExists = true;
736         FindClose( hFind );
737     }
738 
739     return bExists;
740 }
741 
IsQuickstarterInstalled()742 bool ShutdownIcon::IsQuickstarterInstalled()
743 {
744     wchar_t aPath[EXTENDED_MAX_PATH];
745     GetModuleFileNameW(nullptr, aPath, std::size(aPath));
746 
747     std::u16string_view aOfficepath(o3tl::toU(aPath));
748     auto i = aOfficepath.find_last_of('\\');
749     if (i != std::u16string_view::npos)
750         aOfficepath = aOfficepath.substr(0, i);
751 
752     OUString quickstartExe(OUString::Concat(aOfficepath) + "\\quickstart.exe");
753 
754     return FileExistsW( o3tl::toW(quickstartExe.getStr()) );
755 }
756 
EnableAutostartW32(const OUString & aShortcut)757 void ShutdownIcon::EnableAutostartW32( const OUString &aShortcut )
758 {
759     wchar_t aPath[EXTENDED_MAX_PATH];
760     GetModuleFileNameW(nullptr, aPath, std::size(aPath));
761 
762     OUString aOfficepath( o3tl::toU(aPath) );
763     int i = aOfficepath.lastIndexOf('\\');
764     if( i != -1 )
765         aOfficepath = aOfficepath.copy(0, i);
766 
767     OUString quickstartExe(aOfficepath + "\\quickstart.exe");
768 
769     CreateShortcut( quickstartExe, aOfficepath, aShortcut, OUString(), OUString() );
770 }
771 
772 
773 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
774