blob: 9edbbf6766b783aa533613314d084ecf8add7f23 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_fpicker.hxx"
//------------------------------------------------------------------------
// includes
//------------------------------------------------------------------------
#include <tchar.h>
#include "helppopupwindow.hxx"
#include <osl/diagnose.h>
//------------------------------------------------------------------------
//
//------------------------------------------------------------------------
using rtl::OUString;
using osl::Mutex;
//------------------------------------------------------------------------
//
//------------------------------------------------------------------------
namespace /* private */
{
const LPTSTR CURRENT_INSTANCE = TEXT("CurrInst");
};
//------------------------------------------------------------------------
// defines
//------------------------------------------------------------------------
#define HELPPOPUPWND_CLASS_NAME TEXT("hlppopupwnd###")
const sal_Int32 MAX_CHARS_PER_LINE = 55;
const sal_Int32 SHADOW_WIDTH = 6;
const sal_Int32 SHADOW_HEIGHT = 6;
const sal_Int32 SHADOW_OFFSET = 6;
const sal_Int32 YOFFSET = 20;
const DWORD OUTER_FRAME_COLOR = 0; // black
const sal_Int32 OUTER_FRAME_WIDTH = 1; // pixel
// it's the standard windows color of an inactive window border
const DWORD INNER_FRAME_COLOR = 0xC8D0D4;
const sal_Int32 INNER_FRAME_WIDTH = 1; // pixel
//---------------------------------------------------
// static member initialization
//---------------------------------------------------
osl::Mutex CHelpPopupWindow::s_Mutex;
ATOM CHelpPopupWindow::s_ClassAtom = 0;
sal_Int32 CHelpPopupWindow::s_RegisterWndClassCount = 0;
//---------------------------------------------------
//
//---------------------------------------------------
CHelpPopupWindow::CHelpPopupWindow(
HINSTANCE hInstance,
HWND hwndParent ) :
m_hMargins( 0 ),
m_vMargins( 0 ),
m_avCharWidth( 0 ),
m_avCharHeight( 0 ),
m_hwnd( NULL ),
m_hwndParent( hwndParent ),
m_hInstance( hInstance ),
m_hBitmapShadow( NULL ),
m_hBrushShadow( NULL )
{
m_bWndClassRegistered = RegisterWindowClass( ) ? sal_True : sal_False;
// create a pattern brush for the window shadow
WORD aPattern[] = { 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55 };
m_hBitmapShadow = CreateBitmap( 8, 8, 1, 1, aPattern );
m_hBrushShadow = CreatePatternBrush( m_hBitmapShadow );
}
//---------------------------------------------------
//
//---------------------------------------------------
CHelpPopupWindow::~CHelpPopupWindow( )
{
// remember: we don't have to destroy the
// preview window because it will be destroyed
// by it's parent window (the FileOpen dialog)
// but we have to unregister the window class
if ( m_bWndClassRegistered )
UnregisterWindowClass( );
DeleteObject( m_hBitmapShadow );
DeleteObject( m_hBrushShadow );
}
//---------------------------------------------------
//
//---------------------------------------------------
void SAL_CALL CHelpPopupWindow::setText( const rtl::OUString& aHelpText )
{
m_HelpText = aHelpText;
}
//---------------------------------------------------
//
//---------------------------------------------------
void SAL_CALL CHelpPopupWindow::show( sal_Int32 x, sal_Int32 y )
{
OSL_ENSURE( NULL == m_hwnd, "method should not be called twice in sequence" );
// we create a window with length and heigth of 0
// first in order to get a device context of this
// window, then we calculate the upper left corner
// and the dimensions and resize the window
m_hwnd = CreateWindowEx(
NULL,
HELPPOPUPWND_CLASS_NAME,
NULL,
WS_POPUP,
0,
0,
0,
0,
m_hwndParent,
NULL,
m_hInstance,
(LPVOID)this );
OSL_ENSURE( m_hwnd, "creating help popup window failed" );
sal_Int32 cx_new;
sal_Int32 cy_new;
adjustWindowSize( &cx_new, &cy_new );
adjustWindowPos( x, y, cx_new, cy_new );
UpdateWindow( m_hwnd );
ShowWindow( m_hwnd, SW_SHOW );
}
//---------------------------------------------------
//
//---------------------------------------------------
HWND SAL_CALL CHelpPopupWindow::setParent( HWND hwndNewParent )
{
HWND oldParent = m_hwndParent;
m_hwndParent = hwndNewParent;
return oldParent;
}
//---------------------------------------------------
// calculates the necessary dimensions of the popup
// window including the margins etc.
//---------------------------------------------------
void SAL_CALL CHelpPopupWindow::calcWindowRect( LPRECT lprect )
{
OSL_ASSERT( m_hwnd && lprect );
SetRect( lprect, 0, 0, MAX_CHARS_PER_LINE * m_avCharWidth, 0 );
HDC hdc = GetDC( m_hwnd );
// set the font we are using later
HGDIOBJ oldFont = SelectObject(
hdc, GetStockObject( DEFAULT_GUI_FONT ) );
UINT nFormat = DT_WORDBREAK | DT_CALCRECT | DT_EXTERNALLEADING | DT_LEFT;
if ( m_HelpText.getLength( ) <= MAX_CHARS_PER_LINE )
nFormat |= DT_SINGLELINE;
DrawText(
hdc,
reinterpret_cast<LPCTSTR>(m_HelpText.getStr( )),
m_HelpText.getLength( ),
lprect,
nFormat );
// add the necessary space for the frames
// and margins
lprect->bottom +=
m_vMargins +
SHADOW_HEIGHT +
OUTER_FRAME_WIDTH * 2 +
INNER_FRAME_WIDTH * 2;
lprect->right +=
SHADOW_WIDTH +
2 * m_avCharWidth +
OUTER_FRAME_WIDTH * 2 +
INNER_FRAME_WIDTH * 2;
SelectObject( hdc, oldFont );
ReleaseDC( m_hwnd, hdc );
}
//---------------------------------------------------
//
//---------------------------------------------------
void SAL_CALL CHelpPopupWindow::adjustWindowSize( sal_Int32* cx_new, sal_Int32* cy_new )
{
OSL_ASSERT( cx_new && cy_new );
RECT rect;
calcWindowRect( &rect );
// adjust the window size
SetWindowPos(
m_hwnd,
NULL,
0,
0,
rect.right,
rect.bottom,
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER );
*cx_new = rect.right;
*cy_new = rect.bottom;
}
//---------------------------------------------------
//
//---------------------------------------------------
void SAL_CALL CHelpPopupWindow::adjustWindowPos(
sal_Int32 x, sal_Int32 y, sal_Int32 cx, sal_Int32 cy )
{
int popX;
int popY;
int popWidth;
int popHeight;
OSL_ASSERT( m_hwnd );
HDC hdc = GetDC( m_hwnd );
// assuming these are screen coordinates
popWidth = cx;
popHeight = cy;
popX = x - ( popWidth / 2 );
popY = y - YOFFSET;
int xScreen = GetDeviceCaps( hdc, HORZRES );
int yScreen = GetDeviceCaps( hdc, VERTRES );
if (popX < 0)
popX = 0;
if (popY < 0)
popY = 0;
if ((popX + popWidth) > xScreen)
popX = xScreen - popWidth;
if ((popY + popHeight) > yScreen)
popY = yScreen - popHeight;
SetWindowPos(
m_hwnd,
NULL,
popX,
popY,
0,
0,
SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE );
ReleaseDC( m_hwnd, hdc );
}
//---------------------------------------------------
//
//---------------------------------------------------
void SAL_CALL CHelpPopupWindow::onPaint( HWND hWnd, HDC hdc )
{
RECT rc;
RECT rect;
HGDIOBJ hpen, hpenOld;
HGDIOBJ hbrOld;
COLORREF oldBkColor;
COLORREF oldTextColor;
HGDIOBJ oldFont;
HGDIOBJ oldBrush;
HGDIOBJ hBrush;
GetClientRect( hWnd, &rc );
// draw the black border
hBrush = CreateSolidBrush( GetSysColor( COLOR_INFOBK ) );
oldBrush = SelectObject( hdc, hBrush );
hpen = CreatePen( PS_SOLID, 0, OUTER_FRAME_COLOR );
hpenOld = SelectObject( hdc, hpen );
Rectangle( hdc,
rc.left + OUTER_FRAME_WIDTH,
rc.top + OUTER_FRAME_WIDTH,
rc.right - SHADOW_WIDTH,
rc.bottom - SHADOW_HEIGHT);
SelectObject( hdc, oldBrush );
SelectObject( hdc, hpenOld );
DeleteObject( hBrush );
DeleteObject( hpen );
// draw a light gray border
hBrush = CreateSolidBrush( GetSysColor( COLOR_INFOBK ) );
oldBrush = SelectObject( hdc, hBrush );
hpen = CreatePen( PS_SOLID, 0, INNER_FRAME_COLOR );
hpenOld = SelectObject( hdc, hpen );
Rectangle( hdc,
rc.left + OUTER_FRAME_WIDTH + 1,
rc.top + OUTER_FRAME_WIDTH + 1,
rc.right - SHADOW_WIDTH - OUTER_FRAME_WIDTH,
rc.bottom - SHADOW_HEIGHT - OUTER_FRAME_WIDTH );
SelectObject( hdc, oldBrush );
SelectObject( hdc, hpenOld );
DeleteObject( hBrush );
DeleteObject( hpen );
// Write some text to this window
rect.left = rc.left + OUTER_FRAME_WIDTH + INNER_FRAME_WIDTH + 1 + m_hMargins;
rect.top = rc.top + OUTER_FRAME_WIDTH + INNER_FRAME_WIDTH + 1 + m_vMargins / 2;
rect.right = rc.right - SHADOW_WIDTH - OUTER_FRAME_WIDTH - INNER_FRAME_WIDTH - m_hMargins;
rect.bottom = rc.bottom - SHADOW_HEIGHT - OUTER_FRAME_WIDTH - INNER_FRAME_WIDTH - m_vMargins / 2;
oldBkColor = SetBkColor( hdc, GetSysColor( COLOR_INFOBK ) );
oldTextColor = SetTextColor( hdc, COLOR_INFOTEXT );
oldFont = SelectObject( hdc, GetStockObject( DEFAULT_GUI_FONT ) );
UINT nFormat = DT_WORDBREAK | DT_EXTERNALLEADING | DT_LEFT;
if ( m_HelpText.getLength( ) <= MAX_CHARS_PER_LINE )
nFormat |= DT_SINGLELINE;
DrawText(
hdc,
(LPWSTR)m_HelpText.getStr( ),
m_HelpText.getLength( ),
&rect,
nFormat );
SelectObject( hdc, oldFont );
SetTextColor( hdc, oldTextColor );
SetBkColor( hdc, oldBkColor );
// set text color and text background color
// see MSDN PatBlt
oldBkColor = SetBkColor( hdc, RGB( 0, 0, 0 ) );
oldTextColor = SetTextColor( hdc, RGB( 255, 255, 255 ) );
// Get our brush for the shadow
UnrealizeObject( m_hBrushShadow );
hbrOld = SelectObject( hdc, m_hBrushShadow );
// bottom shadow
PatBlt(hdc,
rc.left + SHADOW_OFFSET,
rc.bottom - SHADOW_HEIGHT,
rc.right - SHADOW_OFFSET - SHADOW_WIDTH,
SHADOW_HEIGHT,
0xA000C9);
// right-side shadow
PatBlt(hdc,
rc.right - SHADOW_WIDTH,
rc.top + SHADOW_OFFSET,
SHADOW_WIDTH,
rc.bottom - SHADOW_OFFSET,
0xA000C9);
SelectObject(hdc, hbrOld);
SetTextColor( hdc, oldTextColor );
SetBkColor( hdc, oldBkColor );
}
//---------------------------------------------------
//
//---------------------------------------------------
void SAL_CALL CHelpPopupWindow::onNcDestroy()
{
m_hwnd = NULL;
}
//---------------------------------------------------
//
//---------------------------------------------------
void SAL_CALL CHelpPopupWindow::onCreate( HWND hwnd )
{
m_hwnd = hwnd;
HDC hdc = GetDC( m_hwnd );
HGDIOBJ oldFont = SelectObject(
hdc, GetStockObject( DEFAULT_GUI_FONT ) );
TEXTMETRIC tm;
GetTextMetrics( hdc, &tm );
m_avCharWidth = tm.tmAveCharWidth;
m_avCharHeight = tm.tmHeight;
if ( 0 == m_hMargins )
m_hMargins = m_avCharWidth;
if ( 0 == m_vMargins )
m_vMargins = m_avCharHeight;
SelectObject( hdc, oldFont );
ReleaseDC( m_hwnd, hdc );
}
//---------------------------------------------------
//
//---------------------------------------------------
LRESULT CALLBACK CHelpPopupWindow::WndProc(
HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
LRESULT lResult = 0;
switch ( uMsg )
{
case WM_CREATE:
{
LPCREATESTRUCT lpcs =
reinterpret_cast< LPCREATESTRUCT >( lParam );
OSL_ASSERT( lpcs->lpCreateParams );
CHelpPopupWindow* pImpl = reinterpret_cast< CHelpPopupWindow* >(
lpcs->lpCreateParams );
// connect the instance handle to the window
SetProp( hWnd, CURRENT_INSTANCE, pImpl );
pImpl->onCreate( hWnd );
// capture mouse and keybord events
SetCapture( hWnd );
}
break;
case WM_PAINT:
{
CHelpPopupWindow* pImpl = reinterpret_cast< CHelpPopupWindow* >(
GetProp( hWnd, CURRENT_INSTANCE ) );
OSL_ASSERT( pImpl );
PAINTSTRUCT ps;
BeginPaint(hWnd, &ps);
pImpl->onPaint( hWnd, ps.hdc );
EndPaint(hWnd, &ps);
}
break;
case WM_NCDESTROY:
{
// RemoveProp returns the saved value on success
CHelpPopupWindow* pImpl = reinterpret_cast< CHelpPopupWindow* >(
RemoveProp( hWnd, CURRENT_INSTANCE ) );
OSL_ASSERT( pImpl );
pImpl->onNcDestroy();
}
break;
case WM_LBUTTONDOWN:
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONDOWN:
ReleaseCapture();
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return lResult;
}
//---------------------------------------------------
//
//---------------------------------------------------
ATOM SAL_CALL CHelpPopupWindow::RegisterWindowClass( )
{
osl::MutexGuard aGuard( s_Mutex );
if ( 0 == s_ClassAtom )
{
// register the window class
WNDCLASSEX wndClsEx;
ZeroMemory(&wndClsEx, sizeof(wndClsEx));
wndClsEx.cbSize = sizeof(wndClsEx);
wndClsEx.lpfnWndProc = CHelpPopupWindow::WndProc;
wndClsEx.hInstance = m_hInstance;
wndClsEx.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClsEx.hbrBackground = (HBRUSH)GetStockObject( NULL_BRUSH );
wndClsEx.lpszClassName = HELPPOPUPWND_CLASS_NAME;
// register the preview window class
// !!! Win95 - the window class will be unregistered automaticly
// if the dll is unloaded
// Win2000 - the window class must be unregistered manually
// if the dll is unloaded
s_ClassAtom = RegisterClassEx( &wndClsEx );
OSL_ASSERT(s_ClassAtom);
}
// increment the register class counter
// so that we keep track of the number
// of class registrations
if (0 != s_ClassAtom)
s_RegisterWndClassCount++;
return s_ClassAtom;
}
//---------------------------------------------------
//
//---------------------------------------------------
void SAL_CALL CHelpPopupWindow::UnregisterWindowClass( )
{
osl::MutexGuard aGuard( s_Mutex );
OSL_ASSERT( ( (0 != s_ClassAtom) && (s_RegisterWndClassCount > 0)) ||
( (0 == s_ClassAtom) && (0 == s_RegisterWndClassCount) ) );
// update the register class counter
// and unregister the window class if
// counter drops to zero
if ( 0 != s_ClassAtom )
{
s_RegisterWndClassCount--;
OSL_ASSERT( s_RegisterWndClassCount >= 0 );
}
if ( 0 == s_RegisterWndClassCount )
{
if ( !UnregisterClass(
(LPCTSTR)MAKELONG( s_ClassAtom, 0 ), m_hInstance ) )
{
OSL_ENSURE( false, "unregister window class failed" );
}
s_ClassAtom = 0;
}
}