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