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