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