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
CHelpPopupWindow(HINSTANCE hInstance,HWND hwndParent)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
~CHelpPopupWindow()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
setText(const rtl::OUString & aHelpText)128 void SAL_CALL CHelpPopupWindow::setText( const rtl::OUString& aHelpText )
129 {
130 m_HelpText = aHelpText;
131 }
132
133 //---------------------------------------------------
134 //
135 //---------------------------------------------------
136
show(sal_Int32 x,sal_Int32 y)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 height 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
setParent(HWND hwndNewParent)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
calcWindowRect(LPRECT lprect)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
adjustWindowSize(sal_Int32 * cx_new,sal_Int32 * cy_new)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
adjustWindowPos(sal_Int32 x,sal_Int32 y,sal_Int32 cx,sal_Int32 cy)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
onPaint(HWND hWnd,HDC hdc)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
onNcDestroy()432 void SAL_CALL CHelpPopupWindow::onNcDestroy()
433 {
434 m_hwnd = NULL;
435 }
436
437 //---------------------------------------------------
438 //
439 //---------------------------------------------------
440
onCreate(HWND hwnd)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
WndProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)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
RegisterWindowClass()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 automatically
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
UnregisterWindowClass()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