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 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 
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 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 
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