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