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