1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_fpicker.hxx" 30 31 //------------------------------------------------------------------------ 32 // includes 33 //------------------------------------------------------------------------ 34 35 #include <tchar.h> 36 #include "helppopupwindow.hxx" 37 #include <osl/diagnose.h> 38 39 //------------------------------------------------------------------------ 40 // 41 //------------------------------------------------------------------------ 42 43 using rtl::OUString; 44 using osl::Mutex; 45 46 //------------------------------------------------------------------------ 47 // 48 //------------------------------------------------------------------------ 49 50 namespace /* private */ 51 { 52 53 const LPTSTR CURRENT_INSTANCE = TEXT("CurrInst"); 54 55 }; 56 57 //------------------------------------------------------------------------ 58 // defines 59 //------------------------------------------------------------------------ 60 61 #define HELPPOPUPWND_CLASS_NAME TEXT("hlppopupwnd###") 62 63 const sal_Int32 MAX_CHARS_PER_LINE = 55; 64 65 const sal_Int32 SHADOW_WIDTH = 6; 66 const sal_Int32 SHADOW_HEIGHT = 6; 67 const sal_Int32 SHADOW_OFFSET = 6; 68 const sal_Int32 YOFFSET = 20; 69 70 const DWORD OUTER_FRAME_COLOR = 0; // black 71 const sal_Int32 OUTER_FRAME_WIDTH = 1; // pixel 72 73 // it's the standard windows color of an inactive window border 74 const DWORD INNER_FRAME_COLOR = 0xC8D0D4; 75 const sal_Int32 INNER_FRAME_WIDTH = 1; // pixel 76 77 //--------------------------------------------------- 78 // static member initialization 79 //--------------------------------------------------- 80 81 osl::Mutex CHelpPopupWindow::s_Mutex; 82 ATOM CHelpPopupWindow::s_ClassAtom = 0; 83 sal_Int32 CHelpPopupWindow::s_RegisterWndClassCount = 0; 84 85 //--------------------------------------------------- 86 // 87 //--------------------------------------------------- 88 89 CHelpPopupWindow::CHelpPopupWindow( 90 HINSTANCE hInstance, 91 HWND hwndParent ) : 92 m_hMargins( 0 ), 93 m_vMargins( 0 ), 94 m_avCharWidth( 0 ), 95 m_avCharHeight( 0 ), 96 m_hwnd( NULL ), 97 m_hwndParent( hwndParent ), 98 m_hInstance( hInstance ), 99 m_hBitmapShadow( NULL ), 100 m_hBrushShadow( NULL ) 101 { 102 m_bWndClassRegistered = RegisterWindowClass( ) ? sal_True : sal_False; 103 104 // create a pattern brush for the window shadow 105 WORD aPattern[] = { 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55 }; 106 107 m_hBitmapShadow = CreateBitmap( 8, 8, 1, 1, aPattern ); 108 m_hBrushShadow = CreatePatternBrush( m_hBitmapShadow ); 109 } 110 111 //--------------------------------------------------- 112 // 113 //--------------------------------------------------- 114 115 CHelpPopupWindow::~CHelpPopupWindow( ) 116 { 117 // remember: we don't have to destroy the 118 // preview window because it will be destroyed 119 // by it's parent window (the FileOpen dialog) 120 // but we have to unregister the window class 121 if ( m_bWndClassRegistered ) 122 UnregisterWindowClass( ); 123 124 DeleteObject( m_hBitmapShadow ); 125 DeleteObject( m_hBrushShadow ); 126 } 127 128 //--------------------------------------------------- 129 // 130 //--------------------------------------------------- 131 132 void SAL_CALL CHelpPopupWindow::setText( const rtl::OUString& aHelpText ) 133 { 134 m_HelpText = aHelpText; 135 } 136 137 //--------------------------------------------------- 138 // 139 //--------------------------------------------------- 140 141 void SAL_CALL CHelpPopupWindow::show( sal_Int32 x, sal_Int32 y ) 142 { 143 OSL_ENSURE( NULL == m_hwnd, "method should not be called twice in sequence" ); 144 145 // we create a window with length and heigth of 0 146 // first in order to get a device context of this 147 // window, then we calculate the upper left corner 148 // and the dimensions and resize the window 149 150 m_hwnd = CreateWindowEx( 151 NULL, 152 HELPPOPUPWND_CLASS_NAME, 153 NULL, 154 WS_POPUP, 155 0, 156 0, 157 0, 158 0, 159 m_hwndParent, 160 NULL, 161 m_hInstance, 162 (LPVOID)this ); 163 164 OSL_ENSURE( m_hwnd, "creating help popup window failed" ); 165 166 sal_Int32 cx_new; 167 sal_Int32 cy_new; 168 169 adjustWindowSize( &cx_new, &cy_new ); 170 adjustWindowPos( x, y, cx_new, cy_new ); 171 172 UpdateWindow( m_hwnd ); 173 ShowWindow( m_hwnd, SW_SHOW ); 174 } 175 176 //--------------------------------------------------- 177 // 178 //--------------------------------------------------- 179 180 HWND SAL_CALL CHelpPopupWindow::setParent( HWND hwndNewParent ) 181 { 182 HWND oldParent = m_hwndParent; 183 184 m_hwndParent = hwndNewParent; 185 186 return oldParent; 187 } 188 189 //--------------------------------------------------- 190 // calculates the necessary dimensions of the popup 191 // window including the margins etc. 192 //--------------------------------------------------- 193 194 void SAL_CALL CHelpPopupWindow::calcWindowRect( LPRECT lprect ) 195 { 196 OSL_ASSERT( m_hwnd && lprect ); 197 198 SetRect( lprect, 0, 0, MAX_CHARS_PER_LINE * m_avCharWidth, 0 ); 199 200 HDC hdc = GetDC( m_hwnd ); 201 202 // set the font we are using later 203 HGDIOBJ oldFont = SelectObject( 204 hdc, GetStockObject( DEFAULT_GUI_FONT ) ); 205 206 UINT nFormat = DT_WORDBREAK | DT_CALCRECT | DT_EXTERNALLEADING | DT_LEFT; 207 208 if ( m_HelpText.getLength( ) <= MAX_CHARS_PER_LINE ) 209 nFormat |= DT_SINGLELINE; 210 211 DrawText( 212 hdc, 213 reinterpret_cast<LPCTSTR>(m_HelpText.getStr( )), 214 m_HelpText.getLength( ), 215 lprect, 216 nFormat ); 217 218 // add the necessary space for the frames 219 // and margins 220 221 lprect->bottom += 222 m_vMargins + 223 SHADOW_HEIGHT + 224 OUTER_FRAME_WIDTH * 2 + 225 INNER_FRAME_WIDTH * 2; 226 227 lprect->right += 228 SHADOW_WIDTH + 229 2 * m_avCharWidth + 230 OUTER_FRAME_WIDTH * 2 + 231 INNER_FRAME_WIDTH * 2; 232 233 SelectObject( hdc, oldFont ); 234 235 ReleaseDC( m_hwnd, hdc ); 236 } 237 238 //--------------------------------------------------- 239 // 240 //--------------------------------------------------- 241 242 void SAL_CALL CHelpPopupWindow::adjustWindowSize( sal_Int32* cx_new, sal_Int32* cy_new ) 243 { 244 OSL_ASSERT( cx_new && cy_new ); 245 246 RECT rect; 247 calcWindowRect( &rect ); 248 249 // adjust the window size 250 SetWindowPos( 251 m_hwnd, 252 NULL, 253 0, 254 0, 255 rect.right, 256 rect.bottom, 257 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER ); 258 259 *cx_new = rect.right; 260 *cy_new = rect.bottom; 261 } 262 263 //--------------------------------------------------- 264 // 265 //--------------------------------------------------- 266 267 void SAL_CALL CHelpPopupWindow::adjustWindowPos( 268 sal_Int32 x, sal_Int32 y, sal_Int32 cx, sal_Int32 cy ) 269 { 270 int popX; 271 int popY; 272 int popWidth; 273 int popHeight; 274 275 OSL_ASSERT( m_hwnd ); 276 277 HDC hdc = GetDC( m_hwnd ); 278 279 // assuming these are screen coordinates 280 popWidth = cx; 281 popHeight = cy; 282 popX = x - ( popWidth / 2 ); 283 popY = y - YOFFSET; 284 285 int xScreen = GetDeviceCaps( hdc, HORZRES ); 286 int yScreen = GetDeviceCaps( hdc, VERTRES ); 287 288 if (popX < 0) 289 popX = 0; 290 291 if (popY < 0) 292 popY = 0; 293 294 if ((popX + popWidth) > xScreen) 295 popX = xScreen - popWidth; 296 297 if ((popY + popHeight) > yScreen) 298 popY = yScreen - popHeight; 299 300 SetWindowPos( 301 m_hwnd, 302 NULL, 303 popX, 304 popY, 305 0, 306 0, 307 SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE ); 308 309 ReleaseDC( m_hwnd, hdc ); 310 } 311 312 //--------------------------------------------------- 313 // 314 //--------------------------------------------------- 315 316 void SAL_CALL CHelpPopupWindow::onPaint( HWND hWnd, HDC hdc ) 317 { 318 RECT rc; 319 RECT rect; 320 HGDIOBJ hpen, hpenOld; 321 HGDIOBJ hbrOld; 322 COLORREF oldBkColor; 323 COLORREF oldTextColor; 324 HGDIOBJ oldFont; 325 HGDIOBJ oldBrush; 326 HGDIOBJ hBrush; 327 328 GetClientRect( hWnd, &rc ); 329 330 // draw the black border 331 332 hBrush = CreateSolidBrush( GetSysColor( COLOR_INFOBK ) ); 333 oldBrush = SelectObject( hdc, hBrush ); 334 335 hpen = CreatePen( PS_SOLID, 0, OUTER_FRAME_COLOR ); 336 hpenOld = SelectObject( hdc, hpen ); 337 338 Rectangle( hdc, 339 rc.left + OUTER_FRAME_WIDTH, 340 rc.top + OUTER_FRAME_WIDTH, 341 rc.right - SHADOW_WIDTH, 342 rc.bottom - SHADOW_HEIGHT); 343 344 SelectObject( hdc, oldBrush ); 345 SelectObject( hdc, hpenOld ); 346 347 DeleteObject( hBrush ); 348 DeleteObject( hpen ); 349 350 // draw a light gray border 351 352 hBrush = CreateSolidBrush( GetSysColor( COLOR_INFOBK ) ); 353 oldBrush = SelectObject( hdc, hBrush ); 354 355 hpen = CreatePen( PS_SOLID, 0, INNER_FRAME_COLOR ); 356 hpenOld = SelectObject( hdc, hpen ); 357 358 Rectangle( hdc, 359 rc.left + OUTER_FRAME_WIDTH + 1, 360 rc.top + OUTER_FRAME_WIDTH + 1, 361 rc.right - SHADOW_WIDTH - OUTER_FRAME_WIDTH, 362 rc.bottom - SHADOW_HEIGHT - OUTER_FRAME_WIDTH ); 363 364 SelectObject( hdc, oldBrush ); 365 SelectObject( hdc, hpenOld ); 366 367 DeleteObject( hBrush ); 368 DeleteObject( hpen ); 369 370 // Write some text to this window 371 372 rect.left = rc.left + OUTER_FRAME_WIDTH + INNER_FRAME_WIDTH + 1 + m_hMargins; 373 rect.top = rc.top + OUTER_FRAME_WIDTH + INNER_FRAME_WIDTH + 1 + m_vMargins / 2; 374 rect.right = rc.right - SHADOW_WIDTH - OUTER_FRAME_WIDTH - INNER_FRAME_WIDTH - m_hMargins; 375 rect.bottom = rc.bottom - SHADOW_HEIGHT - OUTER_FRAME_WIDTH - INNER_FRAME_WIDTH - m_vMargins / 2; 376 377 oldBkColor = SetBkColor( hdc, GetSysColor( COLOR_INFOBK ) ); 378 oldTextColor = SetTextColor( hdc, COLOR_INFOTEXT ); 379 380 oldFont = SelectObject( hdc, GetStockObject( DEFAULT_GUI_FONT ) ); 381 382 UINT nFormat = DT_WORDBREAK | DT_EXTERNALLEADING | DT_LEFT; 383 384 if ( m_HelpText.getLength( ) <= MAX_CHARS_PER_LINE ) 385 nFormat |= DT_SINGLELINE; 386 387 DrawText( 388 hdc, 389 (LPWSTR)m_HelpText.getStr( ), 390 m_HelpText.getLength( ), 391 &rect, 392 nFormat ); 393 394 SelectObject( hdc, oldFont ); 395 SetTextColor( hdc, oldTextColor ); 396 SetBkColor( hdc, oldBkColor ); 397 398 // set text color and text background color 399 // see MSDN PatBlt 400 401 oldBkColor = SetBkColor( hdc, RGB( 0, 0, 0 ) ); 402 oldTextColor = SetTextColor( hdc, RGB( 255, 255, 255 ) ); 403 404 // Get our brush for the shadow 405 406 UnrealizeObject( m_hBrushShadow ); 407 hbrOld = SelectObject( hdc, m_hBrushShadow ); 408 409 // bottom shadow 410 411 PatBlt(hdc, 412 rc.left + SHADOW_OFFSET, 413 rc.bottom - SHADOW_HEIGHT, 414 rc.right - SHADOW_OFFSET - SHADOW_WIDTH, 415 SHADOW_HEIGHT, 416 0xA000C9); 417 418 // right-side shadow 419 420 PatBlt(hdc, 421 rc.right - SHADOW_WIDTH, 422 rc.top + SHADOW_OFFSET, 423 SHADOW_WIDTH, 424 rc.bottom - SHADOW_OFFSET, 425 0xA000C9); 426 427 SelectObject(hdc, hbrOld); 428 SetTextColor( hdc, oldTextColor ); 429 SetBkColor( hdc, oldBkColor ); 430 } 431 432 //--------------------------------------------------- 433 // 434 //--------------------------------------------------- 435 436 void SAL_CALL CHelpPopupWindow::onNcDestroy() 437 { 438 m_hwnd = NULL; 439 } 440 441 //--------------------------------------------------- 442 // 443 //--------------------------------------------------- 444 445 void SAL_CALL CHelpPopupWindow::onCreate( HWND hwnd ) 446 { 447 m_hwnd = hwnd; 448 449 HDC hdc = GetDC( m_hwnd ); 450 451 HGDIOBJ oldFont = SelectObject( 452 hdc, GetStockObject( DEFAULT_GUI_FONT ) ); 453 454 TEXTMETRIC tm; 455 GetTextMetrics( hdc, &tm ); 456 457 m_avCharWidth = tm.tmAveCharWidth; 458 m_avCharHeight = tm.tmHeight; 459 460 if ( 0 == m_hMargins ) 461 m_hMargins = m_avCharWidth; 462 463 if ( 0 == m_vMargins ) 464 m_vMargins = m_avCharHeight; 465 466 SelectObject( hdc, oldFont ); 467 468 ReleaseDC( m_hwnd, hdc ); 469 } 470 471 //--------------------------------------------------- 472 // 473 //--------------------------------------------------- 474 475 LRESULT CALLBACK CHelpPopupWindow::WndProc( 476 HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) 477 { 478 LRESULT lResult = 0; 479 480 switch ( uMsg ) 481 { 482 case WM_CREATE: 483 { 484 LPCREATESTRUCT lpcs = 485 reinterpret_cast< LPCREATESTRUCT >( lParam ); 486 487 OSL_ASSERT( lpcs->lpCreateParams ); 488 489 CHelpPopupWindow* pImpl = reinterpret_cast< CHelpPopupWindow* >( 490 lpcs->lpCreateParams ); 491 492 // connect the instance handle to the window 493 SetProp( hWnd, CURRENT_INSTANCE, pImpl ); 494 495 pImpl->onCreate( hWnd ); 496 497 // capture mouse and keybord events 498 SetCapture( hWnd ); 499 } 500 break; 501 502 case WM_PAINT: 503 { 504 CHelpPopupWindow* pImpl = reinterpret_cast< CHelpPopupWindow* >( 505 GetProp( hWnd, CURRENT_INSTANCE ) ); 506 507 OSL_ASSERT( pImpl ); 508 509 PAINTSTRUCT ps; 510 511 BeginPaint(hWnd, &ps); 512 pImpl->onPaint( hWnd, ps.hdc ); 513 EndPaint(hWnd, &ps); 514 } 515 break; 516 517 case WM_NCDESTROY: 518 { 519 // RemoveProp returns the saved value on success 520 CHelpPopupWindow* pImpl = reinterpret_cast< CHelpPopupWindow* >( 521 RemoveProp( hWnd, CURRENT_INSTANCE ) ); 522 523 OSL_ASSERT( pImpl ); 524 525 pImpl->onNcDestroy(); 526 } 527 break; 528 529 case WM_LBUTTONDOWN: 530 case WM_KEYDOWN: 531 case WM_SYSKEYDOWN: 532 case WM_MBUTTONDOWN: 533 case WM_RBUTTONDOWN: 534 ReleaseCapture(); 535 DestroyWindow(hWnd); 536 break; 537 538 default: 539 return DefWindowProc(hWnd, uMsg, wParam, lParam); 540 } 541 542 return lResult; 543 } 544 545 //--------------------------------------------------- 546 // 547 //--------------------------------------------------- 548 549 ATOM SAL_CALL CHelpPopupWindow::RegisterWindowClass( ) 550 { 551 osl::MutexGuard aGuard( s_Mutex ); 552 553 if ( 0 == s_ClassAtom ) 554 { 555 // register the window class 556 WNDCLASSEX wndClsEx; 557 558 ZeroMemory(&wndClsEx, sizeof(wndClsEx)); 559 560 wndClsEx.cbSize = sizeof(wndClsEx); 561 wndClsEx.lpfnWndProc = CHelpPopupWindow::WndProc; 562 wndClsEx.hInstance = m_hInstance; 563 wndClsEx.hCursor = LoadCursor(NULL, IDC_ARROW); 564 wndClsEx.hbrBackground = (HBRUSH)GetStockObject( NULL_BRUSH ); 565 wndClsEx.lpszClassName = HELPPOPUPWND_CLASS_NAME; 566 567 // register the preview window class 568 // !!! Win95 - the window class will be unregistered automaticly 569 // if the dll is unloaded 570 // Win2000 - the window class must be unregistered manually 571 // if the dll is unloaded 572 s_ClassAtom = RegisterClassEx( &wndClsEx ); 573 OSL_ASSERT(s_ClassAtom); 574 } 575 576 // increment the register class counter 577 // so that we keep track of the number 578 // of class registrations 579 if (0 != s_ClassAtom) 580 s_RegisterWndClassCount++; 581 582 return s_ClassAtom; 583 } 584 585 //--------------------------------------------------- 586 // 587 //--------------------------------------------------- 588 589 void SAL_CALL CHelpPopupWindow::UnregisterWindowClass( ) 590 { 591 osl::MutexGuard aGuard( s_Mutex ); 592 593 OSL_ASSERT( ( (0 != s_ClassAtom) && (s_RegisterWndClassCount > 0)) || 594 ( (0 == s_ClassAtom) && (0 == s_RegisterWndClassCount) ) ); 595 596 // update the register class counter 597 // and unregister the window class if 598 // counter drops to zero 599 if ( 0 != s_ClassAtom ) 600 { 601 s_RegisterWndClassCount--; 602 OSL_ASSERT( s_RegisterWndClassCount >= 0 ); 603 } 604 605 if ( 0 == s_RegisterWndClassCount ) 606 { 607 if ( !UnregisterClass( 608 (LPCTSTR)MAKELONG( s_ClassAtom, 0 ), m_hInstance ) ) 609 { 610 OSL_ENSURE( false, "unregister window class failed" ); 611 } 612 613 s_ClassAtom = 0; 614 } 615 } 616