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