diff options
Diffstat (limited to 'vcl/unx/generic')
81 files changed, 58495 insertions, 0 deletions
diff --git a/vcl/unx/generic/app/i18n_cb.cxx b/vcl/unx/generic/app/i18n_cb.cxx new file mode 100644 index 000000000000..92429e63c91e --- /dev/null +++ b/vcl/unx/generic/app/i18n_cb.cxx @@ -0,0 +1,658 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <stdio.h> +#include <string.h> + +#include <sal/alloca.h> +#include <osl/thread.h> + +#include <tools/prex.h> +#include <X11/Xlocale.h> +#include <X11/Xlib.h> +#include <tools/postx.h> + +#include "unx/salunx.h" +#include "unx/XIM.h" +#include "unx/i18n_cb.hxx" +#include "unx/i18n_status.hxx" +#include "unx/i18n_ic.hxx" +#include "unx/i18n_im.hxx" +#include "salframe.hxx" + +// ------------------------------------------------------------------------- +// +// i. preedit start callback +// +// ------------------------------------------------------------------------- + +int +PreeditStartCallback ( XIC, XPointer client_data, XPointer ) +{ + preedit_data_t* pPreeditData = (preedit_data_t*)client_data; + if ( pPreeditData->eState == ePreeditStatusActivationRequired ) + { + pPreeditData->eState = ePreeditStatusActive; + pPreeditData->aText.nCursorPos = 0; + pPreeditData->aText.nLength = 0; + } + + return -1; +} + +// ------------------------------------------------------------------------- +// +// ii. preedit done callback +// +// ------------------------------------------------------------------------- + +void +PreeditDoneCallback ( XIC, XPointer client_data, XPointer ) +{ + preedit_data_t* pPreeditData = (preedit_data_t*)client_data; + if (pPreeditData->eState == ePreeditStatusActive ) + { + if( pPreeditData->pFrame ) + pPreeditData->pFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, (void*)NULL ); + } + pPreeditData->eState = ePreeditStatusStartPending; +} + +// ------------------------------------------------------------------------- +// +// iii. preedit draw callback +// +// ------------------------------------------------------------------------- + +// +// Handle deletion of text in a preedit_draw_callback +// from and howmuch are guaranteed to be nonnegative +// + +void +Preedit_DeleteText(preedit_text_t *ptext, int from, int howmuch) +{ + // If we've been asked to delete no text then just set + // nLength correctly and return + if (ptext->nLength == 0) + { + ptext->nLength = from; + return; + } + + int to = from + howmuch; + + if (to == (int)ptext->nLength) + { + // delete from the end of the text + ptext->nLength = from; + } + else + if (to < (int)ptext->nLength) + { + // cut out of the middle of the text + memmove( (void*)(ptext->pUnicodeBuffer + from), + (void*)(ptext->pUnicodeBuffer + to), + (ptext->nLength - to) * sizeof(sal_Unicode)); + memmove( (void*)(ptext->pCharStyle + from), + (void*)(ptext->pCharStyle + to), + (ptext->nLength - to) * sizeof(XIMFeedback)); + ptext->nLength -= howmuch; + } + else + { + // XXX this indicates an error, are we out of sync ? + fprintf(stderr, "Preedit_DeleteText( from=%i to=%i length=%i )\n", + from, to, ptext->nLength ); + fprintf (stderr, "\t XXX internal error, out of sync XXX\n"); + + ptext->nLength = from; + } + + // NULL-terminate the string + ptext->pUnicodeBuffer[ptext->nLength] = (sal_Unicode)0; +} + +// reallocate the textbuffer with sufficiently large size 2^x +// nnewlimit is presupposed to be larger than ptext->size +void +enlarge_buffer ( preedit_text_t *ptext, int nnewlimit ) +{ + size_t nnewsize = ptext->nSize; + + while ( nnewsize <= (size_t)nnewlimit ) + nnewsize *= 2; + + ptext->nSize = nnewsize; + ptext->pUnicodeBuffer = (sal_Unicode*)realloc((void*)ptext->pUnicodeBuffer, + nnewsize * sizeof(sal_Unicode)); + ptext->pCharStyle = (XIMFeedback*)realloc((void*)ptext->pCharStyle, + nnewsize * sizeof(XIMFeedback)); +} + +// +// Handle insertion of text in a preedit_draw_callback +// string field of XIMText struct is guaranteed to be != NULL +// + +void +Preedit_InsertText(preedit_text_t *pText, XIMText *pInsertText, int where, + Bool isMultilingual) +{ + sal_Unicode *pInsertTextString; + int nInsertTextLength = 0; + XIMFeedback *pInsertTextCharStyle = pInsertText->feedback; + + nInsertTextLength = pInsertText->length; + + if (isMultilingual) + { + XIMUnicodeText *pUniText = (XIMUnicodeText*)pInsertText; + pInsertTextString = pUniText->string.utf16_char; + } + else + { + // can't handle wchar_t strings, so convert to multibyte chars first + char *pMBString; + size_t nMBLength; + if (pInsertText->encoding_is_wchar) + { + wchar_t *pWCString = pInsertText->string.wide_char; + size_t nBytes = wcstombs ( NULL, pWCString, 1024 /* dont care */); + pMBString = (char*)alloca( nBytes + 1 ); + nMBLength = wcstombs ( pMBString, pWCString, nBytes + 1); + } + else + { + pMBString = pInsertText->string.multi_byte; + nMBLength = strlen(pMBString); // xxx + } + + // convert multibyte chars to unicode + rtl_TextEncoding nEncoding = osl_getThreadTextEncoding(); + + if (nEncoding != RTL_TEXTENCODING_UNICODE) + { + rtl_TextToUnicodeConverter aConverter = + rtl_createTextToUnicodeConverter( nEncoding ); + rtl_TextToUnicodeContext aContext = + rtl_createTextToUnicodeContext(aConverter); + + sal_Size nBufferSize = nInsertTextLength * 2; + + pInsertTextString = (sal_Unicode*)alloca(nBufferSize); + + sal_uInt32 nConversionInfo; + sal_Size nConvertedChars; + + rtl_convertTextToUnicode( aConverter, aContext, + pMBString, nMBLength, + pInsertTextString, nBufferSize, + RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_IGNORE + | RTL_TEXTTOUNICODE_FLAGS_INVALID_IGNORE, + &nConversionInfo, &nConvertedChars ); + + rtl_destroyTextToUnicodeContext(aConverter, aContext); + rtl_destroyTextToUnicodeConverter(aConverter); + + } + else + { + pInsertTextString = (sal_Unicode*)pMBString; + } + } + + // enlarge target text-buffer if necessary + if (pText->nSize <= (pText->nLength + nInsertTextLength)) + enlarge_buffer(pText, pText->nLength + nInsertTextLength); + + // insert text: displace old mem and put new bytes in + int from = where; + int to = where + nInsertTextLength; + int howmany = pText->nLength - where; + + memmove((void*)(pText->pUnicodeBuffer + to), + (void*)(pText->pUnicodeBuffer + from), + howmany * sizeof(sal_Unicode)); + memmove((void*)(pText->pCharStyle + to), + (void*)(pText->pCharStyle + from), + howmany * sizeof(XIMFeedback)); + + to = from; + howmany = nInsertTextLength; + + memcpy((void*)(pText->pUnicodeBuffer + to), (void*)pInsertTextString, + howmany * sizeof(sal_Unicode)); + memcpy((void*)(pText->pCharStyle + to), (void*)pInsertTextCharStyle, + howmany * sizeof(XIMFeedback)); + + pText->nLength += howmany; + + // NULL-terminate the string + pText->pUnicodeBuffer[pText->nLength] = (sal_Unicode)0; +} + +// +// Handle the change of attributes in a preedit_draw_callback +// +void +Preedit_UpdateAttributes ( preedit_text_t* ptext, XIMFeedback* feedback, + int from, int amount ) +{ + if ( (from + amount) > (int)ptext->nLength ) + { + // XXX this indicates an error, are we out of sync ? + fprintf (stderr, "Preedit_UpdateAttributes( %i + %i > %i )\n", + from, amount, ptext->nLength ); + fprintf (stderr, "\t XXX internal error, out of sync XXX\n"); + + return; + } + + memcpy ( ptext->pCharStyle + from, + feedback, amount * sizeof(XIMFeedback) ); +} + +// Convert the XIM feedback values into appropriate VCL +// SAL_EXTTEXTINPUT_ATTR values +// returns an allocate list of attributes, which must be freed by caller +sal_uInt16* +Preedit_FeedbackToSAL ( XIMFeedback* pfeedback, int nlength, std::vector<sal_uInt16>& rSalAttr ) +{ + sal_uInt16 *psalattr; + sal_uInt16 nval; + sal_uInt16 noldval = 0; + XIMFeedback nfeedback; + + // only work with reasonable length + if (nlength > 0 && nlength > sal::static_int_cast<int>(rSalAttr.size()) ) + { + rSalAttr.reserve( nlength ); + psalattr = &rSalAttr[0]; + } + else + return (sal_uInt16*)NULL; + + for (int npos = 0; npos < nlength; npos++) + { + nval = 0; + nfeedback = pfeedback[npos]; + + // means to use the feedback of the previous char + if (nfeedback == 0) + { + nval = noldval; + } + // convert feedback to attributes + else + { + if (nfeedback & XIMReverse) + nval |= SAL_EXTTEXTINPUT_ATTR_HIGHLIGHT; + if (nfeedback & XIMUnderline) + nval |= SAL_EXTTEXTINPUT_ATTR_UNDERLINE; + if (nfeedback & XIMHighlight) + nval |= SAL_EXTTEXTINPUT_ATTR_HIGHLIGHT; + if (nfeedback & XIMPrimary) + nval |= SAL_EXTTEXTINPUT_ATTR_DOTTEDUNDERLINE; + if (nfeedback & XIMSecondary) + nval |= SAL_EXTTEXTINPUT_ATTR_DASHDOTUNDERLINE; + if (nfeedback & XIMTertiary) // same as 2ery + nval |= SAL_EXTTEXTINPUT_ATTR_DASHDOTUNDERLINE; + + } + // copy in list + psalattr[npos] = nval; + noldval = nval; + } + // return list of sal attributes + return psalattr; +} + +void +PreeditDrawCallback(XIC ic, XPointer client_data, + XIMPreeditDrawCallbackStruct *call_data) +{ + preedit_data_t* pPreeditData = (preedit_data_t*)client_data; + + // if there's nothing to change then change nothing + if ( ( (call_data->text == NULL) && (call_data->chg_length == 0) ) + || pPreeditData->pFrame == NULL ) + return; + + // Solaris 7 deletes the preedit buffer after commit + // since the next call to preeditstart will have the same effect just skip this. + // if (pPreeditData->eState == ePreeditStatusStartPending && call_data->text == NULL) + // return; + + if ( pPreeditData->eState == ePreeditStatusStartPending ) + pPreeditData->eState = ePreeditStatusActivationRequired; + PreeditStartCallback( ic, client_data, NULL ); + + // Edit the internal textbuffer as indicated by the call_data, + // chg_first and chg_length are guaranteed to be nonnegative + + // handle text deletion + if (call_data->text == NULL) + { + Preedit_DeleteText(&(pPreeditData->aText), + call_data->chg_first, call_data->chg_length ); + } + else + { + // handle text insertion + if ( (call_data->chg_length == 0) + && (call_data->text->string.wide_char != NULL)) + { + Preedit_InsertText(&(pPreeditData->aText), call_data->text, + call_data->chg_first, pPreeditData->bIsMultilingual); + } + else + // handle text replacement by deletion and insertion of text, + // not smart, just good enough + if ( (call_data->chg_length != 0) + && (call_data->text->string.wide_char != NULL)) + { + Preedit_DeleteText(&(pPreeditData->aText), + call_data->chg_first, call_data->chg_length); + Preedit_InsertText(&(pPreeditData->aText), call_data->text, + call_data->chg_first, pPreeditData->bIsMultilingual); + } + else + // not really a text update, only attributes are concerned + if ( (call_data->chg_length != 0) + && (call_data->text->string.wide_char == NULL)) + { + Preedit_UpdateAttributes(&(pPreeditData->aText), + call_data->text->feedback, + call_data->chg_first, call_data->chg_length); + } + } + + // + // build the SalExtTextInputEvent and send it up + // + pPreeditData->aInputEv.mnTime = 0; + pPreeditData->aInputEv.mpTextAttr = Preedit_FeedbackToSAL( + pPreeditData->aText.pCharStyle, pPreeditData->aText.nLength, pPreeditData->aInputFlags); + pPreeditData->aInputEv.mnCursorPos = call_data->caret; + pPreeditData->aInputEv.maText = String (pPreeditData->aText.pUnicodeBuffer, + pPreeditData->aText.nLength); + pPreeditData->aInputEv.mnCursorFlags = 0; // default: make cursor visible + pPreeditData->aInputEv.mnDeltaStart = 0; // call_data->chg_first; + pPreeditData->aInputEv.mbOnlyCursor = False; + + if ( pPreeditData->eState == ePreeditStatusActive && pPreeditData->pFrame ) + pPreeditData->pFrame->CallCallback(SALEVENT_EXTTEXTINPUT, (void*)&pPreeditData->aInputEv); + if (pPreeditData->aText.nLength == 0 && pPreeditData->pFrame ) + pPreeditData->pFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, (void*)NULL ); + + if (pPreeditData->aText.nLength == 0) + pPreeditData->eState = ePreeditStatusStartPending; + + GetPreeditSpotLocation(ic, (XPointer)pPreeditData); +} + +void +GetPreeditSpotLocation(XIC ic, XPointer client_data) +{ + // + // Send SalEventExtTextInputPos event to get spotlocation + // + SalExtTextInputPosEvent mPosEvent; + preedit_data_t* pPreeditData = (preedit_data_t*)client_data; + + if( pPreeditData->pFrame ) + pPreeditData->pFrame->CallCallback(SALEVENT_EXTTEXTINPUTPOS, (void*)&mPosEvent); + + XPoint point; + point.x = mPosEvent.mnX + mPosEvent.mnWidth; + point.y = mPosEvent.mnY + mPosEvent.mnHeight; + + XVaNestedList preedit_attr; + preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &point, NULL); + XSetICValues(ic, XNPreeditAttributes, preedit_attr, NULL); + XFree(preedit_attr); + + return; +} + +// ------------------------------------------------------------------------- +// +// iv. preedit caret callback +// +// ------------------------------------------------------------------------- + +#if OSL_DEBUG_LEVEL > 1 +void +PreeditCaretCallback ( XIC ic, XPointer client_data, + XIMPreeditCaretCallbackStruct *call_data ) +#else +void +PreeditCaretCallback ( XIC, XPointer,XIMPreeditCaretCallbackStruct* ) +#endif +{ + #if OSL_DEBUG_LEVEL > 1 + // XXX PreeditCaretCallback is pure debug code for now + const char *direction = "?"; + const char *style = "?"; + + switch ( call_data->style ) + { + case XIMIsInvisible: style = "Invisible"; break; + case XIMIsPrimary: style = "Primary"; break; + case XIMIsSecondary: style = "Secondary"; break; + } + switch ( call_data->direction ) + { + case XIMForwardChar: direction = "Forward char"; break; + case XIMBackwardChar: direction = "Backward char"; break; + case XIMForwardWord: direction = "Forward word"; break; + case XIMBackwardWord: direction = "Backward word"; break; + case XIMCaretUp: direction = "Caret up"; break; + case XIMCaretDown: direction = "Caret down"; break; + case XIMNextLine: direction = "Next line"; break; + case XIMPreviousLine: direction = "Previous line"; break; + case XIMLineStart: direction = "Line start"; break; + case XIMLineEnd: direction = "Line end"; break; + case XIMAbsolutePosition: direction = "Absolute"; break; + case XIMDontChange: direction = "Dont change"; break; + } + + fprintf (stderr, "PreeditCaretCallback( ic=%p, client=%p,\n", + ic, client_data ); + fprintf (stderr, "\t position=%i, direction=\"%s\", style=\"%s\" )\n", + call_data->position, direction, style ); + #endif +} + +// ----------------------------------------------------------------------- +// +// v. commit string callback: convert an extended text input (iiimp ... ) +// into an ordinary key-event +// +// ----------------------------------------------------------------------- + +Bool +IsControlCode(sal_Unicode nChar) +{ + if ( nChar <= 0x1F /* C0 controls */ ) + return True; + else + return False; +} + +int +CommitStringCallback( XIC ic, XPointer client_data, XPointer call_data ) +{ + preedit_data_t* pPreeditData = (preedit_data_t*)client_data; + + XIMUnicodeText *cbtext = (XIMUnicodeText *)call_data; + sal_Unicode *p_unicode_data = (sal_Unicode*)cbtext->string.utf16_char; + + // filter unexpected pure control events + if (cbtext->length == 1 && IsControlCode(p_unicode_data[0]) ) + { + if( pPreeditData->pFrame ) + { + pPreeditData->pFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, (void*)NULL ); + } + } + else + { + if( pPreeditData->pFrame ) + { + pPreeditData->aInputEv.mnTime = 0; + pPreeditData->aInputEv.mpTextAttr = 0; + pPreeditData->aInputEv.mnCursorPos = cbtext->length; + pPreeditData->aInputEv.maText = UniString(p_unicode_data, cbtext->length); + pPreeditData->aInputEv.mnCursorFlags = 0; // default: make cursor visible + pPreeditData->aInputEv.mnDeltaStart = 0; + pPreeditData->aInputEv.mbOnlyCursor = False; + + pPreeditData->pFrame->CallCallback( SALEVENT_EXTTEXTINPUT, (void*)&pPreeditData->aInputEv); + pPreeditData->pFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, (void*)NULL ); + } + } + pPreeditData->eState = ePreeditStatusStartPending; + + GetPreeditSpotLocation(ic, (XPointer)pPreeditData); + + return 0; +} + +// ---------------------------------------------------------------------------------- +// +// vi. status callbacks: for now these are empty, they are just needed for turbo linux +// +// ---------------------------------------------------------------------------------- + +void +StatusStartCallback (XIC, XPointer, XPointer) +{ + return; +} + +void +StatusDoneCallback (XIC, XPointer, XPointer) +{ + return; +} + +void +StatusDrawCallback (XIC ic, XPointer client_data, XIMStatusDrawCallbackStruct *call_data) +{ + preedit_data_t* pPreeditData = (preedit_data_t*)client_data; + if( pPreeditData->bIsMultilingual ) + { + // IIIMP + XIMUnicodeText *cbtext = (XIMUnicodeText *)call_data->data.text; + ::vcl::I18NStatus::get().setStatusText( String( cbtext->string.utf16_char, call_data->data.text->length ) ); + XIMUnicodeCharacterSubset* pSubset = NULL; + if( ! XGetICValues( ic, + XNUnicodeCharacterSubset, & pSubset, + NULL ) + && pSubset ) + { + ::vcl::I18NStatus::get().changeIM( String( ByteString( pSubset->name ), RTL_TEXTENCODING_UTF8 ) ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "got XNUnicodeCharacterSubset\n %d\n %d\n %s\n %d\n", pSubset->index, pSubset->subset_id, pSubset->name, pSubset->is_active ); +#endif + } + } + else if( call_data->type == XIMTextType ) + { + String aText; + if( call_data->data.text ) + { + // XIM with text + sal_Char* pMBString = NULL; + size_t nLength = 0; + if( call_data->data.text->encoding_is_wchar ) + { + if( call_data->data.text->string.wide_char ) + { + wchar_t* pWString = call_data->data.text->string.wide_char; + size_t nBytes = wcstombs( NULL, pWString, 1024 ); + pMBString = (sal_Char*)alloca( nBytes+1 ); + nLength = wcstombs( pMBString, pWString, nBytes+1 ); + } + } + else + { + if( call_data->data.text->string.multi_byte ) + { + pMBString = call_data->data.text->string.multi_byte; + nLength = strlen( pMBString ); + } + } + if( nLength ) + aText = String( pMBString, nLength, gsl_getSystemTextEncoding() ); + } + ::vcl::I18NStatus::get().setStatusText( aText ); + } +#if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "XIMStatusDataType %s not supported\n", + call_data->type == XIMBitmapType ? "XIMBitmapType" : ByteString::CreateFromInt32( call_data->type ).GetBuffer() ); +#endif + return; +} + +void +SwitchIMCallback (XIC, XPointer, XPointer call_data) +{ + XIMSwitchIMNotifyCallbackStruct* pCallData = (XIMSwitchIMNotifyCallbackStruct*)call_data; + ::vcl::I18NStatus::get().changeIM( String( ByteString( pCallData->to->name ), RTL_TEXTENCODING_UTF8 ) ); +} + +// ---------------------------------------------------------------------------------- +// +// vii. destroy callbacks: internally disable all IC/IM calls +// +// ---------------------------------------------------------------------------------- + +void +IC_IMDestroyCallback (XIM, XPointer client_data, XPointer) +{ + SalI18N_InputContext *pContext = (SalI18N_InputContext*)client_data; + if (pContext != NULL) + pContext->HandleDestroyIM(); +} + +void +IM_IMDestroyCallback (XIM, XPointer client_data, XPointer) +{ + SalI18N_InputMethod *pMethod = (SalI18N_InputMethod*)client_data; + if (pMethod != NULL) + pMethod->HandleDestroyIM(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/app/i18n_ic.cxx b/vcl/unx/generic/app/i18n_ic.cxx new file mode 100644 index 000000000000..8a8b5d620cb6 --- /dev/null +++ b/vcl/unx/generic/app/i18n_ic.cxx @@ -0,0 +1,783 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <stdio.h> + +#include <sal/alloca.h> +#include <osl/thread.h> + +#include <tools/prex.h> +#include <X11/Xlocale.h> +#include <X11/Xlib.h> +#include <tools/postx.h> + +#include <unx/salunx.h> +#include <unx/XIM.h> +#include <unx/i18n_ic.hxx> +#include <unx/i18n_im.hxx> +#include <unx/i18n_status.hxx> + +#include <unx/salframe.h> +#include <unx/saldata.hxx> +#include <unx/saldisp.hxx> + +using namespace vcl; + +static void sendEmptyCommit( SalFrame* pFrame ) +{ + vcl::DeletionListener aDel( pFrame ); + + SalExtTextInputEvent aEmptyEv; + aEmptyEv.mnTime = 0; + aEmptyEv.mpTextAttr = 0; + aEmptyEv.maText = String(); + aEmptyEv.mnCursorPos = 0; + aEmptyEv.mnCursorFlags = 0; + aEmptyEv.mnDeltaStart = 0; + aEmptyEv.mbOnlyCursor = False; + pFrame->CallCallback( SALEVENT_EXTTEXTINPUT, (void*)&aEmptyEv ); + if( ! aDel.isDeleted() ) + pFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, NULL ); +} + +// --------------------------------------------------------------------------- +// +// Constructor / Destructor, the InputContext is bound to the SalFrame, as it +// needs the shell window as a focus window +// +// ---------------------------------------------------------------------------- + +SalI18N_InputContext::~SalI18N_InputContext() +{ + if ( maContext != NULL ) + XDestroyIC( maContext ); + if ( mpAttributes != NULL ) + XFree( mpAttributes ); + if ( mpStatusAttributes != NULL ) + XFree( mpStatusAttributes ); + if ( mpPreeditAttributes != NULL ) + XFree( mpPreeditAttributes ); + + if (maClientData.aText.pUnicodeBuffer != NULL) + free(maClientData.aText.pUnicodeBuffer); + if (maClientData.aText.pCharStyle != NULL) + free(maClientData.aText.pCharStyle); +} + +// ---------------------------------------------------------------------------- +// convenience routine to add items to a XVaNestedList +// ---------------------------------------------------------------------------- + +static XVaNestedList +XVaAddToNestedList( XVaNestedList a_srclist, char* name, XPointer value ) +{ + XVaNestedList a_dstlist; + + // if ( value == NULL ) + // return a_srclist; + + if ( a_srclist == NULL ) + { + a_dstlist = XVaCreateNestedList( + 0, + name, value, + NULL ); + } + else + { + a_dstlist = XVaCreateNestedList( + 0, + XNVaNestedList, a_srclist, + name, value, + NULL ); + } + + return a_dstlist != NULL ? a_dstlist : a_srclist ; +} + +// ---------------------------------------------------------------------------- +// convenience routine to create a fontset +// ---------------------------------------------------------------------------- + +static XFontSet +get_font_set( Display *p_display ) +{ + static XFontSet p_font_set = NULL; + + if (p_font_set == NULL) + { + char **pp_missing_list; + int n_missing_count; + char *p_default_string; + + p_font_set = XCreateFontSet(p_display, "-*", + &pp_missing_list, &n_missing_count, &p_default_string); + } + + return p_font_set; +} + +// --------------------------------------------------------------------------- +// +// Constructor for a InputContext (IC) +// +// ---------------------------------------------------------------------------- + +SalI18N_InputContext::SalI18N_InputContext ( SalFrame *pFrame ) : + mbUseable( True ), + maContext( (XIC)NULL ), + mnSupportedStatusStyle( + XIMStatusCallbacks | + XIMStatusNothing | + XIMStatusNone + ), + mnSupportedPreeditStyle( + XIMPreeditCallbacks | + XIMPreeditNothing | + XIMPreeditNone + ), + mnStatusStyle( 0 ), + mnPreeditStyle( 0 ), + mpAttributes( NULL ), + mpStatusAttributes( NULL ), + mpPreeditAttributes( NULL ) +{ +#ifdef SOLARIS + static const char* pIIIMPEnable = getenv( "SAL_DISABLE_OWN_IM_STATUS" ); + if( pIIIMPEnable && *pIIIMPEnable ) + mnSupportedStatusStyle &= ~XIMStatusCallbacks; +#endif + + maClientData.aText.pUnicodeBuffer = NULL; + maClientData.aText.pCharStyle = NULL; + maClientData.aInputEv.mnTime = 0; + maClientData.aInputEv.mpTextAttr = NULL; + maClientData.aInputEv.mnCursorPos = 0; + maClientData.aInputEv.mnDeltaStart = 0; + maClientData.aInputEv.mnCursorFlags = 0; + maClientData.aInputEv.mbOnlyCursor = sal_False; + + SalI18N_InputMethod *pInputMethod; + pInputMethod = GetX11SalData()->GetDisplay()->GetInputMethod(); + mbMultiLingual = pInputMethod->IsMultiLingual(); + + mnSupportedPreeditStyle = XIMPreeditCallbacks | XIMPreeditPosition + | XIMPreeditNothing | XIMPreeditNone; + if (pInputMethod->UseMethod() + && SupportInputMethodStyle( pInputMethod->GetSupportedStyles() ) ) + { + const SystemEnvData* pEnv = pFrame->GetSystemData(); + XLIB_Window aClientWindow = pEnv->aShellWindow; + XLIB_Window aFocusWindow = pEnv->aWindow; + + // for status callbacks and commit string callbacks +#define PREEDIT_BUFSZ 16 + maClientData.bIsMultilingual = mbMultiLingual; + maClientData.eState = ePreeditStatusStartPending; + maClientData.pFrame = pFrame; + maClientData.aText.pUnicodeBuffer = + (sal_Unicode*)malloc(PREEDIT_BUFSZ * sizeof(sal_Unicode)); + maClientData.aText.pCharStyle = + (XIMFeedback*)malloc(PREEDIT_BUFSZ * sizeof(XIMFeedback));; + maClientData.aText.nSize = PREEDIT_BUFSZ; + maClientData.aText.nCursorPos = 0; + maClientData.aText.nLength = 0; + + // + // Status attributes + // + + switch ( mnStatusStyle ) + { + case XIMStatusCallbacks: + { + static XIMCallback aStatusStartCallback; + static XIMCallback aStatusDoneCallback; + static XIMCallback aStatusDrawCallback; + + aStatusStartCallback.callback = (XIMProc)StatusStartCallback; + aStatusStartCallback.client_data = (XPointer)&maClientData; + aStatusDoneCallback.callback = (XIMProc)StatusDoneCallback; + aStatusDoneCallback.client_data = (XPointer)&maClientData; + aStatusDrawCallback.callback = (XIMProc)StatusDrawCallback; + aStatusDrawCallback.client_data = (XPointer)&maClientData; + + mpStatusAttributes = XVaCreateNestedList ( + 0, + XNStatusStartCallback, &aStatusStartCallback, + XNStatusDoneCallback, &aStatusDoneCallback, + XNStatusDrawCallback, &aStatusDrawCallback, + NULL ); + + break; + } + + case XIMStatusArea: + /* not supported */ + break; + + case XIMStatusNone: + case XIMStatusNothing: + default: + /* no arguments needed */ + break; + } + + // + // set preedit attributes + // + + switch ( mnPreeditStyle ) + { + case XIMPreeditCallbacks: + + maPreeditCaretCallback.callback = (XIMProc)PreeditCaretCallback; + maPreeditStartCallback.callback = (XIMProc)PreeditStartCallback; + maPreeditDoneCallback.callback = (XIMProc)PreeditDoneCallback; + maPreeditDrawCallback.callback = (XIMProc)PreeditDrawCallback; + maPreeditCaretCallback.client_data = (XPointer)&maClientData; + maPreeditStartCallback.client_data = (XPointer)&maClientData; + maPreeditDoneCallback.client_data = (XPointer)&maClientData; + maPreeditDrawCallback.client_data = (XPointer)&maClientData; + + mpPreeditAttributes = XVaCreateNestedList ( + 0, + XNPreeditStartCallback, &maPreeditStartCallback, + XNPreeditDoneCallback, &maPreeditDoneCallback, + XNPreeditDrawCallback, &maPreeditDrawCallback, + XNPreeditCaretCallback, &maPreeditCaretCallback, + NULL ); + + break; + + case XIMPreeditArea: + /* not supported */ + break; + + case XIMPreeditPosition: + { + // spot location + SalExtTextInputPosEvent aPosEvent; + pFrame->CallCallback(SALEVENT_EXTTEXTINPUTPOS, (void*)&aPosEvent); + + static XPoint aSpot; + aSpot.x = aPosEvent.mnX + aPosEvent.mnWidth; + aSpot.y = aPosEvent.mnY + aPosEvent.mnHeight; + + // create attributes for preedit position style + mpPreeditAttributes = XVaCreateNestedList ( + 0, + XNSpotLocation, &aSpot, + NULL ); + + // XCreateIC() fails on Redflag Linux 2.0 if there is no + // fontset though the data itself is not evaluated nor is + // it required according to the X specs. + Display* pDisplay = GetX11SalData()->GetDisplay()->GetDisplay(); + XFontSet pFontSet = get_font_set(pDisplay); + + if (pFontSet != NULL) + { + mpPreeditAttributes = XVaAddToNestedList( mpPreeditAttributes, + const_cast<char*>(XNFontSet), (XPointer)pFontSet); + } + + break; + } + + case XIMPreeditNone: + case XIMPreeditNothing: + default: + /* no arguments needed */ + break; + } + + // Create the InputContext by giving it exactly the information it + // deserves, because inappropriate attributes + // let XCreateIC fail on Solaris (eg. for C locale) + + mpAttributes = XVaCreateNestedList( + 0, + XNFocusWindow, aFocusWindow, + XNClientWindow, aClientWindow, + XNInputStyle, mnPreeditStyle | mnStatusStyle, + NULL ); + + if ( mnPreeditStyle != XIMPreeditNone ) + { +#if defined LINUX || defined FREEBSD || defined NETBSD || defined OPENBSD || defined DRAGONFLY + if ( mpPreeditAttributes != NULL ) +#endif + mpAttributes = XVaAddToNestedList( mpAttributes, + const_cast<char*>(XNPreeditAttributes), (XPointer)mpPreeditAttributes ); + } + if ( mnStatusStyle != XIMStatusNone ) + { +#if defined LINUX || defined FREEBSD || defined NETBSD || defined OPENBSD || defined DRAGONFLY + if ( mpStatusAttributes != NULL ) +#endif + mpAttributes = XVaAddToNestedList( mpAttributes, + const_cast<char*>(XNStatusAttributes), (XPointer)mpStatusAttributes ); + } + maContext = XCreateIC( pInputMethod->GetMethod(), + XNVaNestedList, mpAttributes, + NULL ); + } + + if ( maContext == NULL ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf(stderr, "input context creation failed\n"); +#endif + + mbUseable = False; + mbMultiLingual = False; + + if ( mpAttributes != NULL ) + XFree( mpAttributes ); + if ( mpStatusAttributes != NULL ) + XFree( mpStatusAttributes ); + if ( mpPreeditAttributes != NULL ) + XFree( mpPreeditAttributes ); + if ( maClientData.aText.pUnicodeBuffer != NULL ) + free ( maClientData.aText.pUnicodeBuffer ); + if ( maClientData.aText.pCharStyle != NULL ) + free ( maClientData.aText.pCharStyle ); + + mpAttributes = NULL; + mpStatusAttributes = NULL; + mpPreeditAttributes = NULL; + maClientData.aText.pUnicodeBuffer = NULL; + maClientData.aText.pCharStyle = NULL; + } + + if ( maContext != NULL && mbMultiLingual ) + { + maCommitStringCallback.callback = (XIMProc)::CommitStringCallback; + maCommitStringCallback.client_data = (XPointer)&maClientData; + maSwitchIMCallback.callback = (XIMProc)::SwitchIMCallback; + maSwitchIMCallback.client_data = (XPointer)&maClientData; + XSetICValues( maContext, + XNCommitStringCallback, &maCommitStringCallback, + XNSwitchIMNotifyCallback, &maSwitchIMCallback, + NULL ); + } + if ( maContext != NULL) + { + maDestroyCallback.callback = (XIMProc)IC_IMDestroyCallback; + maDestroyCallback.client_data = (XPointer)this; + XSetICValues( maContext, + XNDestroyCallback, &maDestroyCallback, + NULL ); + } + + if( mbMultiLingual ) + { + // set initial IM status + XIMUnicodeCharacterSubset* pSubset = NULL; + if( ! XGetICValues( maContext, + XNUnicodeCharacterSubset, & pSubset, + NULL ) + && pSubset ) + { + String aCurrent( ByteString( pSubset->name ), RTL_TEXTENCODING_UTF8 ); + ::vcl::I18NStatus::get().changeIM( aCurrent ); + ::vcl::I18NStatus::get().setStatusText( aCurrent ); + } + } +} + +// --------------------------------------------------------------------------- +// +// In Solaris 8 the status window does not unmap if the frame unmapps, so +// unmap it the hard way +// +// --------------------------------------------------------------------------- + +void +SalI18N_InputContext::Unmap( SalFrame* pFrame ) +{ + if ( maContext != NULL ) + { + I18NStatus& rStatus( I18NStatus::get() ); + if( rStatus.getParent() == pFrame ) + rStatus.show( false, I18NStatus::contextmap ); + + } + UnsetICFocus( pFrame ); + maClientData.pFrame = NULL; +} + +void +SalI18N_InputContext::Map( SalFrame *pFrame ) +{ + if( mbUseable ) + { + I18NStatus& rStatus(I18NStatus::get() ); + rStatus.setParent( pFrame ); + if( pFrame ) + { + rStatus.show( true, I18NStatus::contextmap ); + if ( maContext == NULL ) + { + SalI18N_InputMethod *pInputMethod; + pInputMethod = GetX11SalData()->GetDisplay()->GetInputMethod(); + + maContext = XCreateIC( pInputMethod->GetMethod(), + XNVaNestedList, mpAttributes, + NULL ); + if ( maContext != NULL && mbMultiLingual ) + XSetICValues( maContext, + XNCommitStringCallback, &maCommitStringCallback, + XNSwitchIMNotifyCallback, &maSwitchIMCallback, + NULL ); + } + if( maClientData.pFrame != pFrame ) + SetICFocus( pFrame ); + } + } +} + +// -------------------------------------------------------------------------- +// +// Handle DestroyCallbacks +// in fact this is a callback called from the XNDestroyCallback +// +// -------------------------------------------------------------------------- + +void +SalI18N_InputContext::HandleDestroyIM() +{ + maContext = 0; // noli me tangere + mbUseable = False; +} + +// --------------------------------------------------------------------------- +// +// make sure, the input method gets all the X-Events it needs, this is only +// called once on each frame, it relys on a valid maContext +// +// --------------------------------------------------------------------------- + +void +SalI18N_InputContext::ExtendEventMask( XLIB_Window aFocusWindow ) +{ + unsigned long nIMEventMask; + XWindowAttributes aWindowAttributes; + + if ( mbUseable ) + { + Display *pDisplay = XDisplayOfIM( XIMOfIC(maContext) ); + + XGetWindowAttributes( pDisplay, aFocusWindow, + &aWindowAttributes ); + XGetICValues ( maContext, + XNFilterEvents, &nIMEventMask, + NULL); + nIMEventMask |= aWindowAttributes.your_event_mask; + XSelectInput ( pDisplay, aFocusWindow, nIMEventMask ); + } +} + +// --------------------------------------------------------------------------- +// +// tune the styles provided by the input method with the supported one +// +// --------------------------------------------------------------------------- + +unsigned int +SalI18N_InputContext::GetWeightingOfIMStyle( XIMStyle nStyle ) const +{ + struct StyleWeightingT { + const XIMStyle nStyle; + const unsigned int nWeight; + }; + + StyleWeightingT const *pWeightPtr; + const StyleWeightingT pWeight[] = { + { XIMPreeditCallbacks, 0x10000000 }, + { XIMPreeditPosition, 0x02000000 }, + { XIMPreeditArea, 0x01000000 }, + { XIMPreeditNothing, 0x00100000 }, + { XIMPreeditNone, 0x00010000 }, + { XIMStatusCallbacks, 0x1000 }, + { XIMStatusArea, 0x0100 }, + { XIMStatusNothing, 0x0010 }, + { XIMStatusNone, 0x0001 }, + { 0, 0x0 } + }; + + int nWeight = 0; + for ( pWeightPtr = pWeight; pWeightPtr->nStyle != 0; pWeightPtr++ ) + { + if ( (pWeightPtr->nStyle & nStyle) != 0 ) + nWeight += pWeightPtr->nWeight; + } + return nWeight; +} + +Bool +SalI18N_InputContext::IsSupportedIMStyle( XIMStyle nStyle ) const +{ + if ( (nStyle & mnSupportedPreeditStyle) + && (nStyle & mnSupportedStatusStyle) ) + { + return True; + } + return False; +} + +Bool +SalI18N_InputContext::SupportInputMethodStyle( XIMStyles *pIMStyles ) +{ + mnPreeditStyle = 0; + mnStatusStyle = 0; + + if ( pIMStyles != NULL ) + { + int nBestScore = 0; + int nActualScore = 0; + + // check whether the XIM supports one of the desired styles + // only a single preedit and a single status style must occure + // in a inpuut method style. Hideki said so, so i trust him + for ( int nStyle = 0; nStyle < pIMStyles->count_styles; nStyle++ ) + { + XIMStyle nProvidedStyle = pIMStyles->supported_styles[ nStyle ]; + if ( IsSupportedIMStyle(nProvidedStyle) ) + { + nActualScore = GetWeightingOfIMStyle( nProvidedStyle ); + if ( nActualScore >= nBestScore ) + { + nBestScore = nActualScore; + mnPreeditStyle = nProvidedStyle & mnSupportedPreeditStyle; + mnStatusStyle = nProvidedStyle & mnSupportedStatusStyle; + } + } + } + } + +#if OSL_DEBUG_LEVEL > 1 + char pBuf[ 128 ]; + fprintf( stderr, "selected inputmethod style = %s\n", + GetMethodName(mnPreeditStyle | mnStatusStyle, pBuf, sizeof(pBuf)) ); +#endif + + return (mnPreeditStyle != 0) && (mnStatusStyle != 0) ; +} + +// --------------------------------------------------------------------------- +// +// handle extended and normal key input +// +// --------------------------------------------------------------------------- + +int +SalI18N_InputContext::CommitStringCallback (sal_Unicode* pText, sal_Size nLength) +{ + XIMUnicodeText call_data; + + call_data.string.utf16_char = pText; + call_data.length = nLength; + call_data.annotations = NULL; + call_data.count_annotations = 0; + call_data.feedback = NULL; + + return ::CommitStringCallback( maContext, + (XPointer)&maClientData, (XPointer)&call_data ); +} + +int +SalI18N_InputContext::CommitKeyEvent(sal_Unicode* pText, sal_Size nLength) +{ + if (nLength == 1 && IsControlCode(pText[0])) + return 0; + + if( maClientData.pFrame ) + { + SalExtTextInputEvent aTextEvent; + + aTextEvent.mnTime = 0; + aTextEvent.mpTextAttr = 0; + aTextEvent.mnCursorPos = nLength; + aTextEvent.maText = UniString(pText, nLength); + aTextEvent.mnCursorFlags = 0; + aTextEvent.mnDeltaStart = 0; + aTextEvent.mbOnlyCursor = False; + + maClientData.pFrame->CallCallback(SALEVENT_EXTTEXTINPUT, (void*)&aTextEvent); + maClientData.pFrame->CallCallback(SALEVENT_ENDEXTTEXTINPUT, (void*)NULL); + } +#if OSL_DEBUG_LEVEL > 1 + else + fprintf(stderr, "CommitKeyEvent without frame\n" ); +#endif + + return 0; +} + +int +SalI18N_InputContext::UpdateSpotLocation() +{ + if (maContext == 0 || maClientData.pFrame == NULL) + return -1; + + SalExtTextInputPosEvent aPosEvent; + maClientData.pFrame->CallCallback(SALEVENT_EXTTEXTINPUTPOS, (void*)&aPosEvent); + + XPoint aSpot; + aSpot.x = aPosEvent.mnX + aPosEvent.mnWidth; + aSpot.y = aPosEvent.mnY + aPosEvent.mnHeight; + + XVaNestedList preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &aSpot, NULL); + XSetICValues(maContext, XNPreeditAttributes, preedit_attr, NULL); + XFree(preedit_attr); + + I18NStatus::get().show( true, I18NStatus::contextmap ); + + return 0; +} + +// --------------------------------------------------------------------------- +// +// set and unset the focus for the Input Context +// the context may be NULL despite it is useable if the framewindow is +// in unmapped state +// +// --------------------------------------------------------------------------- + +void +SalI18N_InputContext::SetICFocus( SalFrame* pFocusFrame ) +{ + I18NStatus::get().setParent( pFocusFrame ); + if ( mbUseable && (maContext != NULL) ) + { + maClientData.pFrame = pFocusFrame; + + const SystemEnvData* pEnv = pFocusFrame->GetSystemData(); + XLIB_Window aClientWindow = pEnv->aShellWindow; + XLIB_Window aFocusWindow = pEnv->aWindow; + + XSetICValues( maContext, + XNFocusWindow, aFocusWindow, + XNClientWindow, aClientWindow, + NULL ); + + if( maClientData.aInputEv.mpTextAttr ) + { + sendEmptyCommit(pFocusFrame); + // begin preedit again + GetX11SalData()->GetDisplay()->SendInternalEvent( pFocusFrame, &maClientData.aInputEv, SALEVENT_EXTTEXTINPUT ); + } + + XSetICFocus( maContext ); + } +} + +void +SalI18N_InputContext::UnsetICFocus( SalFrame* pFrame ) +{ + I18NStatus& rStatus( I18NStatus::get() ); + if( rStatus.getParent() == pFrame ) + rStatus.setParent( NULL ); + + if ( mbUseable && (maContext != NULL) ) + { + // cancel an eventual event posted to begin preedit again + GetX11SalData()->GetDisplay()->CancelInternalEvent( maClientData.pFrame, &maClientData.aInputEv, SALEVENT_EXTTEXTINPUT ); + maClientData.pFrame = NULL; + XUnsetICFocus( maContext ); + } +} + +// --------------------------------------------------------------------------- +// +// multi byte input method only +// +// --------------------------------------------------------------------------- + +void +SalI18N_InputContext::SetPreeditState(Bool aPreeditState) +{ + XIMPreeditState preedit_state = XIMPreeditUnKnown; + XVaNestedList preedit_attr; + + preedit_attr = XVaCreateNestedList( + 0, + XNPreeditState, &preedit_state, + NULL); + if (!XGetICValues(maContext, XNPreeditAttributes, preedit_attr, NULL)) + { + XFree(preedit_attr); + + preedit_state = aPreeditState? XIMPreeditEnable : XIMPreeditDisable; + preedit_attr = XVaCreateNestedList( + 0, + XNPreeditState, preedit_state, + NULL); + XSetICValues(maContext, XNPreeditAttributes, preedit_attr, NULL); + } + + XFree(preedit_attr); + + return; +} + +void +SalI18N_InputContext::SetLanguage(LanguageType) +{ + // not yet implemented + return; +} + +void +SalI18N_InputContext::EndExtTextInput( sal_uInt16 /*nFlags*/ ) +{ + if ( mbUseable && (maContext != NULL) && maClientData.pFrame ) + { + vcl::DeletionListener aDel( maClientData.pFrame ); + // delete preedit in sal (commit an empty string) + sendEmptyCommit( maClientData.pFrame ); + if( ! aDel.isDeleted() ) + { + // mark previous preedit state again (will e.g. be sent at focus gain) + maClientData.aInputEv.mpTextAttr = &maClientData.aInputFlags[0]; + if( static_cast<X11SalFrame*>(maClientData.pFrame)->hasFocus() ) + { + // begin preedit again + GetX11SalData()->GetDisplay()->SendInternalEvent( maClientData.pFrame, &maClientData.aInputEv, SALEVENT_EXTTEXTINPUT ); + } + } + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/app/i18n_im.cxx b/vcl/unx/generic/app/i18n_im.cxx new file mode 100644 index 000000000000..5d30d74455ae --- /dev/null +++ b/vcl/unx/generic/app/i18n_im.cxx @@ -0,0 +1,621 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <stdio.h> +#include <string.h> + +#ifdef LINUX +# ifndef __USE_XOPEN +# define __USE_XOPEN +# endif +#endif +#include <poll.h> + +#include <tools/prex.h> +#include <X11/Xlocale.h> +#include <X11/Xlib.h> +#include <unx/XIM.h> +#include <tools/postx.h> + +#include "unx/salunx.h" +#include "unx/saldisp.hxx" +#include "unx/i18n_im.hxx" +#include "unx/i18n_status.hxx" + +#include <osl/thread.h> +#include <osl/process.h> + +using namespace vcl; +#include "unx/i18n_cb.hxx" +#if defined(SOLARIS) || defined(LINUX) +extern "C" char * XSetIMValues(XIM im, ...); +#endif + +// ------------------------------------------------------------------------------------ +// +// kinput2 IME needs special key handling since key release events are filtered in +// preeditmode and XmbResetIC does not work +// +// ------------------------------------------------------------------------------------ + +Bool +IMServerKinput2 () +{ + const static char* p_xmodifiers = getenv ("XMODIFIERS"); + const static Bool b_kinput2 = (p_xmodifiers != NULL) + && (strcmp(p_xmodifiers, "@im=kinput2") == 0); + + return b_kinput2; +} + +class XKeyEventOp : XKeyEvent +{ + private: + void init(); + + public: + XKeyEventOp(); + ~XKeyEventOp(); + + XKeyEventOp& operator= (const XKeyEvent &rEvent); + void erase (); + Bool match (const XKeyEvent &rEvent) const; +}; + +void +XKeyEventOp::init() +{ + type = 0; /* serial = 0; */ + send_event = 0; display = 0; + window = 0; root = 0; + subwindow = 0; /* time = 0; */ + /* x = 0; y = 0; */ + /* x_root = 0; y_root = 0; */ + state = 0; keycode = 0; + same_screen = 0; +} + +XKeyEventOp::XKeyEventOp() +{ + init(); +} + +XKeyEventOp::~XKeyEventOp() +{ +} + +XKeyEventOp& +XKeyEventOp::operator= (const XKeyEvent &rEvent) +{ + type = rEvent.type; /* serial = rEvent.serial; */ + send_event = rEvent.send_event; display = rEvent.display; + window = rEvent.window; root = rEvent.root; + subwindow = rEvent.subwindow;/* time = rEvent.time; */ + /* x = rEvent.x, y = rEvent.y; */ + /* x_root = rEvent.x_root, y_root = rEvent.y_root; */ + state = rEvent.state; keycode = rEvent.keycode; + same_screen = rEvent.same_screen; + + return *this; +} + +void +XKeyEventOp::erase () +{ + init(); +} + +Bool +XKeyEventOp::match (const XKeyEvent &rEvent) const +{ + return ( (type == XLIB_KeyPress && rEvent.type == KeyRelease) + || (type == KeyRelease && rEvent.type == XLIB_KeyPress )) + /* && serial == rEvent.serial */ + && send_event == rEvent.send_event + && display == rEvent.display + && window == rEvent.window + && root == rEvent.root + && subwindow == rEvent.subwindow + /* && time == rEvent.time + && x == rEvent.x + && y == rEvent.y + && x_root == rEvent.x_root + && y_root == rEvent.y_root */ + && state == rEvent.state + && keycode == rEvent.keycode + && same_screen == rEvent.same_screen; +} + +// ------------------------------------------------------------------------- +// +// locale handling +// +// ------------------------------------------------------------------------- + +// Locale handling of the operating system layer + +static char* +SetSystemLocale( const char* p_inlocale ) +{ + char *p_outlocale; + + if ( (p_outlocale = setlocale(LC_ALL, p_inlocale)) == NULL ) + { + fprintf( stderr, "I18N: Operating system doesn't support locale \"%s\"\n", + p_inlocale ); + } + + return p_outlocale; +} + +#ifdef SOLARIS +static void +SetSystemEnvironment( const rtl::OUString& rLocale ) +{ + rtl::OUString LC_ALL_Var(RTL_CONSTASCII_USTRINGPARAM("LC_ALL")); + osl_setEnvironment(LC_ALL_Var.pData, rLocale.pData); + + rtl::OUString LANG_Var(RTL_CONSTASCII_USTRINGPARAM("LANG")); + osl_setEnvironment(LANG_Var.pData, rLocale.pData); +} +#endif + +static Bool +IsPosixLocale( const char* p_locale ) +{ + if ( p_locale == NULL ) + return False; + if ( (p_locale[ 0 ] == 'C') && (p_locale[ 1 ] == '\0') ) + return True; + if ( strncmp(p_locale, "POSIX", sizeof("POSIX")) == 0 ) + return True; + + return False; +} + +// Locale handling of the X Window System layer + +static Bool +IsXWindowCompatibleLocale( const char* p_locale ) +{ + if ( p_locale == NULL ) + return False; + + if ( !XSupportsLocale() ) + { + fprintf (stderr, "I18N: X Window System doesn't support locale \"%s\"\n", + p_locale ); + return False; + } + return True; +} + +// Set the operating system locale prior to trying to open an +// XIM InputMethod. +// Handle the cases where the current locale is either not supported by the +// operating system (LANG=gaga) or by the XWindow system (LANG=aa_ER@saaho) +// by providing a fallback. +// Upgrade "C" or "POSIX" to "en_US" locale to allow umlauts and accents +// see i8988, i9188, i8930, i16318 +// on Solaris the environment needs to be set equivalent to the locale (#i37047#) + +Bool +SalI18N_InputMethod::SetLocale( const char* pLocale ) +{ + // check whether we want an Input Method engine, if we don't we + // do not need to set the locale + if ( mbUseable ) + { + char *locale = SetSystemLocale( pLocale ); + if ( (!IsXWindowCompatibleLocale(locale)) || IsPosixLocale(locale) ) + { + osl_setThreadTextEncoding (RTL_TEXTENCODING_ISO_8859_1); + locale = SetSystemLocale( "en_US" ); + #ifdef SOLARIS + SetSystemEnvironment( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("en_US")) ); + #endif + if (! IsXWindowCompatibleLocale(locale)) + { + locale = SetSystemLocale( "C" ); + #ifdef SOLARIS + SetSystemEnvironment( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("C")) ); + #endif + if (! IsXWindowCompatibleLocale(locale)) + mbUseable = False; + } + } + + // must not fail if mbUseable since XSupportsLocale() asserts success + if ( mbUseable && XSetLocaleModifiers("") == NULL ) + { + fprintf (stderr, "I18N: Can't set X modifiers for locale \"%s\"\n", + locale); + mbUseable = False; + } + } + + return mbUseable; +} + +Bool +SalI18N_InputMethod::PosixLocale() +{ + if (mbMultiLingual) + return False; + if (maMethod) + return IsPosixLocale (XLocaleOfIM (maMethod)); + return False; +} + +// ------------------------------------------------------------------------ +// +// Constructor / Destructor / Initialisation +// +// ------------------------------------------------------------------------ + +SalI18N_InputMethod::SalI18N_InputMethod( ) : mbUseable( bUseInputMethodDefault ), + mbMultiLingual( False ), + maMethod( (XIM)NULL ), + mpStyles( (XIMStyles*)NULL ) +{ + const char *pUseInputMethod = getenv( "SAL_USEINPUTMETHOD" ); + if ( pUseInputMethod != NULL ) + mbUseable = pUseInputMethod[0] != '\0' ; +} + +SalI18N_InputMethod::~SalI18N_InputMethod() +{ + ::vcl::I18NStatus::free(); + if ( mpStyles != NULL ) + XFree( mpStyles ); + if ( maMethod != NULL ) + XCloseIM ( maMethod ); +} + +// +// XXX +// debug routine: lets have a look at the provided method styles +// + +#if OSL_DEBUG_LEVEL > 1 + +extern "C" char* +GetMethodName( XIMStyle nStyle, char *pBuf, int nBufSize) +{ + struct StyleName { + const XIMStyle nStyle; + const char *pName; + const int nNameLen; + }; + + StyleName *pDescPtr; + static const StyleName pDescription[] = { + { XIMPreeditArea, "PreeditArea ", sizeof("PreeditArea ") }, + { XIMPreeditCallbacks, "PreeditCallbacks ",sizeof("PreeditCallbacks ")}, + { XIMPreeditPosition, "PreeditPosition ", sizeof("PreeditPosition ") }, + { XIMPreeditNothing, "PreeditNothing ", sizeof("PreeditNothing ") }, + { XIMPreeditNone, "PreeditNone ", sizeof("PreeditNone ") }, + { XIMStatusArea, "StatusArea ", sizeof("StatusArea ") }, + { XIMStatusCallbacks, "StatusCallbacks ", sizeof("StatusCallbacks ") }, + { XIMStatusNothing, "StatusNothing ", sizeof("StatusNothing ") }, + { XIMStatusNone, "StatusNone ", sizeof("StatusNone ") }, + { 0, "NULL", 0 } + }; + + if ( nBufSize > 0 ) + pBuf[0] = '\0'; + + char *pBufPtr = pBuf; + for ( pDescPtr = const_cast<StyleName*>(pDescription); pDescPtr->nStyle != 0; pDescPtr++ ) + { + int nSize = pDescPtr->nNameLen - 1; + if ( (nStyle & pDescPtr->nStyle) && (nBufSize > nSize) ) + { + strncpy( pBufPtr, pDescPtr->pName, nSize + 1); + pBufPtr += nSize; + nBufSize -= nSize; + } + } + + return pBuf; +} + +extern "C" void +PrintInputStyle( XIMStyles *pStyle ) +{ + char pBuf[ 128 ]; + int nBuf = sizeof( pBuf ); + + if ( pStyle == NULL ) + fprintf( stderr, "no input method styles\n"); + else + for ( int nStyle = 0; nStyle < pStyle->count_styles; nStyle++ ) + { + fprintf( stderr, "style #%i = %s\n", nStyle, + GetMethodName(pStyle->supported_styles[nStyle], pBuf, nBuf) ); + } +} + +#endif + +// +// this is the real constructing routine, since locale setting has to be done +// prior to xopendisplay, the xopenim call has to be delayed +// + +Bool +SalI18N_InputMethod::CreateMethod ( Display *pDisplay ) +{ + if ( mbUseable ) + { + const bool bTryMultiLingual = + #ifdef LINUX + false; + #else + true; + #endif + if ( bTryMultiLingual && getenv("USE_XOPENIM") == NULL ) + { + mbMultiLingual = True; // set ml-input flag to create input-method + maMethod = XvaOpenIM(pDisplay, NULL, NULL, NULL, + XNMultiLingualInput, mbMultiLingual, /* dummy */ + (void *)0); + // get ml-input flag from input-method + if ( maMethod == (XIM)NULL ) + mbMultiLingual = False; + else + if ( XGetIMValues(maMethod, + XNMultiLingualInput, &mbMultiLingual, NULL ) != NULL ) + mbMultiLingual = False; + if( mbMultiLingual ) + { + XIMUnicodeCharacterSubsets* subsets; + if( XGetIMValues( maMethod, + XNQueryUnicodeCharacterSubset, &subsets, NULL ) == NULL ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "IM reports %d subsets: ", subsets->count_subsets ); +#endif + I18NStatus& rStatus( I18NStatus::get() ); + rStatus.clearChoices(); + for( int i = 0; i < subsets->count_subsets; i++ ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr,"\"%s\" ", subsets->supported_subsets[i].name ); +#endif + rStatus.addChoice( String( subsets->supported_subsets[i].name, RTL_TEXTENCODING_UTF8 ), &subsets->supported_subsets[i] ); + } +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "\n" ); +#endif + } +#if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "query subsets failed\n" ); +#endif + } + } + else + { + maMethod = XOpenIM(pDisplay, NULL, NULL, NULL); + mbMultiLingual = False; + } + + if ((maMethod == (XIM)NULL) && (getenv("XMODIFIERS") != NULL)) + { + rtl::OUString envVar(RTL_CONSTASCII_USTRINGPARAM("XMODIFIERS")); + osl_clearEnvironment(envVar.pData); + XSetLocaleModifiers(""); + maMethod = XOpenIM(pDisplay, NULL, NULL, NULL); + mbMultiLingual = False; + } + + if ( maMethod != (XIM)NULL ) + { + if ( XGetIMValues(maMethod, XNQueryInputStyle, &mpStyles, NULL) + != NULL) + mbUseable = False; + #if OSL_DEBUG_LEVEL > 1 + fprintf(stderr, "Creating %s-Lingual InputMethod\n", + mbMultiLingual ? "Multi" : "Mono" ); + PrintInputStyle( mpStyles ); + #endif + } + else + { + mbUseable = False; + } + } + + #if OSL_DEBUG_LEVEL > 1 + if ( !mbUseable ) + fprintf(stderr, "input method creation failed\n"); + #endif + + maDestroyCallback.callback = (XIMProc)IM_IMDestroyCallback; + maDestroyCallback.client_data = (XPointer)this; + if (mbUseable && maMethod != NULL) + XSetIMValues(maMethod, XNDestroyCallback, &maDestroyCallback, NULL); + + return mbUseable; +} + +// +// give IM the opportunity to look at the event, and possibly hide it +// + +Bool +SalI18N_InputMethod::FilterEvent( XEvent *pEvent, XLIB_Window window ) +{ + if (!mbUseable) + return False; + + Bool bFilterEvent = XFilterEvent (pEvent, window); + + if (pEvent->type != XLIB_KeyPress && pEvent->type != KeyRelease) + return bFilterEvent; + + /* + * fix broken key release handling of some IMs + */ + XKeyEvent* pKeyEvent = &(pEvent->xkey); + static XKeyEventOp maLastKeyPress; + + if (bFilterEvent) + { + if (pKeyEvent->type == KeyRelease) + bFilterEvent = !maLastKeyPress.match (*pKeyEvent); + maLastKeyPress.erase(); + } + else /* (!bFilterEvent) */ + { + if (pKeyEvent->type == XLIB_KeyPress) + maLastKeyPress = *pKeyEvent; + else + maLastKeyPress.erase(); + } + + return bFilterEvent; +} + +void +SalI18N_InputMethod::HandleDestroyIM() +{ + mbUseable = False; + mbMultiLingual = False; + maMethod = NULL; +} + +// ------------------------------------------------------------------------ +// +// add a connection watch into the SalXLib yieldTable to allow iiimp +// connection processing: soffice waits in select() not in XNextEvent(), so +// there may be requests pending on the iiimp internal connection that will +// not be processed until XNextEvent is called the next time. If we do not +// have the focus because the atok12 lookup choice aux window has it we stay +// deaf and dump otherwise. +// +// ------------------------------------------------------------------------ + +int +InputMethod_HasPendingEvent(int nFileDescriptor, void *pData) +{ + if (pData == NULL) + return 0; + + struct pollfd aFileDescriptor; + #ifdef SOLARIS + nfds_t nNumDescriptor = 1; + #else + unsigned int nNumDescriptor = 1; + #endif + aFileDescriptor.fd = nFileDescriptor; + aFileDescriptor.events = POLLRDNORM; + aFileDescriptor.revents = 0; + + int nPoll = poll (&aFileDescriptor, nNumDescriptor, 0 /* timeout */ ); + + if (nPoll > 0) + { + /* at least some conditions in revent are set */ + if ( (aFileDescriptor.revents & POLLHUP) + || (aFileDescriptor.revents & POLLERR) + || (aFileDescriptor.revents & POLLNVAL)) + return 0; /* oops error condition set */ + + if (aFileDescriptor.revents & POLLRDNORM) + return 1; /* success */ + } + + /* nPoll == 0 means timeout, nPoll < 0 means error */ + return 0; +} + +int +InputMethod_IsEventQueued(int nFileDescriptor, void *pData) +{ + return InputMethod_HasPendingEvent (nFileDescriptor, pData); +} + +int +InputMethod_HandleNextEvent(int nFileDescriptor, void *pData) +{ + if (pData != NULL) + XProcessInternalConnection((Display*)pData, nFileDescriptor); + + return 0; +} + +extern "C" void +InputMethod_ConnectionWatchProc (Display *pDisplay, XPointer pClientData, + int nFileDescriptor, Bool bOpening, XPointer*) +{ + SalXLib *pConnectionHandler = (SalXLib*)pClientData; + + if (pConnectionHandler == NULL) + return; + + if (bOpening) + { + pConnectionHandler->Insert (nFileDescriptor, pDisplay, + InputMethod_HasPendingEvent, + InputMethod_IsEventQueued, + InputMethod_HandleNextEvent); + } + else + { + pConnectionHandler->Remove (nFileDescriptor); + } +} + +Bool +SalI18N_InputMethod::AddConnectionWatch(Display *pDisplay, void *pConnectionHandler) +{ + // sanity check + if (pDisplay == NULL || pConnectionHandler == NULL) + return False; + + // if we are not ml all the extended text input comes on the stock X queue, + // so there is no need to monitor additional file descriptors. +#ifndef SOLARIS + if (!mbMultiLingual || !mbUseable) + return False; +#endif + + // pConnectionHandler must be really a pointer to a SalXLib + Status nStatus = XAddConnectionWatch (pDisplay, InputMethod_ConnectionWatchProc, + (XPointer)pConnectionHandler); + return (Bool)nStatus; +} + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/app/i18n_keysym.cxx b/vcl/unx/generic/app/i18n_keysym.cxx new file mode 100644 index 000000000000..2e2005f1a874 --- /dev/null +++ b/vcl/unx/generic/app/i18n_keysym.cxx @@ -0,0 +1,368 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <X11/X.h> +#include <sal/types.h> + +#include <unx/i18n_keysym.hxx> + +// convert keysyms to unicode +// for all keysyms with byte1 and byte2 equal zero, and of course only for +// keysyms that have a unicode counterpart + +typedef const sal_Unicode unicode_t; +typedef struct { + const int first; const int last; + unicode_t *map; +} keymap_t; + +// Latin-1 Byte 3 = 0x00 +unicode_t keymap00_map[] = { + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, + 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, + 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, + 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, + 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, + 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, + 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, + 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, + 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, + 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, + 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, + 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff }; +const keymap_t keymap00 = { 32, 255, keymap00_map }; + +// Latin-2 Byte 3 = 0x01 +unicode_t keymap01_map[] = { + 0x0104, 0x02d8, 0x0141, 0x0000, 0x013d, 0x015a, 0x0000, 0x0000, + 0x0160, 0x015e, 0x0164, 0x0179, 0x0000, 0x017d, 0x017b, 0x0000, + 0x0105, 0x02db, 0x0142, 0x0000, 0x013e, 0x015b, 0x02c7, 0x0000, + 0x0161, 0x015f, 0x0165, 0x017a, 0x02dd, 0x017e, 0x017c, 0x0154, + 0x0000, 0x0000, 0x0102, 0x0000, 0x0139, 0x0106, 0x0000, 0x010c, + 0x0000, 0x0118, 0x0000, 0x011a, 0x0000, 0x0000, 0x010e, 0x0110, + 0x0143, 0x0147, 0x0000, 0x0000, 0x0150, 0x0000, 0x0000, 0x0158, + 0x016e, 0x0000, 0x0170, 0x0000, 0x0000, 0x0162, 0x0000, 0x0155, + 0x0000, 0x0000, 0x0103, 0x0000, 0x013a, 0x0107, 0x0000, 0x010d, + 0x0000, 0x0119, 0x0000, 0x011b, 0x0000, 0x0000, 0x010f, 0x0111, + 0x0144, 0x0148, 0x0000, 0x0000, 0x0151, 0x0000, 0x0000, 0x0159, + 0x016f, 0x0000, 0x0171, 0x0000, 0x0000, 0x0163, 0x02d9 }; +const keymap_t keymap01 = { 161, 255, keymap01_map }; + +// Latin-3 Byte 3 = 0x02 +unicode_t keymap02_map[] = { + 0x0126, 0x0000, 0x0000, 0x0000, 0x0000, 0x0124, 0x0000, 0x0000, + 0x0130, 0x0000, 0x011e, 0x0134, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0127, 0x0000, 0x0000, 0x0000, 0x0000, 0x0125, 0x0000, 0x0000, + 0x0131, 0x0000, 0x011f, 0x0135, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x010a, 0x0108, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0120, 0x0000, 0x0000, 0x011c, + 0x0000, 0x0000, 0x0000, 0x0000, 0x016c, 0x015c, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x010b, 0x0109, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0121, 0x0000, 0x0000, 0x011d, + 0x0000, 0x0000, 0x0000, 0x0000, 0x016d, 0x015d }; +const keymap_t keymap02 = { 161, 254, keymap02_map }; + +// Latin-4 Byte 3 = 0x03 +unicode_t keymap03_map[] = { + 0x0138, 0x0156, 0x0000, 0x0128, 0x013b, 0x0000, 0x0000, 0x0000, + 0x0112, 0x0122, 0x0166, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0157, 0x0000, 0x0129, 0x013c, 0x0000, 0x0000, 0x0000, + 0x0113, 0x0123, 0x0167, 0x014a, 0x0000, 0x014b, 0x0100, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x012e, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0116, 0x0000, 0x0000, 0x012a, 0x0000, 0x0145, + 0x014c, 0x0136, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0172, + 0x0000, 0x0000, 0x0000, 0x0168, 0x016a, 0x0000, 0x0101, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x012f, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0117, 0x0000, 0x0000, 0x012b, 0x0000, 0x0146, + 0x014d, 0x0137, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0173, + 0x0000, 0x0000, 0x0000, 0x0169, 0x016b }; +const keymap_t keymap03 = { 162, 254, keymap03_map }; + +// Kana Byte 3 = 0x04 +unicode_t keymap04_map[] = { + 0x203e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x3002, 0x300c, 0x300d, 0x3001, 0x30fb, + 0x30f2, 0x30a1, 0x30a3, 0x30a5, 0x30a7, 0x30a9, 0x30e3, 0x30e5, + 0x30e7, 0x30c3, 0x30fc, 0x30a2, 0x30a4, 0x30a6, 0x30a8, 0x30aa, + 0x30ab, 0x30ad, 0x30af, 0x30b1, 0x30b3, 0x30b5, 0x30b7, 0x30b9, + 0x30bb, 0x30bd, 0x30bf, 0x30c1, 0x30c4, 0x30c6, 0x30c8, 0x30ca, + 0x30cb, 0x30cc, 0x30cd, 0x30ce, 0x30cf, 0x30d2, 0x30d5, 0x30d8, + 0x30db, 0x30de, 0x30df, 0x30e0, 0x30e1, 0x30e2, 0x30e4, 0x30e6, + 0x30e8, 0x30e9, 0x30ea, 0x30eb, 0x30ec, 0x30ed, 0x30ef, 0x30f3, + 0x309b, 0x309c }; +const keymap_t keymap04 = { 126, 223, keymap04_map }; + +// Arabic Byte 3 = 0x05 +unicode_t keymap05_map[] = { + 0x060c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x061b, + 0x0000, 0x0000, 0x0000, 0x061f, 0x0000, 0x0621, 0x0622, 0x0623, + 0x0624, 0x0625, 0x0626, 0x0627, 0x0628, 0x0629, 0x062a, 0x062b, + 0x062c, 0x062d, 0x062e, 0x062f, 0x0630, 0x0631, 0x0632, 0x0633, + 0x0634, 0x0635, 0x0636, 0x0637, 0x0638, 0x0639, 0x063a, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0640, 0x0641, 0x0642, 0x0643, + 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064a, 0x064b, + 0x064c, 0x064d, 0x064e, 0x064f, 0x0650, 0x0651, 0x0652 }; +const keymap_t keymap05 = { 172, 242, keymap05_map }; + +// Cyrillic Byte 3 = 0x06 +unicode_t keymap06_map[] = { + 0x0452, 0x0453, 0x0451, 0x0454, 0x0455, 0x0456, 0x0457, 0x0458, + 0x0459, 0x045a, 0x045b, 0x045c, 0x0000, 0x045e, 0x045f, 0x2116, + 0x0402, 0x0403, 0x0401, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, + 0x0409, 0x040a, 0x040b, 0x040c, 0x0000, 0x040e, 0x040f, 0x044e, + 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, + 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f, + 0x044f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, 0x044c, + 0x044b, 0x0437, 0x0448, 0x044d, 0x0449, 0x0447, 0x044a, 0x042e, + 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, + 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, + 0x042f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, 0x042c, + 0x042b, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427, 0x042a }; +const keymap_t keymap06 = { 161, 255, keymap06_map }; + +// Greek Byte 3 = 0x07 +unicode_t keymap07_map[] = { + 0x0386, 0x0388, 0x0389, 0x038a, 0x03aa, 0x0000, 0x038c, 0x038e, + 0x03ab, 0x0000, 0x038f, 0x0000, 0x0000, 0x0385, 0x2015, 0x0000, + 0x03ac, 0x03ad, 0x03ae, 0x03af, 0x03ca, 0x0390, 0x03cc, 0x03cd, + 0x03cb, 0x03b0, 0x03ce, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, + 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, 0x03a0, + 0x03a1, 0x03a3, 0x0000, 0x03a4, 0x03a5, 0x03a6, 0x03a7, 0x03a8, + 0x03a9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, 0x03b8, + 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, 0x03c0, + 0x03c1, 0x03c3, 0x03c2, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8, + 0x03c9 }; +const keymap_t keymap07 = { 161, 249, keymap07_map }; + +// Technical Byte 3 = 0x08 +unicode_t keymap08_map[] = { + 0x23b7, 0x250c, 0x2500, 0x2320, 0x2321, 0x2502, 0x23a1, 0x23a3, + 0x23a4, 0x23a6, 0x239b, 0x239d, 0x239e, 0x23a0, 0x23a8, 0x23ac, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x2264, 0x2260, 0x2265, 0x222b, 0x2234, + 0x221d, 0x221e, 0x0000, 0x0000, 0x2207, 0x0000, 0x0000, 0x223c, + 0x2243, 0x0000, 0x0000, 0x0000, 0x21d4, 0x21d2, 0x2261, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x221a, 0x0000, 0x0000, + 0x0000, 0x2282, 0x2283, 0x2229, 0x222a, 0x2227, 0x2228, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2202, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0192, 0x0000, 0x0000, + 0x0000, 0x0000, 0x2190, 0x2191, 0x2192, 0x2193 }; +const keymap_t keymap08 = { 161, 254, keymap08_map }; + +// Special Byte 3 = 0x09 +unicode_t keymap09_map[] = { + 0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x0000, 0x0000, + 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba, + 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c, + 0x2502 }; +const keymap_t keymap09 = { 224, 248, keymap09_map }; + +// Publishing Byte 3 = 0x0a = 10 +unicode_t keymap10_map[] = { + 0x2003, 0x2002, 0x2004, 0x2005, 0x2007, 0x2008, 0x2009, 0x200a, + 0x2014, 0x2013, 0x0000, 0x0000, 0x0000, 0x2026, 0x2025, 0x2153, + 0x2154, 0x2155, 0x2156, 0x2157, 0x2158, 0x2159, 0x215a, 0x2105, + 0x0000, 0x0000, 0x2012, 0x2329, 0x0000, 0x232a, 0x0000, 0x0000, + 0x0000, 0x0000, 0x215b, 0x215c, 0x215d, 0x215e, 0x0000, 0x0000, + 0x2122, 0x2613, 0x0000, 0x25c1, 0x25b7, 0x25cb, 0x25af, 0x2018, + 0x2019, 0x201c, 0x201d, 0x211e, 0x0000, 0x2032, 0x2033, 0x0000, + 0x271d, 0x0000, 0x25ac, 0x25c0, 0x25b6, 0x25cf, 0x25ae, 0x25e6, + 0x25ab, 0x25ad, 0x25b3, 0x25bd, 0x2606, 0x2022, 0x25aa, 0x25b2, + 0x25bc, 0x261c, 0x261e, 0x2663, 0x2666, 0x2665, 0x0000, 0x2720, + 0x2020, 0x2021, 0x2713, 0x2717, 0x266f, 0x266d, 0x2642, 0x2640, + 0x260e, 0x2315, 0x2117, 0x2038, 0x201a, 0x201e }; +const keymap_t keymap10 = { 161, 254, keymap10_map }; + +// APL Byte 3 = 0x0b = 11 +unicode_t keymap11_map[] = { + 0x003c, 0x0000, 0x0000, 0x003e, 0x0000, 0x2228, 0x2227, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00af, 0x0000, 0x22a5, + 0x2229, 0x230a, 0x0000, 0x005f, 0x0000, 0x0000, 0x0000, 0x2218, + 0x0000, 0x2395, 0x0000, 0x22a4, 0x25cb, 0x0000, 0x0000, 0x0000, + 0x2308, 0x0000, 0x0000, 0x222a, 0x0000, 0x2283, 0x0000, 0x2282, + 0x0000, 0x22a2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x22a3 }; +const keymap_t keymap11 = { 163, 252, keymap11_map }; + +// Hebrew Byte 3 = 0x0c = 12 +unicode_t keymap12_map[] = { + 0x2017, 0x05d0, 0x05d1, 0x05d2, 0x05d3, 0x05d4, 0x05d5, 0x05d6, + 0x05d7, 0x05d8, 0x05d9, 0x05da, 0x05db, 0x05dc, 0x05dd, 0x05de, + 0x05df, 0x05e0, 0x05e1, 0x05e2, 0x05e3, 0x05e4, 0x05e5, 0x05e6, + 0x05e7, 0x05e8, 0x05e9, 0x05ea }; +const keymap_t keymap12 = { 223, 250, keymap12_map }; + +// Thai Byte 3 = 0x0d = 13 +unicode_t keymap13_map[] = { + 0x0e01, 0x0e02, 0x0e03, 0x0e04, 0x0e05, 0x0e06, 0x0e07, 0x0e08, + 0x0e09, 0x0e0a, 0x0e0b, 0x0e0c, 0x0e0d, 0x0e0e, 0x0e0f, 0x0e10, + 0x0e11, 0x0e12, 0x0e13, 0x0e14, 0x0e15, 0x0e16, 0x0e17, 0x0e18, + 0x0e19, 0x0e1a, 0x0e1b, 0x0e1c, 0x0e1d, 0x0e1e, 0x0e1f, 0x0e20, + 0x0e21, 0x0e22, 0x0e23, 0x0e24, 0x0e25, 0x0e26, 0x0e27, 0x0e28, + 0x0e29, 0x0e2a, 0x0e2b, 0x0e2c, 0x0e2d, 0x0e2e, 0x0e2f, 0x0e30, + 0x0e31, 0x0e32, 0x0e33, 0x0e34, 0x0e35, 0x0e36, 0x0e37, 0x0e38, + 0x0e39, 0x0e3a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0e3f, 0x0e40, + 0x0e41, 0x0e42, 0x0e43, 0x0e44, 0x0e45, 0x0e46, 0x0e47, 0x0e48, + 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c, 0x0e4d, 0x0000, 0x0000, 0x0e50, + 0x0e51, 0x0e52, 0x0e53, 0x0e54, 0x0e55, 0x0e56, 0x0e57, 0x0e58, + 0x0e59 }; +const keymap_t keymap13 = { 161, 249, keymap13_map }; + +// Korean Byte 3 = 0x0e = 14 +unicode_t keymap14_map[] = { + 0x3131, 0x3132, 0x3133, 0x3134, 0x3135, 0x3136, 0x3137, 0x3138, + 0x3139, 0x313a, 0x313b, 0x313c, 0x313d, 0x313e, 0x313f, 0x3140, + 0x3141, 0x3142, 0x3143, 0x3144, 0x3145, 0x3146, 0x3147, 0x3148, + 0x3149, 0x314a, 0x314b, 0x314c, 0x314d, 0x314e, 0x314f, 0x3150, + 0x3151, 0x3152, 0x3153, 0x3154, 0x3155, 0x3156, 0x3157, 0x3158, + 0x3159, 0x315a, 0x315b, 0x315c, 0x315d, 0x315e, 0x315f, 0x3160, + 0x3161, 0x3162, 0x3163, 0x11a8, 0x11a9, 0x11aa, 0x11ab, 0x11ac, + 0x11ad, 0x11ae, 0x11af, 0x11b0, 0x11b1, 0x11b2, 0x11b3, 0x11b4, + 0x11b5, 0x11b6, 0x11b7, 0x11b8, 0x11b9, 0x11ba, 0x11bb, 0x11bc, + 0x11bd, 0x11be, 0x11bf, 0x11c0, 0x11c1, 0x11c2, 0x316d, 0x3171, + 0x3178, 0x317f, 0x3181, 0x3184, 0x3186, 0x318d, 0x318e, 0x11eb, + 0x11f0, 0x11f9, 0x0000, 0x0000, 0x0000, 0x0000, 0x20a9 }; +const keymap_t keymap14 = { 161, 255, keymap14_map }; + +// missing: +// Latin-8 Byte 3 = 0x12 = 18 + +// Latin-9 Byte 3 = 0x13 = 19 +unicode_t keymap19_map[] = { + 0x0152, 0x0153, 0x0178 }; +const keymap_t keymap19 = { 188, 190, keymap19_map }; + +// missing: +// Armenian Byte 3 = 0x14 = 20 +// Georgian Byte 3 = 0x15 = 21 +// Azeri Byte 3 = 0x16 = 22 +// Vietnamese Byte 3 = 0x1e = 30 + +// Currency Byte 3 = 0x20 = 32 +unicode_t keymap32_map[] = { + 0x20a0, 0x20a1, 0x20a2, 0x20a3, 0x20a4, 0x20a5, 0x20a6, 0x20a7, + 0x20a8, 0x0000, 0x20aa, 0x20ab, 0x20ac }; +const keymap_t keymap32 = { 160, 172, keymap32_map }; + +// Keyboard (Keypad mappings) Byte 3 = 0xff = 255 +unicode_t keymap255_map[] = { + 0x0020, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x0000, 0x0000, 0x0000, 0x003d }; +const keymap_t keymap255 = { 128, 189, keymap255_map }; + +#define INITIAL_KEYMAPS 33 +const keymap_t* p_keymap[INITIAL_KEYMAPS] = { + &keymap00, &keymap01, &keymap02, &keymap03, /* 00 -- 03 */ + &keymap04, &keymap05, &keymap06, &keymap07, /* 04 -- 07 */ + &keymap08, &keymap09, &keymap10, &keymap11, /* 08 -- 11 */ + &keymap12, &keymap13, &keymap14, (keymap_t*)NULL, /* 12 -- 15 */ + (keymap_t*)NULL, (keymap_t*)NULL, (keymap_t*)NULL, &keymap19, /* 16 -- 19 */ + (keymap_t*)NULL, (keymap_t*)NULL, (keymap_t*)NULL, (keymap_t*)NULL, /* 20 -- 23 */ + (keymap_t*)NULL, (keymap_t*)NULL, (keymap_t*)NULL, (keymap_t*)NULL, /* 24 -- 27 */ + (keymap_t*)NULL, (keymap_t*)NULL, (keymap_t*)NULL, (keymap_t*)NULL, /* 28 -- 31 */ + &keymap32 /* 32 */ +}; + +sal_Unicode +KeysymToUnicode (KeySym nKeySym) +{ + // keysym is already unicode + if ((nKeySym & 0xff000000) == 0x01000000) + { + // strip off group indicator and iso10646 plane + // FIXME can't handle chars from surrogate area. + if (! (nKeySym & 0x00ff0000) ) + return (sal_Unicode)(nKeySym & 0x0000ffff); + } + // legacy keysyms, switch to appropriate codeset + else + { + unsigned char n_byte1 = (nKeySym & 0xff000000) >> 24; + unsigned char n_byte2 = (nKeySym & 0x00ff0000) >> 16; + unsigned char n_byte3 = (nKeySym & 0x0000ff00) >> 8; + unsigned char n_byte4 = (nKeySym & 0x000000ff); + + if (n_byte1 != 0) + return 0; + if (n_byte2 != 0) + return 0; + + keymap_t const* p_map = NULL; + if (n_byte3 < INITIAL_KEYMAPS) + p_map = p_keymap[n_byte3]; + else + if (n_byte3 == 255) + p_map = &keymap255; + + if ((p_map != NULL) && (n_byte4 >= p_map->first) && (n_byte4 <= p_map->last) ) + return p_map->map[n_byte4 - p_map->first]; + } + + return 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/app/i18n_status.cxx b/vcl/unx/generic/app/i18n_status.cxx new file mode 100644 index 000000000000..8cae9d12eedd --- /dev/null +++ b/vcl/unx/generic/app/i18n_status.cxx @@ -0,0 +1,735 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#if OSL_DEBUG_LEVEL > 1 +#include <stdio.h> +#endif +#include <sal/alloca.h> + +#include <tools/prex.h> +#include <X11/Xlib.h> +#include <unx/XIM.h> +#include <tools/postx.h> + +#include <unx/salunx.h> +#include <unx/i18n_status.hxx> +#include <unx/i18n_ic.hxx> +#include <unx/saldisp.hxx> +#include <unx/salframe.h> +#include <unx/saldata.hxx> + +#include <vcl/wrkwin.hxx> +#include <vcl/fixed.hxx> +#include <vcl/menubtn.hxx> +#include <vcl/menu.hxx> +#include <vcl/svapp.hxx> +#include <vcl/sysdata.hxx> + +#include <svdata.hxx> + +using namespace vcl; + +namespace vcl { + +class StatusWindow : public WorkWindow +{ +protected: + StatusWindow( WinBits nWinBits ); +public: + virtual ~StatusWindow(); + + virtual void setPosition( SalFrame* ); + virtual void setText( const String & ) = 0; + virtual String getText() const = 0; + virtual void show( bool bShow, I18NStatus::ShowReason eReason ) = 0; + virtual void toggle( bool bOn ) = 0; +}; + +} + +StatusWindow::StatusWindow( WinBits nWinBits ) : + WorkWindow( NULL, nWinBits ) +{ +} + +StatusWindow::~StatusWindow() {} + +void StatusWindow::setPosition( SalFrame* ) +{ +} + +// -------------------------------------------------------------------------- + +namespace vcl { + +class XIMStatusWindow : public StatusWindow +{ + FixedText m_aStatusText; + SalFrame* m_pLastParent; + Size m_aWindowSize; + bool m_bAnchoredAtRight; + // true if the right edge (instead of the left edge) should stay at a + // fixed position when re-sizing the window + + // for delayed showing + bool m_bDelayedShow; + I18NStatus::ShowReason m_eDelayedReason; + sal_uLong m_nDelayedEvent; + // for toggling + bool m_bOn; + + Point updatePosition(); + void layout(); + bool checkLastParent() const; + + DECL_LINK( DelayedShowHdl, void* ); +public: + XIMStatusWindow( bool bOn ); + virtual ~XIMStatusWindow(); + + virtual void setPosition( SalFrame* ); + virtual void setText( const String & ); + virtual String getText() const; + virtual void show( bool bShow, I18NStatus::ShowReason eReason ); + virtual void toggle( bool bOn ); + + // overload WorkWindow::DataChanged + virtual void DataChanged( const DataChangedEvent& rEvt ); +}; + +} + +XIMStatusWindow::XIMStatusWindow( bool bOn ) : + StatusWindow( WB_BORDER | WB_SYSTEMFLOATWIN | WB_TOOLTIPWIN ), + m_aStatusText( this, 0 ), + m_pLastParent( NULL ), + m_bAnchoredAtRight( false ), + m_bDelayedShow( false ), + m_eDelayedReason( I18NStatus::contextmap ), + m_nDelayedEvent( 0 ), + m_bOn( bOn ) +{ + layout(); +} + +XIMStatusWindow::~XIMStatusWindow() +{ + if( m_nDelayedEvent ) + Application::RemoveUserEvent( m_nDelayedEvent ); +} + +void XIMStatusWindow::toggle( bool bOn ) +{ + m_bOn = bOn; + show( bOn, I18NStatus::contextmap ); +} + +void XIMStatusWindow::layout() +{ + m_aWindowSize.Width() = m_aStatusText.GetTextWidth( m_aStatusText.GetText() )+8; + Font aFont( m_aStatusText.GetFont() ); + m_aWindowSize.Height() = aFont.GetHeight()+10; + m_aWindowSize = LogicToPixel( m_aWindowSize ); + + Size aControlSize( m_aWindowSize ); + aControlSize.Width() -= 4; + aControlSize.Height() -= 4; + + m_aStatusText.SetPosSizePixel( Point( 1, 1 ), aControlSize ); + m_aStatusText.SetFont( aFont ); + m_aStatusText.Show( sal_True ); + + if (m_bAnchoredAtRight && IsVisible()) + { + SalFrame* pFrame = (SalFrame*)GetSystemData()->pSalFrame; + long nDelta = pFrame->maGeometry.nWidth - m_aWindowSize.Width(); + pFrame->SetPosSize( pFrame->maGeometry.nX + nDelta, + pFrame->maGeometry.nY, + m_aWindowSize.Width(), + m_aWindowSize.Height(), + SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y | SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT ); + } + else + SetOutputSizePixel( m_aWindowSize ); +} + +bool XIMStatusWindow::checkLastParent() const +{ + if( m_pLastParent ) + { + const std::list< SalFrame* >& rFrames = GetX11SalData()->GetDisplay()->getFrames(); + for( std::list< SalFrame* >::const_iterator it = rFrames.begin(); it != rFrames.end(); ++it ) + { + if( *it == m_pLastParent ) + return true; + } + } + return false; +} + +void XIMStatusWindow::DataChanged( const DataChangedEvent& ) +{ + m_aStatusText.SetSettings( GetSettings() ); + layout(); +} + +Point XIMStatusWindow::updatePosition() +{ + Point aRet; + if( checkLastParent() ) + { + const SystemEnvData* pParentEnvData = m_pLastParent->GetSystemData(); + + SalExtTextInputPosEvent aPosEvent; + m_pLastParent->CallCallback( SALEVENT_EXTTEXTINPUTPOS, (void*)&aPosEvent ); + int x, y; + XLIB_Window aChild; + XTranslateCoordinates( (Display*)pParentEnvData->pDisplay, + (XLIB_Window)pParentEnvData->aShellWindow, + GetX11SalData()->GetDisplay()->GetRootWindow( GetX11SalData()->GetDisplay()->GetDefaultScreenNumber() ), + 0, 0, + &x, &y, + &aChild ); + + // TODO: Currently, place the status window to the (physical) left of + // the cursor iff in vertical mode (assuming that the columns in + // vertical mode are always written from right to left, this causes the + // status window to keep out of the text already written). This + // heuristic would break if there is ever a vertical mode in which the + // columns are written from left to right. Also, more elaborate + // positioning for (both horizontal and vertical) left-to-right and + // right-to-left text would be possible. + bool bLeft = aPosEvent.mbVertical; + // true if status window is to the left of the cursor + + int const nGap = 4; // between cursor and status window + if (aPosEvent.mbVertical) + { + aRet.X() = x + aPosEvent.mnX + (bLeft + ? -m_aWindowSize.Width() - nGap + : aPosEvent.mnHeight + nGap); + aRet.Y() = y + aPosEvent.mnY; + } + else + { + aRet.X() = x + aPosEvent.mnX + (bLeft ? -m_aWindowSize.Width() : 0); + aRet.Y() = y + aPosEvent.mnY+aPosEvent.mnHeight + nGap; + } + + m_bAnchoredAtRight = bLeft; + } + return aRet; +} + +void XIMStatusWindow::setPosition( SalFrame* pParent ) +{ + if( pParent ) + { + if( pParent != m_pLastParent ) + { + setText( String() ); + m_pLastParent = pParent; + Show( sal_False, SHOW_NOACTIVATE ); + } + if( IsVisible() ) + { + const SystemEnvData* pEnvData = GetSystemData(); + SalFrame* pStatusFrame = (SalFrame*)pEnvData->pSalFrame; + Point aPoint = updatePosition(); + pStatusFrame->SetPosSize( aPoint.X(), aPoint.Y(), m_aWindowSize.Width(), m_aWindowSize.Height(), SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y | SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT ); + } + } +} + +IMPL_LINK( XIMStatusWindow, DelayedShowHdl, void*, EMPTYARG ) +{ + m_nDelayedEvent = 0; + const SystemEnvData* pData = GetSystemData(); + SalFrame* pStatusFrame = (SalFrame*)pData->pSalFrame; + if( m_bDelayedShow ) + { + Size aControlSize( m_aWindowSize.Width()-4, m_aWindowSize.Height()-4 ); + m_aStatusText.SetPosSizePixel( Point( 1, 1 ), aControlSize ); + Point aPoint = updatePosition(); + pStatusFrame->SetPosSize( aPoint.X(), aPoint.Y(), m_aWindowSize.Width(), m_aWindowSize.Height(), SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y | SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT ); + } + Show( m_bDelayedShow && m_bOn, SHOW_NOACTIVATE ); + if( m_bDelayedShow ) + { + XRaiseWindow( (Display*)pData->pDisplay, + (XLIB_Window)pData->aShellWindow ); + } + return 0; +} + +void XIMStatusWindow::show( bool bShow, I18NStatus::ShowReason eReason ) +{ + if( bShow && ! m_aStatusText.GetText().Len() ) + bShow = false; + + m_bDelayedShow = bShow; + m_eDelayedReason = eReason; + if( ! m_nDelayedEvent ) + m_nDelayedEvent = Application::PostUserEvent( LINK( this, XIMStatusWindow, DelayedShowHdl ) ); +} + +void XIMStatusWindow::setText( const String& rText ) +{ + m_aStatusText.SetText( rText ); + m_aWindowSize.Width() = m_aStatusText.GetTextWidth( rText )+8; +} + +String XIMStatusWindow::getText() const +{ + return m_aStatusText.GetText(); +} + +// -------------------------------------------------------------------------- + +namespace vcl { + +class IIIMPStatusWindow : public StatusWindow +{ + MenuButton m_aStatusBtn; + PopupMenu m_aMenu; + SalFrame* m_pResetFocus; + bool m_bShow; + bool m_bOn; + + DECL_LINK( SelectHdl, MenuButton* ); + + void show(); + +public: + IIIMPStatusWindow( SalFrame* pParent, bool bOn ); // for initial position + virtual ~IIIMPStatusWindow(); + + virtual void setText( const String & ); + virtual String getText() const; + virtual void show( bool bShow, I18NStatus::ShowReason eReason ); + virtual void toggle( bool bOn ); + void layout(); + + // overload Window focus handler + virtual void GetFocus(); + // overload WorkWindow::DataChanged + virtual void DataChanged( const DataChangedEvent& rEvt ); +}; + +} + +IIIMPStatusWindow::IIIMPStatusWindow( SalFrame* pParent, bool bOn ) : + StatusWindow( WB_MOVEABLE ), + m_aStatusBtn( this, WB_BORDER ), + m_pResetFocus( pParent ), + m_bShow( true ), + m_bOn( bOn ) +{ + SetText( String( RTL_CONSTASCII_USTRINGPARAM( "IME Status" ) ) ); + + layout(); + + m_aStatusBtn.SetSelectHdl( LINK( this, IIIMPStatusWindow, SelectHdl ) ); + m_aStatusBtn.SetPopupMenu( &m_aMenu ); + m_aStatusBtn.Show( sal_True ); + + const ::std::vector< I18NStatus::ChoiceData >& rChoices( I18NStatus::get().getChoices() ); + int i = 1; + for( ::std::vector< I18NStatus::ChoiceData >::const_iterator it = rChoices.begin(); it != rChoices.end(); ++it, i++ ) + m_aMenu.InsertItem( i, it->aString ); + + if( pParent ) + { + const SystemEnvData* pEnvData = GetSystemData(); + + const SalFrameGeometry& rGeom( pParent->GetUnmirroredGeometry() ); + int nDistance = rGeom.nTopDecoration; + if( nDistance < 20 ) + nDistance = 20; + XMoveWindow( (Display*)pEnvData->pDisplay, + (XLIB_Window)pEnvData->aShellWindow, + rGeom.nX, + rGeom.nY + rGeom.nHeight + nDistance + ); + } +#if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "Warning: could not reposition status window since no frame\n" ); +#endif + EnableAlwaysOnTop( sal_True ); +} + +IIIMPStatusWindow::~IIIMPStatusWindow() +{ +} + +void IIIMPStatusWindow::layout() +{ + Font aFont( m_aStatusBtn.GetFont() ); + Size aSize( 15*aFont.GetHeight(), aFont.GetHeight()+14 ); + aSize = m_aStatusBtn.LogicToPixel( aSize ); + + m_aStatusBtn.SetPosSizePixel( Point( 0, 0 ), aSize ); + SetOutputSizePixel( aSize ); + if( IsVisible() ) + Invalidate(); +} + +void IIIMPStatusWindow::DataChanged( const DataChangedEvent& ) +{ + m_aStatusBtn.SetSettings( GetSettings() ); + layout(); +} + +void IIIMPStatusWindow::setText( const String& rText ) +{ + m_aStatusBtn.SetText( rText ); +} + +String IIIMPStatusWindow::getText() const +{ + return m_aStatusBtn.GetText(); +} + +void IIIMPStatusWindow::show( bool bShow, I18NStatus::ShowReason eReason ) +{ + // hide IIIMPStatusWindow only in presentations + if( ! bShow + && eReason != I18NStatus::presentation + ) + return; + + m_bShow = bShow; + show(); +} + +void IIIMPStatusWindow::toggle( bool bOn ) +{ + if (bOn != m_bOn) + { + m_bOn = bOn; + show(); + } +} + +void IIIMPStatusWindow::show() +{ + if (m_bOn && m_bShow && !IsVisible()) + m_pResetFocus = I18NStatus::get().getParent(); + Show(m_bOn && m_bShow); +} + +void IIIMPStatusWindow::GetFocus() +{ + /* + * this is here just to put the focus back to the application + * window at startup on clickToFocus WMs + */ + WorkWindow::GetFocus(); + if( m_pResetFocus ) + { + /* + * look if reset focus still exists + * since reset focus really is an internal hack there should + * not be a method to be called in SalFrame destructor + */ + const std::list< SalFrame* >& rFrames = GetX11SalData()->GetDisplay()->getFrames(); + std::list< SalFrame* >::const_iterator it; + for( it = rFrames.begin(); it != rFrames.end() && *it != m_pResetFocus; ++it ) + ; + if( it != rFrames.end() ) + { + const SystemEnvData* pParentEnvData = m_pResetFocus->GetSystemData(); + SalXLib* pXLib = GetX11SalData()->GetDisplay()->GetXLib(); + pXLib->PushXErrorLevel( true ); + XSetInputFocus( (Display*)pParentEnvData->pDisplay, + (XLIB_Window)pParentEnvData->aShellWindow, + RevertToNone, + CurrentTime + ); + XSync( (Display*)pParentEnvData->pDisplay, False ); + pXLib->PopXErrorLevel(); + } + m_pResetFocus = NULL; + } +} + +// -------------------------------------------------------------------------- + +IMPL_LINK( IIIMPStatusWindow, SelectHdl, MenuButton*, pBtn ) +{ + if( pBtn == & m_aStatusBtn ) + { + const ::std::vector< I18NStatus::ChoiceData >& rChoices( I18NStatus::get().getChoices() ); + unsigned int nIndex = m_aStatusBtn.GetCurItemId()-1; + if( nIndex < rChoices.size() ) + { + XSetICValues( static_cast<X11SalFrame*>(I18NStatus::get().getParent())->getInputContext()->GetContext(), + XNUnicodeCharacterSubset, + rChoices[nIndex].pData, + NULL); + // FIXME: get rid of X11SalFrame + X11SalFrame* pParent = static_cast<X11SalFrame*>(I18NStatus::get().getParent()); + if( pParent && pParent->isMapped() ) + { + const SystemEnvData* pEnv = pParent->GetSystemData(); + SalXLib* pXLib = GetX11SalData()->GetDisplay()->GetXLib(); + pXLib->PushXErrorLevel( true ); + XSetInputFocus( (Display*)pEnv->pDisplay, + (XLIB_Window)pEnv->aShellWindow, + RevertToNone, + CurrentTime + ); + XSync( (Display*)pEnv->pDisplay, False ); + pXLib->PopXErrorLevel(); + } + } + } + return 0; +} + +/* + * I18NStatus + */ + +I18NStatus* I18NStatus::pInstance = NULL; + +I18NStatus& I18NStatus::get() +{ + if( ! pInstance ) + pInstance = new I18NStatus(); + return *pInstance; +} + +// -------------------------------------------------------------------------- + +bool I18NStatus::exists() +{ + return pInstance != NULL; +} + +// -------------------------------------------------------------------------- + +void I18NStatus::free() +{ + if( pInstance ) + delete pInstance, pInstance = NULL; +} + +// -------------------------------------------------------------------------- + +I18NStatus::I18NStatus() : + m_pParent( NULL ), + m_pStatusWindow( NULL ) +{ +} + +// -------------------------------------------------------------------------- + +I18NStatus::~I18NStatus() +{ + if( m_pStatusWindow ) + delete m_pStatusWindow, m_pStatusWindow = NULL; + if( pInstance == this ) + pInstance = NULL; +} + +// -------------------------------------------------------------------------- + +void I18NStatus::setParent( SalFrame* pParent ) +{ + m_pParent = pParent; + if( ! m_pStatusWindow ) + { + bool bIIIMPmode = m_aChoices.begin() != m_aChoices.end(); + if( bIIIMPmode ) + m_pStatusWindow = new IIIMPStatusWindow( pParent, + getStatusWindowMode() ); + else + m_pStatusWindow = new XIMStatusWindow( getStatusWindowMode() ); + setStatusText( m_aCurrentIM ); + } + m_pStatusWindow->setPosition( m_pParent ); +} + +// -------------------------------------------------------------------------- + +void I18NStatus::show( bool bShow, ShowReason eReason ) +{ + if( m_pStatusWindow ) + { + m_pStatusWindow->setPosition( m_pParent ); + m_pStatusWindow->show( bShow, eReason ); + } +} + +// -------------------------------------------------------------------------- + +void I18NStatus::setStatusText( const String& rText ) +{ + if( m_pStatusWindow ) + { + /* + * #93614# convert fullwidth ASCII forms to ascii + */ + int nChars = rText.Len()+1; + sal_Unicode* pBuffer = (sal_Unicode*)alloca( nChars*sizeof( sal_Unicode ) ); + const sal_Unicode* pCopy = rText.GetBuffer(); + for( int i = 0; i < nChars; i++ ) + { + if( pCopy[i] >=0xff00 && pCopy[i] <= 0xff5f ) + pBuffer[i] = (pCopy[i] & 0xff) + 0x20; + else + pBuffer[i] = pCopy[i]; + } + String aText( pBuffer ); + m_pStatusWindow->setText( aText ); + m_pStatusWindow->setPosition( m_pParent ); + + bool bVisible = true; + if( m_pParent ) + { + long w, h; + m_pParent->GetClientSize( w, h ); + if( w == 0 || h == 0 ) + { + bVisible = false; + } + } + + m_pStatusWindow->show( bVisible, contextmap ); + } +} + +// -------------------------------------------------------------------------- + +void I18NStatus::changeIM( const String& rIM ) +{ + m_aCurrentIM = rIM; +} + +// -------------------------------------------------------------------------- + +String I18NStatus::getStatusText() const +{ + return m_pStatusWindow ? m_pStatusWindow->getText() : String(); +} + +// -------------------------------------------------------------------------- + +void I18NStatus::clearChoices() +{ + m_aChoices.clear(); +} + +// -------------------------------------------------------------------------- + +void I18NStatus::addChoice( const String& rChoice, void* pData ) +{ + ChoiceData aData; + aData.pData = pData; + aData.aString = rChoice; + m_aChoices.push_back( aData ); +} + +// -------------------------------------------------------------------------- + +void I18NStatus::toTop() const +{ + if( m_pStatusWindow ) + { + const SystemEnvData* pData = m_pStatusWindow->GetSystemData(); + XRaiseWindow( (Display*)pData->pDisplay, + (XLIB_Window)pData->aShellWindow ); + } +} + +// -------------------------------------------------------------------------- + +SalFrame* I18NStatus::getStatusFrame() const +{ + SalFrame* pRet = NULL; + if( m_pStatusWindow ) + { + const SystemEnvData* pData = m_pStatusWindow->GetSystemData(); + pRet = (SalFrame*)pData->pSalFrame; + } + return pRet; +} + +bool I18NStatus::canToggleStatusWindow() const +{ + return true; +} + +void I18NStatus::toggleStatusWindow() +{ + if (m_pStatusWindow != 0) + m_pStatusWindow->toggle(getStatusWindowMode()); +} + +bool I18NStatus::getStatusWindowMode() +{ + switch (ImplGetSVData()->maAppData.meShowImeStatusWindow) + { + default: // ImplSVAppData::ImeStatusWindowMode_UNKNOWN + return Application::GetShowImeStatusWindowDefault(); + case ImplSVAppData::ImeStatusWindowMode_HIDE: + return false; + case ImplSVAppData::ImeStatusWindowMode_SHOW: + return true; + } +} + +/* + * X11ImeStatus + */ +X11ImeStatus::~X11ImeStatus() +{ + vcl::I18NStatus::free(); +} + +bool X11ImeStatus::canToggle() +{ + return vcl::I18NStatus::get().canToggleStatusWindow(); +} + +void X11ImeStatus::toggle() +{ + vcl::I18NStatus::get().toggleStatusWindow(); +} + +SalI18NImeStatus* X11SalInstance::CreateI18NImeStatus() +{ + return new X11ImeStatus(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/app/i18n_wrp.cxx b/vcl/unx/generic/app/i18n_wrp.cxx new file mode 100644 index 000000000000..981dc390e278 --- /dev/null +++ b/vcl/unx/generic/app/i18n_wrp.cxx @@ -0,0 +1,262 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +struct XIMArg +{ + char *name; + char *value; +}; + +#if defined(SOLARIS) && !defined(__GNUC__) +#include <varargs.h> +#else +#include <stdarg.h> +#endif +#include <sal/alloca.h> + +#include <string.h> +#include <dlfcn.h> + +#include <X11/Xlib.h> +#include <X11/Xlibint.h> +#include "unx/XIM.h" + +#define XIIIMP_LIB "xiiimp.so.2" + +#ifdef SOLARIS +#define XIIIMP_PATH "/usr/openwin/lib/locale/common/" XIIIMP_LIB +#else /* Linux */ +#define XIIIMP_PATH "/usr/lib/im/" XIIIMP_LIB +#endif + +extern "C" { +typedef XIM (*OpenFunction)(Display*, XrmDatabase, char*, char*, XIMArg*); +} + +/* global variables */ +static void *g_dlmodule = 0; +static OpenFunction g_open_im = (OpenFunction)NULL; + +/* utility function to transform vararg list into an array of XIMArg */ + +int +XvaCountArgs( XIMArg *pInArgs ) +{ + int nArgs = 0; + char *pName, *pValue; + + while ( (pName = pInArgs->name) != NULL ) + { + pValue = pInArgs->value; + + if ( strcmp(pName, XNVaNestedList) == 0 ) + { + nArgs += XvaCountArgs( (XIMArg*)pValue ); + } + else + { + nArgs += 1; + } + pInArgs++; + } + + return nArgs; +} + +int +XvaCountArgs( va_list pInArgs ) +{ + int nArgs = 0; + char *pName, *pValue; + + while ( (pName = va_arg(pInArgs, char*)) != NULL) + { + pValue = va_arg(pInArgs, char*); + + if ( strcmp(pName, XNVaNestedList) == 0 ) + { + nArgs += XvaCountArgs( (XIMArg*)pValue ); + } + else + { + nArgs += 1; + } + } + + return nArgs; +} + +XIMArg* +XvaGetArgs( XIMArg *pInArgs, XIMArg *pOutArgs ) +{ + char *pName, *pValue; + + while ( (pName = pInArgs->name) != NULL ) + { + pValue = pInArgs->value; + + if ( strcmp(pName, XNVaNestedList) == 0 ) + { + pOutArgs = XvaGetArgs( (XIMArg*)pValue, pOutArgs ); + } + else + { + pOutArgs->name = pName; + pOutArgs->value = pValue; + pOutArgs++; + } + pInArgs++; + } + + return pOutArgs; +} + +void +XvaGetArgs( va_list pInArgs, XIMArg *pOutArgs ) +{ + char *pName, *pValue; + + while ((pName = va_arg(pInArgs, char*)) != NULL) + { + pValue = va_arg(pInArgs, char*); + + if ( strcmp(pName, XNVaNestedList) == 0 ) + { + pOutArgs = XvaGetArgs( (XIMArg*)pValue, pOutArgs ); + } + else + { + pOutArgs->name = pName; + pOutArgs->value = pValue; + pOutArgs++; + } + } + + pOutArgs->name = NULL; + pOutArgs->value = NULL; +} + + +/* Puplic functions */ + +#ifdef __cplusplus +extern "C" +#endif +XIM +XvaOpenIM(Display *display, XrmDatabase rdb, + char *res_name, char *res_class, ...) +{ + XIM xim = (XIM)0; + va_list variable; + int total_count = 0; + + /* + * so count the stuff dangling here + */ + +#if defined(SOLARIS) && !defined(__GNUC__) + va_start(variable); +#else + va_start(variable, res_class); +#endif + total_count = XvaCountArgs(variable); + va_end(variable); + + if (total_count > 0) + { + /* call a new open IM method */ + + XIMArg* args = (XIMArg*)alloca( (total_count + 1) * sizeof(XIMArg) ); + + /* + * now package it up so we can set it along + */ +#if defined(SOLARIS) && !defined(__GNUC__) + va_start(variable); +#else + va_start(variable, res_class); +#endif + XvaGetArgs( variable, args ); + va_end(variable); + + if (!g_dlmodule) + { + g_dlmodule = dlopen(XIIIMP_LIB, RTLD_LAZY); + if(!g_dlmodule) + { + g_dlmodule = dlopen(XIIIMP_PATH, RTLD_LAZY); + if (!g_dlmodule) + goto legacy_XIM; + } + g_open_im = (OpenFunction)(long)dlsym(g_dlmodule, "__XOpenIM"); + if (!g_open_im) + goto legacy_XIM; + + xim = (*g_open_im)(display, (XrmDatabase)rdb, + (char*)res_name, (char *)res_class, (XIMArg*)args); + } + else + { + goto legacy_XIM; + } + } + +// in #if to prevent warning "warning: label 'legacy_XIM' defined but not used" + legacy_XIM: + + if (!xim) + xim = XOpenIM(display, rdb, res_name, res_class); + + return xim; +} + +/* + * Close the connection to the input manager, and free the XIM structure + */ + +Status XvaCloseIM(XIM) +{ + Status s = False; + + if (!g_dlmodule) + { + /* assuming one XvaOpenIM call */ + dlclose(g_dlmodule); + g_dlmodule = (void*)0; + g_open_im = (OpenFunction)NULL; + s = True; + } + return (s); +} + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/app/i18n_xkb.cxx b/vcl/unx/generic/app/i18n_xkb.cxx new file mode 100644 index 000000000000..cb93f48b6bd7 --- /dev/null +++ b/vcl/unx/generic/app/i18n_xkb.cxx @@ -0,0 +1,165 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + + +#include <stdio.h> + +#include "unx/saldisp.hxx" +#include "unx/saldata.hxx" +#include "unx/i18n_xkb.hxx" + +SalI18N_KeyboardExtension::SalI18N_KeyboardExtension( Display* +#if __XKeyboardExtension__ +pDisplay +#endif +) + : mbUseExtension( (sal_Bool)__XKeyboardExtension__ ), + mnDefaultGroup( 0 ) +{ + #if __XKeyboardExtension__ + + mpDisplay = pDisplay; + + // allow user to set the default keyboard group idx or to disable the usage + // of x keyboard extension at all: + // setenv SAL_XKEYBOARDGROUP disables keyboard extension + // setenv SAL_XKEYBOARDGROUP 2 sets the keyboard group index to 2 + // keyboard group index must be in [1,4], may be specified in hex or decimal + static char *pUseKeyboardExtension = getenv( "SAL_XKEYBOARDGROUP" ); + if ( pUseKeyboardExtension != NULL ) + { + mbUseExtension = pUseKeyboardExtension[0] != '\0' ; + if ( mbUseExtension ) + mnDefaultGroup = strtol( pUseKeyboardExtension, NULL, 0 ); + if ( mnDefaultGroup > XkbMaxKbdGroup ) + mnDefaultGroup = 0; + } + + // query XServer support for XKB Extension, + // do not call XQueryExtension() / XInitExtension() due to possible version + // clashes ! + if ( mbUseExtension ) + { + int nMajorExtOpcode; + int nExtMajorVersion = XkbMajorVersion; + int nExtMinorVersion = XkbMinorVersion; + + mbUseExtension = (sal_Bool)XkbQueryExtension( mpDisplay, + &nMajorExtOpcode, (int*)&mnEventBase, (int*)&mnErrorBase, + &nExtMajorVersion, &nExtMinorVersion ); + } + + // query notification for changes of the keyboard group + if ( mbUseExtension ) + { + #define XkbGroupMask ( XkbGroupStateMask | XkbGroupBaseMask \ + | XkbGroupLatchMask | XkbGroupLockMask ) + + mbUseExtension = XkbSelectEventDetails( mpDisplay, + XkbUseCoreKbd, XkbStateNotify, XkbGroupMask, XkbGroupMask ); + } + + // query initial keyboard group + if ( mbUseExtension ) + { + XkbStateRec aStateRecord; + XkbGetState( mpDisplay, XkbUseCoreKbd, &aStateRecord ); + mnGroup = aStateRecord.group; + } + + #endif // __XKeyboardExtension__ +} + +void +SalI18N_KeyboardExtension::Dispatch( XEvent* +#if __XKeyboardExtension__ +pEvent +#endif +) +{ + #if __XKeyboardExtension__ + + // must the event be handled? + if ( !mbUseExtension + || (pEvent->type != mnEventBase) ) + return; + + // only handle state notify events for now, and only interested + // in group details + sal_uInt32 nXKBType = ((XkbAnyEvent*)pEvent)->xkb_type; + switch ( nXKBType ) + { + case XkbStateNotify: + + mnGroup = ((XkbStateNotifyEvent*)pEvent)->group; + break; + + default: + + #if OSL_DEBUG_LEVEL > 1 + fprintf(stderr, "Got unrequested XkbAnyEvent %#x/%i\n", + static_cast<unsigned int>(nXKBType), static_cast<int>(nXKBType) ); + #endif + break; + } + #endif // __XKeyboardExtension__ +} + +#if __XKeyboardExtension__ +sal_uInt32 +SalI18N_KeyboardExtension::LookupKeysymInGroup( sal_uInt32 nKeyCode, + sal_uInt32 nShiftState, + sal_uInt32 nGroup ) const +#else +sal_uInt32 +SalI18N_KeyboardExtension::LookupKeysymInGroup( sal_uInt32,sal_uInt32,sal_uInt32 ) const +#endif +{ + #if __XKeyboardExtension__ + + if ( !mbUseExtension ) + return NoSymbol; + + nShiftState &= ShiftMask; + + KeySym nKeySymbol; + nKeySymbol = XkbKeycodeToKeysym( mpDisplay, nKeyCode, nGroup, nShiftState ); + return nKeySymbol; + + #else + + return NoSymbol; + + #endif // __XKeyboardExtension__ +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/app/keysymnames.cxx b/vcl/unx/generic/app/keysymnames.cxx new file mode 100644 index 000000000000..0615a2d44450 --- /dev/null +++ b/vcl/unx/generic/app/keysymnames.cxx @@ -0,0 +1,692 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#if !defined(SOLARIS) && !defined(AIX) +#include <tools/prex.h> +#include <X11/XKBlib.h> +#include <tools/postx.h> +#endif + +#include <unx/saldisp.hxx> +#include <X11/keysym.h> +#include <sal/macros.h> + +#if !defined (SunXK_Undo) +#define SunXK_Undo 0x0000FF65 // XK_Undo +#define SunXK_Again 0x0000FF66 // XK_Redo +#define SunXK_Find 0x0000FF68 // XK_Find +#define SunXK_Stop 0x0000FF69 // XK_Cancel +#define SunXK_Props 0x1005FF70 +#define SunXK_Front 0x1005FF71 +#define SunXK_Copy 0x1005FF72 +#define SunXK_Open 0x1005FF73 +#define SunXK_Paste 0x1005FF74 +#define SunXK_Cut 0x1005FF75 +#endif + +#ifdef SOLARIS +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/kbio.h> +#include <sys/kbd.h> +#include <stdio.h> +#include <fcntl.h> +#include <deflt.h> +#include <unistd.h> +#include <stdlib.h> +#endif + +#include <string.h> + +namespace vcl_sal { + + struct KeysymNameReplacement + { + KeySym aSymbol; + const char* pName; + }; + + struct KeyboardReplacements + { + const char* pKeyboardName; + const KeysymNameReplacement* pReplacements; + int nReplacements; + }; + + // ==================================================================== + // + // CAUTION CAUTION CAUTION + // every string value in the replacements tables must be in UTF8 + // be careful with your editor ! + // + // ==================================================================== + + static const struct KeysymNameReplacement aImplReplacements_English[] = + { + { XK_Control_L, "Ctrl" }, + { XK_Control_R, "Ctrl" }, + { XK_Escape, "Esc" }, + { XK_space, "Space" }, + { XK_minus, "-" }, + { XK_plus, "+" } + }; + + static const struct KeysymNameReplacement aImplReplacements_Turkish[] = + { + { XK_Control_L, "Ctrl" }, + { XK_Control_R, "Ctrl" }, + { XK_Right, "Sağ" }, + { XK_Left, "Sol" }, + { XK_Up, "Yukarı" }, + { XK_Down, "Aşağı" }, + { XK_space, "Boşluk" } + }; + + static const struct KeysymNameReplacement aImplReplacements_Russian[] = + { + { XK_Right, "Вправо" }, + { XK_Left, "Влево" }, + { XK_Up, "Вверх" }, + { XK_Down, "Вниз" }, + { XK_space, "Пробел" } + }; + + static const struct KeysymNameReplacement aImplReplacements_German[] = + { + { XK_Control_L, "Strg" }, + { XK_Control_R, "Strg" }, + { XK_Shift_L, "Umschalt" }, + { XK_Shift_R, "Umschalt" }, + { XK_Alt_L, "Alt" }, + { XK_Alt_R, "Alt Gr" }, + { XK_Page_Up, "Bild auf" }, + { XK_Page_Down, "Bild ab" }, + { XK_End, "Ende" }, + { XK_Home, "Pos 1" }, + { XK_Insert, "Einfg" }, + { XK_Delete, "Entf" }, + { XK_Escape, "Esc" }, + { XK_Right, "Rechts" }, + { XK_Left, "Links" }, + { XK_Up, "Oben" }, + { XK_Down, "Unten" }, + { XK_BackSpace, "Rückschritt" }, + { XK_Return, "Eingabe" }, + { XK_slash, "Schrägstrich" }, + { XK_space, "Leertaste" }, + { SunXK_Stop, "Stop" }, + { SunXK_Again, "Wiederholen" }, + { SunXK_Props, "Eigenschaften" }, + { SunXK_Undo, "Zurücknehmen" }, + { SunXK_Front, "Vordergrund" }, + { SunXK_Copy, "Kopieren" }, + { SunXK_Open, "Öffnen" }, + { SunXK_Paste, "Einsetzen" }, + { SunXK_Find, "Suchen" }, + { SunXK_Cut, "Ausschneiden" }, + { XK_minus, "-" }, + { XK_plus, "+" } + }; + + static const struct KeysymNameReplacement aImplReplacements_French[] = + { + { XK_Shift_L, "Maj" }, + { XK_Shift_R, "Maj" }, + { XK_Page_Up, "Pg. Préc" }, + { XK_Page_Down, "Pg. Suiv" }, + { XK_End, "Fin" }, + { XK_Home, "Origine" }, + { XK_Insert, "Insérer" }, + { XK_Delete, "Suppr" }, + { XK_Escape, "Esc" }, + { XK_Right, "Droite" }, + { XK_Left, "Gauche" }, + { XK_Up, "Haut" }, + { XK_Down, "Bas" }, + { XK_BackSpace, "Ret. Arr" }, + { XK_Return, "Retour" }, + { XK_KP_Enter, "Entrée" }, + { SunXK_Stop, "Stop" }, + { SunXK_Again, "Encore" }, + { SunXK_Props, "Props" }, + { SunXK_Undo, "Annuler" }, + { SunXK_Front, "Devant" }, + { SunXK_Copy, "Copy" }, + { SunXK_Open, "Ouvrir" }, + { SunXK_Paste, "Coller" }, + { SunXK_Find, "Cher." }, + { SunXK_Cut, "Couper" }, + { XK_minus, "-" }, + { XK_plus, "+" } + }; + + static const struct KeysymNameReplacement aImplReplacements_Italian[] = + { + { XK_Shift_L, "Maiusc" }, + { XK_Shift_R, "Maiusc" }, + { XK_Page_Up, "PgSu" }, + { XK_Page_Down, "PgGiu" }, + { XK_End, "Fine" }, + { XK_Insert, "Ins" }, + { XK_Delete, "Canc" }, + { XK_Escape, "Esc" }, + { XK_Right, "A destra" }, + { XK_Left, "A sinistra" }, + { XK_Up, "Sposta verso l'alto" }, + { XK_Down, "Sposta verso il basso" }, + { XK_BackSpace, "Backspace" }, + { XK_Return, "Invio" }, + { XK_space, "Spazio" }, + { SunXK_Stop, "Stop" }, + { SunXK_Again, "Ancora" }, + { SunXK_Props, "Proprietà" }, + { SunXK_Undo, "Annulla" }, + { SunXK_Front, "Davanti" }, + { SunXK_Copy, "Copia" }, + { SunXK_Open, "Apri" }, + { SunXK_Paste, "Incolla" }, + { SunXK_Find, "Trova" }, + { SunXK_Cut, "Taglia" }, + { XK_minus, "-" }, + { XK_plus, "+" } + }; + + static const struct KeysymNameReplacement aImplReplacements_Dutch[] = + { + { XK_Page_Up, "PageUp" }, + { XK_Page_Down, "PageDown" }, + { XK_Escape, "Esc" }, + { XK_Right, "Rechts" }, + { XK_Left, "Links" }, + { XK_Up, "Boven" }, + { XK_Down, "Onder" }, + { XK_BackSpace, "Backspace" }, + { XK_Return, "Return" }, + { XK_space, "Spatiebalk" }, + { SunXK_Stop, "Stop" }, + { SunXK_Again, "Again" }, + { SunXK_Props, "Props" }, + { SunXK_Undo, "Undo" }, + { SunXK_Front, "Front" }, + { SunXK_Copy, "Copy" }, + { SunXK_Open, "Open" }, + { SunXK_Paste, "Paste" }, + { SunXK_Find, "Find" }, + { SunXK_Cut, "Cut" }, + { XK_minus, "-" }, + { XK_plus, "+" } + }; + + static const struct KeysymNameReplacement aImplReplacements_Norwegian[] = + { + { XK_Shift_L, "Skift" }, + { XK_Shift_R, "Skift" }, + { XK_Page_Up, "PageUp" }, + { XK_Page_Down, "PageDown" }, + { XK_Escape, "Esc" }, + { XK_Right, "Hyre" }, + { XK_Left, "Venstre" }, + { XK_Up, "Opp" }, + { XK_Down, "Ned" }, + { XK_BackSpace, "Tilbake" }, + { XK_Return, "Enter" }, + { SunXK_Stop, "Avbryt" }, + { SunXK_Again, "Gjenta" }, + { SunXK_Props, "Egenskaper" }, + { SunXK_Undo, "Angre" }, + { SunXK_Front, "Front" }, + { SunXK_Copy, "Kopi" }, + { SunXK_Open, "Åpne" }, + { SunXK_Paste, "Lim" }, + { SunXK_Find, "Søk" }, + { SunXK_Cut, "Klipp" }, + { XK_minus, "-" }, + { XK_plus, "+" } + }; + + static const struct KeysymNameReplacement aImplReplacements_Swedish[] = + { + { XK_Shift_L, "Skift" }, + { XK_Shift_R, "Skift" }, + { XK_Page_Up, "PageUp" }, + { XK_Page_Down, "PageDown" }, + { XK_Escape, "Esc" }, + { XK_Right, "Höger" }, + { XK_Left, "Vänster" }, + { XK_Up, "Up" }, + { XK_Down, "Ned" }, + { XK_BackSpace, "Backsteg" }, + { XK_Return, "Retur" }, + { XK_space, "Blank" }, + { SunXK_Stop, "Avbryt" }, + { SunXK_Again, "Upprepa" }, + { SunXK_Props, "Egenskaper" }, + { SunXK_Undo, "Ångra" }, + { SunXK_Front, "Fram" }, + { SunXK_Copy, "Kopiera" }, + { SunXK_Open, "Öppna" }, + { SunXK_Paste, "Klistra in" }, + { SunXK_Find, "Sök" }, + { SunXK_Cut, "Klipp ut" }, + { XK_minus, "-" }, + { XK_plus, "+" } + }; + + static const struct KeysymNameReplacement aImplReplacements_Portuguese[] = + { + { XK_Page_Up, "PageUp" }, + { XK_Page_Down, "PageDown" }, + { XK_Escape, "Esc" }, + { XK_Right, "Direita" }, + { XK_Left, "Esquerda" }, + { XK_Up, "Acima" }, + { XK_Down, "Abaixo" }, + { XK_BackSpace, "Backspace" }, + { XK_Return, "Enter" }, + { XK_slash, "Barra" }, + { SunXK_Stop, "Stop" }, + { SunXK_Again, "Again" }, + { SunXK_Props, "Props" }, + { SunXK_Undo, "Undo" }, + { SunXK_Front, "Front" }, + { SunXK_Copy, "Copy" }, + { SunXK_Open, "Open" }, + { SunXK_Paste, "Paste" }, + { SunXK_Find, "Find" }, + { SunXK_Cut, "Cut" }, + { XK_minus, "-" }, + { XK_plus, "+" } + }; + + static const struct KeysymNameReplacement aImplReplacements_Spanish[] = + { + { XK_Shift_L, "Mayús" }, + { XK_Shift_R, "Mayús" }, + { XK_Page_Up, "RePág" }, + { XK_Page_Down, "AvPág" }, + { XK_End, "Fin" }, + { XK_Home, "Inicio" }, + { XK_Delete, "Supr" }, + { XK_Escape, "Esc" }, + { XK_Right, "Hacia la derecha" }, + { XK_Left, "Hacia la izquierda" }, + { XK_Up, "Hacia arriba" }, + { XK_Down, "Hacia abajo" }, + { XK_BackSpace, "Ret" }, + { XK_Return, "Entrada" }, + { XK_space, "Espacio" }, + { XK_KP_Enter, "Intro" }, + { SunXK_Stop, "Stop" }, + { SunXK_Again, "Repetir" }, + { SunXK_Props, "Props" }, + { SunXK_Undo, "Anular" }, + { SunXK_Front, "Delante" }, + { SunXK_Copy, "Copiar" }, + { SunXK_Open, "Abrir" }, + { SunXK_Paste, "Pegar" }, + { SunXK_Find, "Buscar" }, + { SunXK_Cut, "Cortar" }, + { XK_minus, "-" }, + { XK_plus, "+" } + }; + + static const struct KeyboardReplacements aKeyboards[] = + { +#ifdef SOLARIS + { "Germany5", aImplReplacements_German, SAL_N_ELEMENTS(aImplReplacements_German) }, + { "Germany4", aImplReplacements_German, SAL_N_ELEMENTS(aImplReplacements_German) }, + { "France5", aImplReplacements_French, SAL_N_ELEMENTS(aImplReplacements_French) }, + { "France6", aImplReplacements_French, SAL_N_ELEMENTS(aImplReplacements_French) }, + { "France_x86", aImplReplacements_French, SAL_N_ELEMENTS(aImplReplacements_French) }, + { "Italy5", aImplReplacements_Italian, SAL_N_ELEMENTS(aImplReplacements_Italian) }, + { "Italy5-Hobo", aImplReplacements_Italian, SAL_N_ELEMENTS(aImplReplacements_Italian) }, + { "Italy4", aImplReplacements_Italian, SAL_N_ELEMENTS(aImplReplacements_Italian) }, + { "Italy6", aImplReplacements_Italian, SAL_N_ELEMENTS(aImplReplacements_Italian) }, + { "Italy_x86", aImplReplacements_Italian, SAL_N_ELEMENTS(aImplReplacements_Italian) }, + { "Netherland4", aImplReplacements_Dutch, SAL_N_ELEMENTS(aImplReplacements_Dutch) }, + { "Netherland5", aImplReplacements_Dutch, SAL_N_ELEMENTS(aImplReplacements_Dutch) }, + { "Netherland5-Hobo", aImplReplacements_Dutch, SAL_N_ELEMENTS(aImplReplacements_Dutch) }, + { "Netherland6", aImplReplacements_Dutch, SAL_N_ELEMENTS(aImplReplacements_Dutch) }, + { "Netherland_x86", aImplReplacements_Dutch, SAL_N_ELEMENTS(aImplReplacements_Dutch) }, + { "Norway5", aImplReplacements_Norwegian, SAL_N_ELEMENTS(aImplReplacements_Norwegian) }, + { "Norway5-Hobo", aImplReplacements_Norwegian, SAL_N_ELEMENTS(aImplReplacements_Norwegian) }, + { "Norway4", aImplReplacements_Norwegian, SAL_N_ELEMENTS(aImplReplacements_Norwegian) }, + { "Norway6", aImplReplacements_Norwegian, SAL_N_ELEMENTS(aImplReplacements_Norwegian) }, + { "Norway_x86", aImplReplacements_Norwegian, SAL_N_ELEMENTS(aImplReplacements_Norwegian) }, + { "Portugal5", aImplReplacements_Portuguese, SAL_N_ELEMENTS(aImplReplacements_Portuguese) }, + { "Portugal5-Hobo", aImplReplacements_Portuguese, SAL_N_ELEMENTS(aImplReplacements_Portuguese) }, + { "Portugal4", aImplReplacements_Portuguese, SAL_N_ELEMENTS(aImplReplacements_Portuguese) }, + { "Portugal6", aImplReplacements_Portuguese, SAL_N_ELEMENTS(aImplReplacements_Portuguese) }, + { "Portugal_x86", aImplReplacements_Portuguese, SAL_N_ELEMENTS(aImplReplacements_Portuguese) }, + { "Spain5", aImplReplacements_Spanish, SAL_N_ELEMENTS(aImplReplacements_Spanish) }, + { "Spain5-Hobo", aImplReplacements_Spanish, SAL_N_ELEMENTS(aImplReplacements_Spanish) }, + { "Spain4", aImplReplacements_Spanish, SAL_N_ELEMENTS(aImplReplacements_Spanish) }, + { "Spain6", aImplReplacements_Spanish, SAL_N_ELEMENTS(aImplReplacements_Spanish) }, + { "Spain_x86", aImplReplacements_Spanish, SAL_N_ELEMENTS(aImplReplacements_Spanish) }, + { "Sweden5", aImplReplacements_Swedish, SAL_N_ELEMENTS(aImplReplacements_Swedish) }, + { "Sweden5-Hobo", aImplReplacements_Swedish, SAL_N_ELEMENTS(aImplReplacements_Swedish) }, + { "Sweden4", aImplReplacements_Swedish, SAL_N_ELEMENTS(aImplReplacements_Swedish) }, + { "Sweden6", aImplReplacements_Swedish, SAL_N_ELEMENTS(aImplReplacements_Swedish) }, + { "Sweden_x86", aImplReplacements_Swedish, SAL_N_ELEMENTS(aImplReplacements_Swedish) }, +#endif + { "U.S. English", aImplReplacements_English, SAL_N_ELEMENTS(aImplReplacements_English) }, + { "United Kingdom", aImplReplacements_English, SAL_N_ELEMENTS(aImplReplacements_English) }, + // Germany, German + { "German", aImplReplacements_German, SAL_N_ELEMENTS(aImplReplacements_German) }, + { "France", aImplReplacements_French, SAL_N_ELEMENTS(aImplReplacements_French) }, + { "French", aImplReplacements_French, SAL_N_ELEMENTS(aImplReplacements_French) }, + // Italy, Italian + { "Ital", aImplReplacements_Italian, SAL_N_ELEMENTS(aImplReplacements_Italian) }, + // Norway, Norwegian + { "Norw", aImplReplacements_Norwegian, SAL_N_ELEMENTS(aImplReplacements_Norwegian) }, + // Portugal, Portuguese + { "Portu", aImplReplacements_Portuguese, SAL_N_ELEMENTS(aImplReplacements_Portuguese) }, + { "Spain", aImplReplacements_Spanish, SAL_N_ELEMENTS(aImplReplacements_Spanish) }, + { "Spanish", aImplReplacements_Spanish, SAL_N_ELEMENTS(aImplReplacements_Spanish) }, + // Sweden, Swedish + { "Swed", aImplReplacements_Swedish, SAL_N_ELEMENTS(aImplReplacements_Swedish) }, + { "Netherland", aImplReplacements_Dutch, SAL_N_ELEMENTS(aImplReplacements_Dutch) }, + { "Dutch", aImplReplacements_Dutch, SAL_N_ELEMENTS(aImplReplacements_Dutch) }, + // Turkish, Turkey + { "Turk", aImplReplacements_Turkish, SAL_N_ELEMENTS(aImplReplacements_Turkish) }, + // Russian, Russia + { "Russia", aImplReplacements_Russian, SAL_N_ELEMENTS(aImplReplacements_Russian) }, + { "English", aImplReplacements_English, SAL_N_ELEMENTS(aImplReplacements_English) } + }; + + String getKeysymReplacementName( const char* pKeyboard, KeySym nSymbol ) + { + for( unsigned int n = 0; n < SAL_N_ELEMENTS(aKeyboards); n++ ) + { + if( ! strncasecmp( pKeyboard, aKeyboards[n].pKeyboardName, strlen( aKeyboards[n].pKeyboardName ) ) ) + { + const struct KeysymNameReplacement* pRepl = aKeyboards[n].pReplacements; + for( int m = aKeyboards[n].nReplacements ; m ; ) + { + if( nSymbol == pRepl[--m].aSymbol ) + return String( pRepl[m].pName, RTL_TEXTENCODING_UTF8 ); + } + } + } + // try english fallbacks + const struct KeysymNameReplacement* pRepl = aImplReplacements_English; + for( int m = SAL_N_ELEMENTS(aImplReplacements_English); m ; ) + { + if( nSymbol == pRepl[--m].aSymbol ) + return String( pRepl[m].pName, RTL_TEXTENCODING_UTF8 ); + } + return String(); + } + +} + +#ifdef SOLARIS +typedef struct { + int n_layout; + const char* p_description; +} keyboard_layout; + +static const keyboard_layout type0_layout[] = +{ + { 0, "US4" }, + { -1, NULL } +}; + +static const keyboard_layout type3_layout[] = +{ + { 0, "US3" }, + { -1, NULL } +}; + +static const keyboard_layout type4_layout[] = +{ + { 0, "US4" }, + { 1, "US4" }, + { 2, "FranceBelg4" }, + { 3, "Canada4" }, + { 4, "Denmark4" }, + { 5, "Germany4" }, + { 6, "Italy4" }, + { 7, "Netherland4" }, + { 8, "Norway4" }, + { 9, "Portugal4" }, + { 10, "SpainLatAm4" }, + { 11, "SwedenFin4" }, + { 12, "Switzer_Fr4" }, + { 13, "Switzer_Ge4" }, + { 14, "UK4" }, + { 16, "Korea4" }, + { 17, "Taiwan4" }, + { 19, "US101A_PC" }, + { 19, "US101A_Sun" }, + { 32, "Japan4" }, + { 33, "US5" }, + { 34, "US_UNIX5" }, + { 35, "France5" }, + { 36, "Denmark5" }, + { 37, "Germany5" }, + { 38, "Italy5" }, + { 39, "Netherland5" }, + { 40, "Norway5" }, + { 41, "Portugal5" }, + { 42, "Spain5" }, + { 43, "Sweden5" }, + { 44, "Switzer_Fr5" }, + { 45, "Switzer_Ge5" }, + { 46, "UK5" }, + { 47, "Korea5" }, + { 48, "Taiwan5" }, + { 49, "Japan5" }, + { 50, "Canada_Fr5" }, + { 51, "Hungary5" }, + { 52, "Poland5" }, + { 53, "Czech5" }, + { 54, "Russia5" }, + { 55, "Latvia5" }, + { 56, "Turkey5" }, + { 57, "Greece5" }, + { 58, "Estonia5" }, + { 59, "Lithuania5" }, + { 63, "Canada_Fr5_TBITS5" }, + { 80, "US5_Hobo" }, + { 81, "US_UNIX5_Hobo" }, + { 82, "France5_Hobo" }, + { 83, "Denmark5_Hobo" }, + { 84, "Germany5_Hobo" }, + { 85, "Italy5_Hobo" }, + { 86, "Netherland5_Hobo" }, + { 87, "Norway5_Hobo" }, + { 88, "Portugal5_Hobo" }, + { 89, "Spain5_Hobo" }, + { 90, "Sweden5_Hobo" }, + { 91, "Switzer_Fr5_Hobo" }, + { 92, "Switzer_Ge5_Hobo" }, + { 93, "UK5_Hobo" }, + { 94, "Korea5_Hobo" }, + { 95, "Taiwan5_Hobo" }, + { 96, "Japan5_Hobo" }, + { 97, "Canada_Fr5_Hobo" }, + { -1, NULL } +}; + +static const keyboard_layout type101_layout[] = +{ + { 0, "US101A_x86" }, + { 1, "US101A_x86" }, + { 34, "J3100_x86" }, + { 35, "France_x86" }, + { 36, "Denmark_x86" }, + { 37, "Germany_x86" }, + { 38, "Italy_x86" }, + { 39, "Netherland_x86" }, + { 40, "Norway_x86" }, + { 41, "Portugal_x86" }, + { 42, "Spain_x86" }, + { 43, "Sweden_x86" }, + { 44, "Switzer_Fr_x86" }, + { 45, "Switzer_Ge_x86" }, + { 46, "UK_x86" }, + { 47, "Korea_x86" }, + { 48, "Taiwan_x86" }, + { 49, "Japan_x86" }, + { 50, "Canada_Fr2_x86" }, + { 51, "Hungary_x86" }, + { 52, "Poland_x86" }, + { 53, "Czech_x86" }, + { 54, "Russia_x86" }, + { 55, "Latvia_x86" }, + { 56, "Turkey_x86" }, + { 57, "Greece_x86" }, + { 59, "Lithuania_x86" }, + { 1001, "MS_US101A_x86" }, + { -1, NULL } +}; + +static const keyboard_layout type6_layout[] = +{ + { 0, "US6" }, + { 6, "Denmark6" }, + { 7, "Finnish6" }, + { 8, "France6" }, + { 9, "Germany6" }, + { 14, "Italy6" }, + { 15, "Japan6" }, + { 16, "Korea6" }, + { 18, "Netherland6" }, + { 19, "Norway6" }, + { 22, "Portugal6" }, + { 25, "Spain6" }, + { 26, "Sweden6" }, + { 27, "Switzer_Fr6" }, + { 28, "Switzer_Ge6" }, + { 30, "Taiwan6" }, + { 32, "UK6" }, + { 33, "US6" }, + { -1, NULL } +}; +#endif + + +#if OSL_DEBUG_LEVEL > 1 +#include <stdio.h> +#endif + +const char* SalDisplay::GetKeyboardName( bool bRefresh ) +{ + if( bRefresh || ! m_aKeyboardName.Len() ) + { +#if defined(SOLARIS) + if( IsLocal() ) + { + int kbd = open( "/dev/kbd", O_RDONLY ); + if( kbd >= 0 ) + { + int kbd_type = 0; + if( ! ioctl( kbd, KIOCTYPE, &kbd_type ) ) + { + int kbd_layout = 0; + if( ! ioctl( kbd, KIOCLAYOUT, &kbd_layout ) ) + { + const keyboard_layout *p_layout = NULL; + switch( kbd_type ) + { + case KB_KLUNK: p_layout = type0_layout; break; + case KB_SUN3: p_layout = type3_layout; break; + case KB_SUN4: p_layout = type4_layout; break; + case KB_USB: p_layout = type6_layout; break; + case KB_PC: p_layout = type101_layout; break; + } + + if( p_layout ) + { + while( p_layout->n_layout != -1 ) + { + if ( p_layout->n_layout == kbd_layout ) + { + m_aKeyboardName = p_layout->p_description; + break; + } + p_layout++; + } + } + } + } + close(kbd); + } + } +#elif !defined(AIX) + int opcode, event, error; + int major = XkbMajorVersion, minor = XkbMinorVersion; + if( XkbQueryExtension( GetDisplay(), &opcode, &event,&error, &major, &minor ) ) + { + XkbDescPtr pXkbDesc = NULL; + // try X keyboard extension + if( (pXkbDesc = XkbGetKeyboard( GetDisplay(), XkbAllComponentsMask, XkbUseCoreKbd )) ) + { + const char* pAtom = NULL; + if( pXkbDesc->names->groups[0] ) + { + pAtom = XGetAtomName( GetDisplay(), pXkbDesc->names->groups[0] ); + m_aKeyboardName = pAtom; + XFree( (void*)pAtom ); + } + else + m_aKeyboardName = "<unknown keyboard>"; +#if OSL_DEBUG_LEVEL > 1 +#define PRINT_ATOM( x ) { if( pXkbDesc->names->x ) { pAtom = XGetAtomName( GetDisplay(), pXkbDesc->names->x ); fprintf( stderr, "%s: %s\n", #x, pAtom ); XFree( (void*)pAtom ); } else fprintf( stderr, "%s: <nil>\n", #x ); } + + PRINT_ATOM( keycodes ); + PRINT_ATOM( geometry ); + PRINT_ATOM( symbols ); + PRINT_ATOM( types ); + PRINT_ATOM( compat ); + PRINT_ATOM( phys_symbols ); + +#define PRINT_ATOM_2( x ) { if( pXkbDesc->names->x[i] ) { pAtom = XGetAtomName( GetDisplay(), pXkbDesc->names->x[i] ); fprintf( stderr, "%s[%d]: %s\n", #x, i, pAtom ); XFree( (void*)pAtom ); } else fprintf( stderr, "%s[%d]: <nil>\n", #x, i ); } + int i; + for( i = 0; i < XkbNumVirtualMods; i++ ) + PRINT_ATOM_2( vmods ); + for( i = 0; i < XkbNumIndicators; i++ ) + PRINT_ATOM_2( indicators ); + for( i = 0; i < XkbNumKbdGroups; i++ ) + PRINT_ATOM_2( groups ); +#endif + XkbFreeKeyboard( pXkbDesc, XkbAllComponentsMask, True ); + } + } +#endif + if( ! m_aKeyboardName.Len() ) + m_aKeyboardName = "<unknown keyboard>"; + } + return m_aKeyboardName.GetBuffer(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/app/randrwrapper.cxx b/vcl/unx/generic/app/randrwrapper.cxx new file mode 100644 index 000000000000..316bde14a83a --- /dev/null +++ b/vcl/unx/generic/app/randrwrapper.cxx @@ -0,0 +1,363 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifdef USE_RANDR + +#include <tools/prex.h> +#include <X11/extensions/Xrandr.h> +#include <tools/postx.h> + +#include "osl/module.h" +#include "rtl/ustring.hxx" + +namespace +{ + +# ifdef XRANDR_DLOPEN + +class RandRWrapper +{ + oslModule m_pRandRLib; + + // function pointers + Bool(*m_pXRRQueryExtension)(Display*,int*,int*); + Status(*m_pXRRQueryVersion)(Display*,int*,int*); + XRRScreenConfiguration*(*m_pXRRGetScreenInfo)(Display*,Drawable); + void(*m_pXRRFreeScreenConfigInfo)(XRRScreenConfiguration*); + void(*m_pXRRSelectInput)(Display*,XLIB_Window,int); + int(*m_pXRRUpdateConfiguration)(XEvent*); + XRRScreenSize*(*m_pXRRSizes)(Display*,int,int*); + XRRScreenSize*(*m_pXRRConfigSizes)(XRRScreenConfiguration*,int*); + SizeID(*m_pXRRConfigCurrentConfiguration)(XRRScreenConfiguration*,Rotation*); + int(*m_pXRRRootToScreen)(Display*, XLIB_Window); + + bool m_bValid; + + void initFromModule(); + + RandRWrapper(Display*); + ~RandRWrapper(); +public: + static RandRWrapper& get(Display*); + static void releaseWrapper(); + + Bool XRRQueryExtension(Display* i_pDisp, int* o_event_base, int* o_error_base ) + { + Bool bRet = False; + if( m_bValid ) + bRet = m_pXRRQueryExtension( i_pDisp, o_event_base, o_error_base ); + return bRet; + } + Status XRRQueryVersion( Display* i_pDisp, int* o_major, int* o_minor ) + { + return m_bValid ? m_pXRRQueryVersion( i_pDisp, o_major, o_minor ) : 0; + } + XRRScreenConfiguration* XRRGetScreenInfo( Display* i_pDisp, Drawable i_aDrawable ) + { + return m_bValid ? m_pXRRGetScreenInfo( i_pDisp, i_aDrawable ) : NULL; + } + void XRRFreeScreenConfigInfo( XRRScreenConfiguration* i_pConfig ) + { + if( m_bValid ) + m_pXRRFreeScreenConfigInfo( i_pConfig ); + } + void XRRSelectInput( Display* i_pDisp, XLIB_Window i_window, int i_nMask ) + { + if( m_bValid ) + m_pXRRSelectInput( i_pDisp, i_window, i_nMask ); + } + int XRRUpdateConfiguration( XEvent* i_pEvent ) + { + return m_bValid ? m_pXRRUpdateConfiguration( i_pEvent ) : 0; + } + XRRScreenSize* XRRSizes( Display* i_pDisp, int i_screen, int* o_nscreens ) + { + return m_bValid ? m_pXRRSizes( i_pDisp, i_screen, o_nscreens ) : NULL; + } + XRRScreenSize* XRRConfigSizes( XRRScreenConfiguration* i_pConfig, int* o_nSizes ) + { + return m_bValid ? m_pXRRConfigSizes( i_pConfig, o_nSizes ) : NULL; + } + SizeID XRRConfigCurrentConfiguration( XRRScreenConfiguration* i_pConfig, Rotation* o_pRot ) + { + return m_bValid ? m_pXRRConfigCurrentConfiguration( i_pConfig, o_pRot ) : 0; + } + int XRRRootToScreen( Display *dpy, XLIB_Window root ) + { + return m_bValid ? m_pXRRRootToScreen( dpy, root ) : -1; + } +}; + +void RandRWrapper::initFromModule() +{ + m_pXRRQueryExtension = (Bool(*)(Display*,int*,int*))osl_getAsciiFunctionSymbol( m_pRandRLib, "XRRQueryExtension" ); + m_pXRRQueryVersion = (Status(*)(Display*,int*,int*))osl_getAsciiFunctionSymbol( m_pRandRLib, "XRRQueryVersion" ); + m_pXRRGetScreenInfo = (XRRScreenConfiguration*(*)(Display*,Drawable))osl_getAsciiFunctionSymbol( m_pRandRLib, "XRRGetScreenInfo" ); + m_pXRRFreeScreenConfigInfo = (void(*)(XRRScreenConfiguration*))osl_getAsciiFunctionSymbol( m_pRandRLib, "XRRFreeScreenConfigInfo" ); + m_pXRRSelectInput = (void(*)(Display*,XLIB_Window,int))osl_getAsciiFunctionSymbol( m_pRandRLib, "XRRSelectInput" ); + m_pXRRUpdateConfiguration = (int(*)(XEvent*))osl_getAsciiFunctionSymbol( m_pRandRLib, "XRRUpdateConfiguration" ); + m_pXRRSizes = (XRRScreenSize*(*)(Display*,int,int*))osl_getAsciiFunctionSymbol( m_pRandRLib, "XRRSizes" ); + m_pXRRConfigSizes = (XRRScreenSize*(*)(XRRScreenConfiguration*,int*))osl_getAsciiFunctionSymbol( m_pRandRLib, "XRRConfigSizes" ); + m_pXRRConfigCurrentConfiguration = (SizeID(*)(XRRScreenConfiguration*,Rotation*))osl_getAsciiFunctionSymbol( m_pRandRLib, "XRRConfigCurrentConfiguration" ); + m_pXRRRootToScreen = (int(*)(Display*,XLIB_Window))osl_getAsciiFunctionSymbol( m_pRandRLib, "XRRRootToScreen" ); + + m_bValid = m_pXRRQueryExtension && + m_pXRRQueryVersion && + m_pXRRGetScreenInfo && + m_pXRRFreeScreenConfigInfo && + m_pXRRSelectInput && + m_pXRRUpdateConfiguration && + m_pXRRSizes && + m_pXRRConfigSizes && + m_pXRRConfigCurrentConfiguration && + m_pXRRRootToScreen + ; +} + +RandRWrapper::RandRWrapper( Display* pDisplay ) : + m_pRandRLib( NULL ), + m_pXRRQueryExtension( NULL ), + m_pXRRQueryVersion( NULL ), + m_pXRRGetScreenInfo( NULL ), + m_pXRRFreeScreenConfigInfo( NULL ), + m_pXRRSelectInput( NULL ), + m_pXRRUpdateConfiguration( NULL ), + m_pXRRSizes( NULL ), + m_pXRRConfigSizes( NULL ), + m_pXRRConfigCurrentConfiguration( NULL ), + m_pXRRRootToScreen( NULL ), + m_bValid( false ) +{ + // first try in process space (e.g. gtk links that ?) + initFromModule(); + if( ! m_bValid ) + { + rtl::OUString aLibName( RTL_CONSTASCII_USTRINGPARAM( "libXrandr.so.2" ) ); + // load and resolve dependencies immediately + // rationale: there are older distributions where libXrandr.so.2 is not linked + // with libXext.so, resulting in a missing symbol and terminating the office + // obviously they expected libXext to be linked in global symbolspace (that is + // linked by the application), which is not the case with us (because we want + // to be able to run in headless mode even without an installed X11 library) + m_pRandRLib = osl_loadModule( aLibName.pData, SAL_LOADMODULE_DEFAULT | SAL_LOADMODULE_NOW ); + initFromModule(); + } + if( m_bValid ) + { + int nEventBase = 0, nErrorBase = 0; + if( ! m_pXRRQueryExtension( pDisplay, &nEventBase, &nErrorBase ) ) + m_bValid = false; + } +} + +RandRWrapper::~RandRWrapper() +{ + if( m_pRandRLib ) + osl_unloadModule( m_pRandRLib ); +} + +static RandRWrapper* pWrapper = NULL; + +RandRWrapper& RandRWrapper::get( Display* i_pDisplay ) +{ + if( ! pWrapper ) + pWrapper = new RandRWrapper( i_pDisplay ); + return *pWrapper; +} + +void RandRWrapper::releaseWrapper() +{ + delete pWrapper; + pWrapper = NULL; +} + +# else + +class RandRWrapper +{ + bool m_bValid; + + RandRWrapper(Display*); +public: + static RandRWrapper& get(Display*); + static void releaseWrapper(); + + Bool XRRQueryExtension(Display* i_pDisp, int* o_event_base, int* o_error_base ) + { + Bool bRet = False; + if( m_bValid ) + bRet = ::XRRQueryExtension( i_pDisp, o_event_base, o_error_base ); + return bRet; + } + Status XRRQueryVersion( Display* i_pDisp, int* o_major, int* o_minor ) + { + return m_bValid ? ::XRRQueryVersion( i_pDisp, o_major, o_minor ) : 0; + } + XRRScreenConfiguration* XRRGetScreenInfo( Display* i_pDisp, Drawable i_aDrawable ) + { + return m_bValid ? ::XRRGetScreenInfo( i_pDisp, i_aDrawable ) : NULL; + } + void XRRFreeScreenConfigInfo( XRRScreenConfiguration* i_pConfig ) + { + if( m_bValid ) + ::XRRFreeScreenConfigInfo( i_pConfig ); + } + void XRRSelectInput( Display* i_pDisp, XLIB_Window i_window, int i_nMask ) + { + if( m_bValid ) + ::XRRSelectInput( i_pDisp, i_window, i_nMask ); + } + int XRRUpdateConfiguration( XEvent* i_pEvent ) + { + return m_bValid ? ::XRRUpdateConfiguration( i_pEvent ) : 0; + } + XRRScreenSize* XRRSizes( Display* i_pDisp, int i_screen, int* o_nscreens ) + { + return m_bValid ? ::XRRSizes( i_pDisp, i_screen, o_nscreens ) : NULL; + } + XRRScreenSize* XRRConfigSizes( XRRScreenConfiguration* i_pConfig, int* o_nSizes ) + { + return m_bValid ? ::XRRConfigSizes( i_pConfig, o_nSizes ) : NULL; + } + SizeID XRRConfigCurrentConfiguration( XRRScreenConfiguration* i_pConfig, Rotation* o_pRot ) + { + return m_bValid ? ::XRRConfigCurrentConfiguration( i_pConfig, o_pRot ) : 0; + } + int XRRRootToScreen( Display *dpy, XLIB_Window root ) + { + return m_bValid ? ::XRRRootToScreen( dpy, root ) : -1; + } +}; + +RandRWrapper::RandRWrapper( Display* pDisplay ) : + m_bValid( true ) +{ + int nEventBase = 0, nErrorBase = 0; + if( !XRRQueryExtension( pDisplay, &nEventBase, &nErrorBase ) ) + m_bValid = false; +} + +static RandRWrapper* pWrapper = NULL; + +RandRWrapper& RandRWrapper::get( Display* i_pDisplay ) +{ + if( ! pWrapper ) + pWrapper = new RandRWrapper( i_pDisplay ); + return *pWrapper; +} + +void RandRWrapper::releaseWrapper() +{ + delete pWrapper; + pWrapper = NULL; +} + +#endif + +} // namespace + +#endif + +#include "unx/saldisp.hxx" +#include "unx/salframe.h" +#if OSL_DEBUG_LEVEL > 1 +#include <cstdio> +#endif + +void SalDisplay::InitRandR( XLIB_Window aRoot ) const +{ + #ifdef USE_RANDR + if( m_bUseRandRWrapper ) + RandRWrapper::get( GetDisplay() ).XRRSelectInput( GetDisplay(), aRoot, RRScreenChangeNotifyMask ); + #else + (void)aRoot; + #endif +} + +void SalDisplay::DeInitRandR() +{ + #ifdef USE_RANDR + if( m_bUseRandRWrapper ) + RandRWrapper::releaseWrapper(); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "SalDisplay::DeInitRandR()\n" ); +#endif + #endif +} + +int SalDisplay::processRandREvent( XEvent* pEvent ) +{ + int nRet = 0; + #ifdef USE_RANDR + XConfigureEvent* pCnfEvent=(XConfigureEvent*)pEvent; + if( m_bUseRandRWrapper && pWrapper && pWrapper->XRRRootToScreen(GetDisplay(),pCnfEvent->window) != -1 ) + { + nRet = pWrapper->XRRUpdateConfiguration( pEvent ); + if( nRet == 1 && pEvent->type != ConfigureNotify) // this should then be a XRRScreenChangeNotifyEvent + { + // update screens + bool bNotify = false; + for( size_t i = 0; i < m_aScreens.size(); i++ ) + { + if( m_aScreens[i].m_bInit ) + { + XRRScreenConfiguration *pConfig = NULL; + XRRScreenSize *pSizes = NULL; + int nSizes = 0; + Rotation nRot = 0; + SizeID nId = 0; + + pConfig = pWrapper->XRRGetScreenInfo( GetDisplay(), m_aScreens[i].m_aRoot ); + nId = pWrapper->XRRConfigCurrentConfiguration( pConfig, &nRot ); + pSizes = pWrapper->XRRConfigSizes( pConfig, &nSizes ); + XRRScreenSize *pTargetSize = pSizes + nId; + + bNotify = bNotify || + m_aScreens[i].m_aSize.Width() != pTargetSize->width || + m_aScreens[i].m_aSize.Height() != pTargetSize->height; + + m_aScreens[i].m_aSize = Size( pTargetSize->width, pTargetSize->height ); + + pWrapper->XRRFreeScreenConfigInfo( pConfig ); + + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "screen %d changed to size %dx%d\n", (int)i, (int)pTargetSize->width, (int)pTargetSize->height ); + #endif + } + } + if( bNotify && ! m_aFrames.empty() ) + m_aFrames.front()->CallCallback( SALEVENT_DISPLAYCHANGED, 0 ); + } + } + #else + (void)pEvent; + #endif + return nRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/app/saldata.cxx b/vcl/unx/generic/app/saldata.cxx new file mode 100644 index 000000000000..233582c07f6e --- /dev/null +++ b/vcl/unx/generic/app/saldata.cxx @@ -0,0 +1,875 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#ifdef USE_XTOOLKIT +# define SAL_XT +#endif + +// -=-= #includes =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +#include <unistd.h> +#include <fcntl.h> + +#include <cstdio> +#include <cstring> +#include <cstdlib> +#include <stdio.h> // snprintf, seems not to be in namespace std on every platform +#include <limits.h> +#include <errno.h> +#include <pthread.h> +#include <sys/resource.h> +#ifdef SUN +#include <sys/systeminfo.h> +#endif +#ifdef AIX +#include <strings.h> +#endif +#ifdef FREEBSD +#include <sys/types.h> +#include <sys/time.h> +#include <unistd.h> +#endif + +#include <osl/process.h> +#include <osl/mutex.hxx> + +#include "unx/Xproto.h" +#include "unx/saldisp.hxx" +#include "unx/saldata.hxx" +#include "unx/salframe.h" +#include "unx/sm.hxx" +#include "unx/i18n_im.hxx" +#include "unx/i18n_xkb.hxx" +#include "salinst.hxx" + +#include <osl/signal.h> +#include <osl/thread.h> +#include <rtl/strbuf.hxx> +#include <rtl/bootstrap.hxx> + +#include <tools/debug.hxx> +#include <vcl/svapp.hxx> + +// -=-= <signal.h> -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +#ifndef UNX +#ifndef SIGBUS +#define SIGBUS 10 +#endif +#ifndef SIGSEGV +#define SIGSEGV 11 +#endif +#ifndef SIGIOT +#define SIGIOT SIGABRT +#endif +#endif + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +static const struct timeval noyield__ = { 0, 0 }; +static const struct timeval yield__ = { 0, 10000 }; + +static const char* XRequest[] = { + // see /usr/lib/X11/XErrorDB, /usr/openwin/lib/XErrorDB ... + NULL, + "X_CreateWindow", + "X_ChangeWindowAttributes", + "X_GetWindowAttributes", + "X_DestroyWindow", + "X_DestroySubwindows", + "X_ChangeSaveSet", + "X_ReparentWindow", + "X_MapWindow", + "X_MapSubwindows", + "X_UnmapWindow", + "X_UnmapSubwindows", + "X_ConfigureWindow", + "X_CirculateWindow", + "X_GetGeometry", + "X_QueryTree", + "X_InternAtom", + "X_GetAtomName", + "X_ChangeProperty", + "X_DeleteProperty", + "X_GetProperty", + "X_ListProperties", + "X_SetSelectionOwner", + "X_GetSelectionOwner", + "X_ConvertSelection", + "X_SendEvent", + "X_GrabPointer", + "X_UngrabPointer", + "X_GrabButton", + "X_UngrabButton", + "X_ChangeActivePointerGrab", + "X_GrabKeyboard", + "X_UngrabKeyboard", + "X_GrabKey", + "X_UngrabKey", + "X_AllowEvents", + "X_GrabServer", + "X_UngrabServer", + "X_QueryPointer", + "X_GetMotionEvents", + "X_TranslateCoords", + "X_WarpPointer", + "X_SetInputFocus", + "X_GetInputFocus", + "X_QueryKeymap", + "X_OpenFont", + "X_CloseFont", + "X_QueryFont", + "X_QueryTextExtents", + "X_ListFonts", + "X_ListFontsWithInfo", + "X_SetFontPath", + "X_GetFontPath", + "X_CreatePixmap", + "X_FreePixmap", + "X_CreateGC", + "X_ChangeGC", + "X_CopyGC", + "X_SetDashes", + "X_SetClipRectangles", + "X_FreeGC", + "X_ClearArea", + "X_CopyArea", + "X_CopyPlane", + "X_PolyPoint", + "X_PolyLine", + "X_PolySegment", + "X_PolyRectangle", + "X_PolyArc", + "X_FillPoly", + "X_PolyFillRectangle", + "X_PolyFillArc", + "X_PutImage", + "X_GetImage", + "X_PolyText8", + "X_PolyText16", + "X_ImageText8", + "X_ImageText16", + "X_CreateColormap", + "X_FreeColormap", + "X_CopyColormapAndFree", + "X_InstallColormap", + "X_UninstallColormap", + "X_ListInstalledColormaps", + "X_AllocColor", + "X_AllocNamedColor", + "X_AllocColorCells", + "X_AllocColorPlanes", + "X_FreeColors", + "X_StoreColors", + "X_StoreNamedColor", + "X_QueryColors", + "X_LookupColor", + "X_CreateCursor", + "X_CreateGlyphCursor", + "X_FreeCursor", + "X_RecolorCursor", + "X_QueryBestSize", + "X_QueryExtension", + "X_ListExtensions", + "X_ChangeKeyboardMapping", + "X_GetKeyboardMapping", + "X_ChangeKeyboardControl", + "X_GetKeyboardControl", + "X_Bell", + "X_ChangePointerControl", + "X_GetPointerControl", + "X_SetScreenSaver", + "X_GetScreenSaver", + "X_ChangeHosts", + "X_ListHosts", + "X_SetAccessControl", + "X_SetCloseDownMode", + "X_KillClient", + "X_RotateProperties", + "X_ForceScreenSaver", + "X_SetPointerMapping", + "X_GetPointerMapping", + "X_SetModifierMapping", + "X_GetModifierMapping", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "X_NoOperation" +}; + +// -=-= C statics =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +int X11SalData::XErrorHdl( Display *pDisplay, XErrorEvent *pEvent ) +{ + GetX11SalData()->XError( pDisplay, pEvent ); + return 0; +} + +int X11SalData::XIOErrorHdl( Display * ) +{ + /* #106197# hack: until a real shutdown procedure exists + * _exit ASAP + */ + if( ImplGetSVData()->maAppData.mbAppQuit ) + _exit(1); + + // really bad hack + if( ! SessionManagerClient::checkDocumentsSaved() ) + /* oslSignalAction eToDo = */ osl_raiseSignal (OSL_SIGNAL_USER_X11SUBSYSTEMERROR, NULL); + + std::fprintf( stderr, "X IO Error\n" ); + std::fflush( stdout ); + std::fflush( stderr ); + + /* #106197# the same reasons to use _exit instead of exit in salmain + * do apply here. Since there is nothing to be done after an XIO + * error we have to _exit immediately. + */ + _exit(0); + return 0; +} + +// -=-= SalData =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +#include <pthread.h> + +X11SalData::X11SalData() +{ + bNoExceptions_ = !!getenv( "SAL_NOSEGV" ); + + pXLib_ = NULL; + m_pSalDisplay = NULL; + m_pInstance = NULL; + m_pPlugin = NULL; + + hMainThread_ = pthread_self(); +} + +const rtl::OUString& X11SalData::GetLocalHostName() +{ + if (!maLocalHostName.getLength()) + osl_getLocalHostname( &maLocalHostName.pData ); + return maLocalHostName; +} + +X11SalData::~X11SalData() +{ + DeleteDisplay(); +} + +void X11SalData::DeleteDisplay() +{ + delete m_pSalDisplay; + m_pSalDisplay = NULL; + delete pXLib_; + pXLib_ = NULL; +} + +void X11SalData::Init() +{ + pXLib_ = new SalXLib(); + pXLib_->Init(); +} + +void X11SalData::initNWF( void ) +{ +} + +void X11SalData::deInitNWF( void ) +{ +} + +// -=-= SalXLib =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +SalXLib::SalXLib() +{ + m_aTimeout.tv_sec = 0; + m_aTimeout.tv_usec = 0; + m_nTimeoutMS = 0; + + nFDs_ = 0; + FD_ZERO( &aReadFDS_ ); + FD_ZERO( &aExceptionFDS_ ); + + m_pTimeoutFDS[0] = m_pTimeoutFDS[1] = -1; + if (pipe (m_pTimeoutFDS) != -1) + { + // initialize 'wakeup' pipe. + int flags; + + // set close-on-exec descriptor flag. + if ((flags = fcntl (m_pTimeoutFDS[0], F_GETFD)) != -1) + { + flags |= FD_CLOEXEC; + fcntl (m_pTimeoutFDS[0], F_SETFD, flags); + } + if ((flags = fcntl (m_pTimeoutFDS[1], F_GETFD)) != -1) + { + flags |= FD_CLOEXEC; + fcntl (m_pTimeoutFDS[1], F_SETFD, flags); + } + + // set non-blocking I/O flag. + if ((flags = fcntl (m_pTimeoutFDS[0], F_GETFL)) != -1) + { + flags |= O_NONBLOCK; + fcntl (m_pTimeoutFDS[0], F_SETFL, flags); + } + if ((flags = fcntl (m_pTimeoutFDS[1], F_GETFL)) != -1) + { + flags |= O_NONBLOCK; + fcntl (m_pTimeoutFDS[1], F_SETFL, flags); + } + + // insert [0] into read descriptor set. + FD_SET( m_pTimeoutFDS[0], &aReadFDS_ ); + nFDs_ = m_pTimeoutFDS[0] + 1; + } + + m_bHaveSystemChildFrames = false; + m_aOrigXIOErrorHandler = XSetIOErrorHandler ( (XIOErrorHandler)X11SalData::XIOErrorHdl ); + PushXErrorLevel( !!getenv( "SAL_IGNOREXERRORS" ) ); +} + +SalXLib::~SalXLib() +{ + // close 'wakeup' pipe. + close (m_pTimeoutFDS[0]); + close (m_pTimeoutFDS[1]); + + PopXErrorLevel(); + XSetIOErrorHandler (m_aOrigXIOErrorHandler); +} + +void SalXLib::PushXErrorLevel( bool bIgnore ) +{ + m_aXErrorHandlerStack.push_back( XErrorStackEntry() ); + XErrorStackEntry& rEnt = m_aXErrorHandlerStack.back(); + rEnt.m_bWas = false; + rEnt.m_bIgnore = bIgnore; + rEnt.m_nLastErrorRequest = 0; + rEnt.m_aHandler = XSetErrorHandler( (XErrorHandler)X11SalData::XErrorHdl ); +} + +void SalXLib::PopXErrorLevel() +{ + if( m_aXErrorHandlerStack.size() ) + { + XSetErrorHandler( m_aXErrorHandlerStack.back().m_aHandler ); + m_aXErrorHandlerStack.pop_back(); + } +} + +void SalXLib::Init() +{ + SalI18N_InputMethod* pInputMethod = new SalI18N_InputMethod; + pInputMethod->SetLocale(); + XrmInitialize(); + + /* + * open connection to X11 Display + * try in this order: + * o -display command line parameter, + * o $DISPLAY environment variable + * o default display + */ + + Display *pDisp = NULL; + + // is there a -display command line parameter? + + sal_uInt32 nParams = osl_getCommandArgCount(); + rtl::OUString aParam; + rtl::OString aDisplay; + for (sal_uInt16 i=0; i<nParams; i++) + { + osl_getCommandArg(i, &aParam.pData); + if (aParam.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("-display"))) + { + osl_getCommandArg(i+1, &aParam.pData); + aDisplay = rtl::OUStringToOString( + aParam, osl_getThreadTextEncoding()); + + if ((pDisp = XOpenDisplay(aDisplay.getStr()))!=NULL) + { + /* + * if a -display switch was used, we need + * to set the environment accoringly since + * the clipboard build another connection + * to the xserver using $DISPLAY + */ + rtl::OUString envVar(RTL_CONSTASCII_USTRINGPARAM("DISPLAY")); + osl_setEnvironment(envVar.pData, aParam.pData); + } + break; + } + } + + if (!pDisp && !aDisplay.getLength()) + { + // Open $DISPLAY or default... + char *pDisplay = getenv("DISPLAY"); + if (pDisplay != NULL) + aDisplay = rtl::OString(pDisplay); + pDisp = XOpenDisplay(pDisplay); + } + + if ( !pDisp ) + { + rtl::OUString aProgramFileURL; + osl_getExecutableFile( &aProgramFileURL.pData ); + rtl::OUString aProgramSystemPath; + osl_getSystemPathFromFileURL (aProgramFileURL.pData, &aProgramSystemPath.pData); + rtl::OString aProgramName = rtl::OUStringToOString( + aProgramSystemPath, + osl_getThreadTextEncoding() ); + std::fprintf( stderr, "%s X11 error: Can't open display: %s\n", + aProgramName.getStr(), aDisplay.getStr()); + std::fprintf( stderr, " Set DISPLAY environment variable, use -display option\n"); + std::fprintf( stderr, " or check permissions of your X-Server\n"); + std::fprintf( stderr, " (See \"man X\" resp. \"man xhost\" for details)\n"); + std::fflush( stderr ); + exit(0); + } + + SalDisplay *pSalDisplay = new SalX11Display( pDisp ); + + pInputMethod->CreateMethod( pDisp ); + pInputMethod->AddConnectionWatch( pDisp, (void*)this ); + pSalDisplay->SetInputMethod( pInputMethod ); + + PushXErrorLevel( true ); + SalI18N_KeyboardExtension *pKbdExtension = new SalI18N_KeyboardExtension( pDisp ); + XSync( pDisp, False ); + + pKbdExtension->UseExtension( ! HasXErrorOccurred() ); + PopXErrorLevel(); + + pSalDisplay->SetKbdExtension( pKbdExtension ); +} + +extern "C" { +void EmitFontpathWarning( void ) +{ + static Bool bOnce = False; + if ( !bOnce ) + { + bOnce = True; + std::fprintf( stderr, "Please verify your fontpath settings\n" + "\t(See \"man xset\" for details" + " or ask your system administrator)\n" ); + } +} + +} /* extern "C" */ + +static void PrintXError( Display *pDisplay, XErrorEvent *pEvent ) +{ + char msg[ 120 ] = ""; +#if ! ( defined LINUX && defined PPC ) + XGetErrorText( pDisplay, pEvent->error_code, msg, sizeof( msg ) ); +#endif + std::fprintf( stderr, "X-Error: %s\n", msg ); + if( pEvent->request_code < capacityof( XRequest ) ) + { + const char* pName = XRequest[pEvent->request_code]; + if( !pName ) + pName = "BadRequest?"; + std::fprintf( stderr, "\tMajor opcode: %d (%s)\n", pEvent->request_code, pName ); + } + else + { + std::fprintf( stderr, "\tMajor opcode: %d\n", pEvent->request_code ); + // TODO: also display extension name? + std::fprintf( stderr, "\tMinor opcode: %d\n", pEvent->minor_code ); + } + + std::fprintf( stderr, "\tResource ID: 0x%lx\n", + pEvent->resourceid ); + std::fprintf( stderr, "\tSerial No: %ld (%ld)\n", + pEvent->serial, LastKnownRequestProcessed(pDisplay) ); + + if( !getenv( "SAL_SYNCHRONIZE" ) ) + { + std::fprintf( stderr, "These errors are reported asynchronously,\n"); + std::fprintf( stderr, "set environment variable SAL_SYNCHRONIZE to 1 to help debugging\n"); + } + + std::fflush( stdout ); + std::fflush( stderr ); +} + +void SalXLib::XError( Display *pDisplay, XErrorEvent *pEvent ) +{ + if( m_bHaveSystemChildFrames ) + return; + + if( ! m_aXErrorHandlerStack.back().m_bIgnore ) + { + if ( (pEvent->error_code == BadAlloc) + && (pEvent->request_code == X_OpenFont) ) + { + static Bool bOnce = False; + if ( !bOnce ) + { + std::fprintf(stderr, "X-Error occurred in a request for X_OpenFont\n"); + EmitFontpathWarning(); + + bOnce = True ; + } + return; + } + /* ignore + * X_SetInputFocus: it's a hint only anyway + * X_GetProperty: this is part of the XGetWindowProperty call and will + * be handled by the return value of that function + */ + else if( pEvent->request_code == X_SetInputFocus || + pEvent->request_code == X_GetProperty + ) + return; + + + if( pDisplay != GetX11SalData()->GetDisplay()->GetDisplay() ) + return; + + PrintXError( pDisplay, pEvent ); + + oslSignalAction eToDo = osl_raiseSignal (OSL_SIGNAL_USER_X11SUBSYSTEMERROR, NULL); + switch (eToDo) + { + case osl_Signal_ActIgnore : + return; + case osl_Signal_ActAbortApp : + abort(); + case osl_Signal_ActKillApp : + exit(0); + case osl_Signal_ActCallNextHdl : + break; + default : + break; + } + + } + + m_aXErrorHandlerStack.back().m_bWas = true; +} + +struct YieldEntry +{ + YieldEntry* next; // pointer to next entry + int fd; // file descriptor for reading + void* data; // data for predicate and callback + YieldFunc pending; // predicate (determins pending events) + YieldFunc queued; // read and queue up events + YieldFunc handle; // handle pending events + + inline int HasPendingEvent() const { return pending( fd, data ); } + inline int IsEventQueued() const { return queued( fd, data ); } + inline void HandleNextEvent() const { handle( fd, data ); } +}; + +#define MAX_NUM_DESCRIPTORS 128 + +static YieldEntry yieldTable[ MAX_NUM_DESCRIPTORS ]; + +void SalXLib::Insert( int nFD, void* data, + YieldFunc pending, + YieldFunc queued, + YieldFunc handle ) +{ + DBG_ASSERT( nFD, "can not insert stdin descriptor" ); + DBG_ASSERT( !yieldTable[nFD].fd, "SalXLib::Insert fd twice" ); + + yieldTable[nFD].fd = nFD; + yieldTable[nFD].data = data; + yieldTable[nFD].pending = pending; + yieldTable[nFD].queued = queued; + yieldTable[nFD].handle = handle; + + FD_SET( nFD, &aReadFDS_ ); + FD_SET( nFD, &aExceptionFDS_ ); + + if( nFD >= nFDs_ ) + nFDs_ = nFD + 1; +} + +void SalXLib::Remove( int nFD ) +{ + FD_CLR( nFD, &aReadFDS_ ); + FD_CLR( nFD, &aExceptionFDS_ ); + + yieldTable[nFD].fd = 0; + + if ( nFD == nFDs_ ) + { + for ( nFD = nFDs_ - 1; + nFD >= 0 && !yieldTable[nFD].fd; + nFD-- ) ; + + nFDs_ = nFD + 1; + } +} + +bool SalXLib::CheckTimeout( bool bExecuteTimers ) +{ + bool bRet = false; + if( m_aTimeout.tv_sec ) // timer is started + { + timeval aTimeOfDay; + gettimeofday( &aTimeOfDay, 0 ); + if( aTimeOfDay >= m_aTimeout ) + { + bRet = true; + if( bExecuteTimers ) + { + // timed out, update timeout + m_aTimeout = aTimeOfDay; + /* + * #107827# autorestart immediately, will be stopped (or set + * to different value in notify hdl if necessary; + * CheckTimeout should return false while + * timers are being dispatched. + */ + m_aTimeout += m_nTimeoutMS; + // notify + GetX11SalData()->Timeout(); + } + } + } + return bRet; +} + +void SalXLib::Yield( bool bWait, bool bHandleAllCurrentEvents ) +{ + // check for timeouts here if you want to make screenshots + static char* p_prioritize_timer = getenv ("SAL_HIGHPRIORITY_REPAINT"); + if (p_prioritize_timer != NULL) + CheckTimeout(); + + // first, check for already queued events. + for ( int nFD = 0; nFD < nFDs_; nFD++ ) + { + YieldEntry* pEntry = &(yieldTable[nFD]); + if ( pEntry->fd ) + { + DBG_ASSERT( nFD == pEntry->fd, "wrong fd in Yield()" ); + if ( pEntry->HasPendingEvent() ) + { + pEntry->HandleNextEvent(); + // #63862# da jetzt alle user-events ueber die interne + // queue kommen, wird die Kontrolle analog zum select + // gesteuerten Zweig einmal bei bWait abgegeben + + /* #i9277# do not reschedule since performance gets down the + the drain under heavy load + YieldMutexReleaser aReleaser; + if ( bWait ) osl_yieldThread(); + */ + + return; + } + } + } + + // next, select with or without timeout according to bWait. + int nFDs = nFDs_; + fd_set ReadFDS = aReadFDS_; + fd_set ExceptionFDS = aExceptionFDS_; + int nFound = 0; + + timeval Timeout = noyield__; + timeval *pTimeout = &Timeout; + + if (bWait) + { + pTimeout = 0; + if (m_aTimeout.tv_sec) // Timer is started. + { + // determine remaining timeout. + gettimeofday (&Timeout, 0); + Timeout = m_aTimeout - Timeout; + if (yield__ >= Timeout) + { + // guard against micro timeout. + Timeout = yield__; + } + pTimeout = &Timeout; + } + } + + { + // release YieldMutex (and re-acquire at block end) + YieldMutexReleaser aReleaser; + nFound = select( nFDs, &ReadFDS, NULL, &ExceptionFDS, pTimeout ); + } + if( nFound < 0 ) // error + { +#ifdef DBG_UTIL + std::fprintf( stderr, "SalXLib::Yield e=%d f=%d\n", errno, nFound ); +#endif + if( EINTR == errno ) + { + errno = 0; + } + } + + // usually handle timeouts here (as in 5.2) + if (p_prioritize_timer == NULL) + CheckTimeout(); + + // handle wakeup events. + if ((nFound > 0) && (FD_ISSET(m_pTimeoutFDS[0], &ReadFDS))) + { + int buffer; + while (read (m_pTimeoutFDS[0], &buffer, sizeof(buffer)) > 0) + continue; + nFound -= 1; + } + + // handle other events. + if( nFound > 0 ) + { + // now we are in the protected section ! + // recall select if we have acquired fd's, ready for reading, + + struct timeval noTimeout = { 0, 0 }; + nFound = select( nFDs_, &ReadFDS, NULL, + &ExceptionFDS, &noTimeout ); + + // someone-else has done the job for us + if (nFound == 0) + return; + + for ( int nFD = 0; nFD < nFDs_; nFD++ ) + { + YieldEntry* pEntry = &(yieldTable[nFD]); + if ( pEntry->fd ) + { + if ( FD_ISSET( nFD, &ExceptionFDS ) ) { +#if OSL_DEBUG_LEVEL > 1 + std::fprintf( stderr, "SalXLib::Yield exception\n" ); +#endif + nFound--; + } + if ( FD_ISSET( nFD, &ReadFDS ) ) + { + int nMaxEvents = bHandleAllCurrentEvents ? 100 : 1; + for( int i = 0; pEntry->IsEventQueued() && i < nMaxEvents; i++ ) + { + pEntry->HandleNextEvent(); + // if a recursive call has done the job + // so abort here + } + nFound--; + } + } + } + } +} + +void SalXLib::Wakeup() +{ + write (m_pTimeoutFDS[1], "", 1); +} + +void SalXLib::PostUserEvent() +{ + Wakeup(); +} + +const char* X11SalData::getFrameResName() +{ + /* according to ICCCM: + * first search command line for -name parameter + * then try RESOURCE_NAME environment variable + * then use argv[0] stripped by directories + */ + static rtl::OStringBuffer aResName; + if( !aResName.getLength() ) + { + int nArgs = osl_getCommandArgCount(); + for( int n = 0; n < nArgs-1; n++ ) + { + rtl::OUString aArg; + if( ! osl_getCommandArg( n, &aArg.pData ) && + aArg.equalsIgnoreAsciiCaseAscii( "-name" ) && + ! osl_getCommandArg( n+1, &aArg.pData ) ) + { + aResName.append( rtl::OUStringToOString( aArg, osl_getThreadTextEncoding() ) ); + break; + } + } + if( !aResName.getLength() ) + { + const char* pEnv = getenv( "RESOURCE_NAME" ); + if( pEnv && *pEnv ) + aResName.append( pEnv ); + } + if( !aResName.getLength() ) + aResName.append( "VCLSalFrame" ); + } + return aResName.getStr(); +} + +const char* X11SalData::getFrameClassName() +{ + static rtl::OStringBuffer aClassName; + if( !aClassName.getLength() ) + { + rtl::OUString aIni, aProduct; + rtl::Bootstrap::get( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "BRAND_BASE_DIR" ) ), aIni ); + aIni += ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/program/" SAL_CONFIGFILE( "bootstrap" ) ) ); + rtl::Bootstrap aBootstrap( aIni ); + aBootstrap.getFrom( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ProductKey" ) ), aProduct ); + + if( aProduct.getLength() ) + aClassName.append( rtl::OUStringToOString( aProduct, osl_getThreadTextEncoding() ) ); + else + aClassName.append( "VCLSalFrame" ); + } + return aClassName.getStr(); +} + +rtl::OString X11SalData::getFrameResName( SalExtStyle nStyle ) +{ + rtl::OStringBuffer aBuf( 64 ); + aBuf.append( getFrameResName() ); + if( (nStyle & SAL_FRAME_EXT_STYLE_DOCUMENT) ) + aBuf.append( ".DocumentWindow" ); + + return aBuf.makeStringAndClear(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/app/saldisp.cxx b/vcl/unx/generic/app/saldisp.cxx new file mode 100644 index 000000000000..5f94db2f1b37 --- /dev/null +++ b/vcl/unx/generic/app/saldisp.cxx @@ -0,0 +1,3535 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#define SAL_XT + +// -=-= #includes =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <sys/time.h> +#include <pthread.h> +#include <unistd.h> +#include <ctype.h> +#include <string.h> + +#if defined(SOLARIS) || defined(AIX) +#include <sal/alloca.h> +#include <osl/module.h> +#endif + +#include <tools/prex.h> +#include <X11/cursorfont.h> +#include "unx/x11_cursors/salcursors.h" +#include "unx/x11_cursors/invert50.h" +#ifdef SOLARIS +#define XK_KOREAN +#endif +#include <X11/keysym.h> +#include <X11/XKBlib.h> +#include <X11/Xatom.h> + +#ifdef USE_XINERAMA +#ifdef USE_XINERAMA_XORG +#include <X11/extensions/Xinerama.h> +#elif defined USE_XINERAMA_XSUN +#if defined(SOLARIS) && defined(INTEL) // missing extension header in standard installation +#define MAXFRAMEBUFFERS 16 +Bool XineramaGetState(Display*, int); +Status XineramaGetInfo(Display*, int, XRectangle*, unsigned char*, int*); +#else +#include <X11/extensions/xinerama.h> +#endif +#else +#error USE_XINERAMA but no xinerama version +#endif +#endif + +#include <tools/postx.h> + +#include <unx/salunx.h> +#include <sal/types.h> +#include "unx/i18n_im.hxx" +#include "unx/i18n_xkb.hxx" +#include <unx/saldisp.hxx> +#include <unx/saldata.hxx> +#include <salinst.hxx> +#include <unx/salgdi.h> +#include <unx/salframe.h> +#include <vcl/keycodes.hxx> +#include <vcl/salbtype.hxx> +#include <unx/salbmp.h> +#include <osl/mutex.h> +#include <unx/salobj.h> +#include <unx/sm.hxx> +#include <unx/wmadaptor.hxx> +#include <unx/dtint.hxx> + +#include <osl/socket.h> +#include <poll.h> + +using namespace vcl_sal; + +using ::rtl::OUString; + +// -=-= #defines -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +#define PSEUDOCOLOR12 +#define PSEUDOCOLOR8 +#define TRUECOLOR24 +#define TRUECOLOR16 +#define TRUECOLOR15 +#define TRUECOLOR12 +#define TRUECOLOR8 + +#define SALCOLOR_WHITE MAKE_SALCOLOR( 0xFF, 0xFF, 0xFF ) +#define SALCOLOR_BLACK MAKE_SALCOLOR( 0x00, 0x00, 0x00 ) + +// -=-= Prototyps =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-= static variables -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +static const char* const VisualClassName[] = { + "StaticGray", + "GrayScale", + "StaticColor", + "PseudoColor", + "TrueColor", + "DirectColor" +}; + +static const char* const EventNames[] = +{ + NULL, + NULL, + "KeyPress", + "KeyRelease", + "ButtonPress", + "ButtonRelease", + "MotionNotify", + "EnterNotify", + "LeaveNotify", + "FocusIn", + "FocusOut", + "KeymapNotify", + "Expose", + "GraphicsExpose", + "NoExpose", + "VisibilityNotify", + "CreateNotify", + "DestroyNotify", + "UnmapNotify", + "MapNotify", + "MapRequest", + "ReparentNotify", + "ConfigureNotify", + "ConfigureRequest", + "GravityNotify", + "ResizeRequest", + "CirculateNotify", + "CirculateRequest", + "PropertyNotify", + "SelectionClear", + "SelectionRequest", + "SelectionNotify", + "ColormapNotify", + "ClientMessage", + "MappingNotify" +}; + +// -=-= global inline =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +inline const char *Null( const char *p ) { return p ? p : ""; } +inline const char *GetEnv( const char *p ) { return Null( getenv( p ) ); } +inline const char *KeyStr( KeySym n ) { return Null( XKeysymToString( n ) ); } + +inline const char *GetAtomName( Display *d, Atom a ) +{ return Null( XGetAtomName( d, a ) ); } + +inline double Hypothenuse( long w, long h ) +{ return sqrt( (double)((w*w)+(h*h)) ); } + +inline int ColorDiff( int r, int g, int b ) +{ return (r*r)+(g*g)+(b*b); } + +inline int ColorDiff( SalColor c1, int r, int g, int b ) +{ return ColorDiff( (int)SALCOLOR_RED (c1)-r, + (int)SALCOLOR_GREEN(c1)-g, + (int)SALCOLOR_BLUE (c1)-b ); } + +// -=-= global functions -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +static int sal_Shift( Pixel nMask ) +{ + int i = 24; + if( nMask < 0x00010000 ) { nMask <<= 16; i -= 16; } + if( nMask < 0x01000000 ) { nMask <<= 8; i -= 8; } + if( nMask < 0x10000000 ) { nMask <<= 4; i -= 4; } + if( nMask < 0x40000000 ) { nMask <<= 2; i -= 2; } + if( nMask < 0x80000000 ) { nMask <<= 1; i -= 1; } + return i; +} + +static int sal_significantBits( Pixel nMask ) +{ + int nRotate = sizeof(Pixel)*4; + int nBits = 0; + while( nRotate-- ) + { + if( nMask & 1 ) + nBits++; + nMask >>= 1; + } + return nBits; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +static sal_Bool sal_GetVisualInfo( Display *pDisplay, XID nVID, XVisualInfo &rVI ) +{ + int nInfos; + XVisualInfo aTemplate; + XVisualInfo*pInfos; + + aTemplate.visualid = nVID; + + pInfos = XGetVisualInfo( pDisplay, VisualIDMask, &aTemplate, &nInfos ); + if( !pInfos ) + return sal_False; + + rVI = *pInfos; + XFree( pInfos ); + + DBG_ASSERT( rVI.visualid == nVID, + "sal_GetVisualInfo: could not get correct visual by visualId" ); + return sal_True; +} + +// --------------------------------------------------------------------------- + +// check wether displaystring is in format N.M or N. or just N +// with N and M beeing natural numbers +static sal_Bool +sal_IsDisplayNumber( const char *pDisplayString ) +{ + if ( ! isdigit(*pDisplayString) ) + return sal_False; + while ( isdigit(*(++pDisplayString)) ) + ; /* do nothing */ + + if ( *pDisplayString == '.' ) + { + while ( isdigit(*(++pDisplayString)) ) + ; /* do nothing */ + } + + return (*pDisplayString == '\0'); +} + +// check whether host1 and host2 point to the same ip address +static sal_Bool +sal_EqualHosts( const OUString& Host1, const OUString& Host2) +{ + oslSocketAddr pHostAddr1; + oslSocketAddr pHostAddr2; + sal_Bool bEqualAddress = sal_False; + + if ( Host1.toChar() >= '0' && Host1.toChar() <= '9' ) + pHostAddr1 = osl_createInetSocketAddr( Host1.pData, 0 ); + else + pHostAddr1 = osl_resolveHostname( Host1.pData ); + + if ( Host2.toChar() >= '0' && Host2.toChar() <= '9' ) + pHostAddr2 = osl_createInetSocketAddr( Host2.pData, 0 ); + else + pHostAddr2 = osl_resolveHostname( Host2.pData ); + + if( pHostAddr1 && pHostAddr2 ) + bEqualAddress = osl_isEqualSocketAddr( pHostAddr1, pHostAddr2 ) ? sal_True : sal_False; + + if( pHostAddr1 ) + osl_destroySocketAddr( pHostAddr1 ); + if( pHostAddr2 ) + osl_destroySocketAddr( pHostAddr2 ); + + return bEqualAddress; +} + +static sal_Bool +sal_IsLocalDisplay( Display *pDisplay ) +{ + const char *pDisplayString = DisplayString( pDisplay ); + + // no string, no idea + if ( pDisplayString == NULL || pDisplayString[ 0 ] == '\0') + return sal_False; + + // check for ":x.y" + if ( pDisplayString[ 0 ] == ':' ) + return sal_IsDisplayNumber( pDisplayString + 1 ); + + // check for fixed token which all mean localhost:x.y + const char pLocal[] = "localhost:"; + const int nLocalLen = sizeof(pLocal) - 1; + if ( strncmp(pDisplayString, pLocal, nLocalLen) == 0 ) + return sal_IsDisplayNumber( pDisplayString + nLocalLen ); + + const char pUnix[] = "unix:"; + const int nUnixLen = sizeof(pUnix) - 1; + if ( strncmp(pDisplayString, pUnix, nUnixLen) == 0 ) + return sal_IsDisplayNumber( pDisplayString + nUnixLen ); + + const char pLoopback[] = "127.0.0.1:"; + const int nLoopbackLen= sizeof(pLoopback) - 1; + if ( strncmp(pDisplayString, pLoopback, nLoopbackLen) == 0 ) + return sal_IsDisplayNumber( pDisplayString + nLoopbackLen ); + + // compare local hostname to displaystring, both may be ip address or + // hostname + sal_Bool bEqual = sal_False; + char *pDisplayHost = strdup( pDisplayString ); + char *pPtr = strrchr( pDisplayHost, ':' ); + + if( pPtr != NULL ) + { + const OUString& rLocalHostname( GetX11SalData()->GetLocalHostName() ); + if( rLocalHostname.getLength() ) + { + *pPtr = '\0'; + OUString aDisplayHostname( pDisplayHost, strlen( pDisplayHost ), osl_getThreadTextEncoding() ); + bEqual = sal_EqualHosts( rLocalHostname, aDisplayHostname ); + bEqual = bEqual && sal_IsDisplayNumber( pPtr + 1 ); + } + } + free( pDisplayHost ); + + return bEqual; +} + +// --------------------------------------------------------------------------- +// IsLocal means soffice is running on the same host as the xserver +// since it is not called very often and sal_IsLocalDisplay() is relative +// expensive bLocal_ is initialized on first call + +sal_Bool SalDisplay::IsLocal() +{ + if ( ! mbLocalIsValid ) + { + bLocal_ = sal_IsLocalDisplay( pDisp_ ); + mbLocalIsValid = sal_True; + } + return (sal_Bool)bLocal_; +} + +// --------------------------------------------------------------------------- +extern "C" srv_vendor_t +sal_GetServerVendor( Display *p_display ) +{ + typedef struct { + srv_vendor_t e_vendor; // vendor as enum + const char *p_name; // vendor name as returned by VendorString() + unsigned int n_len; // number of chars to compare + } vendor_t; + + const vendor_t p_vendorlist[] = { + { vendor_xfree, "The XFree86 Project, Inc", 13 }, + { vendor_sun, "Sun Microsystems, Inc.", 10 }, + { vendor_attachmate, "Attachmate Corporation", 10 }, + { vendor_excursion, + "DECWINDOWS DigitalEquipmentCorporation, eXcursion", 42 }, + { vendor_hp, "Hewlett-Packard Company", 17 }, + { vendor_hummingbird, "Hummingbird Communications Ltd.", 11 }, + { vendor_ibm, "International Business Machines", 24 }, + { vendor_sgi, "Silicon Graphics", 9 }, + { vendor_sco, "The Santa Cruz Operation", 16 }, + { vendor_xinside, "X Inside Inc.", 10 }, + // allways the last entry: vendor_none to indicate eol + { vendor_none, NULL, 0 }, + }; + + // handle regular server vendors + char *p_name = ServerVendor( p_display ); + vendor_t *p_vendor; + for (p_vendor = const_cast<vendor_t*>(p_vendorlist); p_vendor->e_vendor != vendor_none; p_vendor++) + { + if ( strncmp (p_name, p_vendor->p_name, p_vendor->n_len) == 0 ) + return p_vendor->e_vendor; + } + + // vendor not found in list + return vendor_unknown; +} + +static sal_Bool sal_IsTrustedSolaris (Display *p_display) +{ + int n_numextensions = 0; + char **p_extensions = XListExtensions (p_display, &n_numextensions); + sal_Bool b_is = sal_False; + + if (p_extensions != NULL) + { + for (int i = 0; !b_is && i < n_numextensions; i++) + b_is = (strcmp (p_extensions[i], "SUN_TSOL") == 0); + XFreeExtensionList (p_extensions); + } + + return b_is; +} + +// -=-= SalDisplay -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +sal_Bool SalDisplay::BestVisual( Display *pDisplay, + int nScreen, + XVisualInfo &rVI ) +{ + VisualID nDefVID = XVisualIDFromVisual( DefaultVisual( pDisplay, nScreen ) ); + VisualID nVID = 0; + char *pVID = getenv( "SAL_VISUAL" ); + if( pVID ) + sscanf( pVID, "%li", &nVID ); + + if( nVID && sal_GetVisualInfo( pDisplay, nVID, rVI ) ) + return rVI.visualid == nDefVID; + + XVisualInfo aVI; + aVI.screen = nScreen; + // get all visuals + int nVisuals; + XVisualInfo* pVInfos = XGetVisualInfo( pDisplay, VisualScreenMask, + &aVI, &nVisuals ); + // pVInfos should contain at least one visual, otherwise + // we're in trouble + int* pWeight = (int*)alloca( sizeof(int)*nVisuals ); + int i; + for( i = 0; i < nVisuals; i++ ) + { + sal_Bool bUsable = sal_False; + int nTrueColor = 1; + + if ( pVInfos[i].screen != nScreen ) + { + bUsable = sal_False; + } + else + if( pVInfos[i].c_class == TrueColor ) + { + nTrueColor = 2048; + if( pVInfos[i].depth == 24 ) + bUsable = sal_True; +#ifdef sal_TrueCOLOR8 + else if( pVInfos[i].depth == 8 ) + { + nTrueColor = -1; // strongly discourage 8 bit true color + bUsable = sal_True; + } +#endif +#ifdef sal_TrueCOLOR15 + else if( pVInfos[i].depth == 15 ) + bUsable = sal_True; +#endif +#ifdef sal_TrueCOLOR16 + else if( pVInfos[i].depth == 16 ) + bUsable = sal_True; +#endif +#ifdef sal_TrueCOLOR32 + else if( pVInfos[i].depth == 32 ) + { + nTrueColor = 256; + // we do not have use for an alpha channel + // better use a 24 or 16 bit truecolor visual if possible + bUsable = sal_True; + } +#endif + } + else if( pVInfos[i].c_class == PseudoColor ) + { + if( pVInfos[i].depth <= 8 ) + bUsable = sal_True; +#ifdef PSEUDOCOLOR12 + else if( pVInfos[i].depth == 12 ) + bUsable = sal_True; +#endif + } + pWeight[ i ] = bUsable ? nTrueColor*pVInfos[i].depth : -1024; + pWeight[ i ] -= pVInfos[ i ].visualid; + } + + int nBestVisual = 0; + int nBestWeight = -1024; + for( i = 0; i < nVisuals; i++ ) + { + if( pWeight[ i ] > nBestWeight ) + { + nBestWeight = pWeight[ i ]; + nBestVisual = i; + } + } + + rVI = pVInfos[ nBestVisual ]; + + XFree( pVInfos ); + return rVI.visualid == nDefVID; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +SalDisplay::SalDisplay( Display *display ) : + mpInputMethod( NULL ), + pDisp_( display ), + m_pWMAdaptor( NULL ), + m_pDtIntegrator( NULL ), + m_bUseRandRWrapper( true ), + m_nLastUserEventTime( CurrentTime ) +{ +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "SalDisplay::SalDisplay()\n" ); +#endif + X11SalData *pSalData = GetX11SalData(); + + DBG_ASSERT( ! pSalData->GetDisplay(), "Second SalDisplay created !!!\n" ); + pSalData->SetSalDisplay( this ); + + pXLib_ = pSalData->GetLib(); + m_nDefaultScreen = DefaultScreen( pDisp_ ); + +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +SalDisplay::~SalDisplay( ) +{ +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "SalDisplay::~SalDisplay()\n" ); +#endif + if( pDisp_ ) + { + doDestruct(); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "display %p closed\n", pDisp_ ); +#endif + pDisp_ = NULL; + } + // don't do this in doDestruct since RandR extension adds hooks into Display + // that is XCloseDisplay still needs the RandR library if it was used + DeInitRandR(); +} + +void SalDisplay::doDestruct() +{ + X11SalData *pSalData = GetX11SalData(); + + delete m_pWMAdaptor; + m_pWMAdaptor = NULL; + delete m_pDtIntegrator; + m_pDtIntegrator = NULL; + X11SalBitmap::ImplDestroyCache(); + X11SalGraphics::releaseGlyphPeer(); + + if( IsDisplay() ) + { + delete mpInputMethod, mpInputMethod = (SalI18N_InputMethod*)ILLEGAL_POINTER; + delete mpKbdExtension, mpKbdExtension = (SalI18N_KeyboardExtension*)ILLEGAL_POINTER; + + // do not call anything that could implicitly call back into + // this object after this point + osl_destroyMutex( hEventGuard_ ); + + for( unsigned int i = 0; i < m_aScreens.size(); i++ ) + { + ScreenData& rData = m_aScreens[i]; + if( rData.m_bInit ) + { + if( rData.m_aMonoGC != rData.m_aCopyGC ) + XFreeGC( pDisp_, rData.m_aMonoGC ); + XFreeGC( pDisp_, rData.m_aCopyGC ); + XFreeGC( pDisp_, rData.m_aAndInvertedGC ); + XFreeGC( pDisp_, rData.m_aAndGC ); + XFreeGC( pDisp_, rData.m_aOrGC ); + XFreeGC( pDisp_, rData.m_aStippleGC ); + XFreePixmap( pDisp_, rData.m_hInvert50 ); + XDestroyWindow( pDisp_, rData.m_aRefWindow ); + Colormap aColMap = rData.m_aColormap.GetXColormap(); + if( aColMap != None && aColMap != DefaultColormap( pDisp_, i ) ) + XFreeColormap( pDisp_, aColMap ); + } + } + + hEventGuard_ = (oslMutex)ILLEGAL_POINTER; + + for( size_t i = 0; i < POINTER_COUNT; i++ ) + { + if( aPointerCache_[i] ) + XFreeCursor( pDisp_, aPointerCache_[i] ); + } + + pXLib_->Remove( ConnectionNumber( pDisp_ ) ); + } + + if( pSalData->GetDisplay() == this ) + pSalData->SetSalDisplay( NULL ); +} + +static int DisplayHasEvent( int +#ifdef DBG_UTIL +fd +#endif +, SalX11Display *pDisplay ) +{ + DBG_ASSERT( ConnectionNumber( pDisplay->GetDisplay() ) == fd, + "wrong fd in DisplayHasEvent" ); + if( ! pDisplay->IsDisplay() ) + return 0; + + int result; + + GetSalData()->m_pInstance->GetYieldMutex()->acquire(); + result = pDisplay->IsEvent(); + GetSalData()->m_pInstance->GetYieldMutex()->release(); + return result; +} +static int DisplayQueue( int +#ifdef DBG_UTIL +fd +#endif +, SalX11Display *pDisplay ) +{ + DBG_ASSERT( ConnectionNumber( pDisplay->GetDisplay() ) == fd, + "wrong fd in DisplayHasEvent" ); + int result; + + GetSalData()->m_pInstance->GetYieldMutex()->acquire(); + result = XEventsQueued( pDisplay->GetDisplay(), + QueuedAfterReading ); + GetSalData()->m_pInstance->GetYieldMutex()->release(); + + return result; +} +static int DisplayYield( int +#ifdef DBG_UTIL +fd +#endif +, SalX11Display *pDisplay ) +{ + DBG_ASSERT( ConnectionNumber( pDisplay->GetDisplay() ) == fd, + "wrong fd in DisplayHasEvent" ); + + GetSalData()->m_pInstance->GetYieldMutex()->acquire(); + pDisplay->Yield(); + GetSalData()->m_pInstance->GetYieldMutex()->release(); + return sal_True; +} + +SalX11Display::SalX11Display( Display *display ) + : SalDisplay( display ) +{ + Init(); + + pXLib_->Insert( ConnectionNumber( pDisp_ ), + this, + (YieldFunc) DisplayHasEvent, + (YieldFunc) DisplayQueue, + (YieldFunc) DisplayYield ); +} + +SalX11Display::~SalX11Display() +{ +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "SalX11Display::~SalX11Display()\n" ); +#endif + if( pDisp_ ) + { + doDestruct(); + XCloseDisplay( pDisp_ ); + pDisp_ = NULL; + } +} + +void SalDisplay::initScreen( int nScreen ) const +{ + if( nScreen < 0 || nScreen >= static_cast<int>(m_aScreens.size()) ) + nScreen = m_nDefaultScreen; + ScreenData& rSD = const_cast<ScreenData&>(m_aScreens[nScreen]); + if( rSD.m_bInit ) + return; + rSD.m_bInit = true; + + XVisualInfo aVI; + Colormap aColMap; + + if( SalDisplay::BestVisual( pDisp_, nScreen, aVI ) ) // DefaultVisual + aColMap = DefaultColormap( pDisp_, nScreen ); + else + aColMap = XCreateColormap( pDisp_, + RootWindow( pDisp_, nScreen ), + aVI.visual, + AllocNone ); + + Screen* pScreen = ScreenOfDisplay( pDisp_, nScreen ); + + rSD.m_aSize = Size( WidthOfScreen( pScreen ), HeightOfScreen( pScreen ) ); + rSD.m_aRoot = RootWindow( pDisp_, nScreen ); + rSD.m_aVisual = SalVisual( &aVI ); + rSD.m_aColormap = SalColormap( this, aColMap, nScreen ); + + // we're interested in configure notification of root windows + InitRandR( rSD.m_aRoot ); + + // - - - - - - - - - - Reference Window/Default Drawable - - + XSetWindowAttributes aXWAttributes; + aXWAttributes.border_pixel = 0; + aXWAttributes.background_pixel = 0; + aXWAttributes.colormap = aColMap; + rSD.m_aRefWindow = XCreateWindow( pDisp_, + rSD.m_aRoot, + 0,0, 16,16, 0, + rSD.m_aVisual.GetDepth(), + InputOutput, + rSD.m_aVisual.GetVisual(), + CWBorderPixel|CWBackPixel|CWColormap, + &aXWAttributes ); + + // set client leader (session id gets set when session is started) + if( rSD.m_aRefWindow ) + { + // client leader must have WM_CLIENT_LEADER pointing to itself + XChangeProperty( pDisp_, + rSD.m_aRefWindow, + XInternAtom( pDisp_, "WM_CLIENT_LEADER", False ), + XA_WINDOW, + 32, + PropModeReplace, + (unsigned char*)&rSD.m_aRefWindow, + 1 + ); + + rtl::OString aExec(rtl::OUStringToOString(SessionManagerClient::getExecName(), osl_getThreadTextEncoding())); + const char* argv[2]; + argv[0] = "/bin/sh"; + argv[1] = aExec.getStr(); + XSetCommand( pDisp_, rSD.m_aRefWindow, const_cast<char**>(argv), 2 ); + XSelectInput( pDisp_, rSD.m_aRefWindow, PropertyChangeMask ); + + // - - - - - - - - - - GCs - - - - - - - - - - - - - - - - - + XGCValues values; + values.graphics_exposures = False; + values.fill_style = FillOpaqueStippled; + values.background = (1<<rSD.m_aVisual.GetDepth())-1; + values.foreground = 0; + + rSD.m_aCopyGC = XCreateGC( pDisp_, + rSD.m_aRefWindow, + GCGraphicsExposures + | GCForeground + | GCBackground, + &values ); + rSD.m_aAndInvertedGC= XCreateGC( pDisp_, + rSD.m_aRefWindow, + GCGraphicsExposures + | GCForeground + | GCBackground, + &values ); + rSD.m_aAndGC = XCreateGC( pDisp_, + rSD.m_aRefWindow, + GCGraphicsExposures + | GCForeground + | GCBackground, + &values ); + rSD.m_aOrGC = XCreateGC( pDisp_, + rSD.m_aRefWindow, + GCGraphicsExposures + | GCForeground + | GCBackground, + &values ); + rSD.m_aStippleGC = XCreateGC( pDisp_, + rSD.m_aRefWindow, + GCGraphicsExposures + | GCFillStyle + | GCForeground + | GCBackground, + &values ); + + XSetFunction( pDisp_, rSD.m_aAndInvertedGC, GXandInverted ); + XSetFunction( pDisp_, rSD.m_aAndGC, GXand ); + // PowerPC Solaris 2.5 (XSun 3500) Bug: GXor = GXnop + XSetFunction( pDisp_, rSD.m_aOrGC, GXxor ); + + if( 1 == rSD.m_aVisual.GetDepth() ) + { + XSetFunction( pDisp_, rSD.m_aCopyGC, GXcopyInverted ); + rSD.m_aMonoGC = rSD.m_aCopyGC; + } + else + { + Pixmap hPixmap = XCreatePixmap( pDisp_, rSD.m_aRefWindow, 1, 1, 1 ); + rSD.m_aMonoGC = XCreateGC( pDisp_, + hPixmap, + GCGraphicsExposures, + &values ); + XFreePixmap( pDisp_, hPixmap ); + } + rSD.m_hInvert50 = XCreateBitmapFromData( pDisp_, + rSD.m_aRefWindow, + reinterpret_cast<const char*>(invert50_bits), + invert50_width, + invert50_height ); + } +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void SalDisplay::Init() +{ + for( size_t i = 0; i < POINTER_COUNT; i++ ) + aPointerCache_[i] = None; + + eWindowManager_ = otherwm; + nProperties_ = PROPERTY_DEFAULT; + hEventGuard_ = NULL; + mpFactory = (AttributeProvider*)NULL; + m_pCapture = NULL; + m_bXinerama = false; + + int nDisplayScreens = ScreenCount( pDisp_ ); + m_aScreens = std::vector<ScreenData>(nDisplayScreens); + + mbExactResolution = false; + /* #i15507# + * Xft resolution should take precedence since + * it is what modern desktops use. + */ + const char* pValStr = XGetDefault( pDisp_, "Xft", "dpi" ); + if( pValStr != NULL ) + { + const rtl::OString aValStr( pValStr ); + const long nDPI = (long) aValStr.toDouble(); + // guard against insane resolution + if( (nDPI >= 50) && (nDPI <= 500) ) + { + aResolution_ = Pair( nDPI, nDPI ); + mbExactResolution = true; + } + } + if( mbExactResolution == false ) + { + aResolution_ = + Pair( DPI( WidthOfScreen( DefaultScreenOfDisplay( pDisp_ ) ), DisplayWidthMM ( pDisp_, m_nDefaultScreen ) ), + DPI( HeightOfScreen( DefaultScreenOfDisplay( pDisp_ ) ), DisplayHeightMM( pDisp_, m_nDefaultScreen ) ) ); + } + + nMaxRequestSize_ = XExtendedMaxRequestSize( pDisp_ ) * 4; + if( !nMaxRequestSize_ ) + nMaxRequestSize_ = XMaxRequestSize( pDisp_ ) * 4; + + SetServerVendor(); + X11SalBitmap::ImplCreateCache(); + + hEventGuard_ = osl_createMutex(); + bLocal_ = sal_False; /* dont care, initialize later by + calling SalDisplay::IsLocal() */ + mbLocalIsValid = sal_False; /* bLocal_ is not yet initialized */ + + // - - - - - - - - - - Synchronize - - - - - - - - - - - - - + if( getenv( "SAL_SYNCHRONIZE" ) ) + XSynchronize( pDisp_, True ); + + // - - - - - - - - - - Keyboardmapping - - - - - - - - - - - + ModifierMapping(); + + // - - - - - - - - - - Window Manager - - - - - - - - - - - + m_pWMAdaptor = ::vcl_sal::WMAdaptor::createWMAdaptor( this ); + const char *pWM = getenv( "SAL_WM" ); + if( pWM ) + { + long int nWM = 0; + sscanf( pWM, "%li", &nWM ); + eWindowManager_ = SalWM(nWM); + } + else if( XInternAtom( pDisp_, "_SGI_TELL_WM", True ) ) + eWindowManager_ = FourDwm; + else if( XInternAtom( pDisp_, "KWM_RUNNING", True ) ) + eWindowManager_ = mwm; // naja, eigentlich kwm ... + else if( XInternAtom( pDisp_, "_OL_WIN_ATTR", True ) ) + eWindowManager_ = olwm; + else if( m_pWMAdaptor->getWindowManagerName().EqualsAscii( "Dtwm" ) ) + eWindowManager_ = dtwm; + + // - - - - - - - - - - Properties - - - - - - - - - - - - - + const char *pProperties = getenv( "SAL_PROPERTIES" ); + if( pProperties ) + sscanf( pProperties, "%li", &nProperties_ ); + else + { +#if defined DBG_UTIL || defined SUN || defined LINUX || defined FREEBSD || \ + defined NETBSD || defined OPENBSD || defined DRAGONFLY + nProperties_ |= PROPERTY_FEATURE_Maximize; +#endif + // Server Bugs & Properties + if( GetServerVendor() == vendor_excursion ) + { + nProperties_ |= PROPERTY_BUG_Stipple; + nProperties_ |= PROPERTY_BUG_DrawLine; + nProperties_ &= ~PROPERTY_SUPPORT_XSetClipMask; + } + else + if( GetServerVendor() == vendor_attachmate ) + { + nProperties_ |= PROPERTY_BUG_CopyPlane_RevertBWPixel; + } + else + if( GetServerVendor() == vendor_ibm ) + { + nProperties_ |= PROPERTY_BUG_XA_FAMILY_NAME_nil; + + if( otherwm == eWindowManager_ ) eWindowManager_ = mwm; + } + else + if( GetServerVendor() == vendor_xfree ) + { + nProperties_ |= PROPERTY_BUG_XCopyArea_GXxor; +#if defined LINUX || defined FREEBSD || defined NETBSD || defined OPENBSD || \ + defined DRAGONFLY + // otherwm and olwm are a kind of default, which are not detected + // carefully. if we are running linux (i.e. not netbsd) on an xfree + // display, fvwm is most probable the wm to choose, confusing with mwm + // doesn't harm. #57791# start maximized if possible + if( (otherwm == eWindowManager_) + || (olwm == eWindowManager_ )) + { + eWindowManager_ = fvwm; // ??? + nProperties_ |= PROPERTY_FEATURE_Maximize; + } +#else + if( otherwm == eWindowManager_ ) eWindowManager_ = winmgr; +#endif +#if defined SOLARIS && defined SPARC + nProperties_ |= PROPERTY_BUG_Bitmap_Bit_Order; + // solaris xlib seems to have problems with putting images + // in correct bit order to xfree 8 bit displays +#endif + } + else + if( GetServerVendor() == vendor_sun ) + { + // nicht alle! (bekannt: nur Sparc II CG3, CG6?) + nProperties_ &= ~PROPERTY_SUPPORT_XSetClipMask; + + // trusted solaris doesn't allow to change properties on the + // wm decoration window + if (sal_IsTrustedSolaris (pDisp_)) + nProperties_ |= PROPERTY_FEATURE_TrustedSolaris; + + // Fehler im Sun-Solaris X86 Server ! + if (ImageByteOrder(GetDisplay()) == LSBFirst) + { + nProperties_ |= PROPERTY_BUG_Tile; + nProperties_ |= PROPERTY_SUPPORT_3ButtonMouse; + } + else // MSBFirst Sun-Solaris Sparc Server + { + // XCopyPlane reverts black and white for 1bit bitmaps + // only sun, only 8bit pseudocolor target + if ( (GetVisual(m_nDefaultScreen).GetDepth() == 8) + && (GetVisual(m_nDefaultScreen).GetClass() == PseudoColor)) + nProperties_ |= PROPERTY_BUG_CopyPlane_RevertBWPixel; + // Fehler in Solaris 2.5.1 + if (VendorRelease ( GetDisplay() ) < 3600) + nProperties_ |= PROPERTY_BUG_FillPolygon_Tile; + } + + if( otherwm == eWindowManager_ ) + eWindowManager_ = olwm; + } + else + if( GetServerVendor() == vendor_sco ) + { + if( otherwm == eWindowManager_ ) eWindowManager_ = pmwm; + } + else + if( GetServerVendor() == vendor_sgi ) + { + if( GetVisual( m_nDefaultScreen ).GetDepth() > 8 && GetVisual( m_nDefaultScreen ).GetDepth() <= 16 ) + nProperties_ |= PROPERTY_BUG_XCopyArea_GXxor; + nProperties_ |= PROPERTY_SUPPORT_XSetClipMask; + + if( otherwm == eWindowManager_ ) + eWindowManager_ = FourDwm; + } + else + if( GetServerVendor() == vendor_hp ) + { + if( otherwm == eWindowManager_ ) eWindowManager_ = dtwm; + } + else + if( GetServerVendor() == vendor_hummingbird ) + { + if (GetVisual(m_nDefaultScreen).GetDepth() == 24) + nProperties_ |= PROPERTY_BUG_CopyArea_OnlySmallSlices; + } + + if( otherwm == eWindowManager_ ) + { + if( !XInternAtom( pDisp_, "_MOTIF_WM_INFO", True ) ) + eWindowManager_ = olwm; + // ??? + } + + if( winmgr == eWindowManager_ ) + { + nProperties_ &= ~PROPERTY_SUPPORT_WM_SetPos; + nProperties_ &= ~PROPERTY_SUPPORT_WM_Screen; + nProperties_ |= PROPERTY_FEATURE_Maximize; + } + else if( dtwm == eWindowManager_ ) + { + nProperties_ &= ~PROPERTY_SUPPORT_WM_ClientPos; + } + else if( pmwm == eWindowManager_ ) + { + nProperties_ &= ~PROPERTY_SUPPORT_WM_ClientPos; + } + } + + InitXinerama(); + + // initialize system settings update + m_pDtIntegrator = DtIntegrator::CreateDtIntegrator(); + +#ifdef DBG_UTIL + PrintInfo(); +#endif +} + +// Sound +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void SalDisplay::Beep() const +{ + XBell( pDisp_, 0 ); +} + +// Keyboard +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +namespace { + +bool InitXkb(Display* dpy) +{ + int nOpcode, nEvent, nError; + int nXkbMajor = XkbMajorVersion; + int nXkbMinor = XkbMinorVersion; + + if (!XkbLibraryVersion(&nXkbMajor, &nXkbMinor)) + return false; + + return XkbQueryExtension( + dpy, &nOpcode, &nEvent, &nError, &nXkbMajor, &nXkbMinor); +} + +unsigned int GetKeySymMask(Display* dpy, KeySym nKeySym) +{ + int nMask = 0; + XModifierKeymap* pXmkMap = XGetModifierMapping(dpy); + KeyCode nKeyCode = XKeysymToKeycode(dpy, nKeySym); + if (nKeyCode == NoSymbol) + return 0; + + for (int i = 0; i < 8; ++i) + { + KeyCode nThisKeyCode = pXmkMap->modifiermap[pXmkMap->max_keypermod*i]; + if (nThisKeyCode == nKeyCode) + nMask = 1 << i; + } + XFreeModifiermap(pXmkMap); + return nMask; +} + +} + +void SalDisplay::SimulateKeyPress( sal_uInt16 nKeyCode ) +{ + if (nKeyCode == KEY_CAPSLOCK) + { + Display* dpy = GetDisplay(); + if (!InitXkb(dpy)) + return; + + unsigned int nMask = GetKeySymMask(dpy, XK_Caps_Lock); + XkbStateRec xkbState; + XkbGetState(dpy, XkbUseCoreKbd, &xkbState); + unsigned int nCapsLockState = xkbState.locked_mods & nMask; + if (nCapsLockState) + XkbLockModifiers (dpy, XkbUseCoreKbd, nMask, 0); + else + XkbLockModifiers (dpy, XkbUseCoreKbd, nMask, nMask); + } +} + +sal_uInt16 SalDisplay::GetIndicatorState() const +{ + unsigned int _state = 0; + sal_uInt16 nState = 0; + XkbGetIndicatorState(pDisp_, XkbUseCoreKbd, &_state); + + if ((_state & 0x00000001)) + nState |= INDICATOR_CAPSLOCK; + if ((_state & 0x00000002)) + nState |= INDICATOR_NUMLOCK; + if ((_state & 0x00000004)) + nState |= INDICATOR_SCROLLLOCK; + + return nState; +} + +String SalDisplay::GetKeyNameFromKeySym( KeySym nKeySym ) const +{ + String aRet; + + // return an empty string for keysyms that are not bound to + // any key code + XLIB_KeyCode aKeyCode = XKeysymToKeycode( GetDisplay(), nKeySym ); + if( aKeyCode != 0 && aKeyCode != NoSymbol ) + { + if( !nKeySym ) + aRet = String::CreateFromAscii( RTL_CONSTASCII_STRINGPARAM( "???" ) ); + else + { + aRet = ::vcl_sal::getKeysymReplacementName( const_cast<SalDisplay*>(this)->GetKeyboardName(), nKeySym ); + if( ! aRet.Len() ) + { + const char *pString = XKeysymToString( nKeySym ); + int n = strlen( pString ); + if( n > 2 && pString[n-2] == '_' ) + aRet = String( pString, n-2, RTL_TEXTENCODING_ISO_8859_1 ); + else + aRet = String( pString, n, RTL_TEXTENCODING_ISO_8859_1 ); + } + } + } + return aRet; +} + +inline KeySym sal_XModifier2Keysym( Display *pDisplay, + XModifierKeymap *pXModMap, + int n ) +{ + return XKeycodeToKeysym( pDisplay, + pXModMap->modifiermap[n*pXModMap->max_keypermod], + 0 ); +} + +void SalDisplay::ModifierMapping() +{ + XModifierKeymap *pXModMap = XGetModifierMapping( pDisp_ ); + + bNumLockFromXS_ = True; + nShiftKeySym_ = sal_XModifier2Keysym( pDisp_, pXModMap, ShiftMapIndex ); + nCtrlKeySym_ = sal_XModifier2Keysym( pDisp_, pXModMap, ControlMapIndex ); + nMod1KeySym_ = sal_XModifier2Keysym( pDisp_, pXModMap, Mod1MapIndex ); + // Auf Sun-Servern und SCO-Severn beruecksichtigt XLookupString + // nicht den NumLock Modifier. + if( (GetServerVendor() == vendor_sun) + || (GetServerVendor() == vendor_sco) ) + { + XLIB_KeyCode aNumLock = XKeysymToKeycode( pDisp_, XK_Num_Lock ); + + if( aNumLock ) for( int i = ShiftMapIndex; i <= Mod5MapIndex; i++ ) + { + if( pXModMap->modifiermap[i*pXModMap->max_keypermod] == aNumLock ) + { + bNumLockFromXS_ = False; + nNumLockIndex_ = i; + nNumLockMask_ = 1<<i; + break; + } + } + } + + XFreeModifiermap( pXModMap ); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +XubString SalDisplay::GetKeyName( sal_uInt16 nKeyCode ) const +{ + String aStrMap; + String aCustomKeyName; + + if( nKeyCode & KEY_MOD1 ) + aStrMap += GetKeyNameFromKeySym( nCtrlKeySym_ ); + + if( nKeyCode & KEY_MOD2 ) + { + if( aStrMap.Len() ) + aStrMap += '+'; + aStrMap += GetKeyNameFromKeySym( nMod1KeySym_ ); + } + + if( nKeyCode & KEY_SHIFT ) + { + if( aStrMap.Len() ) + aStrMap += '+'; + aStrMap += GetKeyNameFromKeySym( nShiftKeySym_ ); + } + nKeyCode &= 0x0FFF; + + KeySym nKeySym = 0; + + if( KEY_0 <= nKeyCode && nKeyCode <= KEY_9 ) + nKeySym = XK_0 + (nKeyCode - KEY_0); + else if( KEY_A <= nKeyCode && nKeyCode <= KEY_Z ) + nKeySym = XK_A + (nKeyCode - KEY_A); + else if( KEY_F1 <= nKeyCode && nKeyCode <= KEY_F26 ) // Existiert die Taste + nKeySym = XK_F1 + (nKeyCode - KEY_F1); + else switch( nKeyCode ) + { + case KEY_DOWN: + nKeySym = XK_Down; + break; + case KEY_UP: + nKeySym = XK_Up; + break; + case KEY_LEFT: + nKeySym = XK_Left; + break; + case KEY_RIGHT: + nKeySym = XK_Right; + break; + case KEY_HOME: + nKeySym = XK_Home; + break; + case KEY_END: + nKeySym = XK_End; + break; + case KEY_PAGEUP: + nKeySym = XK_Prior; + break; + case KEY_PAGEDOWN: + nKeySym = XK_Next; + break; + case KEY_RETURN: + nKeySym = XK_Return; + break; + case KEY_ESCAPE: + nKeySym = XK_Escape; + break; + case KEY_TAB: + nKeySym = XK_Tab; + break; + case KEY_BACKSPACE: + nKeySym = XK_BackSpace; + break; + case KEY_SPACE: + nKeySym = XK_space; + break; + case KEY_INSERT: + nKeySym = XK_Insert; + break; + case KEY_DELETE: + nKeySym = XK_Delete; + break; + + #if !defined (SunXK_Undo) + #define SunXK_Stop 0x0000FF69 // XK_Cancel + #define SunXK_Props 0x1005FF70 + #define SunXK_Front 0x1005FF71 + #define SunXK_Copy 0x1005FF72 + #define SunXK_Open 0x1005FF73 + #define SunXK_Paste 0x1005FF74 + #define SunXK_Cut 0x1005FF75 + #endif + + case KEY_REPEAT: + nKeySym = XK_Redo; + break; + case KEY_PROPERTIES: + nKeySym = SunXK_Props; + break; + case KEY_UNDO: + nKeySym = XK_Undo; + break; + case KEY_FRONT: + nKeySym = SunXK_Front; + break; + case KEY_COPY: + nKeySym = SunXK_Copy; + break; + case KEY_OPEN: + nKeySym = SunXK_Open; + break; + case KEY_PASTE: + nKeySym = SunXK_Paste; + break; + case KEY_FIND: + nKeySym = XK_Find; + break; + case KEY_CUT: + nKeySym = GetServerVendor() == vendor_sun ? SunXK_Cut : XK_L10; + break; + case KEY_ADD: + nKeySym = XK_plus; + break; + case KEY_SUBTRACT: + nKeySym = XK_minus; + break; + case KEY_MULTIPLY: + nKeySym = XK_asterisk; + break; + case KEY_DIVIDE: + nKeySym = XK_slash; + break; + case KEY_POINT: + nKeySym = XK_period; + break; + case KEY_COMMA: + nKeySym = XK_comma; + break; + case KEY_LESS: + nKeySym = XK_less; + break; + case KEY_GREATER: + nKeySym = XK_greater; + break; + case KEY_EQUAL: + nKeySym = XK_equal; + break; + case KEY_HELP: + nKeySym = XK_Help; + break; + case KEY_HANGUL_HANJA: + nKeySym = XK_Hangul_Hanja; + break; + case KEY_TILDE: + nKeySym = XK_asciitilde; + break; + case KEY_QUOTELEFT: + nKeySym = XK_grave; + break; + case KEY_BRACKETLEFT: + aCustomKeyName = '['; + break; + case KEY_BRACKETRIGHT: + aCustomKeyName = ']'; + break; + case KEY_SEMICOLON: + aCustomKeyName = ';'; + break; + + default: + nKeySym = 0; + break; + } + + if( nKeySym ) + { + String aKeyName = GetKeyNameFromKeySym( nKeySym ); + if( aKeyName.Len() ) + { + if( aStrMap.Len() ) + aStrMap += '+'; + aStrMap += aKeyName; + } + else + aStrMap.Erase(); + } + else if (aCustomKeyName.Len()) + { + // For semicolumn, bracket left and bracket right, it's better to use + // their keys than their names. (fdo#32891) + if (aStrMap.Len()) + aStrMap += '+'; + aStrMap += aCustomKeyName; + } + else + aStrMap.Erase(); + + return aStrMap; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +#ifndef IsISOKey +#define IsISOKey( n ) (0x0000FE00==((n)&0xFFFFFF00)) +#endif + +sal_uInt16 SalDisplay::GetKeyCode( KeySym keysym, char*pcPrintable ) const +{ + sal_uInt16 nKey = 0; + + if( XK_a <= keysym && XK_z >= keysym ) + nKey = (sal_uInt16)(KEY_A + (keysym - XK_a)); + else if( XK_A <= keysym && XK_Z >= keysym ) + nKey = (sal_uInt16)(KEY_A + (keysym - XK_A)); + else if( XK_0 <= keysym && XK_9 >= keysym ) + nKey = (sal_uInt16)(KEY_0 + (keysym - XK_0)); + else if( IsModifierKey( keysym ) ) + ; + else if( IsKeypadKey( keysym ) ) + { + if( (keysym >= XK_KP_0) && (keysym <= XK_KP_9) ) + { + nKey = (sal_uInt16)(KEY_0 + (keysym - XK_KP_0)); + *pcPrintable = '0' + nKey - KEY_0; + } + else if( IsPFKey( keysym ) ) + nKey = (sal_uInt16)(KEY_F1 + (keysym - XK_KP_F1)); + else switch( keysym ) + { + case XK_KP_Space: + nKey = KEY_SPACE; + *pcPrintable = ' '; + break; + case XK_KP_Tab: + nKey = KEY_TAB; + break; + case XK_KP_Enter: + nKey = KEY_RETURN; + break; + case XK_KP_Begin: + case XK_KP_Home: + nKey = KEY_HOME; + break; + case XK_KP_Left: + nKey = KEY_LEFT; + break; + case XK_KP_Up: + nKey = KEY_UP; + break; + case XK_KP_Right: + nKey = KEY_RIGHT; + break; + case XK_KP_Down: + nKey = KEY_DOWN; + break; + case XK_KP_Prior: // XK_KP_Page_Up + nKey = KEY_PAGEUP; + break; + case XK_KP_Next: // XK_KP_Page_Down + nKey = KEY_PAGEDOWN; + break; + case XK_KP_End: + nKey = KEY_END; + break; + case XK_KP_Insert: + nKey = KEY_INSERT; + break; + case XK_KP_Delete: + nKey = KEY_DELETE; + break; + case XK_KP_Equal: + nKey = KEY_EQUAL; + *pcPrintable = '='; + break; + case XK_KP_Multiply: + nKey = KEY_MULTIPLY; + *pcPrintable = '*'; + break; + case XK_KP_Add: + nKey = KEY_ADD; + *pcPrintable = '+'; + break; + case XK_KP_Separator: + nKey = KEY_DECIMAL; + *pcPrintable = ','; + break; + case XK_KP_Subtract: + nKey = KEY_SUBTRACT; + *pcPrintable = '-'; + break; + case XK_KP_Decimal: + nKey = KEY_DECIMAL; + *pcPrintable = '.'; + break; + case XK_KP_Divide: + nKey = KEY_DIVIDE; + *pcPrintable = '/'; + break; + } + } + else if( IsFunctionKey( keysym ) ) + { + if( bNumLockFromXS_ ) + { + if( keysym >= XK_F1 && keysym <= XK_F26 ) + nKey = (sal_uInt16)(KEY_F1 + keysym - XK_F1); + } + else switch( keysym ) + { + // - - - - - Sun X-Server Tastatur ohne Cursorblock ??? - - - + case XK_R7: // XK_F27: + nKey = KEY_HOME; + break; + case XK_R8: // XK_F28: + nKey = KEY_UP; + break; + case XK_R9: // XK_F29: + nKey = KEY_PAGEUP; + break; + case XK_R10: // XK_F30: + nKey = KEY_LEFT; + break; + case XK_R11: // XK_F31: + nKey = 0; // KEY_F31 + break; + case XK_R12: // XK_F32: + nKey = KEY_RIGHT; + break; + case XK_R13: // XK_F33: + nKey = KEY_END; + break; + case XK_R14: // XK_F34: + nKey = KEY_DOWN; + break; + case XK_R15: // XK_F35: + nKey = KEY_PAGEDOWN; + break; + // - - - - - Sun X-Server Tastatur ??? - - - - - - - - - - - - + case XK_L1: // XK_F11: + nKey = KEY_F11; // on a sun keyboard this actually is usally SunXK_Stop, + // but VCL doesn't have a key defintion for that + break; + case XK_L2: // XK_F12: + if ( GetServerVendor() == vendor_sun ) + nKey = KEY_REPEAT; + else + nKey = KEY_F12; + break; + case XK_L3: // XK_F13: + nKey = KEY_PROPERTIES; // KEY_F13 + break; + case XK_L4: // XK_F14: + nKey = KEY_UNDO; // KEY_F14 + break; + case XK_L5: // XK_F15: + nKey = KEY_F15; // KEY_FRONT + break; + case XK_L6: // XK_F16: + nKey = KEY_COPY; // KEY_F16 + break; + case XK_L7: // XK_F17: + nKey = KEY_F17; // KEY_OPEN + break; + case XK_L8: // XK_F18: + nKey = KEY_PASTE; // KEY_F18 + break; + case XK_L9: // XK_F19: + nKey = KEY_F19; // KEY_FIND + break; + case XK_L10: // XK_F20: + nKey = KEY_CUT; // KEY_F20 + break; + default: + if( keysym >= XK_F1 && keysym <= XK_F26 ) + nKey = (sal_uInt16)(KEY_F1 + keysym - XK_F1); + break; + } + } + else if( IsCursorKey( keysym ) ) + { + switch( keysym ) + { + case XK_Begin: + case XK_Home: + nKey = KEY_HOME; + break; + case XK_Left: + nKey = KEY_LEFT; + break; + case XK_Up: + nKey = KEY_UP; + break; + case XK_Right: + nKey = KEY_RIGHT; + break; + case XK_Down: + nKey = KEY_DOWN; + break; + case XK_Prior: // XK_Page_Up + nKey = KEY_PAGEUP; + break; + case XK_Next: // XK_Page_Down + nKey = KEY_PAGEDOWN; + break; + case XK_End: + nKey = KEY_END; + break; + } + } + else if( IsMiscFunctionKey( keysym ) ) + { + switch( keysym ) + { + case XK_Insert: + nKey = KEY_INSERT; + break; + case XK_Redo: + nKey = KEY_REPEAT; + break; + case XK_Undo: + nKey = KEY_UNDO; + break; + case XK_Find: + nKey = KEY_FIND; + break; + case XK_Help: + nKey = KEY_HELP; + break; + case XK_Menu: + nKey = KEY_CONTEXTMENU; + break; + } + } + else if( IsISOKey( keysym ) ) // XK_ISO_ + { + switch( keysym ) + { + case 0xFE20: // XK_ISO_Left_Tab: + nKey = KEY_TAB; + break; + } + } + else switch( keysym ) + { + case XK_Return: + nKey = KEY_RETURN; + break; + case XK_BackSpace: + nKey = KEY_BACKSPACE; + break; + case XK_Delete: + nKey = KEY_DELETE; + break; + case XK_space: + nKey = KEY_SPACE; + break; + case XK_Tab: + nKey = KEY_TAB; + break; + case XK_Escape: + nKey = KEY_ESCAPE; + break; + case XK_plus: + nKey = KEY_ADD; + break; + case XK_minus: + nKey = KEY_SUBTRACT; + break; + case XK_asterisk: + nKey = KEY_MULTIPLY; + break; + case XK_slash: + nKey = KEY_DIVIDE; + break; + case XK_period: + nKey = KEY_POINT; + break; + case XK_comma: + nKey = KEY_COMMA; + break; + case XK_less: + nKey = KEY_LESS; + break; + case XK_greater: + nKey = KEY_GREATER; + break; + case XK_equal: + nKey = KEY_EQUAL; + break; + case XK_Hangul_Hanja: + nKey = KEY_HANGUL_HANJA; + break; + case XK_asciitilde: + nKey = KEY_TILDE; + *pcPrintable = '~'; + break; + case XK_grave: + nKey = KEY_QUOTELEFT; + *pcPrintable = '`'; + break; + case XK_bracketleft: + nKey = KEY_BRACKETLEFT; + *pcPrintable = '['; + break; + case XK_bracketright: + nKey = KEY_BRACKETRIGHT; + *pcPrintable = ']'; + break; + case XK_semicolon: + nKey = KEY_SEMICOLON; + *pcPrintable = ';'; + break; + // - - - - - - - - - - - - - Apollo - - - - - - - - - - - - - 0x1000 + case 0x1000FF02: // apXK_Copy + nKey = KEY_COPY; + break; + case 0x1000FF03: // apXK_Cut + nKey = KEY_CUT; + break; + case 0x1000FF04: // apXK_Paste + nKey = KEY_PASTE; + break; + case 0x1000FF14: // apXK_Repeat + nKey = KEY_REPEAT; + break; + // Exit, Save + // - - - - - - - - - - - - - - D E C - - - - - - - - - - - - - 0x1000 + case 0x1000FF00: + nKey = KEY_DELETE; + break; + // - - - - - - - - - - - - - - H P - - - - - - - - - - - - - 0x1000 + case 0x1000FF73: // hpXK_DeleteChar + nKey = KEY_DELETE; + break; + case 0x1000FF74: // hpXK_BackTab + case 0x1000FF75: // hpXK_KP_BackTab + nKey = KEY_TAB; + break; + // - - - - - - - - - - - - - - I B M - - - - - - - - - - - - - + // - - - - - - - - - - - - - - O S F - - - - - - - - - - - - - 0x1004 + case 0x1004FF02: // osfXK_Copy + nKey = KEY_COPY; + break; + case 0x1004FF03: // osfXK_Cut + nKey = KEY_CUT; + break; + case 0x1004FF04: // osfXK_Paste + nKey = KEY_PASTE; + break; + case 0x1004FF07: // osfXK_BackTab + nKey = KEY_TAB; + break; + case 0x1004FF08: // osfXK_BackSpace + nKey = KEY_BACKSPACE; + break; + case 0x1004FF1B: // osfXK_Escape + nKey = KEY_ESCAPE; + break; + // Up, Down, Left, Right, PageUp, PageDown + // - - - - - - - - - - - - - - S C O - - - - - - - - - - - - - + // - - - - - - - - - - - - - - S G I - - - - - - - - - - - - - 0x1007 + // - - - - - - - - - - - - - - S N I - - - - - - - - - - - - - + // - - - - - - - - - - - - - - S U N - - - - - - - - - - - - - 0x1005 + case 0x1005FF10: // SunXK_F36 + nKey = KEY_F11; + break; + case 0x1005FF11: // SunXK_F37 + nKey = KEY_F12; + break; + case 0x1005FF70: // SunXK_Props + nKey = KEY_PROPERTIES; + break; + case 0x1005FF71: // SunXK_Front + nKey = KEY_FRONT; + break; + case 0x1005FF72: // SunXK_Copy + nKey = KEY_COPY; + break; + case 0x1005FF73: // SunXK_Open + nKey = KEY_OPEN; + break; + case 0x1005FF74: // SunXK_Paste + nKey = KEY_PASTE; + break; + case 0x1005FF75: // SunXK_Cut + nKey = KEY_CUT; + break; + } + return nKey; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +KeySym SalDisplay::GetKeySym( XKeyEvent *pEvent, + unsigned char *pPrintable, + int *pLen, + KeySym *pUnmodifiedKeySym, + Status *pStatusReturn, + XIC aInputContext ) const +{ + KeySym nKeySym = 0; + memset( pPrintable, 0, *pLen ); + *pStatusReturn = 0; + + // first get the printable of the possibly modified KeySym + if ( (aInputContext == 0) + || (pEvent->type == KeyRelease) + || (mpInputMethod != NULL && mpInputMethod->PosixLocale()) ) + { + // XmbLookupString must not be called for KeyRelease events + // Cannot enter space in c locale problem #89616# #88978# btraq #4478197 + *pLen = XLookupString( pEvent, (char*)pPrintable, 1, &nKeySym, NULL ); + } + else + { + *pLen = XmbLookupString( aInputContext, + pEvent, (char*)pPrintable, *pLen - 1, &nKeySym, pStatusReturn ); + + // Lookup the string again, now with appropriate size + if ( *pStatusReturn == XBufferOverflow ) + { + pPrintable[ 0 ] = (char)0; + return 0; + } + + switch ( *pStatusReturn ) + { + case XBufferOverflow: + /* unhandled error */ + break; + case XLookupNone: + /* unhandled error */ + break; + case XLookupKeySym: + /* this is a strange one: on exceed sometimes + * no printable is returned for the first char entered, + * just to retry lookup solves the problem. The problem + * is not yet fully understood, so restrict 2nd lookup + * to 7bit ascii chars */ + if ( (XK_space <= nKeySym) && (XK_asciitilde >= nKeySym) ) + { + *pLen = 1; + pPrintable[ 0 ] = (char)nKeySym; + } + break; + case XLookupBoth: + case XLookupChars: + + /* nothing to, char allready in pPrintable */ + break; + } + } + + if( !bNumLockFromXS_ + && (IsCursorKey(nKeySym) + || IsFunctionKey(nKeySym) + || IsKeypadKey(nKeySym) + || XK_Delete == nKeySym ) ) + { + // Bei einigen X-Servern muss man bei den Keypadtasten + // schon sehr genau hinschauen. ZB. Solaris XServer: + // 2, 4, 6, 8 werden als Cursorkeys klassifiziert (Up, Down, Left, Right + // 1, 3, 5, 9 werden als Functionkeys klassifiziert (F27,F29,F33,F35) + // 0 als Keypadkey und der Dezimalpunkt gar nicht (KP_Insert) + KeySym nNewKeySym = XLookupKeysym( pEvent, nNumLockIndex_ ); + if( nNewKeySym != NoSymbol ) + nKeySym = nNewKeySym; + } + + // Now get the unmodified KeySym for KeyCode retrieval + // try to strip off modifiers, e.g. Ctrl-$ becomes Ctrl-Shift-4 + *pUnmodifiedKeySym = XKeycodeToKeysym( GetDisplay(), pEvent->keycode, 0); + + return nKeySym; +} + +// Pointer +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +#define MAKE_BITMAP( name ) \ + XCreateBitmapFromData( pDisp_, \ + DefaultRootWindow( pDisp_ ), \ + reinterpret_cast<const char*>(name##_bits), \ + name##_width, \ + name##_height ) + +#define MAKE_CURSOR( name ) \ + aCursBitmap = MAKE_BITMAP( name##curs ); \ + aMaskBitmap = MAKE_BITMAP( name##mask ); \ + nXHot = name##curs_x_hot; \ + nYHot = name##curs_y_hot + +XLIB_Cursor SalDisplay::GetPointer( int ePointerStyle ) +{ + if( ePointerStyle >= POINTER_COUNT ) + return 0; + + XLIB_Cursor &aCur = aPointerCache_[ePointerStyle]; + + if( aCur != None ) + return aCur; + + Pixmap aCursBitmap = None, aMaskBitmap = None; + unsigned int nXHot = 0, nYHot = 0; + + switch( ePointerStyle ) + { + case POINTER_NULL: + MAKE_CURSOR( null ); + break; + case POINTER_ARROW: + aCur = XCreateFontCursor( pDisp_, XC_left_ptr ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_WAIT: + aCur = XCreateFontCursor( pDisp_, XC_watch ); + break; + case POINTER_TEXT: // Mouse Pointer ist ein "I" Beam + aCur = XCreateFontCursor( pDisp_, XC_xterm ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_HELP: + aCur = XCreateFontCursor( pDisp_, XC_question_arrow ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_CROSS: // Mouse Pointer ist ein Kreuz + aCur = XCreateFontCursor( pDisp_, XC_crosshair ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_NSIZE: + aCur = XCreateFontCursor( pDisp_, XC_sb_v_double_arrow ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_SSIZE: + aCur = XCreateFontCursor( pDisp_, XC_sb_v_double_arrow ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_WSIZE: + aCur = XCreateFontCursor( pDisp_, XC_sb_h_double_arrow ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_ESIZE: + aCur = XCreateFontCursor( pDisp_, XC_sb_h_double_arrow ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_WINDOW_NSIZE: + aCur = XCreateFontCursor( pDisp_, XC_top_side ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_WINDOW_SSIZE: + aCur = XCreateFontCursor( pDisp_, XC_bottom_side ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_WINDOW_WSIZE: + aCur = XCreateFontCursor( pDisp_, XC_left_side ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_WINDOW_ESIZE: + aCur = XCreateFontCursor( pDisp_, XC_right_side ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_NWSIZE: + aCur = XCreateFontCursor( pDisp_, XC_top_left_corner ); + break; + case POINTER_NESIZE: + aCur = XCreateFontCursor( pDisp_, XC_top_right_corner ); + break; + case POINTER_SWSIZE: + aCur = XCreateFontCursor( pDisp_, XC_bottom_left_corner ); + break; + case POINTER_SESIZE: + aCur = XCreateFontCursor( pDisp_, XC_bottom_right_corner ); + break; + case POINTER_WINDOW_NWSIZE: + aCur = XCreateFontCursor( pDisp_, XC_top_left_corner ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_WINDOW_NESIZE: + aCur = XCreateFontCursor( pDisp_, XC_top_right_corner ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_WINDOW_SWSIZE: + aCur = XCreateFontCursor( pDisp_, XC_bottom_left_corner ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_WINDOW_SESIZE: + aCur = XCreateFontCursor( pDisp_, XC_bottom_right_corner ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_HSPLIT: + aCur = XCreateFontCursor( pDisp_, XC_sb_h_double_arrow ); + break; + case POINTER_VSPLIT: + aCur = XCreateFontCursor( pDisp_, XC_sb_v_double_arrow ); + break; + case POINTER_HSIZEBAR: + aCur = XCreateFontCursor( pDisp_, XC_sb_h_double_arrow ); // ??? + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_VSIZEBAR: + aCur = XCreateFontCursor( pDisp_, XC_sb_v_double_arrow ); // ??? + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_REFHAND: + aCur = XCreateFontCursor( pDisp_, XC_hand1 ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_HAND: + aCur = XCreateFontCursor( pDisp_, XC_hand2 ); + break; + case POINTER_MAGNIFY: + MAKE_CURSOR( magnify_ ); + break; + case POINTER_FILL: + MAKE_CURSOR( fill_ ); + break; + case POINTER_MOVE: + aCur = XCreateFontCursor( pDisp_, XC_fleur ); + break; + case POINTER_MOVEDATA: + MAKE_CURSOR( movedata_ ); + break; + case POINTER_COPYDATA: + MAKE_CURSOR( copydata_ ); + break; + case POINTER_MOVEFILE: + MAKE_CURSOR( movefile_ ); + break; + case POINTER_COPYFILE: + MAKE_CURSOR( copyfile_ ); + break; + case POINTER_MOVEFILES: + MAKE_CURSOR( movefiles_ ); + break; + case POINTER_COPYFILES: + MAKE_CURSOR( copyfiles_ ); + break; + case POINTER_NOTALLOWED: + MAKE_CURSOR( nodrop_ ); + break; + case POINTER_ROTATE: + MAKE_CURSOR( rotate_ ); + break; + case POINTER_HSHEAR: + MAKE_CURSOR( hshear_ ); + break; + case POINTER_VSHEAR: + MAKE_CURSOR( vshear_ ); + break; + case POINTER_DRAW_LINE: + MAKE_CURSOR( drawline_ ); + break; + case POINTER_DRAW_RECT: + MAKE_CURSOR( drawrect_ ); + break; + case POINTER_DRAW_POLYGON: + MAKE_CURSOR( drawpolygon_ ); + break; + case POINTER_DRAW_BEZIER: + MAKE_CURSOR( drawbezier_ ); + break; + case POINTER_DRAW_ARC: + MAKE_CURSOR( drawarc_ ); + break; + case POINTER_DRAW_PIE: + MAKE_CURSOR( drawpie_ ); + break; + case POINTER_DRAW_CIRCLECUT: + MAKE_CURSOR( drawcirclecut_ ); + break; + case POINTER_DRAW_ELLIPSE: + MAKE_CURSOR( drawellipse_ ); + break; + case POINTER_DRAW_CONNECT: + MAKE_CURSOR( drawconnect_ ); + break; + case POINTER_DRAW_TEXT: + MAKE_CURSOR( drawtext_ ); + break; + case POINTER_MIRROR: + MAKE_CURSOR( mirror_ ); + break; + case POINTER_CROOK: + MAKE_CURSOR( crook_ ); + break; + case POINTER_CROP: + MAKE_CURSOR( crop_ ); + break; + case POINTER_MOVEPOINT: + MAKE_CURSOR( movepoint_ ); + break; + case POINTER_MOVEBEZIERWEIGHT: + MAKE_CURSOR( movebezierweight_ ); + break; + case POINTER_DRAW_FREEHAND: + MAKE_CURSOR( drawfreehand_ ); + break; + case POINTER_DRAW_CAPTION: + MAKE_CURSOR( drawcaption_ ); + break; + case POINTER_PEN: // Mouse Pointer ist ein Stift + aCur = XCreateFontCursor( pDisp_, XC_pencil ); + DBG_ASSERT( aCur != None, "GetPointer: Could not define cursor" ); + break; + case POINTER_LINKDATA: + MAKE_CURSOR( linkdata_ ); + break; + case POINTER_MOVEDATALINK: + MAKE_CURSOR( movedlnk_ ); + break; + case POINTER_COPYDATALINK: + MAKE_CURSOR( copydlnk_ ); + break; + case POINTER_LINKFILE: + MAKE_CURSOR( linkfile_ ); + break; + case POINTER_MOVEFILELINK: + MAKE_CURSOR( moveflnk_ ); + break; + case POINTER_COPYFILELINK: + MAKE_CURSOR( copyflnk_ ); + break; + case POINTER_CHART: + MAKE_CURSOR( chart_ ); + break; + case POINTER_DETECTIVE: + MAKE_CURSOR( detective_ ); + break; + case POINTER_PIVOT_COL: + MAKE_CURSOR( pivotcol_ ); + break; + case POINTER_PIVOT_ROW: + MAKE_CURSOR( pivotrow_ ); + break; + case POINTER_PIVOT_FIELD: + MAKE_CURSOR( pivotfld_ ); + break; + case POINTER_PIVOT_DELETE: + MAKE_CURSOR( pivotdel_ ); + break; + case POINTER_CHAIN: + MAKE_CURSOR( chain_ ); + break; + case POINTER_CHAIN_NOTALLOWED: + MAKE_CURSOR( chainnot_ ); + break; + case POINTER_TIMEEVENT_MOVE: + MAKE_CURSOR( timemove_ ); + break; + case POINTER_TIMEEVENT_SIZE: + MAKE_CURSOR( timesize_ ); + break; + case POINTER_AUTOSCROLL_N: + MAKE_CURSOR(asn_ ); + break; + case POINTER_AUTOSCROLL_S: + MAKE_CURSOR( ass_ ); + break; + case POINTER_AUTOSCROLL_W: + MAKE_CURSOR( asw_ ); + break; + case POINTER_AUTOSCROLL_E: + MAKE_CURSOR( ase_ ); + break; + case POINTER_AUTOSCROLL_NW: + MAKE_CURSOR( asnw_ ); + break; + case POINTER_AUTOSCROLL_NE: + MAKE_CURSOR( asne_ ); + break; + case POINTER_AUTOSCROLL_SW: + MAKE_CURSOR( assw_ ); + break; + case POINTER_AUTOSCROLL_SE: + MAKE_CURSOR( asse_ ); + break; + case POINTER_AUTOSCROLL_NS: + MAKE_CURSOR( asns_ ); + break; + case POINTER_AUTOSCROLL_WE: + MAKE_CURSOR( aswe_ ); + break; + case POINTER_AUTOSCROLL_NSWE: + MAKE_CURSOR( asnswe_ ); + break; + case POINTER_AIRBRUSH: + MAKE_CURSOR( airbrush_ ); + break; + case POINTER_TEXT_VERTICAL: + MAKE_CURSOR( vertcurs_ ); + break; + + // #i32329# Enhanced table selection + case POINTER_TAB_SELECT_S: + MAKE_CURSOR( tblsels_ ); + break; + case POINTER_TAB_SELECT_E: + MAKE_CURSOR( tblsele_ ); + break; + case POINTER_TAB_SELECT_SE: + MAKE_CURSOR( tblselse_ ); + break; + case POINTER_TAB_SELECT_W: + MAKE_CURSOR( tblselw_ ); + break; + case POINTER_TAB_SELECT_SW: + MAKE_CURSOR( tblselsw_ ); + break; + + // #i20119# Paintbrush tool + case POINTER_PAINTBRUSH : + MAKE_CURSOR( paintbrush_ ); + break; + + default: + OSL_FAIL("pointer not implemented"); + aCur = XCreateFontCursor( pDisp_, XC_arrow ); + break; + } + + if( None == aCur ) + { + XColor aBlack, aWhite, aDummy; + Colormap hColormap = GetColormap(m_nDefaultScreen).GetXColormap(); + + XAllocNamedColor( pDisp_, hColormap, "black", &aBlack, &aDummy ); + XAllocNamedColor( pDisp_, hColormap, "white", &aWhite, &aDummy ); + + aCur = XCreatePixmapCursor( pDisp_, + aCursBitmap, aMaskBitmap, + &aBlack, &aWhite, + nXHot, nYHot ); + + XFreePixmap( pDisp_, aCursBitmap ); + XFreePixmap( pDisp_, aMaskBitmap ); + } + + return aCur; +} + +int SalDisplay::CaptureMouse( SalFrame *pCapture ) +{ + static const char* pEnv = getenv( "SAL_NO_MOUSEGRABS" ); + + if( !pCapture ) + { + m_pCapture = NULL; + if( !pEnv || !*pEnv ) + XUngrabPointer( GetDisplay(), CurrentTime ); + XFlush( GetDisplay() ); + return 0; + } + + m_pCapture = NULL; + + // FIXME: get rid of X11SalFrame + const SystemEnvData* pEnvData = pCapture->GetSystemData(); + if( !pEnv || !*pEnv ) + { + int ret = XGrabPointer( GetDisplay(), + (XLIB_Window)pEnvData->aWindow, + False, + PointerMotionMask| ButtonPressMask|ButtonReleaseMask, + GrabModeAsync, + GrabModeAsync, + None, + static_cast<X11SalFrame*>(pCapture)->GetCursor(), + CurrentTime ); + + if( ret != GrabSuccess ) + { + DBG_ASSERT( 1, "SalDisplay::CaptureMouse could not grab pointer\n"); + return -1; + } + } + + m_pCapture = pCapture; + return 1; +} + +// Events +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void SalDisplay::SendInternalEvent( SalFrame* pFrame, void* pData, sal_uInt16 nEvent ) +{ + if( osl_acquireMutex( hEventGuard_ ) ) + { + m_aUserEvents.push_back( SalUserEvent( pFrame, pData, nEvent ) ); + + // Notify SalXLib::Yield() of a pending event. + pXLib_->PostUserEvent(); + + osl_releaseMutex( hEventGuard_ ); + } + else { + DBG_ASSERT( 1, "SalDisplay::SendInternalEvent !acquireMutex\n" ); + } +} + +void SalDisplay::CancelInternalEvent( SalFrame* pFrame, void* pData, sal_uInt16 nEvent ) +{ + if( osl_acquireMutex( hEventGuard_ ) ) + { + if( ! m_aUserEvents.empty() ) + { + std::list< SalUserEvent >::iterator it, next; + next = m_aUserEvents.begin(); + do + { + it = next++; + if( it->m_pFrame == pFrame && + it->m_pData == pData && + it->m_nEvent == nEvent ) + { + m_aUserEvents.erase( it ); + } + } while( next != m_aUserEvents.end() ); + } + + osl_releaseMutex( hEventGuard_ ); + } + else { + DBG_ASSERT( 1, "SalDisplay::CancelInternalEvent !acquireMutex\n" ); + } +} + +sal_Bool SalX11Display::IsEvent() +{ + sal_Bool bRet = sal_False; + + if( osl_acquireMutex( hEventGuard_ ) ) + { + if( m_aUserEvents.begin() != m_aUserEvents.end() ) + bRet = sal_True; + osl_releaseMutex( hEventGuard_ ); + } + + if( bRet || XEventsQueued( pDisp_, QueuedAlready ) ) + return sal_True; + + XFlush( pDisp_ ); + return sal_False; +} + +bool SalDisplay::DispatchInternalEvent() +{ + SalFrame* pFrame = NULL; + void* pData = NULL; + sal_uInt16 nEvent = 0; + + if( osl_acquireMutex( hEventGuard_ ) ) + { + if( m_aUserEvents.begin() != m_aUserEvents.end() ) + { + pFrame = m_aUserEvents.front().m_pFrame; + pData = m_aUserEvents.front().m_pData; + nEvent = m_aUserEvents.front().m_nEvent; + + m_aUserEvents.pop_front(); + } + osl_releaseMutex( hEventGuard_ ); + } + else { + DBG_ASSERT( 1, "SalDisplay::Yield !acquireMutex\n" ); + } + + if( pFrame ) + pFrame->CallCallback( nEvent, pData ); + + return pFrame != NULL; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void SalX11Display::Yield() +{ + if( DispatchInternalEvent() ) + return; + + XEvent aEvent; + DBG_ASSERT( static_cast<SalYieldMutex*>(GetSalData()->m_pInstance->GetYieldMutex())->GetThreadId() == + osl::Thread::getCurrentIdentifier(), + "will crash soon since solar mutex not locked in SalDisplay::Yield" ); + + XNextEvent( pDisp_, &aEvent ); + + Dispatch( &aEvent ); + +#ifdef DBG_UTIL + if( pXLib_->HasXErrorOccurred() ) + { + XFlush( pDisp_ ); + PrintEvent( "SalDisplay::Yield (WasXError)", &aEvent ); + } +#endif + pXLib_->ResetXErrorOccurred(); +} + +long SalX11Display::Dispatch( XEvent *pEvent ) +{ + if( pEvent->type == XLIB_KeyPress || pEvent->type == KeyRelease ) + { + XLIB_Window aWindow = pEvent->xkey.window; + + std::list< SalFrame* >::const_iterator it; + for( it = m_aFrames.begin(); it != m_aFrames.end(); ++it ) + { + const X11SalFrame* pFrame = static_cast< const X11SalFrame* >(*it); + if( pFrame->GetWindow() == aWindow || pFrame->GetShellWindow() == aWindow ) + { + aWindow = pFrame->GetWindow(); + break; + } + } + if( it != m_aFrames.end() ) + { + if ( mpInputMethod->FilterEvent( pEvent , aWindow ) ) + return 0; + } + } + else + if ( mpInputMethod->FilterEvent( pEvent, None ) ) + return 0; + + SalInstance* pInstance = GetSalData()->m_pInstance; + pInstance->CallEventCallback( pEvent, sizeof( XEvent ) ); + + switch( pEvent->type ) + { + case MotionNotify: + while( XCheckWindowEvent( pEvent->xany.display, + pEvent->xany.window, + ButtonMotionMask, + pEvent ) ) + ; + m_nLastUserEventTime = pEvent->xmotion.time; + break; + case PropertyNotify: + if( pEvent->xproperty.atom == getWMAdaptor()->getAtom( WMAdaptor::VCL_SYSTEM_SETTINGS ) ) + { + for( unsigned int i = 0; i < m_aScreens.size(); i++ ) + { + if( pEvent->xproperty.window == m_aScreens[i].m_aRefWindow ) + { + std::list< SalFrame* >::const_iterator it; + for( it = m_aFrames.begin(); it != m_aFrames.end(); ++it ) + (*it)->CallCallback( SALEVENT_SETTINGSCHANGED, NULL ); + return 0; + } + } + } + break; + case MappingNotify: + if( MappingKeyboard == pEvent->xmapping.request || + MappingModifier == pEvent->xmapping.request ) + { + XRefreshKeyboardMapping( &pEvent->xmapping ); + if( MappingModifier == pEvent->xmapping.request ) + ModifierMapping(); + if( MappingKeyboard == pEvent->xmapping.request ) // refresh mapping + GetKeyboardName( true ); + } + break; + case ButtonPress: + case ButtonRelease: + m_nLastUserEventTime = pEvent->xbutton.time; + break; + case XLIB_KeyPress: + case KeyRelease: + m_nLastUserEventTime = pEvent->xkey.time; + break; + default: + + if ( GetKbdExtension()->UseExtension() + && GetKbdExtension()->GetEventBase() == pEvent->type ) + { + GetKbdExtension()->Dispatch( pEvent ); + return 1; + } + break; + } + + std::list< SalFrame* >::iterator it; + for( it = m_aFrames.begin(); it != m_aFrames.end(); ++it ) + { + X11SalFrame* pFrame = static_cast< X11SalFrame* >(*it); + XLIB_Window aDispatchWindow = pEvent->xany.window; + if( pFrame->GetWindow() == aDispatchWindow + || pFrame->GetShellWindow() == aDispatchWindow + || pFrame->GetForeignParent() == aDispatchWindow + ) + { + return pFrame->Dispatch( pEvent ); + } + if( pEvent->type == ConfigureNotify && pEvent->xconfigure.window == pFrame->GetStackingWindow() ) + { + return pFrame->Dispatch( pEvent ); + } + } + + // dispatch to salobjects + X11SalObject::Dispatch( pEvent ); + + // is this perhaps a root window that changed size ? + processRandREvent( pEvent ); + + return 0; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void SalDisplay::PrintEvent( const ByteString &rComment, + XEvent *pEvent ) const +{ + if( pEvent->type <= MappingNotify ) + { + fprintf( stderr, "[%s] %s s=%d w=%ld\n", + rComment.GetBuffer(), + EventNames[pEvent->type], + pEvent->xany.send_event, + pEvent->xany.window ); + + switch( pEvent->type ) + { + case XLIB_KeyPress: + case KeyRelease: + fprintf( stderr, "\t\ts=%d c=%d\n", + pEvent->xkey.state, + pEvent->xkey.keycode ); + break; + + case ButtonPress: + case ButtonRelease: + fprintf( stderr, "\t\ts=%d b=%d x=%d y=%d rx=%d ry=%d\n", + pEvent->xbutton.state, + pEvent->xbutton.button, + pEvent->xbutton.x, + pEvent->xbutton.y, + pEvent->xbutton.x_root, + pEvent->xbutton.y_root ); + break; + + case MotionNotify: + fprintf( stderr, "\t\ts=%d x=%d y=%d\n", + pEvent->xmotion.state, + pEvent->xmotion.x, + pEvent->xmotion.y ); + break; + + case EnterNotify: + case LeaveNotify: + fprintf( stderr, "\t\tm=%d f=%d x=%d y=%d\n", + pEvent->xcrossing.mode, + pEvent->xcrossing.focus, + pEvent->xcrossing.x, + pEvent->xcrossing.y ); + break; + + case FocusIn: + case FocusOut: + fprintf( stderr, "\t\tm=%d d=%d\n", + pEvent->xfocus.mode, + pEvent->xfocus.detail ); + break; + + case Expose: + case GraphicsExpose: + fprintf( stderr, "\t\tc=%d %d*%d %d+%d\n", + pEvent->xexpose.count, + pEvent->xexpose.width, + pEvent->xexpose.height, + pEvent->xexpose.x, + pEvent->xexpose.y ); + break; + + case VisibilityNotify: + fprintf( stderr, "\t\ts=%d\n", + pEvent->xvisibility.state ); + break; + + case CreateNotify: + case DestroyNotify: + break; + + case MapNotify: + case UnmapNotify: + break; + + case ReparentNotify: + fprintf( stderr, "\t\tp=%d x=%d y=%d\n", + sal::static_int_cast< int >(pEvent->xreparent.parent), + pEvent->xreparent.x, + pEvent->xreparent.y ); + break; + + case ConfigureNotify: + fprintf( stderr, "\t\tb=%d %d*%d %d+%d\n", + pEvent->xconfigure.border_width, + pEvent->xconfigure.width, + pEvent->xconfigure.height, + pEvent->xconfigure.x, + pEvent->xconfigure.y ); + break; + + case PropertyNotify: + fprintf( stderr, "\t\ta=%s (0x%X)\n", + GetAtomName( pDisp_, pEvent->xproperty.atom ), + sal::static_int_cast< unsigned int >( + pEvent->xproperty.atom) ); + break; + + case ColormapNotify: + fprintf( stderr, "\t\tc=%ld n=%d s=%d\n", + pEvent->xcolormap.colormap, + pEvent->xcolormap.c_new, + pEvent->xcolormap.state ); + break; + + case ClientMessage: + fprintf( stderr, "\t\ta=%s (0x%X) f=%i [0x%lX,0x%lX,0x%lX,0x%lX,0x%lX])\n", + GetAtomName( pDisp_, pEvent->xclient.message_type ), + sal::static_int_cast< unsigned int >( + pEvent->xclient.message_type), + pEvent->xclient.format, + pEvent->xclient.data.l[0], + pEvent->xclient.data.l[1], + pEvent->xclient.data.l[2], + pEvent->xclient.data.l[3], + pEvent->xclient.data.l[4] ); + break; + + case MappingNotify: + fprintf( stderr, "\t\tr=%sd\n", + MappingModifier == pEvent->xmapping.request + ? "MappingModifier" + : MappingKeyboard == pEvent->xmapping.request + ? "MappingKeyboard" + : "MappingPointer" ); + + break; + } + } + else + fprintf( stderr, "[%s] %d s=%d w=%ld\n", + rComment.GetBuffer(), + pEvent->type, + pEvent->xany.send_event, + pEvent->xany.window ); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void SalDisplay::PrintInfo() const +{ + if( IsDisplay() ) + { + fprintf( stderr, "\n" ); + fprintf( stderr, "Environment\n" ); + fprintf( stderr, "\t$XENVIRONMENT \t\"%s\"\n", + GetEnv( "XENVIRONMENT" ) ); + fprintf( stderr, "\t$DISPLAY \t\"%s\"\n", + GetEnv( "DISPLAY" ) ); + fprintf( stderr, "\t$SAL_VISUAL \t\"%s\"\n", + GetEnv( "SAL_VISUAL" ) ); + fprintf( stderr, "\t$SAL_FONTPATH \t\"%s\"\n", + GetEnv( "SAL_FONTPATH" ) ); + fprintf( stderr, "\t$SAL_NOSEGV \t\"%s\"\n", + GetEnv( "SAL_NOSEGV" ) ); + fprintf( stderr, "\t$SAL_IGNOREXERRORS\t\"%s\"\n", + GetEnv( "SAL_IGNOREXERRORS" ) ); + fprintf( stderr, "\t$SAL_PROPERTIES \t\"%s\"\n", + GetEnv( "SAL_PROPERTIES" ) ); + fprintf( stderr, "\t$SAL_WM \t\"%s\"\n", + GetEnv( "SAL_WM" ) ); + fprintf( stderr, "\t$SAL_SYNCHRONIZE \t\"%s\"\n", + GetEnv( "SAL_SYNCHRONIZE" ) ); + + char sHostname[ 120 ]; + gethostname (sHostname, 120 ); + fprintf( stderr, "Client\n" ); + fprintf( stderr, "\tHost \t\"%s\"\n", + sHostname ); + + fprintf( stderr, "Display\n" ); + fprintf( stderr, "\tHost \t\"%s\"\n", + DisplayString(pDisp_) ); + fprintf( stderr, "\tVendor (Release) \t\"%s (%d)\"\n", + ServerVendor(pDisp_), VendorRelease(pDisp_) ); + fprintf( stderr, "\tProtocol \t%d.%d\n", + ProtocolVersion(pDisp_), ProtocolRevision(pDisp_) ); + fprintf( stderr, "\tScreen (count,def)\t%d (%d,%d)\n", + m_nDefaultScreen, ScreenCount(pDisp_), DefaultScreen(pDisp_) ); + fprintf( stderr, "\tshift ctrl alt \t%s (0x%X) %s (0x%X) %s (0x%X)\n", + KeyStr( nShiftKeySym_ ), sal::static_int_cast< unsigned int >(nShiftKeySym_), + KeyStr( nCtrlKeySym_ ), sal::static_int_cast< unsigned int >(nCtrlKeySym_), + KeyStr( nMod1KeySym_ ), sal::static_int_cast< unsigned int >(nMod1KeySym_) ); + if( XExtendedMaxRequestSize(pDisp_) * 4 ) + fprintf( stderr, "\tXMaxRequestSize \t%ld %ld [bytes]\n", + XMaxRequestSize(pDisp_) * 4, XExtendedMaxRequestSize(pDisp_) * 4 ); + if( GetProperties() != PROPERTY_DEFAULT ) + fprintf( stderr, "\tProperties \t0x%lX\n", GetProperties() ); + if( eWindowManager_ != otherwm ) + fprintf( stderr, "\tWindowmanager \t%d\n", eWindowManager_ ); + fprintf( stderr, "\tWMName \t%s\n", rtl::OUStringToOString( getWMAdaptor()->getWindowManagerName(), osl_getThreadTextEncoding() ).getStr() ); + } + fprintf( stderr, "Screen\n" ); + fprintf( stderr, "\tResolution/Size \t%ld*%ld %ld*%ld %.1lf\"\n", + aResolution_.A(), aResolution_.B(), + m_aScreens[m_nDefaultScreen].m_aSize.Width(), m_aScreens[m_nDefaultScreen].m_aSize.Height(), + Hypothenuse( DisplayWidthMM ( pDisp_, m_nDefaultScreen ), + DisplayHeightMM( pDisp_, m_nDefaultScreen ) ) / 25.4 ); + fprintf( stderr, "\tBlack&White \t%lu %lu\n", + GetColormap(m_nDefaultScreen).GetBlackPixel(), GetColormap(m_nDefaultScreen).GetWhitePixel() ); + fprintf( stderr, "\tRGB \t0x%lx 0x%lx 0x%lx\n", + GetVisual(m_nDefaultScreen).red_mask, GetVisual(m_nDefaultScreen).green_mask, GetVisual(m_nDefaultScreen).blue_mask ); + fprintf( stderr, "\tVisual \t%d-bit %s ID=0x%x\n", + GetVisual(m_nDefaultScreen).GetDepth(), + VisualClassName[ GetVisual(m_nDefaultScreen).GetClass() ], + sal::static_int_cast< unsigned int >(GetVisual(m_nDefaultScreen).GetVisualId()) ); +} + +void SalDisplay::addXineramaScreenUnique( int i, long i_nX, long i_nY, long i_nWidth, long i_nHeight ) +{ + // see if any frame buffers are at the same coordinates + // this can happen with weird configuration e.g. on + // XFree86 and Clone displays + const size_t nScreens = m_aXineramaScreens.size(); + for( size_t n = 0; n < nScreens; n++ ) + { + if( m_aXineramaScreens[n].Left() == i_nX && + m_aXineramaScreens[n].Top() == i_nY ) + { + if( m_aXineramaScreens[n].GetWidth() < i_nWidth || + m_aXineramaScreens[n].GetHeight() < i_nHeight ) + { + m_aXineramaScreenIndexMap[i] = n; + m_aXineramaScreens[n].SetSize( Size( i_nWidth, i_nHeight ) ); + } + return; + } + } + m_aXineramaScreenIndexMap[i] = m_aXineramaScreens.size(); + m_aXineramaScreens.push_back( Rectangle( Point( i_nX, i_nY ), Size( i_nWidth, i_nHeight ) ) ); +} + +void SalDisplay::InitXinerama() +{ + if( m_aScreens.size() > 1 ) + { + m_bXinerama = false; + return; // multiple screens mean no xinerama + } +#ifdef USE_XINERAMA +#if defined(USE_XINERAMA_XSUN) + int nFramebuffers = 1; + if( XineramaGetState( pDisp_, m_nDefaultScreen ) ) + { + XRectangle pFramebuffers[MAXFRAMEBUFFERS]; + unsigned char hints[MAXFRAMEBUFFERS]; + int result = XineramaGetInfo( pDisp_, + m_nDefaultScreen, + pFramebuffers, + hints, + &nFramebuffers ); + if( result > 0 && nFramebuffers > 1 ) + { + m_bXinerama = true; + m_aXineramaScreens = std::vector<Rectangle>(); + m_aXineramaScreenIndexMap = std::vector<int>(nFramebuffers); + for( int i = 0; i < nFramebuffers; i++ ) + addXineramaScreenUnique( i, pFramebuffers[i].x, + pFramebuffers[i].y, + pFramebuffers[i].width, + pFramebuffers[i].height ); + } + } +#elif defined(USE_XINERAMA_XORG) +if( XineramaIsActive( pDisp_ ) ) +{ + int nFramebuffers = 1; + XineramaScreenInfo* pScreens = XineramaQueryScreens( pDisp_, &nFramebuffers ); + if( pScreens ) + { + if( nFramebuffers > 1 ) + { + m_aXineramaScreens = std::vector<Rectangle>(); + m_aXineramaScreenIndexMap = std::vector<int>(nFramebuffers); + for( int i = 0; i < nFramebuffers; i++ ) + { + addXineramaScreenUnique( i, pScreens[i].x_org, + pScreens[i].y_org, + pScreens[i].width, + pScreens[i].height ); + } + m_bXinerama = m_aXineramaScreens.size() > 1; + } + XFree( pScreens ); + } +} +#endif +#if OSL_DEBUG_LEVEL > 1 + if( m_bXinerama ) + { + for( std::vector< Rectangle >::const_iterator it = m_aXineramaScreens.begin(); it != m_aXineramaScreens.end(); ++it ) + fprintf( stderr, "Xinerama screen: %ldx%ld+%ld+%ld\n", it->GetWidth(), it->GetHeight(), it->Left(), it->Top() ); + } +#endif +#endif // USE_XINERAMA +} + +void SalDisplay::registerFrame( SalFrame* pFrame ) +{ + m_aFrames.push_front( pFrame ); +} + +void SalDisplay::deregisterFrame( SalFrame* pFrame ) +{ + if( osl_acquireMutex( hEventGuard_ ) ) + { + std::list< SalUserEvent >::iterator it = m_aUserEvents.begin(); + while ( it != m_aUserEvents.end() ) + { + if( it->m_pFrame == pFrame ) + it = m_aUserEvents.erase( it ); + else + ++it; + } + osl_releaseMutex( hEventGuard_ ); + } + else { + OSL_FAIL( "SalDisplay::deregisterFrame !acquireMutex\n" ); + } + + m_aFrames.remove( pFrame ); +} + + +extern "C" +{ + static Bool timestamp_predicate( Display*, XEvent* i_pEvent, XPointer i_pArg ) + { + SalDisplay* pSalDisplay = reinterpret_cast<SalDisplay*>(i_pArg); + if( i_pEvent->type == PropertyNotify && + i_pEvent->xproperty.window == pSalDisplay->GetDrawable( pSalDisplay->GetDefaultScreenNumber() ) && + i_pEvent->xproperty.atom == pSalDisplay->getWMAdaptor()->getAtom( WMAdaptor::SAL_GETTIMEEVENT ) + ) + return True; + + return False; + } +} + +XLIB_Time SalDisplay::GetLastUserEventTime( bool i_bAlwaysReget ) const +{ + if( m_nLastUserEventTime == CurrentTime || i_bAlwaysReget ) + { + // get current server time + unsigned char c = 0; + XEvent aEvent; + Atom nAtom = getWMAdaptor()->getAtom( WMAdaptor::SAL_GETTIMEEVENT ); + XChangeProperty( GetDisplay(), GetDrawable( GetDefaultScreenNumber() ), + nAtom, nAtom, 8, PropModeReplace, &c, 1 ); + XFlush( GetDisplay() ); + + if( ! XIfEventWithTimeout( &aEvent, (XPointer)this, timestamp_predicate ) ) + { + // this should not happen at all; still sometimes it happens + aEvent.xproperty.time = CurrentTime; + } + + m_nLastUserEventTime = aEvent.xproperty.time; + } + return m_nLastUserEventTime; +} + +bool SalDisplay::XIfEventWithTimeout( XEvent* o_pEvent, XPointer i_pPredicateData, + X_if_predicate i_pPredicate, long i_nTimeout ) const +{ + /* #i99360# ugly workaround an X11 library bug + this replaces the following call: + XIfEvent( GetDisplay(), o_pEvent, i_pPredicate, i_pPredicateData ); + */ + bool bRet = true; + + if( ! XCheckIfEvent( GetDisplay(), o_pEvent, i_pPredicate, i_pPredicateData ) ) + { + // wait for some event to arrive + struct pollfd aFD; + aFD.fd = ConnectionNumber(GetDisplay()); + aFD.events = POLLIN; + aFD.revents = 0; + poll( &aFD, 1, i_nTimeout ); + if( ! XCheckIfEvent( GetDisplay(), o_pEvent, i_pPredicate, i_pPredicateData ) ) + { + poll( &aFD, 1, i_nTimeout ); // try once more for a packet of events from the Xserver + if( ! XCheckIfEvent( GetDisplay(), o_pEvent, i_pPredicate, i_pPredicateData ) ) + { + bRet = false; + } + } + } + return bRet; +} + +// -=-= SalVisual -=-=--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +SalVisual::SalVisual() +{ + rtl_zeroMemory( this, sizeof( SalVisual ) ); +} + +SalVisual::SalVisual( const XVisualInfo* pXVI ) +{ + *(XVisualInfo*)this = *pXVI; + if( GetClass() == TrueColor ) + { + nRedShift_ = sal_Shift( red_mask ); + nGreenShift_ = sal_Shift( green_mask ); + nBlueShift_ = sal_Shift( blue_mask ); + + nRedBits_ = sal_significantBits( red_mask ); + nGreenBits_ = sal_significantBits( green_mask ); + nBlueBits_ = sal_significantBits( blue_mask ); + + if( GetDepth() == 24 ) + if( red_mask == 0xFF0000 ) + if( green_mask == 0xFF00 ) + if( blue_mask == 0xFF ) + eRGBMode_ = RGB; + else + eRGBMode_ = otherSalRGB; + else if( blue_mask == 0xFF00 ) + if( green_mask == 0xFF ) + eRGBMode_ = RBG; + else + eRGBMode_ = otherSalRGB; + else + eRGBMode_ = otherSalRGB; + else if( green_mask == 0xFF0000 ) + if( red_mask == 0xFF00 ) + if( blue_mask == 0xFF ) + eRGBMode_ = GRB; + else + eRGBMode_ = otherSalRGB; + else if( blue_mask == 0xFF00 ) + if( red_mask == 0xFF ) + eRGBMode_ = GBR; + else + eRGBMode_ = otherSalRGB; + else + eRGBMode_ = otherSalRGB; + else if( blue_mask == 0xFF0000 ) + if( red_mask == 0xFF00 ) + if( green_mask == 0xFF ) + eRGBMode_ = BRG; + else + eRGBMode_ = otherSalRGB; + else if( green_mask == 0xFF00 ) + if( red_mask == 0xFF ) + eRGBMode_ = BGR; + else + eRGBMode_ = otherSalRGB; + else + eRGBMode_ = otherSalRGB; + else + eRGBMode_ = otherSalRGB; + else + eRGBMode_ = otherSalRGB; + } +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +SalVisual::~SalVisual() +{ + if( -1 == screen && VisualID(-1) == visualid ) delete visual; +} + +// Konvertiert die Reihenfolge der Bytes eines Pixel in Bytes eines SalColors +// fuer die 6 XXXA ist das nicht reversibel +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// SalColor is RGB (ABGR) a=0xFF000000, r=0xFF0000, g=0xFF00, b=0xFF + +#define SALCOLOR RGB +#define SALCOLORREVERSE BGR + +sal_Bool SalVisual::Convert( int &n0, int &n1, int &n2, int &n3 ) +{ + int n; + + switch( GetMode() ) + { + case otherSalRGB: + return sal_False; + case SALCOLOR: + break; + case SALCOLORREVERSE: + case RBG: + case BRG: + case GBR: + case GRB: + return Convert( n0, n1, n2 ); + case RGBA: + n = n0; + n0 = n1; + n1 = n2; + n2 = n3; + n3 = n; + break; + case BGRA: + case RBGA: + case BRGA: + case GBRA: + case GRBA: + default: + fprintf( stderr, "SalVisual::Convert %d\n", GetMode() ); + abort(); + } + return sal_True; +} + +sal_Bool SalVisual::Convert( int &n0, int &n1, int &n2 ) +{ + int n; + + switch( GetMode() ) + { + case otherSalRGB: + return sal_False; + case SALCOLOR: + break; + case RBG: + n = n0; + n0 = n1; + n1 = n; + break; + case GRB: + n = n1; + n1 = n2; + n2 = n; + break; + case SALCOLORREVERSE: + n = n0; + n0 = n2; + n2 = n; + break; + case BRG: + n = n0; + n0 = n1; + n1 = n2; + n2 = n; + break; + case GBR: + n = n2; + n2 = n1; + n1 = n0; + n0 = n; + break; + default: + fprintf( stderr, "SalVisual::Convert %d\n", GetMode() ); + abort(); + } + return sal_True; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +SalColor SalVisual::GetTCColor( Pixel nPixel ) const +{ + if( SALCOLOR == eRGBMode_ ) + return (SalColor)nPixel; + + if( SALCOLORREVERSE == eRGBMode_ ) + return MAKE_SALCOLOR( (nPixel & 0x0000FF), + (nPixel & 0x00FF00) >> 8, + (nPixel & 0xFF0000) >> 16); + + Pixel r = nPixel & red_mask; + Pixel g = nPixel & green_mask; + Pixel b = nPixel & blue_mask; + + if( otherSalRGB != eRGBMode_ ) // 8+8+8=24 + return MAKE_SALCOLOR( r >> nRedShift_, + g >> nGreenShift_, + b >> nBlueShift_ ); + + if( nRedShift_ > 0 ) r >>= nRedShift_; else r <<= -nRedShift_; + if( nGreenShift_ > 0 ) g >>= nGreenShift_; else g <<= -nGreenShift_; + if( nBlueShift_ > 0 ) b >>= nBlueShift_; else b <<= -nBlueShift_; + + if( nRedBits_ != 8 ) + r |= (r & 0xff) >> (8-nRedBits_); + if( nGreenBits_ != 8 ) + g |= (g & 0xff) >> (8-nGreenBits_); + if( nBlueBits_ != 8 ) + b |= (b & 0xff) >> (8-nBlueBits_); + + return MAKE_SALCOLOR( r, g, b ); +} + +Pixel SalVisual::GetTCPixel( SalColor nSalColor ) const +{ + if( SALCOLOR == eRGBMode_ ) + return (Pixel)nSalColor; + + Pixel r = (Pixel)SALCOLOR_RED( nSalColor ); + Pixel g = (Pixel)SALCOLOR_GREEN( nSalColor ); + Pixel b = (Pixel)SALCOLOR_BLUE( nSalColor ); + + if( SALCOLORREVERSE == eRGBMode_ ) + return (b << 16) | (g << 8) | (r); + + if( otherSalRGB != eRGBMode_ ) // 8+8+8=24 + return (r << nRedShift_) | (g << nGreenShift_) | (b << nBlueShift_); + + if( nRedShift_ > 0 ) r <<= nRedShift_; else r >>= -nRedShift_; + if( nGreenShift_ > 0 ) g <<= nGreenShift_; else g >>= -nGreenShift_; + if( nBlueShift_ > 0 ) b <<= nBlueShift_; else b >>= -nBlueShift_; + + return (r&red_mask) | (g&green_mask) | (b&blue_mask); +} + +// -=-= SalColormap -=--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +SalColormap::SalColormap( const SalDisplay *pDisplay, Colormap hColormap, int nScreen ) + : m_pDisplay( pDisplay ), + m_hColormap( hColormap ), + m_nScreen( nScreen ) +{ + m_aVisual = m_pDisplay->GetVisual( m_nScreen ); + + XColor aColor; + + GetXPixel( aColor, 0x00, 0x00, 0x00 ); + m_nBlackPixel = aColor.pixel; + + GetXPixel( aColor, 0xFF, 0xFF, 0xFF ); + m_nWhitePixel = aColor.pixel; + + m_nUsed = 1 << m_aVisual.GetDepth(); + + if( m_aVisual.GetClass() == PseudoColor ) + { + int r, g, b; + + // black, white, gray, ~gray = 4 + GetXPixels( aColor, 0xC0, 0xC0, 0xC0 ); + + // light colors: 3 * 2 = 6 + + GetXPixels( aColor, 0x00, 0x00, 0xFF ); + GetXPixels( aColor, 0x00, 0xFF, 0x00 ); + GetXPixels( aColor, 0x00, 0xFF, 0xFF ); + + // standard colors: 7 * 2 = 14 + GetXPixels( aColor, 0x00, 0x00, 0x80 ); + GetXPixels( aColor, 0x00, 0x80, 0x00 ); + GetXPixels( aColor, 0x00, 0x80, 0x80 ); + GetXPixels( aColor, 0x80, 0x00, 0x00 ); + GetXPixels( aColor, 0x80, 0x00, 0x80 ); + GetXPixels( aColor, 0x80, 0x80, 0x00 ); + GetXPixels( aColor, 0x80, 0x80, 0x80 ); + GetXPixels( aColor, 0x00, 0xB8, 0xFF ); // Blue 7 + + // cube: 6*6*6 - 8 = 208 + for( r = 0; r < 0x100; r += 0x33 ) // 0x33, 0x66, 0x99, 0xCC, 0xFF + for( g = 0; g < 0x100; g += 0x33 ) + for( b = 0; b < 0x100; b += 0x33 ) + GetXPixels( aColor, r, g, b ); + + // gray: 16 - 6 = 10 + for( g = 0x11; g < 0xFF; g += 0x11 ) + GetXPixels( aColor, g, g, g ); + + // green: 16 - 6 = 10 + for( g = 0x11; g < 0xFF; g += 0x11 ) + GetXPixels( aColor, 0, g, 0 ); + + // red: 16 - 6 = 10 + for( r = 0x11; r < 0xFF; r += 0x11 ) + GetXPixels( aColor, r, 0, 0 ); + + // blue: 16 - 6 = 10 + for( b = 0x11; b < 0xFF; b += 0x11 ) + GetXPixels( aColor, 0, 0, b ); + } +} + +// PseudoColor +SalColormap::SalColormap( const BitmapPalette &rPalette ) + : m_pDisplay( GetX11SalData()->GetDisplay() ), + m_hColormap( None ), + m_nWhitePixel( SALCOLOR_NONE ), + m_nBlackPixel( SALCOLOR_NONE ), + m_nUsed( rPalette.GetEntryCount() ), + m_nScreen( GetX11SalData()->GetDisplay()->GetDefaultScreenNumber() ) +{ + m_aPalette = std::vector<SalColor>(m_nUsed); + + for( unsigned int i = 0; i < m_nUsed; i++ ) + { + const BitmapColor &rColor = rPalette[i]; + m_aPalette[i] = MAKE_SALCOLOR( rColor.GetRed(), + rColor.GetGreen(), + rColor.GetBlue() ); + if( (m_nBlackPixel == SALCOLOR_NONE) && (SALCOLOR_BLACK == m_aPalette[i]) ) + m_nBlackPixel = i; + else if( (m_nWhitePixel == SALCOLOR_NONE) && (SALCOLOR_WHITE == m_aPalette[i]) ) + m_nWhitePixel = i; + } +} + +// MonoChrome +SalColormap::SalColormap() + : m_pDisplay( GetX11SalData()->GetDisplay() ), + m_hColormap( None ), + m_nWhitePixel( 1 ), + m_nBlackPixel( 0 ), + m_nUsed( 2 ), + m_nScreen( 0 ) +{ + if( m_pDisplay ) + m_nScreen = m_pDisplay->GetDefaultScreenNumber(); + m_aPalette = std::vector<SalColor>(m_nUsed); + + m_aPalette[m_nBlackPixel] = SALCOLOR_BLACK; + m_aPalette[m_nWhitePixel] = SALCOLOR_WHITE; +} + +// TrueColor +SalColormap::SalColormap( sal_uInt16 nDepth ) + : m_pDisplay( GetX11SalData()->GetDisplay() ), + m_hColormap( None ), + m_nWhitePixel( (1 << nDepth) - 1 ), + m_nBlackPixel( 0x00000000 ), + m_nUsed( 1 << nDepth ), + m_nScreen( GetX11SalData()->GetDisplay()->GetDefaultScreenNumber() ) +{ + const SalVisual *pVisual = &m_pDisplay->GetVisual( m_nScreen ); + + if( pVisual->GetClass() == TrueColor && pVisual->GetDepth() == nDepth ) + m_aVisual = *pVisual; + else + { + XVisualInfo aVI; + + if( !XMatchVisualInfo( m_pDisplay->GetDisplay(), + m_pDisplay->GetDefaultScreenNumber(), + nDepth, + TrueColor, + &aVI ) ) + { + aVI.visual = new Visual(); + aVI.visualid = (VisualID)0; // beware of temporary destructor below + aVI.screen = 0; + aVI.depth = nDepth; + aVI.c_class = TrueColor; + if( 24 == nDepth ) // 888 + { + aVI.red_mask = 0xFF0000; + aVI.green_mask = 0x00FF00; + aVI.blue_mask = 0x0000FF; + } + else if( 16 == nDepth ) // 565 + { + aVI.red_mask = 0x00F800; + aVI.green_mask = 0x0007E0; + aVI.blue_mask = 0x00001F; + } + else if( 15 == nDepth ) // 555 + { + aVI.red_mask = 0x007C00; + aVI.green_mask = 0x0003E0; + aVI.blue_mask = 0x00001F; + } + else if( 12 == nDepth ) // 444 + { + aVI.red_mask = 0x000F00; + aVI.green_mask = 0x0000F0; + aVI.blue_mask = 0x00000F; + } + else if( 8 == nDepth ) // 332 + { + aVI.red_mask = 0x0000E0; + aVI.green_mask = 0x00001C; + aVI.blue_mask = 0x000003; + } + else + { + aVI.red_mask = 0x000000; + aVI.green_mask = 0x000000; + aVI.blue_mask = 0x000000; + } + aVI.colormap_size = 0; + aVI.bits_per_rgb = 8; + + aVI.visual->ext_data = NULL; + aVI.visual->visualid = aVI.visualid; + aVI.visual->c_class = aVI.c_class; + aVI.visual->red_mask = aVI.red_mask; + aVI.visual->green_mask = aVI.green_mask; + aVI.visual->blue_mask = aVI.blue_mask; + aVI.visual->bits_per_rgb = aVI.bits_per_rgb; + aVI.visual->map_entries = aVI.colormap_size; + + m_aVisual = SalVisual( &aVI ); + // give ownership of constructed Visual() to m_aVisual + // see SalVisual destructor + m_aVisual.visualid = (VisualID)-1; + m_aVisual.screen = -1; + } + else + m_aVisual = SalVisual( &aVI ); + } +} + +SalColormap::~SalColormap() +{ +#ifdef DBG_UTIL + m_hColormap = (Colormap)ILLEGAL_POINTER; + m_pDisplay = (SalDisplay*)ILLEGAL_POINTER; +#endif +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void SalColormap::SetPalette( const BitmapPalette &rPalette ) +{ + if( this != &GetX11SalData()->GetDisplay()->GetColormap(m_nScreen) ) + { + m_nBlackPixel = SALCOLOR_NONE; + m_nWhitePixel = SALCOLOR_NONE; + } + + if( rPalette.GetEntryCount() > m_nUsed ) + { + m_nBlackPixel = SALCOLOR_NONE; + m_nWhitePixel = SALCOLOR_NONE; + m_nUsed = rPalette.GetEntryCount(); + m_aPalette = std::vector<SalColor>(m_nUsed); + } + + for( int i = 0; i < rPalette.GetEntryCount(); i++ ) + { + const BitmapColor &rColor = rPalette[i]; + m_aPalette[i] = MAKE_SALCOLOR( rColor.GetRed(), + rColor.GetGreen(), + rColor.GetBlue() ); + if( (m_nBlackPixel == SALCOLOR_NONE) && (SALCOLOR_BLACK == m_aPalette[i]) ) + m_nBlackPixel = i; + else if( (m_nWhitePixel == SALCOLOR_NONE) && (SALCOLOR_WHITE == m_aPalette[i]) ) + m_nWhitePixel = i; + } +} + +void SalColormap::GetPalette() +{ + Pixel i; + m_aPalette = std::vector<SalColor>(m_nUsed); + + XColor *aColor = new XColor[m_nUsed]; + + for( i = 0; i < m_nUsed; i++ ) + { + aColor[i].red = aColor[i].green = aColor[i].blue = 0; + aColor[i].pixel = i; + } + + XQueryColors( m_pDisplay->GetDisplay(), m_hColormap, aColor, m_nUsed ); + + for( i = 0; i < m_nUsed; i++ ) + { + m_aPalette[i] = MAKE_SALCOLOR( aColor[i].red >> 8, + aColor[i].green >> 8, + aColor[i].blue >> 8 ); + } + + delete [] aColor; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +static sal_uInt16 sal_Lookup( const std::vector<SalColor>& rPalette, + int r, int g, int b, + Pixel nUsed ) +{ + sal_uInt16 nPixel = 0; + int nBest = ColorDiff( rPalette[0], r, g, b ); + + for( sal_uInt16 i = 1; i < nUsed; i++ ) + { + int n = ColorDiff( rPalette[i], r, g, b ); + + if( n < nBest ) + { + if( !n ) + return i; + + nPixel = i; + nBest = n; + } + } + return nPixel; +} + +void SalColormap::GetLookupTable() +{ + m_aLookupTable = std::vector<sal_uInt16>(16*16*16); + + int i = 0; + for( int r = 0; r < 256; r += 17 ) + for( int g = 0; g < 256; g += 17 ) + for( int b = 0; b < 256; b += 17 ) + m_aLookupTable[i++] = sal_Lookup( m_aPalette, r, g, b, m_nUsed ); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +SalColor SalColormap::GetColor( Pixel nPixel ) const +{ + if( m_nBlackPixel == nPixel ) return SALCOLOR_BLACK; + if( m_nWhitePixel == nPixel ) return SALCOLOR_WHITE; + + if( m_aVisual.GetVisual() ) + { + if( m_aVisual.GetClass() == TrueColor ) + return m_aVisual.GetTCColor( nPixel ); + + if( m_aPalette.empty() + && m_hColormap +#ifdef PSEUDOCOLOR12 + && m_aVisual.GetDepth() <= 12 +#else + && m_aVisual.GetDepth() <= 8 +#endif + && m_aVisual.GetClass() == PseudoColor ) + ((SalColormap*)this)->GetPalette(); + } + + if( !m_aPalette.empty() && nPixel < m_nUsed ) + return m_aPalette[nPixel]; + + if( m_hColormap ) + { + DBG_ASSERT( 1, "SalColormap::GetColor() !hColormap_\n" ); + return nPixel; + } + + // DirectColor, StaticColor, StaticGray, GrayScale + XColor aColor; + + aColor.pixel = nPixel; + + XQueryColor( m_pDisplay->GetDisplay(), m_hColormap, &aColor ); + + return MAKE_SALCOLOR( aColor.red>>8, aColor.green>>8, aColor.blue>>8 ); +} + +inline sal_Bool SalColormap::GetXPixel( XColor &rColor, + int r, + int g, + int b ) const +{ + rColor.red = r * 257; + rColor.green = g * 257; + rColor.blue = b * 257; + return XAllocColor( GetXDisplay(), m_hColormap, &rColor ); +} + +sal_Bool SalColormap::GetXPixels( XColor &rColor, + int r, + int g, + int b ) const +{ + if( !GetXPixel( rColor, r, g, b ) ) + return sal_False; + if( rColor.pixel & 1 ) + return sal_True; + return GetXPixel( rColor, r^0xFF, g^0xFF, b^0xFF ); +} + +Pixel SalColormap::GetPixel( SalColor nSalColor ) const +{ + if( SALCOLOR_NONE == nSalColor ) return 0; + if( SALCOLOR_BLACK == nSalColor ) return m_nBlackPixel; + if( SALCOLOR_WHITE == nSalColor ) return m_nWhitePixel; + + if( m_aVisual.GetClass() == TrueColor ) + return m_aVisual.GetTCPixel( nSalColor ); + + if( m_aLookupTable.empty() ) + { + if( m_aPalette.empty() + && m_hColormap +#ifdef PSEUDOCOLOR12 + && m_aVisual.GetDepth() <= 12 +#else + && m_aVisual.GetDepth() <= 8 +#endif + && m_aVisual.GetClass() == PseudoColor ) // what else ??? + ((SalColormap*)this)->GetPalette(); + + if( !m_aPalette.empty() ) + for( Pixel i = 0; i < m_nUsed; i++ ) + if( m_aPalette[i] == nSalColor ) + return i; + + if( m_hColormap ) + { + // DirectColor, StaticColor, StaticGray, GrayScale (PseudoColor) + XColor aColor; + + if( GetXPixel( aColor, + SALCOLOR_RED ( nSalColor ), + SALCOLOR_GREEN( nSalColor ), + SALCOLOR_BLUE ( nSalColor ) ) ) + { + if( !m_aPalette.empty() && !m_aPalette[aColor.pixel] ) + { + const_cast<SalColormap*>(this)->m_aPalette[aColor.pixel] = nSalColor; + + if( !(aColor.pixel & 1) && !m_aPalette[aColor.pixel+1] ) + { + XColor aInversColor; + + SalColor nInversColor = nSalColor ^ 0xFFFFFF; + + GetXPixel( aInversColor, + SALCOLOR_RED ( nInversColor ), + SALCOLOR_GREEN( nInversColor ), + SALCOLOR_BLUE ( nInversColor ) ); + + if( !m_aPalette[aInversColor.pixel] ) + const_cast<SalColormap*>(this)->m_aPalette[aInversColor.pixel] = nInversColor; +#ifdef DBG_UTIL + else + fprintf( stderr, "SalColormap::GetPixel() 0x%06lx=%lu 0x%06lx=%lu\n", + static_cast< unsigned long >(nSalColor), aColor.pixel, + static_cast< unsigned long >(nInversColor), aInversColor.pixel); +#endif + } + } + + return aColor.pixel; + } + +#ifdef DBG_UTIL + fprintf( stderr, "SalColormap::GetPixel() !XAllocColor %lx\n", + static_cast< unsigned long >(nSalColor) ); +#endif + } + + if( m_aPalette.empty() ) + { +#ifdef DBG_UTIL + fprintf( stderr, "SalColormap::GetPixel() Palette empty %lx\n", + static_cast< unsigned long >(nSalColor)); +#endif + return nSalColor; + } + + ((SalColormap*)this)->GetLookupTable(); + } + + // Colormatching ueber Palette + sal_uInt16 r = SALCOLOR_RED ( nSalColor ); + sal_uInt16 g = SALCOLOR_GREEN( nSalColor ); + sal_uInt16 b = SALCOLOR_BLUE ( nSalColor ); + return m_aLookupTable[ (((r+8)/17) << 8) + + (((g+8)/17) << 4) + + ((b+8)/17) ]; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/app/salinst.cxx b/vcl/unx/generic/app/salinst.cxx new file mode 100644 index 000000000000..7233225f4d9b --- /dev/null +++ b/vcl/unx/generic/app/salinst.cxx @@ -0,0 +1,455 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#include "osl/module.hxx" +#include "tools/solarmutex.hxx" +#include "vos/mutex.hxx" + + +#include "unx/salunx.h" +#include "unx/saldata.hxx" +#include "unx/saldisp.hxx" +#include "unx/salinst.h" +#include "unx/salframe.h" +#include "unx/dtint.hxx" +#include "unx/salprn.h" +#include "unx/sm.hxx" + +#include "vcl/apptypes.hxx" +#include "vcl/helper.hxx" + +#include "salwtype.hxx" +#include <sal/macros.h> + +// ------------------------------------------------------------------------- +// +// SalYieldMutex +// +// ------------------------------------------------------------------------- + +SalYieldMutex::SalYieldMutex() +{ + mnCount = 0; + mnThreadId = 0; + ::tools::SolarMutex::SetSolarMutex( this ); +} + +void SalYieldMutex::acquire() +{ + SolarMutexObject::acquire(); + mnThreadId = osl::Thread::getCurrentIdentifier(); + mnCount++; +} + +void SalYieldMutex::release() +{ + if ( mnThreadId == osl::Thread::getCurrentIdentifier() ) + { + if ( mnCount == 1 ) + mnThreadId = 0; + mnCount--; + } + SolarMutexObject::release(); +} + +sal_Bool SalYieldMutex::tryToAcquire() +{ + if ( SolarMutexObject::tryToAcquire() ) + { + mnThreadId = osl::Thread::getCurrentIdentifier(); + mnCount++; + return True; + } + else + return False; +} + +//---------------------------------------------------------------------------- + +// -=-= SalInstance =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +// plugin factory function +extern "C" +{ + VCLPLUG_GEN_PUBLIC SalInstance* create_SalInstance() + { + /* #i92121# workaround deadlocks in the X11 implementation + */ + static const char* pNoXInitThreads = getenv( "SAL_NO_XINITTHREADS" ); + /* #i90094# + from now on we know that an X connection will be + established, so protect X against itself + */ + if( ! ( pNoXInitThreads && *pNoXInitThreads ) ) + XInitThreads(); + + X11SalInstance* pInstance = new X11SalInstance( new SalYieldMutex() ); + + // initialize SalData + X11SalData *pSalData = new X11SalData; + SetSalData( pSalData ); + pSalData->m_pInstance = pInstance; + pSalData->Init(); + + return pInstance; + } +} + +X11SalInstance::~X11SalInstance() +{ + // close session management + SessionManagerClient::close(); + + // dispose SalDisplay list from SalData + // would be done in a static destructor else which is + // a little late + + X11SalData *pSalData = GetX11SalData(); + pSalData->deInitNWF(); + delete pSalData; + SetSalData( NULL ); + + ::tools::SolarMutex::SetSolarMutex( 0 ); + delete mpSalYieldMutex; +} + + +// -------------------------------------------------------- +// AnyInput from sv/mow/source/app/svapp.cxx + +struct PredicateReturn +{ + sal_uInt16 nType; + sal_Bool bRet; +}; + +extern "C" { +Bool ImplPredicateEvent( Display *, XEvent *pEvent, char *pData ) +{ + PredicateReturn *pPre = (PredicateReturn *)pData; + + if ( pPre->bRet ) + return False; + + sal_uInt16 nType; + + switch( pEvent->type ) + { + case ButtonPress: + case ButtonRelease: + case MotionNotify: + case EnterNotify: + case LeaveNotify: + nType = INPUT_MOUSE; + break; + + case XLIB_KeyPress: + //case KeyRelease: + nType = INPUT_KEYBOARD; + break; + case Expose: + case GraphicsExpose: + case NoExpose: + nType = INPUT_PAINT; + break; + default: + nType = 0; + } + + if ( (nType & pPre->nType) || ( ! nType && (pPre->nType & INPUT_OTHER) ) ) + pPre->bRet = sal_True; + + return False; +} +} + +bool X11SalInstance::AnyInput(sal_uInt16 nType) +{ + X11SalData *pSalData = GetX11SalData(); + Display *pDisplay = pSalData->GetDisplay()->GetDisplay(); + sal_Bool bRet = sal_False; + + if( (nType & INPUT_TIMER) && + pSalData->GetDisplay()->GetXLib()->CheckTimeout( false ) ) + { + bRet = sal_True; + } + else if (XPending(pDisplay) ) + { + PredicateReturn aInput; + XEvent aEvent; + + aInput.bRet = sal_False; + aInput.nType = nType; + + XCheckIfEvent(pDisplay, &aEvent, ImplPredicateEvent, + (char *)&aInput ); + + bRet = aInput.bRet; + } + return bRet; +} + +osl::SolarMutex* X11SalInstance::GetYieldMutex() +{ + return mpSalYieldMutex; +} + +// ----------------------------------------------------------------------- + +sal_uLong X11SalInstance::ReleaseYieldMutex() +{ + SalYieldMutex* pYieldMutex = mpSalYieldMutex; + if ( pYieldMutex->GetThreadId() == + osl::Thread::getCurrentIdentifier() ) + { + sal_uLong nCount = pYieldMutex->GetAcquireCount(); + sal_uLong n = nCount; + while ( n ) + { + pYieldMutex->release(); + n--; + } + + return nCount; + } + else + return 0; +} + +// ----------------------------------------------------------------------- + +void X11SalInstance::AcquireYieldMutex( sal_uLong nCount ) +{ + SalYieldMutex* pYieldMutex = mpSalYieldMutex; + while ( nCount ) + { + pYieldMutex->acquire(); + nCount--; + } +} + +// ----------------------------------------------------------------------- + +bool X11SalInstance::CheckYieldMutex() +{ + bool bRet = true; + + SalYieldMutex* pYieldMutex = mpSalYieldMutex; + if ( pYieldMutex->GetThreadId() != osl::Thread::getCurrentIdentifier() ) + { + bRet = false; + } + + return bRet; +} + +// ----------------------------------------------------------------------- + +void X11SalInstance::Yield( bool bWait, bool bHandleAllCurrentEvents ) +{ GetX11SalData()->GetLib()->Yield( bWait, bHandleAllCurrentEvents ); } + +void* X11SalInstance::GetConnectionIdentifier( ConnectionIdentifierType& rReturnedType, int& rReturnedBytes ) +{ + static const char* pDisplay = getenv( "DISPLAY" ); + rReturnedType = AsciiCString; + rReturnedBytes = pDisplay ? strlen( pDisplay )+1 : 1; + return pDisplay ? (void*)pDisplay : (void*)""; +} + +SalFrame *X11SalInstance::CreateFrame( SalFrame *pParent, sal_uLong nSalFrameStyle ) +{ + SalFrame *pFrame = new X11SalFrame( pParent, nSalFrameStyle ); + + return pFrame; +} + +SalFrame* X11SalInstance::CreateChildFrame( SystemParentData* pParentData, sal_uLong nStyle ) +{ + SalFrame* pFrame = new X11SalFrame( NULL, nStyle, pParentData ); + + return pFrame; +} + +void X11SalInstance::DestroyFrame( SalFrame* pFrame ) +{ + delete pFrame; +} + +static void getServerDirectories( std::list< rtl::OString >& o_rFontPaths ) +{ +#ifdef LINUX + /* + * chkfontpath exists on some (RH derived) Linux distributions + */ + static const char* pCommands[] = { + "/usr/sbin/chkfontpath 2>/dev/null", "chkfontpath 2>/dev/null" + }; + ::std::list< ByteString > aLines; + + for( unsigned int i = 0; i < SAL_N_ELEMENTS(pCommands); i++ ) + { + FILE* pPipe = popen( pCommands[i], "r" ); + aLines.clear(); + if( pPipe ) + { + char line[1024]; + char* pSearch; + while( fgets( line, sizeof(line), pPipe ) ) + { + int nLen = strlen( line ); + if( line[nLen-1] == '\n' ) + line[nLen-1] = 0; + pSearch = strstr( line, ": " ); + if( pSearch ) + aLines.push_back( pSearch+2 ); + } + if( ! pclose( pPipe ) ) + break; + } + } + + for( ::std::list< ByteString >::iterator it = aLines.begin(); it != aLines.end(); ++it ) + { + if( ! access( it->GetBuffer(), F_OK ) ) + { + o_rFontPaths.push_back( *it ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "adding fs dir %s\n", it->GetBuffer() ); +#endif + } + } +#else + (void)o_rFontPaths; +#endif +} + + + +void X11SalInstance::FillFontPathList( std::list< rtl::OString >& o_rFontPaths ) +{ + Display *pDisplay = GetX11SalData()->GetDisplay()->GetDisplay(); + + DBG_ASSERT( pDisplay, "No Display !" ); + if( pDisplay ) + { + // get font paths to look for fonts + int nPaths = 0, i; + char** pPaths = XGetFontPath( pDisplay, &nPaths ); + + bool bServerDirs = false; + for( i = 0; i < nPaths; i++ ) + { + OString aPath( pPaths[i] ); + sal_Int32 nPos = 0; + if( ! bServerDirs + && ( nPos = aPath.indexOf( ':' ) ) > 0 + && ( !aPath.copy(nPos).equals( ":unscaled" ) ) ) + { + bServerDirs = true; + getServerDirectories( o_rFontPaths ); + } + else + { + psp::normPath( aPath ); + o_rFontPaths.push_back( aPath ); + } + } + + if( nPaths ) + XFreeFontPath( pPaths ); + } + + // insert some standard directories + o_rFontPaths.push_back( "/usr/openwin/lib/X11/fonts/TrueType" ); + o_rFontPaths.push_back( "/usr/openwin/lib/X11/fonts/Type1" ); + o_rFontPaths.push_back( "/usr/openwin/lib/X11/fonts/Type1/sun" ); + o_rFontPaths.push_back( "/usr/X11R6/lib/X11/fonts/truetype" ); + o_rFontPaths.push_back( "/usr/X11R6/lib/X11/fonts/Type1" ); + + #ifdef SOLARIS + /* cde specials, from /usr/dt/bin/Xsession: here are the good fonts, + the OWfontpath file may contain as well multiple lines as a comma + separated list of fonts in each line. to make it even more weird + environment variables are allowed as well */ + + const char* lang = getenv("LANG"); + if ( lang != NULL ) + { + String aOpenWinDir( String::CreateFromAscii( "/usr/openwin/lib/locale/" ) ); + aOpenWinDir.AppendAscii( lang ); + aOpenWinDir.AppendAscii( "/OWfontpath" ); + + SvFileStream aStream( aOpenWinDir, STREAM_READ ); + + // TODO: replace environment variables + while( aStream.IsOpen() && ! aStream.IsEof() ) + { + ByteString aLine; + aStream.ReadLine( aLine ); + // need an OString for normpath + OString aNLine( aLine ); + psp::normPath( aNLine ); + aLine = aNLine; + // try to avoid bad fonts in some cases + static bool bAvoid = (strncasecmp( lang, "ar", 2 ) == 0) || (strncasecmp( lang, "he", 2 ) == 0) || strncasecmp( lang, "iw", 2 ) == 0 || (strncasecmp( lang, "hi", 2 ) == 0); + if( bAvoid && aLine.Search( "iso_8859" ) != STRING_NOTFOUND ) + continue; + o_rFontPaths.push_back( aLine ); + } + } + #endif /* SOLARIS */ +} + +extern "C" { static void SAL_CALL thisModule() {} } + +void X11SalInstance::AddToRecentDocumentList(const rtl::OUString& rFileUrl, const rtl::OUString& rMimeType) +{ + const rtl::OUString SYM_ADD_TO_RECENTLY_USED_FILE_LIST(RTL_CONSTASCII_USTRINGPARAM("add_to_recently_used_file_list")); + const rtl::OUString LIB_RECENT_FILE(RTL_CONSTASCII_USTRINGPARAM("librecentfile.so")); + typedef void (*PFUNC_ADD_TO_RECENTLY_USED_LIST)(const rtl::OUString&, const rtl::OUString&); + + PFUNC_ADD_TO_RECENTLY_USED_LIST add_to_recently_used_file_list = 0; + + osl::Module module; + module.loadRelative( &thisModule, LIB_RECENT_FILE ); + if (module.is()) + add_to_recently_used_file_list = (PFUNC_ADD_TO_RECENTLY_USED_LIST)module.getFunctionSymbol(SYM_ADD_TO_RECENTLY_USED_FILE_LIST); + if (add_to_recently_used_file_list) + add_to_recently_used_file_list(rFileUrl, rMimeType); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/app/salsys.cxx b/vcl/unx/generic/app/salsys.cxx new file mode 100644 index 000000000000..c5f7174e3d4c --- /dev/null +++ b/vcl/unx/generic/app/salsys.cxx @@ -0,0 +1,229 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <unx/salunx.h> +#include <unx/dtint.hxx> +#include <unx/saldata.hxx> +#include <unx/salinst.h> +#include <unx/saldisp.hxx> +#include <unx/salsys.h> + +#include <vcl/msgbox.hxx> +#include <vcl/button.hxx> + +#include <svdata.hxx> + +#include <rtl/ustrbuf.hxx> +#include <osl/thread.h> + + +SalSystem* X11SalInstance::CreateSalSystem() +{ + return new X11SalSystem(); +} + +// ----------------------------------------------------------------------- + +X11SalSystem::~X11SalSystem() +{ +} + +// for the moment only handle xinerama case +unsigned int X11SalSystem::GetDisplayScreenCount() +{ + SalDisplay* pSalDisp = GetX11SalData()->GetDisplay(); + return pSalDisp->IsXinerama() ? pSalDisp->GetXineramaScreens().size() : pSalDisp->GetScreenCount(); +} + +bool X11SalSystem::IsMultiDisplay() +{ + SalDisplay* pSalDisp = GetX11SalData()->GetDisplay(); + unsigned int nScreenCount = pSalDisp->GetScreenCount(); + return pSalDisp->IsXinerama() ? false : (nScreenCount > 1); +} + +unsigned int X11SalSystem::GetDefaultDisplayNumber() +{ + SalDisplay* pSalDisp = GetX11SalData()->GetDisplay(); + return pSalDisp->IsXinerama() ? pSalDisp->GetDefaultMonitorNumber() : pSalDisp->GetDefaultScreenNumber(); +} + +Rectangle X11SalSystem::GetDisplayScreenPosSizePixel( unsigned int nScreen ) +{ + Rectangle aRet; + SalDisplay* pSalDisp = GetX11SalData()->GetDisplay(); + if( pSalDisp->IsXinerama() ) + { + const std::vector< Rectangle >& rScreens = pSalDisp->GetXineramaScreens(); + if( nScreen < rScreens.size() ) + aRet = rScreens[nScreen]; + } + else + { + const SalDisplay::ScreenData& rScreen = pSalDisp->getDataForScreen( nScreen ); + aRet = Rectangle( Point( 0, 0 ), rScreen.m_aSize ); + } + + return aRet; +} + +Rectangle X11SalSystem::GetDisplayWorkAreaPosSizePixel( unsigned int nScreen ) +{ + // FIXME: workareas + return GetDisplayScreenPosSizePixel( nScreen ); +} + +rtl::OUString X11SalSystem::GetScreenName( unsigned int nScreen ) +{ + rtl::OUString aScreenName; + SalDisplay* pSalDisp = GetX11SalData()->GetDisplay(); + if( pSalDisp->IsXinerama() ) + { + const std::vector< Rectangle >& rScreens = pSalDisp->GetXineramaScreens(); + if( nScreen >= rScreens.size() ) + nScreen = 0; + rtl::OUStringBuffer aBuf( 256 ); + aBuf.append( rtl::OStringToOUString( rtl::OString( DisplayString( pSalDisp->GetDisplay() ) ), osl_getThreadTextEncoding() ) ); + aBuf.appendAscii( " [" ); + aBuf.append( static_cast<sal_Int32>(nScreen) ); + aBuf.append( sal_Unicode(']') ); + aScreenName = aBuf.makeStringAndClear(); + } + else + { + if( nScreen >= static_cast<unsigned int>(pSalDisp->GetScreenCount()) ) + nScreen = 0; + rtl::OUStringBuffer aBuf( 256 ); + aBuf.append( rtl::OStringToOUString( rtl::OString( DisplayString( pSalDisp->GetDisplay() ) ), osl_getThreadTextEncoding() ) ); + // search backwards for ':' + int nPos = aBuf.getLength(); + if( nPos > 0 ) + nPos--; + while( nPos > 0 && aBuf.charAt( nPos ) != ':' ) + nPos--; + // search forward to '.' + while( nPos < aBuf.getLength() && aBuf.charAt( nPos ) != '.' ) + nPos++; + if( nPos < aBuf.getLength() ) + aBuf.setLength( nPos+1 ); + else + aBuf.append( sal_Unicode('.') ); + aBuf.append( static_cast<sal_Int32>(nScreen) ); + aScreenName = aBuf.makeStringAndClear(); + } + return aScreenName; +} + +int X11SalSystem::ShowNativeDialog( const String& rTitle, const String& rMessage, const std::list< String >& rButtons, int nDefButton ) +{ + int nRet = -1; + + ImplSVData* pSVData = ImplGetSVData(); + if( pSVData->mpIntroWindow ) + pSVData->mpIntroWindow->Hide(); + + WarningBox aWarn( NULL, WB_STDWORK, rMessage ); + aWarn.SetText( rTitle ); + aWarn.Clear(); + + sal_uInt16 nButton = 0; + for( std::list< String >::const_iterator it = rButtons.begin(); it != rButtons.end(); ++it ) + { + aWarn.AddButton( *it, nButton+1, nButton == (sal_uInt16)nDefButton ? BUTTONDIALOG_DEFBUTTON : 0 ); + nButton++; + } + aWarn.SetFocusButton( (sal_uInt16)nDefButton+1 ); + + nRet = ((int)aWarn.Execute()) - 1; + + // normalize behaviour, actually this should never happen + if( nRet < -1 || nRet >= int(rButtons.size()) ) + nRet = -1; + + return nRet; +} + +int X11SalSystem::ShowNativeMessageBox(const String& rTitle, const String& rMessage, int nButtonCombination, int nDefaultButton) +{ + int nDefButton = 0; + std::list< String > aButtons; + int nButtonIds[5], nBut = 0; + + if( nButtonCombination == SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_OK || + nButtonCombination == SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_OK_CANCEL ) + { + aButtons.push_back( Button::GetStandardText( BUTTON_OK ) ); + nButtonIds[nBut++] = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_OK; + } + if( nButtonCombination == SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_YES_NO_CANCEL || + nButtonCombination == SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_YES_NO ) + { + aButtons.push_back( Button::GetStandardText( BUTTON_YES ) ); + nButtonIds[nBut++] = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_YES; + aButtons.push_back( Button::GetStandardText( BUTTON_NO ) ); + nButtonIds[nBut++] = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_NO; + if( nDefaultButton == SALSYSTEM_SHOWNATIVEMSGBOX_BTN_NO ) + nDefButton = 1; + } + if( nButtonCombination == SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_OK_CANCEL || + nButtonCombination == SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_YES_NO_CANCEL || + nButtonCombination == SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_RETRY_CANCEL ) + { + if( nButtonCombination == SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_RETRY_CANCEL ) + { + aButtons.push_back( Button::GetStandardText( BUTTON_RETRY ) ); + nButtonIds[nBut++] = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_RETRY; + } + aButtons.push_back( Button::GetStandardText( BUTTON_CANCEL ) ); + nButtonIds[nBut++] = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_CANCEL; + if( nDefaultButton == SALSYSTEM_SHOWNATIVEMSGBOX_BTN_CANCEL ) + nDefButton = aButtons.size()-1; + } + if( nButtonCombination == SALSYSTEM_SHOWNATIVEMSGBOX_BTNCOMBI_ABORT_RETRY_IGNORE ) + { + aButtons.push_back( Button::GetStandardText( BUTTON_ABORT ) ); + nButtonIds[nBut++] = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_ABORT; + aButtons.push_back( Button::GetStandardText( BUTTON_RETRY ) ); + nButtonIds[nBut++] = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_RETRY; + aButtons.push_back( Button::GetStandardText( BUTTON_IGNORE ) ); + nButtonIds[nBut++] = SALSYSTEM_SHOWNATIVEMSGBOX_BTN_IGNORE; + switch( nDefaultButton ) + { + case SALSYSTEM_SHOWNATIVEMSGBOX_BTN_RETRY: nDefButton = 1;break; + case SALSYSTEM_SHOWNATIVEMSGBOX_BTN_IGNORE: nDefButton = 2;break; + } + } + int nResult = ShowNativeDialog( rTitle, rMessage, aButtons, nDefButton ); + + return nResult != -1 ? nButtonIds[ nResult ] : 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/app/saltimer.cxx b/vcl/unx/generic/app/saltimer.cxx new file mode 100644 index 000000000000..98d6ccde5943 --- /dev/null +++ b/vcl/unx/generic/app/saltimer.cxx @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <stdio.h> +#include <sys/time.h> +#include <sys/times.h> +#include <time.h> +#include <unistd.h> + +#include <unx/salunx.h> +#include <unx/saldata.hxx> +#include <unx/saldisp.hxx> +#include <unx/saltimer.h> +#include <unx/salinst.h> + +// -=-= SalData =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalData::Timeout() const +{ + ImplSVData* pSVData = ImplGetSVData(); + if( pSVData->mpSalTimer ) + pSVData->mpSalTimer->CallCallback(); +} + +// -=-= SalXLib =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void SalXLib::StopTimer() +{ + m_aTimeout.tv_sec = 0; + m_aTimeout.tv_usec = 0; + m_nTimeoutMS = 0; +} + +void SalXLib::StartTimer( sal_uLong nMS ) +{ + timeval Timeout (m_aTimeout); // previous timeout. + gettimeofday (&m_aTimeout, 0); + + m_nTimeoutMS = nMS; + m_aTimeout += m_nTimeoutMS; + + if ((Timeout > m_aTimeout) || (Timeout.tv_sec == 0)) + { + // Wakeup from previous timeout (or stopped timer). + Wakeup(); + } +} + +// -=-= SalTimer -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +SalTimer* X11SalInstance::CreateSalTimer() +{ + return new X11SalTimer(); +} + +X11SalTimer::~X11SalTimer() +{ +} + +void X11SalTimer::Stop() +{ + GetX11SalData()->GetLib()->StopTimer(); +} + +void X11SalTimer::Start( sal_uLong nMS ) +{ + GetX11SalData()->GetLib()->StartTimer( nMS ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/app/sm.cxx b/vcl/unx/generic/app/sm.cxx new file mode 100644 index 000000000000..ac2682df4510 --- /dev/null +++ b/vcl/unx/generic/app/sm.cxx @@ -0,0 +1,806 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include <string.h> +#include <unistd.h> +#include <sys/poll.h> +#include <fcntl.h> + +#include <stdio.h> + +#include <osl/process.h> +#include <osl/security.h> +#include <osl/conditn.h> + +#include <tools/prex.h> +#include <X11/Xatom.h> +#include <tools/postx.h> + +#include <unx/sm.hxx> +#include <unx/saldata.hxx> +#include <unx/saldisp.hxx> +#include <unx/salframe.h> +#include <unx/salinst.h> + +#include <vcl/svapp.hxx> +#include <vcl/window.hxx> + +#define USE_SM_EXTENSION + +#if OSL_DEBUG_LEVEL > 1 +#include <cstdarg> +static bool bFirstAssert = true; +#endif + +#if OSL_DEBUG_LEVEL > 1 +inline void SMprintf( const char* pFormat, ... ) +#else +inline void SMprintf( const char*, ... ) +#endif +{ +#if OSL_DEBUG_LEVEL > 1 + FILE* fp = fopen( "/tmp/sessionlog.txt", bFirstAssert ? "w" : "a" ); + if(!fp) return; + bFirstAssert = false; + std::va_list ap; + va_start( ap, pFormat ); + vfprintf( fp, pFormat, ap ); + fclose( fp ); + va_end( ap ); +#endif +}; + +static IceSalSession* pOneInstance = NULL; + +SalSession* X11SalInstance::CreateSalSession() +{ + if( ! pOneInstance ) + pOneInstance = new IceSalSession(); + SessionManagerClient::open(); + return pOneInstance; +} + +/* + * class IceSalSession + */ + +static X11SalFrame* pOldStyleSaveFrame = NULL; + +IceSalSession::IceSalSession() +{ +} + +IceSalSession::~IceSalSession() +{ + if( pOneInstance == this ) + pOneInstance = NULL; +} + +void IceSalSession::queryInteraction() +{ + if( ! SessionManagerClient::queryInteraction() ) + { + SalSessionInteractionEvent aEvent( false ); + CallCallback( &aEvent ); + } +} + +void IceSalSession::interactionDone() +{ + SessionManagerClient::interactionDone( false ); +} + +void IceSalSession::saveDone() +{ + SessionManagerClient::saveDone(); + if( pOldStyleSaveFrame ) + { + // note: does nothing if not running in generic plugin + X11SalFrame::SaveYourselfDone( pOldStyleSaveFrame ); + } +} + +bool IceSalSession::cancelShutdown() +{ + SessionManagerClient::interactionDone( true ); + return false; +} + +void IceSalSession::handleOldX11SaveYourself( SalFrame* pFrame ) +{ + // do this only once + if( ! pOldStyleSaveFrame ) + { + pOldStyleSaveFrame = static_cast<X11SalFrame*>(pFrame); + if( pOneInstance ) + { + SalSessionSaveRequestEvent aEvent( true, false ); + pOneInstance->CallCallback( &aEvent ); + } + } +} + +extern "C" void SAL_CALL ICEConnectionWorker( void* ); + +class ICEConnectionObserver +{ + friend void SAL_CALL ICEConnectionWorker(void*); + static sal_Bool bIsWatching; + static void ICEWatchProc( IceConn connection, IcePointer client_data, + Bool opening, IcePointer* watch_data ); + + static struct pollfd* pFilehandles; + static IceConn* pConnections; + static int nConnections; + static int nWakeupFiles[2]; + static oslMutex ICEMutex; + static oslThread ICEThread; +#ifdef USE_SM_EXTENSION + static IceIOErrorHandler origIOErrorHandler; + static IceErrorHandler origErrorHandler; +#endif +public: + + static void activate(); + static void deactivate(); + static void lock(); + static void unlock(); + static void wakeup(); +}; + + +SmcConn SessionManagerClient::aSmcConnection = NULL; +ByteString SessionManagerClient::aClientID; +sal_Bool ICEConnectionObserver::bIsWatching = sal_False; +struct pollfd* ICEConnectionObserver::pFilehandles = NULL; +IceConn* ICEConnectionObserver::pConnections = NULL; +int ICEConnectionObserver::nConnections = 0; +oslMutex ICEConnectionObserver::ICEMutex = NULL; +oslThread ICEConnectionObserver::ICEThread = NULL; +int ICEConnectionObserver::nWakeupFiles[2] = { 0, 0 }; + +#ifdef USE_SM_EXTENSION +IceIOErrorHandler ICEConnectionObserver::origIOErrorHandler = NULL; +IceErrorHandler ICEConnectionObserver::origErrorHandler = NULL; + +static void IgnoreIceErrors(IceConn, Bool, int, unsigned long, int, int, IcePointer) +{ +} + +static void IgnoreIceIOErrors(IceConn) +{ +} +#endif + +// HACK +bool SessionManagerClient::bDocSaveDone = false; + + +static SmProp* pSmProps = NULL; +static SmProp** ppSmProps = NULL; +static int nSmProps = 0; +static unsigned char *pSmRestartHint = NULL; + + +static void BuildSmPropertyList() +{ + if( ! pSmProps ) + { + rtl::OString aExec(rtl::OUStringToOString(SessionManagerClient::getExecName(), osl_getThreadTextEncoding())); + + nSmProps = 5; + pSmProps = new SmProp[ nSmProps ]; + + pSmProps[ 0 ].name = const_cast<char*>(SmCloneCommand); + pSmProps[ 0 ].type = const_cast<char*>(SmLISTofARRAY8); + pSmProps[ 0 ].num_vals = 1; + pSmProps[ 0 ].vals = new SmPropValue; + pSmProps[ 0 ].vals->length = aExec.getLength()+1; + pSmProps[ 0 ].vals->value = strdup( aExec.getStr() ); + + pSmProps[ 1 ].name = const_cast<char*>(SmProgram); + pSmProps[ 1 ].type = const_cast<char*>(SmARRAY8); + pSmProps[ 1 ].num_vals = 1; + pSmProps[ 1 ].vals = new SmPropValue; + pSmProps[ 1 ].vals->length = aExec.getLength()+1; + pSmProps[ 1 ].vals->value = strdup( aExec.getStr() ); + + pSmProps[ 2 ].name = const_cast<char*>(SmRestartCommand); + pSmProps[ 2 ].type = const_cast<char*>(SmLISTofARRAY8); + pSmProps[ 2 ].num_vals = 3; + pSmProps[ 2 ].vals = new SmPropValue[3]; + pSmProps[ 2 ].vals[0].length = aExec.getLength()+1; + pSmProps[ 2 ].vals[0].value = strdup( aExec.getStr() ); + ByteString aRestartOption( "-session=" ); + aRestartOption.Append( SessionManagerClient::getSessionID() ); + pSmProps[ 2 ].vals[1].length = aRestartOption.Len()+1; + pSmProps[ 2 ].vals[1].value = strdup( aRestartOption.GetBuffer() ); + ByteString aRestartOptionNoLogo( "-nologo" ); + pSmProps[ 2 ].vals[2].length = aRestartOptionNoLogo.Len()+1; + pSmProps[ 2 ].vals[2].value = strdup( aRestartOptionNoLogo.GetBuffer() ); + + rtl::OUString aUserName; + rtl::OString aUser; + oslSecurity aSec = osl_getCurrentSecurity(); + if( aSec ) + { + osl_getUserName( aSec, &aUserName.pData ); + aUser = rtl::OUStringToOString( aUserName, osl_getThreadTextEncoding() ); + osl_freeSecurityHandle( aSec ); + } + + pSmProps[ 3 ].name = const_cast<char*>(SmUserID); + pSmProps[ 3 ].type = const_cast<char*>(SmARRAY8); + pSmProps[ 3 ].num_vals = 1; + pSmProps[ 3 ].vals = new SmPropValue; + pSmProps[ 3 ].vals->value = strdup( aUser.getStr() ); + pSmProps[ 3 ].vals->length = strlen( (char *)pSmProps[ 3 ].vals->value )+1; + + pSmProps[ 4 ].name = const_cast<char*>(SmRestartStyleHint); + pSmProps[ 4 ].type = const_cast<char*>(SmCARD8); + pSmProps[ 4 ].num_vals = 1; + pSmProps[ 4 ].vals = new SmPropValue; + pSmProps[ 4 ].vals->value = malloc(1); + pSmRestartHint = (unsigned char *)pSmProps[ 4 ].vals->value; + *pSmRestartHint = SmRestartIfRunning; + pSmProps[ 4 ].vals->length = 1; + + ppSmProps = new SmProp*[ nSmProps ]; + for( int i = 0; i < nSmProps; i++ ) + ppSmProps[ i ] = &pSmProps[i]; + } +} + +bool SessionManagerClient::checkDocumentsSaved() +{ + return bDocSaveDone; +} + +IMPL_STATIC_LINK( SessionManagerClient, SaveYourselfHdl, void*, EMPTYARG ) +{ + SMprintf( "posting save documents event shutdown = %s\n", (pThis!=0) ? "true" : "false" ); + + static bool bFirstShutdown=true; + if (pThis != 0 && bFirstShutdown) //first shutdown request + { + bFirstShutdown = false; + /* + If we have no actual frames open, e.g. we launched a quickstarter, + and then shutdown all our frames leaving just a quickstarter running, + then we don't want to launch an empty toplevel frame on the next + start. (The job of scheduling the restart of the quick-starter is a + task of the quick-starter) + */ + *pSmRestartHint = SmRestartNever; + const std::list< SalFrame* >& rFrames = GetX11SalData()->GetDisplay()->getFrames(); + for( std::list< SalFrame* >::const_iterator it = rFrames.begin(); it != rFrames.end(); ++it ) + { + Window *pWindow = (*it)->GetWindow(); + if (pWindow && pWindow->IsVisible()) + { + *pSmRestartHint = SmRestartIfRunning; + break; + } + } + } + + if( pOneInstance ) + { + SalSessionSaveRequestEvent aEvent( pThis != 0, false ); + pOneInstance->CallCallback( &aEvent ); + } + else + saveDone(); + + return 0; +} + +IMPL_STATIC_LINK_NOINSTANCE( SessionManagerClient, InteractionHdl, void*, EMPTYARG ) +{ + SMprintf( "interaction link\n" ); + if( pOneInstance ) + { + SalSessionInteractionEvent aEvent( true ); + pOneInstance->CallCallback( &aEvent ); + } + + return 0; +} + +IMPL_STATIC_LINK_NOINSTANCE( SessionManagerClient, ShutDownCancelHdl, void*, EMPTYARG ) +{ + SMprintf( "shutdown cancel\n" ); + if( pOneInstance ) + { + SalSessionShutdownCancelEvent aEvent; + pOneInstance->CallCallback( &aEvent ); + } + + return 0; +} + +void SessionManagerClient::SaveYourselfProc( + SmcConn, + SmPointer, + int save_type, + Bool shutdown, + int interact_style, + Bool + ) +{ + SMprintf( "Session: save yourself, save_type = %s, shutdown = %s, interact_style = %s, fast = %s\n", + save_type == SmSaveLocal ? "SmcSaveLocal" : + ( save_type == SmSaveGlobal ? "SmcSaveGlobal" : + ( save_type == SmSaveBoth ? "SmcSaveBoth" : "<unknown>" ) ), + shutdown ? "true" : "false", + interact_style == SmInteractStyleNone ? "SmInteractStyleNone" : + ( interact_style == SmInteractStyleErrors ? "SmInteractStyleErrors" : + ( interact_style == SmInteractStyleAny ? "SmInteractStyleAny" : "<unknown>" ) ), + false ? "true" : "false" + ); + BuildSmPropertyList(); +#ifdef USE_SM_EXTENSION + bDocSaveDone = false; + /* #i49875# some session managers send a "die" message if the + * saveDone does not come early enough for their convenience + * this can occasionally happen on startup, especially the first + * startup. So shortcut the "not shutting down" case since the + * upper layers are currently not interested in that event anyway. + */ + if( ! shutdown ) + { + SessionManagerClient::saveDone(); + return; + } + sal_uIntPtr nStateVal = shutdown ? 0xffffffff : 0x0; + Application::PostUserEvent( STATIC_LINK( (void*)nStateVal, SessionManagerClient, SaveYourselfHdl ) ); + SMprintf( "waiting for save yourself event to be processed\n" ); +#endif +} + +IMPL_STATIC_LINK_NOINSTANCE( SessionManagerClient, ShutDownHdl, void*, EMPTYARG ) +{ + if( pOneInstance ) + { + SalSessionQuitEvent aEvent; + pOneInstance->CallCallback( &aEvent ); + } + + const std::list< SalFrame* >& rFrames = GetX11SalData()->GetDisplay()->getFrames(); + SMprintf( rFrames.begin() != rFrames.end() ? "shutdown on first frame\n" : "shutdown event but no frame\n" ); + if( rFrames.begin() != rFrames.end() ) + rFrames.front()->CallCallback( SALEVENT_SHUTDOWN, 0 ); + return 0; +} + +void SessionManagerClient::DieProc( + SmcConn connection, + SmPointer + ) +{ + SMprintf( "Session: die\n" ); + if( connection == aSmcConnection ) + { + Application::PostUserEvent( STATIC_LINK( NULL, SessionManagerClient, ShutDownHdl ) ); + SMprintf( "waiting for shutdown event to be processed\n" ); + } +} + +void SessionManagerClient::SaveCompleteProc( + SmcConn, + SmPointer + ) +{ + SMprintf( "Session: save complete\n" ); +} + +void SessionManagerClient::ShutdownCanceledProc( + SmcConn connection, + SmPointer ) +{ + SMprintf( "Session: shutdown canceled\n" ); + if( connection == aSmcConnection ) + Application::PostUserEvent( STATIC_LINK( NULL, SessionManagerClient, ShutDownCancelHdl ) ); +} + +void SessionManagerClient::InteractProc( + SmcConn connection, + SmPointer ) +{ + SMprintf( "Session: interaction request completed\n" ); + if( connection == aSmcConnection ) + Application::PostUserEvent( STATIC_LINK( NULL, SessionManagerClient, InteractionHdl ) ); +} + +void SessionManagerClient::saveDone() +{ + if( aSmcConnection ) + { + ICEConnectionObserver::lock(); + SmcSetProperties( aSmcConnection, nSmProps, ppSmProps ); + SmcSaveYourselfDone( aSmcConnection, True ); + SMprintf( "sent SaveYourselfDone SmRestartHint of %d\n", *pSmRestartHint ); + bDocSaveDone = true; + ICEConnectionObserver::unlock(); + } +} + + +void SessionManagerClient::open() +{ + static SmcCallbacks aCallbacks; + +#ifdef USE_SM_EXTENSION + // this is the way Xt does it, so we can too + if( ! aSmcConnection && getenv( "SESSION_MANAGER" ) ) + { + char aErrBuf[1024]; + ICEConnectionObserver::activate(); + ICEConnectionObserver::lock(); + + char* pClientID = NULL; + const ByteString& rPrevId( getPreviousSessionID() ); + + aCallbacks.save_yourself.callback = SaveYourselfProc; + aCallbacks.save_yourself.client_data = NULL; + aCallbacks.die.callback = DieProc; + aCallbacks.die.client_data = NULL; + aCallbacks.save_complete.callback = SaveCompleteProc; + aCallbacks.save_complete.client_data = NULL; + aCallbacks.shutdown_cancelled.callback = ShutdownCanceledProc; + aCallbacks.shutdown_cancelled.client_data = NULL; + aSmcConnection = SmcOpenConnection( NULL, + NULL, + SmProtoMajor, + SmProtoMinor, + SmcSaveYourselfProcMask | + SmcDieProcMask | + SmcSaveCompleteProcMask | + SmcShutdownCancelledProcMask , + &aCallbacks, + rPrevId.Len() ? const_cast<char*>(rPrevId.GetBuffer()) : NULL, + &pClientID, + sizeof( aErrBuf ), + aErrBuf ); + if( ! aSmcConnection ) + SMprintf( "SmcOpenConnection failed: %s\n", aErrBuf ); + else + SMprintf( "SmcOpenConnection succeeded, client ID is \"%s\"\n", pClientID ); + aClientID = ByteString( pClientID ); + free( pClientID ); + pClientID = NULL; + ICEConnectionObserver::unlock(); + + SalDisplay* pDisp = GetX11SalData()->GetDisplay(); + if( pDisp->GetDrawable( pDisp->GetDefaultScreenNumber() ) && aClientID.Len() ) + { + XChangeProperty( pDisp->GetDisplay(), + pDisp->GetDrawable( pDisp->GetDefaultScreenNumber() ), + XInternAtom( pDisp->GetDisplay(), "SM_CLIENT_ID", False ), + XA_STRING, + 8, + PropModeReplace, + (unsigned char*)aClientID.GetBuffer(), + aClientID.Len() + ); + } + } + else if( ! aSmcConnection ) + SMprintf( "no SESSION_MANAGER\n" ); +#endif +} + +const ByteString& SessionManagerClient::getSessionID() +{ + return aClientID; +} + +void SessionManagerClient::close() +{ + if( aSmcConnection ) + { +#ifdef USE_SM_EXTENSION + ICEConnectionObserver::lock(); + SMprintf( "attempting SmcCloseConnection\n" ); + SmcCloseConnection( aSmcConnection, 0, NULL ); + SMprintf( "SmcConnection closed\n" ); + ICEConnectionObserver::unlock(); + ICEConnectionObserver::deactivate(); +#endif + aSmcConnection = NULL; + } +} + +bool SessionManagerClient::queryInteraction() +{ + bool bRet = false; + if( aSmcConnection ) + { + ICEConnectionObserver::lock(); + if( SmcInteractRequest( aSmcConnection, SmDialogNormal, InteractProc, NULL ) ) + bRet = true; + ICEConnectionObserver::unlock(); + } + return bRet; +} + +void SessionManagerClient::interactionDone( bool bCancelShutdown ) +{ + if( aSmcConnection ) + { + ICEConnectionObserver::lock(); + SmcInteractDone( aSmcConnection, bCancelShutdown ? True : False ); + ICEConnectionObserver::unlock(); + } +} + + +rtl::OUString SessionManagerClient::getExecName() +{ + rtl::OUString aExec, aSysExec; + osl_getExecutableFile( &aExec.pData ); + osl_getSystemPathFromFileURL( aExec.pData, &aSysExec.pData ); + + int nPos = aSysExec.indexOf( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(".bin")) ); + if( nPos != -1 ) + aSysExec = aSysExec.copy( 0, nPos ); + return aSysExec; +} + + +const ByteString& SessionManagerClient::getPreviousSessionID() +{ + static ByteString aPrevId; + + int nCommands = osl_getCommandArgCount(); + for( int i = 0; i < nCommands; i++ ) + { + ::rtl::OUString aArg; + osl_getCommandArg( i, &aArg.pData ); + if( aArg.compareToAscii( "-session=", 9 ) == 0 ) + { + aPrevId = ByteString( ::rtl::OUStringToOString( aArg.copy( 9 ), osl_getThreadTextEncoding() ) ); + break; + } + } + SMprintf( "previous ID = \"%s\"\n", aPrevId.GetBuffer() ); + return aPrevId; +} + +void ICEConnectionObserver::lock() +{ + osl_acquireMutex( ICEMutex ); +} + +void ICEConnectionObserver::unlock() +{ + osl_releaseMutex( ICEMutex ); +} + +void ICEConnectionObserver::activate() +{ + if( ! bIsWatching ) + { + nWakeupFiles[0] = nWakeupFiles[1] = 0; + ICEMutex = osl_createMutex(); + bIsWatching = sal_True; +#ifdef USE_SM_EXTENSION + /* + * Default handlers call exit, we don't care that strongly if something + * happens to fail + */ + origIOErrorHandler = IceSetIOErrorHandler( IgnoreIceIOErrors ); + origErrorHandler = IceSetErrorHandler( IgnoreIceErrors ); + IceAddConnectionWatch( ICEWatchProc, NULL ); +#endif + } +} + +void ICEConnectionObserver::deactivate() +{ + if( bIsWatching ) + { + lock(); + bIsWatching = sal_False; +#ifdef USE_SM_EXTENSION + IceRemoveConnectionWatch( ICEWatchProc, NULL ); + IceSetErrorHandler( origErrorHandler ); + IceSetIOErrorHandler( origIOErrorHandler ); +#endif + nConnections = 0; + if( ICEThread ) + { + osl_terminateThread( ICEThread ); + wakeup(); + } + unlock(); + if( ICEThread ) + { + osl_joinWithThread( ICEThread ); + osl_destroyThread( ICEThread ); + close( nWakeupFiles[1] ); + close( nWakeupFiles[0] ); + ICEThread = NULL; + } + osl_destroyMutex( ICEMutex ); + ICEMutex = NULL; + } +} + +void ICEConnectionObserver::wakeup() +{ + char cChar = 'w'; + write( nWakeupFiles[1], &cChar, 1 ); +} + +void ICEConnectionWorker( void* ) +{ +#ifdef USE_SM_EXTENSION + while( osl_scheduleThread(ICEConnectionObserver::ICEThread) && ICEConnectionObserver::nConnections ) + { + ICEConnectionObserver::lock(); + int nConnectionsBefore = ICEConnectionObserver::nConnections; + int nBytes = sizeof( struct pollfd )*(nConnectionsBefore+1); + struct pollfd* pLocalFD = (struct pollfd*)rtl_allocateMemory( nBytes ); + rtl_copyMemory( pLocalFD, ICEConnectionObserver::pFilehandles, nBytes ); + ICEConnectionObserver::unlock(); + + int nRet = poll( pLocalFD,nConnectionsBefore+1,-1 ); + bool bWakeup = (pLocalFD[0].revents & POLLIN); + rtl_freeMemory( pLocalFD ); + + if( nRet < 1 ) + continue; + + // clear wakeup pipe + if( bWakeup ) + { + char buf[4]; + while( read( ICEConnectionObserver::nWakeupFiles[0], buf, sizeof( buf ) ) > 0 ) + ; + SMprintf( "file handles active in wakeup: %d\n", nRet ); + if( nRet == 1 ) + continue; + } + + // check fd's after we obtained the lock + ICEConnectionObserver::lock(); + if( ICEConnectionObserver::nConnections > 0 && ICEConnectionObserver::nConnections == nConnectionsBefore ) + { + nRet = poll( ICEConnectionObserver::pFilehandles+1, ICEConnectionObserver::nConnections, 0 ); + if( nRet > 0 ) + { + SMprintf( "IceProcessMessages\n" ); + Bool bReply; + for( int i = 0; i < ICEConnectionObserver::nConnections; i++ ) + if( ICEConnectionObserver::pFilehandles[i+1].revents & POLLIN ) + IceProcessMessages( ICEConnectionObserver::pConnections[i], NULL, &bReply ); + } + } + ICEConnectionObserver::unlock(); + } +#endif + SMprintf( "shutting donw ICE dispatch thread\n" ); +} + +void ICEConnectionObserver::ICEWatchProc( + IceConn connection, + IcePointer, + Bool opening, + IcePointer* + ) +{ + // note: this is a callback function for ICE + // this implicitly means that a call into ICE lib is calling this + // so the ICEMutex MUST already be locked by the caller + +#ifdef USE_SM_EXTENSION + if( opening ) + { + int fd = IceConnectionNumber( connection ); + nConnections++; + pConnections = (IceConn*)rtl_reallocateMemory( pConnections, sizeof( IceConn )*nConnections ); + pFilehandles = (struct pollfd*)rtl_reallocateMemory( pFilehandles, sizeof( struct pollfd )*(nConnections+1) ); + pConnections[ nConnections-1 ] = connection; + pFilehandles[ nConnections ].fd = fd; + pFilehandles[ nConnections ].events = POLLIN; + if( nConnections == 1 ) + { + if( ! pipe( nWakeupFiles ) ) + { + int flags; + pFilehandles[0].fd = nWakeupFiles[0]; + pFilehandles[0].events = POLLIN; + // set close-on-exec and nonblock descriptor flag. + if ((flags = fcntl (nWakeupFiles[0], F_GETFD)) != -1) + { + flags |= FD_CLOEXEC; + fcntl (nWakeupFiles[0], F_SETFD, flags); + } + if ((flags = fcntl (nWakeupFiles[0], F_GETFL)) != -1) + { + flags |= O_NONBLOCK; + fcntl (nWakeupFiles[0], F_SETFL, flags); + } + // set close-on-exec and nonblock descriptor flag. + if ((flags = fcntl (nWakeupFiles[1], F_GETFD)) != -1) + { + flags |= FD_CLOEXEC; + fcntl (nWakeupFiles[1], F_SETFD, flags); + } + if ((flags = fcntl (nWakeupFiles[1], F_GETFL)) != -1) + { + flags |= O_NONBLOCK; + fcntl (nWakeupFiles[1], F_SETFL, flags); + } + ICEThread = osl_createSuspendedThread( ICEConnectionWorker, NULL ); + osl_resumeThread( ICEThread ); + } + } + } + else + { + for( int i = 0; i < nConnections; i++ ) + { + if( pConnections[i] == connection ) + { + if( i < nConnections-1 ) + { + rtl_moveMemory( pConnections+i, pConnections+i+1, sizeof( IceConn )*(nConnections-i-1) ); + rtl_moveMemory( pFilehandles+i+1, pFilehandles+i+2, sizeof( struct pollfd )*(nConnections-i-1) ); + } + nConnections--; + pConnections = (IceConn*)rtl_reallocateMemory( pConnections, sizeof( IceConn )*nConnections ); + pFilehandles = (struct pollfd*)rtl_reallocateMemory( pFilehandles, sizeof( struct pollfd )*(nConnections+1) ); + break; + } + } + if( nConnections == 0 && ICEThread ) + { + SMprintf( "terminating ICEThread\n" ); + osl_terminateThread( ICEThread ); + wakeup(); + // must release the mutex here + osl_releaseMutex( ICEMutex ); + osl_joinWithThread( ICEThread ); + osl_destroyThread( ICEThread ); + close( nWakeupFiles[1] ); + close( nWakeupFiles[0] ); + ICEThread = NULL; + } + } + SMprintf( "ICE connection on %d %s\n", + IceConnectionNumber( connection ), + opening ? "inserted" : "removed" ); + SMprintf( "Display connection is %d\n", ConnectionNumber( GetX11SalData()->GetDisplay()->GetDisplay() ) ); +#endif +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/app/soicon.cxx b/vcl/unx/generic/app/soicon.cxx new file mode 100644 index 000000000000..1557b064977e --- /dev/null +++ b/vcl/unx/generic/app/soicon.cxx @@ -0,0 +1,118 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <unx/salunx.h> +#include <unx/saldisp.hxx> +#include <unx/salbmp.h> +#include <unx/soicon.hxx> + +#include <vcl/salbtype.hxx> +#include <vcl/bitmap.hxx> +#include <vcl/bitmapex.hxx> +#include <vcl/graph.hxx> + +#include <svdata.hxx> +#include <svids.hrc> +#include <salbmp.hxx> +#include <impbmp.hxx> + + +sal_Bool SelectAppIconPixmap( SalDisplay *pDisplay, int nScreen,sal_uInt16 nIcon, sal_uInt16 iconSize, + Pixmap& icon_pixmap, Pixmap& icon_mask) +{ + if( ! ImplGetResMgr() ) + return sal_False; + + sal_uInt16 nIconSizeOffset; + + if( iconSize >= 48 ) + nIconSizeOffset = SV_ICON_SIZE48_START; + else if( iconSize >= 32 ) + nIconSizeOffset = SV_ICON_SIZE32_START; + else if( iconSize >= 16 ) + nIconSizeOffset = SV_ICON_SIZE16_START; + else + return sal_False; + + BitmapEx aIcon( ResId(nIconSizeOffset + nIcon, *ImplGetResMgr())); + if( sal_True == aIcon.IsEmpty() ) + return sal_False; + + SalTwoRect aRect; + aRect.mnSrcX = 0; aRect.mnSrcY = 0; + aRect.mnSrcWidth = iconSize; aRect.mnSrcHeight = iconSize; + aRect.mnDestX = 0; aRect.mnDestY = 0; + aRect.mnDestWidth = iconSize; aRect.mnDestHeight = iconSize; + + X11SalBitmap *pBitmap = static_cast < X11SalBitmap * > + (aIcon.ImplGetBitmapImpBitmap()->ImplGetSalBitmap()); + + icon_pixmap = XCreatePixmap( pDisplay->GetDisplay(), + pDisplay->GetRootWindow( nScreen ), + iconSize, iconSize, + DefaultDepth( pDisplay->GetDisplay(), nScreen ) + ); + + pBitmap->ImplDraw( icon_pixmap, + nScreen, + DefaultDepth( pDisplay->GetDisplay(), nScreen ), + aRect, + DefaultGC(pDisplay->GetDisplay(), nScreen ) ); + + icon_mask = None; + + if( TRANSPARENT_BITMAP == aIcon.GetTransparentType() ) + { + icon_mask = XCreatePixmap( pDisplay->GetDisplay(), + pDisplay->GetRootWindow( pDisplay->GetDefaultScreenNumber() ), + iconSize, iconSize, 1); + + XGCValues aValues; + aValues.foreground = 0xffffffff; + aValues.background = 0; + aValues.function = GXcopy; + GC aMonoGC = XCreateGC( pDisplay->GetDisplay(), icon_mask, + GCFunction|GCForeground|GCBackground, &aValues ); + + Bitmap aMask = aIcon.GetMask(); + aMask.Invert(); + + X11SalBitmap *pMask = static_cast < X11SalBitmap * > + (aMask.ImplGetImpBitmap()->ImplGetSalBitmap()); + + pMask->ImplDraw(icon_mask, nScreen, 1, aRect, aMonoGC); + XFreeGC( pDisplay->GetDisplay(), aMonoGC ); + } + + return sal_True; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/app/wmadaptor.cxx b/vcl/unx/generic/app/wmadaptor.cxx new file mode 100644 index 000000000000..ad9b5ee22d6c --- /dev/null +++ b/vcl/unx/generic/app/wmadaptor.cxx @@ -0,0 +1,2551 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#include "sal/alloca.h" +#include "rtl/locale.h" + +#include "osl/thread.h" +#include "osl/process.h" +#include <sal/macros.h> +#include "vcl/configsettings.hxx" + +#include "unx/wmadaptor.hxx" +#include "unx/saldisp.hxx" +#include "unx/saldata.hxx" +#include "unx/salframe.h" + +#include "salgdi.hxx" + +#include "tools/prex.h" +#include <X11/X.h> +#include <X11/Xatom.h> +#include <X11/Xresource.h> +#include "tools/postx.h" + +#if OSL_DEBUG_LEVEL > 1 +#include <stdio.h> +#endif + +namespace vcl_sal { + +class NetWMAdaptor : public WMAdaptor +{ + void setNetWMState( X11SalFrame* pFrame ) const; + void initAtoms(); + virtual bool isValid() const; +public: + NetWMAdaptor( SalDisplay* ); + virtual ~NetWMAdaptor(); + + virtual void setWMName( X11SalFrame* pFrame, const String& rWMName ) const; + virtual void maximizeFrame( X11SalFrame* pFrame, bool bHorizontal = true, bool bVertical = true ) const; + virtual void shade( X11SalFrame* pFrame, bool bToShaded ) const; + virtual void setFrameTypeAndDecoration( X11SalFrame* pFrame, WMWindowType eType, int nDecorationFlags, X11SalFrame* pTransientFrame = NULL ) const; + virtual bool supportsICCCMPos() const; + virtual void enableAlwaysOnTop( X11SalFrame* pFrame, bool bEnable ) const; + virtual int handlePropertyNotify( X11SalFrame* pFrame, XPropertyEvent* pEvent ) const; + virtual void showFullScreen( X11SalFrame* pFrame, bool bFullScreen ) const; + virtual void frameIsMapping( X11SalFrame* pFrame ) const; + virtual void setFrameStruts( X11SalFrame* pFrame, + int left, int right, int top, int bottom, + int left_start_y, int left_end_y, + int right_start_y, int right_end_y, + int top_start_x, int top_end_x, + int bottom_start_x, int bottom_end_x ) const; + virtual void setUserTime( X11SalFrame* i_pFrame, long i_nUserTime ) const; +}; + +class GnomeWMAdaptor : public WMAdaptor +{ + bool m_bValid; + + void setGnomeWMState( X11SalFrame* pFrame ) const; + void initAtoms(); + virtual bool isValid() const; +public: + GnomeWMAdaptor( SalDisplay * ); + virtual ~GnomeWMAdaptor(); + + virtual void maximizeFrame( X11SalFrame* pFrame, bool bHorizontal = true, bool bVertical = true ) const; + virtual void shade( X11SalFrame* pFrame, bool bToShaded ) const; + virtual void enableAlwaysOnTop( X11SalFrame* pFrame, bool bEnable ) const; + virtual int handlePropertyNotify( X11SalFrame* pFrame, XPropertyEvent* pEvent ) const; +}; + +} + +using namespace vcl_sal; + +struct WMAdaptorProtocol +{ + const char* pProtocol; + int nProtocol; +}; + + +/* + * table must be sorted ascending in strings + * since it is use with bsearch + */ +static const WMAdaptorProtocol aProtocolTab[] = +{ + { "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE", WMAdaptor::KDE_NET_WM_WINDOW_TYPE_OVERRIDE }, + { "_NET_CURRENT_DESKTOP", WMAdaptor::NET_CURRENT_DESKTOP }, + { "_NET_NUMBER_OF_DESKTOPS", WMAdaptor::NET_NUMBER_OF_DESKTOPS }, + { "_NET_WM_DESKTOP", WMAdaptor::NET_WM_DESKTOP }, + { "_NET_WM_ICON_NAME", WMAdaptor::NET_WM_ICON_NAME }, + { "_NET_WM_PING", WMAdaptor::NET_WM_PING }, + { "_NET_WM_STATE", WMAdaptor::NET_WM_STATE }, + { "_NET_WM_STATE_ABOVE", WMAdaptor::NET_WM_STATE_STAYS_ON_TOP }, + { "_NET_WM_STATE_FULLSCREEN", WMAdaptor::NET_WM_STATE_FULLSCREEN }, + { "_NET_WM_STATE_MAXIMIZED_HORIZ", WMAdaptor::NET_WM_STATE_MAXIMIZED_HORZ }, // common bug in e.g. older kwin and sawfish implementations + { "_NET_WM_STATE_MAXIMIZED_HORZ", WMAdaptor::NET_WM_STATE_MAXIMIZED_HORZ }, + { "_NET_WM_STATE_MAXIMIZED_VERT", WMAdaptor::NET_WM_STATE_MAXIMIZED_VERT }, + { "_NET_WM_STATE_MODAL", WMAdaptor::NET_WM_STATE_MODAL }, + { "_NET_WM_STATE_SHADED", WMAdaptor::NET_WM_STATE_SHADED }, + { "_NET_WM_STATE_SKIP_PAGER", WMAdaptor::NET_WM_STATE_SKIP_PAGER }, + { "_NET_WM_STATE_SKIP_TASKBAR", WMAdaptor::NET_WM_STATE_SKIP_TASKBAR }, + { "_NET_WM_STATE_STAYS_ON_TOP", WMAdaptor::NET_WM_STATE_STAYS_ON_TOP }, + { "_NET_WM_STATE_STICKY", WMAdaptor::NET_WM_STATE_STICKY }, + { "_NET_WM_STRUT", WMAdaptor::NET_WM_STRUT }, + { "_NET_WM_STRUT_PARTIAL", WMAdaptor::NET_WM_STRUT_PARTIAL }, + { "_NET_WM_WINDOW_TYPE", WMAdaptor::NET_WM_WINDOW_TYPE }, + { "_NET_WM_WINDOW_TYPE_DESKTOP", WMAdaptor::NET_WM_WINDOW_TYPE_DESKTOP }, + { "_NET_WM_WINDOW_TYPE_DIALOG", WMAdaptor::NET_WM_WINDOW_TYPE_DIALOG }, + { "_NET_WM_WINDOW_TYPE_DOCK", WMAdaptor::NET_WM_WINDOW_TYPE_DOCK }, + { "_NET_WM_WINDOW_TYPE_MENU", WMAdaptor::NET_WM_WINDOW_TYPE_MENU }, + { "_NET_WM_WINDOW_TYPE_NORMAL", WMAdaptor::NET_WM_WINDOW_TYPE_NORMAL }, + { "_NET_WM_WINDOW_TYPE_SPLASH", WMAdaptor::NET_WM_WINDOW_TYPE_SPLASH }, + { "_NET_WM_WINDOW_TYPE_SPLASHSCREEN", WMAdaptor::NET_WM_WINDOW_TYPE_SPLASH }, // bug in Metacity 2.4.1 + { "_NET_WM_WINDOW_TYPE_TOOLBAR", WMAdaptor::NET_WM_WINDOW_TYPE_TOOLBAR }, + { "_NET_WM_WINDOW_TYPE_UTILITY", WMAdaptor::NET_WM_WINDOW_TYPE_UTILITY }, + { "_NET_WORKAREA", WMAdaptor::NET_WORKAREA }, + { "_WIN_APP_STATE", WMAdaptor::WIN_APP_STATE }, + { "_WIN_CLIENT_LIST", WMAdaptor::WIN_CLIENT_LIST }, + { "_WIN_EXPANDED_SIZE", WMAdaptor::WIN_EXPANDED_SIZE }, + { "_WIN_HINTS", WMAdaptor::WIN_HINTS }, + { "_WIN_ICONS", WMAdaptor::WIN_ICONS }, + { "_WIN_LAYER", WMAdaptor::WIN_LAYER }, + { "_WIN_STATE", WMAdaptor::WIN_STATE }, + { "_WIN_WORKSPACE", WMAdaptor::WIN_WORKSPACE }, + { "_WIN_WORKSPACE_COUNT", WMAdaptor::WIN_WORKSPACE_COUNT } +}; + +/* + * table containing atoms to get anyway + */ + +static const WMAdaptorProtocol aAtomTab[] = +{ + { "WM_STATE", WMAdaptor::WM_STATE }, + { "_MOTIF_WM_HINTS", WMAdaptor::MOTIF_WM_HINTS }, + { "WM_PROTOCOLS", WMAdaptor::WM_PROTOCOLS }, + { "WM_DELETE_WINDOW", WMAdaptor::WM_DELETE_WINDOW }, + { "WM_TAKE_FOCUS", WMAdaptor::WM_TAKE_FOCUS }, + { "WM_SAVE_YOURSELF", WMAdaptor::WM_SAVE_YOURSELF }, + { "WM_COMMAND", WMAdaptor::WM_COMMAND }, + { "WM_CLIENT_LEADER", WMAdaptor::WM_CLIENT_LEADER }, + { "WM_LOCALE_NAME", WMAdaptor::WM_LOCALE_NAME }, + { "WM_TRANSIENT_FOR", WMAdaptor::WM_TRANSIENT_FOR }, + { "SAL_QUITEVENT", WMAdaptor::SAL_QUITEVENT }, + { "SAL_USEREVENT", WMAdaptor::SAL_USEREVENT }, + { "SAL_EXTTEXTEVENT", WMAdaptor::SAL_EXTTEXTEVENT }, + { "SAL_GETTIMEEVENT", WMAdaptor::SAL_GETTIMEEVENT }, + { "VCL_SYSTEM_SETTINGS", WMAdaptor::VCL_SYSTEM_SETTINGS }, + { "DTWM_IS_RUNNING", WMAdaptor::DTWM_IS_RUNNING }, + { "_XSETTINGS_SETTINGS", WMAdaptor::XSETTINGS }, + { "_XEMBED", WMAdaptor::XEMBED }, + { "_XEMBED_INFO", WMAdaptor::XEMBED_INFO }, + { "_NET_WM_USER_TIME", WMAdaptor::NET_WM_USER_TIME }, + { "_NET_WM_PID", WMAdaptor::NET_WM_PID } +}; + +extern "C" { +static int compareProtocol( const void* pLeft, const void* pRight ) +{ + return strcmp( ((const WMAdaptorProtocol*)pLeft)->pProtocol, ((const WMAdaptorProtocol*)pRight)->pProtocol ); +} +} + +WMAdaptor* WMAdaptor::createWMAdaptor( SalDisplay* pSalDisplay ) +{ + WMAdaptor* pAdaptor = NULL; + + // try a NetWM + pAdaptor = new NetWMAdaptor( pSalDisplay ); + if( ! pAdaptor->isValid() ) + delete pAdaptor, pAdaptor = NULL; +#if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "WM supports extended WM hints\n" ); +#endif + + // try a GnomeWM + if( ! pAdaptor ) + { + pAdaptor = new GnomeWMAdaptor( pSalDisplay ); + if( ! pAdaptor->isValid() ) + delete pAdaptor, pAdaptor = NULL; +#if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "WM supports GNOME WM hints\n" ); +#endif + } + + if( ! pAdaptor ) + pAdaptor = new WMAdaptor( pSalDisplay ); + +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "Window Manager's name is \"%s\"\n", + ByteString( pAdaptor->getWindowManagerName(), RTL_TEXTENCODING_ISO_8859_1 ).GetBuffer() ); +#endif + return pAdaptor; +} + + +/* + * WMAdaptor constructor + */ + +WMAdaptor::WMAdaptor( SalDisplay* pDisplay ) : + m_pSalDisplay( pDisplay ), + m_bTransientBehaviour( true ), + m_bEnableAlwaysOnTopWorks( false ), + m_bLegacyPartialFullscreen( false ), + m_nWinGravity( StaticGravity ), + m_nInitWinGravity( StaticGravity ), + m_bWMshouldSwitchWorkspace( true ), + m_bWMshouldSwitchWorkspaceInit( false ) +{ + Atom aRealType = None; + int nFormat = 8; + unsigned long nItems = 0; + unsigned long nBytesLeft = 0; + unsigned char* pProperty = NULL; + + // default desktops + m_nDesktops = 1; + m_aWMWorkAreas = ::std::vector< Rectangle > + ( 1, Rectangle( Point(), m_pSalDisplay->GetScreenSize( m_pSalDisplay->GetDefaultScreenNumber() ) ) ); + m_bEqualWorkAreas = true; + + memset( m_aWMAtoms, 0, sizeof( m_aWMAtoms ) ); + m_pDisplay = m_pSalDisplay->GetDisplay(); + + initAtoms(); + getNetWmName(); // try to discover e.g. Sawfish + + // check for dtwm running + if( m_aWMAtoms[ DTWM_IS_RUNNING ] ) + { + if ( (XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + m_aWMAtoms[ DTWM_IS_RUNNING ], + 0, 1, + False, + XA_INTEGER, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty) == 0 + && nItems) + || (XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + m_aWMAtoms[ DTWM_IS_RUNNING ], + 0, 1, + False, + m_aWMAtoms[ DTWM_IS_RUNNING ], + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty) == 0 + && nItems)) + { + if (*pProperty) + { + m_aWMName = String(RTL_CONSTASCII_USTRINGPARAM("Dtwm")); + m_bTransientBehaviour = false; + m_nWinGravity = CenterGravity; + } + XFree (pProperty); + } + else if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } + } + if( m_aWMName.Len() == 0 ) + { + // check for window maker - needs different gravity + Atom aWMakerRunning = XInternAtom( m_pDisplay, "_WINDOWMAKER_WM_PROTOCOLS", True ); + if( aWMakerRunning != None && + XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + aWMakerRunning, + 0, 32, + False, + XA_ATOM, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 ) + { + if( aRealType == XA_ATOM ) + m_aWMName = String( RTL_CONSTASCII_USTRINGPARAM("Windowmaker" ) ); + XFree( pProperty ); + m_nInitWinGravity = NorthWestGravity; + } + else if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } + } + if( m_aWMName.Len() == 0 ) + { + if( XInternAtom( m_pDisplay, "_OL_WIN_ATTR", True ) ) + { + m_aWMName = String( RTL_CONSTASCII_USTRINGPARAM( "Olwm" ) ); + m_nInitWinGravity = NorthWestGravity; + } + } + if( m_aWMName.Len() == 0 ) + { + // check for ReflectionX wm (as it needs a workaround in Windows mode + Atom aRwmRunning = XInternAtom( m_pDisplay, "RWM_RUNNING", True ); + if( aRwmRunning != None && + XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + aRwmRunning, + 0, 32, + False, + aRwmRunning, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 ) + { + if( aRealType == aRwmRunning ) + m_aWMName = String( RTL_CONSTASCII_USTRINGPARAM("ReflectionX" ) ); + XFree( pProperty ); + } + else if( (aRwmRunning = XInternAtom( m_pDisplay, "_WRQ_WM_RUNNING", True )) != None && + XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + aRwmRunning, + 0, 32, + False, + XA_STRING, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 ) + { + if( aRealType == XA_STRING ) + m_aWMName = String( RTL_CONSTASCII_USTRINGPARAM( "ReflectionX Windows" ) ); + XFree( pProperty ); + } + } + if( m_aWMName.Len() == 0 ) + { + Atom aTTAPlatform = XInternAtom( m_pDisplay, "TTA_CLIENT_PLATFORM", True ); + if( aTTAPlatform != None && + XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + aTTAPlatform, + 0, 32, + False, + XA_STRING, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 ) + { + if( aRealType == XA_STRING ) + { + m_aWMName = String( RTL_CONSTASCII_USTRINGPARAM("Tarantella" ) ); + // #i62319# pretend that AlwaysOnTop works since + // the alwaysontop workaround in salframe.cxx results + // in a raise/lower loop on a Windows tarantella client + // FIXME: this property contains an identification string that + // in theory should be good enough to recognize running on a + // Windows client; however this string does not seem to be + // documented as well as the property itself. + m_bEnableAlwaysOnTopWorks = true; + } + XFree( pProperty ); + } + } +} + +/* + * WMAdaptor destructor + */ + +WMAdaptor::~WMAdaptor() +{ +} + +/* + * NetWMAdaptor constructor + */ + +NetWMAdaptor::NetWMAdaptor( SalDisplay* pSalDisplay ) : + WMAdaptor( pSalDisplay ) +{ + // currently all _NET WMs do transient like expected + m_bTransientBehaviour = true; + + Atom aRealType = None; + int nFormat = 8; + unsigned long nItems = 0; + unsigned long nBytesLeft = 0; + unsigned char* pProperty = NULL; + bool bNetWM = false; + + initAtoms(); + + // check for NetWM + bNetWM = getNetWmName(); + if( bNetWM + && XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + m_aWMAtoms[ NET_SUPPORTED ], + 0, 0, + False, + XA_ATOM, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 + && aRealType == XA_ATOM + && nFormat == 32 + ) + { + if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } + // collect supported protocols + if( XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + m_aWMAtoms[ NET_SUPPORTED ], + 0, nBytesLeft/4, + False, + XA_ATOM, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 + && nItems + ) + { + Atom* pAtoms = (Atom*)pProperty; + char** pAtomNames = (char**)alloca( sizeof(char*)*nItems ); + if( XGetAtomNames( m_pDisplay, pAtoms, nItems, pAtomNames ) ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "supported protocols:\n" ); +#endif + for( unsigned int i = 0; i < nItems; i++ ) + { + // #i80971# protect against invalid atoms + if( pAtomNames[i] == NULL ) + continue; + + int nProtocol = -1; + WMAdaptorProtocol aSearch; + aSearch.pProtocol = pAtomNames[i]; + WMAdaptorProtocol* pMatch = (WMAdaptorProtocol*) + bsearch( &aSearch, + aProtocolTab, + SAL_N_ELEMENTS( aProtocolTab ), + sizeof( struct WMAdaptorProtocol ), + compareProtocol ); + if( pMatch ) + { + nProtocol = pMatch->nProtocol; + m_aWMAtoms[ nProtocol ] = pAtoms[ i ]; + if( pMatch->nProtocol == NET_WM_STATE_STAYS_ON_TOP ) + m_bEnableAlwaysOnTopWorks = true; + } +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, " %s%s\n", pAtomNames[i], nProtocol != -1 ? "" : " (unsupported)" ); +#endif + + XFree( pAtomNames[i] ); + } + } + XFree( pProperty ); + pProperty = NULL; + } + else if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } + + // get number of desktops + if( m_aWMAtoms[ NET_NUMBER_OF_DESKTOPS ] + && XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + m_aWMAtoms[ NET_NUMBER_OF_DESKTOPS ], + 0, 1, + False, + XA_CARDINAL, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 + && pProperty + ) + { + m_nDesktops = *(long*)pProperty; + XFree( pProperty ); + pProperty = NULL; + // get work areas + if( m_aWMAtoms[ NET_WORKAREA ] + && XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + m_aWMAtoms[ NET_WORKAREA ], + 0, 4*m_nDesktops, + False, + XA_CARDINAL, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty + ) == 0 + && nItems == 4*(unsigned)m_nDesktops + ) + { + m_aWMWorkAreas = ::std::vector< Rectangle > ( m_nDesktops ); + long* pValues = (long*)pProperty; + for( int i = 0; i < m_nDesktops; i++ ) + { + Point aPoint( pValues[4*i], + pValues[4*i+1] ); + Size aSize( pValues[4*i+2], + pValues[4*i+3] ); + Rectangle aWorkArea( aPoint, aSize ); + m_aWMWorkAreas[i] = aWorkArea; + if( aWorkArea != m_aWMWorkAreas[0] ) + m_bEqualWorkAreas = false; +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "workarea %d: %ldx%ld+%ld+%ld\n", + i, + m_aWMWorkAreas[i].GetWidth(), + m_aWMWorkAreas[i].GetHeight(), + m_aWMWorkAreas[i].Left(), + m_aWMWorkAreas[i].Top() ); +#endif + } + XFree( pProperty ); + } + else + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "%ld workareas for %d desktops !\n", nItems/4, m_nDesktops ); +#endif + if( pProperty ) + { + XFree(pProperty); + pProperty = NULL; + } + } + } + else if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } + } + else if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } +} + +/* + * NetWMAdaptor destructor + */ +NetWMAdaptor::~NetWMAdaptor() +{ +} + +/* + * GnomeWMAdaptor constructor + */ + +GnomeWMAdaptor::GnomeWMAdaptor( SalDisplay* pSalDisplay ) : + WMAdaptor( pSalDisplay ), + m_bValid( false ) +{ + // currently all Gnome WMs do transient like expected + m_bTransientBehaviour = true; + + Atom aRealType = None; + int nFormat = 8; + unsigned long nItems = 0; + unsigned long nBytesLeft = 0; + unsigned char* pProperty = NULL; + + initAtoms(); + + // check for GnomeWM + if( m_aWMAtoms[ WIN_SUPPORTING_WM_CHECK ] && m_aWMAtoms[ WIN_PROTOCOLS ] ) + { + XLIB_Window aWMChild = None; + if( XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + m_aWMAtoms[ WIN_SUPPORTING_WM_CHECK ], + 0, 1, + False, + XA_CARDINAL, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 + && aRealType == XA_CARDINAL + && nFormat == 32 + && nItems != 0 + ) + { + aWMChild = *(XLIB_Window*)pProperty; + XFree( pProperty ); + pProperty = NULL; + XLIB_Window aCheckWindow = None; + m_pSalDisplay->GetXLib()->PushXErrorLevel( true ); + if( XGetWindowProperty( m_pDisplay, + aWMChild, + m_aWMAtoms[ WIN_SUPPORTING_WM_CHECK ], + 0, 1, + False, + XA_CARDINAL, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 + && aRealType == XA_CARDINAL + && nFormat == 32 + && nItems != 0 + && ! m_pSalDisplay->GetXLib()->HasXErrorOccurred() + ) + { + aCheckWindow = *(XLIB_Window*)pProperty; + XFree( pProperty ); + pProperty = NULL; + if( aCheckWindow == aWMChild ) + { + m_bValid = true; + /* + * get name of WM + * this is NOT part of the GNOME WM hints, but e.g. Sawfish + * already supports this part of the extended WM hints + */ + m_aWMAtoms[ UTF8_STRING ] = XInternAtom( m_pDisplay, "UTF8_STRING", False ); + getNetWmName(); + } + } + m_pSalDisplay->GetXLib()->PopXErrorLevel(); + } + else if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } + } + if( m_bValid + && XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + m_aWMAtoms[ WIN_PROTOCOLS ], + 0, 0, + False, + XA_ATOM, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 + && aRealType == XA_ATOM + && nFormat == 32 + ) + { + if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } + // collect supported protocols + if( XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + m_aWMAtoms[ WIN_PROTOCOLS ], + 0, nBytesLeft/4, + False, + XA_ATOM, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 + && pProperty + ) + { + Atom* pAtoms = (Atom*)pProperty; + char** pAtomNames = (char**)alloca( sizeof(char*)*nItems ); + if( XGetAtomNames( m_pDisplay, pAtoms, nItems, pAtomNames ) ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "supported protocols:\n" ); +#endif + for( unsigned int i = 0; i < nItems; i++ ) + { + // #i80971# protect against invalid atoms + if( pAtomNames[i] == NULL ) + continue; + + int nProtocol = -1; + WMAdaptorProtocol aSearch; + aSearch.pProtocol = pAtomNames[i]; + WMAdaptorProtocol* pMatch = (WMAdaptorProtocol*) + bsearch( &aSearch, + aProtocolTab, + SAL_N_ELEMENTS( aProtocolTab ), + sizeof( struct WMAdaptorProtocol ), + compareProtocol ); + if( pMatch ) + { + nProtocol = pMatch->nProtocol; + m_aWMAtoms[ nProtocol ] = pAtoms[ i ]; + if( pMatch->nProtocol == WIN_LAYER ) + m_bEnableAlwaysOnTopWorks = true; + } + if( strncmp( "_ICEWM_TRAY", pAtomNames[i], 11 ) == 0 ) + { + m_aWMName = String(RTL_CONSTASCII_USTRINGPARAM("IceWM" )); + m_nWinGravity = NorthWestGravity; + m_nInitWinGravity = NorthWestGravity; + } +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, " %s%s\n", pAtomNames[i], nProtocol != -1 ? "" : " (unsupported)" ); +#endif + + XFree( pAtomNames[i] ); + } + } + XFree( pProperty ); + pProperty = NULL; + } + else if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } + + // get number of desktops + if( m_aWMAtoms[ WIN_WORKSPACE_COUNT ] + && XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + m_aWMAtoms[ WIN_WORKSPACE_COUNT ], + 0, 1, + False, + XA_CARDINAL, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 + && pProperty + ) + { + m_nDesktops = *(long*)pProperty; + XFree( pProperty ); + pProperty = NULL; + } + else if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } + } + else if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } +} + +/* + * GnomeWMAdaptor destructor + */ +GnomeWMAdaptor::~GnomeWMAdaptor() +{ +} + +/* + * getNetWmName() + */ +bool WMAdaptor::getNetWmName() +{ + Atom aRealType = None; + int nFormat = 8; + unsigned long nItems = 0; + unsigned long nBytesLeft = 0; + unsigned char* pProperty = NULL; + bool bNetWM = false; + + if( m_aWMAtoms[ NET_SUPPORTING_WM_CHECK ] && m_aWMAtoms[ NET_WM_NAME ] ) + { + XLIB_Window aWMChild = None; + if( XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + m_aWMAtoms[ NET_SUPPORTING_WM_CHECK ], + 0, 1, + False, + XA_WINDOW, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 + && aRealType == XA_WINDOW + && nFormat == 32 + && nItems != 0 + ) + { + aWMChild = *(XLIB_Window*)pProperty; + XFree( pProperty ); + pProperty = NULL; + XLIB_Window aCheckWindow = None; + m_pSalDisplay->GetXLib()->PushXErrorLevel( true ); + if( XGetWindowProperty( m_pDisplay, + aWMChild, + m_aWMAtoms[ NET_SUPPORTING_WM_CHECK ], + 0, 1, + False, + XA_WINDOW, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 + && aRealType == XA_WINDOW + && nFormat == 32 + && nItems != 0 + && ! m_pSalDisplay->GetXLib()->HasXErrorOccurred() + ) + { + aCheckWindow = *(XLIB_Window*)pProperty; + XFree( pProperty ); + pProperty = NULL; + if( aCheckWindow == aWMChild ) + { + bNetWM = true; + // get name of WM + m_aWMAtoms[ UTF8_STRING ] = XInternAtom( m_pDisplay, "UTF8_STRING", False ); + if( XGetWindowProperty( m_pDisplay, + aWMChild, + m_aWMAtoms[ NET_WM_NAME ], + 0, 256, + False, + AnyPropertyType, /* m_aWMAtoms[ UTF8_STRING ],*/ + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 + && nItems != 0 + ) + { + if (aRealType == m_aWMAtoms[ UTF8_STRING ]) + { + m_aWMName = String( (sal_Char*)pProperty, nItems, RTL_TEXTENCODING_UTF8 ); + } + else + if (aRealType == XA_STRING) + { + m_aWMName = String( (sal_Char*)pProperty, nItems, RTL_TEXTENCODING_ISO_8859_1 ); + } + + XFree( pProperty ); + pProperty = NULL; + } + else if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } + // if this is metacity, check for version to enable a legacy workaround + if( m_aWMName.EqualsAscii( "Metacity" ) ) + { + int nVersionMajor = 0, nVersionMinor = 0; + Atom nVersionAtom = XInternAtom( m_pDisplay, "_METACITY_VERSION", True ); + if( nVersionAtom ) + { + if( XGetWindowProperty( m_pDisplay, + aWMChild, + nVersionAtom, + 0, 256, + False, + m_aWMAtoms[ UTF8_STRING ], + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 + && nItems != 0 + ) + { + String aMetaVersion( (sal_Char*)pProperty, nItems, RTL_TEXTENCODING_UTF8 ); + nVersionMajor = aMetaVersion.GetToken( 0, '.' ).ToInt32(); + nVersionMinor = aMetaVersion.GetToken( 1, '.' ).ToInt32(); + } + if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } + } + if( nVersionMajor < 2 || (nVersionMajor == 2 && nVersionMinor < 12) ) + m_bLegacyPartialFullscreen = true; + } + } + } + else if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } + m_pSalDisplay->GetXLib()->PopXErrorLevel(); + } + else if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } + } + return bNetWM; +} + +bool WMAdaptor::getWMshouldSwitchWorkspace() const +{ + if( ! m_bWMshouldSwitchWorkspaceInit ) + { + WMAdaptor * pWMA = const_cast<WMAdaptor*>(this); + + pWMA->m_bWMshouldSwitchWorkspace = true; + vcl::SettingsConfigItem* pItem = vcl::SettingsConfigItem::get(); + rtl::OUString aSetting( pItem->getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "WM" ) ), + rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ShouldSwitchWorkspace" ) ) ) ); + if( aSetting.getLength() == 0 ) + { + if( m_aWMName.EqualsAscii( "awesome" ) ) + { + pWMA->m_bWMshouldSwitchWorkspace = false; + } + } + else + pWMA->m_bWMshouldSwitchWorkspace = aSetting.toBoolean(); + pWMA->m_bWMshouldSwitchWorkspaceInit = true; + } + return m_bWMshouldSwitchWorkspace; +} + +/* + * WMAdaptor::isValid() + */ +bool WMAdaptor::isValid() const +{ + return true; +} + +/* + * NetWMAdaptor::isValid() + */ +bool NetWMAdaptor::isValid() const +{ + // some necessary sanity checks; there are WMs out there + // which implement some of the WM hints spec without + // real functionality + return + m_aWMAtoms[ NET_SUPPORTED ] + && m_aWMAtoms[ NET_SUPPORTING_WM_CHECK ] + && m_aWMAtoms[ NET_WM_NAME ] + && m_aWMAtoms[ NET_WM_WINDOW_TYPE_NORMAL ] + && m_aWMAtoms[ NET_WM_WINDOW_TYPE_DIALOG ] + ; +} + +/* + * GnomeWMAdaptor::isValid() + */ +bool GnomeWMAdaptor::isValid() const +{ + return m_bValid; +} + +/* + * WMAdaptor::initAtoms + */ + +void WMAdaptor::initAtoms() +{ + // get basic atoms + for( unsigned int i = 0; i < SAL_N_ELEMENTS( aAtomTab ); i++ ) + m_aWMAtoms[ aAtomTab[i].nProtocol ] = XInternAtom( m_pDisplay, aAtomTab[i].pProtocol, False ); + m_aWMAtoms[ NET_SUPPORTING_WM_CHECK ] = XInternAtom( m_pDisplay, "_NET_SUPPORTING_WM_CHECK", True ); + m_aWMAtoms[ NET_WM_NAME ] = XInternAtom( m_pDisplay, "_NET_WM_NAME", True ); +} + +/* + * NetWMAdaptor::initAtoms + */ + +void NetWMAdaptor::initAtoms() +{ + WMAdaptor::initAtoms(); + + m_aWMAtoms[ NET_SUPPORTED ] = XInternAtom( m_pDisplay, "_NET_SUPPORTED", True ); +} + +/* + * GnomeWMAdaptor::initAtoms + */ + +void GnomeWMAdaptor::initAtoms() +{ + WMAdaptor::initAtoms(); + + m_aWMAtoms[ WIN_PROTOCOLS ] = XInternAtom( m_pDisplay, "_WIN_PROTOCOLS", True ); + m_aWMAtoms[ WIN_SUPPORTING_WM_CHECK ] = XInternAtom( m_pDisplay, "_WIN_SUPPORTING_WM_CHECK", True ); +} + +/* + * WMAdaptor::setWMName + * sets WM_NAME + * WM_ICON_NAME + */ + +void WMAdaptor::setWMName( X11SalFrame* pFrame, const String& rWMName ) const +{ + ByteString aTitle( rWMName, osl_getThreadTextEncoding() ); + + if( ! rWMName.Len() && m_aWMName.EqualsAscii( "Dtwm" ) ) + aTitle = " "; + + ::rtl::OString aWMLocale; + rtl_Locale* pLocale = NULL; + osl_getProcessLocale( &pLocale ); + if( pLocale ) + { + ::rtl::OUString aLocaleString( pLocale->Language ); + ::rtl::OUString aCountry( pLocale->Country ); + ::rtl::OUString aVariant( pLocale->Variant ); + + if( aCountry.getLength() ) + { + aLocaleString += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("_")); + aLocaleString += aCountry; + } + if( aVariant.getLength() ) + aLocaleString += aVariant; + aWMLocale = ::rtl::OUStringToOString( aLocaleString, RTL_TEXTENCODING_ISO_8859_1 ); + } + else + { + static const char* pLang = getenv( "LANG" ); + aWMLocale = pLang ? pLang : "C"; + } + + static bool bTrustXmb = true; + #ifdef SOLARIS + /* #i64273# there are some weird cases when using IIIMP on Solaris + * where for unknown reasons XmbTextListToTextProperty results in + * garbage. Test one string once to ensure safety. + * + * FIXME: This must be a bug in xiiimp.so.2 somewhere. However + * it was not possible to recreate this in a small sample program. + * This reeks of memory corruption somehow. + */ + static bool bOnce = true; + if( bOnce ) + { + bOnce = false; + XTextProperty aTestProp = { NULL, None, 0, 0 }; + const char *pText = "trustme"; + XmbTextListToTextProperty( m_pDisplay, + &const_cast<char*>(pText), + 1, + XStdICCTextStyle, + &aTestProp ); + bTrustXmb = (aTestProp.nitems == 7) && + (aTestProp.value != NULL ) && + (strncmp( (char*)aTestProp.value, pText, 7 ) == 0) && + (aTestProp.encoding == XA_STRING); + if( aTestProp.value ) + XFree( aTestProp.value ); + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "%s\n", + bTrustXmb ? + "XmbTextListToTextProperty seems to work" : + "XmbTextListToTextProperty does not seem to work" ); + #endif + } + #endif + + char* pT = const_cast<char*>(aTitle.GetBuffer()); + XTextProperty aProp = { NULL, None, 0, 0 }; + if( bTrustXmb ) + { + XmbTextListToTextProperty( m_pDisplay, + &pT, + 1, + XStdICCTextStyle, + &aProp ); + } + + unsigned char* pData = aProp.nitems ? aProp.value : (unsigned char*)aTitle.GetBuffer(); + Atom nType = aProp.nitems ? aProp.encoding : XA_STRING; + int nFormat = aProp.nitems ? aProp.format : 8; + int nBytes = aProp.nitems ? aProp.nitems : aTitle.Len(); + const SystemEnvData* pEnv = pFrame->GetSystemData(); + XChangeProperty( m_pDisplay, + (XLIB_Window)pEnv->aShellWindow, + XA_WM_NAME, + nType, + nFormat, + PropModeReplace, + pData, + nBytes ); + XChangeProperty( m_pDisplay, + (XLIB_Window)pEnv->aShellWindow, + XA_WM_ICON_NAME, + nType, + nFormat, + PropModeReplace, + pData, + nBytes ); + XChangeProperty( m_pDisplay, + (XLIB_Window)pEnv->aShellWindow, + m_aWMAtoms[ WM_LOCALE_NAME ], + XA_STRING, + 8, + PropModeReplace, + (unsigned char*)aWMLocale.getStr(), + aWMLocale.getLength() ); + if (aProp.value != NULL) + XFree( aProp.value ); +} + +/* + * NetWMAdaptor::setWMName + * sets WM_NAME + * _NET_WM_NAME + * WM_ICON_NAME + * _NET_WM_ICON_NAME + */ +void NetWMAdaptor::setWMName( X11SalFrame* pFrame, const String& rWMName ) const +{ + WMAdaptor::setWMName( pFrame, rWMName ); + + ByteString aTitle( rWMName, RTL_TEXTENCODING_UTF8 ); + const SystemEnvData* pEnv = pFrame->GetSystemData(); + if( m_aWMAtoms[ NET_WM_NAME ] ) + XChangeProperty( m_pDisplay, + (XLIB_Window)pEnv->aShellWindow, + m_aWMAtoms[ NET_WM_NAME ], + m_aWMAtoms[ UTF8_STRING ], + 8, + PropModeReplace, + (unsigned char*)aTitle.GetBuffer(), + aTitle.Len()+1 ); + if( m_aWMAtoms[ NET_WM_ICON_NAME ] ) + XChangeProperty( m_pDisplay, + (XLIB_Window)pEnv->aShellWindow, + m_aWMAtoms[ NET_WM_ICON_NAME ], + m_aWMAtoms[ UTF8_STRING ], + 8, + PropModeReplace, + (unsigned char*)aTitle.GetBuffer(), + aTitle.Len()+1 ); + // The +1 copies the terminating null byte. Although + // the spec says, this should not be necessary + // at least the kwin implementation seems to depend + // on the null byte +} + +/* + * NetWMAdaptor::setNetWMState + * sets _NET_WM_STATE + */ +void NetWMAdaptor::setNetWMState( X11SalFrame* pFrame ) const +{ + if( m_aWMAtoms[ NET_WM_STATE ] ) + { + Atom aStateAtoms[ 10 ]; + int nStateAtoms = 0; + + // set NET_WM_STATE_MODAL + if( m_aWMAtoms[ NET_WM_STATE_MODAL ] + && pFrame->meWindowType == windowType_ModalDialogue ) + { + aStateAtoms[ nStateAtoms++ ] = m_aWMAtoms[ NET_WM_STATE_MODAL ]; + /* + * #90998# NET_WM_STATE_SKIP_TASKBAR set on a frame will + * cause kwin not to give it the focus on map request + * this seems to be a bug in kwin + * aStateAtoms[ nStateAtoms++ ] = m_aWMAtoms[ NET_WM_STATE_SKIP_TASKBAR ]; + */ + } + if( pFrame->mbMaximizedVert + && m_aWMAtoms[ NET_WM_STATE_MAXIMIZED_VERT ] ) + aStateAtoms[ nStateAtoms++ ] = m_aWMAtoms[ NET_WM_STATE_MAXIMIZED_VERT ]; + if( pFrame->mbMaximizedHorz + && m_aWMAtoms[ NET_WM_STATE_MAXIMIZED_HORZ ] ) + aStateAtoms[ nStateAtoms++ ] = m_aWMAtoms[ NET_WM_STATE_MAXIMIZED_HORZ ]; + if( pFrame->bAlwaysOnTop_ && m_aWMAtoms[ NET_WM_STATE_STAYS_ON_TOP ] ) + aStateAtoms[ nStateAtoms++ ] = m_aWMAtoms[ NET_WM_STATE_STAYS_ON_TOP ]; + if( pFrame->mbShaded && m_aWMAtoms[ NET_WM_STATE_SHADED ] ) + aStateAtoms[ nStateAtoms++ ] = m_aWMAtoms[ NET_WM_STATE_SHADED ]; + if( pFrame->mbFullScreen && m_aWMAtoms[ NET_WM_STATE_FULLSCREEN ] ) + aStateAtoms[ nStateAtoms++ ] = m_aWMAtoms[ NET_WM_STATE_FULLSCREEN ]; + if( pFrame->meWindowType == windowType_Utility && m_aWMAtoms[ NET_WM_STATE_SKIP_TASKBAR ] ) + aStateAtoms[ nStateAtoms++ ] = m_aWMAtoms[ NET_WM_STATE_SKIP_TASKBAR ]; + + if( nStateAtoms ) + { + XChangeProperty( m_pDisplay, + pFrame->GetShellWindow(), + m_aWMAtoms[ NET_WM_STATE ], + XA_ATOM, + 32, + PropModeReplace, + (unsigned char*)aStateAtoms, + nStateAtoms + ); + } + else + XDeleteProperty( m_pDisplay, + pFrame->GetShellWindow(), + m_aWMAtoms[ NET_WM_STATE ] ); + if( pFrame->mbMaximizedHorz + && pFrame->mbMaximizedVert + && ! ( pFrame->nStyle_ & SAL_FRAME_STYLE_SIZEABLE ) ) + { + /* + * for maximizing use NorthWestGravity (including decoration) + */ + XSizeHints hints; + long supplied; + bool bHint = false; + if( XGetWMNormalHints( m_pDisplay, + pFrame->GetShellWindow(), + &hints, + &supplied ) ) + { + bHint = true; + hints.flags |= PWinGravity; + hints.win_gravity = NorthWestGravity; + XSetWMNormalHints( m_pDisplay, + pFrame->GetShellWindow(), + &hints ); + XSync( m_pDisplay, False ); + } + + // SetPosSize necessary to set width/height, min/max w/h + sal_Int32 nCurrent = 0; + /* + * get current desktop here if work areas have different size + * (does this happen on any platform ?) + */ + if( ! m_bEqualWorkAreas ) + { + nCurrent = getCurrentWorkArea(); + if( nCurrent < 0 ) + nCurrent = 0; + } + Rectangle aPosSize = m_aWMWorkAreas[nCurrent]; + const SalFrameGeometry& rGeom( pFrame->GetUnmirroredGeometry() ); + aPosSize = Rectangle( Point( aPosSize.Left() + rGeom.nLeftDecoration, + aPosSize.Top() + rGeom.nTopDecoration ), + Size( aPosSize.GetWidth() + - rGeom.nLeftDecoration + - rGeom.nRightDecoration, + aPosSize.GetHeight() + - rGeom.nTopDecoration + - rGeom.nBottomDecoration ) + ); + pFrame->SetPosSize( aPosSize ); + + /* + * reset gravity hint to static gravity + * (this should not move window according to ICCCM) + */ + if( bHint && pFrame->nShowState_ != SHOWSTATE_UNKNOWN ) + { + hints.win_gravity = StaticGravity; + XSetWMNormalHints( m_pDisplay, + pFrame->GetShellWindow(), + &hints ); + } + } + } +} + +/* + * GnomeWMAdaptor::setNetWMState + * sets _WIN_STATE + */ +void GnomeWMAdaptor::setGnomeWMState( X11SalFrame* pFrame ) const +{ + if( m_aWMAtoms[ WIN_STATE ] ) + { + sal_uInt32 nWinWMState = 0; + + if( pFrame->mbMaximizedVert ) + nWinWMState |= 1 << 2; + if( pFrame->mbMaximizedHorz ) + nWinWMState |= 1 << 3; + if( pFrame->mbShaded ) + nWinWMState |= 1 << 5; + + XChangeProperty( m_pDisplay, + pFrame->GetShellWindow(), + m_aWMAtoms[ WIN_STATE ], + XA_CARDINAL, + 32, + PropModeReplace, + (unsigned char*)&nWinWMState, + 1 + ); + if( pFrame->mbMaximizedHorz + && pFrame->mbMaximizedVert + && ! ( pFrame->nStyle_ & SAL_FRAME_STYLE_SIZEABLE ) ) + { + /* + * for maximizing use NorthWestGravity (including decoration) + */ + XSizeHints hints; + long supplied; + bool bHint = false; + if( XGetWMNormalHints( m_pDisplay, + pFrame->GetShellWindow(), + &hints, + &supplied ) ) + { + bHint = true; + hints.flags |= PWinGravity; + hints.win_gravity = NorthWestGravity; + XSetWMNormalHints( m_pDisplay, + pFrame->GetShellWindow(), + &hints ); + XSync( m_pDisplay, False ); + } + + // SetPosSize necessary to set width/height, min/max w/h + sal_Int32 nCurrent = 0; + /* + * get current desktop here if work areas have different size + * (does this happen on any platform ?) + */ + if( ! m_bEqualWorkAreas ) + { + nCurrent = getCurrentWorkArea(); + if( nCurrent < 0 ) + nCurrent = 0; + } + Rectangle aPosSize = m_aWMWorkAreas[nCurrent]; + const SalFrameGeometry& rGeom( pFrame->GetUnmirroredGeometry() ); + aPosSize = Rectangle( Point( aPosSize.Left() + rGeom.nLeftDecoration, + aPosSize.Top() + rGeom.nTopDecoration ), + Size( aPosSize.GetWidth() + - rGeom.nLeftDecoration + - rGeom.nRightDecoration, + aPosSize.GetHeight() + - rGeom.nTopDecoration + - rGeom.nBottomDecoration ) + ); + pFrame->SetPosSize( aPosSize ); + + /* + * reset gravity hint to static gravity + * (this should not move window according to ICCCM) + */ + if( bHint && pFrame->nShowState_ != SHOWSTATE_UNKNOWN ) + { + hints.win_gravity = StaticGravity; + XSetWMNormalHints( m_pDisplay, + pFrame->GetShellWindow(), + &hints ); + } + } + } +} + +/* + * WMAdaptor::setFrameDecoration + * sets _MOTIF_WM_HINTS + * WM_TRANSIENT_FOR + */ + +void WMAdaptor::setFrameTypeAndDecoration( X11SalFrame* pFrame, WMWindowType eType, int nDecorationFlags, X11SalFrame* pReferenceFrame ) const +{ + pFrame->meWindowType = eType; + pFrame->mnDecorationFlags = nDecorationFlags; + + if( ! pFrame->mbFullScreen ) + { + // set mwm hints + struct _mwmhints { + unsigned long flags, func, deco; + long input_mode; + unsigned long status; + } aHint; + + aHint.flags = 15; /* flags for functions, decoration, input mode and status */ + aHint.deco = 0; + aHint.func = 1L << 2; + aHint.status = 0; + aHint.input_mode = 0; + + // evaluate decoration flags + if( nDecorationFlags & decoration_All ) + aHint.deco = 1, aHint.func = 1; + else + { + if( nDecorationFlags & decoration_Title ) + aHint.deco |= 1L << 3; + if( nDecorationFlags & decoration_Border ) + aHint.deco |= 1L << 1; + if( nDecorationFlags & decoration_Resize ) + aHint.deco |= 1L << 2, aHint.func |= 1L << 1; + if( nDecorationFlags & decoration_MinimizeBtn ) + aHint.deco |= 1L << 5, aHint.func |= 1L << 3; + if( nDecorationFlags & decoration_MaximizeBtn ) + aHint.deco |= 1L << 6, aHint.func |= 1L << 4; + if( nDecorationFlags & decoration_CloseBtn ) + aHint.deco |= 1L << 4, aHint.func |= 1L << 5; + } + // evaluate window type + switch( eType ) + { + case windowType_ModalDialogue: + aHint.input_mode = 1; + break; + default: + break; + } + + // set the hint + XChangeProperty( m_pDisplay, + pFrame->GetShellWindow(), + m_aWMAtoms[ MOTIF_WM_HINTS ], + m_aWMAtoms[ MOTIF_WM_HINTS ], + 32, + PropModeReplace, + (unsigned char*)&aHint, + 5 ); + } + + // set transientFor hint + /* #91030# dtwm will not map a dialogue if the transient + * window is iconified. This is deemed undesireable because + * message boxes do not get mapped, so use the root as transient + * instead. + */ + if( pReferenceFrame ) + { + XSetTransientForHint( m_pDisplay, + pFrame->GetShellWindow(), + pReferenceFrame->bMapped_ ? + pReferenceFrame->GetShellWindow() : + m_pSalDisplay->GetRootWindow( pFrame->GetScreenNumber() ) + ); + if( ! pReferenceFrame->bMapped_ ) + pFrame->mbTransientForRoot = true; + } + // #110333# in case no one ever sets a title prevent + // the Dtwm taking the class instead + if( m_aWMName.EqualsAscii( "Dtwm" ) ) + setWMName( pFrame, String() ); +} + +/* + * NetWMAdaptor::setFrameDecoration + * sets _MOTIF_WM_HINTS + * _NET_WM_WINDOW_TYPE + * _NET_WM_STATE + * WM_TRANSIENT_FOR + */ + +void NetWMAdaptor::setFrameTypeAndDecoration( X11SalFrame* pFrame, WMWindowType eType, int nDecorationFlags, X11SalFrame* pReferenceFrame ) const +{ + WMAdaptor::setFrameTypeAndDecoration( pFrame, eType, nDecorationFlags, pReferenceFrame ); + + setNetWMState( pFrame ); + + // set NET_WM_WINDOW_TYPE + if( m_aWMAtoms[ NET_WM_WINDOW_TYPE ] ) + { + Atom aWindowTypes[4]; + int nWindowTypes = 0; + switch( eType ) + { + case windowType_Utility: + aWindowTypes[nWindowTypes++] = + m_aWMAtoms[ NET_WM_WINDOW_TYPE_UTILITY ] ? + m_aWMAtoms[ NET_WM_WINDOW_TYPE_UTILITY ] : + m_aWMAtoms[ NET_WM_WINDOW_TYPE_DIALOG ]; + break; + case windowType_ModelessDialogue: + case windowType_ModalDialogue: + aWindowTypes[nWindowTypes++] = + m_aWMAtoms[ NET_WM_WINDOW_TYPE_DIALOG ]; + break; + case windowType_Splash: + aWindowTypes[nWindowTypes++] = + m_aWMAtoms[ NET_WM_WINDOW_TYPE_SPLASH ] ? + m_aWMAtoms[ NET_WM_WINDOW_TYPE_SPLASH ] : + m_aWMAtoms[ NET_WM_WINDOW_TYPE_NORMAL ]; + break; + case windowType_Toolbar: + if( m_aWMAtoms[ KDE_NET_WM_WINDOW_TYPE_OVERRIDE ] ) + aWindowTypes[nWindowTypes++] = m_aWMAtoms[ KDE_NET_WM_WINDOW_TYPE_OVERRIDE ]; + aWindowTypes[nWindowTypes++] = + m_aWMAtoms[ NET_WM_WINDOW_TYPE_TOOLBAR ] ? + m_aWMAtoms[ NET_WM_WINDOW_TYPE_TOOLBAR ] : + m_aWMAtoms[ NET_WM_WINDOW_TYPE_NORMAL]; + break; + case windowType_Dock: + aWindowTypes[nWindowTypes++] = + m_aWMAtoms[ NET_WM_WINDOW_TYPE_DOCK ] ? + m_aWMAtoms[ NET_WM_WINDOW_TYPE_DOCK ] : + m_aWMAtoms[ NET_WM_WINDOW_TYPE_NORMAL]; + break; + default: + aWindowTypes[nWindowTypes++] = m_aWMAtoms[ NET_WM_WINDOW_TYPE_NORMAL ]; + break; + } + XChangeProperty( m_pDisplay, + pFrame->GetShellWindow(), + m_aWMAtoms[ NET_WM_WINDOW_TYPE ], + XA_ATOM, + 32, + PropModeReplace, + (unsigned char*)aWindowTypes, + nWindowTypes ); + } + if( ( eType == windowType_ModalDialogue || + eType == windowType_ModelessDialogue ) + && ! pReferenceFrame ) + { + XSetTransientForHint( m_pDisplay, + pFrame->GetShellWindow(), + m_pSalDisplay->GetRootWindow( pFrame->GetScreenNumber() ) ); + pFrame->mbTransientForRoot = true; + } +} + +/* + * WMAdaptor::maximizeFrame + */ + +void WMAdaptor::maximizeFrame( X11SalFrame* pFrame, bool bHorizontal, bool bVertical ) const +{ + pFrame->mbMaximizedVert = bVertical; + pFrame->mbMaximizedHorz = bHorizontal; + + const SalFrameGeometry& rGeom( pFrame->GetUnmirroredGeometry() ); + + // discard pending configure notifies for this frame + XSync( m_pDisplay, False ); + XEvent aDiscard; + while( XCheckTypedWindowEvent( m_pDisplay, + pFrame->GetShellWindow(), + ConfigureNotify, + &aDiscard ) ) + ; + while( XCheckTypedWindowEvent( m_pDisplay, + pFrame->GetWindow(), + ConfigureNotify, + &aDiscard ) ) + ; + + if( bHorizontal || bVertical ) + { + Size aScreenSize( m_pSalDisplay->GetScreenSize( pFrame->GetScreenNumber() ) ); + Point aTL( rGeom.nLeftDecoration, rGeom.nTopDecoration ); + if( m_pSalDisplay->IsXinerama() ) + { + Point aMed( aTL.X() + rGeom.nWidth/2, aTL.Y() + rGeom.nHeight/2 ); + const std::vector< Rectangle >& rScreens = m_pSalDisplay->GetXineramaScreens(); + for( unsigned int i = 0; i < rScreens.size(); i++ ) + if( rScreens[i].IsInside( aMed ) ) + { + aTL += rScreens[i].TopLeft(); + aScreenSize = rScreens[i].GetSize(); + break; + } + } + Rectangle aTarget( aTL, + Size( aScreenSize.Width() - rGeom.nLeftDecoration - rGeom.nTopDecoration, + aScreenSize.Height() - rGeom.nTopDecoration - rGeom.nBottomDecoration ) + ); + if( ! bHorizontal ) + { + aTarget.SetSize( + Size( + pFrame->maRestorePosSize.IsEmpty() ? + rGeom.nWidth : pFrame->maRestorePosSize.GetWidth(), + aTarget.GetHeight() + ) + ); + aTarget.Left() = + pFrame->maRestorePosSize.IsEmpty() ? + rGeom.nX : pFrame->maRestorePosSize.Left(); + } + else if( ! bVertical ) + { + aTarget.SetSize( + Size( + aTarget.GetWidth(), + pFrame->maRestorePosSize.IsEmpty() ? + rGeom.nHeight : pFrame->maRestorePosSize.GetHeight() + ) + ); + aTarget.Top() = + pFrame->maRestorePosSize.IsEmpty() ? + rGeom.nY : pFrame->maRestorePosSize.Top(); + } + + Rectangle aRestore( Point( rGeom.nX, rGeom.nY ), Size( rGeom.nWidth, rGeom.nHeight ) ); + if( pFrame->bMapped_ ) + { + XSetInputFocus( m_pDisplay, + pFrame->GetShellWindow(), + RevertToNone, + CurrentTime + ); + if( m_aWMName.EqualsAscii( "Dtwm" ) ) + { + /* + * Dtwm will only position correctly with center gravity + * and in this case the request actually changes the frame + * not the shell window + */ + aTarget = Rectangle( Point( 0, 0 ), aScreenSize ); + aRestore.Move( -rGeom.nLeftDecoration, -rGeom.nTopDecoration ); + } + } + + if( pFrame->maRestorePosSize.IsEmpty() ) + pFrame->maRestorePosSize = aRestore; + + pFrame->SetPosSize( aTarget ); + pFrame->nWidth_ = aTarget.GetWidth(); + pFrame->nHeight_ = aTarget.GetHeight(); + XRaiseWindow( m_pDisplay, + pFrame->GetShellWindow() + ); + if( pFrame->GetStackingWindow() ) + XRaiseWindow( m_pDisplay, + pFrame->GetStackingWindow() + ); + + } + else + { + pFrame->SetPosSize( pFrame->maRestorePosSize ); + pFrame->maRestorePosSize = Rectangle(); + pFrame->nWidth_ = rGeom.nWidth; + pFrame->nHeight_ = rGeom.nHeight; + if( m_aWMName.EqualsAscii( "Dtwm" ) && pFrame->bMapped_ ) + { + pFrame->maGeometry.nX += rGeom.nLeftDecoration; + pFrame->maGeometry.nY += rGeom.nTopDecoration; + } + } +} + +/* + * NetWMAdaptor::maximizeFrame + * changes _NET_WM_STATE by sending a client message + */ + +void NetWMAdaptor::maximizeFrame( X11SalFrame* pFrame, bool bHorizontal, bool bVertical ) const +{ + pFrame->mbMaximizedVert = bVertical; + pFrame->mbMaximizedHorz = bHorizontal; + + if( m_aWMAtoms[ NET_WM_STATE ] + && m_aWMAtoms[ NET_WM_STATE_MAXIMIZED_VERT ] + && m_aWMAtoms[ NET_WM_STATE_MAXIMIZED_HORZ ] + && ( pFrame->nStyle_ & ~SAL_FRAME_STYLE_DEFAULT ) + ) + { + if( pFrame->bMapped_ ) + { + // window already mapped, send WM a message + XEvent aEvent; + aEvent.type = ClientMessage; + aEvent.xclient.display = m_pDisplay; + aEvent.xclient.window = pFrame->GetShellWindow(); + aEvent.xclient.message_type = m_aWMAtoms[ NET_WM_STATE ]; + aEvent.xclient.format = 32; + aEvent.xclient.data.l[0] = bHorizontal ? 1 : 0; + aEvent.xclient.data.l[1] = m_aWMAtoms[ NET_WM_STATE_MAXIMIZED_HORZ ]; + aEvent.xclient.data.l[2] = bHorizontal == bVertical ? m_aWMAtoms[ NET_WM_STATE_MAXIMIZED_VERT ] : 0; + aEvent.xclient.data.l[3] = 0; + aEvent.xclient.data.l[4] = 0; + XSendEvent( m_pDisplay, + m_pSalDisplay->GetRootWindow( pFrame->GetScreenNumber() ), + False, + SubstructureNotifyMask | SubstructureRedirectMask, + &aEvent + ); + if( bHorizontal != bVertical ) + { + aEvent.xclient.data.l[0]= bVertical ? 1 : 0; + aEvent.xclient.data.l[1]= m_aWMAtoms[ NET_WM_STATE_MAXIMIZED_VERT ]; + aEvent.xclient.data.l[2]= 0; + XSendEvent( m_pDisplay, + m_pSalDisplay->GetRootWindow( pFrame->GetScreenNumber() ), + False, + SubstructureNotifyMask | SubstructureRedirectMask, + &aEvent + ); + } + } + else + { + // window not mapped yet, set _NET_WM_STATE directly + setNetWMState( pFrame ); + } + if( !bHorizontal && !bVertical ) + pFrame->maRestorePosSize = Rectangle(); + else if( pFrame->maRestorePosSize.IsEmpty() ) + { + const SalFrameGeometry& rGeom( pFrame->GetUnmirroredGeometry() ); + pFrame->maRestorePosSize = + Rectangle( Point( rGeom.nX, rGeom.nY ), Size( rGeom.nWidth, rGeom.nHeight ) ); + } + } + else + WMAdaptor::maximizeFrame( pFrame, bHorizontal, bVertical ); +} + +/* + * GnomeWMAdaptor::maximizeFrame + * changes _WIN_STATE by sending a client message + */ + +void GnomeWMAdaptor::maximizeFrame( X11SalFrame* pFrame, bool bHorizontal, bool bVertical ) const +{ + pFrame->mbMaximizedVert = bVertical; + pFrame->mbMaximizedHorz = bHorizontal; + + if( m_aWMAtoms[ WIN_STATE ] + && ( pFrame->nStyle_ & ~SAL_FRAME_STYLE_DEFAULT ) + ) + { + if( pFrame->bMapped_ ) + { + // window already mapped, send WM a message + XEvent aEvent; + aEvent.type = ClientMessage; + aEvent.xclient.display = m_pDisplay; + aEvent.xclient.window = pFrame->GetShellWindow(); + aEvent.xclient.message_type = m_aWMAtoms[ WIN_STATE ]; + aEvent.xclient.format = 32; + aEvent.xclient.data.l[0] = (1<<2)|(1<<3); + aEvent.xclient.data.l[1] = + (bVertical ? (1<<2) : 0) + | (bHorizontal ? (1<<3) : 0); + aEvent.xclient.data.l[2] = 0; + aEvent.xclient.data.l[3] = 0; + aEvent.xclient.data.l[4] = 0; + XSendEvent( m_pDisplay, + m_pSalDisplay->GetRootWindow( pFrame->GetScreenNumber() ), + False, + SubstructureNotifyMask, + &aEvent + ); + } + else + // window not mapped yet, set _WIN_STATE directly + setGnomeWMState( pFrame ); + + if( !bHorizontal && !bVertical ) + pFrame->maRestorePosSize = Rectangle(); + else if( pFrame->maRestorePosSize.IsEmpty() ) + { + const SalFrameGeometry& rGeom( pFrame->GetUnmirroredGeometry() ); + pFrame->maRestorePosSize = + Rectangle( Point( rGeom.nX, rGeom.nY ), Size( rGeom.nWidth, rGeom.nHeight ) ); + } + } + else + WMAdaptor::maximizeFrame( pFrame, bHorizontal, bVertical ); +} + +/* + * WMAdaptor::supportsICCCMPos + */ + +bool WMAdaptor::supportsICCCMPos() const +{ + return + m_aWMName.EqualsAscii( "Sawfish" ) + || m_aWMName.EqualsAscii( "Dtwm" ); +} + +/* + * NetWMAdaptor::supportsICCCMPos + */ + +bool NetWMAdaptor::supportsICCCMPos() const +{ + return true; +} + + +/* + * WMAdaptor::enableAlwaysOnTop + */ +void WMAdaptor::enableAlwaysOnTop( X11SalFrame*, bool /*bEnable*/ ) const +{ +} + +/* + * NetWMAdaptor::enableAlwaysOnTop + */ +void NetWMAdaptor::enableAlwaysOnTop( X11SalFrame* pFrame, bool bEnable ) const +{ + pFrame->bAlwaysOnTop_ = bEnable; + if( m_aWMAtoms[ NET_WM_STATE_STAYS_ON_TOP ] ) + { + if( pFrame->bMapped_ ) + { + // window already mapped, send WM a message + XEvent aEvent; + aEvent.type = ClientMessage; + aEvent.xclient.display = m_pDisplay; + aEvent.xclient.window = pFrame->GetShellWindow(); + aEvent.xclient.message_type = m_aWMAtoms[ NET_WM_STATE ]; + aEvent.xclient.format = 32; + aEvent.xclient.data.l[0] = bEnable ? 1 : 0; + aEvent.xclient.data.l[1] = m_aWMAtoms[ NET_WM_STATE_STAYS_ON_TOP ]; + aEvent.xclient.data.l[2] = 0; + aEvent.xclient.data.l[3] = 0; + aEvent.xclient.data.l[4] = 0; + XSendEvent( m_pDisplay, + m_pSalDisplay->GetRootWindow( pFrame->GetScreenNumber() ), + False, + SubstructureNotifyMask | SubstructureRedirectMask, + &aEvent + ); + } + else + setNetWMState( pFrame ); + } +} + +/* + * GnomeWMAdaptor::enableAlwaysOnTop + */ +void GnomeWMAdaptor::enableAlwaysOnTop( X11SalFrame* pFrame, bool bEnable ) const +{ + pFrame->bAlwaysOnTop_ = bEnable; + if( m_aWMAtoms[ WIN_LAYER ] ) + { + if( pFrame->bMapped_ ) + { + // window already mapped, send WM a message + XEvent aEvent; + aEvent.type = ClientMessage; + aEvent.xclient.display = m_pDisplay; + aEvent.xclient.window = pFrame->GetShellWindow(); + aEvent.xclient.message_type = m_aWMAtoms[ WIN_LAYER ]; + aEvent.xclient.format = 32; + aEvent.xclient.data.l[0] = bEnable ? 6 : 4; + aEvent.xclient.data.l[1] = 0; + aEvent.xclient.data.l[2] = 0; + aEvent.xclient.data.l[3] = 0; + aEvent.xclient.data.l[4] = 0; + XSendEvent( m_pDisplay, + m_pSalDisplay->GetRootWindow( pFrame->GetScreenNumber() ), + False, + SubstructureNotifyMask | SubstructureRedirectMask, + &aEvent + ); + } + else + { + sal_uInt32 nNewLayer = bEnable ? 6 : 4; + XChangeProperty( m_pDisplay, + pFrame->GetShellWindow(), + m_aWMAtoms[ WIN_LAYER ], + XA_CARDINAL, + 32, + PropModeReplace, + (unsigned char*)&nNewLayer, + 1 + ); + } + } +} + +/* + * WMAdaptor::changeReferenceFrame + */ +void WMAdaptor::changeReferenceFrame( X11SalFrame* pFrame, X11SalFrame* pReferenceFrame ) const +{ + if( ! ( pFrame->nStyle_ & SAL_FRAME_STYLE_PLUG ) + && ! pFrame->IsOverrideRedirect() + && ! pFrame->IsFloatGrabWindow() + ) + { + XLIB_Window aTransient = pFrame->pDisplay_->GetRootWindow( pFrame->GetScreenNumber() ); + pFrame->mbTransientForRoot = true; + if( pReferenceFrame ) + { + aTransient = pReferenceFrame->GetShellWindow(); + pFrame->mbTransientForRoot = false; + } + XSetTransientForHint( m_pDisplay, + pFrame->GetShellWindow(), + aTransient ); + } +} + +/* + * WMAdaptor::handlePropertyNotify + */ +int WMAdaptor::handlePropertyNotify( X11SalFrame*, XPropertyEvent* ) const +{ + return 0; +} + +/* + * NetWMAdaptor::handlePropertyNotify + */ +int NetWMAdaptor::handlePropertyNotify( X11SalFrame* pFrame, XPropertyEvent* pEvent ) const +{ + int nHandled = 1; + if( pEvent->atom == m_aWMAtoms[ NET_WM_STATE ] ) + { + pFrame->mbMaximizedHorz = pFrame->mbMaximizedVert = false; + pFrame->mbShaded = false; + + if( pEvent->state == PropertyNewValue ) + { + Atom nType, *pStates; + int nFormat; + unsigned long nItems, nBytesLeft; + unsigned char* pData = NULL; + long nOffset = 0; + do + { + XGetWindowProperty( m_pDisplay, + pEvent->window, + m_aWMAtoms[ NET_WM_STATE ], + nOffset, 64, + False, + XA_ATOM, + &nType, + &nFormat, + &nItems, &nBytesLeft, + &pData ); + if( pData ) + { + if( nType == XA_ATOM && nFormat == 32 && nItems > 0 ) + { + pStates = (Atom*)pData; + for( unsigned long i = 0; i < nItems; i++ ) + { + if( pStates[i] == m_aWMAtoms[ NET_WM_STATE_MAXIMIZED_VERT ] && m_aWMAtoms[ NET_WM_STATE_MAXIMIZED_VERT ] ) + pFrame->mbMaximizedVert = true; + else if( pStates[i] == m_aWMAtoms[ NET_WM_STATE_MAXIMIZED_HORZ ] && m_aWMAtoms[ NET_WM_STATE_MAXIMIZED_HORZ ] ) + pFrame->mbMaximizedHorz = true; + else if( pStates[i] == m_aWMAtoms[ NET_WM_STATE_SHADED ] && m_aWMAtoms[ NET_WM_STATE_SHADED ] ) + pFrame->mbShaded = true; + } + } + XFree( pData ); + pData = NULL; + nOffset += nItems * nFormat / 32; + } + else + break; + } while( nBytesLeft > 0 ); + } + + if( ! (pFrame->mbMaximizedHorz || pFrame->mbMaximizedVert ) ) + pFrame->maRestorePosSize = Rectangle(); + else + { + const SalFrameGeometry& rGeom = pFrame->GetUnmirroredGeometry(); + // the current geometry may already be changed by the corresponding + // ConfigureNotify, but this cannot be helped + pFrame->maRestorePosSize = + Rectangle( Point( rGeom.nX, rGeom.nY ), + Size( rGeom.nWidth, rGeom.nHeight ) ); + } + } + else if( pEvent->atom == m_aWMAtoms[ NET_WM_DESKTOP ] ) + { + pFrame->m_nWorkArea = getWindowWorkArea( pFrame->GetShellWindow() ); + } + else + nHandled = 0; + + return nHandled; +} + +/* + * GnomeWMAdaptor::handlePropertyNotify + */ +int GnomeWMAdaptor::handlePropertyNotify( X11SalFrame* pFrame, XPropertyEvent* pEvent ) const +{ + int nHandled = 1; + if( pEvent->atom == m_aWMAtoms[ WIN_STATE ] ) + { + pFrame->mbMaximizedHorz = pFrame->mbMaximizedVert = false; + pFrame->mbShaded = false; + + if( pEvent->state == PropertyNewValue ) + { + Atom nType; + int nFormat = 0; + unsigned long nItems = 0; + unsigned long nBytesLeft = 0; + unsigned char* pData = 0; + XGetWindowProperty( m_pDisplay, + pEvent->window, + m_aWMAtoms[ WIN_STATE ], + 0, 1, + False, + XA_CARDINAL, + &nType, + &nFormat, + &nItems, &nBytesLeft, + &pData ); + if( pData ) + { + if( nType == XA_CARDINAL && nFormat == 32 && nItems == 1 ) + { + sal_uInt32 nWinState = *(sal_uInt32*)pData; + if( nWinState & (1<<2) ) + pFrame->mbMaximizedVert = true; + if( nWinState & (1<<3) ) + pFrame->mbMaximizedHorz = true; + if( nWinState & (1<<5) ) + pFrame->mbShaded = true; + } + XFree( pData ); + } + } + + if( ! (pFrame->mbMaximizedHorz || pFrame->mbMaximizedVert ) ) + pFrame->maRestorePosSize = Rectangle(); + else + { + const SalFrameGeometry& rGeom = pFrame->GetUnmirroredGeometry(); + // the current geometry may already be changed by the corresponding + // ConfigureNotify, but this cannot be helped + pFrame->maRestorePosSize = + Rectangle( Point( rGeom.nX, rGeom.nY ), + Size( rGeom.nWidth, rGeom.nHeight ) ); + } + } + else if( pEvent->atom == m_aWMAtoms[ NET_WM_DESKTOP ] ) + { + pFrame->m_nWorkArea = getWindowWorkArea( pFrame->GetShellWindow() ); + } + else + nHandled = 0; + + return nHandled; +} + +/* + * WMAdaptor::shade + */ +void WMAdaptor::shade( X11SalFrame*, bool /*bToShaded*/ ) const +{ +} + +/* + * NetWMAdaptor::shade + */ +void NetWMAdaptor::shade( X11SalFrame* pFrame, bool bToShaded ) const +{ + if( m_aWMAtoms[ NET_WM_STATE ] + && m_aWMAtoms[ NET_WM_STATE_SHADED ] + && ( pFrame->nStyle_ & ~SAL_FRAME_STYLE_DEFAULT ) + ) + { + pFrame->mbShaded = bToShaded; + if( pFrame->bMapped_ ) + { + // window already mapped, send WM a message + XEvent aEvent; + aEvent.type = ClientMessage; + aEvent.xclient.display = m_pDisplay; + aEvent.xclient.window = pFrame->GetShellWindow(); + aEvent.xclient.message_type = m_aWMAtoms[ NET_WM_STATE ]; + aEvent.xclient.format = 32; + aEvent.xclient.data.l[0] = bToShaded ? 1 : 0; + aEvent.xclient.data.l[1] = m_aWMAtoms[ NET_WM_STATE_SHADED ]; + aEvent.xclient.data.l[2] = 0; + aEvent.xclient.data.l[3] = 0; + aEvent.xclient.data.l[4] = 0; + XSendEvent( m_pDisplay, + m_pSalDisplay->GetRootWindow( pFrame->GetScreenNumber() ), + False, + SubstructureNotifyMask | SubstructureRedirectMask, + &aEvent + ); + } + else + { + // window not mapped yet, set _NET_WM_STATE directly + setNetWMState( pFrame ); + } + } +} + +/* + * GnomeWMAdaptor::shade + */ +void GnomeWMAdaptor::shade( X11SalFrame* pFrame, bool bToShaded ) const +{ + if( m_aWMAtoms[ WIN_STATE ] ) + { + pFrame->mbShaded = bToShaded; + if( pFrame->bMapped_ ) + { + // window already mapped, send WM a message + XEvent aEvent; + aEvent.type = ClientMessage; + aEvent.xclient.display = m_pDisplay; + aEvent.xclient.window = pFrame->GetShellWindow(); + aEvent.xclient.message_type = m_aWMAtoms[ WIN_STATE ]; + aEvent.xclient.format = 32; + aEvent.xclient.data.l[0] = (1<<5); + aEvent.xclient.data.l[1] = bToShaded ? (1<<5) : 0; + aEvent.xclient.data.l[2] = 0; + aEvent.xclient.data.l[3] = 0; + aEvent.xclient.data.l[4] = 0; + XSendEvent( m_pDisplay, + m_pSalDisplay->GetRootWindow( pFrame->GetScreenNumber() ), + False, + SubstructureNotifyMask | SubstructureRedirectMask, + &aEvent + ); + } + else + setGnomeWMState( pFrame ); + } +} + +/* + * WMAdaptor::showFullScreen + */ +void WMAdaptor::showFullScreen( X11SalFrame* pFrame, bool bFullScreen ) const +{ + pFrame->mbFullScreen = bFullScreen; + maximizeFrame( pFrame, bFullScreen, bFullScreen ); +} + +/* + * NetWMAdaptor::showFullScreen + */ +void NetWMAdaptor::showFullScreen( X11SalFrame* pFrame, bool bFullScreen ) const +{ + if( m_aWMAtoms[ NET_WM_STATE_FULLSCREEN ] ) + { + pFrame->mbFullScreen = bFullScreen; + if( bFullScreen ) + { + if( m_aWMAtoms[ MOTIF_WM_HINTS ] ) + { + XDeleteProperty( m_pDisplay, + pFrame->GetShellWindow(), + m_aWMAtoms[ MOTIF_WM_HINTS ] ); + } + } + if( pFrame->bMapped_ ) + { + // window already mapped, send WM a message + XEvent aEvent; + aEvent.type = ClientMessage; + aEvent.xclient.display = m_pDisplay; + aEvent.xclient.window = pFrame->GetShellWindow(); + aEvent.xclient.message_type = m_aWMAtoms[ NET_WM_STATE ]; + aEvent.xclient.format = 32; + aEvent.xclient.data.l[0] = bFullScreen ? 1 : 0; + aEvent.xclient.data.l[1] = m_aWMAtoms[ NET_WM_STATE_FULLSCREEN ]; + aEvent.xclient.data.l[2] = 0; + aEvent.xclient.data.l[3] = 0; + aEvent.xclient.data.l[4] = 0; + XSendEvent( m_pDisplay, + m_pSalDisplay->GetRootWindow( pFrame->GetScreenNumber() ), + False, + SubstructureNotifyMask | SubstructureRedirectMask, + &aEvent + ); + } + else + { + // window not mapped yet, set _NET_WM_STATE directly + setNetWMState( pFrame ); + } + // #i42750# guess size before resize event shows up + if( bFullScreen ) + { + if( m_pSalDisplay->IsXinerama() ) + { + XLIB_Window aRoot, aChild; + int root_x = 0, root_y = 0, lx, ly; + unsigned int mask; + XQueryPointer( m_pDisplay, + m_pSalDisplay->GetRootWindow( pFrame->GetScreenNumber() ), + &aRoot, &aChild, + &root_x, &root_y, &lx, &ly, &mask ); + const std::vector< Rectangle >& rScreens = m_pSalDisplay->GetXineramaScreens(); + Point aMousePoint( root_x, root_y ); + for( unsigned int i = 0; i < rScreens.size(); i++ ) + { + if( rScreens[i].IsInside( aMousePoint ) ) + { + pFrame->maGeometry.nX = rScreens[i].Left(); + pFrame->maGeometry.nY = rScreens[i].Top(); + pFrame->maGeometry.nWidth = rScreens[i].GetWidth(); + pFrame->maGeometry.nHeight = rScreens[i].GetHeight(); + break; + } + } + } + else + { + Size aSize = m_pSalDisplay->GetScreenSize( pFrame->GetScreenNumber() ); + pFrame->maGeometry.nX = 0; + pFrame->maGeometry.nY = 0; + pFrame->maGeometry.nWidth = aSize.Width(); + pFrame->maGeometry.nHeight = aSize.Height(); + } + pFrame->CallCallback( SALEVENT_MOVERESIZE, NULL ); + } + } + else WMAdaptor::showFullScreen( pFrame, bFullScreen ); +} + +/* + * WMAdaptor::getCurrentWorkArea + */ +// FIXME: multiscreen case +int WMAdaptor::getCurrentWorkArea() const +{ + int nCurrent = -1; + if( m_aWMAtoms[ NET_CURRENT_DESKTOP ] ) + { + Atom aRealType = None; + int nFormat = 8; + unsigned long nItems = 0; + unsigned long nBytesLeft = 0; + unsigned char* pProperty = NULL; + if( XGetWindowProperty( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + m_aWMAtoms[ NET_CURRENT_DESKTOP ], + 0, 1, + False, + XA_CARDINAL, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 + && pProperty + ) + { + nCurrent = int(*(sal_Int32*)pProperty); + XFree( pProperty ); + } + else if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } + } + return nCurrent; +} + +/* + * WMAdaptor::getWindowWorkArea + */ +int WMAdaptor::getWindowWorkArea( XLIB_Window aWindow ) const +{ + int nCurrent = -1; + if( m_aWMAtoms[ NET_WM_DESKTOP ] ) + { + Atom aRealType = None; + int nFormat = 8; + unsigned long nItems = 0; + unsigned long nBytesLeft = 0; + unsigned char* pProperty = NULL; + if( XGetWindowProperty( m_pDisplay, + aWindow, + m_aWMAtoms[ NET_WM_DESKTOP ], + 0, 1, + False, + XA_CARDINAL, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ) == 0 + && pProperty + ) + { + nCurrent = int(*(sal_Int32*)pProperty); + XFree( pProperty ); + } + else if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } + } + return nCurrent; +} + +/* + * WMAdaptor::getCurrentWorkArea + */ +// fixme: multi screen case +void WMAdaptor::switchToWorkArea( int nWorkArea, bool bConsiderWM ) const +{ + if( bConsiderWM && ! getWMshouldSwitchWorkspace() ) + return; + + if( m_aWMAtoms[ NET_CURRENT_DESKTOP ] ) + { + XEvent aEvent; + aEvent.type = ClientMessage; + aEvent.xclient.display = m_pDisplay; + aEvent.xclient.window = m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ); + aEvent.xclient.message_type = m_aWMAtoms[ NET_CURRENT_DESKTOP ]; + aEvent.xclient.format = 32; + aEvent.xclient.data.l[0] = nWorkArea; + aEvent.xclient.data.l[1] = 0; + aEvent.xclient.data.l[2] = 0; + aEvent.xclient.data.l[3] = 0; + aEvent.xclient.data.l[4] = 0; + XSendEvent( m_pDisplay, + m_pSalDisplay->GetRootWindow( m_pSalDisplay->GetDefaultScreenNumber() ), + False, + SubstructureNotifyMask | SubstructureRedirectMask, + &aEvent + ); + } +} + +/* + * WMAdaptor::frameIsMapping + */ +void WMAdaptor::frameIsMapping( X11SalFrame* ) const +{ +} + +/* + * NetWMAdaptor::frameIsMapping + */ +void NetWMAdaptor::frameIsMapping( X11SalFrame* pFrame ) const +{ + setNetWMState( pFrame ); +} + +/* + * WMAdaptor::setFrameStruts + */ +void WMAdaptor::setFrameStruts( X11SalFrame*, + int, int, int, int, + int, int, int, int, + int, int, int, int ) const +{ +} + +/* + * NetWMAdaptor::setFrameStruts + */ +void NetWMAdaptor::setFrameStruts( X11SalFrame* pFrame, + int left, int right, int top, int bottom, + int left_start_y, int left_end_y, + int right_start_y, int right_end_y, + int top_start_x, int top_end_x, + int bottom_start_x, int bottom_end_x ) const +{ + long nData[12]; + nData[0] = left; + nData[1] = right; + nData[2] = top; + nData[3] = bottom; + nData[4] = left_start_y; + nData[5] = left_end_y; + nData[6] = right_start_y; + nData[7] = right_end_y; + nData[8] = top_start_x; + nData[9] = top_end_x; + nData[10]= bottom_start_x; + nData[11]= bottom_end_x; + Atom aProperty = None; + int nSetData = 0; + + if( m_aWMAtoms[NET_WM_STRUT_PARTIAL] ) + { + aProperty = m_aWMAtoms[NET_WM_STRUT_PARTIAL]; + nSetData = 12; + } + else if( m_aWMAtoms[NET_WM_STRUT] ) + { + aProperty = m_aWMAtoms[NET_WM_STRUT]; + nSetData = 4; + } + if( nSetData ) + { + XChangeProperty( m_pDisplay, + pFrame->GetShellWindow(), + aProperty, + XA_CARDINAL, + 32, + PropModeReplace, + (unsigned char*)&nData, + nSetData + ); + } +} + +/* + * WMAdaptor::setUserTime + */ +void WMAdaptor::setUserTime( X11SalFrame*, long ) const +{ +} + +/* + * NetWMAdaptor::setUserTime + */ +void NetWMAdaptor::setUserTime( X11SalFrame* i_pFrame, long i_nUserTime ) const +{ + if( m_aWMAtoms[NET_WM_USER_TIME] ) + { + XChangeProperty( m_pDisplay, + i_pFrame->GetShellWindow(), + m_aWMAtoms[NET_WM_USER_TIME], + XA_CARDINAL, + 32, + PropModeReplace, + (unsigned char*)&i_nUserTime, + 1 + ); + } +} + +/* + * WMAdaptor::setPID + */ +void WMAdaptor::setPID( X11SalFrame* i_pFrame ) const +{ + if( m_aWMAtoms[NET_WM_PID] ) + { + long nPID = (long)getpid(); + XChangeProperty( m_pDisplay, + i_pFrame->GetShellWindow(), + m_aWMAtoms[NET_WM_PID], + XA_CARDINAL, + 32, + PropModeReplace, + (unsigned char*)&nPID, + 1 + ); + } +} + +/* +* WMAdaptor::setClientMachine +*/ +void WMAdaptor::setClientMachine( X11SalFrame* i_pFrame ) const +{ + rtl::OString aWmClient( rtl::OUStringToOString( GetX11SalData()->GetLocalHostName(), RTL_TEXTENCODING_ASCII_US ) ); + XTextProperty aClientProp = { (unsigned char*)aWmClient.getStr(), XA_STRING, 8, aWmClient.getLength() }; + XSetWMClientMachine( m_pDisplay, i_pFrame->GetShellWindow(), &aClientProp ); +} + +void WMAdaptor::answerPing( X11SalFrame* i_pFrame, XClientMessageEvent* i_pEvent ) const +{ + if( m_aWMAtoms[NET_WM_PING] && + i_pEvent->message_type == m_aWMAtoms[ WM_PROTOCOLS ] && + (Atom)i_pEvent->data.l[0] == m_aWMAtoms[ NET_WM_PING ] ) + { + XEvent aEvent; + aEvent.xclient = *i_pEvent; + aEvent.xclient.window = m_pSalDisplay->GetRootWindow( i_pFrame->GetScreenNumber() ); + XSendEvent( m_pDisplay, + m_pSalDisplay->GetRootWindow( i_pFrame->GetScreenNumber() ), + False, + SubstructureNotifyMask | SubstructureRedirectMask, + &aEvent + ); + XFlush( m_pDisplay ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/desktopdetect/desktopdetector.cxx b/vcl/unx/generic/desktopdetect/desktopdetector.cxx new file mode 100644 index 000000000000..568507822c1e --- /dev/null +++ b/vcl/unx/generic/desktopdetect/desktopdetector.cxx @@ -0,0 +1,356 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <unx/svunx.h> +#include <tools/prex.h> +#include <X11/Xatom.h> +#include <tools/postx.h> + +#include "rtl/ustrbuf.hxx" +#include "osl/module.h" +#include "osl/process.h" +#include "osl/thread.h" + +#include "vclpluginapi.h" + +#include <unistd.h> +#include <string.h> + +using ::rtl::OUString; +using ::rtl::OString; +enum { + DESKTOP_NONE = 0, + DESKTOP_UNKNOWN, + DESKTOP_GNOME, + DESKTOP_KDE, + DESKTOP_KDE4, + DESKTOP_CDE +}; + +static const char * desktop_strings[] = { "none", "unknown", "GNOME", "KDE", "KDE4", "CDE" }; + +static bool is_gnome_desktop( Display* pDisplay ) +{ + bool ret = false; + + // warning: these checks are coincidental, GNOME does not + // explicitly advertise itself + + if ( NULL != getenv( "GNOME_DESKTOP_SESSION_ID" ) ) + ret = true; + + if( ! ret ) + { + Atom nAtom1 = XInternAtom( pDisplay, "GNOME_SM_PROXY", True ); + Atom nAtom2 = XInternAtom( pDisplay, "NAUTILUS_DESKTOP_WINDOW_ID", True ); + if( nAtom1 || nAtom2 ) + { + int nProperties = 0; + Atom* pProperties = XListProperties( pDisplay, DefaultRootWindow( pDisplay ), &nProperties ); + if( pProperties && nProperties ) + { + for( int i = 0; i < nProperties; i++ ) + if( pProperties[ i ] == nAtom1 || + pProperties[ i ] == nAtom2 ) + { + ret = true; + } + XFree( pProperties ); + } + } + } + + if( ! ret ) + { + Atom nUTFAtom = XInternAtom( pDisplay, "UTF8_STRING", True ); + Atom nNetWMNameAtom = XInternAtom( pDisplay, "_NET_WM_NAME", True ); + if( nUTFAtom && nNetWMNameAtom ) + { + // another, more expensive check: search for a gnome-panel + XLIB_Window aRoot, aParent, *pChildren = NULL; + unsigned int nChildren = 0; + XQueryTree( pDisplay, DefaultRootWindow( pDisplay ), + &aRoot, &aParent, &pChildren, &nChildren ); + if( pChildren && nChildren ) + { + for( unsigned int i = 0; i < nChildren && ! ret; i++ ) + { + Atom nType = None; + int nFormat = 0; + unsigned long nItems = 0, nBytes = 0; + unsigned char* pProp = NULL; + XGetWindowProperty( pDisplay, + pChildren[i], + nNetWMNameAtom, + 0, 8, + False, + nUTFAtom, + &nType, + &nFormat, + &nItems, + &nBytes, + &pProp ); + if( pProp && nType == nUTFAtom ) + { + OString aWMName( (sal_Char*)pProp ); + if( aWMName.equalsIgnoreAsciiCase( "gnome-panel" ) ) + ret = true; + } + if( pProp ) + XFree( pProp ); + } + XFree( pChildren ); + } + } + } + + return ret; +} + +static bool bWasXError = false; + +static inline bool WasXError() +{ + bool bRet = bWasXError; + bWasXError = false; + return bRet; +} + +extern "C" +{ + static int autodect_error_handler( Display*, XErrorEvent* ) + { + bWasXError = true; + return 0; + } + + typedef int(* XErrorHandler)(Display*,XErrorEvent*); +} + +static int KDEVersion( Display* pDisplay ) +{ + int nRet = 0; + + Atom nFullSession = XInternAtom( pDisplay, "KDE_FULL_SESSION", True ); + Atom nKDEVersion = XInternAtom( pDisplay, "KDE_SESSION_VERSION", True ); + + if( nFullSession ) + { + if( !nKDEVersion ) + return 3; + + Atom aRealType = None; + int nFormat = 8; + unsigned long nItems = 0; + unsigned long nBytesLeft = 0; + unsigned char* pProperty = NULL; + XGetWindowProperty( pDisplay, + DefaultRootWindow( pDisplay ), + nKDEVersion, + 0, 1, + False, + AnyPropertyType, + &aRealType, + &nFormat, + &nItems, + &nBytesLeft, + &pProperty ); + if( !WasXError() && nItems != 0 && pProperty ) + { + nRet = *reinterpret_cast< sal_Int32* >( pProperty ); + } + if( pProperty ) + { + XFree( pProperty ); + pProperty = NULL; + } + } + return nRet; +} + +static bool is_kde_desktop( Display* pDisplay ) +{ + if ( NULL != getenv( "KDE_FULL_SESSION" ) ) + { + const char *pVer = getenv( "KDE_SESSION_VERSION" ); + if ( !pVer || pVer[0] == '0' ) + { + return true; // does not exist => KDE3 + } + + rtl::OUString aVer( RTL_CONSTASCII_USTRINGPARAM( "3" ) ); + if ( aVer.equalsIgnoreAsciiCaseAscii( pVer ) ) + { + return true; + } + } + + if ( KDEVersion( pDisplay ) == 3 ) + return true; + + return false; +} + +static bool is_kde4_desktop( Display* pDisplay ) +{ + if ( NULL != getenv( "KDE_FULL_SESSION" ) ) + { + rtl::OUString aVer( RTL_CONSTASCII_USTRINGPARAM( "4" ) ); + + const char *pVer = getenv( "KDE_SESSION_VERSION" ); + if ( pVer && aVer.equalsIgnoreAsciiCaseAscii( pVer ) ) + return true; + } + + if ( KDEVersion( pDisplay ) == 4 ) + return true; + + return false; +} + +static bool is_cde_desktop( Display* pDisplay ) +{ + void* pLibrary = NULL; + + Atom nDtAtom = XInternAtom( pDisplay, "_DT_WM_READY", True ); + OUString aPathName( RTL_CONSTASCII_USTRINGPARAM( "file:///usr/dt/lib/libDtSvc.so" ) ); + if( nDtAtom && ( pLibrary = osl_loadModule( aPathName.pData, SAL_LOADMODULE_DEFAULT ) ) ) + { + osl_unloadModule( (oslModule)pLibrary ); + return true; + } + + return false; +} + + +extern "C" +{ + +DESKTOP_DETECTOR_PUBLIC rtl::OUString get_desktop_environment() +{ + rtl::OUStringBuffer aRet( 8 ); + static const char *pOverride = getenv( "OOO_FORCE_DESKTOP" ); + + if ( pOverride && *pOverride ) + { + OString aOver( pOverride ); + + if ( aOver.equalsIgnoreAsciiCase( "cde" ) ) + aRet.appendAscii( desktop_strings[DESKTOP_CDE] ); + if ( aOver.equalsIgnoreAsciiCase( "kde4" ) ) + aRet.appendAscii( desktop_strings[DESKTOP_KDE4] ); + if ( aOver.equalsIgnoreAsciiCase( "gnome" ) ) + aRet.appendAscii( desktop_strings[DESKTOP_GNOME] ); + if ( aOver.equalsIgnoreAsciiCase( "kde" ) ) + aRet.appendAscii( desktop_strings[DESKTOP_KDE] ); + if ( aOver.equalsIgnoreAsciiCase( "none" ) ) + aRet.appendAscii( desktop_strings[DESKTOP_UNKNOWN] ); + } + + if( aRet.getLength() == 0 ) + { + // get display to connect to + const char* pDisplayStr = getenv( "DISPLAY" ); + + const char* pUsePlugin = getenv( "SAL_USE_VCLPLUGIN" ); + + if (pUsePlugin && (strcmp(pUsePlugin, "svp") == 0)) + pDisplayStr = NULL; + else + { + int nParams = osl_getCommandArgCount(); + OUString aParam; + OString aBParm; + for( int i = 0; i < nParams; i++ ) + { + osl_getCommandArg( i, &aParam.pData ); + if( aParam.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "-headless" ) ) || + aParam.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "--headless" ) ) ) + { + pDisplayStr = NULL; + break; + } + if( i < nParams-1 && (aParam.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "-display" ) ) || aParam.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "--display" ) )) ) + { + osl_getCommandArg( i+1, &aParam.pData ); + aBParm = OUStringToOString( aParam, osl_getThreadTextEncoding() ); + pDisplayStr = aBParm.getStr(); + break; + } + } + } + + // no server at all + if( ! pDisplayStr || !*pDisplayStr ) + aRet.appendAscii( desktop_strings[DESKTOP_NONE] ); + else + { + /* #i92121# workaround deadlocks in the X11 implementation + */ + static const char* pNoXInitThreads = getenv( "SAL_NO_XINITTHREADS" ); + /* #i90094# + from now on we know that an X connection will be + established, so protect X against itself + */ + if( ! ( pNoXInitThreads && *pNoXInitThreads ) ) + XInitThreads(); + + Display* pDisplay = XOpenDisplay( pDisplayStr ); + if( pDisplay ) + { + XErrorHandler pOldHdl = XSetErrorHandler( autodect_error_handler ); + + if ( is_kde4_desktop( pDisplay ) ) + aRet.appendAscii( desktop_strings[DESKTOP_KDE4] ); + else if ( is_gnome_desktop( pDisplay ) ) + aRet.appendAscii( desktop_strings[DESKTOP_GNOME] ); + else if ( is_cde_desktop( pDisplay ) ) + aRet.appendAscii( desktop_strings[DESKTOP_CDE] ); + else if ( is_kde_desktop( pDisplay ) ) + aRet.appendAscii( desktop_strings[DESKTOP_KDE] ); + else + aRet.appendAscii( desktop_strings[DESKTOP_UNKNOWN] ); + + // set the default handler again + XSetErrorHandler( pOldHdl ); + + XCloseDisplay( pDisplay ); + } + } + } + + return aRet.makeStringAndClear(); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/dtrans/X11_clipboard.cxx b/vcl/unx/generic/dtrans/X11_clipboard.cxx new file mode 100644 index 000000000000..4f1934397397 --- /dev/null +++ b/vcl/unx/generic/dtrans/X11_clipboard.cxx @@ -0,0 +1,296 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <X11/Xatom.h> +#include <X11_clipboard.hxx> +#include <X11_transferable.hxx> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/datatransfer/clipboard/RenderingCapabilities.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/registry/XRegistryKey.hpp> +#include <uno/dispatcher.h> // declaration of generic uno interface +#include <uno/mapping.hxx> // mapping stuff +#include <cppuhelper/factory.hxx> +#include <rtl/tencinfo.h> + +#if OSL_DEBUG_LEVEL > 1 +#include <stdio.h> +#endif + +using namespace com::sun::star::datatransfer; +using namespace com::sun::star::datatransfer::clipboard; +using namespace com::sun::star::lang; +using namespace com::sun::star::uno; +using namespace com::sun::star::awt; +using namespace cppu; +using namespace osl; +using namespace x11; + +using ::rtl::OUString; + +X11Clipboard::X11Clipboard( SelectionManager& rManager, Atom aSelection ) : + ::cppu::WeakComponentImplHelper4< + ::com::sun::star::datatransfer::clipboard::XClipboardEx, + ::com::sun::star::datatransfer::clipboard::XClipboardNotifier, + ::com::sun::star::lang::XServiceInfo, + ::com::sun::star::lang::XInitialization + >( rManager.getMutex() ), + + m_rSelectionManager( rManager ), + m_xSelectionManager( & rManager ), + m_aSelection( aSelection ) +{ +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "creating instance of X11Clipboard (this=%p)\n", this ); +#endif + + if( m_aSelection != None ) + { + m_rSelectionManager.registerHandler( m_aSelection, *this ); + } + else + { + m_rSelectionManager.registerHandler( XA_PRIMARY, *this ); + m_rSelectionManager.registerHandler( m_rSelectionManager.getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("CLIPBOARD")) ), *this ); + } +} + +// ------------------------------------------------------------------------ + +X11Clipboard::~X11Clipboard() +{ + MutexGuard aGuard( *Mutex::getGlobalMutex() ); + +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "shutting down instance of X11Clipboard (this=%p, Selecttion=\"%s\")\n", this, OUStringToOString( m_rSelectionManager.getString( m_aSelection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); +#endif + if( m_aSelection != None ) + m_rSelectionManager.deregisterHandler( m_aSelection ); + else + { + m_rSelectionManager.deregisterHandler( XA_PRIMARY ); + m_rSelectionManager.deregisterHandler( m_rSelectionManager.getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("CLIPBOARD")) ) ); + } +} + + +// ------------------------------------------------------------------------ + +void X11Clipboard::fireChangedContentsEvent() +{ + ClearableMutexGuard aGuard( m_rSelectionManager.getMutex() ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "X11Clipboard::fireChangedContentsEvent for %s (%" SAL_PRI_SIZET "u listeners)\n", + OUStringToOString( m_rSelectionManager.getString( m_aSelection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), m_aListeners.size() ); +#endif + ::std::list< Reference< XClipboardListener > > listeners( m_aListeners ); + aGuard.clear(); + + ClipboardEvent aEvent( static_cast<OWeakObject*>(this), m_aContents); + while( listeners.begin() != listeners.end() ) + { + if( listeners.front().is() ) + listeners.front()->changedContents(aEvent); + listeners.pop_front(); + } +} + +// ------------------------------------------------------------------------ + +void X11Clipboard::clearContents() +{ + ClearableMutexGuard aGuard(m_rSelectionManager.getMutex()); + // protect against deletion during outside call + Reference< XClipboard > xThis( static_cast<XClipboard*>(this)); + // copy member references on stack so they can be called + // without having the mutex + Reference< XClipboardOwner > xOwner( m_aOwner ); + Reference< XTransferable > xTrans( m_aContents ); + // clear members + m_aOwner.clear(); + m_aContents.clear(); + + // release the mutex + aGuard.clear(); + + // inform previous owner of lost ownership + if ( xOwner.is() ) + xOwner->lostOwnership(xThis, m_aContents); +} + +// ------------------------------------------------------------------------ + +Reference< XTransferable > SAL_CALL X11Clipboard::getContents() + throw(RuntimeException) +{ + MutexGuard aGuard(m_rSelectionManager.getMutex()); + + if( ! m_aContents.is() ) + m_aContents = new X11Transferable( SelectionManager::get(), static_cast< OWeakObject* >(this), m_aSelection ); + return m_aContents; +} + +// ------------------------------------------------------------------------ + +void SAL_CALL X11Clipboard::setContents( + const Reference< XTransferable >& xTrans, + const Reference< XClipboardOwner >& xClipboardOwner ) + throw(RuntimeException) +{ + // remember old values for callbacks before setting the new ones. + ClearableMutexGuard aGuard(m_rSelectionManager.getMutex()); + + Reference< XClipboardOwner > oldOwner( m_aOwner ); + m_aOwner = xClipboardOwner; + + Reference< XTransferable > oldContents( m_aContents ); + m_aContents = xTrans; + + aGuard.clear(); + + // for now request ownership for both selections + if( m_aSelection != None ) + m_rSelectionManager.requestOwnership( m_aSelection ); + else + { + m_rSelectionManager.requestOwnership( XA_PRIMARY ); + m_rSelectionManager.requestOwnership( m_rSelectionManager.getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("CLIPBOARD")) ) ); + } + + // notify old owner on loss of ownership + if( oldOwner.is() ) + oldOwner->lostOwnership(static_cast < XClipboard * > (this), oldContents); + + // notify all listeners on content changes + fireChangedContentsEvent(); +} + +// ------------------------------------------------------------------------ + +OUString SAL_CALL X11Clipboard::getName() + throw(RuntimeException) +{ + return m_rSelectionManager.getString( m_aSelection ); +} + +// ------------------------------------------------------------------------ + +sal_Int8 SAL_CALL X11Clipboard::getRenderingCapabilities() + throw(RuntimeException) +{ + return RenderingCapabilities::Delayed; +} + + +// ------------------------------------------------------------------------ +void SAL_CALL X11Clipboard::addClipboardListener( const Reference< XClipboardListener >& listener ) + throw(RuntimeException) +{ + MutexGuard aGuard( m_rSelectionManager.getMutex() ); + m_aListeners.push_back( listener ); +} + +// ------------------------------------------------------------------------ + +void SAL_CALL X11Clipboard::removeClipboardListener( const Reference< XClipboardListener >& listener ) + throw(RuntimeException) +{ + MutexGuard aGuard( m_rSelectionManager.getMutex() ); + m_aListeners.remove( listener ); +} + + +// ------------------------------------------------------------------------ + +Reference< XTransferable > X11Clipboard::getTransferable() +{ + return getContents(); +} + +// ------------------------------------------------------------------------ + +void X11Clipboard::clearTransferable() +{ + clearContents(); +} + +// ------------------------------------------------------------------------ + +void X11Clipboard::fireContentsChanged() +{ + fireChangedContentsEvent(); +} + +// ------------------------------------------------------------------------ + +Reference< XInterface > X11Clipboard::getReference() throw() +{ + return Reference< XInterface >( static_cast< OWeakObject* >(this) ); +} + +// ------------------------------------------------------------------------ + +OUString SAL_CALL X11Clipboard::getImplementationName( ) + throw(RuntimeException) +{ + return OUString(RTL_CONSTASCII_USTRINGPARAM(X11_CLIPBOARD_IMPLEMENTATION_NAME)); +} + +// ------------------------------------------------------------------------ + +sal_Bool SAL_CALL X11Clipboard::supportsService( const OUString& ServiceName ) + throw(RuntimeException) +{ + Sequence < OUString > SupportedServicesNames = X11Clipboard_getSupportedServiceNames(); + + for ( sal_Int32 n = SupportedServicesNames.getLength(); n--; ) + if (SupportedServicesNames[n].compareTo(ServiceName) == 0) + return sal_True; + + return sal_False; +} + +// ------------------------------------------------------------------------ + +void SAL_CALL X11Clipboard::initialize( const Sequence< Any >& ) throw( ::com::sun::star::uno::Exception ) +{ +} + +// ------------------------------------------------------------------------ + +Sequence< OUString > SAL_CALL X11Clipboard::getSupportedServiceNames( ) + throw(RuntimeException) +{ + return X11Clipboard_getSupportedServiceNames(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/dtrans/X11_clipboard.hxx b/vcl/unx/generic/dtrans/X11_clipboard.hxx new file mode 100644 index 000000000000..73240d8715e6 --- /dev/null +++ b/vcl/unx/generic/dtrans/X11_clipboard.hxx @@ -0,0 +1,150 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _DTRANS_X11_CLIPBOARD_HXX_ +#define _DTRANS_X11_CLIPBOARD_HXX_ + +#include <X11_selection.hxx> + +#include <com/sun/star/datatransfer/clipboard/XClipboardEx.hpp> + +#include <com/sun/star/datatransfer/clipboard/XClipboardNotifier.hpp> +#include <cppuhelper/compbase4.hxx> + +// ------------------------------------------------------------------------ + +#define X11_CLIPBOARD_IMPLEMENTATION_NAME "com.sun.star.datatransfer.X11ClipboardSupport" + +namespace x11 { + + class X11Clipboard : + public ::cppu::WeakComponentImplHelper4 < + ::com::sun::star::datatransfer::clipboard::XClipboardEx, + ::com::sun::star::datatransfer::clipboard::XClipboardNotifier, + ::com::sun::star::lang::XServiceInfo, + ::com::sun::star::lang::XInitialization + >, + public SelectionAdaptor + { + com::sun::star::uno::Reference< ::com::sun::star::datatransfer::XTransferable > m_aContents; + com::sun::star::uno::Reference< ::com::sun::star::datatransfer::clipboard::XClipboardOwner > m_aOwner; + + SelectionManager& m_rSelectionManager; + com::sun::star::uno::Reference< ::com::sun::star::lang::XInitialization > m_xSelectionManager; + ::std::list< com::sun::star::uno::Reference< ::com::sun::star::datatransfer::clipboard::XClipboardListener > > m_aListeners; + Atom m_aSelection; + + protected: + + + friend class SelectionManager; + friend class X11_Transferable; + + void fireChangedContentsEvent(); + void clearContents(); + + public: + + X11Clipboard( SelectionManager& rManager, Atom aSelection ); + virtual ~X11Clipboard(); + + static X11Clipboard* get( const ::rtl::OUString& rDisplayName, Atom aSelection ); + + /* + * XInitialization + */ + virtual void SAL_CALL initialize( const Sequence< Any >& arguments ) throw( ::com::sun::star::uno::Exception ); + + /* + * XServiceInfo + */ + + virtual ::rtl::OUString SAL_CALL getImplementationName( ) + throw(RuntimeException); + + virtual sal_Bool SAL_CALL supportsService( const ::rtl::OUString& ServiceName ) + throw(RuntimeException); + + virtual Sequence< ::rtl::OUString > SAL_CALL getSupportedServiceNames( ) + throw(RuntimeException); + + /* + * XClipboard + */ + + virtual com::sun::star::uno::Reference< ::com::sun::star::datatransfer::XTransferable > SAL_CALL getContents() + throw(RuntimeException); + + virtual void SAL_CALL setContents( + const com::sun::star::uno::Reference< ::com::sun::star::datatransfer::XTransferable >& xTrans, + const com::sun::star::uno::Reference< ::com::sun::star::datatransfer::clipboard::XClipboardOwner >& xClipboardOwner ) + throw(RuntimeException); + + virtual ::rtl::OUString SAL_CALL getName() + throw(RuntimeException); + + /* + * XClipboardEx + */ + + virtual sal_Int8 SAL_CALL getRenderingCapabilities() + throw(RuntimeException); + + /* + * XClipboardNotifier + */ + virtual void SAL_CALL addClipboardListener( + const com::sun::star::uno::Reference< ::com::sun::star::datatransfer::clipboard::XClipboardListener >& listener ) + throw(RuntimeException); + + virtual void SAL_CALL removeClipboardListener( + const com::sun::star::uno::Reference< ::com::sun::star::datatransfer::clipboard::XClipboardListener >& listener ) + throw(RuntimeException); + + /* + * SelectionAdaptor + */ + virtual com::sun::star::uno::Reference< ::com::sun::star::datatransfer::XTransferable > getTransferable(); + virtual void clearTransferable(); + virtual void fireContentsChanged(); + virtual com::sun::star::uno::Reference< XInterface > getReference() throw(); + }; + +// ------------------------------------------------------------------------ + + Sequence< ::rtl::OUString > SAL_CALL X11Clipboard_getSupportedServiceNames(); + com::sun::star::uno::Reference< XInterface > SAL_CALL X11Clipboard_createInstance( + const com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > & xMultiServiceFactory); + +// ------------------------------------------------------------------------ + +} // namepspace + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/dtrans/X11_dndcontext.cxx b/vcl/unx/generic/dtrans/X11_dndcontext.cxx new file mode 100644 index 000000000000..988dce430a51 --- /dev/null +++ b/vcl/unx/generic/dtrans/X11_dndcontext.cxx @@ -0,0 +1,141 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <X11_dndcontext.hxx> +#include <X11_selection.hxx> + +using namespace cppu; +using namespace x11; + +/* + * DropTargetDropContext + */ + +DropTargetDropContext::DropTargetDropContext( + XLIB_Window aDropWindow, + XLIB_Time aTimestamp, + SelectionManager& rManager ) : + m_aDropWindow( aDropWindow ), + m_nTimestamp( aTimestamp ), + m_rManager( rManager ), + m_xManagerRef( static_cast< OWeakObject* >(&rManager) ) +{ +} + +DropTargetDropContext::~DropTargetDropContext() +{ +} + +void DropTargetDropContext::acceptDrop( sal_Int8 dragOperation ) throw() +{ + m_rManager.accept( dragOperation, m_aDropWindow, m_nTimestamp ); +} + +void DropTargetDropContext::rejectDrop() throw() +{ + m_rManager.reject( m_aDropWindow, m_nTimestamp ); +} + +void DropTargetDropContext::dropComplete( sal_Bool success ) throw() +{ + m_rManager.dropComplete( success, m_aDropWindow, m_nTimestamp ); +} + + +/* + * DropTargetDragContext + */ + +DropTargetDragContext::DropTargetDragContext( + XLIB_Window aDropWindow, + XLIB_Time aTimestamp, + SelectionManager& rManager ) : + m_aDropWindow( aDropWindow ), + m_nTimestamp( aTimestamp ), + m_rManager( rManager ), + m_xManagerRef( static_cast< OWeakObject* >(&rManager) ) +{ +} + +DropTargetDragContext::~DropTargetDragContext() +{ +} + +void DropTargetDragContext::acceptDrag( sal_Int8 dragOperation ) throw() +{ + m_rManager.accept( dragOperation, m_aDropWindow, m_nTimestamp ); +} + +void DropTargetDragContext::rejectDrag() throw() +{ + m_rManager.reject( m_aDropWindow, m_nTimestamp ); +} + +/* + * DragSourceContext + */ + +DragSourceContext::DragSourceContext( + XLIB_Window aDropWindow, + XLIB_Time aTimestamp, + SelectionManager& rManager ) : + m_aDropWindow( aDropWindow ), + m_nTimestamp( aTimestamp ), + m_rManager( rManager ), + m_xManagerRef( static_cast< OWeakObject* >(&rManager) ) +{ +} + +DragSourceContext::~DragSourceContext() +{ +} + +sal_Int32 DragSourceContext::getCurrentCursor() throw() +{ + return m_rManager.getCurrentCursor(); +} + +void DragSourceContext::setCursor( sal_Int32 cursorId ) throw() +{ + m_rManager.setCursor( cursorId, m_aDropWindow, m_nTimestamp ); +} + +void DragSourceContext::setImage( sal_Int32 imageId ) throw() +{ + m_rManager.setImage( imageId, m_aDropWindow, m_nTimestamp ); +} + +void DragSourceContext::transferablesFlavorsChanged() throw() +{ + m_rManager.transferablesFlavorsChanged(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/dtrans/X11_dndcontext.hxx b/vcl/unx/generic/dtrans/X11_dndcontext.hxx new file mode 100644 index 000000000000..bca708eb24c7 --- /dev/null +++ b/vcl/unx/generic/dtrans/X11_dndcontext.hxx @@ -0,0 +1,107 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _DTRANS_X11_DNDCONTEXT_HXX +#define _DTRANS_X11_DNDCONTEXT_HXX + +#include <com/sun/star/datatransfer/dnd/XDragSourceContext.hpp> +#include <com/sun/star/datatransfer/dnd/XDropTargetDropContext.hpp> +#include <com/sun/star/datatransfer/dnd/XDropTargetDragContext.hpp> +#include <cppuhelper/implbase1.hxx> + +#include "tools/prex.h" +#include <X11/Xlib.h> +#include "tools/postx.h" + +using namespace com::sun::star::uno; + +namespace x11 { + + class SelectionManager; + + class DropTargetDropContext : + public ::cppu::WeakImplHelper1< + ::com::sun::star::datatransfer::dnd::XDropTargetDropContext + > + { + XLIB_Window m_aDropWindow; + XLIB_Time m_nTimestamp; + SelectionManager& m_rManager; + com::sun::star::uno::Reference< XInterface > m_xManagerRef; + public: + DropTargetDropContext( XLIB_Window, XLIB_Time, SelectionManager& ); + virtual ~DropTargetDropContext(); + + // XDropTargetDropContext + virtual void SAL_CALL acceptDrop( sal_Int8 dragOperation ) throw(); + virtual void SAL_CALL rejectDrop() throw(); + virtual void SAL_CALL dropComplete( sal_Bool success ) throw(); + }; + + class DropTargetDragContext : + public ::cppu::WeakImplHelper1< + ::com::sun::star::datatransfer::dnd::XDropTargetDragContext + > + { + XLIB_Window m_aDropWindow; + XLIB_Time m_nTimestamp; + SelectionManager& m_rManager; + com::sun::star::uno::Reference< XInterface > m_xManagerRef; + public: + DropTargetDragContext( XLIB_Window, XLIB_Time, SelectionManager& ); + virtual ~DropTargetDragContext(); + + // XDropTargetDragContext + virtual void SAL_CALL acceptDrag( sal_Int8 dragOperation ) throw(); + virtual void SAL_CALL rejectDrag() throw(); + }; + + class DragSourceContext : + public ::cppu::WeakImplHelper1< + ::com::sun::star::datatransfer::dnd::XDragSourceContext + > + { + XLIB_Window m_aDropWindow; + XLIB_Time m_nTimestamp; + SelectionManager& m_rManager; + com::sun::star::uno::Reference< XInterface > m_xManagerRef; + public: + DragSourceContext( XLIB_Window, XLIB_Time, SelectionManager& ); + virtual ~DragSourceContext(); + + // XDragSourceContext + virtual sal_Int32 SAL_CALL getCurrentCursor() throw(); + virtual void SAL_CALL setCursor( sal_Int32 cursorId ) throw(); + virtual void SAL_CALL setImage( sal_Int32 imageId ) throw(); + virtual void SAL_CALL transferablesFlavorsChanged() throw(); + }; +} // namespace + +#endif // _DTRANS_X11_DNDCONTEXT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/dtrans/X11_droptarget.cxx b/vcl/unx/generic/dtrans/X11_droptarget.cxx new file mode 100644 index 000000000000..d72c5c4c7eeb --- /dev/null +++ b/vcl/unx/generic/dtrans/X11_droptarget.cxx @@ -0,0 +1,231 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <X11_selection.hxx> + +using namespace x11; +using namespace com::sun::star::lang; +using namespace com::sun::star::awt; +using namespace com::sun::star::datatransfer; +using namespace com::sun::star::datatransfer::dnd; + +using ::rtl::OUString; + +DropTarget::DropTarget() : + ::cppu::WeakComponentImplHelper3< + XDropTarget, + XInitialization, + XServiceInfo + >( m_aMutex ), + m_bActive( false ), + m_nDefaultActions( 0 ), + m_aTargetWindow( None ), + m_pSelectionManager( NULL ) +{ +} + +DropTarget::~DropTarget() +{ + if( m_pSelectionManager ) + m_pSelectionManager->deregisterDropTarget( m_aTargetWindow ); +} + +// -------------------------------------------------------------------------- + +void DropTarget::initialize( const Sequence< Any >& arguments ) throw( ::com::sun::star::uno::Exception ) +{ + if( arguments.getLength() > 1 ) + { + OUString aDisplayName; + Reference< XDisplayConnection > xConn; + arguments.getConstArray()[0] >>= xConn; + if( xConn.is() ) + { + Any aIdentifier; + aIdentifier >>= aDisplayName; + } + + m_pSelectionManager = &SelectionManager::get( aDisplayName ); + m_xSelectionManager = static_cast< XDragSource* >(m_pSelectionManager); + m_pSelectionManager->initialize( arguments ); + + if( m_pSelectionManager->getDisplay() ) // #136582# sanity check + { + sal_Size aWindow = None; + arguments.getConstArray()[1] >>= aWindow; + m_pSelectionManager->registerDropTarget( aWindow, this ); + m_aTargetWindow = aWindow; + m_bActive = true; + } + } +} + +// -------------------------------------------------------------------------- + +void DropTarget::addDropTargetListener( const Reference< XDropTargetListener >& xListener ) throw() +{ + ::osl::Guard< ::osl::Mutex > aGuard( m_aMutex ); + + m_aListeners.push_back( xListener ); +} + +// -------------------------------------------------------------------------- + +void DropTarget::removeDropTargetListener( const Reference< XDropTargetListener >& xListener ) throw() +{ + ::osl::Guard< ::osl::Mutex > aGuard( m_aMutex ); + + m_aListeners.remove( xListener ); +} + +// -------------------------------------------------------------------------- + +sal_Bool DropTarget::isActive() throw() +{ + return m_bActive; +} + +// -------------------------------------------------------------------------- + +void DropTarget::setActive( sal_Bool active ) throw() +{ + ::osl::Guard< ::osl::Mutex > aGuard( m_aMutex ); + + m_bActive = active; +} + +// -------------------------------------------------------------------------- + +sal_Int8 DropTarget::getDefaultActions() throw() +{ + return m_nDefaultActions; +} + +// -------------------------------------------------------------------------- + +void DropTarget::setDefaultActions( sal_Int8 actions ) throw() +{ + ::osl::Guard< ::osl::Mutex > aGuard( m_aMutex ); + + m_nDefaultActions = actions; +} + +// -------------------------------------------------------------------------- + +void DropTarget::drop( const DropTargetDropEvent& dtde ) throw() +{ + osl::ClearableGuard< ::osl::Mutex > aGuard( m_aMutex ); + std::list< Reference< XDropTargetListener > > aListeners( m_aListeners ); + aGuard.clear(); + + for( std::list< Reference< XDropTargetListener > >::iterator it = aListeners.begin(); it!= aListeners.end(); ++it ) + { + (*it)->drop( dtde ); + } +} + +// -------------------------------------------------------------------------- + +void DropTarget::dragEnter( const DropTargetDragEnterEvent& dtde ) throw() +{ + osl::ClearableGuard< ::osl::Mutex > aGuard( m_aMutex ); + std::list< Reference< XDropTargetListener > > aListeners( m_aListeners ); + aGuard.clear(); + + for( std::list< Reference< XDropTargetListener > >::iterator it = aListeners.begin(); it!= aListeners.end(); ++it ) + { + (*it)->dragEnter( dtde ); + } +} + +// -------------------------------------------------------------------------- + +void DropTarget::dragExit( const DropTargetEvent& dte ) throw() +{ + osl::ClearableGuard< ::osl::Mutex > aGuard( m_aMutex ); + std::list< Reference< XDropTargetListener > > aListeners( m_aListeners ); + aGuard.clear(); + + for( std::list< Reference< XDropTargetListener > >::iterator it = aListeners.begin(); it!= aListeners.end(); ++it ) + { + (*it)->dragExit( dte ); + } +} + +// -------------------------------------------------------------------------- + +void DropTarget::dragOver( const DropTargetDragEvent& dtde ) throw() +{ + osl::ClearableGuard< ::osl::Mutex > aGuard( m_aMutex ); + std::list< Reference< XDropTargetListener > > aListeners( m_aListeners ); + aGuard.clear(); + + for( std::list< Reference< XDropTargetListener > >::iterator it = aListeners.begin(); it!= aListeners.end(); ++it ) + { + (*it)->dragOver( dtde ); + } +} + +// -------------------------------------------------------------------------- + +/* + * XServiceInfo + */ + +// ------------------------------------------------------------------------ + +OUString DropTarget::getImplementationName() throw() +{ + return OUString(RTL_CONSTASCII_USTRINGPARAM(XDND_DROPTARGET_IMPLEMENTATION_NAME)); +} + +// ------------------------------------------------------------------------ + +sal_Bool DropTarget::supportsService( const OUString& ServiceName ) throw() +{ + Sequence < OUString > SupportedServicesNames = Xdnd_dropTarget_getSupportedServiceNames(); + + for ( sal_Int32 n = SupportedServicesNames.getLength(); n--; ) + if (SupportedServicesNames[n].compareTo(ServiceName) == 0) + return sal_True; + + return sal_False; +} + +// ------------------------------------------------------------------------ + +Sequence< OUString > DropTarget::getSupportedServiceNames() throw() +{ + return Xdnd_dropTarget_getSupportedServiceNames(); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/dtrans/X11_selection.cxx b/vcl/unx/generic/dtrans/X11_selection.cxx new file mode 100644 index 000000000000..facc134b3b56 --- /dev/null +++ b/vcl/unx/generic/dtrans/X11_selection.cxx @@ -0,0 +1,4193 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "unx/saldisp.hxx" +#include "unx/saldata.hxx" + +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <sys/time.h> + +#include "tools/prex.h" +#include <X11/Xatom.h> +#include <X11/keysym.h> +#include <X11/Xutil.h> +#include "tools/postx.h" +#if defined(LINUX) || defined(NETBSD) || defined (FREEBSD) || defined(OPENBSD) +#include <sys/poll.h> +#else +#include <poll.h> +#endif +#include <sal/alloca.h> +#include <sal/macros.h> + +#include <X11_selection.hxx> +#include <X11_clipboard.hxx> +#include <X11_transferable.hxx> +#include <X11_dndcontext.hxx> +#include <bmp.hxx> + +#include "vcl/svapp.hxx" + +// pointer bitmaps +#include <copydata_curs.h> +#include <copydata_mask.h> +#include <movedata_curs.h> +#include <movedata_mask.h> +#include <linkdata_curs.h> +#include <linkdata_mask.h> +#include <nodrop_curs.h> +#include <nodrop_mask.h> +#include <com/sun/star/datatransfer/dnd/DNDConstants.hpp> +#include <com/sun/star/awt/MouseEvent.hpp> +#include <com/sun/star/awt/MouseButton.hpp> +#include <rtl/tencinfo.h> +#include <osl/process.h> + +#include <comphelper/processfactory.hxx> +#include <osl/mutex.hxx> + +#define DRAG_EVENT_MASK ButtonPressMask |\ + ButtonReleaseMask |\ + PointerMotionMask |\ + EnterWindowMask |\ + LeaveWindowMask + +namespace { + +namespace css = com::sun::star; + +} + +using namespace com::sun::star::datatransfer; +using namespace com::sun::star::datatransfer::dnd; +using namespace com::sun::star::lang; +using namespace com::sun::star::awt; +using namespace com::sun::star::uno; +using namespace com::sun::star::frame; +using namespace cppu; + +using namespace x11; + +using ::rtl::OUString; +using ::rtl::OUStringHash; +using ::rtl::OStringToOUString; + +// stubs to satisfy solaris compiler's rather rigid linking warning +extern "C" +{ + static void call_SelectionManager_run( void * pMgr ) + { + SelectionManager::run( pMgr ); + } + + static void call_SelectionManager_runDragExecute( void * pMgr ) + { + SelectionManager::runDragExecute( pMgr ); + } +} + + +static const long nXdndProtocolRevision = 5; + +// mapping between mime types (or what the office thinks of mime types) +// and X convention types +struct NativeTypeEntry +{ + Atom nAtom; + const char* pType; // Mime encoding on our side + const char* pNativeType; // string corresponding to nAtom for the case of nAtom being uninitialized + int nFormat; // the corresponding format +}; + +// the convention for Xdnd is mime types as specified by the corresponding +// RFC's with the addition that text/plain without charset tag contains iso8859-1 +// sadly some applications (e.g. gtk) do not honor the mimetype only rule, +// so for compatibility add UTF8_STRING +static NativeTypeEntry aXdndConversionTab[] = +{ + { 0, "text/plain;charset=iso8859-1", "text/plain", 8 }, + { 0, "text/plain;charset=utf-8", "UTF8_STRING", 8 } +}; + +// for clipboard and primary selections there is only a convention for text +// that the encoding name of the text is taken as type in all capitalized letters +static NativeTypeEntry aNativeConversionTab[] = +{ + { 0, "text/plain;charset=utf-16", "ISO10646-1", 16 }, + { 0, "text/plain;charset=utf-8", "UTF8_STRING", 8 }, + { 0, "text/plain;charset=utf-8", "UTF-8", 8 }, + { 0, "text/plain;charset=utf-8", "text/plain;charset=UTF-8", 8 }, + // ISO encodings + { 0, "text/plain;charset=iso8859-2", "ISO8859-2", 8 }, + { 0, "text/plain;charset=iso8859-3", "ISO8859-3", 8 }, + { 0, "text/plain;charset=iso8859-4", "ISO8859-4", 8 }, + { 0, "text/plain;charset=iso8859-5", "ISO8859-5", 8 }, + { 0, "text/plain;charset=iso8859-6", "ISO8859-6", 8 }, + { 0, "text/plain;charset=iso8859-7", "ISO8859-7", 8 }, + { 0, "text/plain;charset=iso8859-8", "ISO8859-8", 8 }, + { 0, "text/plain;charset=iso8859-9", "ISO8859-9", 8 }, + { 0, "text/plain;charset=iso8859-10", "ISO8859-10", 8 }, + { 0, "text/plain;charset=iso8859-13", "ISO8859-13", 8 }, + { 0, "text/plain;charset=iso8859-14", "ISO8859-14", 8 }, + { 0, "text/plain;charset=iso8859-15", "ISO8859-15", 8 }, + // asian encodings + { 0, "text/plain;charset=jisx0201.1976-0", "JISX0201.1976-0", 8 }, + { 0, "text/plain;charset=jisx0208.1983-0", "JISX0208.1983-0", 8 }, + { 0, "text/plain;charset=jisx0208.1990-0", "JISX0208.1990-0", 8 }, + { 0, "text/plain;charset=jisx0212.1990-0", "JISX0212.1990-0", 8 }, + { 0, "text/plain;charset=gb2312.1980-0", "GB2312.1980-0", 8 }, + { 0, "text/plain;charset=ksc5601.1992-0", "KSC5601.1992-0", 8 }, + // eastern european encodings + { 0, "text/plain;charset=koi8-r", "KOI8-R", 8 }, + { 0, "text/plain;charset=koi8-u", "KOI8-U", 8 }, + // String (== iso8859-1) + { XA_STRING, "text/plain;charset=iso8859-1", "STRING", 8 }, + // special for compound text + { 0, "text/plain;charset=compound_text", "COMPOUND_TEXT", 8 }, + + // PIXMAP + { XA_PIXMAP, "image/bmp", "PIXMAP", 32 } +}; + +rtl_TextEncoding x11::getTextPlainEncoding( const OUString& rMimeType ) +{ + rtl_TextEncoding aEncoding = RTL_TEXTENCODING_DONTKNOW; + OUString aMimeType( rMimeType.toAsciiLowerCase() ); + sal_Int32 nIndex = 0; + if( aMimeType.getToken( 0, ';', nIndex ).equalsAsciiL( "text/plain" , 10 ) ) + { + if( aMimeType.getLength() == 10 ) // only "text/plain" + aEncoding = RTL_TEXTENCODING_ISO_8859_1; + else + { + while( nIndex != -1 ) + { + OUString aToken = aMimeType.getToken( 0, ';', nIndex ); + sal_Int32 nPos = 0; + if( aToken.getToken( 0, '=', nPos ).equalsAsciiL( "charset", 7 ) ) + { + OString aEncToken = OUStringToOString( aToken.getToken( 0, '=', nPos ), RTL_TEXTENCODING_ISO_8859_1 ); + aEncoding = rtl_getTextEncodingFromUnixCharset( aEncToken.getStr() ); + if( aEncoding == RTL_TEXTENCODING_DONTKNOW ) + { + if( aEncToken.equalsIgnoreAsciiCase( "utf-8" ) ) + aEncoding = RTL_TEXTENCODING_UTF8; + } + if( aEncoding != RTL_TEXTENCODING_DONTKNOW ) + break; + } + } + } + } +#if OSL_DEBUG_LEVEL > 1 + if( aEncoding == RTL_TEXTENCODING_DONTKNOW ) + fprintf( stderr, "getTextPlainEncoding( %s ) failed\n", OUStringToOString( rMimeType, RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); +#endif + return aEncoding; +} + +// ------------------------------------------------------------------------ + +::boost::unordered_map< OUString, SelectionManager*, OUStringHash >& SelectionManager::getInstances() +{ + static ::boost::unordered_map< OUString, SelectionManager*, OUStringHash > aInstances; + return aInstances; +} + +// ------------------------------------------------------------------------ + +SelectionManager::SelectionManager() : + m_nIncrementalThreshold( 15*1024 ), + m_pDisplay( NULL ), + m_aThread( NULL ), + m_aDragExecuteThread( NULL ), + m_aWindow( None ), + m_nSelectionTimeout( 0 ), + m_nSelectionTimestamp( CurrentTime ), + m_bDropEnterSent( true ), + m_aCurrentDropWindow( None ), + m_nDropTime( None ), + m_nLastDropAction( 0 ), + m_nLastX( 0 ), + m_nLastY( 0 ), + m_nDropTimestamp( 0 ), + m_bDropWaitingForCompletion( false ), + m_aDropWindow( None ), + m_aDropProxy( None ), + m_aDragSourceWindow( None ), + m_nLastDragX( 0 ), + m_nLastDragY( 0 ), + m_nNoPosX( 0 ), + m_nNoPosY( 0 ), + m_nNoPosWidth( 0 ), + m_nNoPosHeight( 0 ), + m_nDragButton( 0 ), + m_nUserDragAction( 0 ), + m_nTargetAcceptAction( 0 ), + m_nSourceActions( 0 ), + m_bLastDropAccepted( false ), + m_bDropSuccess( false ), + m_bDropSent( false ), + m_bWaitingForPrimaryConversion( false ), + m_nDragTimestamp( None ), + m_aMoveCursor( None ), + m_aCopyCursor( None ), + m_aLinkCursor( None ), + m_aNoneCursor( None ), + m_aCurrentCursor( None ), + m_nCurrentProtocolVersion( nXdndProtocolRevision ), + m_nCLIPBOARDAtom( None ), + m_nTARGETSAtom( None ), + m_nTIMESTAMPAtom( None ), + m_nTEXTAtom( None ), + m_nINCRAtom( None ), + m_nCOMPOUNDAtom( None ), + m_nMULTIPLEAtom( None ), + m_nUTF16Atom( None ), + m_nImageBmpAtom( None ), + m_nXdndAware( None ), + m_nXdndEnter( None ), + m_nXdndLeave( None ), + m_nXdndPosition( None ), + m_nXdndStatus( None ), + m_nXdndDrop( None ), + m_nXdndFinished( None ), + m_nXdndSelection( None ), + m_nXdndTypeList( None ), + m_nXdndProxy( None ), + m_nXdndActionCopy( None ), + m_nXdndActionMove( None ), + m_nXdndActionLink( None ), + m_nXdndActionAsk( None ), + m_nXdndActionPrivate( None ), + m_bShutDown( false ) +{ + m_aDropEnterEvent.data.l[0] = None; + m_aDragRunning.reset(); +} + +XLIB_Cursor SelectionManager::createCursor( const unsigned char* pPointerData, const unsigned char* pMaskData, int width, int height, int hotX, int hotY ) +{ + Pixmap aPointer; + Pixmap aMask; + XColor aBlack, aWhite; + + aBlack.pixel = BlackPixel( m_pDisplay, 0 ); + aBlack.red = aBlack.green = aBlack.blue = 0; + aBlack.flags = DoRed | DoGreen | DoBlue; + + aWhite.pixel = WhitePixel( m_pDisplay, 0 ); + aWhite.red = aWhite.green = aWhite.blue = 0xffff; + aWhite.flags = DoRed | DoGreen | DoBlue; + + aPointer = + XCreateBitmapFromData( m_pDisplay, + m_aWindow, + reinterpret_cast<const char*>(pPointerData), + width, + height ); + aMask + = XCreateBitmapFromData( m_pDisplay, + m_aWindow, + reinterpret_cast<const char*>(pMaskData), + width, + height ); + XLIB_Cursor aCursor = + XCreatePixmapCursor( m_pDisplay, aPointer, aMask, + &aBlack, &aWhite, + hotX, + hotY ); + XFreePixmap( m_pDisplay, aPointer ); + XFreePixmap( m_pDisplay, aMask ); + + return aCursor; +} + +void SelectionManager::initialize( const Sequence< Any >& arguments ) throw (::com::sun::star::uno::Exception) +{ + osl::MutexGuard aGuard(m_aMutex); + + if( ! m_xDisplayConnection.is() ) + { + /* + * first argument must be a ::com::sun::star::awt::XDisplayConnection + * from this we will get the XEvents of the vcl event loop by + * registering us as XEventHandler on it. + * + * implementor's note: + * FIXME: + * finally the clipboard and XDND service is back in the module it belongs + * now cleanup and sharing of resources with the normal vcl event loop + * needs to be added. The display used whould be that of the normal event loop + * and synchronization should be done via the SolarMutex. + */ + if( arguments.getLength() > 0 ) + arguments.getConstArray()[0] >>= m_xDisplayConnection; + if( ! m_xDisplayConnection.is() ) + { + } + else + m_xDisplayConnection->addEventHandler( Any(), this, ~0 ); + } + + if( !m_xBitmapConverter.is() ) + { + if( arguments.getLength() > 2 ) + arguments.getConstArray()[2] >>= m_xBitmapConverter; + } + + OUString aParam; + if( ! m_pDisplay ) + { + OUString aUDisplay; + if( m_xDisplayConnection.is() ) + { + Any aIdentifier; + aIdentifier = m_xDisplayConnection->getIdentifier(); + aIdentifier >>= aUDisplay; + } + + OString aDisplayName( OUStringToOString( aUDisplay, RTL_TEXTENCODING_ISO_8859_1 ) ); + + m_pDisplay = XOpenDisplay( aDisplayName.getLength() ? aDisplayName.getStr() : NULL ); + + if( m_pDisplay ) + { +#ifdef SYNCHRONIZE + XSynchronize( m_pDisplay, True ); +#endif + // clipboard selection + m_nCLIPBOARDAtom = getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("CLIPBOARD")) ); + + // special targets + m_nTARGETSAtom = getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("TARGETS")) ); + m_nTIMESTAMPAtom = getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("TIMESTAMP")) ); + m_nTEXTAtom = getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("TEXT")) ); + m_nINCRAtom = getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("INCR")) ); + m_nCOMPOUNDAtom = getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("COMPOUND_TEXT")) ); + m_nMULTIPLEAtom = getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("MULTIPLE")) ); + m_nUTF16Atom = getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("ISO10646-1")) ); + m_nImageBmpAtom = getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("image/bmp")) ); + + // Atoms for Xdnd protocol + m_nXdndAware = getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndAware")) ); + m_nXdndEnter = getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndEnter")) ); + m_nXdndLeave = getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndLeave")) ); + m_nXdndPosition = getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndPosition")) ); + m_nXdndStatus = getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndStatus")) ); + m_nXdndDrop = getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndDrop")) ); + m_nXdndFinished = getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndFinished")) ); + m_nXdndSelection = getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndSelection")) ); + m_nXdndTypeList = getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndTypeList")) ); + m_nXdndProxy = getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndProxy")) ); + m_nXdndActionCopy = getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndActionCopy")) ); + m_nXdndActionMove = getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndActionMove")) ); + m_nXdndActionLink = getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndActionLink")) ); + m_nXdndActionAsk = getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndActionAsk")) ); + m_nXdndActionPrivate= getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("XdndActionPrivate")) ); + + // initialize map with member none + m_aAtomToString[ 0 ]= OUString(RTL_CONSTASCII_USTRINGPARAM("None")); + m_aAtomToString[ XA_PRIMARY ] = OUString(RTL_CONSTASCII_USTRINGPARAM("PRIMARY")); + + // create a (invisible) message window + m_aWindow = XCreateSimpleWindow( m_pDisplay, DefaultRootWindow( m_pDisplay ), + 10, 10, 10, 10, 0, 0, 1 ); + + // initialize threshold for incremetal transfers + // ICCCM says it should be smaller that the max request size + // which in turn is guaranteed to be at least 16k bytes + m_nIncrementalThreshold = XMaxRequestSize( m_pDisplay ) - 1024; + + if( m_aWindow ) + { + // initialize default cursors + m_aMoveCursor = createCursor( movedata_curs_bits, + movedata_mask_bits, + movedata_curs_width, + movedata_curs_height, + movedata_curs_x_hot, + movedata_curs_y_hot ); + m_aCopyCursor = createCursor( copydata_curs_bits, + copydata_mask_bits, + copydata_curs_width, + copydata_curs_height, + copydata_curs_x_hot, + copydata_curs_y_hot ); + m_aLinkCursor = createCursor( linkdata_curs_bits, + linkdata_mask_bits, + linkdata_curs_width, + linkdata_curs_height, + linkdata_curs_x_hot, + linkdata_curs_y_hot ); + m_aNoneCursor = createCursor( nodrop_curs_bits, + nodrop_mask_bits, + nodrop_curs_width, + nodrop_curs_height, + nodrop_curs_x_hot, + nodrop_curs_y_hot ); + + + + + // just interested in SelectionClear/Notify/Request and PropertyChange + XSelectInput( m_pDisplay, m_aWindow, PropertyChangeMask ); + // create the transferable for Drag operations + m_xDropTransferable = new X11Transferable( *this, static_cast< OWeakObject* >(this), m_nXdndSelection ); + registerHandler( m_nXdndSelection, *this ); + + m_aThread = osl_createSuspendedThread( call_SelectionManager_run, this ); + if( m_aThread ) + osl_resumeThread( m_aThread ); +#if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "SelectionManager::initialize: creation of dispatch thread failed !\n" ); +#endif + } + } + } +} + +// ------------------------------------------------------------------------ + +SelectionManager::~SelectionManager() +{ +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "SelectionManager::~SelectionManager (%s)\n", m_pDisplay ? DisplayString(m_pDisplay) : "no display" ); +#endif + { + osl::MutexGuard aGuard( *osl::Mutex::getGlobalMutex() ); + + ::boost::unordered_map< OUString, SelectionManager*, OUStringHash >::iterator it; + for( it = getInstances().begin(); it != getInstances().end(); ++it ) + if( it->second == this ) + { + getInstances().erase( it ); + break; + } + } + + if( m_aThread ) + { + osl_terminateThread( m_aThread ); + osl_joinWithThread( m_aThread ); + osl_destroyThread( m_aThread ); + } + + if( m_aDragExecuteThread ) + { + osl_terminateThread( m_aDragExecuteThread ); + osl_joinWithThread( m_aDragExecuteThread ); + m_aDragExecuteThread = NULL; + // thread handle is freed in dragDoDispatch() + } + + osl::MutexGuard aGuard(m_aMutex); + +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "shutting down SelectionManager\n" ); +#endif + + if( m_xDisplayConnection.is() ) + { + m_xDisplayConnection->removeEventHandler( Any(), this ); + m_xDisplayConnection.clear(); + } + + if( m_pDisplay ) + { + deregisterHandler( m_nXdndSelection ); + // destroy message window + if( m_aWindow ) + XDestroyWindow( m_pDisplay, m_aWindow ); + // release cursors + if (m_aMoveCursor != None) + XFreeCursor(m_pDisplay, m_aMoveCursor); + if (m_aCopyCursor != None) + XFreeCursor(m_pDisplay, m_aCopyCursor); + if (m_aLinkCursor != None) + XFreeCursor(m_pDisplay, m_aLinkCursor); + if (m_aNoneCursor != None) + XFreeCursor(m_pDisplay, m_aNoneCursor); + + // paranoia setting, the drag thread should have + // done that already + XUngrabPointer( m_pDisplay, CurrentTime ); + XUngrabKeyboard( m_pDisplay, CurrentTime ); + + XCloseDisplay( m_pDisplay ); + } +} + +// ------------------------------------------------------------------------ + +SelectionAdaptor* SelectionManager::getAdaptor( Atom selection ) +{ + ::boost::unordered_map< Atom, Selection* >::iterator it = + m_aSelections.find( selection ); + return it != m_aSelections.end() ? it->second->m_pAdaptor : NULL; +} + +// ------------------------------------------------------------------------ + +OUString SelectionManager::convertFromCompound( const char* pText, int nLen ) +{ + osl::MutexGuard aGuard( m_aMutex ); + OUString aRet; + if( nLen < 0 ) + nLen = strlen( pText ); + + char** pTextList = NULL; + int nTexts = 0; + + XTextProperty aProp; + aProp.value = (unsigned char*)pText; + aProp.encoding = m_nCOMPOUNDAtom; + aProp.format = 8; + aProp.nitems = nLen; + XmbTextPropertyToTextList( m_pDisplay, + &aProp, + &pTextList, + &nTexts ); + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + for( int i = 0; i < nTexts; i++ ) + aRet += OStringToOUString( pTextList[i], aEncoding ); + + if( pTextList ) + XFreeStringList( pTextList ); + + return aRet; +} + +// ------------------------------------------------------------------------ + +OString SelectionManager::convertToCompound( const OUString& rText ) +{ + osl::MutexGuard aGuard( m_aMutex ); + XTextProperty aProp; + aProp.value = NULL; + aProp.encoding = XA_STRING; + aProp.format = 8; + aProp.nitems = 0; + + OString aRet( rText.getStr(), rText.getLength(), osl_getThreadTextEncoding() ); + char* pT = const_cast<char*>(aRet.getStr()); + + XmbTextListToTextProperty( m_pDisplay, + &pT, + 1, + XCompoundTextStyle, + &aProp ); + if( aProp.value ) + { + aRet = (char*)aProp.value; + XFree( aProp.value ); +#ifdef SOLARIS + /* + * for currently unknown reasons XmbTextListToTextProperty on Solaris returns + * no data in ISO8859-n encodings (at least for n = 1, 15) + * in these encodings the directly converted text does the + * trick, also. + */ + if( ! aRet.getLength() && rText.getLength() ) + aRet = OUStringToOString( rText, osl_getThreadTextEncoding() ); +#endif + } + else + aRet = OString(); + + return aRet; +} + +// ------------------------------------------------------------------------ + +bool SelectionManager::convertData( + const css::uno::Reference< XTransferable >& xTransferable, + Atom nType, + Atom nSelection, + int& rFormat, + Sequence< sal_Int8 >& rData ) +{ + bool bSuccess = false; + + if( ! xTransferable.is() ) + return bSuccess; + + try + { + + DataFlavor aFlavor; + aFlavor.MimeType = convertTypeFromNative( nType, nSelection, rFormat ); + + sal_Int32 nIndex = 0; + if( aFlavor.MimeType.getToken( 0, ';', nIndex ).compareToAscii( "text/plain" ) == 0 ) + { + if( aFlavor.MimeType.getToken( 0, ';', nIndex ).compareToAscii( "charset=utf-16" ) == 0 ) + aFlavor.DataType = getCppuType( (OUString *) 0 ); + else + aFlavor.DataType = getCppuType( (Sequence< sal_Int8 >*)0 ); + } + else + aFlavor.DataType = getCppuType( (Sequence< sal_Int8 >*)0 ); + + if( xTransferable->isDataFlavorSupported( aFlavor ) ) + { + Any aValue( xTransferable->getTransferData( aFlavor ) ); + if( aValue.getValueTypeClass() == TypeClass_STRING ) + { + OUString aString; + aValue >>= aString; + rData = Sequence< sal_Int8 >( (sal_Int8*)aString.getStr(), aString.getLength() * sizeof( sal_Unicode ) ); + bSuccess = true; + } + else if( aValue.getValueType() == getCppuType( (Sequence< sal_Int8 >*)0 ) ) + { + aValue >>= rData; + bSuccess = true; + } + } + else if( aFlavor.MimeType.compareToAscii( "text/plain", 10 ) == 0 ) + { + rtl_TextEncoding aEncoding = RTL_TEXTENCODING_DONTKNOW; + bool bCompoundText = false; + if( nType == m_nCOMPOUNDAtom ) + bCompoundText = true; + else + aEncoding = getTextPlainEncoding( aFlavor.MimeType ); + if( aEncoding != RTL_TEXTENCODING_DONTKNOW || bCompoundText ) + { + aFlavor.MimeType = OUString(RTL_CONSTASCII_USTRINGPARAM("text/plain;charset=utf-16")); + aFlavor.DataType = getCppuType( (OUString *) 0 ); + if( xTransferable->isDataFlavorSupported( aFlavor ) ) + { + Any aValue( xTransferable->getTransferData( aFlavor ) ); + OUString aString; + aValue >>= aString; + OString aByteString( bCompoundText ? convertToCompound( aString ) : OUStringToOString( aString, aEncoding ) ); + rData = Sequence< sal_Int8 >( (sal_Int8*)aByteString.getStr(), aByteString.getLength() * sizeof( sal_Char ) ); + bSuccess = true; + } + } + } + } + // various exceptions possible ... which all lead to a failed conversion + // so simplify here to a catch all + catch(...) + { + } + + return bSuccess; +} + +// ------------------------------------------------------------------------ + +SelectionManager& SelectionManager::get( const OUString& rDisplayName ) +{ + osl::MutexGuard aGuard( *osl::Mutex::getGlobalMutex() ); + + OUString aDisplayName( rDisplayName ); + if( ! aDisplayName.getLength() ) + aDisplayName = OStringToOUString( getenv( "DISPLAY" ), RTL_TEXTENCODING_ISO_8859_1 ); + SelectionManager* pInstance = NULL; + + ::boost::unordered_map< OUString, SelectionManager*, OUStringHash >::iterator it = getInstances().find( aDisplayName ); + if( it != getInstances().end() ) + pInstance = it->second; + else pInstance = getInstances()[ aDisplayName ] = new SelectionManager(); + + return *pInstance; +} + +// ------------------------------------------------------------------------ + +const OUString& SelectionManager::getString( Atom aAtom ) +{ + osl::MutexGuard aGuard(m_aMutex); + + ::boost::unordered_map< Atom, OUString >::const_iterator it; + if( ( it = m_aAtomToString.find( aAtom ) ) == m_aAtomToString.end() ) + { + static OUString aEmpty; + char* pAtom = m_pDisplay ? XGetAtomName( m_pDisplay, aAtom ) : NULL; + if( ! pAtom ) + return aEmpty; + OUString aString( OStringToOUString( pAtom, RTL_TEXTENCODING_ISO_8859_1 ) ); + XFree( pAtom ); + m_aStringToAtom[ aString ] = aAtom; + m_aAtomToString[ aAtom ] = aString; + } + return m_aAtomToString[ aAtom ]; +} + +// ------------------------------------------------------------------------ + +Atom SelectionManager::getAtom( const OUString& rString ) +{ + osl::MutexGuard aGuard(m_aMutex); + + ::boost::unordered_map< OUString, Atom, OUStringHash >::const_iterator it; + if( ( it = m_aStringToAtom.find( rString ) ) == m_aStringToAtom.end() ) + { + static Atom nNoDisplayAtoms = 1; + Atom aAtom = m_pDisplay ? XInternAtom( m_pDisplay, OUStringToOString( rString, RTL_TEXTENCODING_ISO_8859_1 ), False ) : nNoDisplayAtoms++; + m_aStringToAtom[ rString ] = aAtom; + m_aAtomToString[ aAtom ] = rString; + } + return m_aStringToAtom[ rString ]; +} + +// ------------------------------------------------------------------------ + +bool SelectionManager::requestOwnership( Atom selection ) +{ + bool bSuccess = false; + if( m_pDisplay && m_aWindow ) + { + osl::MutexGuard aGuard(m_aMutex); + + SelectionAdaptor* pAdaptor = getAdaptor( selection ); + if( pAdaptor ) + { + XSetSelectionOwner( m_pDisplay, selection, m_aWindow, CurrentTime ); + if( XGetSelectionOwner( m_pDisplay, selection ) == m_aWindow ) + bSuccess = true; +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "%s ownership for selection %s\n", + bSuccess ? "acquired" : "failed to acquire", + OUStringToOString( getString( selection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); +#endif + Selection* pSel = m_aSelections[ selection ]; + pSel->m_bOwner = bSuccess; + delete pSel->m_pPixmap; + pSel->m_pPixmap = NULL; + pSel->m_nOrigTimestamp = m_nSelectionTimestamp; + } +#if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "no adaptor for selection %s\n", + OUStringToOString( getString( selection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); + + if( pAdaptor->getTransferable().is() ) + { + Sequence< DataFlavor > aTypes = pAdaptor->getTransferable()->getTransferDataFlavors(); + for( int i = 0; i < aTypes.getLength(); i++ ) + { + fprintf( stderr, " %s\n", OUStringToOString( aTypes.getConstArray()[i].MimeType, RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); + } + } +#endif + } + return bSuccess; +} + +// ------------------------------------------------------------------------ + +void SelectionManager::convertTypeToNative( const OUString& rType, Atom selection, int& rFormat, ::std::list< Atom >& rConversions, bool bPushFront ) +{ + NativeTypeEntry* pTab = selection == m_nXdndSelection ? aXdndConversionTab : aNativeConversionTab; + int nTabEntries = selection == m_nXdndSelection ? SAL_N_ELEMENTS(aXdndConversionTab) : SAL_N_ELEMENTS(aNativeConversionTab); + + OString aType( OUStringToOString( rType, RTL_TEXTENCODING_ISO_8859_1 ) ); + rFormat = 0; + for( int i = 0; i < nTabEntries; i++ ) + { + if( aType.equalsIgnoreAsciiCase( pTab[i].pType ) ) + { + if( ! pTab[i].nAtom ) + pTab[i].nAtom = getAtom( OStringToOUString( pTab[i].pNativeType, RTL_TEXTENCODING_ISO_8859_1 ) ); + rFormat = pTab[i].nFormat; + if( bPushFront ) + rConversions.push_front( pTab[i].nAtom ); + else + rConversions.push_back( pTab[i].nAtom ); + if( pTab[i].nFormat == XA_PIXMAP ) + { + if( bPushFront ) + { + rConversions.push_front( XA_VISUALID ); + rConversions.push_front( XA_COLORMAP ); + } + else + { + rConversions.push_back( XA_VISUALID ); + rConversions.push_back( XA_COLORMAP ); + } + } + } + } + if( ! rFormat ) + rFormat = 8; // byte buffer + if( bPushFront ) + rConversions.push_front( getAtom( rType ) ); + else + rConversions.push_back( getAtom( rType ) ); +}; + +// ------------------------------------------------------------------------ + +void SelectionManager::getNativeTypeList( const Sequence< DataFlavor >& rTypes, std::list< Atom >& rOutTypeList, Atom targetselection ) +{ + rOutTypeList.clear(); + + int nFormat; + int nFlavors = rTypes.getLength(); + const DataFlavor* pFlavors = rTypes.getConstArray(); + bool bHaveText = false; + for( int i = 0; i < nFlavors; i++ ) + { + if( pFlavors[i].MimeType.compareToAscii( "text/plain", 10 ) == 0) + bHaveText = true; + else + convertTypeToNative( pFlavors[i].MimeType, targetselection, nFormat, rOutTypeList ); + } + if( bHaveText ) + { + if( targetselection != m_nXdndSelection ) + { + // only mimetypes should go into Xdnd type list + rOutTypeList.push_front( XA_STRING ); + rOutTypeList.push_front( m_nCOMPOUNDAtom ); + } + convertTypeToNative( OUString(RTL_CONSTASCII_USTRINGPARAM("text/plain;charset=utf-8")), targetselection, nFormat, rOutTypeList, true ); + } + if( targetselection != m_nXdndSelection ) + rOutTypeList.push_back( m_nMULTIPLEAtom ); +} + +// ------------------------------------------------------------------------ + +OUString SelectionManager::convertTypeFromNative( Atom nType, Atom selection, int& rFormat ) +{ + NativeTypeEntry* pTab = (selection == m_nXdndSelection) ? aXdndConversionTab : aNativeConversionTab; + int nTabEntries = (selection == m_nXdndSelection) ? SAL_N_ELEMENTS(aXdndConversionTab) : SAL_N_ELEMENTS(aNativeConversionTab); + + for( int i = 0; i < nTabEntries; i++ ) + { + if( ! pTab[i].nAtom ) + pTab[i].nAtom = getAtom( OStringToOUString( pTab[i].pNativeType, RTL_TEXTENCODING_ISO_8859_1 ) ); + if( nType == pTab[i].nAtom ) + { + rFormat = pTab[i].nFormat; + return OStringToOUString( pTab[i].pType, RTL_TEXTENCODING_ISO_8859_1 ); + } + } + rFormat = 8; + return getString( nType ); +} + +// ------------------------------------------------------------------------ + +bool SelectionManager::getPasteData( Atom selection, Atom type, Sequence< sal_Int8 >& rData ) +{ + osl::ResettableMutexGuard aGuard(m_aMutex); + ::boost::unordered_map< Atom, Selection* >::iterator it; + bool bSuccess = false; + +#if OSL_DEBUG_LEVEL > 1 + OUString aSelection( getString( selection ) ); + OUString aType( getString( type ) ); + fprintf( stderr, "getPasteData( %s, native: %s )\n", + OUStringToOString( aSelection, RTL_TEXTENCODING_ISO_8859_1 ).getStr(), + OUStringToOString( aType, RTL_TEXTENCODING_ISO_8859_1 ).getStr() + ); +#endif + + if( ! m_pDisplay ) + return false; + + it = m_aSelections.find( selection ); + if( it == m_aSelections.end() ) + return false; + + XLIB_Window aSelectionOwner = XGetSelectionOwner( m_pDisplay, selection ); + if( aSelectionOwner == None ) + return false; + if( aSelectionOwner == m_aWindow ) + { + // probably bad timing led us here +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "Innere Nabelschau\n" ); +#endif + return false; + } + + // ICCCM recommends to destroy property before convert request unless + // parameters are transported; we do only in case of MULTIPLE, + // so destroy property unless target is MULTIPLE + if( type != m_nMULTIPLEAtom ) + XDeleteProperty( m_pDisplay, m_aWindow, selection ); + + XConvertSelection( m_pDisplay, selection, type, selection, m_aWindow, selection == m_nXdndSelection ? m_nDropTime : CurrentTime ); + it->second->m_eState = Selection::WaitingForResponse; + it->second->m_aRequestedType = type; + it->second->m_aData = Sequence< sal_Int8 >(); + it->second->m_aDataArrived.reset(); + // really start the request; if we don't flush the + // queue the request won't leave it because there are no more + // X calls after this until the data arrived or timeout + XFlush( m_pDisplay ); + + // do a reschedule + struct timeval tv_last, tv_current; + gettimeofday( &tv_last, NULL ); + tv_current = tv_last; + + XEvent aEvent; + do + { + bool bAdjustTime = false; + { + bool bHandle = false; + + if( XCheckTypedEvent( m_pDisplay, + PropertyNotify, + &aEvent + ) ) + { + bHandle = true; + if( aEvent.xproperty.window == m_aWindow + && aEvent.xproperty.atom == selection ) + bAdjustTime = true; + } + else + if( XCheckTypedEvent( m_pDisplay, + SelectionClear, + &aEvent + ) ) + { + bHandle = true; + } + else + if( XCheckTypedEvent( m_pDisplay, + SelectionRequest, + &aEvent + ) ) + bHandle = true; + else + if( XCheckTypedEvent( m_pDisplay, + SelectionNotify, + &aEvent + ) ) + { + bHandle = true; + if( aEvent.xselection.selection == selection + && ( aEvent.xselection.requestor == m_aWindow || + aEvent.xselection.requestor == m_aCurrentDropWindow ) + ) + bAdjustTime = true; + } + else + { + TimeValue aTVal; + aTVal.Seconds = 0; + aTVal.Nanosec = 100000000; + aGuard.clear(); + osl_waitThread( &aTVal ); + aGuard.reset(); + } + if( bHandle ) + { + aGuard.clear(); + handleXEvent( aEvent ); + aGuard.reset(); + } + } + gettimeofday( &tv_current, NULL ); + if( bAdjustTime ) + tv_last = tv_current; + } while( ! it->second->m_aDataArrived.check() && (tv_current.tv_sec - tv_last.tv_sec) < getSelectionTimeout() ); + +#if OSL_DEBUG_LEVEL > 1 + if( (tv_current.tv_sec - tv_last.tv_sec) > getSelectionTimeout() ) + fprintf( stderr, "timed out\n" ); +#endif + if( it->second->m_aDataArrived.check() && + it->second->m_aData.getLength() ) + { + rData = it->second->m_aData; + bSuccess = true; + } +#if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "conversion unsuccessfull\n" ); +#endif + return bSuccess; +} + +// ------------------------------------------------------------------------ + +bool SelectionManager::getPasteData( Atom selection, const ::rtl::OUString& rType, Sequence< sal_Int8 >& rData ) +{ + bool bSuccess = false; + + ::boost::unordered_map< Atom, Selection* >::iterator it; + { + osl::MutexGuard aGuard(m_aMutex); + + it = m_aSelections.find( selection ); + if( it == m_aSelections.end() ) + return false; + } + + if( it->second->m_aTypes.getLength() == 0 ) + { + Sequence< DataFlavor > aFlavors; + getPasteDataTypes( selection, aFlavors ); + if( it->second->m_aTypes.getLength() == 0 ) + return false; + } + + const Sequence< DataFlavor >& rTypes( it->second->m_aTypes ); + const std::vector< Atom >& rNativeTypes( it->second->m_aNativeTypes ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "getPasteData( \"%s\", \"%s\" )\n", + OUStringToOString( getString( selection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), + OUStringToOString( rType, RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); +#endif + + if( rType.equalsAsciiL( "text/plain;charset=utf-16", 25 ) ) + { + // lets see if we have UTF16 else try to find something convertible + if( it->second->m_aTypes.getLength() && ! it->second->m_bHaveUTF16 ) + { + Sequence< sal_Int8 > aData; + if( it->second->m_aUTF8Type != None && + getPasteData( selection, + it->second->m_aUTF8Type, + aData ) + ) + { + OUString aRet( (const sal_Char*)aData.getConstArray(), aData.getLength(), RTL_TEXTENCODING_UTF8 ); + rData = Sequence< sal_Int8 >( (sal_Int8*)aRet.getStr(), (aRet.getLength()+1)*sizeof( sal_Unicode ) ); + bSuccess = true; + } + else if( it->second->m_bHaveCompound && + getPasteData( selection, + m_nCOMPOUNDAtom, + aData ) + ) + { + OUString aRet( convertFromCompound( (const char*)aData.getConstArray(), aData.getLength() ) ); + rData = Sequence< sal_Int8 >( (sal_Int8*)aRet.getStr(), (aRet.getLength()+1)*sizeof( sal_Unicode ) ); + bSuccess = true; + } + else + { + for( int i = 0; i < rTypes.getLength(); i++ ) + { + rtl_TextEncoding aEncoding = getTextPlainEncoding( rTypes.getConstArray()[i].MimeType ); + if( aEncoding != RTL_TEXTENCODING_DONTKNOW && + aEncoding != RTL_TEXTENCODING_UNICODE && + getPasteData( selection, + rNativeTypes[i], + aData ) + ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "using \"%s\" instead of \"%s\"\n", + OUStringToOString( rTypes.getConstArray()[i].MimeType, RTL_TEXTENCODING_ISO_8859_1 ).getStr(), + OUStringToOString( rType, RTL_TEXTENCODING_ISO_8859_1 ).getStr() + ); +#endif + OString aConvert( (sal_Char*)aData.getConstArray(), aData.getLength() ); + OUString aUTF( OStringToOUString( aConvert, aEncoding ) ); + rData = Sequence< sal_Int8 >( (sal_Int8*)aUTF.getStr(), (aUTF.getLength()+1)*sizeof( sal_Unicode ) ); + bSuccess = true; + break; + } + } + } + } + } + else if( rType.equalsAsciiL( "image/bmp", 9 ) ) + { + // #i83376# try if someone has the data in image/bmp already before + // doing the PIXMAP stuff (e.g. the gimp has this) + bSuccess = getPasteData( selection, m_nImageBmpAtom, rData ); + #if OSL_DEBUG_LEVEL > 1 + if( bSuccess ) + fprintf( stderr, "got %d bytes of image/bmp\n", (int)rData.getLength() ); + #endif + if( ! bSuccess ) + { + Pixmap aPixmap = None; + Colormap aColormap = None; + + // prepare property for MULTIPLE request + Sequence< sal_Int8 > aData; + Atom pTypes[4] = { XA_PIXMAP, XA_PIXMAP, + XA_COLORMAP, XA_COLORMAP }; + { + osl::MutexGuard aGuard(m_aMutex); + + XChangeProperty( m_pDisplay, + m_aWindow, + selection, + XA_ATOM, + 32, + PropModeReplace, + (unsigned char*)pTypes, + 4 ); + } + + // try MULTIPLE request + if( getPasteData( selection, m_nMULTIPLEAtom, aData ) ) + { + Atom* pReturnedTypes = (Atom*)aData.getArray(); + if( pReturnedTypes[0] == XA_PIXMAP && pReturnedTypes[1] == XA_PIXMAP ) + { + osl::MutexGuard aGuard(m_aMutex); + + Atom type = None; + int format = 0; + unsigned long nItems = 0; + unsigned long nBytes = 0; + unsigned char* pReturn = NULL; + XGetWindowProperty( m_pDisplay, m_aWindow, XA_PIXMAP, 0, 1, True, XA_PIXMAP, &type, &format, &nItems, &nBytes, &pReturn ); + if( pReturn ) + { + if( type == XA_PIXMAP ) + aPixmap = *(Pixmap*)pReturn; + XFree( pReturn ); + pReturn = NULL; + if( pReturnedTypes[2] == XA_COLORMAP && pReturnedTypes[3] == XA_COLORMAP ) + { + XGetWindowProperty( m_pDisplay, m_aWindow, XA_COLORMAP, 0, 1, True, XA_COLORMAP, &type, &format, &nItems, &nBytes, &pReturn ); + if( pReturn ) + { + if( type == XA_COLORMAP ) + aColormap = *(Colormap*)pReturn; + XFree( pReturn ); + } + } + } + #if OSL_DEBUG_LEVEL > 1 + else + { + fprintf( stderr, "could not get PIXMAP property: type=%s, format=%d, items=%ld, bytes=%ld, ret=0x%p\n", OUStringToOString( getString( type ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), format, nItems, nBytes, pReturn ); + } + #endif + } + } + + if( aPixmap == None ) + { + // perhaps two normal requests will work + if( getPasteData( selection, XA_PIXMAP, aData ) ) + { + aPixmap = *(Pixmap*)aData.getArray(); + if( aColormap == None && getPasteData( selection, XA_COLORMAP, aData ) ) + aColormap = *(Colormap*)aData.getArray(); + } + } + + // convert data if possible + if( aPixmap != None ) + { + osl::MutexGuard aGuard(m_aMutex); + + sal_Int32 nOutSize = 0; + sal_uInt8* pBytes = X11_getBmpFromPixmap( m_pDisplay, aPixmap, aColormap, nOutSize ); + if( pBytes && nOutSize ) + { + rData = Sequence< sal_Int8 >( nOutSize ); + memcpy( rData.getArray(), pBytes, nOutSize ); + X11_freeBmp( pBytes ); + bSuccess = true; + } + } + } + } + + if( ! bSuccess ) + { + int nFormat; + ::std::list< Atom > aTypes; + convertTypeToNative( rType, selection, nFormat, aTypes ); + ::std::list< Atom >::const_iterator type_it; + Atom nSelectedType = None; + for( type_it = aTypes.begin(); type_it != aTypes.end() && nSelectedType == None; ++type_it ) + { + for( unsigned int i = 0; i < rNativeTypes.size() && nSelectedType == None; i++ ) + if( rNativeTypes[i] == *type_it ) + nSelectedType = *type_it; + } + if( nSelectedType != None ) + bSuccess = getPasteData( selection, nSelectedType, rData ); + } +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "getPasteData for selection %s and data type %s returns %s, returned sequence has length %" SAL_PRIdINT32 "\n", + OUStringToOString( getString( selection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), + OUStringToOString( rType, RTL_TEXTENCODING_ISO_8859_1 ).getStr(), + bSuccess ? "true" : "false", + rData.getLength() + ); +#endif + return bSuccess; +} + +// ------------------------------------------------------------------------ + +bool SelectionManager::getPasteDataTypes( Atom selection, Sequence< DataFlavor >& rTypes ) +{ + ::boost::unordered_map< Atom, Selection* >::iterator it; + { + osl::MutexGuard aGuard(m_aMutex); + + it = m_aSelections.find( selection ); + if( it != m_aSelections.end() && + it->second->m_aTypes.getLength() && + abs( it->second->m_nLastTimestamp - time( NULL ) ) < 2 + ) + { + rTypes = it->second->m_aTypes; + return true; + } + } + + bool bSuccess = false; + bool bHaveUTF16 = false; + Atom aUTF8Type = None; + bool bHaveCompound = false; + bool bHaveText = false; + Sequence< sal_Int8 > aAtoms; + + if( selection == m_nXdndSelection ) + { + // xdnd sends first three types with XdndEnter + // if more than three types are supported then the XDndTypeList + // property on the source window is used + if( m_aDropEnterEvent.data.l[0] && m_aCurrentDropWindow ) + { + if( m_aDropEnterEvent.data.l[1] & 1 ) + { + const unsigned int atomcount = 256; + // more than three types; look in property + osl::MutexGuard aGuard(m_aMutex); + + Atom nType; + int nFormat; + unsigned long nItems, nBytes; + unsigned char* pBytes = NULL; + + XGetWindowProperty( m_pDisplay, m_aDropEnterEvent.data.l[0], + m_nXdndTypeList, 0, atomcount, False, + XA_ATOM, + &nType, &nFormat, &nItems, &nBytes, &pBytes ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "have %ld data types in XdndTypeList\n", nItems ); +#endif + if( nItems == atomcount && nBytes > 0 ) + { + // wow ... more than 256 types ! + aAtoms.realloc( sizeof( Atom )*atomcount+nBytes ); + memcpy( aAtoms.getArray(), pBytes, sizeof( Atom )*atomcount ); + XFree( pBytes ); + pBytes = NULL; + XGetWindowProperty( m_pDisplay, m_aDropEnterEvent.data.l[0], + m_nXdndTypeList, atomcount, nBytes/sizeof(Atom), + False, XA_ATOM, + &nType, &nFormat, &nItems, &nBytes, &pBytes ); + { + memcpy( aAtoms.getArray()+atomcount*sizeof(Atom), pBytes, nItems*sizeof(Atom) ); + XFree( pBytes ); + } + } + else + { + aAtoms.realloc( sizeof(Atom)*nItems ); + memcpy( aAtoms.getArray(), pBytes, nItems*sizeof(Atom) ); + XFree( pBytes ); + } + } + else + { + // one to three types + int n = 0, i; + for( i = 0; i < 3; i++ ) + if( m_aDropEnterEvent.data.l[2+i] ) + n++; +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "have %d data types in XdndEnter\n", n ); +#endif + aAtoms.realloc( sizeof(Atom)*n ); + for( i = 0, n = 0; i < 3; i++ ) + if( m_aDropEnterEvent.data.l[2+i] ) + ((Atom*)aAtoms.getArray())[n++] = m_aDropEnterEvent.data.l[2+i]; + } + } + } + // get data of type TARGETS + else if( ! getPasteData( selection, m_nTARGETSAtom, aAtoms ) ) + aAtoms = Sequence< sal_Int8 >(); + + std::vector< Atom > aNativeTypes; + if( aAtoms.getLength() ) + { + sal_Int32 nAtoms = aAtoms.getLength() / sizeof(Atom); + Atom* pAtoms = (Atom*)aAtoms.getArray(); + rTypes.realloc( nAtoms ); + aNativeTypes.resize( nAtoms ); + DataFlavor* pFlavors = rTypes.getArray(); + sal_Int32 nNativeTypesIndex = 0; + while( nAtoms-- ) + { +#if OSL_DEBUG_LEVEL > 1 + if( *pAtoms && *pAtoms < 0x01000000 ) + fprintf( stderr, "native type: %s\n", OUStringToOString( getString( *pAtoms ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); +#endif + if( *pAtoms == m_nCOMPOUNDAtom ) + bHaveText = bHaveCompound = true; + else if( *pAtoms && *pAtoms < 0x01000000 ) + { + int nFormat; + pFlavors->MimeType = convertTypeFromNative( *pAtoms, selection, nFormat ); + pFlavors->DataType = getCppuType( (Sequence< sal_Int8 >*)0 ); + sal_Int32 nIndex = 0; + if( pFlavors->MimeType.getToken( 0, ';', nIndex ).equalsAsciiL( "text/plain", 10 ) ) + { + OUString aToken(pFlavors->MimeType.getToken( 0, ';', nIndex )); + // omit text/plain;charset=unicode since it is not well defined + if( aToken.compareToAscii( "charset=unicode" ) == 0 ) + { + pAtoms++; + continue; + } + bHaveText = true; + if( aToken.compareToAscii( "charset=utf-16" ) == 0 ) + { + bHaveUTF16 = true; + pFlavors->DataType = getCppuType( (OUString*)0 ); + } + else if( aToken.compareToAscii( "charset=utf-8" ) == 0 ) + { + aUTF8Type = *pAtoms; + } + } + pFlavors++; + aNativeTypes[ nNativeTypesIndex ] = *pAtoms; + nNativeTypesIndex++; + } + pAtoms++; + } + if( (pFlavors - rTypes.getArray()) < rTypes.getLength() ) + rTypes.realloc(pFlavors - rTypes.getArray()); + bSuccess = rTypes.getLength() ? true : false; + if( bHaveText && ! bHaveUTF16 ) + { + int i = 0; + + int nNewFlavors = rTypes.getLength()+1; + Sequence< DataFlavor > aTemp( nNewFlavors ); + for( i = 0; i < nNewFlavors-1; i++ ) + aTemp.getArray()[i+1] = rTypes.getConstArray()[i]; + aTemp.getArray()[0].MimeType = OUString(RTL_CONSTASCII_USTRINGPARAM("text/plain;charset=utf-16")); + aTemp.getArray()[0].DataType = getCppuType( (OUString*)0 ); + rTypes = aTemp; + + std::vector< Atom > aNativeTemp( nNewFlavors ); + for( i = 0; i < nNewFlavors-1; i++ ) + aNativeTemp[ i + 1 ] = aNativeTypes[ i ]; + aNativeTemp[0] = None; + aNativeTypes = aNativeTemp; + } + } + + { + osl::MutexGuard aGuard(m_aMutex); + + it = m_aSelections.find( selection ); + if( it != m_aSelections.end() ) + { + if( bSuccess ) + { + it->second->m_aTypes = rTypes; + it->second->m_aNativeTypes = aNativeTypes; + it->second->m_nLastTimestamp = time( NULL ); + it->second->m_bHaveUTF16 = bHaveUTF16; + it->second->m_aUTF8Type = aUTF8Type; + it->second->m_bHaveCompound = bHaveCompound; + } + else + { + it->second->m_aTypes = Sequence< DataFlavor >(); + it->second->m_aNativeTypes = std::vector< Atom >(); + it->second->m_nLastTimestamp = 0; + it->second->m_bHaveUTF16 = false; + it->second->m_aUTF8Type = None; + it->second->m_bHaveCompound = false; + } + } + } + +#if OSL_DEBUG_LEVEL > 1 + { + fprintf( stderr, "SelectionManager::getPasteDataTypes( %s ) = %s\n", OUStringToOString( getString( selection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), bSuccess ? "true" : "false" ); + for( int i = 0; i < rTypes.getLength(); i++ ) + fprintf( stderr, "type: %s\n", OUStringToOString( rTypes.getConstArray()[i].MimeType, RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); + } +#endif + + return bSuccess; +} + +// ------------------------------------------------------------------------ + +PixmapHolder* SelectionManager::getPixmapHolder( Atom selection ) +{ + boost::unordered_map< Atom, Selection* >::const_iterator it = m_aSelections.find( selection ); + if( it == m_aSelections.end() ) + return NULL; + if( ! it->second->m_pPixmap ) + it->second->m_pPixmap = new PixmapHolder( m_pDisplay ); + return it->second->m_pPixmap; +} + +static sal_Size GetTrueFormatSize(int nFormat) +{ + // http://mail.gnome.org/archives/wm-spec-list/2003-March/msg00067.html + return nFormat == 32 ? sizeof(long) : nFormat/8; +} + +bool SelectionManager::sendData( SelectionAdaptor* pAdaptor, + XLIB_Window requestor, + Atom target, + Atom property, + Atom selection ) +{ + osl::ResettableMutexGuard aGuard( m_aMutex ); + + // handle targets related to image/bmp + if( target == XA_COLORMAP || target == XA_PIXMAP || target == XA_BITMAP || target == XA_VISUALID ) + { + PixmapHolder* pPixmap = getPixmapHolder( selection ); + if( ! pPixmap ) return false; + XID nValue = None; + + // handle colormap request + if( target == XA_COLORMAP ) + nValue = (XID)pPixmap->getColormap(); + else if( target == XA_VISUALID ) + nValue = (XID)pPixmap->getVisualID(); + else if( target == XA_PIXMAP || target == XA_BITMAP ) + { + nValue = (XID)pPixmap->getPixmap(); + if( nValue == None ) + { + // first conversion + Sequence< sal_Int8 > aData; + int nFormat; + aGuard.clear(); + bool bConverted = convertData( pAdaptor->getTransferable(), target, selection, nFormat, aData ); + aGuard.reset(); + if( bConverted ) + { + // get pixmap again since clearing the guard could have invalidated + // the pixmap in another thread + pPixmap = getPixmapHolder( selection ); + // conversion succeeded, so aData contains image/bmp now + if( pPixmap->needsConversion( (const sal_uInt8*)aData.getConstArray() ) + && m_xBitmapConverter.is() ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "trying bitmap conversion\n" ); +#endif + css::uno::Reference<XBitmap> xBM( new BmpTransporter( aData ) ); + Sequence<Any> aArgs(2), aOutArgs; + Sequence<sal_Int16> aOutIndex; + aArgs.getArray()[0] = makeAny( xBM ); + aArgs.getArray()[1] = makeAny( (sal_uInt16)pPixmap->getDepth() ); + aGuard.clear(); + try + { + Any aResult = + m_xBitmapConverter->invoke( OUString(RTL_CONSTASCII_USTRINGPARAM("convert-bitmap-depth")), + aArgs, aOutIndex, aOutArgs ); + if( aResult >>= xBM ) + aData = xBM->getDIB(); + } + catch(...) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "exception in bitmap converter\n" ); +#endif + } + aGuard.reset(); + } + // get pixmap again since clearing the guard could have invalidated + // the pixmap in another thread + pPixmap = getPixmapHolder( selection ); + nValue = (XID)pPixmap->setBitmapData( (const sal_uInt8*)aData.getConstArray() ); + } + if( nValue == None ) + return false; + } + if( target == XA_BITMAP ) + nValue = (XID)pPixmap->getBitmap(); + } + + XChangeProperty( m_pDisplay, + requestor, + property, + target, + 32, + PropModeReplace, + (const unsigned char*)&nValue, + 1); + return true; + } + + /* + * special target TEXT allows us to transfer + * the data in an encoding of our choice + * COMPOUND_TEXT will work with most applications + */ + if( target == m_nTEXTAtom ) + target = m_nCOMPOUNDAtom; + + Sequence< sal_Int8 > aData; + int nFormat; + aGuard.clear(); + bool bConverted = convertData( pAdaptor->getTransferable(), target, selection, nFormat, aData ); + aGuard.reset(); + if( bConverted ) + { + // conversion succeeded + if( aData.getLength() > m_nIncrementalThreshold ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "using INCR protocol\n" ); + boost::unordered_map< XLIB_Window, boost::unordered_map< Atom, IncrementalTransfer > >::const_iterator win_it = m_aIncrementals.find( requestor ); + if( win_it != m_aIncrementals.end() ) + { + boost::unordered_map< Atom, IncrementalTransfer >::const_iterator inc_it = win_it->second.find( property ); + if( inc_it != win_it->second.end() ) + { + const IncrementalTransfer& rInc = inc_it->second; + fprintf( stderr, "premature end and new start for INCR transfer for window 0x%lx, property %s, type %s\n", + rInc.m_aRequestor, + OUStringToOString( getString( rInc.m_aProperty ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), + OUStringToOString( getString( rInc.m_aTarget ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() + ); + } + } +#endif + + // insert IncrementalTransfer + IncrementalTransfer& rInc = m_aIncrementals[ requestor ][ property ]; + rInc.m_aData = aData; + rInc.m_nBufferPos = 0; + rInc.m_aRequestor = requestor; + rInc.m_aProperty = property; + rInc.m_aTarget = target; + rInc.m_nFormat = nFormat; + rInc.m_nTransferStartTime = time( NULL ); + + // use incr protocol, signal start to requestor + long nMinSize = m_nIncrementalThreshold; + XSelectInput( m_pDisplay, requestor, PropertyChangeMask ); + XChangeProperty( m_pDisplay, requestor, property, + m_nINCRAtom, 32, PropModeReplace, (unsigned char*)&nMinSize, 1 ); + XFlush( m_pDisplay ); + } + else + { + sal_Size nUnitSize = GetTrueFormatSize(nFormat); + XChangeProperty( m_pDisplay, + requestor, + property, + target, + nFormat, + PropModeReplace, + (const unsigned char*)aData.getConstArray(), + aData.getLength()/nUnitSize ); + } + } +#if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "convertData failed for type: %s \n", + OUStringToOString( convertTypeFromNative( target, selection, nFormat ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); +#endif + return bConverted; +} + +// ------------------------------------------------------------------------ + +bool SelectionManager::handleSelectionRequest( XSelectionRequestEvent& rRequest ) +{ + osl::ResettableMutexGuard aGuard( m_aMutex ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "handleSelectionRequest for selection %s and target %s\n", + OUStringToOString( getString( rRequest.selection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), + OUStringToOString( getString( rRequest.target ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() + ); +#endif + + XEvent aNotify; + + aNotify.type = SelectionNotify; + aNotify.xselection.display = rRequest.display; + aNotify.xselection.send_event = True; + aNotify.xselection.requestor = rRequest.requestor; + aNotify.xselection.selection = rRequest.selection; + aNotify.xselection.time = rRequest.time; + aNotify.xselection.target = rRequest.target; + aNotify.xselection.property = None; + + SelectionAdaptor* pAdaptor = getAdaptor( rRequest.selection ); + // ensure that we still own that selection + if( pAdaptor && + XGetSelectionOwner( m_pDisplay, rRequest.selection ) == m_aWindow ) + { + css::uno::Reference< XTransferable > xTrans( pAdaptor->getTransferable() ); + if( rRequest.target == m_nTARGETSAtom ) + { + // someone requests our types + if( xTrans.is() ) + { + aGuard.clear(); + Sequence< DataFlavor > aFlavors = xTrans->getTransferDataFlavors(); + aGuard.reset(); + + ::std::list< Atom > aConversions; + getNativeTypeList( aFlavors, aConversions, rRequest.selection ); + + int i, nTypes = aConversions.size(); + Atom* pTypes = (Atom*)alloca( nTypes * sizeof( Atom ) ); + std::list< Atom >::const_iterator it; + for( i = 0, it = aConversions.begin(); i < nTypes; i++, ++it ) + pTypes[i] = *it; + XChangeProperty( m_pDisplay, rRequest.requestor, rRequest.property, + XA_ATOM, 32, PropModeReplace, (const unsigned char*)pTypes, nTypes ); + aNotify.xselection.property = rRequest.property; +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "sending type list:\n" ); + for( int k = 0; k < nTypes; k++ ) + fprintf( stderr, " %s\n", pTypes[k] ? XGetAtomName( m_pDisplay, pTypes[k] ) : "<None>" ); +#endif + } + } + else if( rRequest.target == m_nTIMESTAMPAtom ) + { + long nTimeStamp = (long)m_aSelections[rRequest.selection]->m_nOrigTimestamp; + XChangeProperty( m_pDisplay, rRequest.requestor, rRequest.property, + XA_INTEGER, 32, PropModeReplace, (const unsigned char*)&nTimeStamp, 1 ); + aNotify.xselection.property = rRequest.property; +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "sending timestamp: %d\n", (int)nTimeStamp ); +#endif + } + else + { + bool bEventSuccess = false; + if( rRequest.target == m_nMULTIPLEAtom ) + { + // get all targets + Atom nType = None; + int nFormat = 0; + unsigned long nItems = 0, nBytes = 0; + unsigned char* pData = NULL; + + // get number of atoms + XGetWindowProperty( m_pDisplay, + rRequest.requestor, + rRequest.property, + 0, 0, + False, + AnyPropertyType, + &nType, &nFormat, + &nItems, &nBytes, + &pData ); + if( nFormat == 32 && nBytes/4 ) + { + if( pData ) // ?? should not happen + { + XFree( pData ); + pData = NULL; + } + XGetWindowProperty( m_pDisplay, + rRequest.requestor, + rRequest.property, + 0, nBytes/4, + False, + nType, + &nType, &nFormat, + &nItems, &nBytes, + &pData ); + if( pData && nItems ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "found %ld atoms in MULTIPLE request\n", nItems ); +#endif + bEventSuccess = true; + bool bResetAtoms = false; + Atom* pAtoms = (Atom*)pData; + aGuard.clear(); + for( unsigned int i = 0; i < nItems; i += 2 ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, " %s => %s: ", + OUStringToOString( getString( pAtoms[i] ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), + OUStringToOString( getString( pAtoms[i+1] ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); +#endif + bool bSuccess = sendData( pAdaptor, rRequest.requestor, pAtoms[i], pAtoms[i+1], rRequest.selection ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "%s\n", bSuccess ? "succeeded" : "failed" ); +#endif + if( ! bSuccess ) + { + pAtoms[i] = None; + bResetAtoms = true; + } + } + aGuard.reset(); + if( bResetAtoms ) + XChangeProperty( m_pDisplay, + rRequest.requestor, + rRequest.property, + XA_ATOM, + 32, + PropModeReplace, + pData, + nBytes/4 ); + } + if( pData ) + XFree( pData ); + } +#if OSL_DEBUG_LEVEL > 1 + else + { + fprintf( stderr, "could not get type list from \"%s\" of type \"%s\" on requestor 0x%lx, requestor has properties:", + OUStringToOString( getString( rRequest.property ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), + OUStringToOString( getString( nType ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), + rRequest.requestor ); + int nProps = 0; + Atom* pProps = XListProperties( m_pDisplay, rRequest.requestor, &nProps ); + if( pProps ) + { + for( int i = 0; i < nProps; i++ ) + fprintf( stderr, " \"%s\"", OUStringToOString( getString( pProps[i]), RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); + XFree( pProps ); + } + } +#endif + } + else + { + aGuard.clear(); + bEventSuccess = sendData( pAdaptor, rRequest.requestor, rRequest.target, rRequest.property, rRequest.selection ); + aGuard.reset(); + } + if( bEventSuccess ) + { + aNotify.xselection.target = rRequest.target; + aNotify.xselection.property = rRequest.property; + } + } + aGuard.clear(); + xTrans.clear(); + aGuard.reset(); + } + XSendEvent( m_pDisplay, rRequest.requestor, False, 0, &aNotify ); + + if( rRequest.selection == XA_PRIMARY && + m_bWaitingForPrimaryConversion && + m_xDragSourceListener.is() ) + { + DragSourceDropEvent dsde; + dsde.Source = static_cast< OWeakObject* >(this); + dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, rRequest.time, *this ); + dsde.DragSource = static_cast< XDragSource* >(this); + if( aNotify.xselection.property != None ) + { + dsde.DropAction = DNDConstants::ACTION_COPY; + dsde.DropSuccess = sal_True; + } + else + { + dsde.DropAction = DNDConstants::ACTION_NONE; + dsde.DropSuccess = sal_False; + } + css::uno::Reference< XDragSourceListener > xListener( m_xDragSourceListener ); + m_xDragSourceListener.clear(); + aGuard.clear(); + if( xListener.is() ) + xListener->dragDropEnd( dsde ); + } + + // we handled the event in any case by answering + return true; +} + +// ------------------------------------------------------------------------ + +bool SelectionManager::handleReceivePropertyNotify( XPropertyEvent& rNotify ) +{ + osl::MutexGuard aGuard( m_aMutex ); + // data we requested arrived +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "handleReceivePropertyNotify for property %s\n", + OUStringToOString( getString( rNotify.atom ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); +#endif + bool bHandled = false; + + ::boost::unordered_map< Atom, Selection* >::iterator it = + m_aSelections.find( rNotify.atom ); + if( it != m_aSelections.end() && + rNotify.state == PropertyNewValue && + ( it->second->m_eState == Selection::WaitingForResponse || + it->second->m_eState == Selection::WaitingForData || + it->second->m_eState == Selection::IncrementalTransfer + ) + ) + { + // MULTIPLE requests are only complete after selection notify + if( it->second->m_aRequestedType == m_nMULTIPLEAtom && + ( it->second->m_eState == Selection::WaitingForResponse || + it->second->m_eState == Selection::WaitingForData ) ) + return false; + + bHandled = true; + + Atom nType = None; + int nFormat = 0; + unsigned long nItems = 0, nBytes = 0; + unsigned char* pData = NULL; + + // get type and length + XGetWindowProperty( m_pDisplay, + rNotify.window, + rNotify.atom, + 0, 0, + False, + AnyPropertyType, + &nType, &nFormat, + &nItems, &nBytes, + &pData ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "found %ld bytes data of type %s and format %d, items = %ld\n", + nBytes, + OUStringToOString( getString( nType ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), + nFormat, nItems ); +#endif + if( pData ) + { + XFree( pData ); + pData = NULL; + } + + if( nType == m_nINCRAtom ) + { + // start data transfer + XDeleteProperty( m_pDisplay, rNotify.window, rNotify.atom ); + it->second->m_eState = Selection::IncrementalTransfer; + } + else if( nType != None ) + { + XGetWindowProperty( m_pDisplay, + rNotify.window, + rNotify.atom, + 0, nBytes/4 +1, + True, + nType, + &nType, &nFormat, + &nItems, &nBytes, + &pData ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "read %ld items data of type %s and format %d, %ld bytes left in property\n", + nItems, + OUStringToOString( getString( nType ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), + nFormat, nBytes ); +#endif + + sal_Size nUnitSize = GetTrueFormatSize(nFormat); + + if( it->second->m_eState == Selection::WaitingForData || + it->second->m_eState == Selection::WaitingForResponse ) + { + // copy data + it->second->m_aData = Sequence< sal_Int8 >( (sal_Int8*)pData, nItems*nUnitSize ); + it->second->m_eState = Selection::Inactive; + it->second->m_aDataArrived.set(); + } + else if( it->second->m_eState == Selection::IncrementalTransfer ) + { + if( nItems ) + { + // append data + Sequence< sal_Int8 > aData( it->second->m_aData.getLength() + nItems*nUnitSize ); + memcpy( aData.getArray(), it->second->m_aData.getArray(), it->second->m_aData.getLength() ); + memcpy( aData.getArray() + it->second->m_aData.getLength(), pData, nItems*nUnitSize ); + it->second->m_aData = aData; + } + else + { + it->second->m_eState = Selection::Inactive; + it->second->m_aDataArrived.set(); + } + } + if( pData ) + XFree( pData ); + } + else if( it->second->m_eState == Selection::IncrementalTransfer ) + { + it->second->m_eState = Selection::Inactive; + it->second->m_aDataArrived.set(); + } + } + return bHandled; +} + +// ------------------------------------------------------------------------ + +bool SelectionManager::handleSendPropertyNotify( XPropertyEvent& rNotify ) +{ + osl::MutexGuard aGuard( m_aMutex ); + + // ready for next part of a IncrementalTransfer +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "handleSendPropertyNotify for property %s (%s)\n", + OUStringToOString( getString( rNotify.atom ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), + rNotify.state == PropertyNewValue ? "new value" : ( rNotify.state == PropertyDelete ? "deleted" : "unknown") + ); +#endif + + bool bHandled = false; + // feed incrementals + if( rNotify.state == PropertyDelete ) + { + boost::unordered_map< XLIB_Window, boost::unordered_map< Atom, IncrementalTransfer > >::iterator it; + it = m_aIncrementals.find( rNotify.window ); + if( it != m_aIncrementals.end() ) + { + bHandled = true; + int nCurrentTime = time( NULL ); + boost::unordered_map< Atom, IncrementalTransfer >::iterator inc_it; + // throw out aborted transfers + std::list< Atom > aTimeouts; + for( inc_it = it->second.begin(); inc_it != it->second.end(); ++inc_it ) + { + if( (nCurrentTime - inc_it->second.m_nTransferStartTime) > (getSelectionTimeout()+2) ) + { + aTimeouts.push_back( inc_it->first ); +#if OSL_DEBUG_LEVEL > 1 + const IncrementalTransfer& rInc = inc_it->second; + fprintf( stderr, "timeout on INCR transfer for window 0x%lx, property %s, type %s\n", + rInc.m_aRequestor, + OUStringToOString( getString( rInc.m_aProperty ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), + OUStringToOString( getString( rInc.m_aTarget ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() + ); +#endif + } + } + + while( aTimeouts.begin() != aTimeouts.end() ) + { + // transfer broken, might even be a new client with the + // same window id + it->second.erase( aTimeouts.front() ); + aTimeouts.pop_front(); + } + + inc_it = it->second.find( rNotify.atom ); + if( inc_it != it->second.end() ) + { + IncrementalTransfer& rInc = inc_it->second; + + int nBytes = rInc.m_aData.getLength() - rInc.m_nBufferPos; + nBytes = (nBytes > m_nIncrementalThreshold) ? m_nIncrementalThreshold : nBytes; + if( nBytes < 0 ) // sanity check + nBytes = 0; +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "pushing %d bytes: \"%.*s\"...\n", + nBytes, nBytes > 32 ? 32 : nBytes, + (const unsigned char*)rInc.m_aData.getConstArray()+rInc.m_nBufferPos ); +#endif + + sal_Size nUnitSize = GetTrueFormatSize(rInc.m_nFormat); + + XChangeProperty( m_pDisplay, + rInc.m_aRequestor, + rInc.m_aProperty, + rInc.m_aTarget, + rInc.m_nFormat, + PropModeReplace, + (const unsigned char*)rInc.m_aData.getConstArray()+rInc.m_nBufferPos, + nBytes/nUnitSize ); + rInc.m_nBufferPos += nBytes; + rInc.m_nTransferStartTime = nCurrentTime; + + if( nBytes == 0 ) // transfer finished + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "finished INCR transfer for window 0x%lx, property %s, type %s\n", + rInc.m_aRequestor, + OUStringToOString( getString( rInc.m_aProperty ), RTL_TEXTENCODING_ISO_8859_1 ).getStr(), + OUStringToOString( getString( rInc.m_aTarget ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() + ); +#endif + it->second.erase( inc_it ); + } + + } + // eventually clean up the hash map + if( it->second.begin() == it->second.end() ) + m_aIncrementals.erase( it ); + } + } + return bHandled; +} + +// ------------------------------------------------------------------------ + +bool SelectionManager::handleSelectionNotify( XSelectionEvent& rNotify ) +{ + osl::MutexGuard aGuard( m_aMutex ); + + bool bHandled = false; + + // notification about success/failure of one of our conversion requests +#if OSL_DEBUG_LEVEL > 1 + OUString aSelection( getString( rNotify.selection ) ); + OUString aProperty(RTL_CONSTASCII_USTRINGPARAM("None")); + if( rNotify.property ) + aProperty = getString( rNotify.property ); + fprintf( stderr, "handleSelectionNotify for selection %s and property %s (0x%lx)\n", + OUStringToOString( aSelection, RTL_TEXTENCODING_ISO_8859_1 ).getStr(), + OUStringToOString( aProperty, RTL_TEXTENCODING_ISO_8859_1 ).getStr(), + rNotify.property + ); + if( rNotify.requestor != m_aWindow && rNotify.requestor != m_aCurrentDropWindow ) + fprintf( stderr, "Warning: selection notify for unknown window 0x%lx\n", rNotify.requestor ); +#endif + ::boost::unordered_map< Atom, Selection* >::iterator it = + m_aSelections.find( rNotify.selection ); + if ( + (rNotify.requestor == m_aWindow || rNotify.requestor == m_aCurrentDropWindow) && + it != m_aSelections.end() && + ( + (it->second->m_eState == Selection::WaitingForResponse) || + (it->second->m_eState == Selection::WaitingForData) + ) + ) + { + bHandled = true; + if( it->second->m_aRequestedType == m_nMULTIPLEAtom ) + { + Atom nType = None; + int nFormat = 0; + unsigned long nItems = 0, nBytes = 0; + unsigned char* pData = NULL; + + // get type and length + XGetWindowProperty( m_pDisplay, + rNotify.requestor, + rNotify.property, + 0, 256, + False, + AnyPropertyType, + &nType, &nFormat, + &nItems, &nBytes, + &pData ); + if( nBytes ) // HUGE request !!! + { + if( pData ) + XFree( pData ); + XGetWindowProperty( m_pDisplay, + rNotify.requestor, + rNotify.property, + 0, 256+(nBytes+3)/4, + False, + AnyPropertyType, + &nType, &nFormat, + &nItems, &nBytes, + &pData ); + } + it->second->m_eState = Selection::Inactive; + sal_Size nUnitSize = GetTrueFormatSize(nFormat); + it->second->m_aData = Sequence< sal_Int8 >((sal_Int8*)pData, nItems * nUnitSize); + it->second->m_aDataArrived.set(); + if( pData ) + XFree( pData ); + } + // WaitingForData can actually happen; some + // applications (e.g. cmdtool on Solaris) first send + // a success and then cancel it. Weird ! + else if( rNotify.property == None ) + { + // conversion failed, stop transfer + it->second->m_eState = Selection::Inactive; + it->second->m_aData = Sequence< sal_Int8 >(); + it->second->m_aDataArrived.set(); + } + // get the bytes, by INCR if necessary + else + it->second->m_eState = Selection::WaitingForData; + } +#if OSL_DEBUG_LEVEL > 1 + else if( it != m_aSelections.end() ) + fprintf( stderr, "Warning: selection in state %d\n", it->second->m_eState ); +#endif + return bHandled; +} + +// ------------------------------------------------------------------------ + +bool SelectionManager::handleDropEvent( XClientMessageEvent& rMessage ) +{ + osl::ResettableMutexGuard aGuard(m_aMutex); + + // handle drop related events + XLIB_Window aSource = rMessage.data.l[0]; + XLIB_Window aTarget = rMessage.window; + + bool bHandled = false; + + ::boost::unordered_map< XLIB_Window, DropTargetEntry >::iterator it = + m_aDropTargets.find( aTarget ); + +#if OSL_DEBUG_LEVEL > 1 + if( rMessage.message_type == m_nXdndEnter || + rMessage.message_type == m_nXdndLeave || + rMessage.message_type == m_nXdndDrop || + rMessage.message_type == m_nXdndPosition ) + { + fprintf( stderr, "got drop event %s, ", OUStringToOString( getString( rMessage.message_type ), RTL_TEXTENCODING_ASCII_US).getStr() ); + if( it == m_aDropTargets.end() ) + fprintf( stderr, "but no target found\n" ); + else if( ! it->second.m_pTarget->m_bActive ) + fprintf( stderr, "but target is inactive\n" ); + else if( m_aDropEnterEvent.data.l[0] != None && (XLIB_Window)m_aDropEnterEvent.data.l[0] != aSource ) + fprintf( stderr, "but source 0x%lx is unknown (expected 0x%lx or 0)\n", aSource, m_aDropEnterEvent.data.l[0] ); + else + fprintf( stderr, "processing.\n" ); + } +#endif + + if( it != m_aDropTargets.end() && it->second.m_pTarget->m_bActive && + m_bDropWaitingForCompletion && m_aDropEnterEvent.data.l[0] ) + { + bHandled = true; + OSL_FAIL( "someone forgot to call dropComplete ?" ); + // some listener forgot to call dropComplete in the last operation + // let us end it now and accept the new enter event + aGuard.clear(); + dropComplete( sal_False, m_aCurrentDropWindow, m_nDropTime ); + aGuard.reset(); + } + + if( it != m_aDropTargets.end() && + it->second.m_pTarget->m_bActive && + ( m_aDropEnterEvent.data.l[0] == None || XLIB_Window(m_aDropEnterEvent.data.l[0]) == aSource ) + ) + { + if( rMessage.message_type == m_nXdndEnter ) + { + bHandled = true; + m_aDropEnterEvent = rMessage; + m_bDropEnterSent = false; + m_aCurrentDropWindow = aTarget; + m_nCurrentProtocolVersion = m_aDropEnterEvent.data.l[1] >> 24; +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "received XdndEnter on 0x%lx\n", aTarget ); +#endif + } + else if( + rMessage.message_type == m_nXdndPosition && + aSource == XLIB_Window(m_aDropEnterEvent.data.l[0]) + ) + { + bHandled = true; + m_nDropTime = m_nCurrentProtocolVersion > 0 ? rMessage.data.l[3] : CurrentTime; + if( ! m_bDropEnterSent ) + m_nDropTimestamp = m_nDropTime; + + XLIB_Window aChild; + XTranslateCoordinates( m_pDisplay, + it->second.m_aRootWindow, + it->first, + rMessage.data.l[2] >> 16, + rMessage.data.l[2] & 0xffff, + &m_nLastX, &m_nLastY, + &aChild ); + +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "received XdndPosition on 0x%lx (%d, %d)\n", aTarget, m_nLastX, m_nLastY ); +#endif + DropTargetDragEnterEvent aEvent; + aEvent.Source = static_cast< XDropTarget* >(it->second.m_pTarget); + aEvent.Context = new DropTargetDragContext( m_aCurrentDropWindow, m_nDropTimestamp, *this ); + aEvent.LocationX = m_nLastX; + aEvent.LocationY = m_nLastY; + aEvent.SourceActions = m_nSourceActions; + if( m_nCurrentProtocolVersion < 2 ) + aEvent.DropAction = DNDConstants::ACTION_COPY; + else if( Atom(rMessage.data.l[4]) == m_nXdndActionCopy ) + aEvent.DropAction = DNDConstants::ACTION_COPY; + else if( Atom(rMessage.data.l[4]) == m_nXdndActionMove ) + aEvent.DropAction = DNDConstants::ACTION_MOVE; + else if( Atom(rMessage.data.l[4]) == m_nXdndActionLink ) + aEvent.DropAction = DNDConstants::ACTION_LINK; + else if( Atom(rMessage.data.l[4]) == m_nXdndActionAsk ) + // currently no interface to implement ask + aEvent.DropAction = ~0; + else + aEvent.DropAction = DNDConstants::ACTION_NONE; + + m_nLastDropAction = aEvent.DropAction; + if( ! m_bDropEnterSent ) + { + m_bDropEnterSent = true; + aEvent.SupportedDataFlavors = m_xDropTransferable->getTransferDataFlavors(); + aGuard.clear(); + it->second->dragEnter( aEvent ); + } + else + { + aGuard.clear(); + it->second->dragOver( aEvent ); + } + } + else if( + rMessage.message_type == m_nXdndLeave && + aSource == XLIB_Window(m_aDropEnterEvent.data.l[0]) + ) + { + bHandled = true; +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "received XdndLeave on 0x%lx\n", aTarget ); +#endif + DropTargetEvent aEvent; + aEvent.Source = static_cast< XDropTarget* >(it->second.m_pTarget); + m_aDropEnterEvent.data.l[0] = None; + if( m_aCurrentDropWindow == aTarget ) + m_aCurrentDropWindow = None; + m_nCurrentProtocolVersion = nXdndProtocolRevision; + aGuard.clear(); + it->second->dragExit( aEvent ); + } + else if( + rMessage.message_type == m_nXdndDrop && + aSource == XLIB_Window(m_aDropEnterEvent.data.l[0]) + ) + { + bHandled = true; + m_nDropTime = m_nCurrentProtocolVersion > 0 ? rMessage.data.l[2] : CurrentTime; + +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "received XdndDrop on 0x%lx (%d, %d)\n", aTarget, m_nLastX, m_nLastY ); +#endif + if( m_bLastDropAccepted ) + { + DropTargetDropEvent aEvent; + aEvent.Source = static_cast< XDropTarget* >(it->second.m_pTarget); + aEvent.Context = new DropTargetDropContext( m_aCurrentDropWindow, m_nDropTimestamp, *this ); + aEvent.LocationX = m_nLastX; + aEvent.LocationY = m_nLastY; + aEvent.DropAction = m_nLastDropAction; + // there is nothing corresponding to source supported actions + // every source can do link, copy and move + aEvent.SourceActions= m_nLastDropAction; + aEvent.Transferable = m_xDropTransferable; + + m_bDropWaitingForCompletion = true; + aGuard.clear(); + it->second->drop( aEvent ); + } + else + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "XdndDrop canceled due to m_bLastDropAccepted = fale\n" ); +#endif + DropTargetEvent aEvent; + aEvent.Source = static_cast< XDropTarget* >(it->second.m_pTarget); + aGuard.clear(); + it->second->dragExit( aEvent ); + // reset the drop status, notify source + dropComplete( sal_False, m_aCurrentDropWindow, m_nDropTime ); + } + } + } + return bHandled; +} + +/* + * methods for XDropTargetDropContext + */ + +void SelectionManager::dropComplete( sal_Bool bSuccess, XLIB_Window aDropWindow, XLIB_Time ) +{ + osl::ClearableMutexGuard aGuard(m_aMutex); + + if( aDropWindow == m_aCurrentDropWindow ) + { + if( m_xDragSourceListener.is() ) + { + DragSourceDropEvent dsde; + dsde.Source = static_cast< OWeakObject* >(this); + dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this ); + dsde.DragSource = static_cast< XDragSource* >(this); + dsde.DropAction = getUserDragAction(); + dsde.DropSuccess = bSuccess; + css::uno::Reference< XDragSourceListener > xListener = m_xDragSourceListener; + m_xDragSourceListener.clear(); + + aGuard.clear(); + xListener->dragDropEnd( dsde ); + } + else if( m_aDropEnterEvent.data.l[0] && m_aCurrentDropWindow ) + { + XEvent aEvent; + aEvent.xclient.type = ClientMessage; + aEvent.xclient.display = m_pDisplay; + aEvent.xclient.window = m_aDropEnterEvent.data.l[0]; + aEvent.xclient.message_type = m_nXdndFinished; + aEvent.xclient.format = 32; + aEvent.xclient.data.l[0] = m_aCurrentDropWindow; + aEvent.xclient.data.l[1] = bSuccess ? 1 : 0; + aEvent.xclient.data.l[2] = 0; + aEvent.xclient.data.l[3] = 0; + aEvent.xclient.data.l[4] = 0; + if( bSuccess ) + { + if( m_nLastDropAction & DNDConstants::ACTION_MOVE ) + aEvent.xclient.data.l[2] = m_nXdndActionMove; + else if( m_nLastDropAction & DNDConstants::ACTION_COPY ) + aEvent.xclient.data.l[2] = m_nXdndActionCopy; + else if( m_nLastDropAction & DNDConstants::ACTION_LINK ) + aEvent.xclient.data.l[2] = m_nXdndActionLink; + } + +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "Sending XdndFinished to 0x%lx\n", + m_aDropEnterEvent.data.l[0] + ); +#endif + + XSendEvent( m_pDisplay, m_aDropEnterEvent.data.l[0], + False, NoEventMask, & aEvent ); + + m_aDropEnterEvent.data.l[0] = None; + m_aCurrentDropWindow = None; + m_nCurrentProtocolVersion = nXdndProtocolRevision; + } + m_bDropWaitingForCompletion = false; + } + else + OSL_FAIL( "dropComplete from invalid DropTargetDropContext" ); +} + +/* + * methods for XDropTargetDragContext + */ + +// ------------------------------------------------------------------------ + +void SelectionManager::sendDragStatus( Atom nDropAction ) +{ + osl::ClearableMutexGuard aGuard(m_aMutex); + + if( m_xDragSourceListener.is() ) + { + sal_Int8 nNewDragAction; + if( nDropAction == m_nXdndActionMove ) + nNewDragAction = DNDConstants::ACTION_MOVE; + else if( nDropAction == m_nXdndActionCopy ) + nNewDragAction = DNDConstants::ACTION_COPY; + else if( nDropAction == m_nXdndActionLink ) + nNewDragAction = DNDConstants::ACTION_LINK; + else + nNewDragAction = DNDConstants::ACTION_NONE; + nNewDragAction &= m_nSourceActions; + + if( nNewDragAction != m_nTargetAcceptAction ) + { + setCursor( getDefaultCursor( nNewDragAction ), m_aDropWindow, m_nDragTimestamp ); + m_nTargetAcceptAction = nNewDragAction; + } + + DragSourceDragEvent dsde; + dsde.Source = static_cast< OWeakObject* >(this); + dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this ); + dsde.DragSource = static_cast< XDragSource* >(this); + dsde.DropAction = m_nSourceActions; + dsde.UserAction = getUserDragAction(); + + css::uno::Reference< XDragSourceListener > xListener( m_xDragSourceListener ); + // caution: do not change anything after this + aGuard.clear(); + if( xListener.is() ) + xListener->dragOver( dsde ); + } + else if( m_aDropEnterEvent.data.l[0] && m_aCurrentDropWindow ) + { + XEvent aEvent; + aEvent.xclient.type = ClientMessage; + aEvent.xclient.display = m_pDisplay; + aEvent.xclient.window = m_aDropEnterEvent.data.l[0]; + aEvent.xclient.message_type = m_nXdndStatus; + aEvent.xclient.format = 32; + aEvent.xclient.data.l[0] = m_aCurrentDropWindow; + aEvent.xclient.data.l[1] = 2; + if( nDropAction == m_nXdndActionMove || + nDropAction == m_nXdndActionLink || + nDropAction == m_nXdndActionCopy ) + aEvent.xclient.data.l[1] |= 1; + aEvent.xclient.data.l[2] = 0; + aEvent.xclient.data.l[3] = 0; + aEvent.xclient.data.l[4] = m_nCurrentProtocolVersion > 1 ? nDropAction : 0; + +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "Sending XdndStatus to 0x%lx with action %s\n", + m_aDropEnterEvent.data.l[0], + OUStringToOString( getString( nDropAction ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() + ); +#endif + + XSendEvent( m_pDisplay, m_aDropEnterEvent.data.l[0], + False, NoEventMask, & aEvent ); + XFlush( m_pDisplay ); + } +} + +// ------------------------------------------------------------------------ + +sal_Int8 SelectionManager::getUserDragAction() const +{ + return (m_nTargetAcceptAction != DNDConstants::ACTION_DEFAULT) ? m_nTargetAcceptAction : m_nUserDragAction; +} + +// ------------------------------------------------------------------------ + +bool SelectionManager::updateDragAction( int modifierState ) +{ + bool bRet = false; + + sal_Int8 nNewDropAction = DNDConstants::ACTION_MOVE; + if( ( modifierState & ShiftMask ) && ! ( modifierState & ControlMask ) ) + nNewDropAction = DNDConstants::ACTION_MOVE; + else if( ( modifierState & ControlMask ) && ! ( modifierState & ShiftMask ) ) + nNewDropAction = DNDConstants::ACTION_COPY; + else if( ( modifierState & ShiftMask ) && ( modifierState & ControlMask ) ) + nNewDropAction = DNDConstants::ACTION_LINK; + if( m_nCurrentProtocolVersion < 0 && m_aDropWindow != None ) + nNewDropAction = DNDConstants::ACTION_COPY; + nNewDropAction &= m_nSourceActions; + + if( ! ( modifierState & ( ControlMask | ShiftMask ) ) ) + { + if( ! nNewDropAction ) + { + // default to an action so the user does not have to press + // keys explicitly + if( m_nSourceActions & DNDConstants::ACTION_MOVE ) + nNewDropAction = DNDConstants::ACTION_MOVE; + else if( m_nSourceActions & DNDConstants::ACTION_COPY ) + nNewDropAction = DNDConstants::ACTION_COPY; + else if( m_nSourceActions & DNDConstants::ACTION_LINK ) + nNewDropAction = DNDConstants::ACTION_LINK; + } + nNewDropAction |= DNDConstants::ACTION_DEFAULT; + } + + if( nNewDropAction != m_nUserDragAction || m_nTargetAcceptAction != DNDConstants::ACTION_DEFAULT ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "updateDragAction: %x -> %x\n", (int)m_nUserDragAction, (int)nNewDropAction ); +#endif + bRet = true; + m_nUserDragAction = nNewDropAction; + + DragSourceDragEvent dsde; + dsde.Source = static_cast< OWeakObject* >(this); + dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this ); + dsde.DragSource = static_cast< XDragSource* >(this); + dsde.DropAction = m_nUserDragAction; + dsde.UserAction = m_nUserDragAction; + m_nTargetAcceptAction = DNDConstants::ACTION_DEFAULT; // invalidate last accept + m_xDragSourceListener->dropActionChanged( dsde ); + } + return bRet; +} + +// ------------------------------------------------------------------------ + +void SelectionManager::sendDropPosition( bool bForce, XLIB_Time eventTime ) +{ + osl::ClearableMutexGuard aGuard(m_aMutex); + + if( m_bDropSent ) + return; + + ::boost::unordered_map< XLIB_Window, DropTargetEntry >::const_iterator it = + m_aDropTargets.find( m_aDropWindow ); + if( it != m_aDropTargets.end() ) + { + if( it->second.m_pTarget->m_bActive ) + { + int x, y; + XLIB_Window aChild; + XTranslateCoordinates( m_pDisplay, it->second.m_aRootWindow, m_aDropWindow, m_nLastDragX, m_nLastDragY, &x, &y, &aChild ); + DropTargetDragEvent dtde; + dtde.Source = static_cast< OWeakObject* >(it->second.m_pTarget ); + dtde.Context = new DropTargetDragContext( m_aCurrentDropWindow, m_nDropTimestamp, *this ); + dtde.LocationX = x; + dtde.LocationY = y; + dtde.DropAction = getUserDragAction(); + dtde.SourceActions = m_nSourceActions; + aGuard.clear(); + it->second->dragOver( dtde ); + } + } + else if( bForce || + + m_nLastDragX < m_nNoPosX || m_nLastDragX >= m_nNoPosX+m_nNoPosWidth || + m_nLastDragY < m_nNoPosY || m_nLastDragY >= m_nNoPosY+m_nNoPosHeight + ) + { + // send XdndPosition + XEvent aEvent; + aEvent.type = ClientMessage; + aEvent.xclient.display = m_pDisplay; + aEvent.xclient.format = 32; + aEvent.xclient.message_type = m_nXdndPosition; + aEvent.xclient.window = m_aDropWindow; + aEvent.xclient.data.l[0] = m_aWindow; + aEvent.xclient.data.l[1] = 0; + aEvent.xclient.data.l[2] = m_nLastDragX << 16 | (m_nLastDragY&0xffff); + aEvent.xclient.data.l[3] = eventTime; + + if( m_nUserDragAction & DNDConstants::ACTION_COPY ) + aEvent.xclient.data.l[4]=m_nXdndActionCopy; + else if( m_nUserDragAction & DNDConstants::ACTION_MOVE ) + aEvent.xclient.data.l[4]=m_nXdndActionMove; + else if( m_nUserDragAction & DNDConstants::ACTION_LINK ) + aEvent.xclient.data.l[4]=m_nXdndActionLink; + else + aEvent.xclient.data.l[4]=m_nXdndActionCopy; + XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent ); + m_nNoPosX = m_nNoPosY = m_nNoPosWidth = m_nNoPosHeight = 0; + } +} + +// ------------------------------------------------------------------------ + +bool SelectionManager::handleDragEvent( XEvent& rMessage ) +{ + if( ! m_xDragSourceListener.is() ) + return false; + + osl::ResettableMutexGuard aGuard(m_aMutex); + + bool bHandled = false; + + // for shortcut + ::boost::unordered_map< XLIB_Window, DropTargetEntry >::const_iterator it = + m_aDropTargets.find( m_aDropWindow ); +#if OSL_DEBUG_LEVEL > 1 + switch( rMessage.type ) + { + case ClientMessage: + fprintf( stderr, "handleDragEvent: %s\n", OUStringToOString( getString( rMessage.xclient.message_type ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); + break; + case MotionNotify: + break; + case EnterNotify: + fprintf( stderr, "handleDragEvent: EnterNotify\n" ); + break; + case LeaveNotify: + fprintf( stderr, "handleDragEvent: LeaveNotify\n" ); + break; + case ButtonPress: + fprintf( stderr, "handleDragEvent: ButtonPress %d (m_nDragButton = %d)\n", rMessage.xbutton.button, m_nDragButton ); + break; + case ButtonRelease: + fprintf( stderr, "handleDragEvent: ButtonRelease %d (m_nDragButton = %d)\n", rMessage.xbutton.button, m_nDragButton ); + break; + case XLIB_KeyPress: + fprintf( stderr, "handleDragEvent: KeyPress\n" ); + break; + case KeyRelease: + fprintf( stderr, "handleDragEvent: KeyRelease\n" ); + break; + default: + fprintf( stderr, "handleDragEvent: <unknown type %d>\n", rMessage.type ); + break; + } +#endif + + // handle drag related events + if( rMessage.type == ClientMessage ) + { + if( Atom(rMessage.xclient.message_type) == m_nXdndStatus && Atom(rMessage.xclient.data.l[0]) == m_aDropWindow ) + { + bHandled = true; + DragSourceDragEvent dsde; + dsde.Source = static_cast< OWeakObject* >(this); + dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this ); + dsde.DragSource = static_cast< XDragSource* >( this ); + dsde.UserAction = getUserDragAction(); + dsde.DropAction = DNDConstants::ACTION_NONE; + m_bDropSuccess = rMessage.xclient.data.l[1] & 1 ? true : false; +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "status drop action: accept = %s, %s\n", + m_bDropSuccess ? "true" : "false", + OUStringToOString( getString( rMessage.xclient.data.l[4] ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); +#endif + if( rMessage.xclient.data.l[1] & 1 ) + { + if( m_nCurrentProtocolVersion > 1 ) + { + if( Atom(rMessage.xclient.data.l[4]) == m_nXdndActionCopy ) + dsde.DropAction = DNDConstants::ACTION_COPY; + else if( Atom(rMessage.xclient.data.l[4]) == m_nXdndActionMove ) + dsde.DropAction = DNDConstants::ACTION_MOVE; + else if( Atom(rMessage.xclient.data.l[4]) == m_nXdndActionLink ) + dsde.DropAction = DNDConstants::ACTION_LINK; + } + else + dsde.DropAction = DNDConstants::ACTION_COPY; + } + m_nTargetAcceptAction = dsde.DropAction; + + if( ! ( rMessage.xclient.data.l[1] & 2 ) ) + { + m_nNoPosX = rMessage.xclient.data.l[2] >> 16; + m_nNoPosY = rMessage.xclient.data.l[2] & 0xffff; + m_nNoPosWidth = rMessage.xclient.data.l[3] >> 16; + m_nNoPosHeight = rMessage.xclient.data.l[3] & 0xffff; + } + else + m_nNoPosX = m_nNoPosY = m_nNoPosWidth = m_nNoPosHeight = 0; + + setCursor( getDefaultCursor( dsde.DropAction ), m_aDropWindow, m_nDragTimestamp ); + aGuard.clear(); + m_xDragSourceListener->dragOver( dsde ); + } + else if( Atom(rMessage.xclient.message_type) == m_nXdndFinished && m_aDropWindow == Atom(rMessage.xclient.data.l[0]) ) + { + bHandled = true; + // notify the listener + DragSourceDropEvent dsde; + dsde.Source = static_cast< OWeakObject* >(this); + dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this ); + dsde.DragSource = static_cast< XDragSource* >(this); + dsde.DropAction = m_nTargetAcceptAction; + dsde.DropSuccess = m_bDropSuccess; + css::uno::Reference< XDragSourceListener > xListener( m_xDragSourceListener ); + m_xDragSourceListener.clear(); + aGuard.clear(); + xListener->dragDropEnd( dsde ); + } + } + else if( rMessage.type == MotionNotify || + rMessage.type == EnterNotify || rMessage.type == LeaveNotify + ) + { + bHandled = true; + bool bForce = false; + int root_x = rMessage.type == MotionNotify ? rMessage.xmotion.x_root : rMessage.xcrossing.x_root; + int root_y = rMessage.type == MotionNotify ? rMessage.xmotion.y_root : rMessage.xcrossing.y_root; + XLIB_Window root = rMessage.type == MotionNotify ? rMessage.xmotion.root : rMessage.xcrossing.root; + m_nDragTimestamp = rMessage.type == MotionNotify ? rMessage.xmotion.time : rMessage.xcrossing.time; + + aGuard.clear(); + if( rMessage.type == MotionNotify ) + { + bForce = updateDragAction( rMessage.xmotion.state ); + } + updateDragWindow( root_x, root_y, root ); + aGuard.reset(); + + if( m_nCurrentProtocolVersion >= 0 && m_aDropProxy != None ) + { + aGuard.clear(); + sendDropPosition( bForce, rMessage.type == MotionNotify ? rMessage.xmotion.time : rMessage.xcrossing.time ); + } + } + else if( rMessage.type == XLIB_KeyPress || rMessage.type == KeyRelease ) + { + bHandled = true; + KeySym aKey = XKeycodeToKeysym( m_pDisplay, rMessage.xkey.keycode, 0 ); + if( aKey == XK_Escape ) + { + // abort drag + if( it != m_aDropTargets.end() ) + { + DropTargetEvent dte; + dte.Source = static_cast< OWeakObject* >( it->second.m_pTarget ); + aGuard.clear(); + it->second.m_pTarget->dragExit( dte ); + } + else if( m_aDropProxy != None && m_nCurrentProtocolVersion >= 0 ) + { + // send XdndLeave + XEvent aEvent; + aEvent.type = ClientMessage; + aEvent.xclient.display = m_pDisplay; + aEvent.xclient.format = 32; + aEvent.xclient.message_type = m_nXdndLeave; + aEvent.xclient.window = m_aDropWindow; + aEvent.xclient.data.l[0] = m_aWindow; + memset( aEvent.xclient.data.l+1, 0, sizeof(long)*4); + m_aDropWindow = m_aDropProxy = None; + XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent ); + } + // notify the listener + DragSourceDropEvent dsde; + dsde.Source = static_cast< OWeakObject* >(this); + dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this ); + dsde.DragSource = static_cast< XDragSource* >(this); + dsde.DropAction = DNDConstants::ACTION_NONE; + dsde.DropSuccess = sal_False; + css::uno::Reference< XDragSourceListener > xListener( m_xDragSourceListener ); + m_xDragSourceListener.clear(); + aGuard.clear(); + xListener->dragDropEnd( dsde ); + } + else + { + /* + * man page says: state is state immediate PRIOR to the + * event. It would seem that this is a somewhat arguable + * design decision. + */ + int nState = rMessage.xkey.state; + int nNewState = 0; + switch( aKey ) + { + case XK_Shift_R: + case XK_Shift_L: nNewState = ShiftMask;break; + case XK_Control_R: + case XK_Control_L: nNewState = ControlMask;break; + // just interested in shift and ctrl for dnd + } + if( rMessage.type == XLIB_KeyPress ) + nState |= nNewState; + else + nState &= ~nNewState; + aGuard.clear(); + if( updateDragAction( nState ) ) + sendDropPosition( true, rMessage.xkey.time ); + } + } + else if( + ( rMessage.type == ButtonPress || rMessage.type == ButtonRelease ) && + rMessage.xbutton.button == m_nDragButton ) + { + bool bCancel = true; + if( m_aDropWindow != None ) + { + if( it != m_aDropTargets.end() ) + { + if( it->second.m_pTarget->m_bActive && m_nUserDragAction != DNDConstants::ACTION_NONE && m_bLastDropAccepted ) + { + bHandled = true; + int x, y; + XLIB_Window aChild; + XTranslateCoordinates( m_pDisplay, rMessage.xbutton.root, m_aDropWindow, rMessage.xbutton.x_root, rMessage.xbutton.y_root, &x, &y, &aChild ); + DropTargetDropEvent dtde; + dtde.Source = static_cast< OWeakObject* >(it->second.m_pTarget ); + dtde.Context = new DropTargetDropContext( m_aCurrentDropWindow, m_nDropTimestamp, *this ); + dtde.LocationX = x; + dtde.LocationY = y; + dtde.DropAction = m_nUserDragAction; + dtde.SourceActions = m_nSourceActions; + dtde.Transferable = m_xDragSourceTransferable; + m_bDropSent = true; + m_nDropTimeout = time( NULL ); + m_bDropWaitingForCompletion = true; + aGuard.clear(); + it->second->drop( dtde ); + bCancel = false; + } + else bCancel = true; + } + else if( m_nCurrentProtocolVersion >= 0 ) + { + bHandled = true; + + XEvent aEvent; + aEvent.type = ClientMessage; + aEvent.xclient.display = m_pDisplay; + aEvent.xclient.format = 32; + aEvent.xclient.message_type = m_nXdndDrop; + aEvent.xclient.window = m_aDropWindow; + aEvent.xclient.data.l[0] = m_aWindow; + aEvent.xclient.data.l[1] = 0; + aEvent.xclient.data.l[2] = rMessage.xbutton.time; + aEvent.xclient.data.l[3] = 0; + aEvent.xclient.data.l[4] = 0; + + m_bDropSent = true; + m_nDropTimeout = time( NULL ); + XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent ); + bCancel = false; + } + else + { + // dropping on non XdndWindows: acquire ownership of + // PRIMARY and send a middle mouse button click down/up to + // target window + SelectionAdaptor* pAdaptor = getAdaptor( XA_PRIMARY ); + if( pAdaptor ) + { + bHandled = true; + + XLIB_Window aDummy; + XEvent aEvent; + aEvent.type = ButtonPress; + aEvent.xbutton.display = m_pDisplay; + aEvent.xbutton.window = m_aDropWindow; + aEvent.xbutton.root = rMessage.xbutton.root; + aEvent.xbutton.subwindow = m_aDropWindow; + aEvent.xbutton.time = rMessage.xbutton.time+1; + aEvent.xbutton.x_root = rMessage.xbutton.x_root; + aEvent.xbutton.y_root = rMessage.xbutton.y_root; + aEvent.xbutton.state = rMessage.xbutton.state; + aEvent.xbutton.button = Button2; + aEvent.xbutton.same_screen = True; + XTranslateCoordinates( m_pDisplay, + rMessage.xbutton.root, m_aDropWindow, + rMessage.xbutton.x_root, rMessage.xbutton.y_root, + &aEvent.xbutton.x, &aEvent.xbutton.y, + &aDummy ); + XSendEvent( m_pDisplay, m_aDropWindow, False, ButtonPressMask, &aEvent ); + aEvent.xbutton.type = ButtonRelease; + aEvent.xbutton.time++; + aEvent.xbutton.state |= Button2Mask; + XSendEvent( m_pDisplay, m_aDropWindow, False, ButtonReleaseMask, &aEvent ); + + m_bDropSent = true; + m_nDropTimeout = time( NULL ); + XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent ); + m_bWaitingForPrimaryConversion = true; + m_bDropSent = true; + m_nDropTimeout = time( NULL ); + // HACK :-) + aGuard.clear(); + static_cast< X11Clipboard* >( pAdaptor )->setContents( m_xDragSourceTransferable, css::uno::Reference< ::com::sun::star::datatransfer::clipboard::XClipboardOwner >() ); + aGuard.reset(); + bCancel = false; + } + } + } + if( bCancel ) + { + // cancel drag + DragSourceDropEvent dsde; + dsde.Source = static_cast< OWeakObject* >(this); + dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this ); + dsde.DragSource = static_cast< XDragSource* >(this); + dsde.DropAction = DNDConstants::ACTION_NONE; + dsde.DropSuccess = sal_False; + css::uno::Reference< XDragSourceListener > xListener( m_xDragSourceListener ); + m_xDragSourceListener.clear(); + aGuard.clear(); + xListener->dragDropEnd( dsde ); + bHandled = true; + } + } + return bHandled; +} + +// ------------------------------------------------------------------------ + +void SelectionManager::accept( sal_Int8 dragOperation, XLIB_Window aDropWindow, XLIB_Time ) +{ + if( aDropWindow == m_aCurrentDropWindow ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "accept: %x\n", dragOperation ); +#endif + Atom nAction = None; + dragOperation &= (DNDConstants::ACTION_MOVE | DNDConstants::ACTION_COPY | DNDConstants::ACTION_LINK); + if( dragOperation & DNDConstants::ACTION_MOVE ) + nAction = m_nXdndActionMove; + else if( dragOperation & DNDConstants::ACTION_COPY ) + nAction = m_nXdndActionCopy; + else if( dragOperation & DNDConstants::ACTION_LINK ) + nAction = m_nXdndActionLink; + m_bLastDropAccepted = true; + sendDragStatus( nAction ); + } +} + +// ------------------------------------------------------------------------ + +void SelectionManager::reject( XLIB_Window aDropWindow, XLIB_Time ) +{ + if( aDropWindow == m_aCurrentDropWindow ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "reject\n" ); +#endif + m_bLastDropAccepted = false; + sendDragStatus( None ); + if( m_bDropSent && m_xDragSourceListener.is() ) + { + DragSourceDropEvent dsde; + dsde.Source = static_cast< OWeakObject* >(this); + dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this ); + dsde.DragSource = static_cast< XDragSource* >(this); + dsde.DropAction = DNDConstants::ACTION_NONE; + dsde.DropSuccess = sal_False; + m_xDragSourceListener->dragDropEnd( dsde ); + m_xDragSourceListener.clear(); + } + } +} + +/* + * XDragSource + */ + +sal_Bool SelectionManager::isDragImageSupported() throw() +{ + return sal_False; +} + +// ------------------------------------------------------------------------ + +sal_Int32 SelectionManager::getDefaultCursor( sal_Int8 dragAction ) throw() +{ + XLIB_Cursor aCursor = m_aNoneCursor; + if( dragAction & DNDConstants::ACTION_MOVE ) + aCursor = m_aMoveCursor; + else if( dragAction & DNDConstants::ACTION_COPY ) + aCursor = m_aCopyCursor; + else if( dragAction & DNDConstants::ACTION_LINK ) + aCursor = m_aLinkCursor; + return aCursor; +} + +// ------------------------------------------------------------------------ + +int SelectionManager::getXdndVersion( XLIB_Window aWindow, XLIB_Window& rProxy ) +{ + Atom* pProperties = NULL; + int nProperties = 0; + Atom nType; + int nFormat; + unsigned long nItems, nBytes; + unsigned char* pBytes = NULL; + + int nVersion = -1; + rProxy = None; + + /* + * XListProperties is used here to avoid unnecessary XGetWindowProperty calls + * and therefore reducing latency penalty + */ + pProperties = XListProperties( m_pDisplay, aWindow, &nProperties ); + // first look for proxy + int i; + for( i = 0; i < nProperties; i++ ) + { + if( pProperties[i] == m_nXdndProxy ) + { + XGetWindowProperty( m_pDisplay, aWindow, m_nXdndProxy, 0, 1, False, XA_WINDOW, + &nType, &nFormat, &nItems, &nBytes, &pBytes ); + if( pBytes ) + { + if( nType == XA_WINDOW ) + rProxy = *(XLIB_Window*)pBytes; + XFree( pBytes ); + pBytes = NULL; + if( rProxy != None ) + { + // now check proxy wether it points to itself + XGetWindowProperty( m_pDisplay, rProxy, m_nXdndProxy, 0, 1, False, XA_WINDOW, + &nType, &nFormat, &nItems, &nBytes, &pBytes ); + if( pBytes ) + { + if( nType == XA_WINDOW && *(XLIB_Window*)pBytes != rProxy ) + rProxy = None; + XFree( pBytes ); + pBytes = NULL; + } + else + rProxy = None; + } + } + break; + } + } + XLIB_Window aAwareWindow = rProxy != None ? rProxy : aWindow; + + XGetWindowProperty( m_pDisplay, aAwareWindow, m_nXdndAware, 0, 1, False, XA_ATOM, + &nType, &nFormat, &nItems, &nBytes, &pBytes ); + if( pBytes ) + { + if( nType == XA_ATOM ) + nVersion = *(Atom*)pBytes; + XFree( pBytes ); + } + + nVersion = nVersion > nXdndProtocolRevision ? nXdndProtocolRevision : nVersion; + + return nVersion; +} + +// ------------------------------------------------------------------------ + +void SelectionManager::updateDragWindow( int nX, int nY, XLIB_Window aRoot ) +{ + osl::ResettableMutexGuard aGuard( m_aMutex ); + + css::uno::Reference< XDragSourceListener > xListener( m_xDragSourceListener ); + + m_nLastDragX = nX; + m_nLastDragY = nY; + + XLIB_Window aParent = aRoot; + XLIB_Window aChild; + XLIB_Window aNewProxy = None, aNewCurrentWindow = None; + int nNewProtocolVersion = -1; + int nWinX, nWinY; + + // find the first XdndAware window or check if root window is + // XdndAware or has XdndProxy + do + { + XTranslateCoordinates( m_pDisplay, aRoot, aParent, nX, nY, &nWinX, &nWinY, &aChild ); + if( aChild != None ) + { + if( aChild == m_aCurrentDropWindow && aChild != aRoot && m_nCurrentProtocolVersion >= 0 ) + { + aParent = aChild; + break; + } + nNewProtocolVersion = getXdndVersion( aChild, aNewProxy ); + aParent = aChild; + } + } while( aChild != None && nNewProtocolVersion < 0 ); + + aNewCurrentWindow = aParent; + if( aNewCurrentWindow == aRoot ) + { + // no children, try root drop + nNewProtocolVersion = getXdndVersion( aNewCurrentWindow, aNewProxy ); + if( nNewProtocolVersion < 3 ) + { + aNewCurrentWindow = aNewProxy = None; + nNewProtocolVersion = nXdndProtocolRevision; + } + } + + + DragSourceDragEvent dsde; + dsde.Source = static_cast< OWeakObject* >(this); + dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this ); + dsde.DragSource = static_cast< XDragSource* >(this); + dsde.DropAction = nNewProtocolVersion >= 0 ? m_nUserDragAction : DNDConstants::ACTION_COPY; + dsde.UserAction = nNewProtocolVersion >= 0 ? m_nUserDragAction : DNDConstants::ACTION_COPY; + + ::boost::unordered_map< XLIB_Window, DropTargetEntry >::const_iterator it; + if( aNewCurrentWindow != m_aDropWindow ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "drag left window 0x%lx (rev. %d), entered window 0x%lx (rev %d)\n", m_aDropWindow, m_nCurrentProtocolVersion, aNewCurrentWindow, nNewProtocolVersion ); +#endif + + if( m_aDropWindow != None ) + { + it = m_aDropTargets.find( m_aDropWindow ); + if( it != m_aDropTargets.end() ) + // shortcut for own drop targets + { + DropTargetEvent dte; + dte.Source = static_cast< OWeakObject* >( it->second.m_pTarget ); + aGuard.clear(); + it->second.m_pTarget->dragExit( dte ); + aGuard.reset(); + } + else + { + // send old drop target a XdndLeave + XEvent aEvent; + aEvent.type = ClientMessage; + aEvent.xclient.display = m_pDisplay; + aEvent.xclient.format = 32; + aEvent.xclient.message_type = m_nXdndLeave; + aEvent.xclient.window = m_aDropWindow; + aEvent.xclient.data.l[0] = m_aWindow; + aEvent.xclient.data.l[1] = 0; + XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent ); + } + if( xListener.is() ) + { + aGuard.clear(); + xListener->dragExit( dsde ); + aGuard.reset(); + } + } + + m_nCurrentProtocolVersion = nNewProtocolVersion; + m_aDropWindow = aNewCurrentWindow; + m_aDropProxy = aNewProxy != None ? aNewProxy : m_aDropWindow; + + it = m_aDropTargets.find( m_aDropWindow ); + if( it != m_aDropTargets.end() && ! it->second.m_pTarget->m_bActive ) + m_aDropProxy = None; + + if( m_aDropProxy != None && xListener.is() ) + { + aGuard.clear(); + xListener->dragEnter( dsde ); + aGuard.reset(); + } + // send XdndEnter + if( m_aDropProxy != None && m_nCurrentProtocolVersion >= 0 ) + { + it = m_aDropTargets.find( m_aDropWindow ); + if( it != m_aDropTargets.end() ) + { + XTranslateCoordinates( m_pDisplay, aRoot, m_aDropWindow, nX, nY, &nWinX, &nWinY, &aChild ); + DropTargetDragEnterEvent dtde; + dtde.Source = static_cast< OWeakObject* >( it->second.m_pTarget ); + dtde.Context = new DropTargetDragContext( m_aCurrentDropWindow, m_nDropTimestamp, *this ); + dtde.LocationX = nWinX; + dtde.LocationY = nWinY; + dtde.DropAction = m_nUserDragAction; + dtde.SourceActions = m_nSourceActions; + dtde.SupportedDataFlavors = m_xDragSourceTransferable->getTransferDataFlavors(); + aGuard.clear(); + it->second.m_pTarget->dragEnter( dtde ); + aGuard.reset(); + } + else + { + XEvent aEvent; + aEvent.type = ClientMessage; + aEvent.xclient.display = m_pDisplay; + aEvent.xclient.format = 32; + aEvent.xclient.message_type = m_nXdndEnter; + aEvent.xclient.window = m_aDropWindow; + aEvent.xclient.data.l[0] = m_aWindow; + aEvent.xclient.data.l[1] = m_nCurrentProtocolVersion << 24; + memset( aEvent.xclient.data.l + 2, 0, sizeof( long )*3 ); + // fill in data types + ::std::list< Atom > aConversions; + getNativeTypeList( m_aDragFlavors, aConversions, m_nXdndSelection ); + if( aConversions.size() > 3 ) + aEvent.xclient.data.l[1] |= 1; + ::std::list< Atom >::const_iterator type_it = aConversions.begin(); + for( int i = 0; type_it != aConversions.end() && i < 3; i++, ++type_it ) + aEvent.xclient.data.l[i+2] = *type_it; + XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent ); + } + } + m_nNoPosX = m_nNoPosY = m_nNoPosWidth = m_nNoPosHeight = 0; + } + else if( m_aDropProxy != None && xListener.is() ) + { + aGuard.clear(); + // drag over for XdndAware windows comes when receiving XdndStatus + xListener->dragOver( dsde ); + } +} + +// ------------------------------------------------------------------------ + +void SelectionManager::startDrag( + const DragGestureEvent& trigger, + sal_Int8 sourceActions, + sal_Int32, + sal_Int32, + const css::uno::Reference< XTransferable >& transferable, + const css::uno::Reference< XDragSourceListener >& listener + ) throw() +{ +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "startDrag( sourceActions = %x )\n", (int)sourceActions ); +#endif + + DragSourceDropEvent aDragFailedEvent; + aDragFailedEvent.Source = static_cast< OWeakObject* >(this); + aDragFailedEvent.DragSource = static_cast< XDragSource* >(this); + aDragFailedEvent.DragSourceContext = new DragSourceContext( None, CurrentTime, *this ); + aDragFailedEvent.DropAction = DNDConstants::ACTION_NONE; + aDragFailedEvent.DropSuccess = sal_False; + + if( m_aDragRunning.check() ) + { + if( listener.is() ) + listener->dragDropEnd( aDragFailedEvent ); + +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "*** ERROR *** second drag and drop started.\n" ); + if( m_xDragSourceListener.is() ) + fprintf( stderr, "*** ERROR *** drag source listener already set.\n" ); + else + fprintf( stderr, "*** ERROR *** drag thread already running.\n" ); +#endif + return; + } + + SalFrame* pCaptureFrame = NULL; + + { + osl::ClearableMutexGuard aGuard(m_aMutex); + + // first get the current pointer position and the window that + // the pointer is located in. since said window should be one + // of our DropTargets at the time of executeDrag we can use + // them for a start + XLIB_Window aRoot, aParent, aChild; + int root_x, root_y, win_x, win_y; + unsigned int mask; + + ::boost::unordered_map< XLIB_Window, DropTargetEntry >::const_iterator it; + it = m_aDropTargets.begin(); + while( it != m_aDropTargets.end() ) + { + if( XQueryPointer( m_pDisplay, it->second.m_aRootWindow, + &aRoot, &aParent, + &root_x, &root_y, + &win_x, &win_y, + &mask ) ) + { + aParent = it->second.m_aRootWindow; + break; + } + ++it; + } + + // don't start DnD if there is none of our windows on the same screen as + // the pointer or if no mouse button is pressed + if( it == m_aDropTargets.end() || (mask & (Button1Mask|Button2Mask|Button3Mask)) == 0 ) + { + aGuard.clear(); + if( listener.is() ) + listener->dragDropEnd( aDragFailedEvent ); + return; + } + + // try to find which of our drop targets is the drag source + // if that drop target is deregistered we should stop executing + // the drag (actually this is a poor substitute for an "endDrag" + // method ). + m_aDragSourceWindow = None; + aParent = aRoot = it->second.m_aRootWindow; + do + { + XTranslateCoordinates( m_pDisplay, aRoot, aParent, root_x, root_y, &win_x, &win_y, &aChild ); + if( aChild != None && m_aDropTargets.find( aChild ) != m_aDropTargets.end() ) + { + m_aDragSourceWindow = aChild; +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "found drag source window 0x%lx\n", m_aDragSourceWindow ); +#endif + break; + } + aParent = aChild; + } while( aChild != None ); + + +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "try to grab pointer ... " ); +#endif + int nPointerGrabSuccess = + XGrabPointer( m_pDisplay, it->second.m_aRootWindow, True, + DRAG_EVENT_MASK, + GrabModeAsync, GrabModeAsync, + None, + None, + CurrentTime ); + /* if we could not grab the pointer here, there is a chance + that the pointer is grabbed by the other vcl display (the main loop) + so let's break that grab an reset it later + + remark: this whole code should really be molten into normal vcl so only + one display is used .... + */ + if( nPointerGrabSuccess != GrabSuccess ) + { + osl::SolarMutex& rSolarMutex( Application::GetSolarMutex() ); + if( rSolarMutex.tryToAcquire() ) + { + pCaptureFrame = GetX11SalData()->GetDisplay()->GetCaptureFrame(); + if( pCaptureFrame ) + { + GetX11SalData()->GetDisplay()->CaptureMouse( NULL ); + nPointerGrabSuccess = + XGrabPointer( m_pDisplay, it->second.m_aRootWindow, True, + DRAG_EVENT_MASK, + GrabModeAsync, GrabModeAsync, + None, + None, + CurrentTime ); + } + } + } +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "%d\n", nPointerGrabSuccess ); +#endif +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "try to grab keyboard ... " ); +#endif + int nKeyboardGrabSuccess = + XGrabKeyboard( m_pDisplay, it->second.m_aRootWindow, True, + GrabModeAsync, GrabModeAsync, CurrentTime ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "%d\n", nKeyboardGrabSuccess ); +#endif + if( nPointerGrabSuccess != GrabSuccess || nKeyboardGrabSuccess != GrabSuccess ) + { + if( nPointerGrabSuccess == GrabSuccess ) + XUngrabPointer( m_pDisplay, CurrentTime ); + if( nKeyboardGrabSuccess == GrabSuccess ) + XUngrabKeyboard( m_pDisplay, CurrentTime ); + XFlush( m_pDisplay ); + aGuard.clear(); + if( listener.is() ) + listener->dragDropEnd( aDragFailedEvent ); + if( pCaptureFrame ) + { + osl::SolarMutex& rSolarMutex( Application::GetSolarMutex() ); + if( rSolarMutex.tryToAcquire() ) + GetX11SalData()->GetDisplay()->CaptureMouse( pCaptureFrame ); +#if OSL_DEBUG_LEVEL > 0 + else + OSL_FAIL( "failed to acquire SolarMutex to reset capture frame" ); +#endif + } + return; + } + + m_xDragSourceTransferable = transferable; + m_xDragSourceListener = listener; + m_aDragFlavors = transferable->getTransferDataFlavors(); + m_aCurrentCursor = None; + + requestOwnership( m_nXdndSelection ); + + ::std::list< Atom > aConversions; + ::std::list< Atom >::const_iterator type_it; + getNativeTypeList( m_aDragFlavors, aConversions, m_nXdndSelection ); + + int nTypes = aConversions.size(); + Atom* pTypes = (Atom*)alloca( sizeof(Atom)*nTypes ); + type_it = aConversions.begin(); + for( int n = 0; n < nTypes; n++, ++type_it ) + pTypes[n] = *type_it; + + XChangeProperty( m_pDisplay, m_aWindow, m_nXdndTypeList, XA_ATOM, 32, PropModeReplace, (unsigned char*)pTypes, nTypes ); + + m_nSourceActions = sourceActions | DNDConstants::ACTION_DEFAULT; + m_nUserDragAction = DNDConstants::ACTION_MOVE & m_nSourceActions; + if( ! m_nUserDragAction ) + m_nUserDragAction = DNDConstants::ACTION_COPY & m_nSourceActions; + if( ! m_nUserDragAction ) + m_nUserDragAction = DNDConstants::ACTION_LINK & m_nSourceActions; + m_nTargetAcceptAction = DNDConstants::ACTION_DEFAULT; + m_bDropSent = false; + m_bDropSuccess = false; + m_bWaitingForPrimaryConversion = false; + m_nDragButton = Button1; // default to left button + com::sun::star::awt::MouseEvent aEvent; + if( trigger.Event >>= aEvent ) + { + if( aEvent.Buttons & MouseButton::LEFT ) + m_nDragButton = Button1; + else if( aEvent.Buttons & MouseButton::RIGHT ) + m_nDragButton = Button3; + else if( aEvent.Buttons & MouseButton::MIDDLE ) + m_nDragButton = Button2; + } +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "m_nUserDragAction = %x\n", (int)m_nUserDragAction ); +#endif + updateDragWindow( root_x, root_y, aRoot ); + m_nUserDragAction = ~0; + updateDragAction( mask ); + } + + m_aDragRunning.set(); + m_aDragExecuteThread = osl_createSuspendedThread( call_SelectionManager_runDragExecute, this ); + if( m_aDragExecuteThread ) + osl_resumeThread( m_aDragExecuteThread ); + else + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "osl_createSuspendedThread failed for drag execute\n" ); +#endif + m_xDragSourceListener.clear(); + m_xDragSourceTransferable.clear(); + + m_bDropSent = false; + m_bDropSuccess = false; + m_bWaitingForPrimaryConversion = false; + m_aDropWindow = None; + m_aDropProxy = None; + m_nCurrentProtocolVersion = nXdndProtocolRevision; + m_nNoPosX = 0; + m_nNoPosY = 0; + m_nNoPosWidth = 0; + m_nNoPosHeight = 0; + m_aCurrentCursor = None; + + XUngrabPointer( m_pDisplay, CurrentTime ); + XUngrabKeyboard( m_pDisplay, CurrentTime ); + XFlush( m_pDisplay ); + + if( pCaptureFrame ) + { + osl::SolarMutex& rSolarMutex( Application::GetSolarMutex() ); + if( rSolarMutex.tryToAcquire() ) + GetX11SalData()->GetDisplay()->CaptureMouse( pCaptureFrame ); +#if OSL_DEBUG_LEVEL > 0 + else + OSL_FAIL( "failed to acquire SolarMutex to reset capture frame" ); +#endif + } + + m_aDragRunning.reset(); + + if( listener.is() ) + listener->dragDropEnd( aDragFailedEvent ); + } +} + +void SelectionManager::runDragExecute( void* pThis ) +{ + SelectionManager* This = (SelectionManager*)pThis; + This->dragDoDispatch(); +} + +void SelectionManager::dragDoDispatch() +{ + + // do drag + // m_xDragSourceListener will be cleared on finished drop +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "begin executeDrag dispatching\n" ); +#endif + TimeValue aTVal; + aTVal.Seconds = 0; + aTVal.Nanosec = 200000000; + oslThread aThread = m_aDragExecuteThread; + while( m_xDragSourceListener.is() && ( ! m_bDropSent || time(NULL)-m_nDropTimeout < 5 ) && osl_scheduleThread( aThread ) ) + { + // let the thread in the run method do the dispatching + // just look occasionally here whether drop timed out or is completed + osl_waitThread( &aTVal ); + } +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "end executeDrag dispatching\n" ); +#endif + { + osl::ClearableMutexGuard aGuard(m_aMutex); + + css::uno::Reference< XDragSourceListener > xListener( m_xDragSourceListener ); + css::uno::Reference< XTransferable > xTransferable( m_xDragSourceTransferable ); + m_xDragSourceListener.clear(); + m_xDragSourceTransferable.clear(); + + DragSourceDropEvent dsde; + dsde.Source = static_cast< OWeakObject* >(this); + dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this ); + dsde.DragSource = static_cast< XDragSource* >(this); + dsde.DropAction = DNDConstants::ACTION_NONE; + dsde.DropSuccess = sal_False; + + // cleanup after drag + if( m_bWaitingForPrimaryConversion ) + getAdaptor( XA_PRIMARY )->clearTransferable(); + + m_bDropSent = false; + m_bDropSuccess = false; + m_bWaitingForPrimaryConversion = false; + m_aDropWindow = None; + m_aDropProxy = None; + m_nCurrentProtocolVersion = nXdndProtocolRevision; + m_nNoPosX = 0; + m_nNoPosY = 0; + m_nNoPosWidth = 0; + m_nNoPosHeight = 0; + m_aCurrentCursor = None; + + XUngrabPointer( m_pDisplay, CurrentTime ); + XUngrabKeyboard( m_pDisplay, CurrentTime ); + XFlush( m_pDisplay ); + + m_aDragExecuteThread = NULL; + m_aDragRunning.reset(); + + aGuard.clear(); + if( xListener.is() ) + { + xTransferable.clear(); + xListener->dragDropEnd( dsde ); + } + } + osl_destroyThread( aThread ); +} + +/* + * XDragSourceContext + */ + +sal_Int32 SelectionManager::getCurrentCursor() +{ + return m_aCurrentCursor; +} + +// ------------------------------------------------------------------------ + +void SelectionManager::setCursor( sal_Int32 cursor, XLIB_Window aDropWindow, XLIB_Time ) +{ + osl::MutexGuard aGuard( m_aMutex ); + if( aDropWindow == m_aDropWindow && XLIB_Cursor(cursor) != m_aCurrentCursor ) + { + if( m_xDragSourceListener.is() && ! m_bDropSent ) + { + m_aCurrentCursor = cursor; + XChangeActivePointerGrab( m_pDisplay, DRAG_EVENT_MASK, cursor, CurrentTime ); + XFlush( m_pDisplay ); + } + } +} + +// ------------------------------------------------------------------------ + +void SelectionManager::setImage( sal_Int32, XLIB_Window, XLIB_Time ) +{ +} + +// ------------------------------------------------------------------------ + +void SelectionManager::transferablesFlavorsChanged() +{ + osl::MutexGuard aGuard(m_aMutex); + + m_aDragFlavors = m_xDragSourceTransferable->getTransferDataFlavors(); + int i; + + std::list< Atom > aConversions; + std::list< Atom >::const_iterator type_it; + + getNativeTypeList( m_aDragFlavors, aConversions, m_nXdndSelection ); + + int nTypes = aConversions.size(); + Atom* pTypes = (Atom*)alloca( sizeof(Atom)*aConversions.size() ); + for( i = 0, type_it = aConversions.begin(); type_it != aConversions.end(); ++type_it, i++ ) + pTypes[i] = *type_it; + XChangeProperty( m_pDisplay, m_aWindow, m_nXdndTypeList, XA_ATOM, 32, PropModeReplace, (unsigned char*)pTypes, nTypes ); + + if( m_aCurrentDropWindow != None && m_nCurrentProtocolVersion >= 0 ) + { + // send synthetic leave and enter events + + XEvent aEvent; + + aEvent.type = ClientMessage; + aEvent.xclient.display = m_pDisplay; + aEvent.xclient.format = 32; + aEvent.xclient.window = m_aDropWindow; + aEvent.xclient.data.l[0] = m_aWindow; + + aEvent.xclient.message_type = m_nXdndLeave; + aEvent.xclient.data.l[1] = 0; + XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent ); + + aEvent.xclient.message_type = m_nXdndEnter; + aEvent.xclient.data.l[1] = m_nCurrentProtocolVersion << 24; + memset( aEvent.xclient.data.l + 2, 0, sizeof( long )*3 ); + // fill in data types + if( nTypes > 3 ) + aEvent.xclient.data.l[1] |= 1; + for( int j = 0; j < nTypes && j < 3; j++ ) + aEvent.xclient.data.l[j+2] = pTypes[j]; + + XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent ); + } +} + +/* + * dispatch loop + */ + +// ------------------------------------------------------------------------ + +bool SelectionManager::handleXEvent( XEvent& rEvent ) +{ + /* + * since we are XConnectionListener to a second X display + * to get client messages it is essential not to dispatch + * events twice that we get on both connections + * + * between dispatching ButtonPress and startDrag + * the user can already have released the mouse. The ButtonRelease + * will then be dispatched in VCLs queue and never turn up here. + * Which is not so good, since startDrag will XGrabPointer and + * XGrabKeyboard -> solid lock. + */ + if( rEvent.xany.display != m_pDisplay + && rEvent.type != ClientMessage + && rEvent.type != ButtonPress + && rEvent.type != ButtonRelease + ) + return false; + + bool bHandled = false; + switch (rEvent.type) + { + case SelectionClear: + { + osl::ClearableMutexGuard aGuard(m_aMutex); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "SelectionClear for selection %s\n", + OUStringToOString( getString( rEvent.xselectionclear.selection ), RTL_TEXTENCODING_ISO_8859_1 ).getStr() + ); +#endif + SelectionAdaptor* pAdaptor = getAdaptor( rEvent.xselectionclear.selection ); + boost::unordered_map< Atom, Selection* >::iterator it( m_aSelections.find( rEvent.xselectionclear.selection ) ); + if( it != m_aSelections.end() ) + it->second->m_bOwner = false; + aGuard.clear(); + if ( pAdaptor ) + pAdaptor->clearTransferable(); + } + break; + + case SelectionRequest: + bHandled = handleSelectionRequest( rEvent.xselectionrequest ); + break; + case PropertyNotify: + if( rEvent.xproperty.window == m_aWindow || + rEvent.xproperty.window == m_aCurrentDropWindow + ) + bHandled = handleReceivePropertyNotify( rEvent.xproperty ); + else + bHandled = handleSendPropertyNotify( rEvent.xproperty ); + break; + case SelectionNotify: + bHandled = handleSelectionNotify( rEvent.xselection ); + break; + case ClientMessage: + // messages from drag target + if( rEvent.xclient.message_type == m_nXdndStatus || + rEvent.xclient.message_type == m_nXdndFinished ) + bHandled = handleDragEvent( rEvent ); + // messages from drag source + else if( + rEvent.xclient.message_type == m_nXdndEnter || + rEvent.xclient.message_type == m_nXdndLeave || + rEvent.xclient.message_type == m_nXdndPosition || + rEvent.xclient.message_type == m_nXdndDrop + ) + bHandled = handleDropEvent( rEvent.xclient ); + break; + case EnterNotify: + case LeaveNotify: + case MotionNotify: + case ButtonPress: + case ButtonRelease: + case XLIB_KeyPress: + case KeyRelease: + bHandled = handleDragEvent( rEvent ); + break; + default: + ; + } + return bHandled; +} + +// ------------------------------------------------------------------------ + +void SelectionManager::dispatchEvent( int millisec ) +{ + // acquire the mutex to prevent other threads + // from using the same X connection + osl::ResettableMutexGuard aGuard(m_aMutex); + + if( !XPending( m_pDisplay )) + { // wait for any events if none are already queued + pollfd aPollFD; + aPollFD.fd = XConnectionNumber( m_pDisplay ); + aPollFD.events = POLLIN; + aPollFD.revents = 0; + // release mutex for the time of waiting for possible data + aGuard.clear(); + if( poll( &aPollFD, 1, millisec ) <= 0 ) + return; + aGuard.reset(); + } + while( XPending( m_pDisplay )) + { + XEvent event; + XNextEvent( m_pDisplay, &event ); + aGuard.clear(); + handleXEvent( event ); + aGuard.reset(); + } +} + +// ------------------------------------------------------------------------ + +void SelectionManager::run( void* pThis ) +{ +#if OSL_DEBUG_LEVEL > 1 + fprintf(stderr, "SelectionManager::run\n" ); +#endif + // dispatch until the cows come home + + SelectionManager* This = (SelectionManager*)pThis; + + timeval aLast; + gettimeofday( &aLast, 0 ); + + css::uno::Reference< XMultiServiceFactory > xFact( ::comphelper::getProcessServiceFactory() ); + if( xFact.is() ) + { + css::uno::Reference< XDesktop > xDesktop( xFact->createInstance( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.frame.Desktop")) ), UNO_QUERY ); + if( xDesktop.is() ) + xDesktop->addTerminateListener(This); + } + + while( osl_scheduleThread(This->m_aThread) ) + { + This->dispatchEvent( 1000 ); + + timeval aNow; + gettimeofday( &aNow, 0 ); + + if( (aNow.tv_sec - aLast.tv_sec) > 0 ) + { + osl::ClearableMutexGuard aGuard(This->m_aMutex); + std::list< std::pair< SelectionAdaptor*, css::uno::Reference< XInterface > > > aChangeList; + + for( boost::unordered_map< Atom, Selection* >::iterator it = This->m_aSelections.begin(); it != This->m_aSelections.end(); ++it ) + { + if( it->first != This->m_nXdndSelection && ! it->second->m_bOwner ) + { + XLIB_Window aOwner = XGetSelectionOwner( This->m_pDisplay, it->first ); + if( aOwner != it->second->m_aLastOwner ) + { + it->second->m_aLastOwner = aOwner; + std::pair< SelectionAdaptor*, css::uno::Reference< XInterface > > + aKeep( it->second->m_pAdaptor, it->second->m_pAdaptor->getReference() ); + aChangeList.push_back( aKeep ); + } + } + } + aGuard.clear(); + while( aChangeList.begin() != aChangeList.end() ) + { + aChangeList.front().first->fireContentsChanged(); + aChangeList.pop_front(); + } + aLast = aNow; + } + } +#if OSL_DEBUG_LEVEL > 1 + fprintf(stderr, "SelectionManager::run end\n" ); +#endif +} + +void SelectionManager::shutdown() throw() +{ + osl::ResettableMutexGuard aGuard(m_aMutex); + if( m_bShutDown ) + { + return; + } + m_bShutDown = true; + // stop dispatching + if( m_aThread ) + { + osl_terminateThread( m_aThread ); + /* + * Allow thread to finish before app exits to avoid pulling the carpet + * out from under it if pasting is occuring during shutdown + * + * a) allow it to have the Mutex and + * b) reschedule to allow it to complete callbacks to any + * Application::GetSolarMutex protected regions, etc. e.g. + * TransferableHelper::getTransferDataFlavors (via + * SelectionManager::handleSelectionRequest) which it might + * currently be trying to enter. + * + * Otherwise the thread may be left still waiting on a GlobalMutex + * when that gets destroyed, letting the thread blow up and die + * when enters the section in a now dead OOo instance. + */ + aGuard.clear(); + while (osl_isThreadRunning(m_aThread)) + { + SolarMutexGuard guard2; + Application::Reschedule(); + } + osl_joinWithThread( m_aThread ); + osl_destroyThread( m_aThread ); + m_aThread = NULL; + aGuard.reset(); + } + m_xDisplayConnection->removeEventHandler( Any(), this ); + m_xDisplayConnection.clear(); +} + +// ------------------------------------------------------------------------ + +sal_Bool SelectionManager::handleEvent( const Any& event ) throw() +{ + Sequence< sal_Int8 > aSeq; + if( (event >>= aSeq) ) + { + XEvent* pEvent = (XEvent*)aSeq.getArray(); + XLIB_Time nTimestamp = CurrentTime; + if( pEvent->type == ButtonPress || pEvent->type == ButtonRelease ) + nTimestamp = pEvent->xbutton.time; + else if( pEvent->type == XLIB_KeyPress || pEvent->type == KeyRelease ) + nTimestamp = pEvent->xkey.time; + else if( pEvent->type == MotionNotify ) + nTimestamp = pEvent->xmotion.time; + else if( pEvent->type == PropertyNotify ) + nTimestamp = pEvent->xproperty.time; + + if( nTimestamp != CurrentTime ) + { + osl::MutexGuard aGuard(m_aMutex); + + m_nSelectionTimestamp = nTimestamp; + } + + return sal_Bool( handleXEvent( *pEvent ) ); + } + else + { + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "SelectionManager got downing event\n" ); + #endif + shutdown(); + } + return sal_True; +} + +void SAL_CALL SelectionManager::disposing( const ::com::sun::star::lang::EventObject& ) + throw( ::com::sun::star::uno::RuntimeException ) +{ +} + +void SAL_CALL SelectionManager::queryTermination( const ::com::sun::star::lang::EventObject& ) + throw( ::com::sun::star::frame::TerminationVetoException, ::com::sun::star::uno::RuntimeException ) +{ +} + +/* + * To be safe, shutdown needs to be called before the ~SfxApplication is called, waiting until + * the downing event can be too late if paste are requested during shutdown and ~SfxApplication + * has been called before vcl is shutdown + */ +void SAL_CALL SelectionManager::notifyTermination( const ::com::sun::star::lang::EventObject& rEvent ) + throw( ::com::sun::star::uno::RuntimeException ) +{ + css::uno::Reference< XDesktop > xDesktop( rEvent.Source, UNO_QUERY ); + if( xDesktop.is() == sal_True ) + xDesktop->removeTerminateListener( this ); + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "SelectionManager got app termination event\n" ); + #endif + shutdown(); +} + +// ------------------------------------------------------------------------ + +void SelectionManager::registerHandler( Atom selection, SelectionAdaptor& rAdaptor ) +{ + osl::MutexGuard aGuard(m_aMutex); + + Selection* pNewSelection = new Selection(); + pNewSelection->m_pAdaptor = &rAdaptor; + pNewSelection->m_aAtom = selection; + m_aSelections[ selection ] = pNewSelection; +} + +// ------------------------------------------------------------------------ + +void SelectionManager::deregisterHandler( Atom selection ) +{ + osl::MutexGuard aGuard(m_aMutex); + + ::boost::unordered_map< Atom, Selection* >::iterator it = + m_aSelections.find( selection ); + if( it != m_aSelections.end() ) + { + delete it->second->m_pPixmap; + delete it->second; + m_aSelections.erase( it ); + } +} + +// ------------------------------------------------------------------------ + +static bool bWasError = false; + +extern "C" +{ + int local_xerror_handler(Display* , XErrorEvent*) + { + bWasError = true; + return 0; + } + typedef int(*xerror_hdl_t)(Display*,XErrorEvent*); +} + +void SelectionManager::registerDropTarget( XLIB_Window aWindow, DropTarget* pTarget ) +{ + osl::MutexGuard aGuard(m_aMutex); + + // sanity check + ::boost::unordered_map< XLIB_Window, DropTargetEntry >::const_iterator it = + m_aDropTargets.find( aWindow ); + if( it != m_aDropTargets.end() ) + OSL_FAIL( "attempt to register window as drop target twice" ); + else if( aWindow && m_pDisplay ) + { + DropTargetEntry aEntry( pTarget ); + bWasError=false; + /* #i100000# ugly workaround: gtk sets its own XErrorHandler which is not suitable for us + unfortunately XErrorHandler is not per display, so this is just and ugly hack + Need to remove separate display and integrate clipboard/dnd into vcl's unx code ASAP + */ + xerror_hdl_t pOldHandler = XSetErrorHandler( local_xerror_handler ); + XSelectInput( m_pDisplay, aWindow, PropertyChangeMask ); + if( ! bWasError ) + { + // set XdndAware + XChangeProperty( m_pDisplay, aWindow, m_nXdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char*)&nXdndProtocolRevision, 1 ); + if( ! bWasError ) + { + // get root window of window (in 99.999% of all cases this will be + // DefaultRootWindow( m_pDisplay ) + int x, y; + unsigned int w, h, bw, d; + XGetGeometry( m_pDisplay, aWindow, &aEntry.m_aRootWindow, + &x, &y, &w, &h, &bw, &d ); + } + } + XSetErrorHandler( pOldHandler ); + if(bWasError) + return; + m_aDropTargets[ aWindow ] = aEntry; + } + else + OSL_FAIL( "attempt to register None as drop target" ); +} + +// ------------------------------------------------------------------------ + +void SelectionManager::deregisterDropTarget( XLIB_Window aWindow ) +{ + osl::ClearableMutexGuard aGuard(m_aMutex); + + m_aDropTargets.erase( aWindow ); + if( aWindow == m_aDragSourceWindow && m_aDragRunning.check() ) + { + // abort drag + boost::unordered_map< XLIB_Window, DropTargetEntry >::const_iterator it = + m_aDropTargets.find( m_aDropWindow ); + if( it != m_aDropTargets.end() ) + { + DropTargetEvent dte; + dte.Source = static_cast< OWeakObject* >( it->second.m_pTarget ); + aGuard.clear(); + it->second.m_pTarget->dragExit( dte ); + } + else if( m_aDropProxy != None && m_nCurrentProtocolVersion >= 0 ) + { + // send XdndLeave + XEvent aEvent; + aEvent.type = ClientMessage; + aEvent.xclient.display = m_pDisplay; + aEvent.xclient.format = 32; + aEvent.xclient.message_type = m_nXdndLeave; + aEvent.xclient.window = m_aDropWindow; + aEvent.xclient.data.l[0] = m_aWindow; + memset( aEvent.xclient.data.l+1, 0, sizeof(long)*4); + m_aDropWindow = m_aDropProxy = None; + XSendEvent( m_pDisplay, m_aDropProxy, False, NoEventMask, &aEvent ); + } + // notify the listener + DragSourceDropEvent dsde; + dsde.Source = static_cast< OWeakObject* >(this); + dsde.DragSourceContext = new DragSourceContext( m_aDropWindow, m_nDragTimestamp, *this ); + dsde.DragSource = static_cast< XDragSource* >(this); + dsde.DropAction = DNDConstants::ACTION_NONE; + dsde.DropSuccess = sal_False; + css::uno::Reference< XDragSourceListener > xListener( m_xDragSourceListener ); + m_xDragSourceListener.clear(); + aGuard.clear(); + xListener->dragDropEnd( dsde ); + } +} + +/* + * SelectionAdaptor + */ + +css::uno::Reference< XTransferable > SelectionManager::getTransferable() throw() +{ + return m_xDragSourceTransferable; +} + +// ------------------------------------------------------------------------ + +void SelectionManager::clearTransferable() throw() +{ + m_xDragSourceTransferable.clear(); +} + +// ------------------------------------------------------------------------ + +void SelectionManager::fireContentsChanged() throw() +{ +} + +// ------------------------------------------------------------------------ + +css::uno::Reference< XInterface > SelectionManager::getReference() throw() +{ + return css::uno::Reference< XInterface >( static_cast<OWeakObject*>(this) ); +} + +// ------------------------------------------------------------------------ + +/* + * SelectionManagerHolder + */ + +SelectionManagerHolder::SelectionManagerHolder() : + ::cppu::WeakComponentImplHelper3< + XDragSource, + XInitialization, + XServiceInfo > (m_aMutex) +{ +} + +// ------------------------------------------------------------------------ + +SelectionManagerHolder::~SelectionManagerHolder() +{ +} + +// ------------------------------------------------------------------------ + +void SelectionManagerHolder::initialize( const Sequence< Any >& arguments ) throw( ::com::sun::star::uno::Exception ) +{ + OUString aDisplayName; + + if( arguments.getLength() > 0 ) + { + css::uno::Reference< XDisplayConnection > xConn; + arguments.getConstArray()[0] >>= xConn; + if( xConn.is() ) + { + Any aIdentifier; + aIdentifier >>= aDisplayName; + } + } + + SelectionManager& rManager = SelectionManager::get( aDisplayName ); + rManager.initialize( arguments ); + m_xRealDragSource = static_cast< XDragSource* >(&rManager); +} + +/* + * XDragSource + */ + +sal_Bool SelectionManagerHolder::isDragImageSupported() throw() +{ + return m_xRealDragSource.is() ? m_xRealDragSource->isDragImageSupported() : sal_False; +} + +// ------------------------------------------------------------------------ + +sal_Int32 SelectionManagerHolder::getDefaultCursor( sal_Int8 dragAction ) throw() +{ + return m_xRealDragSource.is() ? m_xRealDragSource->getDefaultCursor( dragAction ) : 0; +} + +// ------------------------------------------------------------------------ + +void SelectionManagerHolder::startDrag( + const ::com::sun::star::datatransfer::dnd::DragGestureEvent& trigger, + sal_Int8 sourceActions, sal_Int32 cursor, sal_Int32 image, + const css::uno::Reference< ::com::sun::star::datatransfer::XTransferable >& transferable, + const css::uno::Reference< ::com::sun::star::datatransfer::dnd::XDragSourceListener >& listener + ) throw() +{ + if( m_xRealDragSource.is() ) + m_xRealDragSource->startDrag( trigger, sourceActions, cursor, image, transferable, listener ); +} + +// ------------------------------------------------------------------------ + +/* + * XServiceInfo + */ + +// ------------------------------------------------------------------------ + +OUString SelectionManagerHolder::getImplementationName() throw() +{ + return OUString(RTL_CONSTASCII_USTRINGPARAM(XDND_IMPLEMENTATION_NAME)); +} + +// ------------------------------------------------------------------------ + +sal_Bool SelectionManagerHolder::supportsService( const OUString& ServiceName ) throw() +{ + Sequence < OUString > SupportedServicesNames = Xdnd_getSupportedServiceNames(); + + for ( sal_Int32 n = SupportedServicesNames.getLength(); n--; ) + if (SupportedServicesNames[n].compareTo(ServiceName) == 0) + return sal_True; + + return sal_False; +} + +// ------------------------------------------------------------------------ + +Sequence< OUString > SelectionManagerHolder::getSupportedServiceNames() throw() +{ + return Xdnd_getSupportedServiceNames(); +} + + +// ------------------------------------------------------------------------ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/dtrans/X11_selection.hxx b/vcl/unx/generic/dtrans/X11_selection.hxx new file mode 100644 index 000000000000..42158e99802b --- /dev/null +++ b/vcl/unx/generic/dtrans/X11_selection.hxx @@ -0,0 +1,533 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _DTRANS_X11_SELECTION_HXX_ +#define _DTRANS_X11_SELECTION_HXX_ + +#include <cppuhelper/compbase3.hxx> +#include <cppuhelper/compbase4.hxx> +#include <com/sun/star/datatransfer/XTransferable.hpp> +#include <com/sun/star/datatransfer/dnd/XDropTarget.hpp> +#include <com/sun/star/datatransfer/dnd/XDragSource.hpp> +#include <com/sun/star/awt/XDisplayConnection.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/script/XInvocation.hpp> +#include <com/sun/star/frame/XDesktop.hpp> +#include <osl/thread.h> + +#include <osl/conditn.hxx> + +#include <boost/unordered_map.hpp> +#include <list> + +#include "tools/prex.h" +#include <X11/Xlib.h> +#include "tools/postx.h" + +#define XDND_IMPLEMENTATION_NAME "com.sun.star.datatransfer.dnd.XdndSupport" +#define XDND_DROPTARGET_IMPLEMENTATION_NAME "com.sun.star.datatransfer.dnd.XdndDropTarget" + +using namespace ::com::sun::star::uno; + +namespace x11 { + + class PixmapHolder; // in bmp.hxx + +// ------------------------------------------------------------------------ + rtl_TextEncoding getTextPlainEncoding( const ::rtl::OUString& rMimeType ); + + class SelectionAdaptor + { + public: + virtual com::sun::star::uno::Reference< ::com::sun::star::datatransfer::XTransferable > getTransferable() = 0; + virtual void clearTransferable() = 0; + virtual void fireContentsChanged() = 0; + virtual com::sun::star::uno::Reference< XInterface > getReference() = 0; + // returns a reference that will keep the SelectionAdaptor alive until the + // refernce is released + }; + + class DropTarget : + public ::cppu::WeakComponentImplHelper3< + ::com::sun::star::datatransfer::dnd::XDropTarget, + ::com::sun::star::lang::XInitialization, + ::com::sun::star::lang::XServiceInfo + > + { + public: + ::osl::Mutex m_aMutex; + bool m_bActive; + sal_Int8 m_nDefaultActions; + XLIB_Window m_aTargetWindow; + class SelectionManager* m_pSelectionManager; + com::sun::star::uno::Reference< ::com::sun::star::datatransfer::dnd::XDragSource > + m_xSelectionManager; + ::std::list< com::sun::star::uno::Reference< ::com::sun::star::datatransfer::dnd::XDropTargetListener > > + m_aListeners; + + DropTarget(); + virtual ~DropTarget(); + + // convenience functions that loop over listeners + void dragEnter( const ::com::sun::star::datatransfer::dnd::DropTargetDragEnterEvent& dtde ) throw(); + void dragExit( const ::com::sun::star::datatransfer::dnd::DropTargetEvent& dte ) throw(); + void dragOver( const ::com::sun::star::datatransfer::dnd::DropTargetDragEvent& dtde ) throw(); + void drop( const ::com::sun::star::datatransfer::dnd::DropTargetDropEvent& dtde ) throw(); + + // XInitialization + virtual void SAL_CALL initialize( const Sequence< Any >& args ) throw ( ::com::sun::star::uno::Exception ); + + // XDropTarget + virtual void SAL_CALL addDropTargetListener( const com::sun::star::uno::Reference< ::com::sun::star::datatransfer::dnd::XDropTargetListener >& ) throw(); + virtual void SAL_CALL removeDropTargetListener( const com::sun::star::uno::Reference< ::com::sun::star::datatransfer::dnd::XDropTargetListener >& ) throw(); + virtual sal_Bool SAL_CALL isActive() throw(); + virtual void SAL_CALL setActive( sal_Bool active ) throw(); + virtual sal_Int8 SAL_CALL getDefaultActions() throw(); + virtual void SAL_CALL setDefaultActions( sal_Int8 actions ) throw(); + + // XServiceInfo + virtual ::rtl::OUString SAL_CALL getImplementationName() throw(); + virtual sal_Bool SAL_CALL supportsService( const ::rtl::OUString& ServiceName ) throw(); + virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > + SAL_CALL getSupportedServiceNames() throw(); + }; + + class SelectionManagerHolder : + public ::cppu::WeakComponentImplHelper3< + ::com::sun::star::datatransfer::dnd::XDragSource, + ::com::sun::star::lang::XInitialization, + ::com::sun::star::lang::XServiceInfo + > + { + ::osl::Mutex m_aMutex; + com::sun::star::uno::Reference< ::com::sun::star::datatransfer::dnd::XDragSource > + m_xRealDragSource; + public: + SelectionManagerHolder(); + virtual ~SelectionManagerHolder(); + + // XServiceInfo + virtual ::rtl::OUString SAL_CALL getImplementationName() throw(); + virtual sal_Bool SAL_CALL supportsService( const ::rtl::OUString& ServiceName ) throw(); + virtual ::com::sun::star::uno::Sequence< ::rtl::OUString > + SAL_CALL getSupportedServiceNames() throw(); + + // XInitialization + virtual void SAL_CALL initialize( const Sequence< Any >& arguments ) throw( ::com::sun::star::uno::Exception ); + + // XDragSource + virtual sal_Bool SAL_CALL isDragImageSupported() throw(); + virtual sal_Int32 SAL_CALL getDefaultCursor( sal_Int8 dragAction ) throw(); + virtual void SAL_CALL startDrag( + const ::com::sun::star::datatransfer::dnd::DragGestureEvent& trigger, + sal_Int8 sourceActions, sal_Int32 cursor, sal_Int32 image, + const com::sun::star::uno::Reference< ::com::sun::star::datatransfer::XTransferable >& transferable, + const com::sun::star::uno::Reference< ::com::sun::star::datatransfer::dnd::XDragSourceListener >& listener + ) throw(); + + }; + + + class SelectionManager : + public ::cppu::WeakImplHelper4< + ::com::sun::star::datatransfer::dnd::XDragSource, + ::com::sun::star::lang::XInitialization, + ::com::sun::star::awt::XEventHandler, + ::com::sun::star::frame::XTerminateListener + >, + public SelectionAdaptor + { + static ::boost::unordered_map< ::rtl::OUString, SelectionManager*, ::rtl::OUStringHash >& getInstances(); + + // for INCR type selection transfer + // INCR protocol is used if the data cannot + // be transported at once but in parts + // IncrementalTransfer holds the bytes to be transmitted + // as well a the current position + // INCR triggers the delivery of the next part by deleting the + // property used to transfer the data + struct IncrementalTransfer + { + Sequence< sal_Int8 > m_aData; + int m_nBufferPos; + XLIB_Window m_aRequestor; + Atom m_aProperty; + Atom m_aTarget; + int m_nFormat; + int m_nTransferStartTime; + }; + int m_nIncrementalThreshold; + + // a struct to hold the data associated with a selection + struct Selection + { + enum State + { + Inactive, WaitingForResponse, WaitingForData, IncrementalTransfer + }; + + State m_eState; + SelectionAdaptor* m_pAdaptor; + Atom m_aAtom; + ::osl::Condition m_aDataArrived; + Sequence< sal_Int8 > m_aData; + Sequence< ::com::sun::star::datatransfer::DataFlavor > + m_aTypes; + std::vector< Atom > m_aNativeTypes; + // this is used for caching + // m_aTypes is invalid after 2 seconds + // m_aNativeTypes contains the corresponding original atom + Atom m_aRequestedType; + // m_aRequestedType is only valid while WaitingForResponse and WaitingFotData + int m_nLastTimestamp; + bool m_bHaveUTF16; + Atom m_aUTF8Type; + bool m_bHaveCompound; + bool m_bOwner; + XLIB_Window m_aLastOwner; + PixmapHolder* m_pPixmap; + // m_nOrigXLIB_Timestamp contains the XLIB_Timestamp at which the seclection + // was acquired; needed for XLIB_TimeSTAMP target + XLIB_Time m_nOrigTimestamp; + + Selection() : m_eState( Inactive ), + m_pAdaptor( NULL ), + m_aAtom( None ), + m_aRequestedType( None ), + m_nLastTimestamp( 0 ), + m_bHaveUTF16( false ), + m_aUTF8Type( None ), + m_bHaveCompound( false ), + m_bOwner( false ), + m_aLastOwner( None ), + m_pPixmap( NULL ), + m_nOrigTimestamp( CurrentTime ) + {} + }; + + // a struct to hold data associated with a XDropTarget + struct DropTargetEntry + { + DropTarget* m_pTarget; + XLIB_Window m_aRootWindow; + + DropTargetEntry() : m_pTarget( NULL ), m_aRootWindow( None ) {} + DropTargetEntry( DropTarget* pTarget ) : + m_pTarget( pTarget ), + m_aRootWindow( None ) + {} + DropTargetEntry( const DropTargetEntry& rEntry ) : + m_pTarget( rEntry.m_pTarget ), + m_aRootWindow( rEntry.m_aRootWindow ) + {} + ~DropTargetEntry() {} + + DropTarget* operator->() const { return m_pTarget; } + DropTargetEntry& operator=(const DropTargetEntry& rEntry) + { m_pTarget = rEntry.m_pTarget; m_aRootWindow = rEntry.m_aRootWindow; return *this; } + }; + + // internal data + Display* m_pDisplay; + oslThread m_aThread; + oslThread m_aDragExecuteThread; + ::osl::Condition m_aDragRunning; + XLIB_Window m_aWindow; + com::sun::star::uno::Reference< ::com::sun::star::awt::XDisplayConnection > + m_xDisplayConnection; + com::sun::star::uno::Reference< com::sun::star::script::XInvocation > + m_xBitmapConverter; + sal_Int32 m_nSelectionTimeout; + XLIB_Time m_nSelectionTimestamp; + + + // members used for Xdnd + + // drop only + + // contains the XdndEnterEvent of a drop action running + // with one of our targets. The data.l[0] member + // (conatining the drag source XLIB_Window) is set + // to None while that is not the case + XClientMessageEvent m_aDropEnterEvent; + // set to false on XdndEnter + // set to true on first XdndPosition or XdndLeave + bool m_bDropEnterSent; + XLIB_Window m_aCurrentDropWindow; + // XLIB_Time code of XdndDrop + XLIB_Time m_nDropTime; + sal_Int8 m_nLastDropAction; + // XTransferable for Xdnd with foreign drag source + com::sun::star::uno::Reference< ::com::sun::star::datatransfer::XTransferable > + m_xDropTransferable; + int m_nLastX, m_nLastY; + XLIB_Time m_nDropTimestamp; + // set to true when calling drop() + // if another XdndEnter is received this shows that + // someone forgot to call dropComplete - we should reset + // and react to the new drop + bool m_bDropWaitingForCompletion; + + // drag only + + // None if no Dnd action is running with us as source + XLIB_Window m_aDropWindow; + // either m_aDropXLIB_Window or its XdndProxy + XLIB_Window m_aDropProxy; + XLIB_Window m_aDragSourceWindow; + // XTransferable for Xdnd when we are drag source + com::sun::star::uno::Reference< ::com::sun::star::datatransfer::XTransferable > + m_xDragSourceTransferable; + com::sun::star::uno::Reference< ::com::sun::star::datatransfer::dnd::XDragSourceListener > + m_xDragSourceListener; + // root coordinates + int m_nLastDragX, m_nLastDragY; + Sequence< ::com::sun::star::datatransfer::DataFlavor > + m_aDragFlavors; + // the rectangle the pointer must leave until a new XdndPosition should + // be sent. empty unless the drop target told to fill + int m_nNoPosX, m_nNoPosY, m_nNoPosWidth, m_nNoPosHeight; + unsigned int m_nDragButton; + sal_Int8 m_nUserDragAction; + sal_Int8 m_nTargetAcceptAction; + sal_Int8 m_nSourceActions; + bool m_bLastDropAccepted; + bool m_bDropSuccess; + bool m_bDropSent; + time_t m_nDropTimeout; + bool m_bWaitingForPrimaryConversion; + XLIB_Time m_nDragTimestamp; + + // drag cursors + XLIB_Cursor m_aMoveCursor; + XLIB_Cursor m_aCopyCursor; + XLIB_Cursor m_aLinkCursor; + XLIB_Cursor m_aNoneCursor; + XLIB_Cursor m_aCurrentCursor; + + + // drag and drop + + int m_nCurrentProtocolVersion; + ::boost::unordered_map< XLIB_Window, DropTargetEntry > + m_aDropTargets; + + + // some special atoms that are needed often + Atom m_nCLIPBOARDAtom; + Atom m_nTARGETSAtom; + Atom m_nTIMESTAMPAtom; + Atom m_nTEXTAtom; + Atom m_nINCRAtom; + Atom m_nCOMPOUNDAtom; + Atom m_nMULTIPLEAtom; + Atom m_nUTF16Atom; + Atom m_nImageBmpAtom; + Atom m_nXdndAware; + Atom m_nXdndEnter; + Atom m_nXdndLeave; + Atom m_nXdndPosition; + Atom m_nXdndStatus; + Atom m_nXdndDrop; + Atom m_nXdndFinished; + Atom m_nXdndSelection; + Atom m_nXdndTypeList; + Atom m_nXdndProxy; + Atom m_nXdndActionCopy; + Atom m_nXdndActionMove; + Atom m_nXdndActionLink; + Atom m_nXdndActionAsk; + Atom m_nXdndActionPrivate; + + // caching for atoms + ::boost::unordered_map< Atom, ::rtl::OUString > + m_aAtomToString; + ::boost::unordered_map< ::rtl::OUString, Atom, ::rtl::OUStringHash > + m_aStringToAtom; + + // the registered selections + ::boost::unordered_map< Atom, Selection* > + m_aSelections; + // IncrementalTransfers in progress + boost::unordered_map< XLIB_Window, boost::unordered_map< Atom, IncrementalTransfer > > + m_aIncrementals; + + // do not use X11 multithreading capabilities + // since this leads to deadlocks in different Xlib implentations + // (XFree as well as Xsun) use an own mutex instead + ::osl::Mutex m_aMutex; + bool m_bShutDown; + + SelectionManager(); + ~SelectionManager(); + + SelectionAdaptor* getAdaptor( Atom selection ); + PixmapHolder* getPixmapHolder( Atom selection ); + + // handle various events + bool handleSelectionRequest( XSelectionRequestEvent& rRequest ); + bool handleSendPropertyNotify( XPropertyEvent& rNotify ); + bool handleReceivePropertyNotify( XPropertyEvent& rNotify ); + bool handleSelectionNotify( XSelectionEvent& rNotify ); + bool handleDragEvent( XEvent& rMessage ); + bool handleDropEvent( XClientMessageEvent& rMessage ); + + // dnd helpers + void sendDragStatus( Atom nDropAction ); + void sendDropPosition( bool bForce, XLIB_Time eventXLIB_Time ); + bool updateDragAction( int modifierState ); + int getXdndVersion( XLIB_Window aXLIB_Window, XLIB_Window& rProxy ); + XLIB_Cursor createCursor( const unsigned char* pPointerData, const unsigned char* pMaskData, int width, int height, int hotX, int hotY ); + // coordinates on root XLIB_Window + void updateDragWindow( int nX, int nY, XLIB_Window aRoot ); + + bool getPasteData( Atom selection, Atom type, Sequence< sal_Int8 >& rData ); + // returns true if conversion was successful + bool convertData( const com::sun::star::uno::Reference< ::com::sun::star::datatransfer::XTransferable >& xTransferable, + Atom nType, + Atom nSelection, + int & rFormat, + Sequence< sal_Int8 >& rData ); + bool sendData( SelectionAdaptor* pAdaptor, XLIB_Window requestor, Atom target, Atom property, Atom selection ); + + // thread dispatch loop + public: + // public for extern "C" stub + static void run( void* ); + private: + void dispatchEvent( int millisec ); + // drag thread dispatch + public: + // public for extern "C" stub + static void runDragExecute( void* ); + private: + void dragDoDispatch(); + bool handleXEvent( XEvent& rEvent ); + + // compound text conversion + ::rtl::OString convertToCompound( const ::rtl::OUString& rText ); + ::rtl::OUString convertFromCompound( const char* pText, int nLen = -1 ); + + sal_Int8 getUserDragAction() const; + sal_Int32 getSelectionTimeout(); + public: + static SelectionManager& get( const ::rtl::OUString& rDisplayName = ::rtl::OUString() ); + + Display * getDisplay() { return m_pDisplay; }; + XLIB_Window getWindow() { return m_aWindow; }; + + + void registerHandler( Atom selection, SelectionAdaptor& rAdaptor ); + void deregisterHandler( Atom selection ); + bool requestOwnership( Atom selection ); + + // allow for synchronization over one mutex for XClipboard + osl::Mutex& getMutex() { return m_aMutex; } + + + Atom getAtom( const ::rtl::OUString& rString ); + const ::rtl::OUString& getString( Atom nAtom ); + + // type conversion + // note: convertTypeToNative does NOT clear the list, so you can append + // multiple types to the same list + void convertTypeToNative( const ::rtl::OUString& rType, Atom selection, int& rFormat, ::std::list< Atom >& rConversions, bool bPushFront = false ); + ::rtl::OUString convertTypeFromNative( Atom nType, Atom selection, int& rFormat ); + void getNativeTypeList( const Sequence< com::sun::star::datatransfer::DataFlavor >& rTypes, std::list< Atom >& rOutTypeList, Atom targetselection ); + + // methods for transferable + bool getPasteDataTypes( Atom selection, Sequence< ::com::sun::star::datatransfer::DataFlavor >& rTypes ); + bool getPasteData( Atom selection, const ::rtl::OUString& rType, Sequence< sal_Int8 >& rData ); + + // for XDropTarget to register/deregister itself + void registerDropTarget( XLIB_Window aXLIB_Window, DropTarget* pTarget ); + void deregisterDropTarget( XLIB_Window aXLIB_Window ); + + // for XDropTarget{Drag|Drop}Context + void accept( sal_Int8 dragOperation, XLIB_Window aDropXLIB_Window, XLIB_Time aXLIB_Timestamp ); + void reject( XLIB_Window aDropXLIB_Window, XLIB_Time aXLIB_Timestamp ); + void dropComplete( sal_Bool success, XLIB_Window aDropXLIB_Window, XLIB_Time aXLIB_Timestamp ); + + // for XDragSourceContext + sal_Int32 getCurrentCursor(); + void setCursor( sal_Int32 cursor, XLIB_Window aDropXLIB_Window, XLIB_Time aXLIB_Timestamp ); + void setImage( sal_Int32 image, XLIB_Window aDropXLIB_Window, XLIB_Time aXLIB_Timestamp ); + void transferablesFlavorsChanged(); + + void shutdown() throw(); + + // XInitialization + virtual void SAL_CALL initialize( const Sequence< Any >& arguments ) throw( ::com::sun::star::uno::Exception ); + + // XEventHandler + virtual sal_Bool SAL_CALL handleEvent( const Any& event ) throw(); + + // XDragSource + virtual sal_Bool SAL_CALL isDragImageSupported() throw(); + virtual sal_Int32 SAL_CALL getDefaultCursor( sal_Int8 dragAction ) throw(); + virtual void SAL_CALL startDrag( + const ::com::sun::star::datatransfer::dnd::DragGestureEvent& trigger, + sal_Int8 sourceActions, sal_Int32 cursor, sal_Int32 image, + const com::sun::star::uno::Reference< ::com::sun::star::datatransfer::XTransferable >& transferable, + const com::sun::star::uno::Reference< ::com::sun::star::datatransfer::dnd::XDragSourceListener >& listener + ) throw(); + + // SelectionAdaptor for XdndSelection Drag (we are drag source) + virtual com::sun::star::uno::Reference< ::com::sun::star::datatransfer::XTransferable > getTransferable() throw(); + virtual void clearTransferable() throw(); + virtual void fireContentsChanged() throw(); + virtual com::sun::star::uno::Reference< XInterface > getReference() throw(); + + // XEventListener + virtual void SAL_CALL disposing( const ::com::sun::star::lang::EventObject& Source ) throw( ::com::sun::star::uno::RuntimeException ); + + // XTerminateListener + virtual void SAL_CALL queryTermination( const ::com::sun::star::lang::EventObject& aEvent ) + throw( ::com::sun::star::frame::TerminationVetoException, ::com::sun::star::uno::RuntimeException ); + virtual void SAL_CALL notifyTermination( const ::com::sun::star::lang::EventObject& aEvent ) + throw( ::com::sun::star::uno::RuntimeException ); + }; + +// ------------------------------------------------------------------------ + + ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL Xdnd_getSupportedServiceNames(); + ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > SAL_CALL Xdnd_createInstance( + const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > & xMultiServiceFactory); + + ::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL Xdnd_dropTarget_getSupportedServiceNames(); + ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > SAL_CALL Xdnd_dropTarget_createInstance( + const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > & xMultiServiceFactory); + +// ------------------------------------------------------------------------ + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/dtrans/X11_service.cxx b/vcl/unx/generic/dtrans/X11_service.cxx new file mode 100644 index 000000000000..bdfdd4bb7a91 --- /dev/null +++ b/vcl/unx/generic/dtrans/X11_service.cxx @@ -0,0 +1,144 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "unx/salinst.h" + +#include <X11_clipboard.hxx> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/registry/XRegistryKey.hpp> +#include <uno/dispatcher.h> // declaration of generic uno interface +#include <uno/mapping.hxx> // mapping stuff +#include <cppuhelper/factory.hxx> +#include <cppuhelper/compbase1.hxx> + +namespace { + +namespace css = com::sun::star; + +} + +using namespace cppu; +using namespace com::sun::star::lang; +using namespace com::sun::star::datatransfer::clipboard; +using namespace com::sun::star::awt; +using namespace x11; + +using ::rtl::OUString; + +Sequence< OUString > SAL_CALL x11::X11Clipboard_getSupportedServiceNames() +{ + Sequence< OUString > aRet(1); + aRet[0] = OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.datatransfer.clipboard.SystemClipboard")); + return aRet; +} + +Sequence< OUString > SAL_CALL x11::Xdnd_getSupportedServiceNames() +{ + Sequence< OUString > aRet(1); + aRet[0] = OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.datatransfer.dnd.X11DragSource")); + return aRet; +} + +Sequence< OUString > SAL_CALL x11::Xdnd_dropTarget_getSupportedServiceNames() +{ + Sequence< OUString > aRet(1); + aRet[0] = OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.datatransfer.dnd.X11DropTarget")); + return aRet; +} + +// ------------------------------------------------------------------------ + +css::uno::Reference< XInterface > X11SalInstance::CreateClipboard( const Sequence< Any >& arguments ) +{ + static boost::unordered_map< OUString, ::boost::unordered_map< Atom, Reference< XClipboard > >, ::rtl::OUStringHash > m_aInstances; + + OUString aDisplayName; + Atom nSelection; + + // extract display name from connection argument. An exception is thrown + // by SelectionManager.initialize() if no display connection is given. + if( arguments.getLength() > 0 ) + { + css::uno::Reference< XDisplayConnection > xConn; + arguments.getConstArray()[0] >>= xConn; + + if( xConn.is() ) + { + Any aIdentifier = xConn->getIdentifier(); + aIdentifier >>= aDisplayName; + } + } + + SelectionManager& rManager = SelectionManager::get( aDisplayName ); + rManager.initialize( arguments ); + + // check if any other selection than clipboard selection is specified + if( arguments.getLength() > 1 ) + { + OUString aSelectionName; + + arguments.getConstArray()[1] >>= aSelectionName; + nSelection = rManager.getAtom( aSelectionName ); + } + else + { + // default atom is clipboard selection + nSelection = rManager.getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("CLIPBOARD")) ); + } + + ::boost::unordered_map< Atom, css::uno::Reference< XClipboard > >& rMap( m_aInstances[ aDisplayName ] ); + ::boost::unordered_map< Atom, css::uno::Reference< XClipboard > >::iterator it = rMap.find( nSelection ); + if( it != rMap.end() ) + return it->second; + + X11Clipboard* pClipboard = new X11Clipboard( rManager, nSelection ); + rMap[ nSelection ] = pClipboard; + + return static_cast<OWeakObject*>(pClipboard); +} + +// ------------------------------------------------------------------------ + +css::uno::Reference< XInterface > X11SalInstance::CreateDragSource() +{ + return css::uno::Reference < XInterface >( ( OWeakObject * ) new SelectionManagerHolder() ); +} + +// ------------------------------------------------------------------------ + +css::uno::Reference< XInterface > X11SalInstance::CreateDropTarget() +{ + return css::uno::Reference < XInterface >( ( OWeakObject * ) new DropTarget() ); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/dtrans/X11_transferable.cxx b/vcl/unx/generic/dtrans/X11_transferable.cxx new file mode 100644 index 000000000000..076da0917a40 --- /dev/null +++ b/vcl/unx/generic/dtrans/X11_transferable.cxx @@ -0,0 +1,136 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#if OSL_DEBUG_LEVEL > 1 +#include <stdio.h> +#endif + +#include <X11_transferable.hxx> +#include <X11/Xatom.h> +#include <com/sun/star/io/IOException.hpp> + +using namespace com::sun::star::datatransfer; +using namespace com::sun::star::lang; +using namespace com::sun::star::io; +using namespace com::sun::star::uno; +using namespace cppu; +using namespace osl; + +using namespace x11; + +using ::rtl::OUString; + + +X11Transferable::X11Transferable( + SelectionManager& rManager, + const Reference< XInterface >& xCreator, + Atom selection + ) : + m_rManager( rManager ), + m_xCreator( xCreator ), + m_aSelection( selection ) +{ +} + +//================================================================================================== + +X11Transferable::~X11Transferable() +{ +} + +//================================================================================================== + +Any SAL_CALL X11Transferable::getTransferData( const DataFlavor& rFlavor ) + throw(UnsupportedFlavorException, IOException, RuntimeException) +{ + Any aRet; + Sequence< sal_Int8 > aData; + bool bSuccess = m_rManager.getPasteData( m_aSelection ? m_aSelection : XA_PRIMARY, rFlavor.MimeType, aData ); + if( ! bSuccess && m_aSelection == 0 ) + bSuccess = m_rManager.getPasteData( m_rManager.getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("CLIPBOARD")) ), rFlavor.MimeType, aData ); + + if( ! bSuccess ) + { + throw UnsupportedFlavorException( rFlavor.MimeType, static_cast < XTransferable * > ( this ) ); + } + if( rFlavor.MimeType.equalsIgnoreAsciiCase( OUString(RTL_CONSTASCII_USTRINGPARAM("text/plain;charset=utf-16")) ) ) + { + int nLen = aData.getLength()/2; + if( ((sal_Unicode*)aData.getConstArray())[nLen-1] == 0 ) + nLen--; + OUString aString( (sal_Unicode*)aData.getConstArray(), nLen ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "X11Transferable::getTransferData( \"%s\" )\n -> \"%s\"\n", + OUStringToOString( rFlavor.MimeType, RTL_TEXTENCODING_ISO_8859_1 ).getStr(), + OUStringToOString( aString, RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); +#endif + aRet <<= aString; + } + else + aRet <<= aData; + return aRet; +} + +//================================================================================================== + +Sequence< DataFlavor > SAL_CALL X11Transferable::getTransferDataFlavors() + throw(RuntimeException) +{ + Sequence< DataFlavor > aFlavorList; + bool bSuccess = m_rManager.getPasteDataTypes( m_aSelection ? m_aSelection : XA_PRIMARY, aFlavorList ); + if( ! bSuccess && m_aSelection == 0 ) + bSuccess = m_rManager.getPasteDataTypes( m_rManager.getAtom( OUString(RTL_CONSTASCII_USTRINGPARAM("CLIPBOARD")) ), aFlavorList ); + + return aFlavorList; +} + +//================================================================================================== + +sal_Bool SAL_CALL X11Transferable::isDataFlavorSupported( const DataFlavor& aFlavor ) + throw(RuntimeException) +{ + if( aFlavor.DataType != getCppuType( (Sequence< sal_Int8 >*)0 ) ) + { + if( ! aFlavor.MimeType.equalsIgnoreAsciiCase( OUString(RTL_CONSTASCII_USTRINGPARAM("text/plain;charset=utf-16")) ) && + aFlavor.DataType == getCppuType( (OUString*)0 ) ) + return false; + } + + Sequence< DataFlavor > aFlavors( getTransferDataFlavors() ); + for( int i = 0; i < aFlavors.getLength(); i++ ) + if( aFlavor.MimeType.equalsIgnoreAsciiCase( aFlavors.getConstArray()[i].MimeType ) && + aFlavor.DataType == aFlavors.getConstArray()[i].DataType ) + return sal_True; + + return sal_False; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/dtrans/X11_transferable.hxx b/vcl/unx/generic/dtrans/X11_transferable.hxx new file mode 100644 index 000000000000..683faa0a7f87 --- /dev/null +++ b/vcl/unx/generic/dtrans/X11_transferable.hxx @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _DTRANS_X11_TRANSFERABLE_HXX_ +#define _DTRANS_X11_TRANSFERABLE_HXX_ + +#include <X11_selection.hxx> +#include <com/sun/star/datatransfer/XTransferable.hpp> + +#include <com/sun/star/lang/XEventListener.hpp> +#include <cppuhelper/implbase1.hxx> + +namespace x11 { + + class X11Transferable : public ::cppu::WeakImplHelper1 < + ::com::sun::star::datatransfer::XTransferable > + { + ::osl::Mutex m_aMutex; + + SelectionManager& m_rManager; + com::sun::star::uno::Reference< XInterface > m_xCreator; + Atom m_aSelection; + public: + X11Transferable( SelectionManager& rManager, const com::sun::star::uno::Reference< XInterface >& xCreator, Atom selection = None ); + virtual ~X11Transferable(); + + /* + * XTransferable + */ + + virtual ::com::sun::star::uno::Any SAL_CALL getTransferData( const ::com::sun::star::datatransfer::DataFlavor& aFlavor ) + throw(::com::sun::star::datatransfer::UnsupportedFlavorException, + ::com::sun::star::io::IOException, + ::com::sun::star::uno::RuntimeException + ); + + virtual ::com::sun::star::uno::Sequence< ::com::sun::star::datatransfer::DataFlavor > SAL_CALL getTransferDataFlavors( ) + throw(::com::sun::star::uno::RuntimeException); + + virtual sal_Bool SAL_CALL isDataFlavorSupported( const ::com::sun::star::datatransfer::DataFlavor& aFlavor ) + throw(::com::sun::star::uno::RuntimeException); + }; + +} // namespace + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/dtrans/bmp.cxx b/vcl/unx/generic/dtrans/bmp.cxx new file mode 100644 index 000000000000..57ad8ecf8a4d --- /dev/null +++ b/vcl/unx/generic/dtrans/bmp.cxx @@ -0,0 +1,742 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <unistd.h> +#include <cstdio> +#include <cstring> + +#include <bmp.hxx> + +#include <X11_selection.hxx> +#include <sal/macros.h> + +using namespace x11; +using namespace com::sun::star::uno; +using namespace com::sun::star::script; +using namespace com::sun::star::awt; + +/* + * helper functions + */ + +inline void writeLE( sal_uInt16 nNumber, sal_uInt8* pBuffer ) +{ + pBuffer[ 0 ] = (nNumber & 0xff); + pBuffer[ 1 ] = ((nNumber>>8)&0xff); +} + +inline void writeLE( sal_uInt32 nNumber, sal_uInt8* pBuffer ) +{ + pBuffer[ 0 ] = (nNumber & 0xff); + pBuffer[ 1 ] = ((nNumber>>8)&0xff); + pBuffer[ 2 ] = ((nNumber>>16)&0xff); + pBuffer[ 3 ] = ((nNumber>>24)&0xff); +} + +inline sal_uInt16 readLE16( const sal_uInt8* pBuffer ) +{ + return (((sal_uInt16)pBuffer[1]) << 8 ) | pBuffer[0]; +} + +inline sal_uInt16 readLE32( const sal_uInt8* pBuffer ) +{ + return + (((sal_uInt32)pBuffer[3]) << 24 ) | + (((sal_uInt32)pBuffer[2]) << 16 ) | + (((sal_uInt32)pBuffer[1]) << 8 ) | + pBuffer[0]; +} + + +/* + * BmpTransporter + */ + +BmpTransporter::BmpTransporter( const Sequence<sal_Int8>& rBmp ) : + m_aBM( rBmp ) +{ + const sal_uInt8* pData = (const sal_uInt8*)rBmp.getConstArray(); + + if( pData[0] == 'B' || pData[1] == 'M' ) + { + pData = pData+14; + m_aSize.Width = readLE32( pData+4 ); + m_aSize.Height = readLE32( pData+8 ); + } + else + m_aSize.Width = m_aSize.Height = 0; +} + +BmpTransporter::~BmpTransporter() +{ +} + +com::sun::star::awt::Size SAL_CALL BmpTransporter::getSize() throw() +{ + return m_aSize; +} + +Sequence< sal_Int8 > SAL_CALL BmpTransporter::getDIB() throw() +{ + return m_aBM; +} + +Sequence< sal_Int8 > SAL_CALL BmpTransporter::getMaskDIB() throw() +{ + return Sequence< sal_Int8 >(); +} + +/* + * scanline helpers + */ + +inline void X11_writeScanlinePixel( unsigned long nColor, sal_uInt8* pScanline, int depth, int x ) +{ + switch( depth ) + { + case 1: + pScanline[ x/8 ] &= ~(1 << (x&7)); + pScanline[ x/8 ] |= ((nColor & 1) << (x&7)); + break; + case 4: + pScanline[ x/2 ] &= ((x&1) ? 0x0f : 0xf0); + pScanline[ x/2 ] |= ((x&1) ? (nColor & 0x0f) : ((nColor & 0x0f) << 4)); + break; + default: + case 8: + pScanline[ x ] = (nColor & 0xff); + break; + } +} + +static sal_uInt8* X11_getPaletteBmpFromImage( + Display* pDisplay, + XImage* pImage, + Colormap aColormap, + sal_Int32& rOutSize + ) +{ + sal_uInt32 nColors = 0; + + rOutSize = 0; + + sal_uInt8* pBuffer = 0; + sal_uInt32 nHeaderSize, nScanlineSize; + sal_uInt16 nBitCount; + // determine header and scanline size + switch( pImage->depth ) + { + case 1: + nHeaderSize = 64; + nScanlineSize = (pImage->width+31)/32; + nBitCount = 1; + break; + case 4: + nHeaderSize = 72; + nScanlineSize = (pImage->width+1)/2; + nBitCount = 4; + break; + default: + case 8: + nHeaderSize = 1084; + nScanlineSize = pImage->width; + nBitCount = 8; + break; + } + // adjust scan lines to begin on %4 boundaries + if( nScanlineSize & 3 ) + { + nScanlineSize &= 0xfffffffc; + nScanlineSize += 4; + } + + // allocate buffer to hold header and scanlines, initialize to zero + rOutSize = nHeaderSize + nScanlineSize*pImage->height; + pBuffer = (sal_uInt8*)rtl_allocateZeroMemory( rOutSize ); + for( int y = 0; y < pImage->height; y++ ) + { + sal_uInt8* pScanline = pBuffer + nHeaderSize + (pImage->height-1-y)*nScanlineSize; + for( int x = 0; x < pImage->width; x++ ) + { + unsigned long nPixel = XGetPixel( pImage, x, y ); + if( nPixel >= nColors ) + nColors = nPixel+1; + X11_writeScanlinePixel( nPixel, pScanline, pImage->depth, x ); + } + } + + // fill in header fields + pBuffer[ 0 ] = 'B'; + pBuffer[ 1 ] = 'M'; + + writeLE( nHeaderSize, pBuffer+10 ); + writeLE( (sal_uInt32)40, pBuffer+14 ); + writeLE( (sal_uInt32)pImage->width, pBuffer+18 ); + writeLE( (sal_uInt32)pImage->height, pBuffer+22 ); + writeLE( (sal_uInt16)1, pBuffer+26 ); + writeLE( nBitCount, pBuffer+28 ); + writeLE( (sal_uInt32)(DisplayWidth(pDisplay,DefaultScreen(pDisplay))*1000/DisplayWidthMM(pDisplay,DefaultScreen(pDisplay))), pBuffer+38); + writeLE( (sal_uInt32)(DisplayHeight(pDisplay,DefaultScreen(pDisplay))*1000/DisplayHeightMM(pDisplay,DefaultScreen(pDisplay))), pBuffer+42); + writeLE( nColors, pBuffer+46 ); + writeLE( nColors, pBuffer+50 ); + + XColor aColors[256]; + if( nColors > (1U << nBitCount) ) // paranoia + nColors = (1U << nBitCount); + for( unsigned long nPixel = 0; nPixel < nColors; nPixel++ ) + { + aColors[nPixel].flags = DoRed | DoGreen | DoBlue; + aColors[nPixel].pixel = nPixel; + } + XQueryColors( pDisplay, aColormap, aColors, nColors ); + for( sal_uInt32 i = 0; i < nColors; i++ ) + { + pBuffer[ 54 + i*4 ] = (sal_uInt8)(aColors[i].blue >> 8); + pBuffer[ 55 + i*4 ] = (sal_uInt8)(aColors[i].green >> 8); + pBuffer[ 56 + i*4 ] = (sal_uInt8)(aColors[i].red >> 8); + } + + // done + + return pBuffer; +} + +inline unsigned long doRightShift( unsigned long nValue, int nShift ) +{ + return (nShift > 0) ? (nValue >> nShift) : (nValue << (-nShift)); +} + +inline unsigned long doLeftShift( unsigned long nValue, int nShift ) +{ + return (nShift > 0) ? (nValue << nShift) : (nValue >> (-nShift)); +} + +static void getShift( unsigned long nMask, int& rShift, int& rSigBits, int& rShift2 ) +{ + unsigned long nUseMask = nMask; + rShift = 0; + while( nMask & 0xffffff00 ) + { + rShift++; + nMask >>= 1; + } + if( rShift == 0 ) + while( ! (nMask & 0x00000080) ) + { + rShift--; + nMask <<= 1; + } + + int nRotate = sizeof(unsigned long)*8 - rShift; + rSigBits = 0; + nMask = doRightShift( nUseMask, rShift) ; + while( nRotate-- ) + { + if( nMask & 1 ) + rSigBits++; + nMask >>= 1; + } + + rShift2 = 0; + if( rSigBits < 8 ) + rShift2 = 8-rSigBits; +} + +static sal_uInt8* X11_getTCBmpFromImage( + Display* pDisplay, + XImage* pImage, + sal_Int32& rOutSize, + int nScreenNo + ) +{ + // get masks from visual info (guesswork) + XVisualInfo aVInfo; + if( ! XMatchVisualInfo( pDisplay, nScreenNo, pImage->depth, TrueColor, &aVInfo ) ) + return NULL; + + rOutSize = 0; + + sal_uInt8* pBuffer = 0; + sal_uInt32 nHeaderSize = 60; + sal_uInt32 nScanlineSize = pImage->width*3; + + // adjust scan lines to begin on %4 boundaries + if( nScanlineSize & 3 ) + { + nScanlineSize &= 0xfffffffc; + nScanlineSize += 4; + } + int nRedShift, nRedSig, nRedShift2 = 0; + getShift( aVInfo.red_mask, nRedShift, nRedSig, nRedShift2 ); + int nGreenShift, nGreenSig, nGreenShift2 = 0; + getShift( aVInfo.green_mask, nGreenShift, nGreenSig, nGreenShift2 ); + int nBlueShift, nBlueSig, nBlueShift2 = 0; + getShift( aVInfo.blue_mask, nBlueShift, nBlueSig, nBlueShift2 ); + + // allocate buffer to hold header and scanlines, initialize to zero + rOutSize = nHeaderSize + nScanlineSize*pImage->height; + pBuffer = (sal_uInt8*)rtl_allocateZeroMemory( rOutSize ); + for( int y = 0; y < pImage->height; y++ ) + { + sal_uInt8* pScanline = pBuffer + nHeaderSize + (pImage->height-1-y)*nScanlineSize; + for( int x = 0; x < pImage->width; x++ ) + { + unsigned long nPixel = XGetPixel( pImage, x, y ); + + sal_uInt8 nValue = (sal_uInt8)doRightShift( nPixel&aVInfo.blue_mask, nBlueShift); + if( nBlueShift2 ) + nValue |= (nValue >> nBlueShift2 ); + *pScanline++ = nValue; + + nValue = (sal_uInt8)doRightShift( nPixel&aVInfo.green_mask, nGreenShift); + if( nGreenShift2 ) + nValue |= (nValue >> nGreenShift2 ); + *pScanline++ = nValue; + + nValue = (sal_uInt8)doRightShift( nPixel&aVInfo.red_mask, nRedShift); + if( nRedShift2 ) + nValue |= (nValue >> nRedShift2 ); + *pScanline++ = nValue; + } + } + + // fill in header fields + pBuffer[ 0 ] = 'B'; + pBuffer[ 1 ] = 'M'; + + writeLE( nHeaderSize, pBuffer+10 ); + writeLE( (sal_uInt32)40, pBuffer+14 ); + writeLE( (sal_uInt32)pImage->width, pBuffer+18 ); + writeLE( (sal_uInt32)pImage->height, pBuffer+22 ); + writeLE( (sal_uInt16)1, pBuffer+26 ); + writeLE( (sal_uInt16)24, pBuffer+28 ); + writeLE( (sal_uInt32)(DisplayWidth(pDisplay,DefaultScreen(pDisplay))*1000/DisplayWidthMM(pDisplay,DefaultScreen(pDisplay))), pBuffer+38); + writeLE( (sal_uInt32)(DisplayHeight(pDisplay,DefaultScreen(pDisplay))*1000/DisplayHeightMM(pDisplay,DefaultScreen(pDisplay))), pBuffer+42); + + // done + + return pBuffer; +} + +sal_uInt8* x11::X11_getBmpFromPixmap( + Display* pDisplay, + Drawable aDrawable, + Colormap aColormap, + sal_Int32& rOutSize + ) +{ + // get geometry of drawable + XLIB_Window aRoot; + int x,y; + unsigned int w, h, bw, d; + XGetGeometry( pDisplay, aDrawable, &aRoot, &x, &y, &w, &h, &bw, &d ); + + // find which screen we are on + int nScreenNo = ScreenCount( pDisplay ); + while( nScreenNo-- ) + { + if( RootWindow( pDisplay, nScreenNo ) == aRoot ) + break; + } + if( nScreenNo < 0 ) + return NULL; + + if( aColormap == None ) + aColormap = DefaultColormap( pDisplay, nScreenNo ); + + // get the image + XImage* pImage = XGetImage( pDisplay, aDrawable, 0, 0, w, h, AllPlanes, ZPixmap ); + if( ! pImage ) + return NULL; + + sal_uInt8* pBmp = d <= 8 ? + X11_getPaletteBmpFromImage( pDisplay, pImage, aColormap, rOutSize ) : + X11_getTCBmpFromImage( pDisplay, pImage, rOutSize, nScreenNo ); + XDestroyImage( pImage ); + + return pBmp; +} + +void x11::X11_freeBmp( sal_uInt8* pBmp ) +{ + rtl_freeMemory( pBmp ); +} + +/* + * PixmapHolder + */ + +PixmapHolder::PixmapHolder( Display* pDisplay ) : + m_pDisplay( pDisplay ), + m_aColormap( None ), + m_aPixmap( None ), + m_aBitmap( None ) +{ + /* try to get a 24 bit true color visual, if that fails, + * revert to default visual + */ + if( ! XMatchVisualInfo( m_pDisplay, DefaultScreen( m_pDisplay ), 24, TrueColor, &m_aInfo ) ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "PixmapHolder reverting to default visual\n" ); +#endif + Visual* pVisual = DefaultVisual( m_pDisplay, DefaultScreen( m_pDisplay ) ); + m_aInfo.screen = DefaultScreen( m_pDisplay ); + m_aInfo.visual = pVisual; + m_aInfo.visualid = pVisual->visualid; + m_aInfo.c_class = pVisual->c_class; + m_aInfo.red_mask = pVisual->red_mask; + m_aInfo.green_mask = pVisual->green_mask; + m_aInfo.blue_mask = pVisual->blue_mask; + m_aInfo.depth = DefaultDepth( m_pDisplay, m_aInfo.screen ); + } + m_aColormap = DefaultColormap( m_pDisplay, m_aInfo.screen ); +#if OSL_DEBUG_LEVEL > 1 + static const char* pClasses[] = + { "StaticGray", "GrayScale", "StaticColor", "PseudoColor", "TrueColor", "DirectColor" }; + fprintf( stderr, "PixmapHolder visual: id = 0x%lx, class = %s (%d), depth=%d; color map = 0x%lx\n", + m_aInfo.visualid, + (m_aInfo.c_class >= 0 && unsigned(m_aInfo.c_class) < SAL_N_ELEMENTS(pClasses)) ? pClasses[m_aInfo.c_class] : "<unknown>", + m_aInfo.c_class, + m_aInfo.depth, + m_aColormap ); +#endif + if( m_aInfo.c_class == TrueColor ) + { + int nRedSig, nGreenSig, nBlueSig; + m_nRedShift = m_nRedShift2 = 0; + getShift( m_aInfo.red_mask, m_nRedShift, nRedSig, m_nRedShift2 ); + m_nGreenShift = m_nGreenShift2 = 0; + getShift( m_aInfo.green_mask, m_nGreenShift, nGreenSig, m_nGreenShift2 ); + m_nBlueShift = m_nBlueShift2 = 0; + getShift( m_aInfo.blue_mask, m_nBlueShift, nBlueSig, m_nBlueShift2 ); + + m_nBlueShift2Mask = m_nBlueShift2 ? ~((unsigned long)((1<<m_nBlueShift2)-1)) : ~0L; + m_nGreenShift2Mask = m_nGreenShift2 ? ~((unsigned long)((1<<m_nGreenShift2)-1)) : ~0L; + m_nRedShift2Mask = m_nRedShift2 ? ~((unsigned long)((1<<m_nRedShift2)-1)) : ~0L; + } +} + +PixmapHolder::~PixmapHolder() +{ + if( m_aPixmap != None ) + XFreePixmap( m_pDisplay, m_aPixmap ); + if( m_aBitmap != None ) + XFreePixmap( m_pDisplay, m_aBitmap ); +} + +unsigned long PixmapHolder::getTCPixel( sal_uInt8 r, sal_uInt8 g, sal_uInt8 b ) const +{ + unsigned long nPixel = 0; + unsigned long nValue = (unsigned long)b; + nValue &= m_nBlueShift2Mask; + nPixel |= doLeftShift( nValue, m_nBlueShift ); + + nValue = (unsigned long)g; + nValue &= m_nGreenShift2Mask; + nPixel |= doLeftShift( nValue, m_nGreenShift ); + + nValue = (unsigned long)r; + nValue &= m_nRedShift2Mask; + nPixel |= doLeftShift( nValue, m_nRedShift ); + + return nPixel; +} + +void PixmapHolder::setBitmapDataPalette( const sal_uInt8* pData, XImage* pImage ) +{ + // setup palette + XColor aPalette[256]; + + sal_uInt32 nColors = readLE32( pData+32 ); + sal_uInt32 nWidth = readLE32( pData+4 ); + sal_uInt32 nHeight = readLE32( pData+8 ); + sal_uInt16 nDepth = readLE16( pData+14 ); + + for( sal_uInt16 i = 0 ; i < nColors; i++ ) + { + if( m_aInfo.c_class != TrueColor ) + { + aPalette[i].red = ((unsigned short)pData[42 + i*4]) << 8 | ((unsigned short)pData[42 + i*4]); + aPalette[i].green = ((unsigned short)pData[41 + i*4]) << 8 | ((unsigned short)pData[41 + i*4]); + aPalette[i].blue = ((unsigned short)pData[40 + i*4]) << 8 | ((unsigned short)pData[40 + i*4]); + XAllocColor( m_pDisplay, m_aColormap, aPalette+i ); + } + else + aPalette[i].pixel = getTCPixel( pData[42+i*4], pData[41+i*4], pData[40+i*4] ); + } + const sal_uInt8* pBMData = pData + readLE32( pData ) + 4*nColors; + + sal_uInt32 nScanlineSize = 0; + switch( nDepth ) + { + case 1: + nScanlineSize = (nWidth+31)/32; + break; + case 4: + nScanlineSize = (nWidth+1)/2; + break; + case 8: + nScanlineSize = nWidth; + break; + } + // adjust scan lines to begin on %4 boundaries + if( nScanlineSize & 3 ) + { + nScanlineSize &= 0xfffffffc; + nScanlineSize += 4; + } + + // allocate buffer to hold header and scanlines, initialize to zero + for( unsigned int y = 0; y < nHeight; y++ ) + { + const sal_uInt8* pScanline = pBMData + (nHeight-1-y)*nScanlineSize; + for( unsigned int x = 0; x < nWidth; x++ ) + { + int nCol = 0; + switch( nDepth ) + { + case 1: nCol = (pScanline[ x/8 ] & (0x80 >> (x&7))) != 0 ? 0 : 1; break; + case 4: + if( x & 1 ) + nCol = (int)(pScanline[ x/2 ] >> 4); + else + nCol = (int)(pScanline[ x/2 ] & 0x0f); + break; + case 8: nCol = (int)pScanline[x]; + } + XPutPixel( pImage, x, y, aPalette[nCol].pixel ); + } + } +} + +void PixmapHolder::setBitmapDataTCDither( const sal_uInt8* pData, XImage* pImage ) +{ + XColor aPalette[216]; + + int nNonAllocs = 0; + + for( int r = 0; r < 6; r++ ) + { + for( int g = 0; g < 6; g++ ) + { + for( int b = 0; b < 6; b++ ) + { + int i = r*36+g*6+b; + aPalette[i].red = r == 5 ? 0xffff : r*10922; + aPalette[i].green = g == 5 ? 0xffff : g*10922; + aPalette[i].blue = b == 5 ? 0xffff : b*10922; + aPalette[i].pixel = 0; + if( ! XAllocColor( m_pDisplay, m_aColormap, aPalette+i ) ) + nNonAllocs++; + } + } + } + + if( nNonAllocs ) + { + XColor aRealPalette[256]; + int nColors = 1 << m_aInfo.depth; + int i; + for( i = 0; i < nColors; i++ ) + aRealPalette[i].pixel = (unsigned long)i; + XQueryColors( m_pDisplay, m_aColormap, aRealPalette, nColors ); + for( i = 0; i < nColors; i++ ) + { + sal_uInt8 nIndex = + 36*(sal_uInt8)(aRealPalette[i].red/10923) + + 6*(sal_uInt8)(aRealPalette[i].green/10923) + + (sal_uInt8)(aRealPalette[i].blue/10923); + if( aPalette[nIndex].pixel == 0 ) + aPalette[nIndex] = aRealPalette[i]; + } + } + + sal_uInt32 nWidth = readLE32( pData+4 ); + sal_uInt32 nHeight = readLE32( pData+8 ); + + const sal_uInt8* pBMData = pData + readLE32( pData ); + sal_uInt32 nScanlineSize = nWidth*3; + // adjust scan lines to begin on %4 boundaries + if( nScanlineSize & 3 ) + { + nScanlineSize &= 0xfffffffc; + nScanlineSize += 4; + } + + for( int y = 0; y < (int)nHeight; y++ ) + { + const sal_uInt8* pScanline = pBMData + (nHeight-1-(sal_uInt32)y)*nScanlineSize; + for( int x = 0; x < (int)nWidth; x++ ) + { + sal_uInt8 b = pScanline[3*x]; + sal_uInt8 g = pScanline[3*x+1]; + sal_uInt8 r = pScanline[3*x+2]; + sal_uInt8 i = 36*(r/43) + 6*(g/43) + (b/43); + + XPutPixel( pImage, x, y, aPalette[ i ].pixel ); + } + } +} + +void PixmapHolder::setBitmapDataTC( const sal_uInt8* pData, XImage* pImage ) +{ + sal_uInt32 nWidth = readLE32( pData+4 ); + sal_uInt32 nHeight = readLE32( pData+8 ); + + const sal_uInt8* pBMData = pData + readLE32( pData ); + sal_uInt32 nScanlineSize = nWidth*3; + // adjust scan lines to begin on %4 boundaries + if( nScanlineSize & 3 ) + { + nScanlineSize &= 0xfffffffc; + nScanlineSize += 4; + } + + for( int y = 0; y < (int)nHeight; y++ ) + { + const sal_uInt8* pScanline = pBMData + (nHeight-1-(sal_uInt32)y)*nScanlineSize; + for( int x = 0; x < (int)nWidth; x++ ) + { + unsigned long nPixel = getTCPixel( pScanline[3*x+2], pScanline[3*x+1], pScanline[3*x] ); + XPutPixel( pImage, x, y, nPixel ); + } + } +} + +bool PixmapHolder::needsConversion( const sal_uInt8* pData ) +{ + if( pData[0] != 'B' || pData[1] != 'M' ) + return true; + + pData = pData+14; + sal_uInt32 nDepth = readLE32( pData+14 ); + if( nDepth == 24 ) + { + if( m_aInfo.c_class != TrueColor ) + return true; + } + else if( nDepth != (sal_uInt32)m_aInfo.depth ) + { + if( m_aInfo.c_class != TrueColor ) + return true; + } + + return false; +} + +Pixmap PixmapHolder::setBitmapData( const sal_uInt8* pData ) +{ + if( pData[0] != 'B' || pData[1] != 'M' ) + return None; + + pData = pData+14; + + // reject compressed data + if( readLE32( pData + 16 ) != 0 ) + return None; + + sal_uInt32 nWidth = readLE32( pData+4 ); + sal_uInt32 nHeight = readLE32( pData+8 ); + + if( m_aPixmap != None ) + XFreePixmap( m_pDisplay, m_aPixmap ), m_aPixmap = None; + if( m_aBitmap != None ) + XFreePixmap( m_pDisplay, m_aBitmap ), m_aBitmap = None; + + m_aPixmap = XCreatePixmap( m_pDisplay, + RootWindow( m_pDisplay, m_aInfo.screen ), + nWidth, nHeight, m_aInfo.depth ); + + if( m_aPixmap != None ) + { + XImage aImage; + aImage.width = (int)nWidth; + aImage.height = (int)nHeight; + aImage.xoffset = 0; + aImage.format = ZPixmap; + aImage.data = NULL; + aImage.byte_order = ImageByteOrder( m_pDisplay ); + aImage.bitmap_unit = BitmapUnit( m_pDisplay ); + aImage.bitmap_bit_order = BitmapBitOrder( m_pDisplay ); + aImage.bitmap_pad = BitmapPad( m_pDisplay ); + aImage.depth = m_aInfo.depth; + aImage.red_mask = m_aInfo.red_mask; + aImage.green_mask = m_aInfo.green_mask; + aImage.blue_mask = m_aInfo.blue_mask; + aImage.bytes_per_line = 0; // filled in by XInitImage + if( m_aInfo.depth <= 8 ) + aImage.bits_per_pixel = m_aInfo.depth; + else + aImage.bits_per_pixel = 8*((m_aInfo.depth+7)/8); + aImage.obdata = NULL; + + XInitImage( &aImage ); + aImage.data = (char*)rtl_allocateMemory( nHeight*aImage.bytes_per_line ); + + if( readLE32( pData+14 ) == 24 ) + { + if( m_aInfo.c_class == TrueColor ) + setBitmapDataTC( pData, &aImage ); + else + setBitmapDataTCDither( pData, &aImage ); + } + else + setBitmapDataPalette( pData, &aImage ); + + // put the image + XPutImage( m_pDisplay, + m_aPixmap, + DefaultGC( m_pDisplay, m_aInfo.screen ), + &aImage, + 0, 0, + 0, 0, + nWidth, nHeight ); + + // clean up + rtl_freeMemory( aImage.data ); + + // prepare bitmap (mask) + m_aBitmap = XCreatePixmap( m_pDisplay, + RootWindow( m_pDisplay, m_aInfo.screen ), + nWidth, nHeight, 1 ); + XGCValues aVal; + aVal.function = GXcopy; + aVal.foreground = 0xffffffff; + GC aGC = XCreateGC( m_pDisplay, m_aBitmap, GCFunction | GCForeground, &aVal ); + XFillRectangle( m_pDisplay, m_aBitmap, aGC, 0, 0, nWidth, nHeight ); + XFreeGC( m_pDisplay, aGC ); + } + + return m_aPixmap; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/dtrans/bmp.hxx b/vcl/unx/generic/dtrans/bmp.hxx new file mode 100644 index 000000000000..6d2e76bae0bb --- /dev/null +++ b/vcl/unx/generic/dtrans/bmp.hxx @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _DTRANS_BMP_HXX_ +#define _DTRANS_BMP_HXX_ + +#include "tools/prex.h" +#include <X11/Xatom.h> +#include <X11/keysym.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include "tools/postx.h" + +#include <sal/types.h> +#include <com/sun/star/awt/XBitmap.hpp> +#include <cppuhelper/compbase1.hxx> + + + +namespace x11 { + +// helper methods +sal_uInt8* X11_getBmpFromPixmap( Display* pDisplay, + Drawable aDrawable, + Colormap aColormap, + sal_Int32& rOutSize ); + +void X11_freeBmp( sal_uInt8* pBmp ); + +class PixmapHolder +{ + Display* m_pDisplay; + Colormap m_aColormap; + Pixmap m_aPixmap; + Pixmap m_aBitmap; + XVisualInfo m_aInfo; + + int m_nRedShift, m_nRedShift2; + int m_nGreenShift, m_nGreenShift2; + int m_nBlueShift, m_nBlueShift2; + unsigned long m_nBlueShift2Mask, m_nRedShift2Mask, m_nGreenShift2Mask; + + // these expect data pointers to bitmapinfo header + void setBitmapDataTC( const sal_uInt8* pData, XImage* pImage ); + void setBitmapDataTCDither( const sal_uInt8* pData, XImage* pImage ); + void setBitmapDataPalette( const sal_uInt8* pData, XImage* pImage ); + + unsigned long getTCPixel( sal_uInt8 r, sal_uInt8 g, sal_uInt8 b ) const; +public: + PixmapHolder( Display* pDisplay ); + ~PixmapHolder(); + + // accepts bitmap file (including bitmap file header) + Pixmap setBitmapData( const sal_uInt8* pData ); + bool needsConversion( const sal_uInt8* pData ); + + Colormap getColormap() const { return m_aColormap; } + Pixmap getPixmap() const { return m_aPixmap; } + Pixmap getBitmap() const { return m_aBitmap; } + VisualID getVisualID() const { return m_aInfo.visualid; } + int getClass() const { return m_aInfo.c_class; } + int getDepth() const { return m_aInfo.depth; } +}; + +class BmpTransporter : + public cppu::WeakImplHelper1< com::sun::star::awt::XBitmap > +{ + com::sun::star::uno::Sequence<sal_Int8> m_aBM; + com::sun::star::awt::Size m_aSize; +public: + BmpTransporter( const com::sun::star::uno::Sequence<sal_Int8>& rBmp ); + virtual ~BmpTransporter(); + + virtual com::sun::star::awt::Size SAL_CALL getSize() throw(); + virtual com::sun::star::uno::Sequence< sal_Int8 > SAL_CALL getDIB() throw(); + virtual com::sun::star::uno::Sequence< sal_Int8 > SAL_CALL getMaskDIB() throw(); +}; + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/dtrans/config.cxx b/vcl/unx/generic/dtrans/config.cxx new file mode 100644 index 000000000000..5f711da47434 --- /dev/null +++ b/vcl/unx/generic/dtrans/config.cxx @@ -0,0 +1,151 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <cstdio> +#include <unotools/configitem.hxx> + +#include "X11_selection.hxx" + +#define SETTINGS_CONFIGNODE "VCL/Settings/Transfer" +#define SELECTION_PROPERTY "SelectionTimeout" + +namespace x11 +{ + +class DtransX11ConfigItem : public ::utl::ConfigItem +{ + sal_Int32 m_nSelectionTimeout; + + virtual void Notify( const ::com::sun::star::uno::Sequence< ::rtl::OUString >& rPropertyNames ); + virtual void Commit(); +public: + DtransX11ConfigItem(); + virtual ~DtransX11ConfigItem(); + + sal_Int32 getSelectionTimeout() const { return m_nSelectionTimeout; } +}; + +} + +using namespace com::sun::star::lang; +using namespace com::sun::star::uno; +using namespace x11; + +using ::rtl::OUString; + +sal_Int32 SelectionManager::getSelectionTimeout() +{ + if( m_nSelectionTimeout < 1 ) + { + DtransX11ConfigItem aCfg; + m_nSelectionTimeout = aCfg.getSelectionTimeout(); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "initialized selection timeout to %" SAL_PRIdINT32 " seconds\n", m_nSelectionTimeout ); +#endif + } + return m_nSelectionTimeout; +} + +/* + * DtransX11ConfigItem constructor + */ + +DtransX11ConfigItem::DtransX11ConfigItem() : + ConfigItem( OUString( RTL_CONSTASCII_USTRINGPARAM( SETTINGS_CONFIGNODE ) ), + CONFIG_MODE_DELAYED_UPDATE ), + m_nSelectionTimeout( 3 ) +{ + if( IsValidConfigMgr() ) + { + Sequence< OUString > aKeys( 1 ); + aKeys.getArray()[0] = OUString( RTL_CONSTASCII_USTRINGPARAM( SELECTION_PROPERTY ) ); + Sequence< Any > aValues = GetProperties( aKeys ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "found %" SAL_PRIdINT32 " properties for %s\n", aValues.getLength(), SELECTION_PROPERTY ); +#endif + Any* pValue = aValues.getArray(); + for( int i = 0; i < aValues.getLength(); i++, pValue++ ) + { + if( pValue->getValueTypeClass() == TypeClass_STRING ) + { + const OUString* pLine = (const OUString*)pValue->getValue(); + if( pLine->getLength() ) + { + m_nSelectionTimeout = pLine->toInt32(); + if( m_nSelectionTimeout < 1 ) + m_nSelectionTimeout = 1; + } +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "found SelectionTimeout \"%s\"\n", + OUStringToOString( *pLine, osl_getThreadTextEncoding() ).getStr() ); +#endif + } +#if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "found SelectionTimeout of type \"%s\"\n", + OUStringToOString( pValue->getValueType().getTypeName(), osl_getThreadTextEncoding() ).getStr() ); +#endif + } + } +#if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "no valid configmanager, could not read timeout setting\n" ); +#endif +} + +/* + * DtransX11ConfigItem destructor + */ + +DtransX11ConfigItem::~DtransX11ConfigItem() +{ +} + +/* + * DtransX11ConfigItem::Commit + */ + +void DtransX11ConfigItem::Commit() +{ + // for the clipboard service this is readonly, so + // there is nothing to commit +} + +/* + * DtransX11ConfigItem::Notify + */ + +void DtransX11ConfigItem::Notify( const Sequence< OUString >& /*rPropertyNames*/ ) +{ +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/dtrans/copydata_curs.h b/vcl/unx/generic/dtrans/copydata_curs.h new file mode 100644 index 000000000000..a882a541a6d7 --- /dev/null +++ b/vcl/unx/generic/dtrans/copydata_curs.h @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#define copydata_curs_width 32 +#define copydata_curs_height 32 +#define copydata_curs_x_hot 1 +#define copydata_curs_y_hot 1 +static unsigned char copydata_curs_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, + 0xfe, 0x03, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, + 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x10, 0x53, 0x00, 0x00, + 0x28, 0xa3, 0x00, 0x00, 0x10, 0x40, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x10, 0xf0, 0x1f, 0x00, 0x08, 0xf0, 0x1f, 0x00, 0x10, 0xf0, 0x1e, 0x00, + 0xa8, 0xf2, 0x1e, 0x00, 0x50, 0x35, 0x18, 0x00, 0x00, 0xf0, 0x1e, 0x00, + 0x00, 0xf0, 0x1e, 0x00, 0x00, 0xf0, 0x1f, 0x00, 0x00, 0xf0, 0x1f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/dtrans/copydata_mask.h b/vcl/unx/generic/dtrans/copydata_mask.h new file mode 100644 index 000000000000..9cd73b08d106 --- /dev/null +++ b/vcl/unx/generic/dtrans/copydata_mask.h @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#define copydata_mask_width 32 +#define copydata_mask_height 32 +#define copydata_mask_x_hot 1 +#define copydata_mask_y_hot 1 +static unsigned char copydata_mask_bits[] = { + 0x07, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x01, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0xff, 0x07, 0x00, 0x00, + 0xff, 0x07, 0x00, 0x00, 0xff, 0x07, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x01, 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0xe7, 0x03, 0x00, 0x00, + 0xe0, 0x03, 0x00, 0x00, 0xf8, 0xff, 0x00, 0x00, 0xfc, 0xff, 0x01, 0x00, + 0xfc, 0xff, 0x01, 0x00, 0xfc, 0xff, 0x01, 0x00, 0x3c, 0xf8, 0x3f, 0x00, + 0x3c, 0xf8, 0x3f, 0x00, 0x3c, 0xf8, 0x3f, 0x00, 0xfc, 0xff, 0x3f, 0x00, + 0xfc, 0xff, 0x3f, 0x00, 0xfc, 0xff, 0x3f, 0x00, 0xf8, 0xff, 0x3f, 0x00, + 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0x00, + 0x00, 0xf8, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/dtrans/linkdata_curs.h b/vcl/unx/generic/dtrans/linkdata_curs.h new file mode 100644 index 000000000000..054ef55ef2bb --- /dev/null +++ b/vcl/unx/generic/dtrans/linkdata_curs.h @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#define linkdata_curs_width 32 +#define linkdata_curs_height 32 +#define linkdata_curs_x_hot 1 +#define linkdata_curs_y_hot 1 +static unsigned char linkdata_curs_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, + 0xfe, 0x03, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, + 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x10, 0x53, 0x00, 0x00, + 0x28, 0xa3, 0x00, 0x00, 0x10, 0x40, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x10, 0xf0, 0x1f, 0x00, 0x08, 0x70, 0x18, 0x00, 0x10, 0xf0, 0x18, 0x00, + 0xa8, 0x72, 0x18, 0x00, 0x50, 0x35, 0x1a, 0x00, 0x00, 0x30, 0x1f, 0x00, + 0x00, 0xb0, 0x1f, 0x00, 0x00, 0x70, 0x1f, 0x00, 0x00, 0xf0, 0x1f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/dtrans/linkdata_mask.h b/vcl/unx/generic/dtrans/linkdata_mask.h new file mode 100644 index 000000000000..429c603066dc --- /dev/null +++ b/vcl/unx/generic/dtrans/linkdata_mask.h @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#define linkdata_mask_width 32 +#define linkdata_mask_height 32 +#define linkdata_mask_x_hot 1 +#define linkdata_mask_y_hot 1 +static unsigned char linkdata_mask_bits[] = { + 0x07, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x01, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0xff, 0x07, 0x00, 0x00, + 0xff, 0x07, 0x00, 0x00, 0xff, 0x07, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x01, 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0xe7, 0x03, 0x00, 0x00, + 0xe0, 0x03, 0x00, 0x00, 0xf8, 0xff, 0x00, 0x00, 0xfc, 0xff, 0x01, 0x00, + 0xfc, 0xff, 0x01, 0x00, 0xfc, 0xff, 0x01, 0x00, 0x3c, 0xf8, 0x3f, 0x00, + 0x3c, 0xf8, 0x3f, 0x00, 0x3c, 0xf8, 0x3f, 0x00, 0xfc, 0xff, 0x3f, 0x00, + 0xfc, 0xff, 0x3f, 0x00, 0xfc, 0xff, 0x3f, 0x00, 0xf8, 0xff, 0x3f, 0x00, + 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xf8, 0x3f, 0x00, + 0x00, 0xf8, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/dtrans/movedata_curs.h b/vcl/unx/generic/dtrans/movedata_curs.h new file mode 100644 index 000000000000..642bbd176e4b --- /dev/null +++ b/vcl/unx/generic/dtrans/movedata_curs.h @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#define movedata_curs_width 32 +#define movedata_curs_height 32 +#define movedata_curs_x_hot 1 +#define movedata_curs_y_hot 1 +static unsigned char movedata_curs_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, + 0xfe, 0x03, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, + 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x10, 0x53, 0x00, 0x00, + 0x28, 0xa3, 0x00, 0x00, 0x10, 0x40, 0x00, 0x00, 0x08, 0x80, 0x00, 0x00, + 0x10, 0x40, 0x00, 0x00, 0x08, 0x80, 0x00, 0x00, 0x10, 0x40, 0x00, 0x00, + 0xa8, 0xaa, 0x00, 0x00, 0x50, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/dtrans/movedata_mask.h b/vcl/unx/generic/dtrans/movedata_mask.h new file mode 100644 index 000000000000..f06c80f1728c --- /dev/null +++ b/vcl/unx/generic/dtrans/movedata_mask.h @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#define movedata_mask_width 32 +#define movedata_mask_height 32 +#define movedata_mask_x_hot 1 +#define movedata_mask_y_hot 1 +static unsigned char movedata_mask_bits[] = { + 0x07, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x01, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0xff, 0x07, 0x00, 0x00, + 0xff, 0x07, 0x00, 0x00, 0xff, 0x07, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x01, 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0xe7, 0x03, 0x00, 0x00, + 0xe0, 0x03, 0x00, 0x00, 0xf8, 0xff, 0x00, 0x00, 0xfc, 0xff, 0x01, 0x00, + 0xfc, 0xff, 0x01, 0x00, 0xfc, 0xff, 0x01, 0x00, 0x3c, 0xe0, 0x01, 0x00, + 0x3c, 0xe0, 0x01, 0x00, 0x3c, 0xe0, 0x01, 0x00, 0xfc, 0xff, 0x01, 0x00, + 0xfc, 0xff, 0x01, 0x00, 0xfc, 0xff, 0x01, 0x00, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/dtrans/nodrop_curs.h b/vcl/unx/generic/dtrans/nodrop_curs.h new file mode 100644 index 000000000000..5c501d3876c4 --- /dev/null +++ b/vcl/unx/generic/dtrans/nodrop_curs.h @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#define nodrop_curs_width 32 +#define nodrop_curs_height 32 +#define nodrop_curs_x_hot 9 +#define nodrop_curs_y_hot 9 +static unsigned char nodrop_curs_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x0f, 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, + 0xf8, 0x7f, 0x00, 0x00, 0x7c, 0xf8, 0x00, 0x00, 0x1c, 0xfc, 0x00, 0x00, + 0x1e, 0xfe, 0x01, 0x00, 0x0e, 0xdf, 0x01, 0x00, 0x8e, 0xcf, 0x01, 0x00, + 0xce, 0xc7, 0x01, 0x00, 0xee, 0xc3, 0x01, 0x00, 0xfe, 0xe1, 0x01, 0x00, + 0xfc, 0xe0, 0x00, 0x00, 0x7c, 0xf8, 0x00, 0x00, 0xf8, 0x7f, 0x00, 0x00, + 0xf0, 0x3f, 0x00, 0x00, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/dtrans/nodrop_mask.h b/vcl/unx/generic/dtrans/nodrop_mask.h new file mode 100644 index 000000000000..bd315dc28df1 --- /dev/null +++ b/vcl/unx/generic/dtrans/nodrop_mask.h @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#define nodrop_mask_width 32 +#define nodrop_mask_height 32 +#define nodrop_mask_x_hot 9 +#define nodrop_mask_y_hot 9 +static unsigned char nodrop_mask_bits[] = { + 0xc0, 0x0f, 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0xf8, 0x7f, 0x00, 0x00, + 0xfc, 0xff, 0x00, 0x00, 0xfe, 0xff, 0x01, 0x00, 0x7e, 0xfe, 0x01, 0x00, + 0x3f, 0xff, 0x03, 0x00, 0x9f, 0xff, 0x03, 0x00, 0xdf, 0xff, 0x03, 0x00, + 0xff, 0xef, 0x03, 0x00, 0xff, 0xe7, 0x03, 0x00, 0xff, 0xf3, 0x03, 0x00, + 0xfe, 0xf9, 0x01, 0x00, 0xfe, 0xff, 0x01, 0x00, 0xfc, 0xff, 0x00, 0x00, + 0xf8, 0x7f, 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0xc0, 0x0f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/fontmanager/adobeenc.tab b/vcl/unx/generic/fontmanager/adobeenc.tab new file mode 100644 index 000000000000..492e92f3fcf2 --- /dev/null +++ b/vcl/unx/generic/fontmanager/adobeenc.tab @@ -0,0 +1,1087 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +struct AdobeEncEntry { + sal_Unicode aUnicode; + sal_uInt8 aAdobeStandardCode; + const char* const pAdobename; +}; + +static const AdobeEncEntry aAdobeCodes[]= +{ + { 0x0041, 0101, "A" }, + { 0x00C6, 0341, "AE" }, + { 0x01FC, 0, "AEacute" }, + { 0xF7E6, 0, "AEsmall" }, + { 0x00C1, 0, "Aacute" }, + { 0xF7E1, 0, "Aacutesmall" }, + { 0x0102, 0, "Abreve" }, + { 0x00C2, 0, "Acircumflex" }, + { 0xF7E2, 0, "Acircumflexsmall" }, + { 0xF6C9, 0, "Acute" }, + { 0xF7B4, 0, "Acutesmall" }, + { 0x00C4, 0, "Adieresis" }, + { 0xF7E4, 0, "Adieresissmall" }, + { 0x00C0, 0, "Agrave" }, + { 0xF7E0, 0, "Agravesmall" }, + { 0x0391, 0, "Alpha" }, + { 0x0386, 0, "Alphatonos" }, + { 0x0100, 0, "Amacron" }, + { 0x0104, 0, "Aogonek" }, + { 0x00C5, 0, "Aring" }, + { 0x01FA, 0, "Aringacute" }, + { 0xF7E5, 0, "Aringsmall" }, + { 0xF761, 0, "Asmall" }, + { 0x00C3, 0, "Atilde" }, + { 0xF7E3, 0, "Atildesmall" }, + { 0x0042, 0102, "B" }, + { 0x0392, 0, "Beta" }, + { 0xF6F4, 0, "Brevesmall" }, + { 0xF762, 0, "Bsmall" }, + { 0x0043, 0103, "C" }, + { 0x0106, 0, "Cacute" }, + { 0xF6CA, 0, "Caron" }, + { 0xF6F5, 0, "Caronsmall" }, + { 0x010C, 0, "Ccaron" }, + { 0x00C7, 0, "Ccedilla" }, + { 0xF7E7, 0, "Ccedillasmall" }, + { 0x0108, 0, "Ccircumflex" }, + { 0x010A, 0, "Cdotaccent" }, + { 0xF7B8, 0, "Cedillasmall" }, + { 0x03A7, 0, "Chi" }, + { 0xF6F6, 0, "Circumflexsmall" }, + { 0xF763, 0, "Csmall" }, + { 0x0044, 0104, "D" }, + { 0x010E, 0, "Dcaron" }, + { 0x0110, 0, "Dcroat" }, + { 0x2206, 0, "Delta" }, + { 0x0394, 0, "Delta" }, + { 0xF6CB, 0, "Dieresis" }, + { 0xF6CC, 0, "DieresisAcute" }, + { 0xF6CD, 0, "DieresisGrave" }, + { 0xF7A8, 0, "Dieresissmall" }, + { 0xF6F7, 0, "Dotaccentsmall" }, + { 0xF764, 0, "Dsmall" }, + { 0x0045, 0105, "E" }, + { 0x00C9, 0, "Eacute" }, + { 0xF7E9, 0, "Eacutesmall" }, + { 0x0114, 0, "Ebreve" }, + { 0x011A, 0, "Ecaron" }, + { 0x00CA, 0, "Ecircumflex" }, + { 0xF7EA, 0, "Ecircumflexsmall" }, + { 0x00CB, 0, "Edieresis" }, + { 0xF7EB, 0, "Edieresissmall" }, + { 0x0116, 0, "Edotaccent" }, + { 0x00C8, 0, "Egrave" }, + { 0xF7E8, 0, "Egravesmall" }, + { 0x0112, 0, "Emacron" }, + { 0x014A, 0, "Eng" }, + { 0x0118, 0, "Eogonek" }, + { 0x0395, 0, "Epsilon" }, + { 0x0388, 0, "Epsilontonos" }, + { 0xF765, 0, "Esmall" }, + { 0x0397, 0, "Eta" }, + { 0x0389, 0, "Etatonos" }, + { 0x00D0, 0, "Eth" }, + { 0xF7F0, 0, "Ethsmall" }, + { 0x20AC, 0, "Euro" }, + { 0x0046, 0106, "F" }, + { 0xF766, 0, "Fsmall" }, + { 0x0047, 0107, "G" }, + { 0x0393, 0, "Gamma" }, + { 0x011E, 0, "Gbreve" }, + { 0x01E6, 0, "Gcaron" }, + { 0x011C, 0, "Gcircumflex" }, + { 0x0122, 0, "Gcommaaccent" }, + { 0x0120, 0, "Gdotaccent" }, + { 0xF6CE, 0, "Grave" }, + { 0xF760, 0, "Gravesmall" }, + { 0xF767, 0, "Gsmall" }, + { 0x0048, 0110, "H" }, + { 0x25CF, 0, "H18533" }, + { 0x25AA, 0, "H18543" }, + { 0x25AB, 0, "H18551" }, + { 0x25A1, 0, "H22073" }, + { 0x0126, 0, "Hbar" }, + { 0x0124, 0, "Hcircumflex" }, + { 0xF768, 0, "Hsmall" }, + { 0xF6CF, 0, "Hungarumlaut" }, + { 0xF6F8, 0, "Hungarumlautsmall" }, + { 0x0049, 0111, "I" }, + { 0x0132, 0, "IJ" }, + { 0x00CD, 0, "Iacute" }, + { 0xF7ED, 0, "Iacutesmall" }, + { 0x012C, 0, "Ibreve" }, + { 0x00CE, 0, "Icircumflex" }, + { 0xF7EE, 0, "Icircumflexsmall" }, + { 0x00CF, 0, "Idieresis" }, + { 0xF7EF, 0, "Idieresissmall" }, + { 0x0130, 0, "Idotaccent" }, + { 0x2111, 0, "Ifraktur" }, + { 0x00CC, 0, "Igrave" }, + { 0xF7EC, 0, "Igravesmall" }, + { 0x012A, 0, "Imacron" }, + { 0x012E, 0, "Iogonek" }, + { 0x0399, 0, "Iota" }, + { 0x03AA, 0, "Iotadieresis" }, + { 0x038A, 0, "Iotatonos" }, + { 0xF769, 0, "Ismall" }, + { 0x0128, 0, "Itilde" }, + { 0x004A, 0112, "J" }, + { 0x0134, 0, "Jcircumflex" }, + { 0xF76A, 0, "Jsmall" }, + { 0x004B, 0113, "K" }, + { 0x039A, 0, "Kappa" }, + { 0x0136, 0, "Kcommaaccent" }, + { 0xF76B, 0, "Ksmall" }, + { 0x004C, 0114, "L" }, + { 0xF6BF, 0, "LL" }, + { 0x0139, 0, "Lacute" }, + { 0x039B, 0, "Lambda" }, + { 0x013D, 0, "Lcaron" }, + { 0x013B, 0, "Lcommaaccent" }, + { 0x013F, 0, "Ldot" }, + { 0x0141, 0350, "Lslash" }, + { 0xF6F9, 0, "Lslashsmall" }, + { 0xF76C, 0, "Lsmall" }, + { 0x004D, 0115, "M" }, + { 0xF6D0, 0, "Macron" }, + { 0xF7AF, 0, "Macronsmall" }, + { 0xF76D, 0, "Msmall" }, + { 0x039C, 0, "Mu" }, + { 0x004E, 0116, "N" }, + { 0x0143, 0, "Nacute" }, + { 0x0147, 0, "Ncaron" }, + { 0x0145, 0, "Ncommaaccent" }, + { 0xF76E, 0, "Nsmall" }, + { 0x00D1, 0, "Ntilde" }, + { 0xF7F1, 0, "Ntildesmall" }, + { 0x039D, 0, "Nu" }, + { 0x004F, 0117, "O" }, + { 0x0152, 0, "OE" }, + { 0xF6FA, 0, "OEsmall" }, + { 0x00D3, 0, "Oacute" }, + { 0xF7F3, 0, "Oacutesmall" }, + { 0x014E, 0, "Obreve" }, + { 0x00D4, 0, "Ocircumflex" }, + { 0xF7F4, 0, "Ocircumflexsmall" }, + { 0x00D6, 0, "Odieresis" }, + { 0xF7F6, 0, "Odieresissmall" }, + { 0xF6FB, 0, "Ogoneksmall" }, + { 0x00D2, 0, "Ograve" }, + { 0xF7F2, 0, "Ogravesmall" }, + { 0x01A0, 0, "Ohorn" }, + { 0x0150, 0, "Ohungarumlaut" }, + { 0x014C, 0, "Omacron" }, + { 0x2126, 0, "Omega" }, + { 0x03A9, 0, "Omega" }, + { 0x038F, 0, "Omegatonos" }, + { 0x039F, 0, "Omicron" }, + { 0x038C, 0, "Omicrontonos" }, + { 0x00D8, 0351, "Oslash" }, + { 0x01FE, 0, "Oslashacute" }, + { 0xF7F8, 0, "Oslashsmall" }, + { 0xF76F, 0, "Osmall" }, + { 0x00D5, 0, "Otilde" }, + { 0xF7F5, 0, "Otildesmall" }, + { 0x0050, 0120, "P" }, + { 0x03A6, 0, "Phi" }, + { 0x03A0, 0, "Pi" }, + { 0x03A8, 0, "Psi" }, + { 0xF770, 0, "Psmall" }, + { 0x0051, 0121, "Q" }, + { 0xF771, 0, "Qsmall" }, + { 0x0052, 0122, "R" }, + { 0x0154, 0, "Racute" }, + { 0x0158, 0, "Rcaron" }, + { 0x0156, 0, "Rcommaaccent" }, + { 0x211C, 0, "Rfraktur" }, + { 0x03A1, 0, "Rho" }, + { 0xF6FC, 0, "Ringsmall" }, + { 0xF772, 0, "Rsmall" }, + { 0x0053, 0123, "S" }, + { 0x250C, 0, "SF010000" }, + { 0x2514, 0, "SF020000" }, + { 0x2510, 0, "SF030000" }, + { 0x2518, 0, "SF040000" }, + { 0x253C, 0, "SF050000" }, + { 0x252C, 0, "SF060000" }, + { 0x2534, 0, "SF070000" }, + { 0x251C, 0, "SF080000" }, + { 0x2524, 0, "SF090000" }, + { 0x2500, 0, "SF100000" }, + { 0x2502, 0, "SF110000" }, + { 0x2561, 0, "SF190000" }, + { 0x2562, 0, "SF200000" }, + { 0x2556, 0, "SF210000" }, + { 0x2555, 0, "SF220000" }, + { 0x2563, 0, "SF230000" }, + { 0x2551, 0, "SF240000" }, + { 0x2557, 0, "SF250000" }, + { 0x255D, 0, "SF260000" }, + { 0x255C, 0, "SF270000" }, + { 0x255B, 0, "SF280000" }, + { 0x255E, 0, "SF360000" }, + { 0x255F, 0, "SF370000" }, + { 0x255A, 0, "SF380000" }, + { 0x2554, 0, "SF390000" }, + { 0x2569, 0, "SF400000" }, + { 0x2566, 0, "SF410000" }, + { 0x2560, 0, "SF420000" }, + { 0x2550, 0, "SF430000" }, + { 0x256C, 0, "SF440000" }, + { 0x2567, 0, "SF450000" }, + { 0x2568, 0, "SF460000" }, + { 0x2564, 0, "SF470000" }, + { 0x2565, 0, "SF480000" }, + { 0x2559, 0, "SF490000" }, + { 0x2558, 0, "SF500000" }, + { 0x2552, 0, "SF510000" }, + { 0x2553, 0, "SF520000" }, + { 0x256B, 0, "SF530000" }, + { 0x256A, 0, "SF540000" }, + { 0x015A, 0, "Sacute" }, + { 0x0160, 0, "Scaron" }, + { 0xF6FD, 0, "Scaronsmall" }, + { 0x015E, 0, "Scedilla" }, + { 0xF6C1, 0, "Scedilla" }, + { 0x015C, 0, "Scircumflex" }, + { 0x0218, 0, "Scommaaccent" }, + { 0x03A3, 0, "Sigma" }, + { 0xF773, 0, "Ssmall" }, + { 0x0054, 0124, "T" }, + { 0x03A4, 0, "Tau" }, + { 0x0166, 0, "Tbar" }, + { 0x0164, 0, "Tcaron" }, + { 0x0162, 0, "Tcommaaccent" }, + { 0x021A, 0, "Tcommaaccent" }, + { 0x0398, 0, "Theta" }, + { 0x00DE, 0, "Thorn" }, + { 0xF7FE, 0, "Thornsmall" }, + { 0xF6FE, 0, "Tildesmall" }, + { 0xF774, 0, "Tsmall" }, + { 0x0055, 0125, "U" }, + { 0x00DA, 0, "Uacute" }, + { 0xF7FA, 0, "Uacutesmall" }, + { 0x016C, 0, "Ubreve" }, + { 0x00DB, 0, "Ucircumflex" }, + { 0xF7FB, 0, "Ucircumflexsmall" }, + { 0x00DC, 0, "Udieresis" }, + { 0xF7FC, 0, "Udieresissmall" }, + { 0x00D9, 0, "Ugrave" }, + { 0xF7F9, 0, "Ugravesmall" }, + { 0x01AF, 0, "Uhorn" }, + { 0x0170, 0, "Uhungarumlaut" }, + { 0x016A, 0, "Umacron" }, + { 0x0172, 0, "Uogonek" }, + { 0x03A5, 0, "Upsilon" }, + { 0x03D2, 0, "Upsilon1" }, + { 0x03AB, 0, "Upsilondieresis" }, + { 0x038E, 0, "Upsilontonos" }, + { 0x016E, 0, "Uring" }, + { 0xF775, 0, "Usmall" }, + { 0x0168, 0, "Utilde" }, + { 0x0056, 0126, "V" }, + { 0xF776, 0, "Vsmall" }, + { 0x0057, 0127, "W" }, + { 0x1E82, 0, "Wacute" }, + { 0x0174, 0, "Wcircumflex" }, + { 0x1E84, 0, "Wdieresis" }, + { 0x1E80, 0, "Wgrave" }, + { 0xF777, 0, "Wsmall" }, + { 0x0058, 0130, "X" }, + { 0x039E, 0, "Xi" }, + { 0xF778, 0, "Xsmall" }, + { 0x0059, 0131, "Y" }, + { 0x00DD, 0, "Yacute" }, + { 0xF7FD, 0, "Yacutesmall" }, + { 0x0176, 0, "Ycircumflex" }, + { 0x0178, 0, "Ydieresis" }, + { 0xF7FF, 0, "Ydieresissmall" }, + { 0x1EF2, 0, "Ygrave" }, + { 0xF779, 0, "Ysmall" }, + { 0x005A, 0132, "Z" }, + { 0x0179, 0, "Zacute" }, + { 0x017D, 0, "Zcaron" }, + { 0xF6FF, 0, "Zcaronsmall" }, + { 0x017B, 0, "Zdotaccent" }, + { 0x0396, 0, "Zeta" }, + { 0xF77A, 0, "Zsmall" }, + { 0x0061, 0141, "a" }, + { 0x00E1, 0, "aacute" }, + { 0x0103, 0, "abreve" }, + { 0x00E2, 0, "acircumflex" }, + { 0x00B4, 0302, "acute" }, + { 0x0301, 0, "acutecomb" }, + { 0x00E4, 0, "adieresis" }, + { 0x00E6, 0361, "ae" }, + { 0x01FD, 0, "aeacute" }, + { 0x2015, 0, "afii00208" }, + { 0x0410, 0, "afii10017" }, + { 0x0411, 0, "afii10018" }, + { 0x0412, 0, "afii10019" }, + { 0x0413, 0, "afii10020" }, + { 0x0414, 0, "afii10021" }, + { 0x0415, 0, "afii10022" }, + { 0x0401, 0, "afii10023" }, + { 0x0416, 0, "afii10024" }, + { 0x0417, 0, "afii10025" }, + { 0x0418, 0, "afii10026" }, + { 0x0419, 0, "afii10027" }, + { 0x041A, 0, "afii10028" }, + { 0x041B, 0, "afii10029" }, + { 0x041C, 0, "afii10030" }, + { 0x041D, 0, "afii10031" }, + { 0x041E, 0, "afii10032" }, + { 0x041F, 0, "afii10033" }, + { 0x0420, 0, "afii10034" }, + { 0x0421, 0, "afii10035" }, + { 0x0422, 0, "afii10036" }, + { 0x0423, 0, "afii10037" }, + { 0x0424, 0, "afii10038" }, + { 0x0425, 0, "afii10039" }, + { 0x0426, 0, "afii10040" }, + { 0x0427, 0, "afii10041" }, + { 0x0428, 0, "afii10042" }, + { 0x0429, 0, "afii10043" }, + { 0x042A, 0, "afii10044" }, + { 0x042B, 0, "afii10045" }, + { 0x042C, 0, "afii10046" }, + { 0x042D, 0, "afii10047" }, + { 0x042E, 0, "afii10048" }, + { 0x042F, 0, "afii10049" }, + { 0x0490, 0, "afii10050" }, + { 0x0402, 0, "afii10051" }, + { 0x0403, 0, "afii10052" }, + { 0x0404, 0, "afii10053" }, + { 0x0405, 0, "afii10054" }, + { 0x0406, 0, "afii10055" }, + { 0x0407, 0, "afii10056" }, + { 0x0408, 0, "afii10057" }, + { 0x0409, 0, "afii10058" }, + { 0x040A, 0, "afii10059" }, + { 0x040B, 0, "afii10060" }, + { 0x040C, 0, "afii10061" }, + { 0x040E, 0, "afii10062" }, + { 0xF6C4, 0, "afii10063" }, + { 0xF6C5, 0, "afii10064" }, + { 0x0430, 0, "afii10065" }, + { 0x0431, 0, "afii10066" }, + { 0x0432, 0, "afii10067" }, + { 0x0433, 0, "afii10068" }, + { 0x0434, 0, "afii10069" }, + { 0x0435, 0, "afii10070" }, + { 0x0451, 0, "afii10071" }, + { 0x0436, 0, "afii10072" }, + { 0x0437, 0, "afii10073" }, + { 0x0438, 0, "afii10074" }, + { 0x0439, 0, "afii10075" }, + { 0x043A, 0, "afii10076" }, + { 0x043B, 0, "afii10077" }, + { 0x043C, 0, "afii10078" }, + { 0x043D, 0, "afii10079" }, + { 0x043E, 0, "afii10080" }, + { 0x043F, 0, "afii10081" }, + { 0x0440, 0, "afii10082" }, + { 0x0441, 0, "afii10083" }, + { 0x0442, 0, "afii10084" }, + { 0x0443, 0, "afii10085" }, + { 0x0444, 0, "afii10086" }, + { 0x0445, 0, "afii10087" }, + { 0x0446, 0, "afii10088" }, + { 0x0447, 0, "afii10089" }, + { 0x0448, 0, "afii10090" }, + { 0x0449, 0, "afii10091" }, + { 0x044A, 0, "afii10092" }, + { 0x044B, 0, "afii10093" }, + { 0x044C, 0, "afii10094" }, + { 0x044D, 0, "afii10095" }, + { 0x044E, 0, "afii10096" }, + { 0x044F, 0, "afii10097" }, + { 0x0491, 0, "afii10098" }, + { 0x0452, 0, "afii10099" }, + { 0x0453, 0, "afii10100" }, + { 0x0454, 0, "afii10101" }, + { 0x0455, 0, "afii10102" }, + { 0x0456, 0, "afii10103" }, + { 0x0457, 0, "afii10104" }, + { 0x0458, 0, "afii10105" }, + { 0x0459, 0, "afii10106" }, + { 0x045A, 0, "afii10107" }, + { 0x045B, 0, "afii10108" }, + { 0x045C, 0, "afii10109" }, + { 0x045E, 0, "afii10110" }, + { 0x040F, 0, "afii10145" }, + { 0x0462, 0, "afii10146" }, + { 0x0472, 0, "afii10147" }, + { 0x0474, 0, "afii10148" }, + { 0xF6C6, 0, "afii10192" }, + { 0x045F, 0, "afii10193" }, + { 0x0463, 0, "afii10194" }, + { 0x0473, 0, "afii10195" }, + { 0x0475, 0, "afii10196" }, + { 0xF6C7, 0, "afii10831" }, + { 0xF6C8, 0, "afii10832" }, + { 0x04D9, 0, "afii10846" }, + { 0x200E, 0, "afii299" }, + { 0x200F, 0, "afii300" }, + { 0x200D, 0, "afii301" }, + { 0x066A, 0, "afii57381" }, + { 0x060C, 0, "afii57388" }, + { 0x0660, 0, "afii57392" }, + { 0x0661, 0, "afii57393" }, + { 0x0662, 0, "afii57394" }, + { 0x0663, 0, "afii57395" }, + { 0x0664, 0, "afii57396" }, + { 0x0665, 0, "afii57397" }, + { 0x0666, 0, "afii57398" }, + { 0x0667, 0, "afii57399" }, + { 0x0668, 0, "afii57400" }, + { 0x0669, 0, "afii57401" }, + { 0x061B, 0, "afii57403" }, + { 0x061F, 0, "afii57407" }, + { 0x0621, 0, "afii57409" }, + { 0x0622, 0, "afii57410" }, + { 0x0623, 0, "afii57411" }, + { 0x0624, 0, "afii57412" }, + { 0x0625, 0, "afii57413" }, + { 0x0626, 0, "afii57414" }, + { 0x0627, 0, "afii57415" }, + { 0x0628, 0, "afii57416" }, + { 0x0629, 0, "afii57417" }, + { 0x062A, 0, "afii57418" }, + { 0x062B, 0, "afii57419" }, + { 0x062C, 0, "afii57420" }, + { 0x062D, 0, "afii57421" }, + { 0x062E, 0, "afii57422" }, + { 0x062F, 0, "afii57423" }, + { 0x0630, 0, "afii57424" }, + { 0x0631, 0, "afii57425" }, + { 0x0632, 0, "afii57426" }, + { 0x0633, 0, "afii57427" }, + { 0x0634, 0, "afii57428" }, + { 0x0635, 0, "afii57429" }, + { 0x0636, 0, "afii57430" }, + { 0x0637, 0, "afii57431" }, + { 0x0638, 0, "afii57432" }, + { 0x0639, 0, "afii57433" }, + { 0x063A, 0, "afii57434" }, + { 0x0640, 0, "afii57440" }, + { 0x0641, 0, "afii57441" }, + { 0x0642, 0, "afii57442" }, + { 0x0643, 0, "afii57443" }, + { 0x0644, 0, "afii57444" }, + { 0x0645, 0, "afii57445" }, + { 0x0646, 0, "afii57446" }, + { 0x0648, 0, "afii57448" }, + { 0x0649, 0, "afii57449" }, + { 0x064A, 0, "afii57450" }, + { 0x064B, 0, "afii57451" }, + { 0x064C, 0, "afii57452" }, + { 0x064D, 0, "afii57453" }, + { 0x064E, 0, "afii57454" }, + { 0x064F, 0, "afii57455" }, + { 0x0650, 0, "afii57456" }, + { 0x0651, 0, "afii57457" }, + { 0x0652, 0, "afii57458" }, + { 0x0647, 0, "afii57470" }, + { 0x06A4, 0, "afii57505" }, + { 0x067E, 0, "afii57506" }, + { 0x0686, 0, "afii57507" }, + { 0x0698, 0, "afii57508" }, + { 0x06AF, 0, "afii57509" }, + { 0x0679, 0, "afii57511" }, + { 0x0688, 0, "afii57512" }, + { 0x0691, 0, "afii57513" }, + { 0x06BA, 0, "afii57514" }, + { 0x06D2, 0, "afii57519" }, + { 0x06D5, 0, "afii57534" }, + { 0x20AA, 0, "afii57636" }, + { 0x05BE, 0, "afii57645" }, + { 0x05C3, 0, "afii57658" }, + { 0x05D0, 0, "afii57664" }, + { 0x05D1, 0, "afii57665" }, + { 0x05D2, 0, "afii57666" }, + { 0x05D3, 0, "afii57667" }, + { 0x05D4, 0, "afii57668" }, + { 0x05D5, 0, "afii57669" }, + { 0x05D6, 0, "afii57670" }, + { 0x05D7, 0, "afii57671" }, + { 0x05D8, 0, "afii57672" }, + { 0x05D9, 0, "afii57673" }, + { 0x05DA, 0, "afii57674" }, + { 0x05DB, 0, "afii57675" }, + { 0x05DC, 0, "afii57676" }, + { 0x05DD, 0, "afii57677" }, + { 0x05DE, 0, "afii57678" }, + { 0x05DF, 0, "afii57679" }, + { 0x05E0, 0, "afii57680" }, + { 0x05E1, 0, "afii57681" }, + { 0x05E2, 0, "afii57682" }, + { 0x05E3, 0, "afii57683" }, + { 0x05E4, 0, "afii57684" }, + { 0x05E5, 0, "afii57685" }, + { 0x05E6, 0, "afii57686" }, + { 0x05E7, 0, "afii57687" }, + { 0x05E8, 0, "afii57688" }, + { 0x05E9, 0, "afii57689" }, + { 0x05EA, 0, "afii57690" }, + { 0xFB2A, 0, "afii57694" }, + { 0xFB2B, 0, "afii57695" }, + { 0xFB4B, 0, "afii57700" }, + { 0xFB1F, 0, "afii57705" }, + { 0x05F0, 0, "afii57716" }, + { 0x05F1, 0, "afii57717" }, + { 0x05F2, 0, "afii57718" }, + { 0xFB35, 0, "afii57723" }, + { 0x05B4, 0, "afii57793" }, + { 0x05B5, 0, "afii57794" }, + { 0x05B6, 0, "afii57795" }, + { 0x05BB, 0, "afii57796" }, + { 0x05B8, 0, "afii57797" }, + { 0x05B7, 0, "afii57798" }, + { 0x05B0, 0, "afii57799" }, + { 0x05B2, 0, "afii57800" }, + { 0x05B1, 0, "afii57801" }, + { 0x05B3, 0, "afii57802" }, + { 0x05C2, 0, "afii57803" }, + { 0x05C1, 0, "afii57804" }, + { 0x05B9, 0, "afii57806" }, + { 0x05BC, 0, "afii57807" }, + { 0x05BD, 0, "afii57839" }, + { 0x05BF, 0, "afii57841" }, + { 0x05C0, 0, "afii57842" }, + { 0x02BC, 0, "afii57929" }, + { 0x2105, 0, "afii61248" }, + { 0x2113, 0, "afii61289" }, + { 0x2116, 0, "afii61352" }, + { 0x202C, 0, "afii61573" }, + { 0x202D, 0, "afii61574" }, + { 0x202E, 0, "afii61575" }, + { 0x200C, 0, "afii61664" }, + { 0x066D, 0, "afii63167" }, + { 0x02BD, 0, "afii64937" }, + { 0x00E0, 0, "agrave" }, + { 0x2135, 0, "aleph" }, + { 0x03B1, 0, "alpha" }, + { 0x03AC, 0, "alphatonos" }, + { 0x0101, 0, "amacron" }, + { 0x0026, 046, "ampersand" }, + { 0xF726, 0, "ampersandsmall" }, + { 0x2220, 0, "angle" }, + { 0x2329, 0, "angleleft" }, + { 0x232A, 0, "angleright" }, + { 0x0387, 0, "anoteleia" }, + { 0x0105, 0, "aogonek" }, + { 0x2248, 0, "approxequal" }, + { 0x00E5, 0, "aring" }, + { 0x01FB, 0, "aringacute" }, + { 0x2194, 0, "arrowboth" }, + { 0x21D4, 0, "arrowdblboth" }, + { 0x21D3, 0, "arrowdbldown" }, + { 0x21D0, 0, "arrowdblleft" }, + { 0x21D2, 0, "arrowdblright" }, + { 0x21D1, 0, "arrowdblup" }, + { 0x2193, 0, "arrowdown" }, + { 0xF8E7, 0, "arrowhorizex" }, + { 0x2190, 0, "arrowleft" }, + { 0x2192, 0, "arrowright" }, + { 0x2191, 0, "arrowup" }, + { 0x2195, 0, "arrowupdn" }, + { 0x21A8, 0, "arrowupdnbse" }, + { 0xF8E6, 0, "arrowvertex" }, + { 0x005E, 0136, "asciicircum" }, + { 0x007E, 0176, "asciitilde" }, + { 0x002A, 052, "asterisk" }, + { 0x2217, 0, "asteriskmath" }, + { 0xF6E9, 0, "asuperior" }, + { 0x0040, 0100, "at" }, + { 0x00E3, 0, "atilde" }, + { 0x0062, 0142, "b" }, + { 0x005C, 0134, "backslash" }, + { 0x007C, 0174, "bar" }, + { 0x03B2, 0, "beta" }, + { 0x2588, 0, "block" }, + { 0xF8F4, 0, "braceex" }, + { 0x007B, 0173, "braceleft" }, + { 0xF8F3, 0, "braceleftbt" }, + { 0xF8F2, 0, "braceleftmid" }, + { 0xF8F1, 0, "bracelefttp" }, + { 0x007D, 0175, "braceright" }, + { 0xF8FE, 0, "bracerightbt" }, + { 0xF8FD, 0, "bracerightmid" }, + { 0xF8FC, 0, "bracerighttp" }, + { 0x005B, 0133, "bracketleft" }, + { 0xF8F0, 0, "bracketleftbt" }, + { 0xF8EF, 0, "bracketleftex" }, + { 0xF8EE, 0, "bracketlefttp" }, + { 0x005D, 0135, "bracketright" }, + { 0xF8FB, 0, "bracketrightbt" }, + { 0xF8FA, 0, "bracketrightex" }, + { 0xF8F9, 0, "bracketrighttp" }, + { 0x02D8, 0306, "breve" }, + { 0x00A6, 0, "brokenbar" }, + { 0xF6EA, 0, "bsuperior" }, + { 0x2022, 0267, "bullet" }, + { 0x0063, 0143, "c" }, + { 0x0107, 0, "cacute" }, + { 0x02C7, 0317, "caron" }, + { 0x21B5, 0, "carriagereturn" }, + { 0x010D, 0, "ccaron" }, + { 0x00E7, 0, "ccedilla" }, + { 0x0109, 0, "ccircumflex" }, + { 0x010B, 0, "cdotaccent" }, + { 0x00B8, 0313, "cedilla" }, + { 0x00A2, 0242, "cent" }, + { 0xF6DF, 0, "centinferior" }, + { 0xF7A2, 0, "centoldstyle" }, + { 0xF6E0, 0, "centsuperior" }, + { 0x03C7, 0, "chi" }, + { 0x25CB, 0, "circle" }, + { 0x2297, 0, "circlemultiply" }, + { 0x2295, 0, "circleplus" }, + { 0x02C6, 0303, "circumflex" }, + { 0x2663, 0, "club" }, + { 0x003A, 072, "colon" }, + { 0x20A1, 0, "colonmonetary" }, + { 0x002C, 054, "comma" }, + { 0xF6C3, 0, "commaaccent" }, + { 0xF6E1, 0, "commainferior" }, + { 0xF6E2, 0, "commasuperior" }, + { 0x2245, 0, "congruent" }, + { 0x00A9, 0, "copyright" }, + { 0xF8E9, 0, "copyrightsans" }, + { 0xF6D9, 0, "copyrightserif" }, + { 0x00A4, 0250, "currency" }, + { 0xF6D1, 0, "cyrBreve" }, + { 0xF6D2, 0, "cyrFlex" }, + { 0xF6D4, 0, "cyrbreve" }, + { 0xF6D5, 0, "cyrflex" }, + { 0x0064, 0144, "d" }, + { 0x2020, 0262, "dagger" }, + { 0x2021, 0263, "daggerdbl" }, + { 0xF6D3, 0, "dblGrave" }, + { 0xF6D6, 0, "dblgrave" }, + { 0x010F, 0, "dcaron" }, + { 0x0111, 0, "dcroat" }, + { 0x00B0, 0, "degree" }, + { 0x03B4, 0, "delta" }, + { 0x2666, 0, "diamond" }, + { 0x00A8, 0310, "dieresis" }, + { 0xF6D7, 0, "dieresisacute" }, + { 0xF6D8, 0, "dieresisgrave" }, + { 0x0385, 0, "dieresistonos" }, + { 0x00F7, 0, "divide" }, + { 0x2593, 0, "dkshade" }, + { 0x2584, 0, "dnblock" }, + { 0x0024, 044, "dollar" }, + { 0xF6E3, 0, "dollarinferior" }, + { 0xF724, 0, "dollaroldstyle" }, + { 0xF6E4, 0, "dollarsuperior" }, + { 0x20AB, 0, "dong" }, + { 0x02D9, 0307, "dotaccent" }, + { 0x0323, 0, "dotbelowcomb" }, + { 0x0131, 0365, "dotlessi" }, + { 0xF6BE, 0, "dotlessj" }, + { 0x22C5, 0, "dotmath" }, + { 0xF6EB, 0, "dsuperior" }, + { 0x0065, 0145, "e" }, + { 0x00E9, 0, "eacute" }, + { 0x0115, 0, "ebreve" }, + { 0x011B, 0, "ecaron" }, + { 0x00EA, 0, "ecircumflex" }, + { 0x00EB, 0, "edieresis" }, + { 0x0117, 0, "edotaccent" }, + { 0x00E8, 0, "egrave" }, + { 0x0038, 070, "eight" }, + { 0x2088, 0, "eightinferior" }, + { 0xF738, 0, "eightoldstyle" }, + { 0x2078, 0, "eightsuperior" }, + { 0x2208, 0, "element" }, + { 0x2026, 0274, "ellipsis" }, + { 0x0113, 0, "emacron" }, + { 0x2014, 0320, "emdash" }, + { 0x2205, 0, "emptyset" }, + { 0x2013, 0261, "endash" }, + { 0x014B, 0, "eng" }, + { 0x0119, 0, "eogonek" }, + { 0x03B5, 0, "epsilon" }, + { 0x03AD, 0, "epsilontonos" }, + { 0x003D, 075, "equal" }, + { 0x2261, 0, "equivalence" }, + { 0x212E, 0, "estimated" }, + { 0xF6EC, 0, "esuperior" }, + { 0x03B7, 0, "eta" }, + { 0x03AE, 0, "etatonos" }, + { 0x00F0, 0, "eth" }, + { 0x0021, 041, "exclam" }, + { 0x203C, 0, "exclamdbl" }, + { 0x00A1, 0241, "exclamdown" }, + { 0xF7A1, 0, "exclamdownsmall" }, + { 0xF721, 0, "exclamsmall" }, + { 0x2203, 0, "existential" }, + { 0x0066, 0146, "f" }, + { 0x2640, 0, "female" }, + { 0xFB00, 0, "ff" }, + { 0xFB03, 0, "ffi" }, + { 0xFB04, 0, "ffl" }, + { 0xFB01, 0256, "fi" }, + { 0x2012, 0, "figuredash" }, + { 0x25A0, 0, "filledbox" }, + { 0x25AC, 0, "filledrect" }, + { 0x0035, 065, "five" }, + { 0x215D, 0, "fiveeighths" }, + { 0x2085, 0, "fiveinferior" }, + { 0xF735, 0, "fiveoldstyle" }, + { 0x2075, 0, "fivesuperior" }, + { 0xFB02, 0257, "fl" }, + { 0x0192, 0246, "florin" }, + { 0x0034, 064, "four" }, + { 0x2084, 0, "fourinferior" }, + { 0xF734, 0, "fouroldstyle" }, + { 0x2074, 0, "foursuperior" }, + { 0x2044, 0244, "fraction" }, + { 0x2215, 0244, "fraction" }, + { 0x20A3, 0, "franc" }, + { 0x0067, 0147, "g" }, + { 0x03B3, 0, "gamma" }, + { 0x011F, 0, "gbreve" }, + { 0x01E7, 0, "gcaron" }, + { 0x011D, 0, "gcircumflex" }, + { 0x0123, 0, "gcommaaccent" }, + { 0x0121, 0, "gdotaccent" }, + { 0x00DF, 0373, "germandbls" }, + { 0x2207, 0, "gradient" }, + { 0x0060, 0301, "grave" }, + { 0x0300, 0, "gravecomb" }, + { 0x003E, 076, "greater" }, + { 0x2265, 0, "greaterequal" }, + { 0x00AB, 0253, "guillemotleft" }, + { 0x00BB, 0273, "guillemotright" }, + { 0x2039, 0254, "guilsinglleft" }, + { 0x203A, 0255, "guilsinglright" }, + { 0x0068, 0150, "h" }, + { 0x0127, 0, "hbar" }, + { 0x0125, 0, "hcircumflex" }, + { 0x2665, 0, "heart" }, + { 0x0309, 0, "hookabovecomb" }, + { 0x2302, 0, "house" }, + { 0x02DD, 0315, "hungarumlaut" }, + { 0x002D, 055, "hyphen" }, + { 0x00AD, 0, "hyphen" }, + { 0xF6E5, 0, "hypheninferior" }, + { 0xF6E6, 0, "hyphensuperior" }, + { 0x0069, 0151, "i" }, + { 0x00ED, 0, "iacute" }, + { 0x012D, 0, "ibreve" }, + { 0x00EE, 0, "icircumflex" }, + { 0x00EF, 0, "idieresis" }, + { 0x00EC, 0, "igrave" }, + { 0x0133, 0, "ij" }, + { 0x012B, 0, "imacron" }, + { 0x221E, 0, "infinity" }, + { 0x222B, 0, "integral" }, + { 0x2321, 0, "integralbt" }, + { 0xF8F5, 0, "integralex" }, + { 0x2320, 0, "integraltp" }, + { 0x2229, 0, "intersection" }, + { 0x25D8, 0, "invbullet" }, + { 0x25D9, 0, "invcircle" }, + { 0x263B, 0, "invsmileface" }, + { 0x012F, 0, "iogonek" }, + { 0x03B9, 0, "iota" }, + { 0x03CA, 0, "iotadieresis" }, + { 0x0390, 0, "iotadieresistonos" }, + { 0x03AF, 0, "iotatonos" }, + { 0xF6ED, 0, "isuperior" }, + { 0x0129, 0, "itilde" }, + { 0x006A, 0152, "j" }, + { 0x0135, 0, "jcircumflex" }, + { 0x006B, 0153, "k" }, + { 0x03BA, 0, "kappa" }, + { 0x0137, 0, "kcommaaccent" }, + { 0x0138, 0, "kgreenlandic" }, + { 0x006C, 0154, "l" }, + { 0x013A, 0, "lacute" }, + { 0x03BB, 0, "lambda" }, + { 0x013E, 0, "lcaron" }, + { 0x013C, 0, "lcommaaccent" }, + { 0x0140, 0, "ldot" }, + { 0x003C, 074, "less" }, + { 0x2264, 0, "lessequal" }, + { 0x258C, 0, "lfblock" }, + { 0x20A4, 0, "lira" }, + { 0xF6C0, 0, "ll" }, + { 0x2227, 0, "logicaland" }, + { 0x00AC, 0, "logicalnot" }, + { 0x2228, 0, "logicalor" }, + { 0x017F, 0, "longs" }, + { 0x25CA, 0, "lozenge" }, + { 0x0142, 0370, "lslash" }, + { 0xF6EE, 0, "lsuperior" }, + { 0x2591, 0, "ltshade" }, + { 0x006D, 0155, "m" }, + { 0x00AF, 0305, "macron" }, + { 0x02C9, 0305, "macron" }, + { 0x2642, 0, "male" }, + { 0x2212, 0, "minus" }, + { 0x2032, 0, "minute" }, + { 0xF6EF, 0, "msuperior" }, + { 0x00B5, 0, "mu" }, + { 0x03BC, 0, "mu" }, + { 0x00D7, 0, "multiply" }, + { 0x266A, 0, "musicalnote" }, + { 0x266B, 0, "musicalnotedbl" }, + { 0x006E, 0156, "n" }, + { 0x0144, 0, "nacute" }, + { 0x0149, 0, "napostrophe" }, + { 0x0148, 0, "ncaron" }, + { 0x0146, 0, "ncommaaccent" }, + { 0x0039, 071, "nine" }, + { 0x2089, 0, "nineinferior" }, + { 0xF739, 0, "nineoldstyle" }, + { 0x2079, 0, "ninesuperior" }, + { 0x2209, 0, "notelement" }, + { 0x2260, 0, "notequal" }, + { 0x2284, 0, "notsubset" }, + { 0x207F, 0, "nsuperior" }, + { 0x00F1, 0, "ntilde" }, + { 0x03BD, 0, "nu" }, + { 0x0023, 043, "numbersign" }, + { 0x006F, 0157, "o" }, + { 0x00F3, 0, "oacute" }, + { 0x014F, 0, "obreve" }, + { 0x00F4, 0, "ocircumflex" }, + { 0x00F6, 0, "odieresis" }, + { 0x0153, 0372, "oe" }, + { 0x02DB, 0316, "ogonek" }, + { 0x00F2, 0, "ograve" }, + { 0x01A1, 0, "ohorn" }, + { 0x0151, 0, "ohungarumlaut" }, + { 0x014D, 0, "omacron" }, + { 0x03C9, 0, "omega" }, + { 0x03D6, 0, "omega1" }, + { 0x03CE, 0, "omegatonos" }, + { 0x03BF, 0, "omicron" }, + { 0x03CC, 0, "omicrontonos" }, + { 0x0031, 061, "one" }, + { 0x2024, 0, "onedotenleader" }, + { 0x215B, 0, "oneeighth" }, + { 0xF6DC, 0, "onefitted" }, + { 0x00BD, 0, "onehalf" }, + { 0x2081, 0, "oneinferior" }, + { 0xF731, 0, "oneoldstyle" }, + { 0x00BC, 0, "onequarter" }, + { 0x00B9, 0, "onesuperior" }, + { 0x2153, 0, "onethird" }, + { 0x25E6, 0, "openbullet" }, + { 0x00AA, 0343, "ordfeminine" }, + { 0x00BA, 0353, "ordmasculine" }, + { 0x221F, 0, "orthogonal" }, + { 0x00F8, 0371, "oslash" }, + { 0x01FF, 0, "oslashacute" }, + { 0xF6F0, 0, "osuperior" }, + { 0x00F5, 0, "otilde" }, + { 0x0070, 0160, "p" }, + { 0x00B6, 0266, "paragraph" }, + { 0x0028, 050, "parenleft" }, + { 0xF8ED, 0, "parenleftbt" }, + { 0xF8EC, 0, "parenleftex" }, + { 0x208D, 0, "parenleftinferior" }, + { 0x207D, 0, "parenleftsuperior" }, + { 0xF8EB, 0, "parenlefttp" }, + { 0x0029, 051, "parenright" }, + { 0xF8F8, 0, "parenrightbt" }, + { 0xF8F7, 0, "parenrightex" }, + { 0x208E, 0, "parenrightinferior" }, + { 0x207E, 0, "parenrightsuperior" }, + { 0xF8F6, 0, "parenrighttp" }, + { 0x2202, 0, "partialdiff" }, + { 0x0025, 045, "percent" }, + { 0x002E, 056, "period" }, + { 0x00B7, 0264, "periodcentered" }, + { 0x2219, 0, "periodcentered" }, + { 0xF6E7, 0, "periodinferior" }, + { 0xF6E8, 0, "periodsuperior" }, + { 0x22A5, 0, "perpendicular" }, + { 0x2030, 0275, "perthousand" }, + { 0x20A7, 0, "peseta" }, + { 0x03C6, 0, "phi" }, + { 0x03D5, 0, "phi1" }, + { 0x03C0, 0, "pi" }, + { 0x002B, 053, "plus" }, + { 0x00B1, 0, "plusminus" }, + { 0x211E, 0, "prescription" }, + { 0x220F, 0, "product" }, + { 0x2282, 0, "propersubset" }, + { 0x2283, 0, "propersuperset" }, + { 0x221D, 0, "proportional" }, + { 0x03C8, 0, "psi" }, + { 0x0071, 0161, "q" }, + { 0x003F, 077, "question" }, + { 0x00BF, 0277, "questiondown" }, + { 0xF7BF, 0, "questiondownsmall" }, + { 0xF73F, 0, "questionsmall" }, + { 0x0022, 042, "quotedbl" }, + { 0x201E, 0271, "quotedblbase" }, + { 0x201C, 0252, "quotedblleft" }, + { 0x201D, 0272, "quotedblright" }, + { 0x2018, 0140, "quoteleft" }, + { 0x201B, 0, "quotereversed" }, + { 0x2019, 047, "quoteright" }, + { 0x201A, 0270, "quotesinglbase" }, + { 0x0027, 0251, "quotesingle" }, + { 0x0072, 0162, "r" }, + { 0x0155, 0, "racute" }, + { 0x221A, 0, "radical" }, + { 0xF8E5, 0, "radicalex" }, + { 0x0159, 0, "rcaron" }, + { 0x0157, 0, "rcommaaccent" }, + { 0x2286, 0, "reflexsubset" }, + { 0x2287, 0, "reflexsuperset" }, + { 0x00AE, 0, "registered" }, + { 0xF8E8, 0, "registersans" }, + { 0xF6DA, 0, "registerserif" }, + { 0x2310, 0, "revlogicalnot" }, + { 0x03C1, 0, "rho" }, + { 0x02DA, 0312, "ring" }, + { 0xF6F1, 0, "rsuperior" }, + { 0x2590, 0, "rtblock" }, + { 0xF6DD, 0, "rupiah" }, + { 0x0073, 0163, "s" }, + { 0x015B, 0, "sacute" }, + { 0x0161, 0, "scaron" }, + { 0x015F, 0, "scedilla" }, + { 0xF6C2, 0, "scedilla" }, + { 0x015D, 0, "scircumflex" }, + { 0x0219, 0, "scommaaccent" }, + { 0x2033, 0, "second" }, + { 0x00A7, 0247, "section" }, + { 0x003B, 073, "semicolon" }, + { 0x0037, 067, "seven" }, + { 0x215E, 0, "seveneighths" }, + { 0x2087, 0, "seveninferior" }, + { 0xF737, 0, "sevenoldstyle" }, + { 0x2077, 0, "sevensuperior" }, + { 0x2592, 0, "shade" }, + { 0x03C3, 0, "sigma" }, + { 0x03C2, 0, "sigma1" }, + { 0x223C, 0, "similar" }, + { 0x0036, 066, "six" }, + { 0x2086, 0, "sixinferior" }, + { 0xF736, 0, "sixoldstyle" }, + { 0x2076, 0, "sixsuperior" }, + { 0x002F, 057, "slash" }, + { 0x263A, 0, "smileface" }, + { 0x0020, 040, "space" }, + { 0x00A0, 040, "space" }, + { 0x2660, 0, "spade" }, + { 0xF6F2, 0, "ssuperior" }, + { 0x00A3, 0243, "sterling" }, + { 0x220B, 0, "suchthat" }, + { 0x2211, 0, "summation" }, + { 0x263C, 0, "sun" }, + { 0x0074, 0164, "t" }, + { 0x03C4, 0, "tau" }, + { 0x0167, 0, "tbar" }, + { 0x0165, 0, "tcaron" }, + { 0x0163, 0, "tcommaaccent" }, + { 0x021B, 0, "tcommaaccent" }, + { 0x2234, 0, "therefore" }, + { 0x03B8, 0, "theta" }, + { 0x03D1, 0, "theta1" }, + { 0x00FE, 0, "thorn" }, + { 0x0033, 063, "three" }, + { 0x215C, 0, "threeeighths" }, + { 0x2083, 0, "threeinferior" }, + { 0xF733, 0, "threeoldstyle" }, + { 0x00BE, 0, "threequarters" }, + { 0xF6DE, 0, "threequartersemdash" }, + { 0x00B3, 0, "threesuperior" }, + { 0x02DC, 0304, "tilde" }, + { 0x0303, 0, "tildecomb" }, + { 0x0384, 0, "tonos" }, + { 0x2122, 0, "trademark" }, + { 0xF8EA, 0, "trademarksans" }, + { 0xF6DB, 0, "trademarkserif" }, + { 0x25BC, 0, "triagdn" }, + { 0x25C4, 0, "triaglf" }, + { 0x25BA, 0, "triagrt" }, + { 0x25B2, 0, "triagup" }, + { 0xF6F3, 0, "tsuperior" }, + { 0x0032, 062, "two" }, + { 0x2025, 0, "twodotenleader" }, + { 0x2082, 0, "twoinferior" }, + { 0xF732, 0, "twooldstyle" }, + { 0x00B2, 0, "twosuperior" }, + { 0x2154, 0, "twothirds" }, + { 0x0075, 0165, "u" }, + { 0x00FA, 0, "uacute" }, + { 0x016D, 0, "ubreve" }, + { 0x00FB, 0, "ucircumflex" }, + { 0x00FC, 0, "udieresis" }, + { 0x00F9, 0, "ugrave" }, + { 0x01B0, 0, "uhorn" }, + { 0x0171, 0, "uhungarumlaut" }, + { 0x016B, 0, "umacron" }, + { 0x005F, 0137, "underscore" }, + { 0x2017, 0, "underscoredbl" }, + { 0x222A, 0, "union" }, + { 0x2200, 0, "universal" }, + { 0x0173, 0, "uogonek" }, + { 0x2580, 0, "upblock" }, + { 0x03C5, 0, "upsilon" }, + { 0x03CB, 0, "upsilondieresis" }, + { 0x03B0, 0, "upsilondieresistonos" }, + { 0x03CD, 0, "upsilontonos" }, + { 0x016F, 0, "uring" }, + { 0x0169, 0, "utilde" }, + { 0x0076, 0166, "v" }, + { 0x0077, 0167, "w" }, + { 0x1E83, 0, "wacute" }, + { 0x0175, 0, "wcircumflex" }, + { 0x1E85, 0, "wdieresis" }, + { 0x2118, 0, "weierstrass" }, + { 0x1E81, 0, "wgrave" }, + { 0x0078, 0170, "x" }, + { 0x03BE, 0, "xi" }, + { 0x0079, 0171, "y" }, + { 0x00FD, 0, "yacute" }, + { 0x0177, 0, "ycircumflex" }, + { 0x00FF, 0, "ydieresis" }, + { 0x00A5, 0245, "yen" }, + { 0x1EF3, 0, "ygrave" }, + { 0x007A, 0172, "z" }, + { 0x017A, 0, "zacute" }, + { 0x017E, 0, "zcaron" }, + { 0x017C, 0, "zdotaccent" }, + { 0x0030, 060, "zero" }, + { 0x2080, 0, "zeroinferior" }, + { 0xF730, 0, "zerooldstyle" }, + { 0x2070, 0, "zerosuperior" }, + { 0x03B6, 0, "zeta" } +}; diff --git a/vcl/unx/generic/fontmanager/afm_keyword_list b/vcl/unx/generic/fontmanager/afm_keyword_list new file mode 100755 index 000000000000..c9bb13467e3e --- /dev/null +++ b/vcl/unx/generic/fontmanager/afm_keyword_list @@ -0,0 +1,62 @@ +%language=C++ +%global-table +%null-strings +%struct-type +struct hash_entry { const char* name; enum parseKey eKey; }; +%% +Ascender,ASCENDER +Ascent,ASCENT +B,CHARBBOX +C,CODE +CC,COMPCHAR +CH,CODEHEX +CapHeight,CAPHEIGHT +CharWidth,CHARWIDTH +CharacterSet,CHARACTERSET +Characters,CHARACTERS +Comment,COMMENT +Descender,DESCENDER +Descent,DESCENT +Em,EM +EncodingScheme,ENCODINGSCHEME +EndCharMetrics,ENDCHARMETRICS +EndComposites,ENDCOMPOSITES +EndDirection,ENDDIRECTION +EndFontMetrics,ENDFONTMETRICS +EndKernData,ENDKERNDATA +EndKernPairs,ENDKERNPAIRS +EndTrackKern,ENDTRACKKERN +FamilyName,FAMILYNAME +FontBBox,FONTBBOX +FontName,FONTNAME +FullName,FULLNAME +IsBaseFont,ISBASEFONT +IsFixedPitch,ISFIXEDPITCH +ItalicAngle,ITALICANGLE +KP,KERNPAIR +KPX,KERNPAIRXAMT +L,LIGATURE +MappingScheme,MAPPINGSCHEME +MetricsSets,METRICSSETS +N,CHARNAME +Notice,NOTICE +PCC,COMPCHARPIECE +StartCharMetrics,STARTCHARMETRICS +StartComposites,STARTCOMPOSITES +StartDirection,STARTDIRECTION +StartFontMetrics,STARTFONTMETRICS +StartKernData,STARTKERNDATA +StartKernPairs,STARTKERNPAIRS +StartTrackKern,STARTTRACKKERN +StdHW,STDHW +StdVW,STDVW +TrackKern,TRACKKERN +UnderlinePosition,UNDERLINEPOSITION +UnderlineThickness,UNDERLINETHICKNESS +V,VVECTOR +Version,VERSION +W,XYWIDTH +W0X,X0WIDTH +WX,XWIDTH +Weight,WEIGHT +XHeight,XHEIGHT diff --git a/vcl/unx/generic/fontmanager/fontcache.cxx b/vcl/unx/generic/fontmanager/fontcache.cxx new file mode 100644 index 000000000000..96953ab9fc24 --- /dev/null +++ b/vcl/unx/generic/fontmanager/fontcache.cxx @@ -0,0 +1,817 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <cstdlib> +#include <cstring> + +#include "fontcache.hxx" + +#include "osl/thread.h" + +#include "unotools/atom.hxx" + +#include "tools/stream.hxx" + +#include <unistd.h> +#include <sys/stat.h> + +#if OSL_DEBUG_LEVEL >1 +#include <cstdio> +#endif + +#define FONTCACHEFILE "/user/psprint/pspfontcache" +#define CACHE_MAGIC "PspFontCacheFile format 4" + +using namespace std; +using namespace psp; +using namespace utl; + +using ::rtl::OUString; +using ::rtl::OString; +using ::rtl::OUStringToOString; + +/* + * static helpers + */ + +/* + * FontCache constructor + */ + +FontCache::FontCache() +{ + m_bDoFlush = false; + m_aCacheFile = getOfficePath( UserPath ); + if( m_aCacheFile.Len() ) + { + m_aCacheFile.AppendAscii( FONTCACHEFILE ); + read(); + } +} + +/* + * FontCache destructor + */ + +FontCache::~FontCache() +{ + clearCache(); +} + +/* + * FontCache::clearCache + */ +void FontCache::clearCache() +{ + for( FontCacheData::iterator dir_it = m_aCache.begin(); dir_it != m_aCache.end(); ++dir_it ) + { + for( FontDirMap::iterator entry_it = dir_it->second.m_aEntries.begin(); entry_it != dir_it->second.m_aEntries.end(); ++entry_it ) + { + for( FontCacheEntry::iterator font_it = entry_it->second.m_aEntry.begin(); font_it != entry_it->second.m_aEntry.end(); ++font_it ) + delete *font_it; + } + } + m_aCache.clear(); +} + +/* + * FontCache::Commit + */ + +void FontCache::flush() +{ + if( ! m_bDoFlush || ! m_aCacheFile.Len() ) + return; + + SvFileStream aStream; + aStream.Open( m_aCacheFile, STREAM_WRITE | STREAM_TRUNC ); + if( ! (aStream.IsOpen() && aStream.IsWritable()) ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "FontCache::flush: opening cache file %s failed\n", ByteString( m_aCacheFile, osl_getThreadTextEncoding() ).GetBuffer() ); +#endif + return; + } + + aStream.SetLineDelimiter( LINEEND_LF ); + aStream.WriteLine( ByteString( CACHE_MAGIC ) ); + + PrintFontManager& rManager( PrintFontManager::get() ); + MultiAtomProvider* pAtoms = rManager.m_pAtoms; + + for( FontCacheData::const_iterator dir_it = m_aCache.begin(); dir_it != m_aCache.end(); ++ dir_it ) + { + const FontDirMap& rDir( dir_it->second.m_aEntries ); + + ByteString aDirectory( rManager.getDirectory( dir_it->first ) ); + ByteString aLine( "FontCacheDirectory:" ); + aLine.Append( ByteString::CreateFromInt64( dir_it->second.m_nTimestamp ) ); + aLine.Append( ':' ); + aLine.Append( aDirectory ); + if( rDir.empty() && dir_it->second.m_bNoFiles ) + aLine.Insert( "Empty", 0 ); + aStream.WriteLine( aLine ); + + for( FontDirMap::const_iterator entry_it = rDir.begin(); entry_it != rDir.end(); ++entry_it ) + { + // insert cache entries + const FontCacheEntry& rEntry( entry_it->second.m_aEntry ); + if( rEntry.begin() == rEntry.end() ) + continue; + + aLine = "File:"; + aLine.Append( ByteString( entry_it->first ) ); + aStream.WriteLine( aLine ); + + int nEntrySize = entry_it->second.m_aEntry.size(); + // write: type;nfonts + aLine = ByteString::CreateFromInt32( rEntry.front()->m_eType ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( nEntrySize ) ); + aStream.WriteLine( aLine ); + + sal_Int32 nSubEntry = 0; + for( FontCacheEntry::const_iterator it = rEntry.begin(); it != rEntry.end(); ++it, nSubEntry++ ) + { + /* + * for each font entry write: + * name[;name[;name]] + * fontnr;PSName;italic;weight;width;pitch;encoding;ascend;descend;leading;vsubst;gxw;gxh;gyw;gyh;useroverrride;embed;antialias[;{metricfile,typeflags}][;stylename] + */ + if( nEntrySize > 1 ) + nSubEntry = static_cast<const PrintFontManager::TrueTypeFontFile*>(*it)->m_nCollectionEntry; + else + nSubEntry = -1; + + aLine = OUStringToOString( pAtoms->getString( ATOM_FAMILYNAME, (*it)->m_nFamilyName ), RTL_TEXTENCODING_UTF8 ); + for( ::std::list< int >::const_iterator name_it = (*it)->m_aAliases.begin(); name_it != (*it)->m_aAliases.end(); ++name_it ) + { + const OUString& rAdd( pAtoms->getString( ATOM_FAMILYNAME, *name_it ) ); + if( rAdd.getLength() ) + { + aLine.Append( ';' ); + aLine.Append( ByteString( String( rAdd ), RTL_TEXTENCODING_UTF8 ) ); + } + } + aStream.WriteLine( aLine ); + + const OUString& rPSName( pAtoms->getString( ATOM_PSNAME, (*it)->m_nPSName ) ); + aLine = ByteString::CreateFromInt32( nSubEntry ); + aLine.Append( ';' ); + aLine.Append( ByteString( String( rPSName ), RTL_TEXTENCODING_UTF8 ) ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( (*it)->m_eItalic ) ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( (*it)->m_eWeight ) ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( (*it)->m_eWidth ) ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( (*it)->m_ePitch ) ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( (*it)->m_aEncoding ) ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( (*it)->m_nAscend ) ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( (*it)->m_nDescend ) ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( (*it)->m_nLeading ) ); + aLine.Append( ';' ); + aLine.Append( (*it)->m_bHaveVerticalSubstitutedGlyphs ? "1" : "0" ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( (*it)->m_aGlobalMetricX.width ) ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( (*it)->m_aGlobalMetricX.height ) ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( (*it)->m_aGlobalMetricY.width ) ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( (*it)->m_aGlobalMetricY.height ) ); + aLine.Append( ';' ); + aLine.Append( (*it)->m_bUserOverride ? "1" : "0" ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( 0 ) ); + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( 0 ) ); + + switch( (*it)->m_eType ) + { + case fonttype::Type1: + aLine.Append( ';' ); + aLine.Append( ByteString( static_cast<const PrintFontManager::Type1FontFile*>(*it)->m_aMetricFile ) ); + break; + case fonttype::TrueType: + aLine.Append( ';' ); + aLine.Append( ByteString::CreateFromInt32( static_cast<const PrintFontManager::TrueTypeFontFile*>(*it)->m_nTypeFlags ) ); + break; + default: break; + } + if( (*it)->m_aStyleName.getLength() ) + { + aLine.Append( ';' ); + aLine.Append( ByteString( String( (*it)->m_aStyleName ), RTL_TEXTENCODING_UTF8 ) ); + } + aStream.WriteLine( aLine ); + } + aStream.WriteLine( ByteString() ); + } + } + m_bDoFlush = false; +} + +/* + * FontCache::read + */ + +void FontCache::read() +{ + PrintFontManager& rManager( PrintFontManager::get() ); + MultiAtomProvider* pAtoms = rManager.m_pAtoms; + + SvFileStream aStream( m_aCacheFile, STREAM_READ ); + if( ! aStream.IsOpen() ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "FontCache::read: opening cache file %s failed\n", ByteString( m_aCacheFile, osl_getThreadTextEncoding() ).GetBuffer() ); +#endif + return; + } + + + ByteString aLine; + aStream.ReadLine( aLine ); + if( !aLine.Equals( CACHE_MAGIC ) ) + { + #if OSL_DEBUG_LEVEL >1 + fprintf( stderr, "FontCache::read: cache file %s fails magic test\n", ByteString( m_aCacheFile, osl_getThreadTextEncoding() ).GetBuffer() ); + #endif + return; + } + + int nDir = 0; + FontDirMap* pDir = NULL; + xub_StrLen nIndex; + bool bKeepOnlyUserOverridden = false; + do + { + aStream.ReadLine( aLine ); + if( aLine.CompareTo( "FontCacheDirectory:", 19 ) == COMPARE_EQUAL || + aLine.CompareTo( "EmptyFontCacheDirectory:", 24 ) == COMPARE_EQUAL ) + { + bool bEmpty = (aLine.CompareTo( "Empty", 5 ) == COMPARE_EQUAL); + xub_StrLen nSearchIndex = bEmpty ? 24 : 19; + + OString aDir; + sal_Int64 nTimestamp = 0; + xub_StrLen nTEnd = aLine.Search( ':', nSearchIndex ); + if( nTEnd != STRING_NOTFOUND ) + { + nTimestamp = aLine.Copy( nSearchIndex, nTEnd - nSearchIndex ).ToInt64(); + aDir = aLine.Copy( nTEnd+1 ); + } + else + { + // invalid format, remove + pDir = NULL; + nDir = 0; + m_bDoFlush = true; + continue; + } + + // is the directory modified ? + struct stat aStat; + if( stat( aDir.getStr(), &aStat ) || + ! S_ISDIR(aStat.st_mode) ) + { + // remove outdated cache data + pDir = NULL; + nDir = 0; + m_bDoFlush = true; + continue; + } + else + { + nDir = rManager.getDirectoryAtom( aDir, true ); + m_aCache[ nDir ].m_nTimestamp = (sal_Int64)aStat.st_mtime; + m_aCache[ nDir ].m_bNoFiles = bEmpty; + pDir = bEmpty ? NULL : &m_aCache[ nDir ].m_aEntries; + bKeepOnlyUserOverridden = ((sal_Int64)aStat.st_mtime != nTimestamp); + m_aCache[ nDir ].m_bUserOverrideOnly = bKeepOnlyUserOverridden; + } + } + else if( pDir && aLine.CompareTo( "File:", 5 ) == COMPARE_EQUAL ) + { + OString aFile( aLine.Copy( 5 ) ); + aStream.ReadLine( aLine ); + + const char* pLine = aLine.GetBuffer(); + + fonttype::type eType = (fonttype::type)atoi( pLine ); + if( eType != fonttype::TrueType && + eType != fonttype::Type1 && + eType != fonttype::Builtin + ) + continue; + while( *pLine && *pLine != ';' ) + pLine++; + if( *pLine != ';' ) + continue; + + pLine++; + sal_Int32 nFonts = atoi( pLine ); + for( int n = 0; n < nFonts; n++ ) + { + aStream.ReadLine( aLine ); + pLine = aLine.GetBuffer(); + int nLen = aLine.Len(); + + PrintFontManager::PrintFont* pFont = NULL; + switch( eType ) + { + case fonttype::TrueType: + pFont = new PrintFontManager::TrueTypeFontFile(); + break; + case fonttype::Type1: + pFont = new PrintFontManager::Type1FontFile(); + break; + case fonttype::Builtin: + pFont = new PrintFontManager::BuiltinFont(); + break; + default: break; + } + + for( nIndex = 0; nIndex < nLen && pLine[nIndex] != ';'; nIndex++ ) + ; + + pFont->m_nFamilyName = pAtoms->getAtom( ATOM_FAMILYNAME, + OUString( pLine, nIndex, RTL_TEXTENCODING_UTF8 ), + sal_True ); + while( nIndex < nLen ) + { + xub_StrLen nLastIndex = nIndex+1; + for( nIndex = nLastIndex ; nIndex < nLen && pLine[nIndex] != ';'; nIndex++ ) + ; + if( nIndex - nLastIndex ) + { + OUString aAlias( pLine+nLastIndex, nIndex-nLastIndex, RTL_TEXTENCODING_UTF8 ); + pFont->m_aAliases.push_back( pAtoms->getAtom( ATOM_FAMILYNAME, aAlias, sal_True ) ); + } + } + aStream.ReadLine( aLine ); + pLine = aLine.GetBuffer(); + nLen = aLine.Len(); + + // get up to 20 token positions + const int nMaxTokens = 20; + int nTokenPos[nMaxTokens]; + nTokenPos[0] = 0; + int nTokens = 1; + for( int i = 0; i < nLen; i++ ) + { + if( pLine[i] == ';' ) + { + nTokenPos[nTokens++] = i+1; + if( nTokens == nMaxTokens ) + break; + } + } + if( nTokens < 18 ) + { + delete pFont; + continue; + } + int nCollEntry = atoi( pLine ); + pFont->m_nPSName = pAtoms->getAtom( ATOM_PSNAME, OUString( pLine + nTokenPos[1], nTokenPos[2]-nTokenPos[1]-1, RTL_TEXTENCODING_UTF8 ), sal_True ); + pFont->m_eItalic = (italic::type)atoi( pLine+nTokenPos[2] ); + pFont->m_eWeight = (weight::type)atoi( pLine+nTokenPos[3] ); + pFont->m_eWidth = (width::type)atoi( pLine+nTokenPos[4] ); + pFont->m_ePitch = (pitch::type)atoi( pLine+nTokenPos[5] ); + pFont->m_aEncoding = (rtl_TextEncoding)atoi( pLine+nTokenPos[6] ); + pFont->m_nAscend = atoi( pLine + nTokenPos[7] ); + pFont->m_nDescend = atoi( pLine + nTokenPos[8] ); + pFont->m_nLeading = atoi( pLine + nTokenPos[9] ); + pFont->m_bHaveVerticalSubstitutedGlyphs + = (atoi( pLine + nTokenPos[10] ) != 0); + pFont->m_aGlobalMetricX.width + = atoi( pLine + nTokenPos[11] ); + pFont->m_aGlobalMetricX.height + = atoi( pLine + nTokenPos[12] ); + pFont->m_aGlobalMetricY.width + = atoi( pLine + nTokenPos[13] ); + pFont->m_aGlobalMetricY.height + = atoi( pLine + nTokenPos[14] ); + pFont->m_bUserOverride + = (atoi( pLine + nTokenPos[15] ) != 0); + int nStyleTokenNr = 18; + switch( eType ) + { + case fonttype::TrueType: + static_cast<PrintFontManager::TrueTypeFontFile*>(pFont)->m_nTypeFlags = atoi( pLine + nTokenPos[18] ); + static_cast<PrintFontManager::TrueTypeFontFile*>(pFont)->m_nCollectionEntry = nCollEntry; + static_cast<PrintFontManager::TrueTypeFontFile*>(pFont)->m_nDirectory = nDir; + static_cast<PrintFontManager::TrueTypeFontFile*>(pFont)->m_aFontFile = aFile; + nStyleTokenNr++; + break; + case fonttype::Type1: + { + int nTokLen = (nTokens > 19 ) ? nTokenPos[19]-nTokenPos[18]-1 : nLen - nTokenPos[18]; + static_cast<PrintFontManager::Type1FontFile*>(pFont)->m_aMetricFile = OString( pLine + nTokenPos[18], nTokLen ); + static_cast<PrintFontManager::Type1FontFile*>(pFont)->m_nDirectory = nDir; + static_cast<PrintFontManager::Type1FontFile*>(pFont)->m_aFontFile = aFile; + nStyleTokenNr++; + } + break; + case fonttype::Builtin: + static_cast<PrintFontManager::BuiltinFont*>(pFont)->m_nDirectory = nDir; + static_cast<PrintFontManager::BuiltinFont*>(pFont)->m_aMetricFile = aFile; + break; + default: break; + } + if( nTokens > nStyleTokenNr ) + pFont->m_aStyleName = OUString::intern( pLine + nTokenPos[nStyleTokenNr], + nLen - nTokenPos[nStyleTokenNr], + RTL_TEXTENCODING_UTF8 ); + + bool bObsolete = false; + if( bKeepOnlyUserOverridden ) + { + if( pFont->m_bUserOverride ) + { + ByteString aFilePath = rManager.getDirectory( nDir ); + aFilePath.Append( '/' ); + aFilePath.Append( ByteString(aFile) ); + struct stat aStat; + if( stat( aFilePath.GetBuffer(), &aStat ) || + ! S_ISREG( aStat.st_mode ) || + aStat.st_size < 16 ) + { + bObsolete = true; + } + #if OSL_DEBUG_LEVEL > 2 + else + fprintf( stderr, "keeping file %s in outdated cache entry due to user override\n", + aFilePath.GetBuffer() ); + #endif + } + else + bObsolete = true; + } + if( bObsolete ) + { + m_bDoFlush = true; +#if OSL_DEBUG_LEVEL > 2 + fprintf( stderr, "removing obsolete font %s\n", aFile.getStr() ); +#endif + delete pFont; + continue; + } + + FontCacheEntry& rEntry = (*pDir)[aFile].m_aEntry; + rEntry.push_back( pFont ); + } + } + } while( ! aStream.IsEof() ); +} + +/* + * FontCache::updateDirTimestamp + */ +void FontCache::updateDirTimestamp( int nDirID ) +{ + PrintFontManager& rManager( PrintFontManager::get() ); + const OString& rDir = rManager.getDirectory( nDirID ); + + struct stat aStat; + if( ! stat( rDir.getStr(), &aStat ) ) + m_aCache[ nDirID ].m_nTimestamp = (sal_Int64)aStat.st_mtime; +} + + +/* + * FontCache::copyPrintFont + */ +void FontCache::copyPrintFont( const PrintFontManager::PrintFont* pFrom, PrintFontManager::PrintFont* pTo ) const +{ + if( pFrom->m_eType != pTo->m_eType ) + return; + switch( pFrom->m_eType ) + { + case fonttype::TrueType: + static_cast<PrintFontManager::TrueTypeFontFile*>(pTo)->m_nDirectory = static_cast<const PrintFontManager::TrueTypeFontFile*>(pFrom)->m_nDirectory; + static_cast<PrintFontManager::TrueTypeFontFile*>(pTo)->m_aFontFile = static_cast<const PrintFontManager::TrueTypeFontFile*>(pFrom)->m_aFontFile; + static_cast<PrintFontManager::TrueTypeFontFile*>(pTo)->m_nCollectionEntry = static_cast<const PrintFontManager::TrueTypeFontFile*>(pFrom)->m_nCollectionEntry; + static_cast<PrintFontManager::TrueTypeFontFile*>(pTo)->m_nTypeFlags = static_cast<const PrintFontManager::TrueTypeFontFile*>(pFrom)->m_nTypeFlags; + break; + case fonttype::Type1: + static_cast<PrintFontManager::Type1FontFile*>(pTo)->m_nDirectory = static_cast<const PrintFontManager::Type1FontFile*>(pFrom)->m_nDirectory; + static_cast<PrintFontManager::Type1FontFile*>(pTo)->m_aFontFile = static_cast<const PrintFontManager::Type1FontFile*>(pFrom)->m_aFontFile; + static_cast<PrintFontManager::Type1FontFile*>(pTo)->m_aMetricFile = static_cast<const PrintFontManager::Type1FontFile*>(pFrom)->m_aMetricFile; + break; + case fonttype::Builtin: + static_cast<PrintFontManager::BuiltinFont*>(pTo)->m_nDirectory = static_cast<const PrintFontManager::BuiltinFont*>(pFrom)->m_nDirectory; + static_cast<PrintFontManager::BuiltinFont*>(pTo)->m_aMetricFile = static_cast<const PrintFontManager::BuiltinFont*>(pFrom)->m_aMetricFile; + break; + default: break; + } + pTo->m_nFamilyName = pFrom->m_nFamilyName; + pTo->m_aStyleName = pFrom->m_aStyleName; + pTo->m_aAliases = pFrom->m_aAliases; + pTo->m_nPSName = pFrom->m_nPSName; + pTo->m_eItalic = pFrom->m_eItalic; + pTo->m_eWeight = pFrom->m_eWeight; + pTo->m_eWidth = pFrom->m_eWidth; + pTo->m_ePitch = pFrom->m_ePitch; + pTo->m_aEncoding = pFrom->m_aEncoding; + pTo->m_aGlobalMetricX = pFrom->m_aGlobalMetricX; + pTo->m_aGlobalMetricY = pFrom->m_aGlobalMetricY; + pTo->m_nAscend = pFrom->m_nAscend; + pTo->m_nDescend = pFrom->m_nDescend; + pTo->m_nLeading = pFrom->m_nLeading; + pTo->m_nXMin = pFrom->m_nXMin; + pTo->m_nYMin = pFrom->m_nYMin; + pTo->m_nXMax = pFrom->m_nXMax; + pTo->m_nYMax = pFrom->m_nYMax; + pTo->m_bHaveVerticalSubstitutedGlyphs = pFrom->m_bHaveVerticalSubstitutedGlyphs; + pTo->m_bUserOverride = pFrom->m_bUserOverride; +} + +/* + * FontCache::equalsPrintFont + */ +bool FontCache::equalsPrintFont( const PrintFontManager::PrintFont* pLeft, PrintFontManager::PrintFont* pRight ) const +{ + if( pLeft->m_eType != pRight->m_eType ) + return false; + switch( pLeft->m_eType ) + { + case fonttype::TrueType: + { + const PrintFontManager::TrueTypeFontFile* pLT = static_cast<const PrintFontManager::TrueTypeFontFile*>(pLeft); + const PrintFontManager::TrueTypeFontFile* pRT = static_cast<const PrintFontManager::TrueTypeFontFile*>(pRight); + if( pRT->m_nDirectory != pLT->m_nDirectory || + pRT->m_aFontFile != pLT->m_aFontFile || + pRT->m_nCollectionEntry != pLT->m_nCollectionEntry || + pRT->m_nTypeFlags != pLT->m_nTypeFlags ) + return false; + } + break; + case fonttype::Type1: + { + const PrintFontManager::Type1FontFile* pLT = static_cast<const PrintFontManager::Type1FontFile*>(pLeft); + const PrintFontManager::Type1FontFile* pRT = static_cast<const PrintFontManager::Type1FontFile*>(pRight); + if( pRT->m_nDirectory != pLT->m_nDirectory || + pRT->m_aFontFile != pLT->m_aFontFile || + pRT->m_aMetricFile != pLT->m_aMetricFile ) + return false; + } + break; + case fonttype::Builtin: + { + const PrintFontManager::BuiltinFont* pLT = static_cast<const PrintFontManager::BuiltinFont*>(pLeft); + const PrintFontManager::BuiltinFont* pRT = static_cast<const PrintFontManager::BuiltinFont*>(pRight); + if( pRT->m_nDirectory != pLT->m_nDirectory || + pRT->m_aMetricFile != pLT->m_aMetricFile ) + return false; + } + break; + default: break; + } + if( pRight->m_nFamilyName != pLeft->m_nFamilyName || + pRight->m_aStyleName != pLeft->m_aStyleName || + pRight->m_nPSName != pLeft->m_nPSName || + pRight->m_eItalic != pLeft->m_eItalic || + pRight->m_eWeight != pLeft->m_eWeight || + pRight->m_eWidth != pLeft->m_eWidth || + pRight->m_ePitch != pLeft->m_ePitch || + pRight->m_aEncoding != pLeft->m_aEncoding || + pRight->m_aGlobalMetricX != pLeft->m_aGlobalMetricX || + pRight->m_aGlobalMetricY != pLeft->m_aGlobalMetricY || + pRight->m_nAscend != pLeft->m_nAscend || + pRight->m_nDescend != pLeft->m_nDescend || + pRight->m_nLeading != pLeft->m_nLeading || + pRight->m_nXMin != pLeft->m_nXMin || + pRight->m_nYMin != pLeft->m_nYMin || + pRight->m_nXMax != pLeft->m_nXMax || + pRight->m_nYMax != pLeft->m_nYMax || + pRight->m_bHaveVerticalSubstitutedGlyphs != pLeft->m_bHaveVerticalSubstitutedGlyphs || + pRight->m_bUserOverride != pLeft->m_bUserOverride + ) + return false; + std::list< int >::const_iterator lit, rit; + for( lit = pLeft->m_aAliases.begin(), rit = pRight->m_aAliases.begin(); + lit != pLeft->m_aAliases.end() && rit != pRight->m_aAliases.end() && (*lit) == (*rit); + ++lit, ++rit ) + ; + return lit == pLeft->m_aAliases.end() && rit == pRight->m_aAliases.end(); +} + +/* + * FontCache::clonePrintFont + */ +PrintFontManager::PrintFont* FontCache::clonePrintFont( const PrintFontManager::PrintFont* pOldFont ) const +{ + PrintFontManager::PrintFont* pFont = NULL; + switch( pOldFont->m_eType ) + { + case fonttype::TrueType: + pFont = new PrintFontManager::TrueTypeFontFile(); + break; + case fonttype::Type1: + pFont = new PrintFontManager::Type1FontFile(); + break; + case fonttype::Builtin: + pFont = new PrintFontManager::BuiltinFont(); + break; + default: break; + } + if( pFont ) + { + copyPrintFont( pOldFont, pFont ); + } + return pFont; + } + +/* + * FontCache::getFontCacheFile + */ +bool FontCache::getFontCacheFile( int nDirID, const OString& rFile, list< PrintFontManager::PrintFont* >& rNewFonts ) const +{ + bool bSuccess = false; + + FontCacheData::const_iterator dir = m_aCache.find( nDirID ); + if( dir != m_aCache.end() ) + { + FontDirMap::const_iterator entry = dir->second.m_aEntries.find( rFile ); + if( entry != dir->second.m_aEntries.end() ) + { + for( FontCacheEntry::const_iterator font = entry->second.m_aEntry.begin(); font != entry->second.m_aEntry.end(); ++font ) + { + bSuccess = true; + PrintFontManager::PrintFont* pFont = clonePrintFont( *font ); + rNewFonts.push_back( pFont ); + } + } + } + return bSuccess; +} + +/* + * FontCache::updateFontCacheEntry + */ +void FontCache::updateFontCacheEntry( const PrintFontManager::PrintFont* pFont, bool bFlush ) +{ + PrintFontManager& rManager( PrintFontManager::get() ); + + OString aFile; + int nDirID = 0; + switch( pFont->m_eType ) + { + case fonttype::TrueType: + nDirID = static_cast<const PrintFontManager::TrueTypeFontFile*>(pFont)->m_nDirectory; + aFile = static_cast<const PrintFontManager::TrueTypeFontFile*>(pFont)->m_aFontFile; + break; + case fonttype::Type1: + nDirID = static_cast<const PrintFontManager::Type1FontFile*>(pFont)->m_nDirectory; + aFile = static_cast<const PrintFontManager::Type1FontFile*>(pFont)->m_aFontFile; + break; + case fonttype::Builtin: + nDirID = static_cast<const PrintFontManager::BuiltinFont*>(pFont)->m_nDirectory; + aFile = static_cast<const PrintFontManager::BuiltinFont*>(pFont)->m_aMetricFile; + break; + default: + return; + } + FontCacheData::const_iterator dir = m_aCache.find( nDirID ); + FontDirMap::const_iterator entry; + FontCacheEntry::const_iterator font; + PrintFontManager::PrintFont* pCacheFont = NULL; + + if( dir != m_aCache.end() ) + { + entry = dir->second.m_aEntries.find( aFile ); + if( entry != dir->second.m_aEntries.end() ) + { + for( font = entry->second.m_aEntry.begin(); font != entry->second.m_aEntry.end(); ++font ) + { + if( (*font)->m_eType == pFont->m_eType && + ( (*font)->m_eType != fonttype::TrueType || + static_cast<const PrintFontManager::TrueTypeFontFile*>(*font)->m_nCollectionEntry == static_cast<const PrintFontManager::TrueTypeFontFile*>(pFont)->m_nCollectionEntry + ) ) + break; + } + if( font != entry->second.m_aEntry.end() ) + pCacheFont = *font; + } + } + else + createCacheDir( nDirID ); + + if( pCacheFont ) + { + if( ! equalsPrintFont( pFont, pCacheFont ) ) + { + copyPrintFont( pFont, pCacheFont ); + m_bDoFlush = true; + } + } + else + { + pCacheFont = clonePrintFont( pFont ); + m_aCache[nDirID].m_aEntries[aFile].m_aEntry.push_back( pCacheFont ); + + ByteString aPath = rManager.getDirectory( nDirID ); + aPath.Append( '/' ); + aPath.Append( ByteString( aFile ) ); + m_bDoFlush = true; + } + if( bFlush ) + flush(); +} + +/* + * FontCache::listDirectory + */ +bool FontCache::listDirectory( const OString& rDir, std::list< PrintFontManager::PrintFont* >& rNewFonts ) const +{ + PrintFontManager& rManager( PrintFontManager::get() ); + int nDirID = rManager.getDirectoryAtom( rDir ); + FontCacheData::const_iterator dir = m_aCache.find( nDirID ); + bool bFound = (dir != m_aCache.end()); + + if( bFound && !dir->second.m_bNoFiles ) + { + for( FontDirMap::const_iterator file = dir->second.m_aEntries.begin(); file != dir->second.m_aEntries.end(); ++file ) + { + for( FontCacheEntry::const_iterator font = file->second.m_aEntry.begin(); font != file->second.m_aEntry.end(); ++font ) + { + PrintFontManager::PrintFont* pFont = clonePrintFont( *font ); + rNewFonts.push_back( pFont ); + } + } + } + return bFound; +} + +/* + * FontCache::listDirectory + */ +bool FontCache::scanAdditionalFiles( const OString& rDir ) +{ + PrintFontManager& rManager( PrintFontManager::get() ); + int nDirID = rManager.getDirectoryAtom( rDir ); + FontCacheData::const_iterator dir = m_aCache.find( nDirID ); + bool bFound = (dir != m_aCache.end()); + + return (bFound && dir->second.m_bUserOverrideOnly); +} + +/* + * FontCache::createCacheDir + */ +void FontCache::createCacheDir( int nDirID ) +{ + PrintFontManager& rManager( PrintFontManager::get() ); + + const OString& rDir = rManager.getDirectory( nDirID ); + struct stat aStat; + if( ! stat( rDir.getStr(), &aStat ) ) + m_aCache[nDirID].m_nTimestamp = (sal_Int64)aStat.st_mtime; +} + +/* + * FontCache::markEmptyDir + */ +void FontCache::markEmptyDir( int nDirID, bool bNoFiles ) +{ + createCacheDir( nDirID ); + m_aCache[nDirID].m_bNoFiles = bNoFiles; + m_bDoFlush = true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/fontmanager/fontconfig.cxx b/vcl/unx/generic/fontmanager/fontconfig.cxx new file mode 100644 index 000000000000..fe595290160a --- /dev/null +++ b/vcl/unx/generic/fontmanager/fontconfig.cxx @@ -0,0 +1,1444 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "fontcache.hxx" +#include "impfont.hxx" +#include "vcl/fontmanager.hxx" + +using namespace psp; + +#ifdef ENABLE_FONTCONFIG + #include <fontconfig/fontconfig.h> + #include <ft2build.h> + #include <fontconfig/fcfreetype.h> + // allow compile on baseline (currently with fontconfig 2.2.0) + #ifndef FC_WEIGHT_BOOK // TODO: remove when baseline moves to fc>=2.2.1 + #define FC_WEIGHT_BOOK 75 + #endif + #ifndef FC_EMBEDDED_BITMAP // TODO: remove when baseline moves to fc>=2.3.92 + #define FC_EMBEDDED_BITMAP "embeddedbitmap" + #endif + #ifndef FC_FAMILYLANG // TODO: remove when baseline moves to fc>=2.2.97 + #define FC_FAMILYLANG "familylang" + #endif + #ifndef FC_CAPABILITY // TODO: remove when baseline moves to fc>=2.2.97 + #define FC_CAPABILITY "capability" + #endif + #ifndef FC_STYLELANG // TODO: remove when baseline moves to fc>=2.2.97 + #define FC_STYLELANG "stylelang" + #endif + #ifndef FC_HINT_STYLE // TODO: remove when baseline moves to fc>=2.2.91 + #define FC_HINT_STYLE "hintstyle" + #define FC_HINT_NONE 0 + #define FC_HINT_SLIGHT 1 + #define FC_HINT_MEDIUM 2 + #define FC_HINT_FULL 3 + #endif + #ifndef FC_FT_FACE + #define FC_FT_FACE "ftface" + #endif + #ifndef FC_EMBOLDEN + #define FC_EMBOLDEN "embolden" + #endif +#else + typedef void FcConfig; + typedef void FcObjectSet; + typedef void FcPattern; + typedef void FcFontSet; + typedef void FcCharSet; + typedef int FcResult; + typedef int FcBool; + typedef int FcMatchKind; + typedef char FcChar8; + typedef int FcChar32; + typedef unsigned int FT_UInt; + typedef void* FT_Face; + typedef int FcSetName; +#endif + +#include <cstdio> +#include <cstdarg> + +#include "unotools/atom.hxx" + +#include "osl/module.h" +#include "osl/thread.h" +#include "osl/process.h" + +#include "rtl/ustrbuf.hxx" +#include "rtl/locale.hxx" + +#include "sal/alloca.h" + +#include <utility> +#include <algorithm> + +using namespace osl; +using ::rtl::OUString; +using ::rtl::OUStringBuffer; +using ::rtl::OString; + +namespace +{ + typedef std::pair<FcChar8*, FcChar8*> lang_and_element; +} + +class FontCfgWrapper +{ + oslModule m_pLib; + FcFontSet* m_pOutlineSet; + + int m_nFcVersion; + FcBool (*m_pFcInit)(); + void (*m_pFcFini)(); + int (*m_pFcGetVersion)(); + FcConfig* (*m_pFcConfigGetCurrent)(); + FcObjectSet* (*m_pFcObjectSetVaBuild)(const char*,va_list); + void (*m_pFcObjectSetDestroy)(FcObjectSet* pSet); + FcPattern* (*m_pFcPatternCreate)(); + void (*m_pFcPatternDestroy)(FcPattern*); + FcBool (*m_pFcPatternEqual)(const FcPattern*,const FcPattern*); + FcFontSet* (*m_pFcConfigGetFonts)(FcConfig*,FcSetName); + FcFontSet* (*m_pFcFontSetCreate)(); + FcCharSet* (*m_pFcCharSetCreate)(); + FcBool (*m_pFcCharSetAddChar)(FcCharSet *, FcChar32); + FcBool (*m_pFcCharSetHasChar)(FcCharSet *, FcChar32); + void (*m_pFcCharSetDestroy)(FcCharSet*); + void (*m_pFcFontSetDestroy)(FcFontSet*); + FcBool (*m_pFcFontSetAdd)(FcFontSet*,FcPattern*); + void (*m_pFcPatternReference)(FcPattern*); + FcResult (*m_pFcPatternGetCharSet)(const FcPattern*,const char*,int,FcCharSet**); + FcResult (*m_pFcPatternGetString)(const FcPattern*,const char*,int,FcChar8**); + FcResult (*m_pFcPatternGetInteger)(const FcPattern*,const char*,int,int*); + FcResult (*m_pFcPatternGetDouble)(const FcPattern*,const char*,int,double*); + FcResult (*m_pFcPatternGetBool)(const FcPattern*,const char*,int,FcBool*); + void (*m_pFcDefaultSubstitute)(FcPattern *); + FcPattern* (*m_pFcFontSetMatch)(FcConfig*,FcFontSet**, int, FcPattern*,FcResult*); + FcPattern* (*m_pFcFontMatch)(FcConfig*,FcPattern*,FcResult*); + FcBool (*m_pFcConfigAppFontAddFile)(FcConfig*, const FcChar8*); + FcBool (*m_pFcConfigAppFontAddDir)(FcConfig*, const FcChar8*); + FcBool (*m_pFcConfigParseAndLoad)(FcConfig*,const FcChar8*,FcBool); + FcBool (*m_pFcConfigSubstitute)(FcConfig*,FcPattern*,FcMatchKind); + + FcPattern* (*m_pFcPatternDuplicate)(const FcPattern*); + FcBool (*m_pFcPatternAddInteger)(FcPattern*,const char*,int); + FcBool (*m_pFcPatternAddDouble)(FcPattern*,const char*,double); + FcBool (*m_pFcPatternAddBool)(FcPattern*,const char*,FcBool); + FcBool (*m_pFcPatternAddCharSet)(FcPattern*,const char*,const FcCharSet*); + FcBool (*m_pFcPatternAddString)(FcPattern*,const char*,const FcChar8*); + FcBool (*m_pFcPatternDel)(FcPattern*,const char*); + + FT_UInt (*m_pFcFreeTypeCharIndex)(FT_Face,FcChar32); + FcBool (*m_pFcPatternAddFTFace)(FcPattern*,const char*,const FT_Face); + + oslGenericFunction loadSymbol( const char* ); + void addFontSet( FcSetName ); + + FontCfgWrapper(); + ~FontCfgWrapper(); + +public: + static FontCfgWrapper& get(); + static void release(); + + bool isValid() const + { return m_pLib != NULL;} + + FcFontSet* getFontSet(); + + FcBool FcInit() + { return m_pFcInit(); } + + void FcFini() + { + //To-Do: get gtk vclplug smoketest to pass + //if (m_pFcFini) m_pFcFini(); + } + + int FcGetVersion() + { return m_pFcGetVersion(); } + + FcConfig* FcConfigGetCurrent() + { return m_pFcConfigGetCurrent(); } + + FcObjectSet* FcObjectSetBuild( const char* first, ... ) + { + va_list ap; + va_start( ap, first ); + FcObjectSet* pSet = m_pFcObjectSetVaBuild( first, ap ); + va_end( ap ); + return pSet; + } + + void FcObjectSetDestroy( FcObjectSet* pSet ) + { m_pFcObjectSetDestroy( pSet ); } + + FcPattern* FcPatternCreate() + { return m_pFcPatternCreate(); } + + void FcPatternDestroy( FcPattern* pPattern ) + { if (m_pFcPatternDestroy) m_pFcPatternDestroy( pPattern ); } + + FcBool FcPatternEqual( const FcPattern* pPatternA, const FcPattern *pPatternB ) + { return m_pFcPatternEqual( pPatternA, pPatternB ); } + + FcFontSet* FcConfigGetFonts( FcConfig* pConfig, FcSetName eSet) + { return m_pFcConfigGetFonts( pConfig, eSet ); } + + FcFontSet* FcFontSetCreate() + { return m_pFcFontSetCreate(); } + + FcCharSet* FcCharSetCreate() + { return m_pFcCharSetCreate(); } + + FcBool FcCharSetAddChar(FcCharSet *fcs, FcChar32 ucs4) + { return m_pFcCharSetAddChar(fcs, ucs4); } + + FcBool FcCharSetHasChar(FcCharSet *fcs, FcChar32 ucs4) + { return m_pFcCharSetHasChar(fcs, ucs4); } + + void FcCharSetDestroy( FcCharSet* pSet ) + { m_pFcCharSetDestroy( pSet );} + + void FcFontSetDestroy( FcFontSet* pSet ) + { m_pFcFontSetDestroy( pSet );} + + FcBool FcFontSetAdd( FcFontSet* pSet, FcPattern* pPattern ) + { return m_pFcFontSetAdd( pSet, pPattern ); } + + void FcPatternReference( FcPattern* pPattern ) + { m_pFcPatternReference( pPattern ); } + + FcResult FcPatternGetCharSet( const FcPattern* pPattern, const char* object, int n, FcCharSet** s ) + { return m_pFcPatternGetCharSet( pPattern, object, n, s ); } + + FcResult FcPatternGetString( const FcPattern* pPattern, const char* object, int n, FcChar8** s ) + { return m_pFcPatternGetString( pPattern, object, n, s ); } + + FcResult FcPatternGetInteger( const FcPattern* pPattern, const char* object, int n, int* s ) + { return m_pFcPatternGetInteger( pPattern, object, n, s ); } + + FcResult FcPatternGetDouble( const FcPattern* pPattern, const char* object, int n, double* s ) + { return m_pFcPatternGetDouble( pPattern, object, n, s ); } + + FcResult FcPatternGetBool( const FcPattern* pPattern, const char* object, int n, FcBool* s ) + { return m_pFcPatternGetBool( pPattern, object, n, s ); } + FcBool FcConfigAppFontAddFile( FcConfig* pConfig, const FcChar8* pFileName ) + { return m_pFcConfigAppFontAddFile( pConfig, pFileName ); } + FcBool FcConfigAppFontAddDir(FcConfig* pConfig, const FcChar8* pDirName ) + { return m_pFcConfigAppFontAddDir( pConfig, pDirName ); } + FcBool FcConfigParseAndLoad( FcConfig* pConfig, const FcChar8* pFileName, FcBool bComplain ) + { return m_pFcConfigParseAndLoad( pConfig, pFileName, bComplain ); } + + void FcDefaultSubstitute( FcPattern* pPattern ) + { m_pFcDefaultSubstitute( pPattern ); } + FcPattern* FcFontSetMatch( FcConfig* pConfig, FcFontSet **ppFontSet, int nset, FcPattern* pPattern, FcResult* pResult ) + { return m_pFcFontSetMatch ? m_pFcFontSetMatch( pConfig, ppFontSet, nset, pPattern, pResult ) : 0; } + FcPattern* FcFontMatch( FcConfig* pConfig, FcPattern* pPattern, FcResult* pResult ) + { return m_pFcFontMatch( pConfig, pPattern, pResult ); } + FcBool FcConfigSubstitute( FcConfig* pConfig, FcPattern* pPattern, FcMatchKind eKind ) + { return m_pFcConfigSubstitute( pConfig, pPattern, eKind ); } + + FcPattern* FcPatternDuplicate( const FcPattern* pPattern ) const + { return m_pFcPatternDuplicate( pPattern ); } + FcBool FcPatternAddInteger( FcPattern* pPattern, const char* pObject, int nValue ) + { return m_pFcPatternAddInteger( pPattern, pObject, nValue ); } + FcBool FcPatternAddDouble( FcPattern* pPattern, const char* pObject, double nValue ) + { return m_pFcPatternAddDouble( pPattern, pObject, nValue ); } + FcBool FcPatternAddString( FcPattern* pPattern, const char* pObject, const FcChar8* pString ) + { return m_pFcPatternAddString( pPattern, pObject, pString ); } + FcBool FcPatternAddBool( FcPattern* pPattern, const char* pObject, bool nValue ) + { return m_pFcPatternAddBool( pPattern, pObject, nValue ); } + FcBool FcPatternAddCharSet(FcPattern* pPattern,const char* pObject,const FcCharSet*pCharSet) + { return m_pFcPatternAddCharSet(pPattern,pObject,pCharSet); } + FcBool FcPatternDel(FcPattern* pPattern, const char* object) + { return m_pFcPatternDel( pPattern, object); } + + FT_UInt FcFreeTypeCharIndex( FT_Face face, FcChar32 ucs4 ) + { return m_pFcFreeTypeCharIndex ? m_pFcFreeTypeCharIndex( face, ucs4 ) : 0; } + FcBool FcPatternAddFTFace( FcPattern* pPattern, const char* pObject, const FT_Face nValue ) + { + return m_pFcPatternAddFTFace + ? m_pFcPatternAddFTFace( pPattern, pObject, nValue ) + : false; + } +public: + FcResult LocalizedElementFromPattern(FcPattern* pPattern, FcChar8 **family, + const char *elementtype, const char *elementlangtype); +//to-do, make private and add some cleanish accessor methods + boost::unordered_map< rtl::OString, rtl::OString, rtl::OStringHash > m_aFontNameToLocalized; + boost::unordered_map< rtl::OString, rtl::OString, rtl::OStringHash > m_aLocalizedToCanonical; +private: + void cacheLocalizedFontNames(FcChar8 *origfontname, FcChar8 *bestfontname, const std::vector< lang_and_element > &lang_and_elements); +}; + +oslGenericFunction FontCfgWrapper::loadSymbol( const char* pSymbol ) +{ + OUString aSym( OUString::createFromAscii( pSymbol ) ); + oslGenericFunction pSym = osl_getFunctionSymbol( m_pLib, aSym.pData ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "%s %s\n", pSymbol, pSym ? "found" : "not found" ); +#endif + return pSym; +} + +FontCfgWrapper::FontCfgWrapper() + : m_pLib( NULL ), + m_pOutlineSet( NULL ), + m_nFcVersion( 0 ) +{ + OUString aLib( RTL_CONSTASCII_USTRINGPARAM( "libfontconfig.so.1" ) ); + m_pLib = osl_loadModule( aLib.pData, SAL_LOADMODULE_LAZY ); + if( !m_pLib ) + { + aLib = OUString( RTL_CONSTASCII_USTRINGPARAM( "libfontconfig.so" ) ); + m_pLib = osl_loadModule( aLib.pData, SAL_LOADMODULE_LAZY ); + } + + if( ! m_pLib ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "no libfontconfig\n" ); +#endif + return; + } + + m_pFcInit = (FcBool(*)()) + loadSymbol( "FcInit" ); + m_pFcFini = (void(*)()) + loadSymbol( "FcFini" ); + m_pFcGetVersion = (int(*)()) + loadSymbol( "FcGetVersion" ); + m_pFcConfigGetCurrent = (FcConfig *(*)()) + loadSymbol( "FcConfigGetCurrent" ); + m_pFcObjectSetVaBuild = (FcObjectSet*(*)(const char*,va_list)) + loadSymbol( "FcObjectSetVaBuild" ); + m_pFcObjectSetDestroy = (void(*)(FcObjectSet*)) + loadSymbol( "FcObjectSetDestroy" ); + m_pFcPatternCreate = (FcPattern*(*)()) + loadSymbol( "FcPatternCreate" ); + m_pFcPatternDestroy = (void(*)(FcPattern*)) + loadSymbol( "FcPatternDestroy" ); + m_pFcPatternEqual = (FcBool(*)(const FcPattern*,const FcPattern*)) + loadSymbol( "FcPatternEqual" ); + m_pFcConfigGetFonts = (FcFontSet*(*)(FcConfig*,FcSetName)) + loadSymbol( "FcConfigGetFonts" ); + m_pFcFontSetCreate = (FcFontSet*(*)()) + loadSymbol( "FcFontSetCreate" ); + m_pFcCharSetCreate = (FcCharSet*(*)()) + loadSymbol( "FcCharSetCreate" ); + m_pFcCharSetAddChar = (FcBool(*)(FcCharSet*, FcChar32)) + loadSymbol( "FcCharSetAddChar" ); + m_pFcCharSetHasChar = (FcBool(*)(FcCharSet*, FcChar32)) + loadSymbol( "FcCharSetHasChar" ); + m_pFcCharSetDestroy = (void(*)(FcCharSet*)) + loadSymbol( "FcCharSetDestroy" ); + m_pFcFontSetDestroy = (void(*)(FcFontSet*)) + loadSymbol( "FcFontSetDestroy" ); + m_pFcFontSetAdd = (FcBool(*)(FcFontSet*,FcPattern*)) + loadSymbol( "FcFontSetAdd" ); + m_pFcPatternReference = (void(*)(FcPattern*)) + loadSymbol( "FcPatternReference" ); + m_pFcPatternGetCharSet = (FcResult(*)(const FcPattern*,const char*,int,FcCharSet**)) + loadSymbol( "FcPatternGetCharSet" ); + m_pFcPatternGetString = (FcResult(*)(const FcPattern*,const char*,int,FcChar8**)) + loadSymbol( "FcPatternGetString" ); + m_pFcPatternGetInteger = (FcResult(*)(const FcPattern*,const char*,int,int*)) + loadSymbol( "FcPatternGetInteger" ); + m_pFcPatternGetDouble = (FcResult(*)(const FcPattern*,const char*,int,double*)) + loadSymbol( "FcPatternGetDouble" ); + m_pFcPatternGetBool = (FcResult(*)(const FcPattern*,const char*,int,FcBool*)) + loadSymbol( "FcPatternGetBool" ); + m_pFcConfigAppFontAddFile = (FcBool(*)(FcConfig*, const FcChar8*)) + loadSymbol( "FcConfigAppFontAddFile" ); + m_pFcConfigAppFontAddDir = (FcBool(*)(FcConfig*, const FcChar8*)) + loadSymbol( "FcConfigAppFontAddDir" ); + m_pFcConfigParseAndLoad = (FcBool(*)(FcConfig*, const FcChar8*, FcBool)) + loadSymbol( "FcConfigParseAndLoad" ); + m_pFcDefaultSubstitute = (void(*)(FcPattern *)) + loadSymbol( "FcDefaultSubstitute" ); + m_pFcFontSetMatch = (FcPattern*(*)(FcConfig*,FcFontSet**,int,FcPattern*,FcResult*)) + loadSymbol( "FcFontSetMatch" ); + m_pFcFontMatch = (FcPattern*(*)(FcConfig*,FcPattern*,FcResult*)) + loadSymbol( "FcFontMatch" ); + m_pFcConfigSubstitute = (FcBool(*)(FcConfig*,FcPattern*,FcMatchKind)) + loadSymbol( "FcConfigSubstitute" ); + + m_pFcPatternDuplicate = (FcPattern*(*)(const FcPattern*)) + loadSymbol( "FcPatternDuplicate" ); + m_pFcPatternAddInteger = (FcBool(*)(FcPattern*,const char*,int)) + loadSymbol( "FcPatternAddInteger" ); + m_pFcPatternAddDouble = (FcBool(*)(FcPattern*,const char*,double)) + loadSymbol( "FcPatternAddDouble" ); + m_pFcPatternAddBool = (FcBool(*)(FcPattern*,const char*,FcBool)) + loadSymbol( "FcPatternAddBool" ); + m_pFcPatternAddCharSet = (FcBool(*)(FcPattern*,const char*,const FcCharSet *)) + loadSymbol( "FcPatternAddCharSet" ); + m_pFcPatternAddString = (FcBool(*)(FcPattern*,const char*,const FcChar8*)) + loadSymbol( "FcPatternAddString" ); + m_pFcPatternDel = (FcBool(*)(FcPattern*,const char*)) + loadSymbol( "FcPatternDel" ); + + m_pFcFreeTypeCharIndex = (FT_UInt(*)(FT_Face,FcChar32)) + loadSymbol( "FcFreeTypeCharIndex" ); + m_pFcPatternAddFTFace = (FcBool(*)(FcPattern*,const char*,const FT_Face)) + loadSymbol( "FcPatternAddFTFace" ); + + m_nFcVersion = FcGetVersion(); +#if (OSL_DEBUG_LEVEL > 1) + fprintf( stderr,"FC_VERSION = %05d\n", m_nFcVersion ); +#endif + // make minimum version configurable + const char* pMinFcVersion = getenv( "SAL_MIN_FC_VERSION"); + if( pMinFcVersion ) + { + const int nMinFcVersion = atoi( pMinFcVersion ); + if( m_nFcVersion < nMinFcVersion ) + m_pFcInit = NULL; + } + + if( ! ( + m_pFcInit && + m_pFcGetVersion && + m_pFcConfigGetCurrent && + m_pFcObjectSetVaBuild && + m_pFcObjectSetDestroy && + m_pFcPatternCreate && + m_pFcPatternDestroy && + m_pFcPatternEqual && + m_pFcConfigGetFonts && + m_pFcFontSetCreate && + m_pFcCharSetCreate && + m_pFcCharSetAddChar && + m_pFcCharSetHasChar && + m_pFcCharSetDestroy && + m_pFcFontSetDestroy && + m_pFcFontSetAdd && + m_pFcPatternReference && + m_pFcPatternGetCharSet && + m_pFcPatternGetString && + m_pFcPatternGetInteger && + m_pFcPatternGetDouble && + m_pFcPatternGetBool && + m_pFcConfigAppFontAddFile && + m_pFcConfigAppFontAddDir && + m_pFcConfigParseAndLoad && + m_pFcFontMatch && + m_pFcDefaultSubstitute && + m_pFcConfigSubstitute && + m_pFcPatternDuplicate && + m_pFcPatternAddInteger && + m_pFcPatternAddDouble && + m_pFcPatternAddCharSet && + m_pFcPatternAddBool && + m_pFcPatternAddString && + m_pFcPatternDel + ) ) + { + osl_unloadModule( (oslModule)m_pLib ); + m_pLib = NULL; +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "not all needed symbols were found in libfontconfig\n" ); +#endif + return; + } + + + FcInit(); + if( ! FcConfigGetCurrent() ) + { + osl_unloadModule( (oslModule)m_pLib ); + m_pLib = NULL; + } +} + +void FontCfgWrapper::addFontSet( FcSetName eSetName ) +{ + #ifdef ENABLE_FONTCONFIG + /* + add only acceptable outlined fonts to our config, + for future fontconfig use + */ + FcFontSet* pOrig = FcConfigGetFonts( FcConfigGetCurrent(), eSetName ); + if( !pOrig ) + return; + + // filter the font sets to remove obsolete faces + for( int i = 0; i < pOrig->nfont; ++i ) + { + FcPattern* pPattern = pOrig->fonts[i]; + // #i115131# ignore non-outline fonts + FcBool bOutline = FcFalse; + FcResult eOutRes = FcPatternGetBool( pPattern, FC_OUTLINE, 0, &bOutline ); + if( (eOutRes != FcResultMatch) || (bOutline == FcFalse) ) + continue; + FcPatternReference( pPattern ); + FcFontSetAdd( m_pOutlineSet, pPattern ); + } + + // TODO?: FcFontSetDestroy( pOrig ); + #else + (void)eSetName; // prevent compiler warning about unused parameter + #endif +} + +#ifdef ENABLE_FONTCONFIG +namespace +{ + int compareFontNames(FontCfgWrapper& rWrapper, const FcPattern *a, const FcPattern *b) + { + FcChar8 *pNameA=NULL, *pNameB=NULL; + + bool bHaveA = rWrapper.FcPatternGetString(a, FC_FAMILY, 0, &pNameA) == FcResultMatch; + bool bHaveB = rWrapper.FcPatternGetString(b, FC_FAMILY, 0, &pNameB) == FcResultMatch; + + if (bHaveA && bHaveB) + return strcmp((const char*)pNameA, (const char*)pNameB); + + return bHaveA - bHaveB; + } + + //Sort fonts so that fonts with the same family name are side-by-side, with + //those with higher version numbers first + class SortFont : public ::std::binary_function< const FcPattern*, const FcPattern*, bool > + { + private: + FontCfgWrapper& m_rWrapper; + public: + SortFont(FontCfgWrapper& rWrapper) : m_rWrapper(rWrapper) {} + + bool operator()(const FcPattern *a, const FcPattern *b) + { + int comp = compareFontNames(m_rWrapper, a, b); + if (comp != 0) + return comp < 0; + + int nVersionA=0, nVersionB=0; + + bool bHaveA = m_rWrapper.FcPatternGetInteger(a, FC_FONTVERSION, 0, &nVersionA) == FcResultMatch; + bool bHaveB = m_rWrapper.FcPatternGetInteger(b, FC_FONTVERSION, 0, &nVersionB) == FcResultMatch; + + if (bHaveA && bHaveB) + return nVersionA > nVersionB; + + return bHaveA - bHaveA; + } + }; + + //See fdo#30729 for where an old opensymbol installed system-wide can + //clobber the new opensymbol installed locally + // + //See if this font is a duplicate with equal attributes which has already been + //inserted, or if it an older version of an inserted fonts. Depends on FcFontSet + //on being sorted with SortFont + bool isPreviouslyDuplicateOrObsoleted(FontCfgWrapper& rWrapper, FcFontSet *pFSet, int i) + { + if (i == 0) + return false; + + const FcPattern *a = pFSet->fonts[i]; + const FcPattern *b = pFSet->fonts[i-1]; + + if (compareFontNames(rWrapper, a, b) != 0) + return false; + + FcPattern* pTestPatternA = rWrapper.FcPatternDuplicate(a); + rWrapper.FcPatternDel(pTestPatternA, FC_FILE); + rWrapper.FcPatternDel(pTestPatternA, FC_CHARSET); + rWrapper.FcPatternDel(pTestPatternA, FC_CAPABILITY); + rWrapper.FcPatternDel(pTestPatternA, FC_FONTVERSION); + + FcPattern* pTestPatternB = rWrapper.FcPatternDuplicate(b); + rWrapper.FcPatternDel(pTestPatternB, FC_FILE); + rWrapper.FcPatternDel(pTestPatternB, FC_CHARSET); + rWrapper.FcPatternDel(pTestPatternB, FC_CAPABILITY); + rWrapper.FcPatternDel(pTestPatternB, FC_FONTVERSION); + + bool bIsDup = false; + if (rWrapper.FcPatternEqual(pTestPatternA, pTestPatternB)) + bIsDup = true; + + rWrapper.FcPatternDestroy(pTestPatternB); + rWrapper.FcPatternDestroy(pTestPatternA); + + return bIsDup; + } +} +#endif + +FcFontSet* FontCfgWrapper::getFontSet() +{ + #ifdef ENABLE_FONTCONFIG + if( !m_pOutlineSet ) + { + m_pOutlineSet = FcFontSetCreate(); + addFontSet( FcSetSystem ); + if( m_nFcVersion > 20400 ) // #i85462# prevent crashes + addFontSet( FcSetApplication ); + + ::std::sort(m_pOutlineSet->fonts,m_pOutlineSet->fonts+m_pOutlineSet->nfont,SortFont(*this)); + } + #endif + + return m_pOutlineSet; +} + +FontCfgWrapper::~FontCfgWrapper() +{ + if( m_pOutlineSet ) + FcFontSetDestroy( m_pOutlineSet ); + FcFini(); + if( m_pLib ) + osl_unloadModule( (oslModule)m_pLib ); +} + +static FontCfgWrapper* pOneInstance = NULL; + +FontCfgWrapper& FontCfgWrapper::get() +{ + if( ! pOneInstance ) + pOneInstance = new FontCfgWrapper(); + return *pOneInstance; +} + +void FontCfgWrapper::release() +{ + if( pOneInstance ) + { + delete pOneInstance; + pOneInstance = NULL; + } +} + +#ifdef ENABLE_FONTCONFIG +namespace +{ + class localizedsorter + { + rtl::OLocale maLoc; + public: + localizedsorter(rtl_Locale* pLoc) : maLoc(pLoc) {} + FcChar8* bestname(const std::vector<lang_and_element> &elements); + }; + + FcChar8* localizedsorter::bestname(const std::vector<lang_and_element> &elements) + { + FcChar8* candidate = elements.begin()->second; + rtl::OString sLangMatch(rtl::OUStringToOString(maLoc.getLanguage().toAsciiLowerCase(), RTL_TEXTENCODING_UTF8)); + rtl::OString sFullMatch = sLangMatch; + sFullMatch += OString('-'); + sFullMatch += rtl::OUStringToOString(maLoc.getCountry().toAsciiLowerCase(), RTL_TEXTENCODING_UTF8); + + std::vector<lang_and_element>::const_iterator aEnd = elements.end(); + bool alreadyclosematch = false; + for( std::vector<lang_and_element>::const_iterator aIter = elements.begin(); aIter != aEnd; ++aIter ) + { + const char *pLang = (const char*)aIter->first; + if( rtl_str_compare( pLang, sFullMatch.getStr() ) == 0) + { + // both language and country match + candidate = aIter->second; + break; + } + else if( alreadyclosematch ) + { + // override candidate only if there is a perfect match + continue; + } + else if( rtl_str_compare( pLang, sLangMatch.getStr()) == 0) + { + // just the language matches + candidate = aIter->second; + alreadyclosematch = true; + } + else if( rtl_str_compare( pLang, "en") == 0) + { + // fallback to the english element name + candidate = aIter->second; + } + } + return candidate; + } +} + +//Set up maps to quickly map between a fonts best UI name and all the rest of its names, and vice versa +void FontCfgWrapper::cacheLocalizedFontNames(FcChar8 *origfontname, FcChar8 *bestfontname, const std::vector< lang_and_element > &lang_and_elements) +{ + std::vector<lang_and_element>::const_iterator aEnd = lang_and_elements.end(); + for (std::vector<lang_and_element>::const_iterator aIter = lang_and_elements.begin(); aIter != aEnd; ++aIter) + { + const char *candidate = (const char*)(aIter->second); + if (rtl_str_compare(candidate, (const char*)bestfontname) != 0) + m_aFontNameToLocalized[OString(candidate)] = OString((const char*)bestfontname); + } + if (rtl_str_compare((const char*)origfontname, (const char*)bestfontname) != 0) + m_aLocalizedToCanonical[OString((const char*)bestfontname)] = OString((const char*)origfontname); +} + +FcResult FontCfgWrapper::LocalizedElementFromPattern(FcPattern* pPattern, FcChar8 **element, + const char *elementtype, const char *elementlangtype) +{ /* e. g.: ^ FC_FAMILY ^ FC_FAMILYLANG */ + FcChar8 *origelement; + FcResult eElementRes = FcPatternGetString( pPattern, elementtype, 0, &origelement ); + *element = origelement; + + if( eElementRes == FcResultMatch) + { + FcChar8* elementlang = NULL; + if (FcPatternGetString( pPattern, elementlangtype, 0, &elementlang ) == FcResultMatch) + { + std::vector< lang_and_element > lang_and_elements; + lang_and_elements.push_back(lang_and_element(elementlang, *element)); + int k = 1; + while (1) + { + if (FcPatternGetString( pPattern, elementlangtype, k, &elementlang ) != FcResultMatch) + break; + if (FcPatternGetString( pPattern, elementtype, k, element ) != FcResultMatch) + break; + lang_and_elements.push_back(lang_and_element(elementlang, *element)); + ++k; + } + + //possible to-do, sort by UILocale instead of process locale + rtl_Locale* pLoc; + osl_getProcessLocale(&pLoc); + localizedsorter aSorter(pLoc); + *element = aSorter.bestname(lang_and_elements); + + //if this element is a fontname, map the other names to this best-name + if (rtl_str_compare(elementtype, FC_FAMILY) == 0) + cacheLocalizedFontNames(origelement, *element, lang_and_elements); + } + } + + return eElementRes; +} + +/* + * PrintFontManager::initFontconfig + */ +bool PrintFontManager::initFontconfig() +{ + FontCfgWrapper& rWrapper = FontCfgWrapper::get(); + if( ! rWrapper.isValid() ) + return false; + return true; +} + +namespace +{ + weight::type convertWeight(int weight) + { + // set weight + if( weight <= FC_WEIGHT_THIN ) + return weight::Thin; + else if( weight <= FC_WEIGHT_ULTRALIGHT ) + return weight::UltraLight; + else if( weight <= FC_WEIGHT_LIGHT ) + return weight::Light; + else if( weight <= FC_WEIGHT_BOOK ) + return weight::SemiLight; + else if( weight <= FC_WEIGHT_NORMAL ) + return weight::Normal; + else if( weight <= FC_WEIGHT_MEDIUM ) + return weight::Medium; + else if( weight <= FC_WEIGHT_SEMIBOLD ) + return weight::SemiBold; + else if( weight <= FC_WEIGHT_BOLD ) + return weight::Bold; + else if( weight <= FC_WEIGHT_ULTRABOLD ) + return weight::UltraBold; + return weight::Black; + } + + italic::type convertSlant(int slant) + { + // set italic + if( slant == FC_SLANT_ITALIC ) + return italic::Italic; + else if( slant == FC_SLANT_OBLIQUE ) + return italic::Oblique; + return italic::Upright; + } + + pitch::type convertSpacing(int spacing) + { + // set pitch + if( spacing == FC_MONO || spacing == FC_CHARCELL ) + return pitch::Fixed; + return pitch::Variable; + } + + width::type convertWidth(int width) + { + if (width == FC_WIDTH_ULTRACONDENSED) + return width::UltraCondensed; + else if (width == FC_WIDTH_EXTRACONDENSED) + return width::ExtraCondensed; + else if (width == FC_WIDTH_CONDENSED) + return width::Condensed; + else if (width == FC_WIDTH_SEMICONDENSED) + return width::SemiCondensed; + else if (width == FC_WIDTH_SEMIEXPANDED) + return width::SemiExpanded; + else if (width == FC_WIDTH_EXPANDED) + return width::Expanded; + else if (width == FC_WIDTH_EXTRAEXPANDED) + return width::ExtraExpanded; + else if (width == FC_WIDTH_ULTRAEXPANDED) + return width::UltraExpanded; + return width::Normal; + } +} + +int PrintFontManager::countFontconfigFonts( boost::unordered_map<rtl::OString, int, rtl::OStringHash>& o_rVisitedPaths ) +{ + int nFonts = 0; + + FontCfgWrapper& rWrapper = FontCfgWrapper::get(); + if( !rWrapper.isValid() ) + return 0; + + FcFontSet* pFSet = rWrapper.getFontSet(); + if( pFSet ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "found %d entries in fontconfig fontset\n", pFSet->nfont ); +#endif + for( int i = 0; i < pFSet->nfont; i++ ) + { + FcChar8* file = NULL; + FcChar8* family = NULL; + FcChar8* style = NULL; + int slant = 0; + int weight = 0; + int spacing = 0; + int nCollectionEntry = -1; + FcBool outline = false; + + FcResult eFileRes = rWrapper.FcPatternGetString( pFSet->fonts[i], FC_FILE, 0, &file ); + FcResult eFamilyRes = rWrapper.LocalizedElementFromPattern( pFSet->fonts[i], &family, FC_FAMILY, FC_FAMILYLANG ); + FcResult eStyleRes = rWrapper.LocalizedElementFromPattern( pFSet->fonts[i], &style, FC_STYLE, FC_STYLELANG ); + FcResult eSlantRes = rWrapper.FcPatternGetInteger( pFSet->fonts[i], FC_SLANT, 0, &slant ); + FcResult eWeightRes = rWrapper.FcPatternGetInteger( pFSet->fonts[i], FC_WEIGHT, 0, &weight ); + FcResult eSpacRes = rWrapper.FcPatternGetInteger( pFSet->fonts[i], FC_SPACING, 0, &spacing ); + FcResult eOutRes = rWrapper.FcPatternGetBool( pFSet->fonts[i], FC_OUTLINE, 0, &outline ); + FcResult eIndexRes = rWrapper.FcPatternGetInteger( pFSet->fonts[i], FC_INDEX, 0, &nCollectionEntry ); + + if( eFileRes != FcResultMatch || eFamilyRes != FcResultMatch || eOutRes != FcResultMatch ) + continue; + +#if (OSL_DEBUG_LEVEL > 2) + fprintf( stderr, "found font \"%s\" in file %s\n" + " weight = %d, slant = %d, style = \"%s\"\n" + " spacing = %d, outline = %d\n" + , family, file + , eWeightRes == FcResultMatch ? weight : -1 + , eSpacRes == FcResultMatch ? slant : -1 + , eStyleRes == FcResultMatch ? (const char*) style : "<nil>" + , eSpacRes == FcResultMatch ? spacing : -1 + , eOutRes == FcResultMatch ? outline : -1 + ); +#endif + +// OSL_ASSERT(eOutRes != FcResultMatch || outline); + + // only outline fonts are usable to psprint anyway + if( eOutRes == FcResultMatch && ! outline ) + continue; + + if (isPreviouslyDuplicateOrObsoleted(rWrapper, pFSet, i)) + { +#if OSL_DEBUG_LEVEL > 2 + fprintf(stderr, "Ditching %s as duplicate/obsolete\n", file); +#endif + continue; + } + + // see if this font is already cached + // update attributes + std::list< PrintFont* > aFonts; + OString aDir, aBase, aOrgPath( (sal_Char*)file ); + splitPath( aOrgPath, aDir, aBase ); + + o_rVisitedPaths[aDir] = 1; + + int nDirID = getDirectoryAtom( aDir, true ); + if( ! m_pFontCache->getFontCacheFile( nDirID, aBase, aFonts ) ) + { +#if OSL_DEBUG_LEVEL > 2 + fprintf( stderr, "file %s not cached\n", aBase.getStr() ); +#endif + // not known, analyze font file to get attributes + // not described by fontconfig (e.g. alias names, PSName) + std::list< OString > aDummy; + analyzeFontFile( nDirID, aBase, aDummy, aFonts ); +#if OSL_DEBUG_LEVEL > 1 + if( aFonts.empty() ) + fprintf( stderr, "Warning: file \"%s\" is unusable to psprint\n", aOrgPath.getStr() ); +#endif + } + if( aFonts.empty() ) + { + // TODO: remove fonts unusable to psprint from fontset + continue; + } + + int nFamilyName = m_pAtoms->getAtom( ATOM_FAMILYNAME, OStringToOUString( OString( (sal_Char*)family ), RTL_TEXTENCODING_UTF8 ), sal_True ); + PrintFont* pUpdate = aFonts.front(); + std::list<PrintFont*>::const_iterator second_font = aFonts.begin(); + ++second_font; + if( second_font != aFonts.end() ) // more than one font + { + // a collection entry, get the correct index + if( eIndexRes == FcResultMatch && nCollectionEntry != -1 ) + { + for( std::list< PrintFont* >::iterator it = aFonts.begin(); it != aFonts.end(); ++it ) + { + if( (*it)->m_eType == fonttype::TrueType && + static_cast<TrueTypeFontFile*>(*it)->m_nCollectionEntry == nCollectionEntry ) + { + pUpdate = *it; + break; + } + } + // update collection entry + // additional entries will be created in the cache + // if this is a new index (that is if the loop above + // ran to the end of the list) + if( pUpdate->m_eType == fonttype::TrueType ) // sanity check, this should always be the case here + static_cast<TrueTypeFontFile*>(pUpdate)->m_nCollectionEntry = nCollectionEntry; + } + else + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "multiple fonts for file, but no index in fontconfig pattern ! (index res = %d collection entry = %d\nfile will not be used\n", eIndexRes, nCollectionEntry ); +#endif + // we have found more than one font in this file + // but fontconfig will not tell us which index is meant + // -> something is in disorder, do not use this font + pUpdate = NULL; + } + } + + if( pUpdate ) + { + // set family name + if( pUpdate->m_nFamilyName != nFamilyName ) + { + } + if( eWeightRes == FcResultMatch ) + pUpdate->m_eWeight = convertWeight(weight); + if( eSpacRes == FcResultMatch ) + pUpdate->m_ePitch = convertSpacing(spacing); + if( eSlantRes == FcResultMatch ) + pUpdate->m_eItalic = convertSlant(slant); + if( eStyleRes == FcResultMatch ) + { + pUpdate->m_aStyleName = OStringToOUString( OString( (sal_Char*)style ), RTL_TEXTENCODING_UTF8 ); + } + + // update font cache + m_pFontCache->updateFontCacheEntry( pUpdate, false ); + // sort into known fonts + fontID aFont = m_nNextFontID++; + m_aFonts[ aFont ] = pUpdate; + m_aFontFileToFontID[ aBase ].insert( aFont ); + nFonts++; +#if OSL_DEBUG_LEVEL > 2 + fprintf( stderr, "inserted font %s as fontID %d\n", family, aFont ); +#endif + } + // clean up the fonts we did not put into the list + for( std::list< PrintFont* >::iterator it = aFonts.begin(); it != aFonts.end(); ++it ) + { + if( *it != pUpdate ) + { + m_pFontCache->updateFontCacheEntry( *it, false ); // prepare a cache entry for a collection item + delete *it; + } + } + } + } + + // how does one get rid of the config ? +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "inserted %d fonts from fontconfig\n", nFonts ); +#endif + return nFonts; +} + +void PrintFontManager::deinitFontconfig() +{ + FontCfgWrapper::release(); +} + +int PrintFontManager::FreeTypeCharIndex( void *pFace, sal_uInt32 aChar ) +{ + FontCfgWrapper& rWrapper = FontCfgWrapper::get(); + return rWrapper.isValid() ? rWrapper.FcFreeTypeCharIndex( (FT_Face)pFace, aChar ) : 0; +} + +bool PrintFontManager::addFontconfigDir( const rtl::OString& rDirName ) +{ + FontCfgWrapper& rWrapper = FontCfgWrapper::get(); + if( ! rWrapper.isValid() ) + return false; + + // workaround for a stability problems in older FC versions + // when handling application specifc fonts + const int nVersion = rWrapper.FcGetVersion(); + if( nVersion <= 20400 ) + return false; + const char* pDirName = (const char*)rDirName.getStr(); + bool bDirOk = (rWrapper.FcConfigAppFontAddDir( rWrapper.FcConfigGetCurrent(), (FcChar8*)pDirName ) == FcTrue); + +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "FcConfigAppFontAddDir( \"%s\") => %d\n", pDirName, bDirOk ); +#endif + + if( !bDirOk ) + return false; + + // load dir-specific fc-config file too if available + const rtl::OString aConfFileName = rDirName + "/fc_local.conf"; + FILE* pCfgFile = fopen( aConfFileName.getStr(), "rb" ); + if( pCfgFile ) + { + fclose( pCfgFile); + bool bCfgOk = rWrapper.FcConfigParseAndLoad( rWrapper.FcConfigGetCurrent(), + (FcChar8*)aConfFileName.getStr(), FcTrue ); + if( !bCfgOk ) + fprintf( stderr, "FcConfigParseAndLoad( \"%s\") => %d\n", aConfFileName.getStr(), bCfgOk ); + } + + return true; +} + +static void addtopattern(FontCfgWrapper& rWrapper, FcPattern *pPattern, + italic::type eItalic, weight::type eWeight, width::type eWidth, pitch::type ePitch) +{ + if( eItalic != italic::Unknown ) + { + int nSlant = FC_SLANT_ROMAN; + switch( eItalic ) + { + case italic::Italic: nSlant = FC_SLANT_ITALIC;break; + case italic::Oblique: nSlant = FC_SLANT_OBLIQUE;break; + default: + break; + } + rWrapper.FcPatternAddInteger( pPattern, FC_SLANT, nSlant ); + } + if( eWeight != weight::Unknown ) + { + int nWeight = FC_WEIGHT_NORMAL; + switch( eWeight ) + { + case weight::Thin: nWeight = FC_WEIGHT_THIN;break; + case weight::UltraLight: nWeight = FC_WEIGHT_ULTRALIGHT;break; + case weight::Light: nWeight = FC_WEIGHT_LIGHT;break; + case weight::SemiLight: nWeight = FC_WEIGHT_BOOK;break; + case weight::Normal: nWeight = FC_WEIGHT_NORMAL;break; + case weight::Medium: nWeight = FC_WEIGHT_MEDIUM;break; + case weight::SemiBold: nWeight = FC_WEIGHT_SEMIBOLD;break; + case weight::Bold: nWeight = FC_WEIGHT_BOLD;break; + case weight::UltraBold: nWeight = FC_WEIGHT_ULTRABOLD;break; + case weight::Black: nWeight = FC_WEIGHT_BLACK;break; + default: + break; + } + rWrapper.FcPatternAddInteger( pPattern, FC_WEIGHT, nWeight ); + } + if( eWidth != width::Unknown ) + { + int nWidth = FC_WIDTH_NORMAL; + switch( eWidth ) + { + case width::UltraCondensed: nWidth = FC_WIDTH_ULTRACONDENSED;break; + case width::ExtraCondensed: nWidth = FC_WIDTH_EXTRACONDENSED;break; + case width::Condensed: nWidth = FC_WIDTH_CONDENSED;break; + case width::SemiCondensed: nWidth = FC_WIDTH_SEMICONDENSED;break; + case width::Normal: nWidth = FC_WIDTH_NORMAL;break; + case width::SemiExpanded: nWidth = FC_WIDTH_SEMIEXPANDED;break; + case width::Expanded: nWidth = FC_WIDTH_EXPANDED;break; + case width::ExtraExpanded: nWidth = FC_WIDTH_EXTRAEXPANDED;break; + case width::UltraExpanded: nWidth = FC_WIDTH_ULTRACONDENSED;break; + default: + break; + } + rWrapper.FcPatternAddInteger( pPattern, FC_WIDTH, nWidth ); + } + if( ePitch != pitch::Unknown ) + { + int nSpacing = FC_PROPORTIONAL; + switch( ePitch ) + { + case pitch::Fixed: nSpacing = FC_MONO;break; + case pitch::Variable: nSpacing = FC_PROPORTIONAL;break; + default: + break; + } + rWrapper.FcPatternAddInteger( pPattern, FC_SPACING, nSpacing ); + if (nSpacing == FC_MONO) + rWrapper.FcPatternAddString( pPattern, FC_FAMILY, (FcChar8*)"monospace"); + } +} + +rtl::OUString PrintFontManager::Substitute(const rtl::OUString& rFontName, + rtl::OUString& rMissingCodes, const rtl::OString &rLangAttrib, + italic::type &rItalic, weight::type &rWeight, + width::type &rWidth, pitch::type &rPitch) const +{ + rtl::OUString aName; + FontCfgWrapper& rWrapper = FontCfgWrapper::get(); + if( ! rWrapper.isValid() ) + return aName; + + // build pattern argument for fontconfig query + FcPattern* pPattern = rWrapper.FcPatternCreate(); + + // Prefer scalable fonts + rWrapper.FcPatternAddBool( pPattern, FC_SCALABLE, FcTrue ); + + const rtl::OString aTargetName = rtl::OUStringToOString( rFontName, RTL_TEXTENCODING_UTF8 ); + const FcChar8* pTargetNameUtf8 = (FcChar8*)aTargetName.getStr(); + rWrapper.FcPatternAddString( pPattern, FC_FAMILY, pTargetNameUtf8 ); + + if( rLangAttrib.getLength() ) + { + const FcChar8* pLangAttribUtf8; + if (rLangAttrib.equalsIgnoreAsciiCase(OString(RTL_CONSTASCII_STRINGPARAM("pa-in")))) + pLangAttribUtf8 = (FcChar8*)"pa"; + else + pLangAttribUtf8 = (FcChar8*)rLangAttrib.getStr(); + rWrapper.FcPatternAddString( pPattern, FC_LANG, pLangAttribUtf8 ); + } + + // Add required Unicode characters, if any + if ( rMissingCodes.getLength() ) + { + FcCharSet *unicodes = rWrapper.FcCharSetCreate(); + for( sal_Int32 nStrIndex = 0; nStrIndex < rMissingCodes.getLength(); ) + { + // also handle unicode surrogates + const sal_uInt32 nCode = rMissingCodes.iterateCodePoints( &nStrIndex ); + rWrapper.FcCharSetAddChar( unicodes, nCode ); + } + rWrapper.FcPatternAddCharSet( pPattern, FC_CHARSET, unicodes); + rWrapper.FcCharSetDestroy( unicodes ); + } + + addtopattern(rWrapper, pPattern, rItalic, rWeight, rWidth, rPitch); + + // query fontconfig for a substitute + rWrapper.FcConfigSubstitute( rWrapper.FcConfigGetCurrent(), pPattern, FcMatchPattern ); + rWrapper.FcDefaultSubstitute( pPattern ); + + // process the result of the fontconfig query + FcResult eResult = FcResultNoMatch; + FcFontSet* pFontSet = rWrapper.getFontSet(); + FcPattern* pResult = rWrapper.FcFontSetMatch( rWrapper.FcConfigGetCurrent(), &pFontSet, 1, pPattern, &eResult ); + rWrapper.FcPatternDestroy( pPattern ); + + FcFontSet* pSet = NULL; + if( pResult ) + { + pSet = rWrapper.FcFontSetCreate(); + // info: destroying the pSet destroys pResult implicitly + // since pResult was "added" to pSet + rWrapper.FcFontSetAdd( pSet, pResult ); + } + + if( pSet ) + { + if( pSet->nfont > 0 ) + { + //extract the closest match + FcChar8* family = NULL; + FcResult eFileRes = rWrapper.FcPatternGetString( pSet->fonts[0], FC_FAMILY, 0, &family ); + + // get the family name + if( eFileRes == FcResultMatch ) + { + OString sFamily((sal_Char*)family); + boost::unordered_map< rtl::OString, rtl::OString, rtl::OStringHash >::const_iterator aI = rWrapper.m_aFontNameToLocalized.find(sFamily); + if (aI != rWrapper.m_aFontNameToLocalized.end()) + sFamily = aI->second; + aName = rtl::OStringToOUString( sFamily, RTL_TEXTENCODING_UTF8 ); + + + int val = 0; + if ( FcResultMatch == rWrapper.FcPatternGetInteger( pSet->fonts[0], FC_WEIGHT, 0, &val)) + rWeight = convertWeight(val); + if ( FcResultMatch == rWrapper.FcPatternGetInteger( pSet->fonts[0], FC_SLANT, 0, &val)) + rItalic = convertSlant(val); + if ( FcResultMatch == rWrapper.FcPatternGetInteger( pSet->fonts[0], FC_SPACING, 0, &val)) + rPitch = convertSpacing(val); + if ( FcResultMatch == rWrapper.FcPatternGetInteger( pSet->fonts[0], FC_WIDTH, 0, &val)) + rWidth = convertWidth(val); + } + + // update rMissingCodes by removing resolved unicodes + if( rMissingCodes.getLength() > 0 ) + { + sal_uInt32* pRemainingCodes = (sal_uInt32*)alloca( rMissingCodes.getLength() * sizeof(sal_uInt32) ); + int nRemainingLen = 0; + FcCharSet* unicodes; + if( !rWrapper.FcPatternGetCharSet( pSet->fonts[0], FC_CHARSET, 0, &unicodes ) ) + { + for( sal_Int32 nStrIndex = 0; nStrIndex < rMissingCodes.getLength(); ) + { + // also handle unicode surrogates + const sal_uInt32 nCode = rMissingCodes.iterateCodePoints( &nStrIndex ); + if( rWrapper.FcCharSetHasChar( unicodes, nCode ) != FcTrue ) + pRemainingCodes[ nRemainingLen++ ] = nCode; + } + } + rMissingCodes = OUString( pRemainingCodes, nRemainingLen ); + } + } + + rWrapper.FcFontSetDestroy( pSet ); + } + + return aName; +} + +class FontConfigFontOptions : public ImplFontOptions +{ +public: + FontConfigFontOptions() : mpPattern(0) {} + ~FontConfigFontOptions() + { + FontCfgWrapper& rWrapper = FontCfgWrapper::get(); + if( rWrapper.isValid() ) + rWrapper.FcPatternDestroy( mpPattern ); + } + virtual void *GetPattern(void * face, bool bEmbolden) const + { + FontCfgWrapper& rWrapper = FontCfgWrapper::get(); + if( rWrapper.isValid() ) + { + rWrapper.FcPatternAddFTFace(mpPattern, FC_FT_FACE, static_cast<FT_Face>(face)); + rWrapper.FcPatternAddBool(mpPattern, FC_EMBOLDEN, bEmbolden ? FcTrue : FcFalse); + } + return mpPattern; + } + FcPattern* mpPattern; +}; + +ImplFontOptions* PrintFontManager::getFontOptions( + const FastPrintFontInfo& rInfo, int nSize, void (*subcallback)(void*)) const +{ +#ifndef ENABLE_FONTCONFIG + (void)rInfo;(void)nSize;(void)subcallback;(void)rOptions; + return NULL; +#else // ENABLE_FONTCONFIG + FontCfgWrapper& rWrapper = FontCfgWrapper::get(); + if( ! rWrapper.isValid() ) + return NULL; + + FontConfigFontOptions* pOptions = NULL; + FcConfig* pConfig = rWrapper.FcConfigGetCurrent(); + FcPattern* pPattern = rWrapper.FcPatternCreate(); + + OString sFamily = OUStringToOString( rInfo.m_aFamilyName, RTL_TEXTENCODING_UTF8 ); + + boost::unordered_map< rtl::OString, rtl::OString, rtl::OStringHash >::const_iterator aI = rWrapper.m_aLocalizedToCanonical.find(sFamily); + if (aI != rWrapper.m_aLocalizedToCanonical.end()) + sFamily = aI->second; + if( sFamily.getLength() ) + rWrapper.FcPatternAddString( pPattern, FC_FAMILY, (FcChar8*)sFamily.getStr() ); + + addtopattern(rWrapper, pPattern, rInfo.m_eItalic, rInfo.m_eWeight, rInfo.m_eWidth, rInfo.m_ePitch); + rWrapper.FcPatternAddDouble( pPattern, FC_PIXEL_SIZE, nSize); + + FcBool embitmap = true, antialias = true, autohint = true, hinting = true; + int hintstyle = FC_HINT_FULL; + + rWrapper.FcConfigSubstitute( pConfig, pPattern, FcMatchPattern ); + if (subcallback) subcallback(pPattern); + rWrapper.FcDefaultSubstitute( pPattern ); + + FcResult eResult = FcResultNoMatch; + FcFontSet* pFontSet = rWrapper.getFontSet(); + FcPattern* pResult = rWrapper.FcFontSetMatch( pConfig, &pFontSet, 1, pPattern, &eResult ); + if( pResult ) + { + FcResult eEmbeddedBitmap = rWrapper.FcPatternGetBool(pResult, + FC_EMBEDDED_BITMAP, 0, &embitmap); + FcResult eAntialias = rWrapper.FcPatternGetBool(pResult, + FC_ANTIALIAS, 0, &antialias); + FcResult eAutoHint = rWrapper.FcPatternGetBool(pResult, + FC_AUTOHINT, 0, &autohint); + FcResult eHinting = rWrapper.FcPatternGetBool(pResult, + FC_HINTING, 0, &hinting); + /*FcResult eHintStyle =*/ rWrapper.FcPatternGetInteger(pResult, + FC_HINT_STYLE, 0, &hintstyle); + + pOptions = new FontConfigFontOptions; + + pOptions->mpPattern = pResult; + + if( eEmbeddedBitmap == FcResultMatch ) + pOptions->meEmbeddedBitmap = embitmap ? EMBEDDEDBITMAP_TRUE : EMBEDDEDBITMAP_FALSE; + if( eAntialias == FcResultMatch ) + pOptions->meAntiAlias = antialias ? ANTIALIAS_TRUE : ANTIALIAS_FALSE; + if( eAutoHint == FcResultMatch ) + pOptions->meAutoHint = autohint ? AUTOHINT_TRUE : AUTOHINT_FALSE; + if( eHinting == FcResultMatch ) + pOptions->meHinting = hinting ? HINTING_TRUE : HINTING_FALSE; + switch (hintstyle) + { + case FC_HINT_NONE: pOptions->meHintStyle = HINT_NONE; break; + case FC_HINT_SLIGHT: pOptions->meHintStyle = HINT_SLIGHT; break; + case FC_HINT_MEDIUM: pOptions->meHintStyle = HINT_MEDIUM; break; + default: // fall through + case FC_HINT_FULL: pOptions->meHintStyle = HINT_FULL; break; + } + } + + // cleanup + rWrapper.FcPatternDestroy( pPattern ); + + return pOptions; +#endif +} + +bool PrintFontManager::matchFont( FastPrintFontInfo& rInfo, const com::sun::star::lang::Locale& rLocale ) +{ + FontCfgWrapper& rWrapper = FontCfgWrapper::get(); + if( ! rWrapper.isValid() ) + return false; + + FcConfig* pConfig = rWrapper.FcConfigGetCurrent(); + FcPattern* pPattern = rWrapper.FcPatternCreate(); + + OString aLangAttrib; + // populate pattern with font characteristics + if( rLocale.Language.getLength() ) + { + OUStringBuffer aLang(6); + aLang.append( rLocale.Language ); + if( rLocale.Country.getLength() ) + { + aLang.append( sal_Unicode('-') ); + aLang.append( rLocale.Country ); + } + aLangAttrib = OUStringToOString( aLang.makeStringAndClear(), RTL_TEXTENCODING_UTF8 ); + } + if( aLangAttrib.getLength() ) + rWrapper.FcPatternAddString( pPattern, FC_LANG, (FcChar8*)aLangAttrib.getStr() ); + + OString aFamily = OUStringToOString( rInfo.m_aFamilyName, RTL_TEXTENCODING_UTF8 ); + if( aFamily.getLength() ) + rWrapper.FcPatternAddString( pPattern, FC_FAMILY, (FcChar8*)aFamily.getStr() ); + + addtopattern(rWrapper, pPattern, rInfo.m_eItalic, rInfo.m_eWeight, rInfo.m_eWidth, rInfo.m_ePitch); + + rWrapper.FcConfigSubstitute( pConfig, pPattern, FcMatchPattern ); + rWrapper.FcDefaultSubstitute( pPattern ); + FcResult eResult = FcResultNoMatch; + FcFontSet *pFontSet = rWrapper.getFontSet(); + FcPattern* pResult = rWrapper.FcFontSetMatch( pConfig, &pFontSet, 1, pPattern, &eResult ); + bool bSuccess = false; + if( pResult ) + { + FcFontSet* pSet = rWrapper.FcFontSetCreate(); + rWrapper.FcFontSetAdd( pSet, pResult ); + if( pSet->nfont > 0 ) + { + //extract the closest match + FcChar8* file = NULL; + FcResult eFileRes = rWrapper.FcPatternGetString( pSet->fonts[0], FC_FILE, 0, &file ); + if( eFileRes == FcResultMatch ) + { + OString aDir, aBase, aOrgPath( (sal_Char*)file ); + splitPath( aOrgPath, aDir, aBase ); + int nDirID = getDirectoryAtom( aDir, true ); + fontID aFont = findFontFileID( nDirID, aBase ); + if( aFont > 0 ) + bSuccess = getFontFastInfo( aFont, rInfo ); + } + } + // info: destroying the pSet destroys pResult implicitly + // since pResult was "added" to pSet + rWrapper.FcFontSetDestroy( pSet ); + } + + // cleanup + rWrapper.FcPatternDestroy( pPattern ); + + return bSuccess; +} + +#else // ENABLE_FONTCONFIG not defined + +bool PrintFontManager::initFontconfig() +{ + return false; +} + +int PrintFontManager::countFontconfigFonts( boost::unordered_map<rtl::OString, int, rtl::OStringHash>& ) +{ + return 0; +} + +void PrintFontManager::deinitFontconfig() +{} + +bool PrintFontManager::addFontconfigDir( const rtl::OString& ) +{ + return false; +} + +bool PrintFontManager::matchFont( FastPrintFontInfo&, const com::sun::star::lang::Locale& ) +{ + return false; +} + +int PrintFontManager::FreeTypeCharIndex( void*, sal_uInt32 ) +{ + return 0; +} + +rtl::OUString PrintFontManager::Substitute( const rtl::OUString&, + rtl::OUString&, const rtl::OString&, italic::type, weight::type, width::type, pitch::type) const +{ + rtl::OUString aName; + return aName; +} + +#endif // ENABLE_FONTCONFIG + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/fontmanager/fontmanager.cxx b/vcl/unx/generic/fontmanager/fontmanager.cxx new file mode 100644 index 000000000000..5b7ef936d5ca --- /dev/null +++ b/vcl/unx/generic/fontmanager/fontmanager.cxx @@ -0,0 +1,4132 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <unistd.h> +#include <sys/stat.h> +#include <dirent.h> +#include <stdlib.h> +#include <osl/thread.h> + +#include "unotools/atom.hxx" + +#include "fontcache.hxx" +#include "fontsubset.hxx" +#include "impfont.hxx" +#include "svdata.hxx" +#include "salinst.hxx" +#include "vcl/fontmanager.hxx" +#include "vcl/strhelper.hxx" +#include "vcl/ppdparser.hxx" + +#include "tools/urlobj.hxx" +#include "tools/stream.hxx" +#include "tools/debug.hxx" +#include "tools/config.hxx" + +#include "osl/file.hxx" +#include "osl/process.h" + +#include "rtl/tencinfo.h" +#include "rtl/ustrbuf.hxx" +#include "rtl/strbuf.hxx" + +#include <sal/macros.h> + +#include "i18npool/mslangid.hxx" + + +#include "parseAFM.hxx" +#include "sft.hxx" + +#if OSL_DEBUG_LEVEL > 1 +#include <sys/times.h> +#include <stdio.h> +#endif + +#include "sal/alloca.h" + +#include <set> +#include <boost/unordered_set.hpp> +#include <algorithm> + +#include "adobeenc.tab" // get encoding table for AFM metrics + +#ifdef CALLGRIND_COMPILE +#include <valgrind/callgrind.h> +#endif + +#include "comphelper/processfactory.hxx" +#include "com/sun/star/beans/XMaterialHolder.hpp" +#include "com/sun/star/beans/NamedValue.hpp" + +#define PRINTER_METRICDIR "fontmetric" + +namespace { + +namespace css = com::sun::star; + +} + +using namespace vcl; +using namespace utl; +using namespace psp; +using namespace osl; +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; +using namespace com::sun::star::lang; + +using ::rtl::OUString; +using ::rtl::OString; +using ::rtl::OStringHash; +using ::rtl::OStringBuffer; +using ::rtl::OUStringBuffer; +using ::rtl::OUStringHash; +using ::rtl::OStringToOUString; +using ::rtl::OUStringToOString; + +/* + * static helpers + */ + +inline sal_uInt16 getUInt16BE( const sal_uInt8*& pBuffer ) +{ + sal_uInt16 nRet = (sal_uInt16)pBuffer[1] | + (((sal_uInt16)pBuffer[0]) << 8); + pBuffer+=2; + return nRet; +} + +inline sal_uInt32 getUInt32BE( const sal_uInt8*& pBuffer ) +{ + sal_uInt32 nRet = (((sal_uInt32)pBuffer[0]) << 24) | + (((sal_uInt32)pBuffer[1]) << 16) | + (((sal_uInt32)pBuffer[2]) << 8) | + (((sal_uInt32)pBuffer[3]) ); + pBuffer += 4; + return nRet; +} + +static italic::type parseItalic( const ByteString& rItalic ) +{ + italic::type eItalic = italic::Unknown; + if( rItalic.EqualsIgnoreCaseAscii( "i" ) ) + eItalic = italic::Italic; + else if( rItalic.EqualsIgnoreCaseAscii( "o" ) ) + eItalic = italic::Oblique; + else + eItalic = italic::Upright; + return eItalic; +} + +// ------------------------------------------------------------------------- + +static weight::type parseWeight( const ByteString& rWeight ) +{ + weight::type eWeight = weight::Unknown; + if( rWeight.Search( "bold" ) != STRING_NOTFOUND ) + { + if( rWeight.Search( "emi" ) != STRING_NOTFOUND ) // semi, demi + eWeight = weight::SemiBold; + else if( rWeight.Search( "ultra" ) != STRING_NOTFOUND ) + eWeight = weight::UltraBold; + else + eWeight = weight::Bold; + } + else if( rWeight.Search( "heavy" ) != STRING_NOTFOUND ) + eWeight = weight::Bold; + else if( rWeight.Search( "light" ) != STRING_NOTFOUND ) + { + if( rWeight.Search( "emi" ) != STRING_NOTFOUND ) // semi, demi + eWeight = weight::SemiLight; + else if( rWeight.Search( "ultra" ) != STRING_NOTFOUND ) + eWeight = weight::UltraLight; + else + eWeight = weight::Light; + } + else if( rWeight.Search( "black" ) != STRING_NOTFOUND ) + eWeight = weight::Black; + else if( rWeight.Equals( "demi" ) ) + eWeight = weight::SemiBold; + else if( rWeight.Equals( "book" ) || + rWeight.Equals( "semicondensed" ) ) + eWeight = weight::Light; + else if( rWeight.Equals( "medium" ) || rWeight.Equals( "roman" ) ) + eWeight = weight::Medium; + else + eWeight = weight::Normal; + return eWeight; +} + +// ------------------------------------------------------------------------- + +static width::type parseWidth( const ByteString& rWidth ) +{ + width::type eWidth = width::Unknown; + if( rWidth.Equals( "bold" ) || + rWidth.Equals( "semiexpanded" ) ) + eWidth = width::SemiExpanded; + else if( rWidth.Equals( "condensed" ) || + rWidth.Equals( "narrow" ) ) + eWidth = width::Condensed; + else if( rWidth.Equals( "double wide" ) || + rWidth.Equals( "extraexpanded" ) || + rWidth.Equals( "ultraexpanded" ) ) + eWidth = width::UltraExpanded; + else if( rWidth.Equals( "expanded" ) || + rWidth.Equals( "wide" ) ) + eWidth = width::Expanded; + else if( rWidth.Equals( "extracondensed" ) ) + eWidth = width::ExtraCondensed; + else if( rWidth.Equals( "semicondensed" ) ) + eWidth = width::SemiCondensed; + else if( rWidth.Equals( "ultracondensed" ) ) + eWidth = width::UltraCondensed; + else + eWidth = width::Normal; + + return eWidth; +} + +// ------------------------------------------------------------------------- +bool PrintFontManager::XLFDEntry::operator<(const PrintFontManager::XLFDEntry& rRight) const +{ + sal_Int32 nCmp = 0; + if( (nMask & MaskFamily) && (rRight.nMask & MaskFamily) ) + { + nCmp = rtl_str_compareIgnoreAsciiCase_WithLength( aFamily.pData->buffer, + aFamily.pData->length, + rRight.aFamily.pData->buffer, + rRight.aFamily.pData->length ); + if( nCmp != 0 ) + return nCmp < 0; + } + + if( (nMask & MaskFoundry) && (rRight.nMask & MaskFoundry) ) + { + nCmp = rtl_str_compareIgnoreAsciiCase_WithLength( aFoundry.pData->buffer, + aFoundry.pData->length, + rRight.aFoundry.pData->buffer, + rRight.aFoundry.pData->length ); + if( nCmp != 0 ) + return nCmp < 0; + } + + if( (nMask & MaskItalic) && (rRight.nMask & MaskItalic) ) + { + if( eItalic != rRight.eItalic ) + return (int)eItalic < (int)rRight.eItalic; + } + + if( (nMask & MaskWeight) && (rRight.nMask & MaskWeight) ) + { + if( eWeight != rRight.eWeight ) + return (int)eWeight < (int)rRight.eWeight; + } + + if( (nMask & MaskWidth) && (rRight.nMask & MaskWidth) ) + { + if( eWidth != rRight.eWidth ) + return (int)eWidth < (int)rRight.eWidth; + } + + if( (nMask & MaskPitch) && (rRight.nMask & MaskPitch) ) + { + if( ePitch != rRight.ePitch ) + return (int)ePitch < (int)rRight.ePitch; + } + + if( (nMask & MaskAddStyle) && (rRight.nMask & MaskAddStyle) ) + { + nCmp = rtl_str_compareIgnoreAsciiCase_WithLength( aAddStyle.pData->buffer, + aAddStyle.pData->length, + rRight.aAddStyle.pData->buffer, + rRight.aAddStyle.pData->length ); + if( nCmp != 0 ) + return nCmp < 0; + } + + if( (nMask & MaskEncoding) && (rRight.nMask & MaskEncoding) ) + { + if( aEncoding != rRight.aEncoding ) + return aEncoding < rRight.aEncoding; + } + + return false; +} + +bool PrintFontManager::XLFDEntry::operator==(const PrintFontManager::XLFDEntry& rRight) const +{ + sal_Int32 nCmp = 0; + if( (nMask & MaskFamily) && (rRight.nMask & MaskFamily) ) + { + nCmp = rtl_str_compareIgnoreAsciiCase_WithLength( aFamily.pData->buffer, + aFamily.pData->length, + rRight.aFamily.pData->buffer, + rRight.aFamily.pData->length ); + if( nCmp != 0 ) + return false; + } + + if( (nMask & MaskFoundry) && (rRight.nMask & MaskFoundry) ) + { + nCmp = rtl_str_compareIgnoreAsciiCase_WithLength( aFoundry.pData->buffer, + aFoundry.pData->length, + rRight.aFoundry.pData->buffer, + rRight.aFoundry.pData->length ); + if( nCmp != 0 ) + return false; + } + + if( (nMask & MaskItalic) && (rRight.nMask & MaskItalic) ) + { + if( eItalic != rRight.eItalic ) + return false; + } + + if( (nMask & MaskWeight) && (rRight.nMask & MaskWeight) ) + { + if( eWeight != rRight.eWeight ) + return false; + } + + if( (nMask & MaskWidth) && (rRight.nMask & MaskWidth) ) + { + if( eWidth != rRight.eWidth ) + return false; + } + + if( (nMask & MaskPitch) && (rRight.nMask & MaskPitch) ) + { + if( ePitch != rRight.ePitch ) + return false; + } + + if( (nMask & MaskAddStyle) && (rRight.nMask & MaskAddStyle) ) + { + nCmp = rtl_str_compareIgnoreAsciiCase_WithLength( aAddStyle.pData->buffer, + aAddStyle.pData->length, + rRight.aAddStyle.pData->buffer, + rRight.aAddStyle.pData->length ); + if( nCmp != 0 ) + return false; + } + + if( (nMask & MaskEncoding) && (rRight.nMask & MaskEncoding) ) + { + if( aEncoding != rRight.aEncoding ) + return false; + } + + return true; +} + +/* + * PrintFont implementations + */ +PrintFontManager::PrintFont::PrintFont( fonttype::type eType ) : + m_eType( eType ), + m_nFamilyName( 0 ), + m_nPSName( 0 ), + m_eItalic( italic::Unknown ), + m_eWidth( width::Unknown ), + m_eWeight( weight::Unknown ), + m_ePitch( pitch::Unknown ), + m_aEncoding( RTL_TEXTENCODING_DONTKNOW ), + m_bFontEncodingOnly( false ), + m_pMetrics( NULL ), + m_nAscend( 0 ), + m_nDescend( 0 ), + m_nLeading( 0 ), + m_nXMin( 0 ), + m_nYMin( 0 ), + m_nXMax( 0 ), + m_nYMax( 0 ), + m_bHaveVerticalSubstitutedGlyphs( false ), + m_bUserOverride( false ) +{ +} + +// ------------------------------------------------------------------------- + +PrintFontManager::PrintFont::~PrintFont() +{ + if( m_pMetrics ) + delete m_pMetrics; +} + +// ------------------------------------------------------------------------- + +PrintFontManager::Type1FontFile::~Type1FontFile() +{ +} + +// ------------------------------------------------------------------------- + +PrintFontManager::TrueTypeFontFile::TrueTypeFontFile() +: PrintFont( fonttype::TrueType ) +, m_nDirectory( 0 ) +, m_nCollectionEntry(-1) +, m_nTypeFlags( TYPEFLAG_INVALID ) +{} + +// ------------------------------------------------------------------------- + +PrintFontManager::TrueTypeFontFile::~TrueTypeFontFile() +{ +} + +// ------------------------------------------------------------------------- + +PrintFontManager::BuiltinFont::~BuiltinFont() +{ +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::Type1FontFile::queryMetricPage( int /*nPage*/, MultiAtomProvider* pProvider ) +{ + return readAfmMetrics( PrintFontManager::get().getAfmFile( this ), pProvider, false, false ); +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::BuiltinFont::queryMetricPage( int /*nPage*/, MultiAtomProvider* pProvider ) +{ + return readAfmMetrics( PrintFontManager::get().getAfmFile( this ), pProvider, false, false ); +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::TrueTypeFontFile::queryMetricPage( int nPage, MultiAtomProvider* pProvider ) +{ + bool bSuccess = false; + + ByteString aFile( PrintFontManager::get().getFontFile( this ) ); + + TrueTypeFont* pTTFont = NULL; + + if( OpenTTFontFile( aFile.GetBuffer(), m_nCollectionEntry < 0 ? 0 : m_nCollectionEntry, &pTTFont ) == SF_OK ) + { + if( ! m_pMetrics ) + { + m_pMetrics = new PrintFontMetrics; + memset (m_pMetrics->m_aPages, 0, sizeof(m_pMetrics->m_aPages)); + } + m_pMetrics->m_aPages[ nPage/8 ] |= (1 << ( nPage & 7 )); + int i; + sal_uInt16 table[256], table_vert[256]; + + for( i = 0; i < 256; i++ ) + table[ i ] = 256*nPage + i; + + int nCharacters = nPage < 255 ? 256 : 254; + MapString( pTTFont, table, nCharacters, NULL, 0 ); + TTSimpleGlyphMetrics* pMetrics = GetTTSimpleCharMetrics( pTTFont, nPage*256, nCharacters, 0 ); + if( pMetrics ) + { + for( i = 0; i < nCharacters; i++ ) + { + if( table[i] ) + { + CharacterMetric& rChar = m_pMetrics->m_aMetrics[ nPage*256 + i ]; + rChar.width = pMetrics[ i ].adv; + rChar.height = m_aGlobalMetricX.height; + } + } + + free( pMetrics ); + } + + for( i = 0; i < 256; i++ ) + table_vert[ i ] = 256*nPage + i; + MapString( pTTFont, table_vert, nCharacters, NULL, 1 ); + pMetrics = GetTTSimpleCharMetrics( pTTFont, nPage*256, nCharacters, 1 ); + if( pMetrics ) + { + for( i = 0; i < nCharacters; i++ ) + { + if( table_vert[i] ) + { + CharacterMetric& rChar = m_pMetrics->m_aMetrics[ nPage*256 + i + ( 1 << 16 ) ]; + rChar.width = m_aGlobalMetricY.width; + rChar.height = pMetrics[ i ].adv; + if( table_vert[i] != table[i] ) + m_pMetrics->m_bVerticalSubstitutions[ nPage*256 + i ] = 1; + } + } + free( pMetrics ); + } + + if( ! m_pMetrics->m_bKernPairsQueried ) + { + m_pMetrics->m_bKernPairsQueried = true; + // this is really a hack + // in future MapString/KernGlyphs should be used + // but vcl is not in a state where that could be used + // so currently we get kernpairs by accessing the raw data + struct _TrueTypeFont* pImplTTFont = (struct _TrueTypeFont*)pTTFont; + + //----------------------------------------------------------------- + // Kerning: KT_MICROSOFT + //----------------------------------------------------------------- + if( pImplTTFont->nkern && pImplTTFont->kerntype == KT_MICROSOFT ) + { + // create a glyph -> character mapping + ::boost::unordered_map< sal_uInt16, sal_Unicode > aGlyphMap; + ::boost::unordered_map< sal_uInt16, sal_Unicode >::iterator left, right; + for( i = 21; i < 0xfffd; i++ ) + { + sal_uInt16 nGlyph = MapChar( pTTFont, (sal_Unicode)i, 0 ); // kerning for horz only + if( nGlyph != 0 ) + aGlyphMap[ nGlyph ] = (sal_Unicode)i; + } + + + KernPair aPair; + for( i = 0; i < (int)pImplTTFont->nkern; i++ ) + { + const sal_uInt8* pTable = pImplTTFont->kerntables[i]; + + /*sal_uInt16 nVersion =*/ getUInt16BE( pTable ); + /*sal_uInt16 nLength =*/ getUInt16BE( pTable ); + sal_uInt16 nCoverage = getUInt16BE( pTable ); + + aPair.kern_x = 0; + aPair.kern_y = 0; + switch( nCoverage >> 8 ) + { + case 0: + { + sal_uInt16 nPairs = getUInt16BE( pTable ); + pTable += 6; + for( int n = 0; n < nPairs; n++ ) + { + sal_uInt16 nLeftGlyph = getUInt16BE( pTable ); + sal_uInt16 nRightGlyph = getUInt16BE( pTable ); + sal_Int16 nKern = (sal_Int16)getUInt16BE( pTable ); + + left = aGlyphMap.find( nLeftGlyph ); + right = aGlyphMap.find( nRightGlyph ); + if( left != aGlyphMap.end() && right != aGlyphMap.end() ) + { + aPair.first = left->second; + aPair.second = right->second; + switch( nCoverage & 1 ) + { + case 1: + aPair.kern_x = (int)nKern * 1000 / pImplTTFont->unitsPerEm; + m_pMetrics->m_aXKernPairs.push_back( aPair ); + break; + case 0: + aPair.kern_y = (int)nKern * 1000 / pImplTTFont->unitsPerEm; + m_pMetrics->m_aYKernPairs.push_back( aPair ); + break; + } + } + } + } + break; + + case 2: + { + const sal_uInt8* pSubTable = pTable; + /*sal_uInt16 nRowWidth =*/ getUInt16BE( pTable ); + sal_uInt16 nOfLeft = getUInt16BE( pTable ); + sal_uInt16 nOfRight = getUInt16BE( pTable ); + /*sal_uInt16 nOfArray =*/ getUInt16BE( pTable ); + const sal_uInt8* pTmp = pSubTable + nOfLeft; + sal_uInt16 nFirstLeft = getUInt16BE( pTmp ); + sal_uInt16 nLastLeft = getUInt16BE( pTmp ) + nFirstLeft - 1; + pTmp = pSubTable + nOfRight; + sal_uInt16 nFirstRight = getUInt16BE( pTmp ); + sal_uInt16 nLastRight = getUInt16BE( pTmp ) + nFirstRight -1; + + // int nPairs = (int)(nLastLeft-nFirstLeft+1)*(int)(nLastRight-nFirstRight+1); + for( aPair.first = nFirstLeft; aPair.first < nLastLeft; aPair.first++ ) + { + for( aPair.second = 0; aPair.second < nLastRight; aPair.second++ ) + { + sal_Int16 nKern = (sal_Int16)getUInt16BE( pTmp ); + switch( nCoverage & 1 ) + { + case 1: + aPair.kern_x = (int)nKern * 1000 / pImplTTFont->unitsPerEm; + m_pMetrics->m_aXKernPairs.push_back( aPair ); + break; + case 0: + aPair.kern_y = (int)nKern * 1000 / pImplTTFont->unitsPerEm; + m_pMetrics->m_aYKernPairs.push_back( aPair ); + break; + } + } + } + } + break; + } + } + } + + //----------------------------------------------------------------- + // Kerning: KT_APPLE_NEW + //----------------------------------------------------------------- + if( pImplTTFont->nkern && pImplTTFont->kerntype == KT_APPLE_NEW ) + { + // create a glyph -> character mapping + ::boost::unordered_map< sal_uInt16, sal_Unicode > aGlyphMap; + ::boost::unordered_map< sal_uInt16, sal_Unicode >::iterator left, right; + for( i = 21; i < 0xfffd; i++ ) + { + sal_uInt16 nGlyph = MapChar( pTTFont, (sal_Unicode)i, 0 ); // kerning for horz only + if( nGlyph != 0 ) + aGlyphMap[ nGlyph ] = (sal_Unicode)i; + } + + // Loop through each of the 'kern' subtables + KernPair aPair; + for( i = 0; (unsigned int)i < pImplTTFont->nkern; i++ ) + { + const sal_uInt8* pTable = pImplTTFont->kerntables[i]; + + /*sal_uInt32 nLength =*/ getUInt32BE( pTable ); + sal_uInt16 nCoverage = getUInt16BE( pTable ); + /*sal_uInt16 nTupleIndex =*/ getUInt16BE( pTable ); + + // Get kerning type + // sal_Bool bKernVertical = nCoverage & 0x8000; + // sal_Bool bKernCrossStream = nCoverage & 0x4000; + // sal_Bool bKernVariation = nCoverage & 0x2000; + + // Kerning sub-table format, 0 through 3 + sal_uInt8 nSubTableFormat = nCoverage & 0x00FF; + + aPair.kern_x = 0; + aPair.kern_y = 0; + switch( nSubTableFormat ) + { + case 0: + { + // Grab the # of kern pairs but skip over the: + // searchRange + // entrySelector + // rangeShift + sal_uInt16 nPairs = getUInt16BE( pTable ); + pTable += 6; + + for( int n = 0; n < nPairs; n++ ) + { + sal_uInt16 nLeftGlyph = getUInt16BE( pTable ); + sal_uInt16 nRightGlyph = getUInt16BE( pTable ); + sal_Int16 nKern = (sal_Int16)getUInt16BE( pTable ); + + left = aGlyphMap.find( nLeftGlyph ); + right = aGlyphMap.find( nRightGlyph ); + if( left != aGlyphMap.end() && right != aGlyphMap.end() ) + { + aPair.first = left->second; + aPair.second = right->second; + + // Only support horizontal kerning for now + aPair.kern_x = (int)nKern * 1000 / pImplTTFont->unitsPerEm; + aPair.kern_y = 0; + m_pMetrics->m_aXKernPairs.push_back( aPair ); + +/* switch( nCoverage & 1 ) + { + case 1: + aPair.kern_x = (int)nKern * 1000 / pImplTTFont->unitsPerEm; + m_pMetrics->m_aXKernPairs.push_back( aPair ); + break; + case 0: + aPair.kern_y = (int)nKern * 1000 / pImplTTFont->unitsPerEm; + m_pMetrics->m_aYKernPairs.push_back( aPair ); + break; + } +*/ + } + } + } + break; + + case 2: + { + const sal_uInt8* pSubTable = pTable; + /*sal_uInt16 nRowWidth =*/ getUInt16BE( pTable ); + sal_uInt16 nOfLeft = getUInt16BE( pTable ); + sal_uInt16 nOfRight = getUInt16BE( pTable ); + /*sal_uInt16 nOfArray =*/ getUInt16BE( pTable ); + const sal_uInt8* pTmp = pSubTable + nOfLeft; + sal_uInt16 nFirstLeft = getUInt16BE( pTmp ); + sal_uInt16 nLastLeft = getUInt16BE( pTmp ) + nFirstLeft - 1; + pTmp = pSubTable + nOfRight; + sal_uInt16 nFirstRight = getUInt16BE( pTmp ); + sal_uInt16 nLastRight = getUInt16BE( pTmp ) + nFirstRight -1; + + for( aPair.first = nFirstLeft; aPair.first < nLastLeft; aPair.first++ ) + { + for( aPair.second = 0; aPair.second < nLastRight; aPair.second++ ) + { + sal_Int16 nKern = (sal_Int16)getUInt16BE( pTmp ); + switch( nCoverage & 1 ) + { + case 1: + aPair.kern_x = (int)nKern * 1000 / pImplTTFont->unitsPerEm; + m_pMetrics->m_aXKernPairs.push_back( aPair ); + break; + case 0: + aPair.kern_y = (int)nKern * 1000 / pImplTTFont->unitsPerEm; + m_pMetrics->m_aYKernPairs.push_back( aPair ); + break; + } + } + } + } + break; + + default: + fprintf( stderr, "Found unsupported Apple-style kern subtable type %d.\n", nSubTableFormat ); + break; + } + } + } + +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "found %" SAL_PRI_SIZET "u/%" SAL_PRI_SIZET "u kern pairs for %s\n", + m_pMetrics->m_aXKernPairs.size(), + m_pMetrics->m_aYKernPairs.size(), + OUStringToOString( pProvider->getString( ATOM_FAMILYNAME, m_nFamilyName ), RTL_TEXTENCODING_MS_1252 ).getStr() ); +#else + (void) pProvider; /* avoid warnings */ +#endif + } + + CloseTTFont( pTTFont ); + bSuccess = true; + } + return bSuccess; +} + +// ------------------------------------------------------------------------- + +/* #i73387# There seem to be fonts with a rather unwell chosen family name +* consider e.g. "Helvetica Narrow" which defines its family as "Helvetica" +* It can really only be distinguished by its PSName and FullName. Both of +* which are not user presentable in OOo. So replace it by something sensible. +* +* If other fonts feature this behaviour, insert them to the map. +*/ +static bool familyNameOverride( const OUString& i_rPSname, OUString& o_rFamilyName ) +{ + static boost::unordered_map< OUString, OUString, OUStringHash > aPSNameToFamily( 16 ); + if( aPSNameToFamily.empty() ) // initialization + { + aPSNameToFamily[ OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica-Narrow" ) ) ] = + OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica Narrow" ) ); + aPSNameToFamily[ OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica-Narrow-Bold" ) ) ] = + OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica Narrow" ) ); + aPSNameToFamily[ OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica-Narrow-BoldOblique" ) ) ] = + OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica Narrow" ) ); + aPSNameToFamily[ OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica-Narrow-Oblique" ) ) ] = + OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica Narrow" ) ); + } + boost::unordered_map<OUString,OUString,OUStringHash>::const_iterator it = + aPSNameToFamily.find( i_rPSname ); + bool bReplaced = (it != aPSNameToFamily.end() ); + if( bReplaced ) + o_rFamilyName = it->second; + return bReplaced; +}; + +bool PrintFontManager::PrintFont::readAfmMetrics( const OString& rFileName, MultiAtomProvider* pProvider, bool bFillEncodingvector, bool bOnlyGlobalAttributes ) +{ + PrintFontManager& rManager( PrintFontManager::get() ); + + FontInfo* pInfo = NULL; + parseFile( rFileName.getStr(), &pInfo, P_ALL ); + if( ! pInfo || ! pInfo->numOfChars ) + { + if( pInfo ) + freeFontInfo( pInfo ); + return false; + } + + m_aEncodingVector.clear(); + // fill in global info + + // PSName + OUString aPSName( OStringToOUString( pInfo->gfi->fontName, RTL_TEXTENCODING_ISO_8859_1 ) ); + m_nPSName = pProvider->getAtom( ATOM_PSNAME, aPSName, sal_True ); + + // family name (if not already set) + OUString aFamily; + if( ! m_nFamilyName ) + { + aFamily = OStringToOUString( pInfo->gfi->familyName, RTL_TEXTENCODING_ISO_8859_1 ); + if( ! aFamily.getLength() ) + { + aFamily = OStringToOUString( pInfo->gfi->fontName, RTL_TEXTENCODING_ISO_8859_1 ); + sal_Int32 nIndex = 0; + aFamily = aFamily.getToken( 0, '-', nIndex ); + } + familyNameOverride( aPSName, aFamily ); + m_nFamilyName = pProvider->getAtom( ATOM_FAMILYNAME, aFamily, sal_True ); + } + else + aFamily = pProvider->getString( ATOM_FAMILYNAME, m_nFamilyName ); + + // style name: if fullname begins with family name + // interpret the rest of fullname as style + if( ! m_aStyleName.getLength() && pInfo->gfi->fullName && *pInfo->gfi->fullName ) + { + OUString aFullName( OStringToOUString( pInfo->gfi->fullName, RTL_TEXTENCODING_ISO_8859_1 ) ); + if( aFullName.indexOf( aFamily ) == 0 ) + m_aStyleName = WhitespaceToSpace( aFullName.copy( aFamily.getLength() ) ); + } + + // italic + if( pInfo->gfi->italicAngle > 0 ) + m_eItalic = italic::Oblique; + else if( pInfo->gfi->italicAngle < 0 ) + m_eItalic = italic::Italic; + else + m_eItalic = italic::Upright; + + // weight + ByteString aLowerWeight( pInfo->gfi->weight ); + aLowerWeight.ToLowerAscii(); + m_eWeight = parseWeight( aLowerWeight ); + + // pitch + m_ePitch = pInfo->gfi->isFixedPitch ? pitch::Fixed : pitch::Variable; + + // encoding - only set if unknown + int nAdobeEncoding = 0; + if( pInfo->gfi->encodingScheme ) + { + if( !strcmp( pInfo->gfi->encodingScheme, "AdobeStandardEncoding" ) ) + nAdobeEncoding = 1; + else if( !strcmp( pInfo->gfi->encodingScheme, "ISO10646-1" ) ) + { + nAdobeEncoding = 1; + m_aEncoding = RTL_TEXTENCODING_UNICODE; + } + else if( !strcmp( pInfo->gfi->encodingScheme, "Symbol") ) + nAdobeEncoding = 2; + else if( !strcmp( pInfo->gfi->encodingScheme, "FontSpecific") ) + nAdobeEncoding = 3; + + if( m_aEncoding == RTL_TEXTENCODING_DONTKNOW ) + m_aEncoding = nAdobeEncoding == 1 ? + RTL_TEXTENCODING_ADOBE_STANDARD : RTL_TEXTENCODING_SYMBOL; + } + else if( m_aEncoding == RTL_TEXTENCODING_DONTKNOW ) + m_aEncoding = RTL_TEXTENCODING_ADOBE_STANDARD; + + // try to parse the font name and decide wether it might be a + // japanese font. Who invented this PITA ? + OUString aPSNameLastToken( aPSName.copy( aPSName.lastIndexOf( '-' )+1 ) ); + if( ! aPSNameLastToken.compareToAscii( "H" ) || + ! aPSNameLastToken.compareToAscii( "V" ) ) + { + static const char* pEncs[] = + { + "EUC", + "RKSJ", + "SJ" + }; + static const rtl_TextEncoding aEncs[] = + { + RTL_TEXTENCODING_EUC_JP, + RTL_TEXTENCODING_SHIFT_JIS, + RTL_TEXTENCODING_JIS_X_0208 + }; + + for( unsigned int enc = 0; enc < SAL_N_ELEMENTS( aEncs ) && m_aEncoding == RTL_TEXTENCODING_DONTKNOW; enc++ ) + { + sal_Int32 nIndex = 0, nOffset = 1; + do + { + OUString aToken( aPSName.getToken( nOffset, '-', nIndex ) ); + if( nIndex == -1 ) + break; + nOffset = 0; + if( ! aToken.compareToAscii( pEncs[enc] ) ) + { + m_aEncoding = aEncs[ enc ]; + m_bFontEncodingOnly = true; + } + } while( nIndex != -1 ); + } + + // default is jis + if( m_aEncoding == RTL_TEXTENCODING_DONTKNOW ) + m_aEncoding = RTL_TEXTENCODING_JIS_X_0208; +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "Encoding %d for %s\n", m_aEncoding, pInfo->gfi->fontName ); +#endif + } + + // hack for GB encoded builtin fonts posing as FontSpecific + if( m_eType == fonttype::Builtin && ( nAdobeEncoding == 3 || nAdobeEncoding == 0 ) ) + { + int nLen = aFamily.getLength(); + if( nLen > 2 && + aFamily.getStr()[ nLen-2 ] == 'G' && + aFamily.getStr()[ nLen-1 ] == 'B' && + pInfo->numOfChars > 255 ) + { + m_aEncoding = RTL_TEXTENCODING_GBK; + m_bFontEncodingOnly = true; +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "found builtin font %s with GBK encoding\n", pInfo->gfi->fontName ); +#endif + } + } + + // #i37313# check if Fontspecific is not rather some character encoding + if( nAdobeEncoding == 3 && m_aEncoding == RTL_TEXTENCODING_SYMBOL ) + { + bool bYFound = false; + bool bQFound = false; + CharMetricInfo* pChar = pInfo->cmi; + for( int j = 0; j < pInfo->numOfChars && ! (bYFound && bQFound); j++ ) + { + if( pChar[j].name ) + { + if( pChar[j].name[0] == 'Y' && pChar[j].name[1] == 0 ) + bYFound = true; + else if( pChar[j].name[0] == 'Q' && pChar[j].name[1] == 0 ) + bQFound = true; + } + } + if( bQFound && bYFound ) + { + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "setting FontSpecific font %s (file %s) to unicode\n", + pInfo->gfi->fontName, + rFileName.getStr() + ); + #endif + nAdobeEncoding = 4; + m_aEncoding = RTL_TEXTENCODING_UNICODE; + bFillEncodingvector = false; // will be filled anyway, don't do the work twice + } + } + + // ascend + m_nAscend = pInfo->gfi->fontBBox.ury; + + // descend + // descends have opposite sign of our definition + m_nDescend = -pInfo->gfi->fontBBox.lly; + + // fallback to ascender, descender + // interesting: the BBox seems to describe Ascender and Descender better + // as we understand it + if( m_nAscend == 0 ) + m_nAscend = pInfo->gfi->ascender; + if( m_nDescend == 0) + m_nDescend = -pInfo->gfi->descender; + + m_nLeading = m_nAscend + m_nDescend - 1000; + + if( m_pMetrics ) + delete m_pMetrics; + m_pMetrics = new PrintFontMetrics; + // mark all pages as queried (or clear if only global font info queiried) + memset( m_pMetrics->m_aPages, bOnlyGlobalAttributes ? 0 : 0xff, sizeof( m_pMetrics->m_aPages ) ); + + m_aGlobalMetricX.width = m_aGlobalMetricY.width = + pInfo->gfi->charwidth ? pInfo->gfi->charwidth : pInfo->gfi->fontBBox.urx; + m_aGlobalMetricX.height = m_aGlobalMetricY.height = + pInfo->gfi->capHeight ? pInfo->gfi->capHeight : pInfo->gfi->fontBBox.ury; + + m_nXMin = pInfo->gfi->fontBBox.llx; + m_nYMin = pInfo->gfi->fontBBox.lly; + m_nXMax = pInfo->gfi->fontBBox.urx; + m_nYMax = pInfo->gfi->fontBBox.ury; + + if( bFillEncodingvector || !bOnlyGlobalAttributes ) + { + // fill in character metrics + + // first transform the character codes to unicode + // note: this only works with single byte encodings + sal_Unicode* pUnicodes = (sal_Unicode*)alloca( pInfo->numOfChars * sizeof(sal_Unicode)); + CharMetricInfo* pChar = pInfo->cmi; + int i; + + for( i = 0; i < pInfo->numOfChars; i++, pChar++ ) + { + if( nAdobeEncoding == 4 ) + { + if( pChar->name ) + { + pUnicodes[i] = 0; + std::list< sal_Unicode > aCodes = rManager.getUnicodeFromAdobeName( pChar->name ); + for( std::list< sal_Unicode >::const_iterator it = aCodes.begin(); it != aCodes.end(); ++it ) + { + if( *it != 0 ) + { + m_aEncodingVector[ *it ] = pChar->code; + if( pChar->code == -1 ) + m_aNonEncoded[ *it ] = pChar->name; + if( ! pUnicodes[i] ) // map the first + pUnicodes[i] = *it; + } + } + } + } + else if( pChar->code != -1 ) + { + if( nAdobeEncoding == 3 && m_aEncoding == RTL_TEXTENCODING_SYMBOL ) + { + pUnicodes[i] = pChar->code + 0xf000; + if( bFillEncodingvector ) + m_aEncodingVector[ pUnicodes[i] ] = pChar->code; + continue; + } + + if( m_aEncoding == RTL_TEXTENCODING_UNICODE ) + { + pUnicodes[i] = (sal_Unicode)pChar->code; + continue; + } + + ByteString aTranslate; + if( pChar->code & 0xff000000 ) + aTranslate += (char)(pChar->code >> 24 ); + if( pChar->code & 0xffff0000 ) + aTranslate += (char)((pChar->code & 0x00ff0000) >> 16 ); + if( pChar->code & 0xffffff00 ) + aTranslate += (char)((pChar->code & 0x0000ff00) >> 8 ); + aTranslate += (char)(pChar->code & 0xff); + String aUni( aTranslate, m_aEncoding ); + pUnicodes[i] = *aUni.GetBuffer(); + } + else + pUnicodes[i] = 0; + } + + // now fill in the character metrics + // parseAFM.cxx effectively only supports direction 0 (horizontal) + pChar = pInfo->cmi; + CharacterMetric aMetric; + for( i = 0; i < pInfo->numOfChars; i++, pChar++ ) + { + if( pChar->code == -1 && ! pChar->name ) + continue; + + if( bFillEncodingvector && pChar->name ) + { + std::list< sal_Unicode > aCodes = rManager.getUnicodeFromAdobeName( pChar->name ); + for( std::list< sal_Unicode >::const_iterator it = aCodes.begin(); it != aCodes.end(); ++it ) + { + if( *it != 0 ) + { + m_aEncodingVector[ *it ] = pChar->code; + if( pChar->code == -1 ) + m_aNonEncoded[ *it ] = pChar->name; + } + } + } + + aMetric.width = pChar->wx ? pChar->wx : pChar->charBBox.urx; + aMetric.height = pChar->wy ? pChar->wy : pChar->charBBox.ury - pChar->charBBox.lly; + if( aMetric.width == 0 && aMetric.height == 0 ) + // guess something for e.g. space + aMetric.width = m_aGlobalMetricX.width/4; + + if( ( nAdobeEncoding == 0 ) || + ( ( nAdobeEncoding == 3 ) && ( m_aEncoding != RTL_TEXTENCODING_SYMBOL ) ) ) + { + if( pChar->code != -1 ) + { + m_pMetrics->m_aMetrics[ pUnicodes[i] ] = aMetric; + if( bFillEncodingvector ) + m_aEncodingVector[ pUnicodes[i] ] = pChar->code; + } + else if( pChar->name ) + { + std::list< sal_Unicode > aCodes = rManager.getUnicodeFromAdobeName( pChar->name ); + for( std::list< sal_Unicode >::const_iterator it = aCodes.begin(); it != aCodes.end(); ++it ) + { + if( *it != 0 ) + m_pMetrics->m_aMetrics[ *it ] = aMetric; + } + } + } + else if( nAdobeEncoding == 1 || nAdobeEncoding == 2 || nAdobeEncoding == 4) + { + if( pChar->name ) + { + std::list< sal_Unicode > aCodes = rManager.getUnicodeFromAdobeName( pChar->name ); + for( std::list< sal_Unicode >::const_iterator it = aCodes.begin(); it != aCodes.end(); ++it ) + { + if( *it != 0 ) + m_pMetrics->m_aMetrics[ *it ] = aMetric; + } + } + else if( pChar->code != -1 ) + { + ::std::pair< ::boost::unordered_multimap< sal_uInt8, sal_Unicode >::const_iterator, + ::boost::unordered_multimap< sal_uInt8, sal_Unicode >::const_iterator > + aCodes = rManager.getUnicodeFromAdobeCode( pChar->code ); + while( aCodes.first != aCodes.second ) + { + if( (*aCodes.first).second != 0 ) + { + m_pMetrics->m_aMetrics[ (*aCodes.first).second ] = aMetric; + if( bFillEncodingvector ) + m_aEncodingVector[ (*aCodes.first).second ] = pChar->code; + } + ++aCodes.first; + } + } + } + else if( nAdobeEncoding == 3 ) + { + if( pChar->code != -1 ) + { + sal_Unicode code = 0xf000 + pChar->code; + m_pMetrics->m_aMetrics[ code ] = aMetric; + // maybe should try to find the name in the convtabs ? + if( bFillEncodingvector ) + m_aEncodingVector[ code ] = pChar->code; + } + } + } + + m_pMetrics->m_aXKernPairs.clear(); + m_pMetrics->m_aYKernPairs.clear(); + + // now fill in the kern pairs + // parseAFM.cxx effectively only supports direction 0 (horizontal) + PairKernData* pKern = pInfo->pkd; + KernPair aPair; + for( i = 0; i < pInfo->numOfPairs; i++, pKern++ ) + { + // #i37703# broken kern table + if( ! pKern->name1 || ! pKern->name2 ) + continue; + + aPair.first = 0; + aPair.second = 0; + // currently we have to find the adobe character names + // in the already parsed character metrics to find + // the corresponding UCS2 code which is a bit dangerous + // since the character names are not required + // in the metric descriptions + pChar = pInfo->cmi; + for( int j = 0; + j < pInfo->numOfChars && ( aPair.first == 0 || aPair.second == 0 ); + j++, pChar++ ) + { + if( pChar->code != -1 ) + { + if( ! strcmp( pKern->name1, pChar->name ? pChar->name : "" ) ) + aPair.first = pUnicodes[ j ]; + if( ! strcmp( pKern->name2, pChar->name ? pChar->name : "" ) ) + aPair.second = pUnicodes[ j ]; + } + } + if( aPair.first && aPair.second ) + { + aPair.kern_x = pKern->xamt; + aPair.kern_y = pKern->yamt; + m_pMetrics->m_aXKernPairs.push_back( aPair ); + } + } + m_pMetrics->m_bKernPairsQueried = true; + } + + freeFontInfo( pInfo ); + return true; +} + +// ------------------------------------------------------------------------- + +OString PrintFontManager::s_aEmptyOString; + +/* + * one instance only + */ +PrintFontManager& PrintFontManager::get() +{ + static PrintFontManager* pManager = NULL; + if( ! pManager ) + { + static PrintFontManager theManager; + pManager = &theManager; + pManager->initialize(); + } + return *pManager; +} + +// ------------------------------------------------------------------------- + +/* + * the PrintFontManager + */ + +PrintFontManager::PrintFontManager() : + m_nNextFontID( 1 ), + m_pAtoms( new MultiAtomProvider() ), + m_nNextDirAtom( 1 ), + m_pFontCache( NULL ), + m_bFontconfigSuccess( false ) +{ + for( unsigned int i = 0; i < SAL_N_ELEMENTS( aAdobeCodes ); i++ ) + { + m_aUnicodeToAdobename.insert( ::boost::unordered_multimap< sal_Unicode, ::rtl::OString >::value_type( aAdobeCodes[i].aUnicode, aAdobeCodes[i].pAdobename ) ); + m_aAdobenameToUnicode.insert( ::boost::unordered_multimap< ::rtl::OString, sal_Unicode, ::rtl::OStringHash >::value_type( aAdobeCodes[i].pAdobename, aAdobeCodes[i].aUnicode ) ); + if( aAdobeCodes[i].aAdobeStandardCode ) + { + m_aUnicodeToAdobecode.insert( ::boost::unordered_multimap< sal_Unicode, sal_uInt8 >::value_type( aAdobeCodes[i].aUnicode, aAdobeCodes[i].aAdobeStandardCode ) ); + m_aAdobecodeToUnicode.insert( ::boost::unordered_multimap< sal_uInt8, sal_Unicode >::value_type( aAdobeCodes[i].aAdobeStandardCode, aAdobeCodes[i].aUnicode ) ); + } + } +} + +// ------------------------------------------------------------------------- + +PrintFontManager::~PrintFontManager() +{ + deinitFontconfig(); + for( ::boost::unordered_map< fontID, PrintFont* >::const_iterator it = m_aFonts.begin(); it != m_aFonts.end(); ++it ) + delete (*it).second; + delete m_pAtoms; + if( m_pFontCache ) + delete m_pFontCache; +} + +// ------------------------------------------------------------------------- + +const OString& PrintFontManager::getDirectory( int nAtom ) const +{ + ::boost::unordered_map< int, OString >::const_iterator it( m_aAtomToDir.find( nAtom ) ); + return it != m_aAtomToDir.end() ? it->second : s_aEmptyOString; +} + +// ------------------------------------------------------------------------- + +int PrintFontManager::getDirectoryAtom( const OString& rDirectory, bool bCreate ) +{ + int nAtom = 0; + ::boost::unordered_map< OString, int, OStringHash >::const_iterator it + ( m_aDirToAtom.find( rDirectory ) ); + if( it != m_aDirToAtom.end() ) + nAtom = it->second; + else if( bCreate ) + { + nAtom = m_nNextDirAtom++; + m_aDirToAtom[ rDirectory ] = nAtom; + m_aAtomToDir[ nAtom ] = rDirectory; + } + return nAtom; +} + +// ------------------------------------------------------------------------- + +int PrintFontManager::addFontFile( const ::rtl::OString& rFileName, int /*nFaceNum*/ ) +{ + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + INetURLObject aPath( OStringToOUString( rFileName, aEncoding ), INET_PROT_FILE, INetURLObject::ENCODE_ALL ); + OString aName( OUStringToOString( aPath.GetName(), aEncoding ) ); + OString aDir( OUStringToOString( aPath.GetPath(), aEncoding ) ); + + int nDirID = getDirectoryAtom( aDir, true ); + fontID nFontId = findFontFileID( nDirID, aName ); + if( !nFontId ) + { + ::std::list< PrintFont* > aNewFonts; + if( analyzeFontFile( nDirID, aName, ::std::list<OString>(), aNewFonts ) ) + { + for( ::std::list< PrintFont* >::iterator it = aNewFonts.begin(); + it != aNewFonts.end(); ++it ) + { + m_aFonts[ nFontId = m_nNextFontID++ ] = *it; + m_aFontFileToFontID[ aName ].insert( nFontId ); + m_pFontCache->updateFontCacheEntry( *it, true ); + } + } + } + return nFontId; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::analyzeFontFile( int nDirID, const OString& rFontFile, const ::std::list<OString>& rXLFDs, ::std::list< PrintFontManager::PrintFont* >& rNewFonts ) const +{ + rNewFonts.clear(); + + OString aDir( getDirectory( nDirID ) ); + + OString aFullPath( aDir ); + aFullPath += "/"; + aFullPath += rFontFile; + + // #i1872# reject unreadable files + if( access( aFullPath.getStr(), R_OK ) ) + return false; + + ByteString aExt( rFontFile.copy( rFontFile.lastIndexOf( '.' )+1 ) ); + if( aExt.EqualsIgnoreCaseAscii( "pfb" ) || aExt.EqualsIgnoreCaseAscii( "pfa" ) ) + { + // check for corresponding afm metric + // first look for an adjacent file + static const char* pSuffix[] = { ".afm", ".AFM" }; + + for( unsigned int i = 0; i < SAL_N_ELEMENTS(pSuffix); i++ ) + { + ByteString aName( rFontFile ); + aName.Erase( aName.Len()-4 ); + aName.Append( pSuffix[i] ); + + ByteString aFilePath( aDir ); + aFilePath.Append( '/' ); + aFilePath.Append( aName ); + + ByteString aAfmFile; + if( access( aFilePath.GetBuffer(), R_OK ) ) + { + // try in subdirectory afm instead + aFilePath = aDir; + aFilePath.Append( "/afm/" ); + aFilePath.Append( aName ); + + if( ! access( aFilePath.GetBuffer(), R_OK ) ) + { + aAfmFile = "afm/"; + aAfmFile += aName; + } + } + else + aAfmFile = aName; + + if( aAfmFile.Len() ) + { + Type1FontFile* pFont = new Type1FontFile(); + pFont->m_nDirectory = nDirID; + + pFont->m_aFontFile = rFontFile; + pFont->m_aMetricFile = aAfmFile; + + if( ! pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, true ) ) + { + delete pFont; + pFont = NULL; + } + if( pFont && rXLFDs.size() ) + getFontAttributesFromXLFD( pFont, rXLFDs ); + if( pFont ) + rNewFonts.push_back( pFont ); + break; + } + } + } + else if( aExt.EqualsIgnoreCaseAscii( "afm" ) ) + { + ByteString aFilePath( aDir ); + aFilePath.Append( '/' ); + aFilePath.Append( ByteString( rFontFile ) ); + BuiltinFont* pFont = new BuiltinFont(); + pFont->m_nDirectory = nDirID; + pFont->m_aMetricFile = rFontFile; + if( pFont->readAfmMetrics( aFilePath, m_pAtoms, false, true ) ) + rNewFonts.push_back( pFont ); + else + delete pFont; + } + else if( aExt.EqualsIgnoreCaseAscii( "ttf" ) + || aExt.EqualsIgnoreCaseAscii( "tte" ) // #i33947# for Gaiji support + || aExt.EqualsIgnoreCaseAscii( "otf" ) ) // check for TTF- and PS-OpenType too + { + TrueTypeFontFile* pFont = new TrueTypeFontFile(); + pFont->m_nDirectory = nDirID; + pFont->m_aFontFile = rFontFile; + pFont->m_nCollectionEntry = -1; + + if( rXLFDs.size() ) + getFontAttributesFromXLFD( pFont, rXLFDs ); + // need to read the font anyway to get aliases inside the font file + if( ! analyzeTrueTypeFile( pFont ) ) + { + delete pFont; + pFont = NULL; + } + else + rNewFonts.push_back( pFont ); + } + else if( aExt.EqualsIgnoreCaseAscii( "ttc" ) ) + { + // get number of ttc entries + int nLength = CountTTCFonts( aFullPath.getStr() ); + if( nLength ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "%s contains %d fonts\n", aFullPath.getStr(), nLength ); +#endif + for( int i = 0; i < nLength; i++ ) + { + TrueTypeFontFile* pFont = new TrueTypeFontFile(); + pFont->m_nDirectory = nDirID; + pFont->m_aFontFile = rFontFile; + pFont->m_nCollectionEntry = i; + if( nLength == 1 ) + getFontAttributesFromXLFD( pFont, rXLFDs ); + if( ! analyzeTrueTypeFile( pFont ) ) + { + delete pFont; + pFont = NULL; + } + else + rNewFonts.push_back( pFont ); + } + } +#if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "CountTTCFonts( \"%s/%s\" ) failed\n", getDirectory(nDirID).getStr(), rFontFile.getStr() ); +#endif + } + return ! rNewFonts.empty(); +} + +// ------------------------------------------------------------------------- + +fontID PrintFontManager::findFontBuiltinID( int nPSNameAtom ) const +{ + fontID nID = 0; + ::boost::unordered_map< fontID, PrintFont* >::const_iterator it; + for( it = m_aFonts.begin(); nID == 0 && it != m_aFonts.end(); ++it ) + { + if( it->second->m_eType == fonttype::Builtin && + it->second->m_nPSName == nPSNameAtom ) + nID = it->first; + } + return nID; +} + +// ------------------------------------------------------------------------- + +fontID PrintFontManager::findFontFileID( int nDirID, const OString& rFontFile ) const +{ + fontID nID = 0; + + ::boost::unordered_map< OString, ::std::set< fontID >, OStringHash >::const_iterator set_it = m_aFontFileToFontID.find( rFontFile ); + if( set_it != m_aFontFileToFontID.end() ) + { + for( ::std::set< fontID >::const_iterator font_it = set_it->second.begin(); font_it != set_it->second.end() && ! nID; ++font_it ) + { + ::boost::unordered_map< fontID, PrintFont* >::const_iterator it = m_aFonts.find( *font_it ); + if( it != m_aFonts.end() ) + { + switch( it->second->m_eType ) + { + case fonttype::Type1: + { + Type1FontFile* const pFont = static_cast< Type1FontFile* const >((*it).second); + if( pFont->m_nDirectory == nDirID && + pFont->m_aFontFile == rFontFile ) + nID = it->first; + } + break; + case fonttype::TrueType: + { + TrueTypeFontFile* const pFont = static_cast< TrueTypeFontFile* const >((*it).second); + if( pFont->m_nDirectory == nDirID && + pFont->m_aFontFile == rFontFile ) + nID = it->first; + } + break; + case fonttype::Builtin: + if( static_cast<const BuiltinFont*>((*it).second)->m_nDirectory == nDirID && + static_cast<const BuiltinFont*>((*it).second)->m_aMetricFile == rFontFile ) + nID = it->first; + break; + default: + break; + } + } + } + } + return nID; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::parseXLFD( const OString& rXLFD, XLFDEntry& rEntry ) +{ + sal_Int32 nIndex = 0; + OString aFoundry = WhitespaceToSpace( rXLFD.getToken( 1, '-', nIndex ) ); + if( nIndex < 0 ) return false; + OString aFamilyXLFD = WhitespaceToSpace( rXLFD.getToken( 0, '-', nIndex ) ); + if( nIndex < 0 ) return false; + OString aWeight = rXLFD.getToken( 0, '-', nIndex ).toAsciiLowerCase(); + if( nIndex < 0 ) return false; + OString aSlant = rXLFD.getToken( 0, '-', nIndex ).toAsciiLowerCase(); + if( nIndex < 0 ) return false; + OString aWidth = rXLFD.getToken( 0, '-', nIndex ).toAsciiLowerCase(); + if( nIndex < 0 ) return false; + OString aAddStyle = rXLFD.getToken( 0, '-', nIndex ).toAsciiLowerCase(); + if( nIndex < 0 ) return false; + OString aPitch = rXLFD.getToken( 4, '-', nIndex ).toAsciiLowerCase(); + if( nIndex < 0 ) return false; + OString aRegEnc = WhitespaceToSpace( rXLFD.getToken( 1, '-', nIndex ).toAsciiLowerCase() ); + if( nIndex < 0 ) return false; + OString aEnc = WhitespaceToSpace( rXLFD.getToken( 0, '-', nIndex ).toAsciiLowerCase() ); + + // capitalize words + sal_Int32 nFamIndex = 0; + OStringBuffer aFamilyName; + while( nFamIndex >= 0 ) + { + OString aToken = aFamilyXLFD.getToken( 0, ' ', nFamIndex ); + sal_Char aFirst = aToken.toChar(); + if( aFirst >= 'a' && aFirst <= 'z' ) + aFirst = aFirst - 'a' + 'A'; + OStringBuffer aNewToken( aToken.getLength() ); + aNewToken.append( aToken ); + aNewToken.setCharAt( 0, aFirst ); + if( aFamilyName.getLength() > 0 ) + aFamilyName.append( ' ' ); + aFamilyName.append( aNewToken.makeStringAndClear() ); + } + + rEntry.aFoundry = aFoundry; + rEntry.aFamily = aFamilyName.makeStringAndClear(); + rEntry.aAddStyle = aAddStyle; + // evaluate weight + rEntry.eWeight = parseWeight( aWeight ); + // evaluate slant + rEntry.eItalic = parseItalic( aSlant ); + // evaluate width + rEntry.eWidth = parseWidth( aWidth ); + + // evaluate pitch + if( aPitch.toChar() == 'c' || aPitch.toChar() == 'm' ) + rEntry.ePitch = pitch::Fixed; + else + rEntry.ePitch = pitch::Variable; + + OString aToken = aEnc.toAsciiLowerCase(); + // get encoding + if( aAddStyle.indexOf( "symbol" ) != -1 ) + rEntry.aEncoding = RTL_TEXTENCODING_SYMBOL; + else + { + if( aToken.equals( "symbol" ) ) + rEntry.aEncoding = RTL_TEXTENCODING_SYMBOL; + else + { + OStringBuffer aCharset( aRegEnc.getLength() + aEnc.getLength() + 1 ); + aCharset.append( aRegEnc ); + aCharset.append( '-' ); + aCharset.append( aEnc ); + rEntry.aEncoding = rtl_getTextEncodingFromUnixCharset( aCharset.getStr() ); + } + } + + // set correct mask flags + rEntry.nMask = 0; + if( rEntry.aFoundry != "*" ) rEntry.nMask |= XLFDEntry::MaskFoundry; + if( rEntry.aFamily != "*" ) rEntry.nMask |= XLFDEntry::MaskFamily; + if( rEntry.aAddStyle != "*" ) rEntry.nMask |= XLFDEntry::MaskAddStyle; + if( aWeight != "*" ) rEntry.nMask |= XLFDEntry::MaskWeight; + if( aSlant != "*" ) rEntry.nMask |= XLFDEntry::MaskItalic; + if( aWidth != "*" ) rEntry.nMask |= XLFDEntry::MaskWidth; + if( aPitch != "*" ) rEntry.nMask |= XLFDEntry::MaskPitch; + if( aRegEnc != "*" && aEnc != "*" ) rEntry.nMask |= XLFDEntry::MaskEncoding; + + return true; +} + +// ------------------------------------------------------------------------- + +void PrintFontManager::parseXLFD_appendAliases( const std::list< OString >& rXLFDs, std::list< XLFDEntry >& rEntries ) const +{ + for( std::list< OString >::const_iterator it = rXLFDs.begin(); it != rXLFDs.end(); ++it ) + { + XLFDEntry aEntry; + if( ! parseXLFD(*it, aEntry) ) + continue; + rEntries.push_back( aEntry ); + std::map< XLFDEntry, std::list< XLFDEntry > >::const_iterator alias_it = + m_aXLFD_Aliases.find( aEntry ); + if( alias_it != m_aXLFD_Aliases.end() ) + { + rEntries.insert( rEntries.end(), alias_it->second.begin(), alias_it->second.end() ); + } + } +} + +// ------------------------------------------------------------------------- + +void PrintFontManager::getFontAttributesFromXLFD( PrintFont* pFont, const std::list< OString >& rXLFDs ) const +{ + bool bFamilyName = false; + + std::list< XLFDEntry > aXLFDs; + + parseXLFD_appendAliases( rXLFDs, aXLFDs ); + + for( std::list< XLFDEntry >::const_iterator it = aXLFDs.begin(); + it != aXLFDs.end(); ++it ) + { + // set family name or alias + int nFam = + m_pAtoms->getAtom( ATOM_FAMILYNAME, + OStringToOUString( it->aFamily, it->aAddStyle.indexOf( "utf8" ) != -1 ? RTL_TEXTENCODING_UTF8 : RTL_TEXTENCODING_ISO_8859_1 ), + sal_True ); + if( ! bFamilyName ) + { + bFamilyName = true; + pFont->m_nFamilyName = nFam; + switch( pFont->m_eType ) + { + case fonttype::Type1: + static_cast<Type1FontFile*>(pFont)->m_aXLFD = rXLFDs.front(); + break; + case fonttype::TrueType: + static_cast<TrueTypeFontFile*>(pFont)->m_aXLFD = rXLFDs.front(); + break; + default: + break; + } + } + else + { + // make sure that aliases are unique + if( nFam != pFont->m_nFamilyName ) + { + std::list< int >::const_iterator al_it; + for( al_it = pFont->m_aAliases.begin(); al_it != pFont->m_aAliases.end() && *al_it != nFam; ++al_it ) + ; + if( al_it == pFont->m_aAliases.end() ) + pFont->m_aAliases.push_back( nFam ); + + } + // for the rest of the attributes there can only be one value; + // we'll trust the first one + continue; + } + + // fill in weight + pFont->m_eWeight = it->eWeight; + // fill in slant + pFont->m_eItalic = it->eItalic; + // fill in width + pFont->m_eWidth = it->eWidth; + // fill in pitch + pFont->m_ePitch = it->ePitch; + // fill in encoding + pFont->m_aEncoding = it->aEncoding; + } + + // handle iso8859-1 as ms1252 to fill the "gap" starting at 0x80 + if( pFont->m_aEncoding == RTL_TEXTENCODING_ISO_8859_1 ) + pFont->m_aEncoding = RTL_TEXTENCODING_MS_1252; + if( rXLFDs.begin() != rXLFDs.end() ) + { + switch( pFont->m_eType ) + { + case fonttype::Type1: + static_cast<Type1FontFile*>(pFont)->m_aXLFD = rXLFDs.front(); + break; + case fonttype::TrueType: + static_cast<TrueTypeFontFile*>(pFont)->m_aXLFD = rXLFDs.front(); + break; + default: break; + } + } +} + +// ------------------------------------------------------------------------- + +OString PrintFontManager::getXLFD( PrintFont* pFont ) const +{ + if( pFont->m_eType == fonttype::Type1 ) + { + if( static_cast<Type1FontFile*>(pFont)->m_aXLFD.getLength() ) + return static_cast<Type1FontFile*>(pFont)->m_aXLFD; + } + if( pFont->m_eType == fonttype::TrueType ) + { + if( static_cast<TrueTypeFontFile*>(pFont)->m_aXLFD.getLength() ) + return static_cast<TrueTypeFontFile*>(pFont)->m_aXLFD; + } + + OStringBuffer aXLFD( 128 ); + + aXLFD.append( "-misc-" ); + ByteString aFamily( String( m_pAtoms->getString( ATOM_FAMILYNAME, pFont->m_nFamilyName ) ), RTL_TEXTENCODING_UTF8 ); + aFamily.SearchAndReplaceAll( '-',' ' ); + aFamily.SearchAndReplaceAll( '?',' ' ); + aFamily.SearchAndReplaceAll( '*',' ' ); + aXLFD.append( OString( aFamily ) ); + aXLFD.append( '-' ); + switch( pFont->m_eWeight ) + { + case weight::Thin: aXLFD.append("thin");break; + case weight::UltraLight: aXLFD.append("ultralight");break; + case weight::Light: aXLFD.append("light");break; + case weight::SemiLight: aXLFD.append("semilight");break; + case weight::Normal: aXLFD.append("normal");break; + case weight::Medium: aXLFD.append("medium");break; + case weight::SemiBold: aXLFD.append("semibold");break; + case weight::Bold: aXLFD.append("bold");break; + case weight::UltraBold: aXLFD.append("ultrabold");break; + case weight::Black: aXLFD.append("black");break; + default: break; + } + aXLFD.append('-'); + switch( pFont->m_eItalic ) + { + case italic::Upright: aXLFD.append('r');break; + case italic::Oblique: aXLFD.append('o');break; + case italic::Italic: aXLFD.append('i');break; + default: break; + } + aXLFD.append('-'); + switch( pFont->m_eWidth ) + { + case width::UltraCondensed: aXLFD.append("ultracondensed");break; + case width::ExtraCondensed: aXLFD.append("extracondensed");break; + case width::Condensed: aXLFD.append("condensed");break; + case width::SemiCondensed: aXLFD.append("semicondensed");break; + case width::Normal: aXLFD.append("normal");break; + case width::SemiExpanded: aXLFD.append("semiexpanded");break; + case width::Expanded: aXLFD.append("expanded");break; + case width::ExtraExpanded: aXLFD.append("extraexpanded");break; + case width::UltraExpanded: aXLFD.append("ultraexpanded");break; + default: break; + } + aXLFD.append("-utf8-0-0-0-0-"); + aXLFD.append( pFont->m_ePitch == pitch::Fixed ? "m" : "p" ); + aXLFD.append("-0-"); + const char* pEnc = rtl_getBestUnixCharsetFromTextEncoding( pFont->m_aEncoding ); + if( ! pEnc ) + { + if( pFont->m_aEncoding == RTL_TEXTENCODING_ADOBE_STANDARD ) + pEnc = "adobe-standard"; + else + pEnc = "iso8859-1"; + } + aXLFD .append( pEnc ); + + return aXLFD.makeStringAndClear(); +} + +// ------------------------------------------------------------------------- + +OUString PrintFontManager::convertTrueTypeName( void* pRecord ) const +{ + NameRecord* pNameRecord = (NameRecord*)pRecord; + OUString aValue; + if( + ( pNameRecord->platformID == 3 && ( pNameRecord->encodingID == 0 || pNameRecord->encodingID == 1 ) ) // MS, Unicode + || + ( pNameRecord->platformID == 0 ) // Apple, Unicode + ) + { + OUStringBuffer aName( pNameRecord->slen/2 ); + const sal_uInt8* pNameBuffer = pNameRecord->sptr; + for(int n = 0; n < pNameRecord->slen/2; n++ ) + aName.append( (sal_Unicode)getUInt16BE( pNameBuffer ) ); + aValue = aName.makeStringAndClear(); + } + else if( pNameRecord->platformID == 3 ) + { + if( pNameRecord->encodingID >= 2 && pNameRecord->encodingID <= 6 ) + { + /* + * and now for a special kind of madness: + * some fonts encode their byte value string as BE uint16 + * (leading to stray zero bytes in the string) + * while others code two bytes as a uint16 and swap to BE + */ + OStringBuffer aName; + const sal_uInt8* pNameBuffer = pNameRecord->sptr; + for(int n = 0; n < pNameRecord->slen/2; n++ ) + { + sal_Unicode aCode = (sal_Unicode)getUInt16BE( pNameBuffer ); + sal_Char aChar = aCode >> 8; + if( aChar ) + aName.append( aChar ); + aChar = aCode & 0x00ff; + if( aChar ) + aName.append( aChar ); + } + switch( pNameRecord->encodingID ) + { + case 2: + aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_932 ); + break; + case 3: + aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_936 ); + break; + case 4: + aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_950 ); + break; + case 5: + aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_949 ); + break; + case 6: + aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_1361 ); + break; + } + } + } + return aValue; +} + +//fdo#33349.There exists an archaic Berling Antiqua font which has a "Times New +//Roman" name field in it. We don't want the "Times New Roman" name to take +//precedence in this case. We take Berling Antiqua as a higher priority name, +//and erase the "Times New Roman" name +namespace +{ + bool isBadTNR(const OUString &rName, ::std::set< OUString >& rSet) + { + bool bRet = false; + if (rName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("Berling Antiqua"))) + { + ::std::set< OUString >::iterator aEnd = rSet.end(); + ::std::set< OUString >::iterator aI = rSet.find(OUString(RTL_CONSTASCII_USTRINGPARAM("Times New Roman"))); + if (aI != aEnd) + { + bRet = true; + rSet.erase(aI); + } + } + return bRet; + } +} + +// ------------------------------------------------------------------------- + +void PrintFontManager::analyzeTrueTypeFamilyName( void* pTTFont, ::std::list< OUString >& rNames ) const +{ + OUString aFamily; + + rNames.clear(); + ::std::set< OUString > aSet; + + NameRecord* pNameRecords = NULL; + int nNameRecords = GetTTNameRecords( (TrueTypeFont*)pTTFont, &pNameRecords ); + if( nNameRecords && pNameRecords ) + { + LanguageType aLang = MsLangId::getSystemLanguage(); + int nLastMatch = -1; + for( int i = 0; i < nNameRecords; i++ ) + { + if( pNameRecords[i].nameID != 1 || pNameRecords[i].sptr == NULL ) + continue; + int nMatch = -1; + if( pNameRecords[i].platformID == 0 ) // Unicode + nMatch = 4000; + else if( pNameRecords[i].platformID == 3 ) + { + // this bases on the LanguageType actually being a Win LCID + if( pNameRecords[i].languageID == aLang ) + nMatch = 8000; + else if( pNameRecords[i].languageID == LANGUAGE_ENGLISH_US ) + nMatch = 2000; + else if( pNameRecords[i].languageID == LANGUAGE_ENGLISH || + pNameRecords[i].languageID == LANGUAGE_ENGLISH_UK ) + nMatch = 1500; + else + nMatch = 1000; + } + OUString aName = convertTrueTypeName( pNameRecords + i ); + aSet.insert( aName ); + if( nMatch > nLastMatch || isBadTNR(aName, aSet) ) + { + nLastMatch = nMatch; + aFamily = aName; + } + } + DisposeNameRecords( pNameRecords, nNameRecords ); + } + if( aFamily.getLength() ) + { + rNames.push_front( aFamily ); + for( ::std::set< OUString >::const_iterator it = aSet.begin(); it != aSet.end(); ++it ) + if( *it != aFamily ) + rNames.push_back( *it ); + } + return; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::analyzeTrueTypeFile( PrintFont* pFont ) const +{ + bool bSuccess = false; + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + ByteString aFile = getFontFile( pFont ); + TrueTypeFont* pTTFont = NULL; + + TrueTypeFontFile* pTTFontFile = static_cast< TrueTypeFontFile* >(pFont); + if( OpenTTFontFile( aFile.GetBuffer(), pTTFontFile->m_nCollectionEntry < 0 ? 0 : pTTFontFile->m_nCollectionEntry, &pTTFont ) == SF_OK ) + { + TTGlobalFontInfo aInfo; + GetTTGlobalFontInfo( pTTFont, & aInfo ); + + ::std::list< OUString > aNames; + analyzeTrueTypeFamilyName( pTTFont, aNames ); + + // set family name from XLFD if possible + if( ! pFont->m_nFamilyName ) + { + if( aNames.begin() != aNames.end() ) + { + pFont->m_nFamilyName = m_pAtoms->getAtom( ATOM_FAMILYNAME, aNames.front(), sal_True ); + aNames.pop_front(); + } + else + { + sal_Int32 dotIndex; + + // poor font does not have a family name + // name it to file name minus the extension + dotIndex = pTTFontFile->m_aFontFile.lastIndexOf( '.' ); + if ( dotIndex == -1 ) + dotIndex = pTTFontFile->m_aFontFile.getLength(); + + pFont->m_nFamilyName = m_pAtoms->getAtom( ATOM_FAMILYNAME, OStringToOUString( pTTFontFile->m_aFontFile.copy( 0, dotIndex ), aEncoding ), sal_True ); + } + } + for( ::std::list< OUString >::iterator it = aNames.begin(); it != aNames.end(); ++it ) + { + if( it->getLength() ) + { + int nAlias = m_pAtoms->getAtom( ATOM_FAMILYNAME, *it, sal_True ); + if( nAlias != pFont->m_nFamilyName ) + { + std::list< int >::const_iterator al_it; + for( al_it = pFont->m_aAliases.begin(); al_it != pFont->m_aAliases.end() && *al_it != nAlias; ++al_it ) + ; + if( al_it == pFont->m_aAliases.end() ) + pFont->m_aAliases.push_back( nAlias ); + } + } + } + + if( aInfo.usubfamily ) + pFont->m_aStyleName = OUString( aInfo.usubfamily ); + + pFont->m_nPSName = m_pAtoms->getAtom( ATOM_PSNAME, String( ByteString( aInfo.psname ), aEncoding ), sal_True ); + switch( aInfo.weight ) + { + case FW_THIN: pFont->m_eWeight = weight::Thin; break; + case FW_EXTRALIGHT: pFont->m_eWeight = weight::UltraLight; break; + case FW_LIGHT: pFont->m_eWeight = weight::Light; break; + case FW_MEDIUM: pFont->m_eWeight = weight::Medium; break; + case FW_SEMIBOLD: pFont->m_eWeight = weight::SemiBold; break; + case FW_BOLD: pFont->m_eWeight = weight::Bold; break; + case FW_EXTRABOLD: pFont->m_eWeight = weight::UltraBold; break; + case FW_BLACK: pFont->m_eWeight = weight::Black; break; + + case FW_NORMAL: + default: pFont->m_eWeight = weight::Normal; break; + } + + switch( aInfo.width ) + { + case FWIDTH_ULTRA_CONDENSED: pFont->m_eWidth = width::UltraCondensed; break; + case FWIDTH_EXTRA_CONDENSED: pFont->m_eWidth = width::ExtraCondensed; break; + case FWIDTH_CONDENSED: pFont->m_eWidth = width::Condensed; break; + case FWIDTH_SEMI_CONDENSED: pFont->m_eWidth = width::SemiCondensed; break; + case FWIDTH_SEMI_EXPANDED: pFont->m_eWidth = width::SemiExpanded; break; + case FWIDTH_EXPANDED: pFont->m_eWidth = width::Expanded; break; + case FWIDTH_EXTRA_EXPANDED: pFont->m_eWidth = width::ExtraExpanded; break; + case FWIDTH_ULTRA_EXPANDED: pFont->m_eWidth = width::UltraExpanded; break; + + case FWIDTH_NORMAL: + default: pFont->m_eWidth = width::Normal; break; + } + + pFont->m_ePitch = aInfo.pitch ? pitch::Fixed : pitch::Variable; + pFont->m_eItalic = aInfo.italicAngle == 0 ? italic::Upright : ( aInfo.italicAngle < 0 ? italic::Italic : italic::Oblique ); + // #104264# there are fonts that set italic angle 0 although they are + // italic; use macstyle bit here + if( aInfo.italicAngle == 0 && (aInfo.macStyle & 2) ) + pFont->m_eItalic = italic::Italic; + + pFont->m_aEncoding = aInfo.symbolEncoded ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UCS2; + + pFont->m_aGlobalMetricY.width = pFont->m_aGlobalMetricX.width = aInfo.xMax - aInfo.xMin; + pFont->m_aGlobalMetricY.height = pFont->m_aGlobalMetricX.height = aInfo.yMax - aInfo.yMin; + + if( aInfo.winAscent && aInfo.winDescent ) + { + pFont->m_nAscend = aInfo.winAscent; + pFont->m_nDescend = aInfo.winDescent; + pFont->m_nLeading = pFont->m_nAscend + pFont->m_nDescend - 1000; + } + else if( aInfo.typoAscender && aInfo.typoDescender ) + { + pFont->m_nLeading = aInfo.typoLineGap; + pFont->m_nAscend = aInfo.typoAscender; + pFont->m_nDescend = -aInfo.typoDescender; + } + else + { + pFont->m_nLeading = aInfo.linegap; + pFont->m_nAscend = aInfo.ascender; + pFont->m_nDescend = -aInfo.descender; + } + + // last try: font bounding box + if( pFont->m_nAscend == 0 ) + pFont->m_nAscend = aInfo.yMax; + if( pFont->m_nDescend == 0 ) + pFont->m_nDescend = -aInfo.yMin; + if( pFont->m_nLeading == 0 ) + pFont->m_nLeading = 15 * (pFont->m_nAscend+pFont->m_nDescend) / 100; + + if( pFont->m_nAscend ) + pFont->m_aGlobalMetricX.height = pFont->m_aGlobalMetricY.height = pFont->m_nAscend + pFont->m_nDescend; + + // get bounding box + pFont->m_nXMin = aInfo.xMin; + pFont->m_nYMin = aInfo.yMin; + pFont->m_nXMax = aInfo.xMax; + pFont->m_nYMax = aInfo.yMax; + + // get type flags + pTTFontFile->m_nTypeFlags = (unsigned int)aInfo.typeFlags; + + // get vertical substitutions flag + pFont->m_bHaveVerticalSubstitutedGlyphs = DoesVerticalSubstitution( pTTFont, 1 ); + + CloseTTFont( pTTFont ); + bSuccess = true; + } +#if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "could not OpenTTFont \"%s\"\n", aFile.GetBuffer() ); +#endif + + return bSuccess; +} + +// ------------------------------------------------------------------------- + +void PrintFontManager::initFontsAlias() +{ + m_aXLFD_Aliases.clear(); + rtl_TextEncoding aEnc = osl_getThreadTextEncoding(); + for( std::list< OString >::const_iterator dir_it = m_aFontDirectories.begin(); + dir_it != m_aFontDirectories.end(); ++dir_it ) + { + OStringBuffer aDirName(512); + aDirName.append( *dir_it ); + aDirName.append( "/fonts.alias" ); + SvFileStream aStream( OStringToOUString( aDirName.makeStringAndClear(), aEnc ), STREAM_READ ); + if( ! aStream.IsOpen() ) + continue; + + do + { + ByteString aLine; + aStream.ReadLine( aLine ); + + // get the alias and the pattern it gets translated to + ByteString aAlias = GetCommandLineToken( 0, aLine ); + ByteString aMap = GetCommandLineToken( 1, aLine ); + + // remove eventual quotes + aAlias.EraseLeadingChars( '"' ); + aAlias.EraseTrailingChars( '"' ); + aMap.EraseLeadingChars( '"' ); + aMap.EraseTrailingChars( '"' ); + + XLFDEntry aAliasEntry, aMapEntry; + parseXLFD( aAlias, aAliasEntry ); + parseXLFD( aMap, aMapEntry ); + + if( aAliasEntry.nMask && aMapEntry.nMask ) + m_aXLFD_Aliases[ aMapEntry ].push_back( aAliasEntry ); + } while( ! aStream.IsEof() ); + } +} + +// code stolen from vcl's RegisterFontSubstitutors() +// TODO: use that method once psprint gets merged into vcl +static bool AreFCSubstitutionsEnabled() +{ + // init font substitution defaults + int nDisableBits = 0; +#ifdef SOLARIS + // TODO: check the OS version and fc-data maintenance level + nDisableBits = 1; // disable "font fallback" here on default +#endif + // apply the environment variable if any + const char* pEnvStr = ::getenv( "SAL_DISABLE_FC_SUBST" ); + if( pEnvStr ) + { + // + if( (*pEnvStr >= '0') && (*pEnvStr <= '9') ) + nDisableBits = (*pEnvStr - '0'); + else + nDisableBits = ~0U; // no specific bits set: disable all + } + + return ((nDisableBits & 3) == 0); +} + +void PrintFontManager::initialize() +{ + #ifdef CALLGRIND_COMPILE + CALLGRIND_TOGGLE_COLLECT(); + CALLGRIND_ZERO_STATS(); + #endif + + long aDirEntBuffer[ (sizeof(struct dirent)+_PC_NAME_MAX)+1 ]; + + if( ! m_pFontCache ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "creating font cache ... " ); + clock_t aStart; + struct tms tms; + aStart = times( &tms ); +#endif + m_pFontCache = new FontCache(); +#if OSL_DEBUG_LEVEL > 1 + clock_t aStop = times( &tms ); + fprintf( stderr, "done in %lf s\n", (double)(aStop - aStart)/(double)sysconf( _SC_CLK_TCK ) ); +#endif + } + + // initialize may be called twice in the future + { + for( ::boost::unordered_map< fontID, PrintFont* >::const_iterator it = m_aFonts.begin(); it != m_aFonts.end(); ++it ) + delete (*it).second; + m_nNextFontID = 1; + m_aFonts.clear(); + m_aFontDirectories.clear(); + m_aPrivateFontDirectories.clear(); + m_aOverrideFonts.clear(); + } + +#if OSL_DEBUG_LEVEL > 1 + clock_t aStart; + clock_t aStep1; + clock_t aStep2; + clock_t aStep3; + int nBuiltinFonts = 0; + int nCached = 0; + + struct tms tms; + + aStart = times( &tms ); +#endif + + // first try fontconfig + m_bFontconfigSuccess = initFontconfig(); + + // part one - look for downloadable fonts + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + const ::rtl::OUString &rSalPrivatePath = psp::getFontPath(); + + // search for the fonts in SAL_PRIVATE_FONTPATH first; those are + // the fonts installed with the office + if( rSalPrivatePath.getLength() ) + { + OString aPath = rtl::OUStringToOString( rSalPrivatePath, aEncoding ); + const bool bAreFCSubstitutionsEnabled = AreFCSubstitutionsEnabled(); + sal_Int32 nIndex = 0; + do + { + OString aToken = aPath.getToken( 0, ';', nIndex ); + normPath( aToken ); + // if registering an app-specific fontdir with fontconfig fails + // and fontconfig-based substitutions are enabled + // then trying to use these app-specific fonts doesn't make sense + if( m_bFontconfigSuccess && !addFontconfigDir( aToken ) ) + if( bAreFCSubstitutionsEnabled ) + continue; + m_aFontDirectories.push_back( aToken ); + m_aPrivateFontDirectories.push_back( getDirectoryAtom( aToken, true ) ); + } while( nIndex >= 0 ); + } + + // protect against duplicate paths + boost::unordered_map< OString, int, OStringHash > visited_dirs; + + // now that all global and local font dirs are known to fontconfig + // check that there are fonts actually managed by fontconfig + // also don't search directories that fontconfig already did + if( m_bFontconfigSuccess ) + m_bFontconfigSuccess = (countFontconfigFonts( visited_dirs ) > 0); + + // don't search through many directories fontconfig already told us about + if( ! m_bFontconfigSuccess ) + ImplGetSVData()->mpDefInst->FillFontPathList( m_aFontDirectories ); + + // fill XLFD aliases from fonts.alias files + initFontsAlias(); + + // search for font files in each path + std::list< OString >::iterator dir_it; + for( dir_it = m_aFontDirectories.begin(); dir_it != m_aFontDirectories.end(); ++dir_it ) + { + OString aPath( *dir_it ); + // see if we were here already + if( visited_dirs.find( aPath ) != visited_dirs.end() ) + continue; + visited_dirs[ aPath ] = 1; + + // there may be ":unscaled" directories (see XFree86) + // it should be safe to ignore them since they should not + // contain any of our recognizeable fonts + + // ask the font cache whether it handles this directory + std::list< PrintFont* > aCacheFonts; + if( m_pFontCache->listDirectory( aPath, aCacheFonts ) ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "adding cache directory: %s\n", aPath.getStr() ); +#endif + for( ::std::list< PrintFont* >::iterator it = aCacheFonts.begin(); it != aCacheFonts.end(); ++it ) + { + fontID aFont = m_nNextFontID++; + m_aFonts[ aFont ] = *it; + if( (*it)->m_eType == fonttype::Type1 ) + m_aFontFileToFontID[ static_cast<Type1FontFile*>(*it)->m_aFontFile ].insert( aFont ); + else if( (*it)->m_eType == fonttype::TrueType ) + m_aFontFileToFontID[ static_cast<TrueTypeFontFile*>(*it)->m_aFontFile ].insert( aFont ); + else if( (*it)->m_eType == fonttype::Builtin ) + m_aFontFileToFontID[ static_cast<BuiltinFont*>(*it)->m_aMetricFile ].insert( aFont ); +#if OSL_DEBUG_LEVEL > 1 + if( (*it)->m_eType == fonttype::Builtin ) + nBuiltinFonts++; + nCached++; +#if OSL_DEBUG_LEVEL > 2 + fprintf( stderr, "adding cached font %d: \"%s\" from %s\n", aFont, + OUStringToOString( getFontFamily( aFont ), RTL_TEXTENCODING_MS_1252 ).getStr(), + getFontFileSysPath( aFont ).getStr() ); +#endif +#endif + } + if( ! m_pFontCache->scanAdditionalFiles( aPath ) ) + continue; + } + + DIR* pDIR = opendir( aPath.getStr() ); + struct dirent* pEntry = (struct dirent*)aDirEntBuffer; + if( pDIR ) + { + // read fonts.dir if possible + ::boost::unordered_map< OString, ::std::list<OString>, OStringHash > aFontsDir; + int nDirID = getDirectoryAtom( aPath, true ); + // #i38367# no fonts.dir in our own directories anymore + std::list< int >::const_iterator priv_dir; + for( priv_dir = m_aPrivateFontDirectories.begin(); + priv_dir != m_aPrivateFontDirectories.end() && *priv_dir != nDirID; + ++priv_dir ) + ; + + if( priv_dir == m_aPrivateFontDirectories.end() ) + { + ByteString aGccDummy( aPath ); + String aFontsDirPath( aGccDummy, aEncoding ); + aFontsDirPath.AppendAscii( "/fonts.dir" ); + SvFileStream aStream( aFontsDirPath, STREAM_READ ); + if( aStream.IsOpen() ) + { + ByteString aLine; + while( ! aStream.IsEof() ) + { + aStream.ReadLine( aLine ); + ByteString aFileName( GetCommandLineToken( 0, aLine ) ); + ByteString aXLFD( aLine.Copy( aFileName.Len() ) ); + if( aFileName.Len() && aXLFD.Len() ) + aFontsDir[ aFileName ].push_back(aXLFD); + } + } + } + + int nDirFonts = 0; + while( ! readdir_r( pDIR, (struct dirent*)aDirEntBuffer, &pEntry ) && pEntry ) + { + OString aFileName( pEntry->d_name ); + // ignore .afm files here + if( aFileName.getLength() > 3 && + aFileName.lastIndexOf( ".afm" ) == aFileName.getLength()-4 ) + continue; + + struct stat aStat; + ByteString aFilePath( aPath ); + aFilePath.Append( '/' ); + aFilePath.Append( ByteString( aFileName ) ); + if( ! stat( aFilePath.GetBuffer(), &aStat ) && + S_ISREG( aStat.st_mode ) ) + { + if( findFontFileID( nDirID, aFileName ) == 0 ) + { + ::std::list<OString> aXLFDs; + ::boost::unordered_map< OString, ::std::list<OString>, OStringHash >::const_iterator it = + aFontsDir.find( aFileName ); + if( it != aFontsDir.end() ) + aXLFDs = (*it).second; + + // fill in font attributes from XLFD rather + // than reading every file + ::std::list< PrintFont* > aNewFonts; + if( analyzeFontFile( nDirID, aFileName, aXLFDs, aNewFonts ) ) + { + for( ::std::list< PrintFont* >::iterator font_it = aNewFonts.begin(); font_it != aNewFonts.end(); ++font_it ) + { + fontID aFont = m_nNextFontID++; + m_aFonts[ aFont ] = *font_it; + m_aFontFileToFontID[ aFileName ].insert( aFont ); + m_pFontCache->updateFontCacheEntry( *font_it, false ); + nDirFonts++; +#if OSL_DEBUG_LEVEL > 2 + fprintf( stderr, "adding font %d: \"%s\" from %s\n", aFont, + OUStringToOString( getFontFamily( aFont ), RTL_TEXTENCODING_MS_1252 ).getStr(), + getFontFileSysPath( aFont ).getStr() ); +#endif + } + } + } + } + } + closedir( pDIR ); + m_pFontCache->updateDirTimestamp( nDirID ); + if( ! nDirFonts ) + m_pFontCache->markEmptyDir( nDirID ); + } + } + +#if OSL_DEBUG_LEVEL > 1 + aStep1 = times( &tms ); +#endif + + // part two - look for metrics for builtin printer fonts + std::list< OUString > aMetricDirs; + psp::getPrinterPathList( aMetricDirs, PRINTER_METRICDIR ); + + std::list< OString > aEmptyFontsDir; + for( std::list< OUString >::const_iterator met_dir_it = aMetricDirs.begin(); met_dir_it != aMetricDirs.end(); ++met_dir_it ) + { + OString aDir = OUStringToOString( *met_dir_it, aEncoding ); + + // ask the font cache whether it handles this directory + std::list< PrintFont* > aCacheFonts; + + if( m_pFontCache->listDirectory( aDir, aCacheFonts ) ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "adding cache directory: %s\n", aDir.getStr() ); +#endif + for( ::std::list< PrintFont* >::iterator it = aCacheFonts.begin(); it != aCacheFonts.end(); ++it ) + { + fontID aFont = m_nNextFontID++; + m_aFonts[ aFont ] = *it; + if( (*it)->m_eType == fonttype::Type1 ) + m_aFontFileToFontID[ static_cast<Type1FontFile*>(*it)->m_aFontFile ].insert( aFont ); + else if( (*it)->m_eType == fonttype::TrueType ) + m_aFontFileToFontID[ static_cast<TrueTypeFontFile*>(*it)->m_aFontFile ].insert( aFont ); + else if( (*it)->m_eType == fonttype::Builtin ) + m_aFontFileToFontID[ static_cast<BuiltinFont*>(*it)->m_aMetricFile ].insert( aFont ); +#if OSL_DEBUG_LEVEL > 1 + if( (*it)->m_eType == fonttype::Builtin ) + nBuiltinFonts++; + nCached++; +#if OSL_DEBUG_LEVEL > 2 + fprintf( stderr, "adding cached font %d: \"%s\" from %s\n", aFont, + OUStringToOString( getFontFamily( aFont ), RTL_TEXTENCODING_MS_1252 ).getStr(), + getFontFileSysPath( aFont ).getStr() ); +#endif +#endif + } + continue; + } + + DIR* pDIR = opendir( aDir.getStr() ); + if( pDIR ) + { + struct dirent* pDirEntry = (struct dirent*)aDirEntBuffer; + int nDirID = getDirectoryAtom( aDir, true ); + int nDirFonts = 0; + + while( ! readdir_r( pDIR, (struct dirent*)aDirEntBuffer, &pDirEntry ) && pDirEntry ) + { + ByteString aFile( aDir ); + aFile += '/'; + aFile += pDirEntry->d_name; + struct stat aStat; + if( ! stat( aFile.GetBuffer(), &aStat ) + && S_ISREG( aStat.st_mode ) + ) + { + OString aFileName( pDirEntry->d_name, strlen( pDirEntry->d_name ) ); + OString aExt( aFileName.copy( aFileName.lastIndexOf( '.' )+1 ) ); + if( aExt.equalsIgnoreAsciiCase( "afm" ) ) + { + ::std::list< PrintFont* > aNewFonts; + + analyzeFontFile( nDirID, aFileName, aEmptyFontsDir, aNewFonts ); + for( ::std::list< PrintFont* >::iterator it = aNewFonts.begin(); it != aNewFonts.end(); ++it ) + { + if( findFontBuiltinID( (*it)->m_nPSName ) == 0 ) + { + m_aFontFileToFontID[ aFileName ].insert( m_nNextFontID ); + m_aFonts[ m_nNextFontID++ ] = *it; + m_pFontCache->updateFontCacheEntry( *it, false ); +#if OSL_DEBUG_LEVEL > 2 + nBuiltinFonts++; +#endif + } + else + delete *it; + } + } + } + } + closedir( pDIR ); + if( ! nDirFonts ) + m_pFontCache->markEmptyDir( nDirID ); + } + } + +#if OSL_DEBUG_LEVEL > 1 + aStep2 = times( &tms ); +#endif + + // part three - fill in family styles + ::boost::unordered_map< fontID, PrintFont* >::iterator font_it; + for (font_it = m_aFonts.begin(); font_it != m_aFonts.end(); ++font_it) + { + ::boost::unordered_map< int, family::type >::const_iterator it = + m_aFamilyTypes.find( font_it->second->m_nFamilyName ); + if (it != m_aFamilyTypes.end()) + continue; + const ::rtl::OUString& rFamily = + m_pAtoms->getString( ATOM_FAMILYNAME, font_it->second->m_nFamilyName); + family::type eType = matchFamilyName( rFamily ); + m_aFamilyTypes[ font_it->second->m_nFamilyName ] = eType; + } + +#if OSL_DEBUG_LEVEL > 1 + aStep3 = times( &tms ); + fprintf( stderr, "PrintFontManager::initialize: collected %" SAL_PRI_SIZET "u fonts (%d builtin, %d cached)\n", m_aFonts.size(), nBuiltinFonts, nCached ); + double fTick = (double)sysconf( _SC_CLK_TCK ); + fprintf( stderr, "Step 1 took %lf seconds\n", (double)(aStep1 - aStart)/fTick ); + fprintf( stderr, "Step 2 took %lf seconds\n", (double)(aStep2 - aStep1)/fTick ); + fprintf( stderr, "Step 3 took %lf seconds\n", (double)(aStep3 - aStep2)/fTick ); +#endif + + m_pFontCache->flush(); + + #ifdef CALLGRIND_COMPILE + CALLGRIND_DUMP_STATS(); + CALLGRIND_TOGGLE_COLLECT(); + #endif +} + +// ------------------------------------------------------------------------- +inline bool +equalPitch (psp::pitch::type from, psp::pitch::type to) +{ + return from == to; +} + +inline bool +equalWeight (psp::weight::type from, psp::weight::type to) +{ + return from > to ? (from - to) <= 3 : (to - from) <= 3; +} + +inline bool +equalItalic (psp::italic::type from, psp::italic::type to) +{ + if ( (from == psp::italic::Italic) || (from == psp::italic::Oblique) ) + return (to == psp::italic::Italic) || (to == psp::italic::Oblique); + return to == from; +} +inline bool +equalEncoding (rtl_TextEncoding from, rtl_TextEncoding to) +{ + if ((from == RTL_TEXTENCODING_ISO_8859_1) || (from == RTL_TEXTENCODING_MS_1252)) + return (to == RTL_TEXTENCODING_ISO_8859_1) || (to == RTL_TEXTENCODING_MS_1252); + return from == to; +} + +namespace { + struct BuiltinFontIdentifier + { + OUString aFamily; + italic::type eItalic; + weight::type eWeight; + pitch::type ePitch; + rtl_TextEncoding aEncoding; + + BuiltinFontIdentifier( const OUString& rFam, + italic::type eIt, + weight::type eWg, + pitch::type ePt, + rtl_TextEncoding enc ) : + aFamily( rFam ), + eItalic( eIt ), + eWeight( eWg ), + ePitch( ePt ), + aEncoding( enc ) + {} + + bool operator==( const BuiltinFontIdentifier& rRight ) const + { + return equalItalic( eItalic, rRight.eItalic ) && + equalWeight( eWeight, rRight.eWeight ) && + equalPitch( ePitch, rRight.ePitch ) && + equalEncoding( aEncoding, rRight.aEncoding ) && + aFamily.equalsIgnoreAsciiCase( rRight.aFamily ); + } + }; + + struct BuiltinFontIdentifierHash + { + size_t operator()( const BuiltinFontIdentifier& rFont ) const + { + return rFont.aFamily.hashCode() ^ rFont.eItalic ^ rFont.eWeight ^ rFont.ePitch ^ rFont.aEncoding; + } + }; +} + +void PrintFontManager::getFontList( ::std::list< fontID >& rFontIDs, const PPDParser* pParser, bool bUseOverrideMetrics ) +{ + rFontIDs.clear(); + boost::unordered_map< fontID, PrintFont* >::const_iterator it; + + /* + * Note: there are two easy steps making this faster: + * first: insert the printer builtins first, then the not builtins, + * if they do not match. + * drawback: this would change the sequence of fonts; this could have + * subtle, unknown consequences in vcl font matching + * second: instead of comparing attributes to see whether a softfont + * is duplicate to a builtin one could simply compare the PSName (which is + * supposed to be unique), which at this point is just an int. + * drawback: this could change which fonts are listed; especially TrueType + * fonts often have a rather dubious PSName, so this could change the + * font list not so subtle. + * Until getFontList for a printer becomes a performance issue (which is + * currently not the case), best stay with the current algorithm. + */ + + // fill sets of printer supported fonts + if( pParser ) + { + std::set<int> aBuiltinPSNames; + boost::unordered_set< BuiltinFontIdentifier, + BuiltinFontIdentifierHash + > aBuiltinFonts; + + std::map<int, fontID > aOverridePSNames; + if( bUseOverrideMetrics ) + { + readOverrideMetrics(); + for( std::vector<fontID>::const_iterator over = m_aOverrideFonts.begin(); + over != m_aOverrideFonts.end(); ++over ) + { + boost::unordered_map<fontID,PrintFont*>::const_iterator font_it = m_aFonts.find( *over ); + DBG_ASSERT( font_it != m_aFonts.end(), "override to nonexistant font" ); + if( font_it != m_aFonts.end() ) + aOverridePSNames[ font_it->second->m_nPSName ] = *over; + } + } + + int nFonts = pParser->getFonts(); + for( int i = 0; i < nFonts; i++ ) + aBuiltinPSNames.insert( m_pAtoms->getAtom( ATOM_PSNAME, pParser->getFont( i ) ) ); + for( it = m_aFonts.begin(); it != m_aFonts.end(); ++it ) + { + PrintFont* pFont = it->second; + if( it->second->m_eType == fonttype::Builtin && + aBuiltinPSNames.find( pFont->m_nPSName ) != aBuiltinPSNames.end() ) + { + bool bInsert = true; + if( bUseOverrideMetrics ) + { + // in override case only use the override fonts, not their counterparts + std::map<int,fontID>::const_iterator over = aOverridePSNames.find( pFont->m_nPSName ); + if( over != aOverridePSNames.end() && over->second != it->first ) + bInsert = false; + } + else + { + // do not insert override fonts in non override case + if( std::find( m_aOverrideFonts.begin(), m_aOverrideFonts.end(), it->first ) != m_aOverrideFonts.end() ) + bInsert = false; + } + if( bInsert ) + { + aBuiltinFonts.insert( BuiltinFontIdentifier( + m_pAtoms->getString( ATOM_FAMILYNAME, pFont->m_nFamilyName ), + pFont->m_eItalic, + pFont->m_eWeight, + pFont->m_ePitch, + pFont->m_aEncoding + ) ); + } + } + } + for( it = m_aFonts.begin(); it != m_aFonts.end(); ++it ) + { + PrintFont* pFont = it->second; + if( it->second->m_eType == fonttype::Builtin ) + { + if( aBuiltinPSNames.find( pFont->m_nPSName ) != aBuiltinPSNames.end() ) + { + bool bInsert = true; + if( bUseOverrideMetrics ) + { + // in override case only use the override fonts, not their counterparts + std::map<int,fontID>::const_iterator over = aOverridePSNames.find( pFont->m_nPSName ); + if( over != aOverridePSNames.end() && over->second != it->first ) + bInsert = false; + } + else + { + // do not insert override fonts in non override case + if( std::find( m_aOverrideFonts.begin(), m_aOverrideFonts.end(), it->first ) != m_aOverrideFonts.end() ) + bInsert = false; + } + if( bInsert ) + rFontIDs.push_back( it->first ); + } + } + else if( aBuiltinFonts.find( BuiltinFontIdentifier( + m_pAtoms->getString( ATOM_FAMILYNAME, pFont->m_nFamilyName ), + pFont->m_eItalic, + pFont->m_eWeight, + pFont->m_ePitch, + pFont->m_aEncoding + ) ) == aBuiltinFonts.end() ) + { + rFontIDs.push_back( it->first ); + } + } + } + else // no specific printer + { + for( it = m_aFonts.begin(); it != m_aFonts.end(); ++it ) + rFontIDs.push_back( it->first ); + } +} + +// ------------------------------------------------------------------------- + +void PrintFontManager::fillPrintFontInfo( PrintFont* pFont, FastPrintFontInfo& rInfo ) const +{ + ::boost::unordered_map< int, family::type >::const_iterator style_it = + m_aFamilyTypes.find( pFont->m_nFamilyName ); + rInfo.m_eType = pFont->m_eType; + rInfo.m_aFamilyName = m_pAtoms->getString( ATOM_FAMILYNAME, pFont->m_nFamilyName ); + rInfo.m_aStyleName = pFont->m_aStyleName; + rInfo.m_eFamilyStyle = style_it != m_aFamilyTypes.end() ? style_it->second : family::Unknown; + rInfo.m_eItalic = pFont->m_eItalic; + rInfo.m_eWidth = pFont->m_eWidth; + rInfo.m_eWeight = pFont->m_eWeight; + rInfo.m_ePitch = pFont->m_ePitch; + rInfo.m_aEncoding = pFont->m_aEncoding; + + rInfo.m_bEmbeddable = (pFont->m_eType == fonttype::Type1); + rInfo.m_bSubsettable = (pFont->m_eType == fonttype::TrueType); // TODO: rename to SfntType + + rInfo.m_aAliases.clear(); + for( ::std::list< int >::iterator it = pFont->m_aAliases.begin(); it != pFont->m_aAliases.end(); ++it ) + rInfo.m_aAliases.push_back( m_pAtoms->getString( ATOM_FAMILYNAME, *it ) ); +} + +// ------------------------------------------------------------------------- + +void PrintFontManager::fillPrintFontInfo( PrintFont* pFont, PrintFontInfo& rInfo ) const +{ + if( ( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 ) || + ! pFont->m_pMetrics || pFont->m_pMetrics->isEmpty() + ) + { + // might be a truetype font not analyzed or type1 without metrics read + if( pFont->m_eType == fonttype::Type1 ) + pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, false ); + else if( pFont->m_eType == fonttype::TrueType ) + analyzeTrueTypeFile( pFont ); + } + + fillPrintFontInfo( pFont, static_cast< FastPrintFontInfo& >( rInfo ) ); + + rInfo.m_nAscend = pFont->m_nAscend; + rInfo.m_nDescend = pFont->m_nDescend; + rInfo.m_nLeading = pFont->m_nLeading; + rInfo.m_nWidth = pFont->m_aGlobalMetricX.width < pFont->m_aGlobalMetricY.width ? pFont->m_aGlobalMetricY.width : pFont->m_aGlobalMetricX.width; +} + +// ------------------------------------------------------------------------- + +void PrintFontManager::getFontListWithInfo( ::std::list< PrintFontInfo >& rFonts, const PPDParser* pParser, bool bUseOverrideMetrics ) +{ + rFonts.clear(); + ::std::list< fontID > aFontList; + getFontList( aFontList, pParser, bUseOverrideMetrics ); + + ::std::list< fontID >::iterator it; + for( it = aFontList.begin(); it != aFontList.end(); ++it ) + { + PrintFontInfo aInfo; + aInfo.m_nID = *it; + fillPrintFontInfo( getFont( *it ), aInfo ); + rFonts.push_back( aInfo ); + } +} + +// ------------------------------------------------------------------------- + +void PrintFontManager::getFontListWithFastInfo( ::std::list< FastPrintFontInfo >& rFonts, const PPDParser* pParser, bool bUseOverrideMetrics ) +{ + rFonts.clear(); + ::std::list< fontID > aFontList; + getFontList( aFontList, pParser, bUseOverrideMetrics ); + + ::std::list< fontID >::iterator it; + for( it = aFontList.begin(); it != aFontList.end(); ++it ) + { + FastPrintFontInfo aInfo; + aInfo.m_nID = *it; + fillPrintFontInfo( getFont( *it ), aInfo ); + rFonts.push_back( aInfo ); + } +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::getFontInfo( fontID nFontID, PrintFontInfo& rInfo ) const +{ + PrintFont* pFont = getFont( nFontID ); + if( pFont ) + { + rInfo.m_nID = nFontID; + fillPrintFontInfo( pFont, rInfo ); + } + return pFont ? true : false; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::getFontFastInfo( fontID nFontID, FastPrintFontInfo& rInfo ) const +{ + PrintFont* pFont = getFont( nFontID ); + if( pFont ) + { + rInfo.m_nID = nFontID; + fillPrintFontInfo( pFont, rInfo ); + } + return pFont ? true : false; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::getFontBoundingBox( fontID nFontID, int& xMin, int& yMin, int& xMax, int& yMax ) +{ + bool bSuccess = false; + PrintFont* pFont = getFont( nFontID ); + if( pFont ) + { + if( pFont->m_nXMin == 0 && pFont->m_nYMin == 0 && pFont->m_nXMax == 0 && pFont->m_nYMax == 0 ) + { + // might be a truetype font not analyzed or type1 without metrics read + if( pFont->m_eType == fonttype::Type1 || pFont->m_eType == fonttype::Builtin ) + pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, true ); + else if( pFont->m_eType == fonttype::TrueType ) + analyzeTrueTypeFile( pFont ); + } + bSuccess = true; + xMin = pFont->m_nXMin; + yMin = pFont->m_nYMin; + xMax = pFont->m_nXMax; + yMax = pFont->m_nYMax; + } + return bSuccess; +} + +// ------------------------------------------------------------------------- + +int PrintFontManager::getFontFaceNumber( fontID nFontID ) const +{ + int nRet = -1; + PrintFont* pFont = getFont( nFontID ); + if( pFont && pFont->m_eType == fonttype::TrueType ) + nRet = static_cast< TrueTypeFontFile* >(pFont)->m_nCollectionEntry; + return nRet; +} + +// ------------------------------------------------------------------------- + + +family::type PrintFontManager::matchFamilyName( const ::rtl::OUString& rFamily ) const +{ + typedef struct { + const char* mpName; + sal_uInt16 mnLength; + family::type meType; + } family_t; + +#define InitializeClass( p, a ) p, sizeof(p) - 1, a + const family_t pFamilyMatch[] = { + { InitializeClass( "arial", family::Swiss ) }, + { InitializeClass( "arioso", family::Script ) }, + { InitializeClass( "avant garde", family::Swiss ) }, + { InitializeClass( "avantgarde", family::Swiss ) }, + { InitializeClass( "bembo", family::Roman ) }, + { InitializeClass( "bookman", family::Roman ) }, + { InitializeClass( "conga", family::Roman ) }, + { InitializeClass( "courier", family::Modern ) }, + { InitializeClass( "curl", family::Script ) }, + { InitializeClass( "fixed", family::Modern ) }, + { InitializeClass( "gill", family::Swiss ) }, + { InitializeClass( "helmet", family::Modern ) }, + { InitializeClass( "helvetica", family::Swiss ) }, + { InitializeClass( "international", family::Modern ) }, + { InitializeClass( "lucida", family::Swiss ) }, + { InitializeClass( "new century schoolbook", family::Roman ) }, + { InitializeClass( "palatino", family::Roman ) }, + { InitializeClass( "roman", family::Roman ) }, + { InitializeClass( "sans serif", family::Swiss ) }, + { InitializeClass( "sansserif", family::Swiss ) }, + { InitializeClass( "serf", family::Roman ) }, + { InitializeClass( "serif", family::Roman ) }, + { InitializeClass( "times", family::Roman ) }, + { InitializeClass( "utopia", family::Roman ) }, + { InitializeClass( "zapf chancery", family::Script ) }, + { InitializeClass( "zapfchancery", family::Script ) } + }; + + rtl::OString aFamily = rtl::OUStringToOString( rFamily, RTL_TEXTENCODING_ASCII_US ); + sal_uInt32 nLower = 0; + sal_uInt32 nUpper = SAL_N_ELEMENTS(pFamilyMatch); + + while( nLower < nUpper ) + { + sal_uInt32 nCurrent = (nLower + nUpper) / 2; + const family_t* pHaystack = pFamilyMatch + nCurrent; + sal_Int32 nComparison = + rtl_str_compareIgnoreAsciiCase_WithLength + ( + aFamily.getStr(), aFamily.getLength(), + pHaystack->mpName, pHaystack->mnLength + ); + + if( nComparison < 0 ) + nUpper = nCurrent; + else + if( nComparison > 0 ) + nLower = nCurrent + 1; + else + return pHaystack->meType; + } + + return family::Unknown; +} + +// ------------------------------------------------------------------------- + +family::type PrintFontManager::getFontFamilyType( fontID nFontID ) const +{ + PrintFont* pFont = getFont( nFontID ); + if( !pFont ) + return family::Unknown; + + ::boost::unordered_map< int, family::type >::const_iterator it = + m_aFamilyTypes.find( pFont->m_nFamilyName ); + return (it != m_aFamilyTypes.end()) ? it->second : family::Unknown; +} + + +// ------------------------------------------------------------------------- + +const ::rtl::OUString& PrintFontManager::getFontFamily( fontID nFontID ) const +{ + PrintFont* pFont = getFont( nFontID ); + return m_pAtoms->getString( ATOM_FAMILYNAME, pFont ? pFont->m_nFamilyName : INVALID_ATOM ); +} + +// ------------------------------------------------------------------------- + +OString PrintFontManager::getAfmFile( PrintFont* pFont ) const +{ + OString aMetricPath; + if( pFont ) + { + switch( pFont->m_eType ) + { + case fonttype::Type1: + { + Type1FontFile* pPSFont = static_cast< Type1FontFile* >(pFont); + aMetricPath = getDirectory( pPSFont->m_nDirectory ); + aMetricPath += "/"; + aMetricPath += pPSFont->m_aMetricFile; + } + break; + case fonttype::Builtin: + { + BuiltinFont* pBuiltinFont = static_cast< BuiltinFont* >(pFont); + aMetricPath = getDirectory( pBuiltinFont->m_nDirectory ); + aMetricPath += "/"; + aMetricPath += pBuiltinFont->m_aMetricFile; + } + break; + default: break; + } + } + return aMetricPath; +} + +// ------------------------------------------------------------------------- + +OString PrintFontManager::getFontFile( PrintFont* pFont ) const +{ + OString aPath; + + if( pFont && pFont->m_eType == fonttype::Type1 ) + { + Type1FontFile* pPSFont = static_cast< Type1FontFile* >(pFont); + ::boost::unordered_map< int, OString >::const_iterator it = m_aAtomToDir.find( pPSFont->m_nDirectory ); + aPath = it->second; + aPath += "/"; + aPath += pPSFont->m_aFontFile; + } + else if( pFont && pFont->m_eType == fonttype::TrueType ) + { + TrueTypeFontFile* pTTFont = static_cast< TrueTypeFontFile* >(pFont); + ::boost::unordered_map< int, OString >::const_iterator it = m_aAtomToDir.find( pTTFont->m_nDirectory ); + aPath = it->second; + aPath += "/"; + aPath += pTTFont->m_aFontFile; + } + return aPath; +} + +// ------------------------------------------------------------------------- + +const ::rtl::OUString& PrintFontManager::getPSName( fontID nFontID ) const +{ + PrintFont* pFont = getFont( nFontID ); + if( pFont && pFont->m_nPSName == 0 ) + { + if( pFont->m_eType == fonttype::TrueType ) + analyzeTrueTypeFile( pFont ); + } + + return m_pAtoms->getString( ATOM_PSNAME, pFont ? pFont->m_nPSName : INVALID_ATOM ); +} + +// ------------------------------------------------------------------------- + +const CharacterMetric& PrintFontManager::getGlobalFontMetric( fontID nFontID, bool bHorizontal ) const +{ + static CharacterMetric aMetric; + PrintFont* pFont = getFont( nFontID ); + return pFont ? ( bHorizontal ? pFont->m_aGlobalMetricX : pFont->m_aGlobalMetricY ) : aMetric; +} + +// ------------------------------------------------------------------------- + +int PrintFontManager::getFontAscend( fontID nFontID ) const +{ + PrintFont* pFont = getFont( nFontID ); + if( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 ) + { + // might be a truetype font not yet analyzed + if( pFont->m_eType == fonttype::TrueType ) + analyzeTrueTypeFile( pFont ); + else if( pFont->m_eType == fonttype::Type1 || pFont->m_eType == fonttype::Builtin ) + pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, true ); + } + return pFont->m_nAscend; +} + +// ------------------------------------------------------------------------- + +int PrintFontManager::getFontDescend( fontID nFontID ) const +{ + PrintFont* pFont = getFont( nFontID ); + if( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 ) + { + // might be a truetype font not yet analyzed + if( pFont->m_eType == fonttype::TrueType ) + analyzeTrueTypeFile( pFont ); + else if( pFont->m_eType == fonttype::Type1 || pFont->m_eType == fonttype::Builtin ) + pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, true ); + } + return pFont->m_nDescend; +} + +// ------------------------------------------------------------------------- + +int PrintFontManager::getFontLeading( fontID nFontID ) const +{ + PrintFont* pFont = getFont( nFontID ); + if( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 ) + { + // might be a truetype font not yet analyzed + if( pFont->m_eType == fonttype::TrueType ) + analyzeTrueTypeFile( pFont ); + } + return pFont->m_nLeading; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::hasVerticalSubstitutions( fontID nFontID ) const +{ + PrintFont* pFont = getFont( nFontID ); + if( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 ) + { + // might be a truetype font not yet analyzed + if( pFont->m_eType == fonttype::TrueType ) + analyzeTrueTypeFile( pFont ); + } + return pFont->m_bHaveVerticalSubstitutedGlyphs; +} + +// ------------------------------------------------------------------------- + +void PrintFontManager::hasVerticalSubstitutions( fontID nFontID, + const sal_Unicode* pCharacters, int nCharacters, bool* pHasSubst ) const +{ + PrintFont* pFont = getFont( nFontID ); + if( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 ) + { + // might be a truetype font not yet analyzed + if( pFont->m_eType == fonttype::TrueType ) + analyzeTrueTypeFile( pFont ); + } + + if( ! pFont->m_bHaveVerticalSubstitutedGlyphs ) + memset( pHasSubst, 0, sizeof(bool)*nCharacters ); + else + { + for( int i = 0; i < nCharacters; i++ ) + { + sal_Unicode code = pCharacters[i]; + if( ! pFont->m_pMetrics || + ! ( pFont->m_pMetrics->m_aPages[ code >> 11 ] & ( 1 << ( ( code >> 8 ) & 7 ) ) ) ) + pFont->queryMetricPage( code >> 8, m_pAtoms ); + ::boost::unordered_map< sal_Unicode, bool >::const_iterator it = pFont->m_pMetrics->m_bVerticalSubstitutions.find( code ); + pHasSubst[i] = it != pFont->m_pMetrics->m_bVerticalSubstitutions.end(); + } + } +} + +// ------------------------------------------------------------------------- + +OUString PrintFontManager::getFontXLFD( fontID nFontID ) const +{ + PrintFont* pFont = getFont( nFontID ); + OUString aRet; + if( pFont ) + { + ByteString aXLFD( getXLFD( pFont ) ); + rtl_TextEncoding aEncoding = aXLFD.GetToken( 6, '-' ).Search( "utf8" ) != STRING_NOTFOUND ? RTL_TEXTENCODING_UTF8 : RTL_TEXTENCODING_ISO_8859_1; + aRet = OStringToOUString( aXLFD, aEncoding ); + } + return aRet; +} + +// ------------------------------------------------------------------------- + +const ::std::list< KernPair >& PrintFontManager::getKernPairs( fontID nFontID, bool bVertical ) const +{ + static ::std::list< KernPair > aEmpty; + + PrintFont* pFont = getFont( nFontID ); + if( ! pFont ) + return aEmpty; + + if( ! pFont->m_pMetrics || ! pFont->m_pMetrics->m_bKernPairsQueried ) + pFont->queryMetricPage( 0, m_pAtoms ); + if( ! pFont->m_pMetrics || ! pFont->m_pMetrics->m_bKernPairsQueried ) + return aEmpty; + return bVertical ? pFont->m_pMetrics->m_aYKernPairs : pFont->m_pMetrics->m_aXKernPairs; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::isFontDownloadingAllowed( fontID nFont ) const +{ + static const char* pEnable = getenv( "PSPRINT_ENABLE_TTF_COPYRIGHTAWARENESS" ); + bool bRet = true; + + if( pEnable && *pEnable ) + { + PrintFont* pFont = getFont( nFont ); + if( pFont && pFont->m_eType == fonttype::TrueType ) + { + TrueTypeFontFile* pTTFontFile = static_cast<TrueTypeFontFile*>(pFont); + if( pTTFontFile->m_nTypeFlags & TYPEFLAG_INVALID ) + { + TrueTypeFont* pTTFont = NULL; + ByteString aFile = getFontFile( pFont ); + if( OpenTTFontFile( aFile.GetBuffer(), pTTFontFile->m_nCollectionEntry < 0 ? 0 : pTTFontFile->m_nCollectionEntry, &pTTFont ) == SF_OK ) + { + // get type flags + TTGlobalFontInfo aInfo; + GetTTGlobalFontInfo( pTTFont, & aInfo ); + pTTFontFile->m_nTypeFlags = (unsigned int)aInfo.typeFlags; + CloseTTFont( pTTFont ); + } + } + + unsigned int nCopyrightFlags = pTTFontFile->m_nTypeFlags & TYPEFLAG_COPYRIGHT_MASK; + + // font embedding is allowed if either + // no restriction at all (bit 1 clear) + // printing allowed (bit 1 set, bit 2 set ) + bRet = ! ( nCopyrightFlags & 0x02 ) || ( nCopyrightFlags & 0x04 ); + } + } + return bRet; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::getMetrics( fontID nFontID, const sal_Unicode* pString, int nLen, CharacterMetric* pArray, bool bVertical ) const +{ + PrintFont* pFont = getFont( nFontID ); + if( ! pFont ) + return false; + + if( ( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 ) + || ! pFont->m_pMetrics || pFont->m_pMetrics->isEmpty() + ) + { + // might be a font not yet analyzed + if( pFont->m_eType == fonttype::Type1 || pFont->m_eType == fonttype::Builtin ) + pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, false ); + else if( pFont->m_eType == fonttype::TrueType ) + analyzeTrueTypeFile( pFont ); + } + + for( int i = 0; i < nLen; i++ ) + { + if( ! pFont->m_pMetrics || + ! ( pFont->m_pMetrics->m_aPages[ pString[i] >> 11 ] & ( 1 << ( ( pString[i] >> 8 ) & 7 ) ) ) ) + pFont->queryMetricPage( pString[i] >> 8, m_pAtoms ); + pArray[i].width = pArray[i].height = -1; + if( pFont->m_pMetrics ) + { + int effectiveCode = pString[i]; + effectiveCode |= bVertical ? 1 << 16 : 0; + ::boost::unordered_map< int, CharacterMetric >::const_iterator it = + pFont->m_pMetrics->m_aMetrics.find( effectiveCode ); + // if no vertical metrics are available assume rotated horizontal metrics + if( bVertical && (it == pFont->m_pMetrics->m_aMetrics.end()) ) + it = pFont->m_pMetrics->m_aMetrics.find( pString[i] ); + // the character metrics are in it->second + if( it != pFont->m_pMetrics->m_aMetrics.end() ) + pArray[ i ] = it->second; + } + } + + return true; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::getMetrics( fontID nFontID, sal_Unicode minCharacter, sal_Unicode maxCharacter, CharacterMetric* pArray, bool bVertical ) const +{ + PrintFont* pFont = getFont( nFontID ); + if( ! pFont ) + return false; + + if( ( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 ) + || ! pFont->m_pMetrics || pFont->m_pMetrics->isEmpty() + ) + { + // might be a font not yet analyzed + if( pFont->m_eType == fonttype::Type1 || pFont->m_eType == fonttype::Builtin ) + pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, false ); + else if( pFont->m_eType == fonttype::TrueType ) + analyzeTrueTypeFile( pFont ); + } + + sal_Unicode code = minCharacter; + do + { + if( ! pFont->m_pMetrics || + ! ( pFont->m_pMetrics->m_aPages[ code >> 11 ] & ( 1 << ( ( code >> 8 ) & 7 ) ) ) ) + pFont->queryMetricPage( code >> 8, m_pAtoms ); + pArray[ code - minCharacter ].width = -1; + pArray[ code - minCharacter ].height = -1; + if( pFont->m_pMetrics ) + { + int effectiveCode = code; + effectiveCode |= bVertical ? 1 << 16 : 0; + ::boost::unordered_map< int, CharacterMetric >::const_iterator it = + pFont->m_pMetrics->m_aMetrics.find( effectiveCode ); + // if no vertical metrics are available assume rotated horizontal metrics + if( bVertical && (it == pFont->m_pMetrics->m_aMetrics.end()) ) + it = pFont->m_pMetrics->m_aMetrics.find( code ); + // the character metrics are in it->second + if( it != pFont->m_pMetrics->m_aMetrics.end() ) + pArray[ code - minCharacter ] = it->second; + } + } while( code++ != maxCharacter ); + + return true; +} + +// ------------------------------------------------------------------------- + +static bool createWriteablePath( const ByteString& rPath ) +{ + bool bSuccess = false; + + if( access( rPath.GetBuffer(), W_OK ) ) + { + int nPos = rPath.SearchBackward( '/' ); + if( nPos != STRING_NOTFOUND ) + while( nPos > 0 && rPath.GetChar( nPos ) == '/' ) + nPos--; + + if( nPos != STRING_NOTFOUND && nPos != 0 && createWriteablePath( rPath.Copy( 0, nPos+1 ) ) ) + { + bSuccess = mkdir( rPath.GetBuffer(), 0777 ) ? false : true; + } + } + else + bSuccess = true; + + return bSuccess; +} + + +// ------------------------------------------------------------------------- + +int PrintFontManager::importFonts( const ::std::list< OString >& rFiles, bool bLinkOnly, ImportFontCallback* pCallback ) +{ + int nSuccess = 0; + + // find a directory with write access + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + bool bCanWrite = false; + int nDirID = 0; + INetURLObject aDir; + for( ::std::list< int >::const_iterator dir_it = m_aPrivateFontDirectories.begin(); + ! bCanWrite && dir_it != m_aPrivateFontDirectories.end(); ++dir_it ) + { + // check if we can create files in that directory + ByteString aDirPath = getDirectory( *dir_it ); + if( createWriteablePath( aDirPath ) ) + { + aDir = INetURLObject( OStringToOUString( aDirPath, aEncoding ), INET_PROT_FILE, INetURLObject::ENCODE_ALL ); + nDirID = *dir_it; + bCanWrite = true; + } + } + if( bCanWrite ) + { + for( ::std::list< OString >::const_iterator font_it = rFiles.begin(); + font_it != rFiles.end(); ++font_it ) + { + INetURLObject aFrom( OStringToOUString( *font_it, aEncoding ), INET_PROT_FILE, INetURLObject::ENCODE_ALL ); + INetURLObject aTo( aDir ); + aTo.Append( aFrom.GetName() ); + + if( pCallback ) + pCallback->progress( aTo.PathToFileName() ); + + if( pCallback && pCallback->isCanceled() ) + break; + + if( ! access( ByteString( String(aTo.PathToFileName()), aEncoding ).GetBuffer(), F_OK ) ) + { + if( ! ( pCallback ? pCallback->queryOverwriteFile( aTo.PathToFileName() ) : false ) ) + continue; + } + // look for afm if necessary + OUString aAfmCopied; + FileBase::RC nError; + if( aFrom.getExtension().equalsIgnoreAsciiCaseAscii( "pfa" ) || + aFrom.getExtension().equalsIgnoreAsciiCaseAscii( "pfb" ) ) + { + INetURLObject aFromAfm( aFrom ); + aFromAfm.setExtension( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "afm" ) ) ); + if( access( ByteString( String(aFromAfm.PathToFileName()), aEncoding ).GetBuffer(), F_OK ) ) + { + aFromAfm.setExtension( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "AFM" ) ) ); + if( access( ByteString( String(aFromAfm.PathToFileName()), aEncoding ).GetBuffer(), F_OK ) ) + { + aFromAfm.removeSegment(); + aFromAfm.Append( String( RTL_CONSTASCII_USTRINGPARAM( "afm" ) ) ); + aFromAfm.Append( aTo.GetName() ); + aFromAfm.setExtension( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "afm" ) ) ); + if( access( ByteString( String(aFromAfm.PathToFileName()), aEncoding ).GetBuffer(), F_OK ) ) + { + aFromAfm.setExtension( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "AFM" ) ) ); + if( access( ByteString( String(aFromAfm.PathToFileName()), aEncoding ).GetBuffer(), F_OK ) ) + { + // give up + if( pCallback ) + pCallback->importFontFailed( aTo.PathToFileName(), ImportFontCallback::NoAfmMetric ); + continue; + } + } + } + } + INetURLObject aToAfm( aTo ); + aToAfm.setExtension( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "afm" ) ) ); + OUString aFromPath, aToPath; + if( bLinkOnly ) + { + ByteString aLinkFromPath( String(aFromAfm.PathToFileName()), + aEncoding ); + ByteString aLinkToPath( String(aToAfm.PathToFileName()), + aEncoding ); + nError = (FileBase::RC)symlink( aLinkFromPath.GetBuffer(), aLinkToPath.GetBuffer() ); + } + else + nError = File::copy( aFromAfm.GetMainURL(INetURLObject::DECODE_TO_IURI), aToAfm.GetMainURL(INetURLObject::DECODE_TO_IURI) ); + if( nError ) + { + if( pCallback ) + pCallback->importFontFailed( aTo.PathToFileName(), ImportFontCallback::AfmCopyFailed ); + continue; + } + aAfmCopied = aToPath; + } + if( bLinkOnly ) + { + ByteString aFromPath( String(aFrom.PathToFileName()), + aEncoding ); + ByteString aToPath( String(aTo.PathToFileName()), aEncoding ); + nError = (FileBase::RC)symlink( aFromPath.GetBuffer(), + aToPath.GetBuffer() ); + } + else + nError = File::copy( aFrom.GetMainURL(INetURLObject::DECODE_TO_IURI), aTo.GetMainURL(INetURLObject::DECODE_TO_IURI) ); + // copy font file + if( nError ) + { + if( aAfmCopied.getLength() ) + File::remove( aAfmCopied ); + if( pCallback ) + pCallback->importFontFailed( aTo.PathToFileName(), ImportFontCallback::FontCopyFailed ); + continue; + } + + ::std::list< PrintFont* > aNewFonts; + ::std::list< PrintFont* >::iterator it; + if( analyzeFontFile( nDirID, OUStringToOString( aTo.GetName(), aEncoding ), ::std::list<OString>(), aNewFonts ) ) + { + // remove all fonts for the same file + // discarding their font ids + ::boost::unordered_map< fontID, PrintFont* >::iterator current, next; + current = m_aFonts.begin(); + OString aFileName( OUStringToOString( aTo.GetName(), aEncoding ) ); + while( current != m_aFonts.end() ) + { + bool bRemove = false; + switch( current->second->m_eType ) + { + case fonttype::Type1: + if( static_cast<Type1FontFile*>(current->second)->m_aFontFile == aFileName ) + bRemove = true; + break; + case fonttype::TrueType: + if( static_cast<TrueTypeFontFile*>(current->second)->m_aFontFile == aFileName ) + bRemove = true; + break; + default: break; + } + if( bRemove ) + { + next = current; + ++next; + m_aFontFileToFontID[ aFileName ].erase( current->first ); + delete current->second; + m_aFonts.erase( current ); + current = next; + } + else + ++current; + } + + DBG_ASSERT( !findFontFileID( nDirID, aFileName ), "not all fonts removed for file" ); + + nSuccess++; + for( it = aNewFonts.begin(); it != aNewFonts.end(); ++it ) + { + m_aFontFileToFontID[ aFileName ].insert( m_nNextFontID ); + m_aFonts[ m_nNextFontID++ ] = *it; + m_pFontCache->updateFontCacheEntry( *it, false ); + } + } + } + + m_pFontCache->updateDirTimestamp( nDirID ); + m_pFontCache->flush(); + } + else if( pCallback ) + pCallback->importFontsFailed( ImportFontCallback::NoWritableDirectory ); + + return nSuccess; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::checkImportPossible() const +{ + bool bSuccess = false; + + // find a directory with write access + ByteString aDir; + for( std::list< int >::const_iterator dir_it = m_aPrivateFontDirectories.begin(); + dir_it != m_aPrivateFontDirectories.end(); ++dir_it ) + { + aDir = getDirectory( *dir_it ); + if( createWriteablePath( aDir ) ) + { + bSuccess = true; + break; + } + } + +#if OSL_DEBUG_LEVEL > 1 + if( bSuccess ) + fprintf( stderr, "found writable %s\n", aDir.GetBuffer() ); +#endif + + return bSuccess; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::checkChangeFontPropertiesPossible( fontID /*nFontID*/ ) const +{ + // since font properties are changed in the font cache file only nowadays + // they can always be changed + return true; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::changeFontProperties( fontID nFontID, const ::rtl::OUString& rXLFD ) +{ + ByteString aXLFD( OUStringToOString( rXLFD, RTL_TEXTENCODING_UTF8 ) ); + ByteString aAddStyle = aXLFD.GetToken( '-', 6 ); + if( aAddStyle.Search( "utf8" ) == STRING_NOTFOUND ) + { + aAddStyle.Append( aAddStyle.Len() ? ";utf8" : "utf8" ); + aXLFD.SetToken( 6, ';', aAddStyle ); + } + PrintFont* pFont = getFont( nFontID ); + std::list< OString > aDummyList; + aDummyList.push_back( aXLFD ); + getFontAttributesFromXLFD( pFont, aDummyList ); + pFont->m_bUserOverride = true; + m_pFontCache->updateFontCacheEntry( pFont, true ); + + return true; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager:: +getImportableFontProperties( + const OString& rFile, + ::std::list< FastPrintFontInfo >& rFontProps + ) +{ + rFontProps.clear(); + int nIndex = rFile.lastIndexOf( '/' ); + OString aDir, aFile( rFile.copy( nIndex+1 ) ); + if( nIndex != -1 ) + aDir = rFile.copy( 0, nIndex ); + int nDirID = getDirectoryAtom( aDir, true ); + ::std::list< PrintFont* > aFonts; + bool bRet = analyzeFontFile( nDirID, aFile, ::std::list<OString>(), aFonts ); + while( aFonts.begin() != aFonts.end() ) + { + PrintFont* pFont = aFonts.front(); + aFonts.pop_front(); + FastPrintFontInfo aInfo; + fillPrintFontInfo( pFont, aInfo ); + rFontProps.push_back( aInfo ); + delete pFont; + } + return bRet; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::getFileDuplicates( fontID nFont, ::std::list< fontID >& rFonts ) const +{ + bool bRet = false; + + rFonts.clear(); + + PrintFont* pSearchFont = getFont( nFont ); + if( ! pSearchFont || + pSearchFont->m_eType != fonttype::TrueType || + static_cast<TrueTypeFontFile*>(pSearchFont)->m_nCollectionEntry == -1 + ) + return false; + + OString aFile( getFontFileSysPath( nFont ) ); + if( ! aFile.getLength() ) + return false; + + for( ::boost::unordered_map< fontID, PrintFont* >::const_iterator it = m_aFonts.begin(); it != m_aFonts.end(); ++it ) + { + if( nFont != it->first ) + { + OString aCompFile( getFontFile( it->second ) ); + if( aCompFile == aFile ) + { + rFonts.push_back( it->first ); + bRet = true; + } + } + } + return bRet; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::removeFonts( const ::std::list< fontID >& rFonts ) +{ + bool bRet = true; + ::std::list< fontID > aDuplicates; + for( ::std::list< fontID >::const_iterator it = rFonts.begin(); it != rFonts.end(); ++it ) + { + ::boost::unordered_map< fontID, PrintFont* >::const_iterator haveFont = m_aFonts.find( *it ); + if( haveFont == m_aFonts.end() ) + continue; + + PrintFont* pFont = haveFont->second; + bool bRemoveDuplicates = getFileDuplicates( *it, aDuplicates ); + ByteString aFile( getFontFile( pFont ) ); + if( aFile.Len() ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "try unlink( \"%s\" ) ... ", aFile.GetBuffer() ); +#endif + if( unlink( aFile.GetBuffer() ) ) + { + bRet = false; +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "failed\n" ); +#endif + continue; + } +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "succeeded\n" ); +#endif + OString aAfm( getAfmFile( pFont ) ); + if( aAfm.getLength() ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "unlink( \"%s\" )\n", aAfm.getStr() ); +#endif + unlink( aAfm.getStr() ); + } + m_aFonts.erase( *it ); + delete pFont; + if( bRemoveDuplicates ) + { + for( ::std::list< fontID >::iterator dup = aDuplicates.begin(); dup != aDuplicates.end(); ++dup ) + { + m_aFontFileToFontID[ aFile ].erase( *dup ); + PrintFont* pDup = m_aFonts[ *dup ]; + m_aFonts.erase( *dup ); + delete pDup; + } + } + } + } + return bRet; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::isPrivateFontFile( fontID nFont ) const +{ + bool bRet = false; + int nDirID = -1; + PrintFont* pFont = getFont( nFont ); + if( pFont ) + { + switch( pFont->m_eType ) + { + case fonttype::Type1: nDirID = static_cast< Type1FontFile* >(pFont)->m_nDirectory;break; + case fonttype::TrueType: nDirID = static_cast< TrueTypeFontFile* >(pFont)->m_nDirectory;break; + default: break; + } + } + if( nDirID != -1 ) + { + for( ::std::list< int >::const_iterator it = m_aPrivateFontDirectories.begin(); it != m_aPrivateFontDirectories.end(); ++it ) + { + if( nDirID == *it ) + { + bRet = true; + break; + } + } + } + return bRet; +} + +// ------------------------------------------------------------------------- + +bool PrintFontManager::getAlternativeFamilyNames( fontID nFont, ::std::list< OUString >& rNames ) const +{ + rNames.clear(); + + PrintFont* pFont = getFont( nFont ); + if( pFont && pFont->m_eType == fonttype::TrueType ) + { + TrueTypeFontFile* pTTFontFile = static_cast< TrueTypeFontFile* >(pFont); + ByteString aFile( getFontFile( pFont ) ); + TrueTypeFont* pTTFont; + if( OpenTTFontFile( aFile.GetBuffer(), pTTFontFile->m_nCollectionEntry < 0 ? 0 : pTTFontFile->m_nCollectionEntry, &pTTFont ) == SF_OK ) + { + NameRecord* pNameRecords = NULL; + int nNameRecords = GetTTNameRecords( pTTFont, &pNameRecords ); + for( int i = 0; i < nNameRecords; i++ ) + { + if( pNameRecords[i].nameID != 1 ) // family name + continue; + + OUString aFamily( convertTrueTypeName( pNameRecords+i ) ); + if( aFamily.getLength() + && + m_pAtoms->getAtom( ATOM_FAMILYNAME, aFamily, sal_True ) != pFont->m_nFamilyName + ) + { + rNames.push_back( aFamily ); + } + } + + if( nNameRecords ) + DisposeNameRecords( pNameRecords, nNameRecords ); + CloseTTFont( pTTFont ); + } + } + return rNames.begin() != rNames.end(); +} + +// ------------------------------------------------------------------------- + +// TODO: move most of this stuff into the central font-subsetting code +bool PrintFontManager::createFontSubset( + FontSubsetInfo& rInfo, + fontID nFont, + const OUString& rOutFile, + sal_Int32* pGlyphIDs, + sal_uInt8* pNewEncoding, + sal_Int32* pWidths, + int nGlyphs, + bool bVertical + ) +{ + PrintFont* pFont = getFont( nFont ); + if( !pFont ) + return false; + + switch( pFont->m_eType ) + { + case psp::fonttype::TrueType: rInfo.m_nFontType = FontSubsetInfo::SFNT_TTF; break; + case psp::fonttype::Type1: rInfo.m_nFontType = FontSubsetInfo::ANY_TYPE1; break; + default: + return false; + } + // TODO: remove when Type1 subsetting gets implemented + if( pFont->m_eType != fonttype::TrueType ) + return false; + + // reshuffle array of requested glyphs to make sure glyph0==notdef + sal_uInt8 pEnc[256]; + sal_uInt16 pGID[256]; + sal_uInt8 pOldIndex[256]; + memset( pEnc, 0, sizeof( pEnc ) ); + memset( pGID, 0, sizeof( pGID ) ); + memset( pOldIndex, 0, sizeof( pOldIndex ) ); + if( nGlyphs > 256 ) + return false; + int nChar = 1; + for( int i = 0; i < nGlyphs; i++ ) + { + if( pNewEncoding[i] == 0 ) + { + pOldIndex[ 0 ] = i; + } + else + { + DBG_ASSERT( !(pGlyphIDs[i] & 0x007f0000), "overlong glyph id" ); + DBG_ASSERT( (int)pNewEncoding[i] < nGlyphs, "encoding wrong" ); + DBG_ASSERT( pEnc[pNewEncoding[i]] == 0 && pGID[pNewEncoding[i]] == 0, "duplicate encoded glyph" ); + pEnc[ pNewEncoding[i] ] = pNewEncoding[i]; + pGID[ pNewEncoding[i] ] = (sal_uInt16)pGlyphIDs[ i ]; + pOldIndex[ pNewEncoding[i] ] = i; + nChar++; + } + } + nGlyphs = nChar; // either input value or increased by one + + // prepare system name for read access for subset source file + // TODO: since this file is usually already mmapped there is no need to open it again + const ByteString aFromFile = getFontFile( pFont ); + + TrueTypeFont* pTTFont = NULL; // TODO: rename to SfntFont + TrueTypeFontFile* pTTFontFile = static_cast< TrueTypeFontFile* >(pFont); + if( OpenTTFontFile( aFromFile.GetBuffer(), pTTFontFile->m_nCollectionEntry < 0 ? 0 : pTTFontFile->m_nCollectionEntry, &pTTFont ) != SF_OK ) + return false; + + // prepare system name for write access for subset file target + OUString aSysPath; + if( osl_File_E_None != osl_getSystemPathFromFileURL( rOutFile.pData, &aSysPath.pData ) ) + return false; + const rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + const ByteString aToFile( OUStringToOString( aSysPath, aEncoding ) ); + + // do CFF subsetting if possible + int nCffLength = 0; + const sal_uInt8* pCffBytes = NULL; + if( GetSfntTable( pTTFont, O_CFF, &pCffBytes, &nCffLength ) ) + { + rInfo.LoadFont( FontSubsetInfo::CFF_FONT, pCffBytes, nCffLength ); +#if 1 // TODO: remove 16bit->long conversion when related methods handle non-16bit glyphids + long aRequestedGlyphs[256]; + for( int i = 0; i < nGlyphs; ++i ) + aRequestedGlyphs[i] = pGID[i]; +#endif + // create subset file at requested path + FILE* pOutFile = fopen( aToFile.GetBuffer(), "wb" ); + // create font subset + const char* pGlyphSetName = NULL; // TODO: better name? + const bool bOK = rInfo.CreateFontSubset( + FontSubsetInfo::TYPE1_PFB, + pOutFile, pGlyphSetName, + aRequestedGlyphs, pEnc, nGlyphs, pWidths ); + fclose( pOutFile ); + // cleanup before early return + CloseTTFont( pTTFont ); + return bOK; + } + + // do TTF->Type42 or Type3 subsetting + // fill in font info + psp::PrintFontInfo aFontInfo; + if( ! getFontInfo( nFont, aFontInfo ) ) + return false; + + rInfo.m_nAscent = aFontInfo.m_nAscend; + rInfo.m_nDescent = aFontInfo.m_nDescend; + rInfo.m_aPSName = getPSName( nFont ); + + int xMin, yMin, xMax, yMax; + getFontBoundingBox( nFont, xMin, yMin, xMax, yMax ); + rInfo.m_aFontBBox = Rectangle( Point( xMin, yMin ), Size( xMax-xMin, yMax-yMin ) ); + rInfo.m_nCapHeight = yMax; // Well ... + + // fill in glyph advance widths + TTSimpleGlyphMetrics* pMetrics = GetTTSimpleGlyphMetrics( pTTFont, + pGID, + nGlyphs, + bVertical ? 1 : 0 ); + if( pMetrics ) + { + for( int i = 0; i < nGlyphs; i++ ) + pWidths[pOldIndex[i]] = pMetrics[i].adv; + free( pMetrics ); + } + else + { + CloseTTFont( pTTFont ); + return false; + } + + bool bSuccess = ( SF_OK == CreateTTFromTTGlyphs( pTTFont, + aToFile.GetBuffer(), + pGID, + pEnc, + nGlyphs, + 0, + NULL, + 0 ) ); + CloseTTFont( pTTFont ); + + return bSuccess; +} + +void PrintFontManager::getGlyphWidths( fontID nFont, + bool bVertical, + std::vector< sal_Int32 >& rWidths, + std::map< sal_Unicode, sal_uInt32 >& rUnicodeEnc ) +{ + PrintFont* pFont = getFont( nFont ); + if( !pFont || + (pFont->m_eType != fonttype::TrueType && pFont->m_eType != fonttype::Type1) ) + return; + if( pFont->m_eType == fonttype::TrueType ) + { + TrueTypeFont* pTTFont = NULL; + TrueTypeFontFile* pTTFontFile = static_cast< TrueTypeFontFile* >(pFont); + ByteString aFromFile = getFontFile( pFont ); + if( OpenTTFontFile( aFromFile.GetBuffer(), pTTFontFile->m_nCollectionEntry < 0 ? 0 : pTTFontFile->m_nCollectionEntry, &pTTFont ) != SF_OK ) + return; + int nGlyphs = GetTTGlyphCount( pTTFont ); + if( nGlyphs > 0 ) + { + rWidths.resize(nGlyphs); + std::vector<sal_uInt16> aGlyphIds(nGlyphs); + for( int i = 0; i < nGlyphs; i++ ) + aGlyphIds[i] = sal_uInt16(i); + TTSimpleGlyphMetrics* pMetrics = GetTTSimpleGlyphMetrics( pTTFont, + &aGlyphIds[0], + nGlyphs, + bVertical ? 1 : 0 ); + if( pMetrics ) + { + for( int i = 0; i< nGlyphs; i++ ) + rWidths[i] = pMetrics[i].adv; + free( pMetrics ); + rUnicodeEnc.clear(); + } + + // fill the unicode map + // TODO: isn't this map already available elsewhere in the fontmanager? + const sal_uInt8* pCmapData = NULL; + int nCmapSize = 0; + if( GetSfntTable( pTTFont, O_cmap, &pCmapData, &nCmapSize ) ) + { + CmapResult aCmapResult; + if( ParseCMAP( pCmapData, nCmapSize, aCmapResult ) ) + { + const ImplFontCharMap aCharMap( aCmapResult ); + for( sal_uInt32 cOld = 0;;) + { + // get next unicode covered by font + const sal_uInt32 c = aCharMap.GetNextChar( cOld ); + if( c == cOld ) + break; + cOld = c; +#if 1 // TODO: remove when sal_Unicode covers all of unicode + if( c > (sal_Unicode)~0 ) + break; +#endif + // get the matching glyph index + const sal_uInt32 nGlyphId = aCharMap.GetGlyphIndex( c ); + // update the requested map + rUnicodeEnc[ (sal_Unicode)c ] = nGlyphId; + } + } + } + } + CloseTTFont( pTTFont ); + } + else if( pFont->m_eType == fonttype::Type1 ) + { + if( ! pFont->m_aEncodingVector.size() ) + pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, true, true ); + if( pFont->m_pMetrics ) + { + rUnicodeEnc.clear(); + rWidths.clear(); + rWidths.reserve( pFont->m_pMetrics->m_aMetrics.size() ); + for( boost::unordered_map< int, CharacterMetric >::const_iterator it = + pFont->m_pMetrics->m_aMetrics.begin(); + it != pFont->m_pMetrics->m_aMetrics.end(); ++it ) + { + if( (it->first & 0x00010000) == 0 || bVertical ) + { + rUnicodeEnc[ sal_Unicode(it->first & 0x0000ffff) ] = sal_uInt32(rWidths.size()); + rWidths.push_back( it->second.width ); + } + } + } + } +} + +// ------------------------------------------------------------------------- + +const std::map< sal_Unicode, sal_Int32 >* PrintFontManager::getEncodingMap( fontID nFont, const std::map< sal_Unicode, rtl::OString >** pNonEncoded ) const +{ + PrintFont* pFont = getFont( nFont ); + if( !pFont || + (pFont->m_eType != fonttype::Type1 && pFont->m_eType != fonttype::Builtin) + ) + return NULL; + + if( ! pFont->m_aEncodingVector.size() ) + pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, true, true ); + + if( pNonEncoded ) + *pNonEncoded = pFont->m_aNonEncoded.size() ? &pFont->m_aNonEncoded : NULL; + + return pFont->m_aEncodingVector.size() ? &pFont->m_aEncodingVector : NULL; +} + +// ------------------------------------------------------------------------- + +std::list< OString > PrintFontManager::getAdobeNameFromUnicode( sal_Unicode aChar ) const +{ + std::pair< boost::unordered_multimap< sal_Unicode, rtl::OString >::const_iterator, + boost::unordered_multimap< sal_Unicode, rtl::OString >::const_iterator > range + = m_aUnicodeToAdobename.equal_range( aChar ); + + std::list< OString > aRet; + for( ; range.first != range.second; ++range.first ) + aRet.push_back( range.first->second ); + + if( aRet.begin() == aRet.end() && aChar != 0 ) + { + sal_Char aBuf[8]; + sal_Int32 nChars = snprintf( (char*)aBuf, sizeof( aBuf ), "uni%.4hX", aChar ); + aRet.push_back( OString( aBuf, nChars ) ); + } + + return aRet; +} + +// ------------------------------------------------------------------------- +std::list< sal_Unicode > PrintFontManager::getUnicodeFromAdobeName( const rtl::OString& rName ) const +{ + std::pair< boost::unordered_multimap< rtl::OString, sal_Unicode, rtl::OStringHash >::const_iterator, + boost::unordered_multimap< rtl::OString, sal_Unicode, rtl::OStringHash >::const_iterator > range + = m_aAdobenameToUnicode.equal_range( rName ); + + std::list< sal_Unicode > aRet; + for( ; range.first != range.second; ++range.first ) + aRet.push_back( range.first->second ); + + if( aRet.begin() == aRet.end() ) + { + if( rName.getLength() == 7 && rName.indexOf( "uni" ) == 0 ) + { + sal_Unicode aCode = (sal_Unicode)rName.copy( 3 ).toInt32( 16 ); + aRet.push_back( aCode ); + } + } + + return aRet; +} + +// ------------------------------------------------------------------------- +namespace +{ + OUString getString( const Any& rAny ) + { + OUString aStr; + rAny >>= aStr; + return aStr; + } + bool getBool( const Any& rAny ) + { + sal_Bool bBool = sal_False; + rAny >>= bBool; + return static_cast<bool>(bBool); + } + sal_Int32 getInt( const Any& rAny ) + { + sal_Int32 n = 0; + rAny >>= n; + return n; + } +} +bool PrintFontManager::readOverrideMetrics() +{ + if( ! m_aOverrideFonts.empty() ) + return false; + + css::uno::Reference< XMultiServiceFactory > xFact( comphelper::getProcessServiceFactory() ); + if( !xFact.is() ) + return false; + css::uno::Reference< XMaterialHolder > xMat( + xFact->createInstance( OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.psprint.CompatMetricOverride" ) ) ), + UNO_QUERY ); + if( !xMat.is() ) + return false; + + Any aAny( xMat->getMaterial() ); + Sequence< Any > aOverrideFonts; + if( ! (aAny >>= aOverrideFonts ) ) + return false; + sal_Int32 nFonts = aOverrideFonts.getLength(); + for( sal_Int32 i = 0; i < nFonts; i++ ) + { + Sequence< NamedValue > aMetrics; + if( ! (aOverrideFonts.getConstArray()[i] >>= aMetrics) ) + continue; + BuiltinFont* pFont = new BuiltinFont(); + pFont->m_nDirectory = 0; + pFont->m_bUserOverride = false; + pFont->m_pMetrics = new PrintFontMetrics; + memset( pFont->m_pMetrics->m_aPages, 0xff, sizeof( pFont->m_pMetrics->m_aPages ) ); + pFont->m_pMetrics->m_bKernPairsQueried = true; + sal_Int32 nProps = aMetrics.getLength(); + const NamedValue* pProps = aMetrics.getConstArray(); + for( sal_Int32 n = 0; n < nProps; n++ ) + { + if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "FamilyName" ) ) ) + pFont->m_nFamilyName = m_pAtoms->getAtom( ATOM_FAMILYNAME, + getString(pProps[n].Value), + sal_True ); + else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "PSName" ) ) ) + pFont->m_nPSName = m_pAtoms->getAtom( ATOM_PSNAME, + getString(pProps[n].Value), + sal_True ); + else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "StyleName" ) ) ) + pFont->m_aStyleName = getString(pProps[n].Value); + else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Italic" ) ) ) + pFont->m_eItalic = static_cast<italic::type>(getInt(pProps[n].Value)); + else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Width" ) ) ) + pFont->m_eWidth = static_cast<width::type>(getInt(pProps[n].Value)); + else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Weight" ) ) ) + pFont->m_eWeight = static_cast<weight::type>(getInt(pProps[n].Value)); + else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Pitch" ) ) ) + pFont->m_ePitch = static_cast<pitch::type>(getInt(pProps[n].Value)); + else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Encoding" ) ) ) + pFont->m_aEncoding = static_cast<rtl_TextEncoding>(getInt(pProps[n].Value)); + else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "FontEncodingOnly" ) ) ) + pFont->m_bFontEncodingOnly = getBool(pProps[n].Value); + else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "GlobalMetricXWidth" ) ) ) + pFont->m_aGlobalMetricX.width = getInt(pProps[n].Value); + else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "GlobalMetricXHeight" ) ) ) + pFont->m_aGlobalMetricX.height = getInt(pProps[n].Value); + else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "GlobalMetricYWidth" ) ) ) + pFont->m_aGlobalMetricY.width = getInt(pProps[n].Value); + else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "GlobalMetricYHeight" ) ) ) + pFont->m_aGlobalMetricY.height = getInt(pProps[n].Value); + else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Ascend" ) ) ) + pFont->m_nAscend = getInt(pProps[n].Value); + else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Descend" ) ) ) + pFont->m_nDescend = getInt(pProps[n].Value); + else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Leading" ) ) ) + pFont->m_nLeading = getInt(pProps[n].Value); + else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "XMin" ) ) ) + pFont->m_nXMin = getInt(pProps[n].Value); + else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "YMin" ) ) ) + pFont->m_nYMin = getInt(pProps[n].Value); + else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "XMax" ) ) ) + pFont->m_nXMax = getInt(pProps[n].Value); + else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "YMax" ) ) ) + pFont->m_nYMax = getInt(pProps[n].Value); + else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "VerticalSubstitutes" ) ) ) + pFont->m_bHaveVerticalSubstitutedGlyphs = getBool(pProps[n].Value); + else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "EncodingVector" ) ) ) + { + Sequence< NamedValue > aEncoding; + pProps[n].Value >>= aEncoding; + sal_Int32 nEnc = aEncoding.getLength(); + const NamedValue* pEnc = aEncoding.getConstArray(); + for( sal_Int32 m = 0; m < nEnc; m++ ) + { + sal_Unicode cCode = *pEnc[m].Name.getStr(); + sal_Int32 nGlyph = getInt(pEnc[m].Value); + pFont->m_aEncodingVector[ cCode ] = nGlyph; + } + } + else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "NonEncoded" ) ) ) + { + Sequence< NamedValue > aEncoding; + pProps[n].Value >>= aEncoding; + sal_Int32 nEnc = aEncoding.getLength(); + const NamedValue* pEnc = aEncoding.getConstArray(); + for( sal_Int32 m = 0; m < nEnc; m++ ) + { + sal_Unicode cCode = *pEnc[m].Name.getStr(); + OUString aGlyphName( getString(pEnc[m].Value) ); + pFont->m_aNonEncoded[ cCode ] = OUStringToOString(aGlyphName,RTL_TEXTENCODING_ASCII_US); + } + } + else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "CharacterMetrics" ) ) ) + { + // fill pFont->m_pMetrics->m_aMetrics + // expect triples of int: int -> CharacterMetric.{ width, height } + Sequence< sal_Int32 > aSeq; + pProps[n].Value >>= aSeq; + sal_Int32 nInts = aSeq.getLength(); + const sal_Int32* pInts = aSeq.getConstArray(); + for( sal_Int32 m = 0; m < nInts; m+=3 ) + { + pFont->m_pMetrics->m_aMetrics[ pInts[m] ].width = static_cast<short int>(pInts[m+1]); + pFont->m_pMetrics->m_aMetrics[ pInts[m] ].height = static_cast<short int>(pInts[m+2]); + } + } + else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "XKernPairs" ) ) ) + { + // fill pFont->m_pMetrics->m_aXKernPairs + // expection name: <unicode1><unicode2> value: ((height << 16)| width) + Sequence< NamedValue > aKern; + pProps[n].Value >>= aKern; + KernPair aPair; + const NamedValue* pVals = aKern.getConstArray(); + int nPairs = aKern.getLength(); + for( int m = 0; m < nPairs; m++ ) + { + if( pVals[m].Name.getLength() == 2 ) + { + aPair.first = pVals[m].Name.getStr()[0]; + aPair.second = pVals[m].Name.getStr()[1]; + sal_Int32 nKern = getInt( pVals[m].Value ); + aPair.kern_x = static_cast<short int>(nKern & 0xffff); + aPair.kern_y = static_cast<short int>((sal_uInt32(nKern) >> 16) & 0xffff); + pFont->m_pMetrics->m_aXKernPairs.push_back( aPair ); + } + } + } + } + // sanity check + if( pFont->m_nPSName && + pFont->m_nFamilyName && + ! pFont->m_pMetrics->m_aMetrics.empty() ) + { + m_aOverrideFonts.push_back( m_nNextFontID ); + m_aFonts[ m_nNextFontID++ ] = pFont; + } + else + { + DBG_ASSERT( 0, "override font failed" ); + delete pFont; + } + } + + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/fontmanager/helper.cxx b/vcl/unx/generic/fontmanager/helper.cxx new file mode 100644 index 000000000000..4b0d327a9a03 --- /dev/null +++ b/vcl/unx/generic/fontmanager/helper.cxx @@ -0,0 +1,411 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <cstring> +#include <sys/stat.h> +#include <unistd.h> +#include <limits.h> + +#include "vcl/helper.hxx" +#include "vcl/ppdparser.hxx" +#include "tools/string.hxx" +#include "tools/urlobj.hxx" +#include "osl/file.hxx" +#include "osl/process.h" +#include "rtl/bootstrap.hxx" + +using ::rtl::Bootstrap; +using ::rtl::OUString; +using ::rtl::OUStringBuffer; +using ::rtl::OString; +using ::rtl::OStringToOUString; +using ::rtl::OUStringToOString; + +namespace psp { + +OUString getOfficePath( enum whichOfficePath ePath ) +{ + static OUString aNetPath; + static OUString aUserPath; + static OUString aConfigPath; + static OUString aEmpty; + static bool bOnce = false; + + if( ! bOnce ) + { + bOnce = true; + OUString aIni; + Bootstrap::get( OUString( RTL_CONSTASCII_USTRINGPARAM( "BRAND_BASE_DIR" ) ), aIni ); + aIni += OUString( RTL_CONSTASCII_USTRINGPARAM( "/program/" SAL_CONFIGFILE( "bootstrap" ) ) ); + Bootstrap aBootstrap( aIni ); + aBootstrap.getFrom( OUString( RTL_CONSTASCII_USTRINGPARAM( "CustomDataUrl" ) ), aConfigPath ); + aBootstrap.getFrom( OUString( RTL_CONSTASCII_USTRINGPARAM( "BaseInstallation" ) ), aNetPath ); + aBootstrap.getFrom( OUString( RTL_CONSTASCII_USTRINGPARAM( "UserInstallation" ) ), aUserPath ); + OUString aUPath = aUserPath; + + if( ! aConfigPath.compareToAscii( "file://", 7 ) ) + { + OUString aSysPath; + if( osl_getSystemPathFromFileURL( aConfigPath.pData, &aSysPath.pData ) == osl_File_E_None ) + aConfigPath = aSysPath; + } + if( ! aNetPath.compareToAscii( "file://", 7 ) ) + { + OUString aSysPath; + if( osl_getSystemPathFromFileURL( aNetPath.pData, &aSysPath.pData ) == osl_File_E_None ) + aNetPath = aSysPath; + } + if( ! aUserPath.compareToAscii( "file://", 7 ) ) + { + OUString aSysPath; + if( osl_getSystemPathFromFileURL( aUserPath.pData, &aSysPath.pData ) == osl_File_E_None ) + aUserPath = aSysPath; + } + // ensure user path exists + aUPath += OUString( RTL_CONSTASCII_USTRINGPARAM( "/user/psprint" ) ); + #if OSL_DEBUG_LEVEL > 1 + oslFileError eErr = + #endif + osl_createDirectoryPath( aUPath.pData, NULL, NULL ); + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "try to create \"%s\" = %d\n", OUStringToOString( aUPath, RTL_TEXTENCODING_UTF8 ).getStr(), eErr ); + #endif + } + + switch( ePath ) + { + case ConfigPath: return aConfigPath; + case NetPath: return aNetPath; + case UserPath: return aUserPath; + } + return aEmpty; +} + +static OString getEnvironmentPath( const char* pKey ) +{ + OString aPath; + + const char* pValue = getenv( pKey ); + if( pValue && *pValue ) + { + aPath = OString( pValue ); + } + return aPath; +} + +} // namespace psp + +void psp::getPrinterPathList( std::list< OUString >& rPathList, const char* pSubDir ) +{ + rPathList.clear(); + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + + OUStringBuffer aPathBuffer( 256 ); + + // append net path + aPathBuffer.append( getOfficePath( psp::NetPath ) ); + if( aPathBuffer.getLength() ) + { + aPathBuffer.appendAscii( "/share/psprint" ); + if( pSubDir ) + { + aPathBuffer.append( sal_Unicode('/') ); + aPathBuffer.appendAscii( pSubDir ); + } + rPathList.push_back( aPathBuffer.makeStringAndClear() ); + } + // append user path + aPathBuffer.append( getOfficePath( psp::UserPath ) ); + if( aPathBuffer.getLength() ) + { + aPathBuffer.appendAscii( "/user/psprint" ); + if( pSubDir ) + { + aPathBuffer.append( sal_Unicode('/') ); + aPathBuffer.appendAscii( pSubDir ); + } + rPathList.push_back( aPathBuffer.makeStringAndClear() ); + } + + OString aPath( getEnvironmentPath("SAL_PSPRINT") ); + sal_Int32 nIndex = 0; + do + { + OString aDir( aPath.getToken( 0, ':', nIndex ) ); + if( ! aDir.getLength() ) + continue; + + if( pSubDir ) + { + aDir += "/"; + aDir += pSubDir; + } + struct stat aStat; + if( stat( aDir.getStr(), &aStat ) || ! S_ISDIR( aStat.st_mode ) ) + continue; + + rPathList.push_back( OStringToOUString( aDir, aEncoding ) ); + } while( nIndex != -1 ); + + #ifdef SYSTEM_PPD_DIR + if( pSubDir && rtl_str_compare( pSubDir, PRINTER_PPDDIR ) == 0 ) + { + rPathList.push_back( rtl::OStringToOUString( rtl::OString( SYSTEM_PPD_DIR ), RTL_TEXTENCODING_UTF8 ) ); + } + #endif + + if( rPathList.empty() ) + { + // last resort: next to program file (mainly for setup) + OUString aExe; + if( osl_getExecutableFile( &aExe.pData ) == osl_Process_E_None ) + { + INetURLObject aDir( aExe ); + aDir.removeSegment(); + aExe = aDir.GetMainURL( INetURLObject::NO_DECODE ); + OUString aSysPath; + if( osl_getSystemPathFromFileURL( aExe.pData, &aSysPath.pData ) == osl_File_E_None ) + { + rPathList.push_back( aSysPath ); + } + } + } +} + +OUString psp::getFontPath() +{ + static OUString aPath; + + if( ! aPath.getLength() ) + { + OUStringBuffer aPathBuffer( 512 ); + + OUString aConfigPath( getOfficePath( psp::ConfigPath ) ); + OUString aNetPath( getOfficePath( psp::NetPath ) ); + OUString aUserPath( getOfficePath( psp::UserPath ) ); + if( aConfigPath.getLength() ) + { + // #i53530# Path from CustomDataUrl will completely + // replace net and user paths if the path exists + aPathBuffer.append(aConfigPath); + aPathBuffer.appendAscii("/share/fonts"); + // check existance of config path + struct stat aStat; + if( 0 != stat( OUStringToOString( aPathBuffer.makeStringAndClear(), osl_getThreadTextEncoding() ).getStr(), &aStat ) + || ! S_ISDIR( aStat.st_mode ) ) + aConfigPath = OUString(); + else + { + aPathBuffer.append(aConfigPath); + aPathBuffer.appendAscii("/share/fonts"); + } + } + if( aConfigPath.getLength() == 0 ) + { + if( aNetPath.getLength() ) + { + aPathBuffer.append( aNetPath ); + aPathBuffer.appendAscii( "/share/fonts/truetype;"); + aPathBuffer.append( aNetPath ); + aPathBuffer.appendAscii( "/share/fonts/type1;" ); + } + if( aUserPath.getLength() ) + { + aPathBuffer.append( aUserPath ); + aPathBuffer.appendAscii( "/user/fonts" ); + } + } + OString aEnvPath( getEnvironmentPath( "SAL_FONTPATH_PRIVATE" ) ); + if( aEnvPath.getLength() ) + { + aPathBuffer.append( sal_Unicode(';') ); + aPathBuffer.append( OStringToOUString( aEnvPath, osl_getThreadTextEncoding() ) ); + } + + aPath = aPathBuffer.makeStringAndClear(); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "initializing font path to \"%s\"\n", OUStringToOString( aPath, RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); +#endif + } + return aPath; +} + +bool psp::convertPfbToPfa( ::osl::File& rInFile, ::osl::File& rOutFile ) +{ + static unsigned char hexDigits[] = + { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + + bool bSuccess = true; + bool bEof = false; + unsigned char buffer[256]; + sal_uInt64 nRead; + sal_uInt64 nOrgPos = 0; + rInFile.getPos( nOrgPos ); + + while( bSuccess && ! bEof ) + { + // read leading bytes + bEof = ! rInFile.read( buffer, 6, nRead ) && nRead == 6 ? false : true; + unsigned int nType = buffer[ 1 ]; + unsigned int nBytesToRead = buffer[2] | buffer[3] << 8 | buffer[4] << 16 | buffer[5] << 24; + if( buffer[0] != 0x80 ) // test for pfb m_agic number + { + // this migt be a pfa font already + if( ! rInFile.read( buffer+6, 9, nRead ) && nRead == 9 && + ( ! std::strncmp( (char*)buffer, "%!FontType1-", 12 ) || + ! std::strncmp( (char*)buffer, "%!PS-AdobeFont-", 15 ) ) ) + { + sal_uInt64 nWrite = 0; + if( rOutFile.write( buffer, 15, nWrite ) || nWrite != 15 ) + bSuccess = false; + while( bSuccess && + ! rInFile.read( buffer, sizeof( buffer ), nRead ) && + nRead != 0 ) + { + if( rOutFile.write( buffer, nRead, nWrite ) || + nWrite != nRead ) + bSuccess = false; + } + bEof = true; + } + else + bSuccess = false; + } + else if( nType == 1 || nType == 2 ) + { + unsigned char* pBuffer = new unsigned char[ nBytesToRead+1 ]; + + if( ! rInFile.read( pBuffer, nBytesToRead, nRead ) && nRead == nBytesToRead ) + { + if( nType == 1 ) + { + // ascii data, convert dos lineends( \r\n ) and + // m_ac lineends( \r ) to \n + unsigned char * pWriteBuffer = new unsigned char[ nBytesToRead ]; + unsigned int nBytesToWrite = 0; + for( unsigned int i = 0; i < nBytesToRead; i++ ) + { + if( pBuffer[i] != '\r' ) + pWriteBuffer[ nBytesToWrite++ ] = pBuffer[i]; + else if( pBuffer[ i+1 ] == '\n' ) + { + i++; + pWriteBuffer[ nBytesToWrite++ ] = '\n'; + } + else + pWriteBuffer[ nBytesToWrite++ ] = '\n'; + } + if( rOutFile.write( pWriteBuffer, nBytesToWrite, nRead ) || nRead != nBytesToWrite ) + bSuccess = false; + + delete [] pWriteBuffer; + } + else + { + // binary data + unsigned int nBuffer = 0; + for( unsigned int i = 0; i < nBytesToRead && bSuccess; i++ ) + { + buffer[ nBuffer++ ] = hexDigits[ pBuffer[ i ] >> 4 ]; + buffer[ nBuffer++ ] = hexDigits[ pBuffer[ i ] & 15 ]; + if( nBuffer >= 80 ) + { + buffer[ nBuffer++ ] = '\n'; + if( rOutFile.write( buffer, nBuffer, nRead ) || nRead != nBuffer ) + bSuccess = false; + nBuffer = 0; + } + } + if( nBuffer > 0 && bSuccess ) + { + buffer[ nBuffer++ ] = '\n'; + if( rOutFile.write( buffer, nBuffer, nRead ) || nRead != nBuffer ) + bSuccess = false; + } + } + } + else + bSuccess = false; + + delete [] pBuffer; + } + else if( nType == 3 ) + bEof = true; + else + bSuccess = false; + } + + return bSuccess; +} + +void psp::normPath( OString& rPath ) +{ + char buf[PATH_MAX]; + + ByteString aPath( rPath ); + + // double slashes and slash at end are probably + // removed by realpath anyway, but since this runs + // on many different platforms let's play it safe + while( aPath.SearchAndReplace( "//", "/" ) != STRING_NOTFOUND ) + ; + if( aPath.Len() > 0 && aPath.GetChar( aPath.Len()-1 ) == '/' ) + aPath.Erase( aPath.Len()-1 ); + + if( ( aPath.Search( "./" ) != STRING_NOTFOUND || + aPath.Search( "~" ) != STRING_NOTFOUND ) + && realpath( aPath.GetBuffer(), buf ) ) + { + rPath = buf; + } + else + { + rPath = aPath; + } +} + +void psp::splitPath( OString& rPath, OString& rDir, OString& rBase ) +{ + normPath( rPath ); + sal_Int32 nIndex = rPath.lastIndexOf( '/' ); + if( nIndex > 0 ) + rDir = rPath.copy( 0, nIndex ); + else if( nIndex == 0 ) // root dir + rDir = rPath.copy( 0, 1 ); + if( rPath.getLength() > nIndex+1 ) + rBase = rPath.copy( nIndex+1 ); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/fontmanager/parseAFM.cxx b/vcl/unx/generic/fontmanager/parseAFM.cxx new file mode 100644 index 000000000000..242537c8e699 --- /dev/null +++ b/vcl/unx/generic/fontmanager/parseAFM.cxx @@ -0,0 +1,1492 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * (C) 1988, 1989, 1990 by Adobe Systems Incorporated. All rights reserved. + * + * This file may be freely copied and redistributed as long as: + * 1) This entire notice continues to be included in the file, + * 2) If the file has been modified in any way, a notice of such + * modification is conspicuously indicated. + * + * PostScript, Display PostScript, and Adobe are registered trademarks of + * Adobe Systems Incorporated. + * + * ************************************************************************ + * THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO CHANGE WITHOUT + * NOTICE, AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY ADOBE SYSTEMS + * INCORPORATED. ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY OR + * LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO WARRANTY OF ANY + * KIND (EXPRESS, IMPLIED OR STATUTORY) WITH RESPECT TO THIS INFORMATION, + * AND EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + * ************************************************************************ + */ + +/* + * Changes made for OpenOffice.org + * + * 10/24/2000 pl - changed code to compile with c++-compilers + * - added namespace to avoid symbol clashes + * - replaced BOOL by bool + * - added function to free space allocated by parseFile + * 10/26/2000 pl - added additional keys + * - added ability to parse slightly broken files + * - added charwidth member to GlobalFontInfo + * 04/26/2001 pl - added OpenOffice header + * 10/19/2005 pl - performance increase: + * - fread file in one pass + * - replace file io by buffer access + * 10/20/2005 pl - performance increase: + * - use one table lookup in token() routine + * instead of many conditions + * - return token length in toke() routine + * - use hash lookup instead of binary search + * in recognize() routine + */ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +/* parseAFM.c + * + * This file is used in conjuction with the parseAFM.h header file. + * This file contains several procedures that are used to parse AFM + * files. It is intended to work with an application program that needs + * font metric information. The program can be used as is by making a + * procedure call to "parseFile" (passing in the expected parameters) + * and having it fill in a data structure with the data from the + * AFM file, or an application developer may wish to customize this + * code. + * + * There is also a file, parseAFMclient.c, that is a sample application + * showing how to call the "parseFile" procedure and how to use the data + * after "parseFile" has returned. + * + * Please read the comments in parseAFM.h and parseAFMclient.c. + * + * History: + * original: DSM Thu Oct 20 17:39:59 PDT 1988 + * modified: DSM Mon Jul 3 14:17:50 PDT 1989 + * - added 'storageProblem' return code + * - fixed bug of not allocating extra byte for string duplication + * - fixed typos + * modified: DSM Tue Apr 3 11:18:34 PDT 1990 + * - added free(ident) at end of parseFile routine + * modified: DSM Tue Jun 19 10:16:29 PDT 1990 + * - changed (width == 250) to (width = 250) in initializeArray + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <math.h> + +#include "parseAFM.hxx" +#include "vcl/strhelper.hxx" + +#include "rtl/alloc.h" + +#define lineterm EOL /* line terminating character */ +#define normalEOF 1 /* return code from parsing routines used only */ +/* in this module */ +#define Space "space" /* used in string comparison to look for the width */ +/* of the space character to init the widths array */ +#define False "false" /* used in string comparison to check the value of */ +/* boolean keys (e.g. IsFixedPitch) */ + +#define MATCH(A,B) (strncmp((A),(B), MAX_NAME) == 0) + +namespace psp { + +class FileInputStream +{ + char* m_pMemory; + unsigned int m_nPos; + unsigned int m_nLen; + public: + FileInputStream( const char* pFilename ); + ~FileInputStream(); + + int getChar() { return (m_nPos < m_nLen) ? int(m_pMemory[m_nPos++]) : -1; } + void ungetChar() + { + if( m_nPos > 0 ) + m_nPos--; + } + unsigned int tell() const { return m_nPos; } + void seek( unsigned int nPos ) + // NOTE: do not check input data since only results of tell() + // get seek()ed in this file + { m_nPos = nPos; } +}; + +FileInputStream::FileInputStream( const char* pFilename ) : + m_pMemory( NULL ), + m_nPos( 0 ), + m_nLen( 0 ) +{ + struct stat aStat; + if( ! stat( pFilename, &aStat ) && + S_ISREG( aStat.st_mode ) && + aStat.st_size > 0 + ) + { + FILE* fp = fopen( pFilename, "r" ); + if( fp ) + { + m_pMemory = (char*)rtl_allocateMemory( aStat.st_size ); + m_nLen = (unsigned int)fread( m_pMemory, 1, aStat.st_size, fp ); + fclose( fp ); + } + } +} + +FileInputStream::~FileInputStream() +{ + rtl_freeMemory( m_pMemory ); +} + +/*************************** GLOBALS ***********************/ +/* "shorts" for fast case statement + * The values of each of these enumerated items correspond to an entry in the + * table of strings defined below. Therefore, if you add a new string as + * new keyword into the keyStrings table, you must also add a corresponding + * parseKey AND it MUST be in the same position! + * + * IMPORTANT: since the sorting algorithm is a binary search, the strings of + * keywords must be placed in lexicographical order, below. [Therefore, the + * enumerated items are not necessarily in lexicographical order, depending + * on the name chosen. BUT, they must be placed in the same position as the + * corresponding key string.] The NOPE shall remain in the last position, + * since it does not correspond to any key string, and it is used in the + * "recognize" procedure to calculate how many possible keys there are. + */ + +// some metrics have Ascent, Descent instead Ascender, Descender or Em +// which is not allowed per afm spcification, but let us handle +// this gently +enum parseKey { + ASCENDER, ASCENT, CHARBBOX, CODE, COMPCHAR, CODEHEX, CAPHEIGHT, CHARWIDTH, CHARACTERSET, CHARACTERS, COMMENT, + DESCENDER, DESCENT, EM, ENCODINGSCHEME, ENDCHARMETRICS, ENDCOMPOSITES, ENDDIRECTION, + ENDFONTMETRICS, ENDKERNDATA, ENDKERNPAIRS, ENDTRACKKERN, + FAMILYNAME, FONTBBOX, FONTNAME, FULLNAME, ISBASEFONT, ISFIXEDPITCH, + ITALICANGLE, KERNPAIR, KERNPAIRXAMT, LIGATURE, MAPPINGSCHEME, METRICSSETS, CHARNAME, + NOTICE, COMPCHARPIECE, STARTCHARMETRICS, STARTCOMPOSITES, STARTDIRECTION, + STARTFONTMETRICS, STARTKERNDATA, STARTKERNPAIRS, + STARTTRACKKERN, STDHW, STDVW, TRACKKERN, UNDERLINEPOSITION, + UNDERLINETHICKNESS, VVECTOR, VERSION, XYWIDTH, X0WIDTH, XWIDTH, WEIGHT, XHEIGHT, + NOPE +}; + +/*************************** PARSING ROUTINES **************/ + +/*************************** token *************************/ + +/* A "AFM file Conventions" tokenizer. That means that it will + * return the next token delimited by white space. See also + * the `linetoken' routine, which does a similar thing but + * reads all tokens until the next end-of-line. + */ + +// token white space is ' ', '\n', '\r', ',', '\t', ';' +static const bool is_white_Array[ 256 ] = +{ false, false, false, false, false, false, false, false, // 0-7 + false, true, true, false, false, true, false, false, // 8-15 + false, false, false, false, false, false, false, false, // 16-23 + false, false, false, false, false, false, false, false, // 24-31 + true, false, false, false, false, false, false, false, // 32-39 + false, false, false, false, true, false, false, false, // 40-47 + false, false, false, false, false, false, false, false, // 48-55 + false, false, false, true, false, false, false, false, // 56-63 + + false, false, false, false, false, false, false, false, // 64 - + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, // 127 + + false, false, false, false, false, false, false, false, // 128 - + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, // 191 + + false, false, false, false, false, false, false, false, // 192 - + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, // 255 +}; +// token delimiters are ' ', '\n', '\r', '\t', ':', ';' +static const bool is_delimiter_Array[ 256 ] = +{ false, false, false, false, false, false, false, false, // 0-7 + false, true, true, false, false, true, false, false, // 8-15 + false, false, false, false, false, false, false, false, // 16-23 + false, false, false, false, false, false, false, false, // 24-31 + true, false, false, false, false, false, false, false, // 32-39 + false, false, false, false, false, false, false, false, // 40-47 + false, false, false, false, false, false, false, false, // 48-55 + false, false, true, true, false, false, false, false, // 56-63 + + false, false, false, false, false, false, false, false, // 64 - + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, // 127 + + false, false, false, false, false, false, false, false, // 128 - + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, // 191 + + false, false, false, false, false, false, false, false, // 192 - + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, // 255 +}; +static char *token( FileInputStream* stream, int& rLen ) +{ + static char ident[MAX_NAME]; /* storage buffer for keywords */ + + int ch, idx; + + /* skip over white space */ + // relies on EOF = -1 + while( is_white_Array[ (ch = stream->getChar()) & 255 ] ) + ; + + idx = 0; + while( ch != -1 && ! is_delimiter_Array[ ch & 255 ] && idx < MAX_NAME-1 ) + { + ident[idx++] = ch; + ch = stream->getChar(); + } + + if (ch == -1 && idx < 1) return ((char *)NULL); + if (idx >= 1 && ch != ':' && ch != -1) stream->ungetChar(); + if (idx < 1 ) ident[idx++] = ch; /* single-character token */ + ident[idx] = 0; + rLen = idx; + + return(ident); /* returns pointer to the token */ + +} /* token */ + + +/*************************** linetoken *************************/ + +/* "linetoken" will get read all tokens until the EOL character from + * the given stream. This is used to get any arguments that can be + * more than one word (like Comment lines and FullName). + */ + +static char *linetoken( FileInputStream* stream ) +{ + static char ident[MAX_NAME]; /* storage buffer for keywords */ + int ch, idx; + + while ((ch = stream->getChar()) == ' ' || ch == '\t' ) ; + + idx = 0; + while (ch != -1 && ch != lineterm && ch != '\r' && idx < MAX_NAME-1 ) + { + ident[idx++] = ch; + ch = stream->getChar(); + } /* while */ + + stream->ungetChar(); + ident[idx] = 0; + + return(ident); /* returns pointer to the token */ + +} /* linetoken */ + + +/*************************** recognize *************************/ + +/* This function tries to match a string to a known list of + * valid AFM entries (check the keyStrings array above). + * "ident" contains everything from white space through the + * next space, tab, or ":" character. + * + * The algorithm is a standard Knuth binary search. + */ +#include "afm_hash.cpp" + +static inline enum parseKey recognize( register char* ident, int len) +{ + const hash_entry* pEntry = AfmKeywordHash::in_word_set( ident, len ); + return pEntry ? pEntry->eKey : NOPE; + +} /* recognize */ + + +/************************* parseGlobals *****************************/ + +/* This function is called by "parseFile". It will parse the AFM file + * up to the "StartCharMetrics" keyword, which essentially marks the + * end of the Global Font Information and the beginning of the character + * metrics information. + * + * If the caller of "parseFile" specified that it wanted the Global + * Font Information (as defined by the "AFM file Specification" + * document), then that information will be stored in the returned + * data structure. + * + * Any Global Font Information entries that are not found in a + * given file, will have the usual default initialization value + * for its type (i.e. entries of type int will be 0, etc). + * + * This function returns an error code specifying whether there was + * a premature EOF or a parsing error. This return value is used by + * parseFile to determine if there is more file to parse. + */ + +static int parseGlobals( FileInputStream* fp, register GlobalFontInfo* gfi ) +{ + bool cont = true, save = (gfi != NULL); + int error = ok; + register char *keyword; + int direction = -1; + int tokenlen; + + while (cont) + { + keyword = token(fp, tokenlen); + + if (keyword == NULL) + /* Have reached an early and unexpected EOF. */ + /* Set flag and stop parsing */ + { + error = earlyEOF; + break; /* get out of loop */ + } + if (!save) + /* get tokens until the end of the Global Font info section */ + /* without saving any of the data */ + switch (recognize(keyword, tokenlen)) + { + case STARTCHARMETRICS: + cont = false; + break; + case ENDFONTMETRICS: + cont = false; + error = normalEOF; + break; + default: + break; + } /* switch */ + else + /* otherwise parse entire global font info section, */ + /* saving the data */ + switch(recognize(keyword, tokenlen)) + { + case STARTFONTMETRICS: + if ((keyword = token(fp,tokenlen)) != NULL) + gfi->afmVersion = strdup( keyword ); + break; + case COMMENT: + keyword = linetoken(fp); + break; + case FONTNAME: + if ((keyword = token(fp,tokenlen)) != NULL) + gfi->fontName = strdup( keyword ); + break; + case ENCODINGSCHEME: + if ((keyword = token(fp,tokenlen)) != NULL) + gfi->encodingScheme = strdup( keyword ); + break; + case FULLNAME: + if ((keyword = linetoken(fp)) != NULL) + gfi->fullName = strdup( keyword ); + break; + case FAMILYNAME: + if ((keyword = linetoken(fp)) != NULL) + gfi->familyName = strdup( keyword ); + break; + case WEIGHT: + if ((keyword = token(fp,tokenlen)) != NULL) + gfi->weight = strdup( keyword ); + break; + case ITALICANGLE: + if ((keyword = token(fp,tokenlen)) != NULL) + gfi->italicAngle = StringToDouble( keyword ); + break; + case ISFIXEDPITCH: + if ((keyword = token(fp,tokenlen)) != NULL) + { + if (MATCH(keyword, False)) + gfi->isFixedPitch = 0; + else + gfi->isFixedPitch = 1; + } + break; + case UNDERLINEPOSITION: + if ((keyword = token(fp,tokenlen)) != NULL) + gfi->underlinePosition = atoi(keyword); + break; + case UNDERLINETHICKNESS: + if ((keyword = token(fp,tokenlen)) != NULL) + gfi->underlineThickness = atoi(keyword); + break; + case VERSION: + if ((keyword = token(fp,tokenlen)) != NULL) + gfi->version = strdup( keyword ); + break; + case NOTICE: + if ((keyword = linetoken(fp)) != NULL) + gfi->notice = strdup( keyword ); + break; + case FONTBBOX: + if ((keyword = token(fp,tokenlen)) != NULL) + gfi->fontBBox.llx = atoi(keyword); + if ((keyword = token(fp,tokenlen)) != NULL) + gfi->fontBBox.lly = atoi(keyword); + if ((keyword = token(fp,tokenlen)) != NULL) + gfi->fontBBox.urx = atoi(keyword); + if ((keyword = token(fp,tokenlen)) != NULL) + gfi->fontBBox.ury = atoi(keyword); + break; + case CAPHEIGHT: + if ((keyword = token(fp,tokenlen)) != NULL) + gfi->capHeight = atoi(keyword); + break; + case XHEIGHT: + if ((keyword = token(fp,tokenlen)) != NULL) + gfi->xHeight = atoi(keyword); + break; + case DESCENT: + if ((keyword = token(fp,tokenlen)) != NULL) + gfi->descender = -atoi(keyword); + break; + case DESCENDER: + if ((keyword = token(fp,tokenlen)) != NULL) + gfi->descender = atoi(keyword); + break; + case ASCENT: + case ASCENDER: + if ((keyword = token(fp,tokenlen)) != NULL) + gfi->ascender = atoi(keyword); + break; + case STARTCHARMETRICS: + cont = false; + break; + case ENDFONTMETRICS: + cont = false; + error = normalEOF; + break; + case EM: + // skip one token + keyword = token(fp,tokenlen); + break; + case STARTDIRECTION: + if ((keyword = token(fp,tokenlen)) != NULL) + direction = atoi(keyword); + break; /* ignore this for now */ + case ENDDIRECTION: + break; /* ignore this for now */ + case MAPPINGSCHEME: + keyword = token(fp,tokenlen); + break; /* ignore this for now */ + case CHARACTERS: + keyword = token(fp,tokenlen); + break; /* ignore this for now */ + case ISBASEFONT: + keyword = token(fp,tokenlen); + break; /* ignore this for now */ + case CHARACTERSET: + keyword=token(fp,tokenlen); //ignore + break; + case STDHW: + keyword=token(fp,tokenlen); //ignore + break; + case STDVW: + keyword=token(fp,tokenlen); //ignore + break; + case CHARWIDTH: + if ((keyword = token(fp,tokenlen)) != NULL) + { + if (direction == 0) + gfi->charwidth = atoi(keyword); + } + keyword = token(fp,tokenlen); + /* ignore y-width for now */ + break; + case METRICSSETS: + keyword = token(fp,tokenlen); + break; /* ignore this for now */ + case NOPE: + default: + error = parseError; + break; + } /* switch */ + } /* while */ + + return(error); + +} /* parseGlobals */ + + +/************************* parseCharWidths **************************/ + +/* This function is called by "parseFile". It will parse the AFM file + * up to the "EndCharMetrics" keyword. It will save the character + * width info (as opposed to all of the character metric information) + * if requested by the caller of parseFile. Otherwise, it will just + * parse through the section without saving any information. + * + * If data is to be saved, parseCharWidths is passed in a pointer + * to an array of widths that has already been initialized by the + * standard value for unmapped character codes. This function parses + * the Character Metrics section only storing the width information + * for the encoded characters into the array using the character code + * as the index into that array. + * + * This function returns an error code specifying whether there was + * a premature EOF or a parsing error. This return value is used by + * parseFile to determine if there is more file to parse. + */ + +static int parseCharWidths( FileInputStream* fp, register int* cwi) +{ + bool cont = true, save = (cwi != NULL); + int pos = 0, error = ok, tokenlen; + register char *keyword; + + while (cont) + { + keyword = token(fp,tokenlen); + /* Have reached an early and unexpected EOF. */ + /* Set flag and stop parsing */ + if (keyword == NULL) + { + error = earlyEOF; + break; /* get out of loop */ + } + if (!save) + /* get tokens until the end of the Char Metrics section without */ + /* saving any of the data*/ + switch (recognize(keyword,tokenlen)) + { + case ENDCHARMETRICS: + cont = false; + break; + case ENDFONTMETRICS: + cont = false; + error = normalEOF; + break; + default: + break; + } /* switch */ + else + /* otherwise parse entire char metrics section, saving */ + /* only the char x-width info */ + switch(recognize(keyword,tokenlen)) + { + case COMMENT: + keyword = linetoken(fp); + break; + case CODE: + if ((keyword = token(fp,tokenlen)) != NULL) + pos = atoi(keyword); + break; + case XYWIDTH: + /* PROBLEM: Should be no Y-WIDTH when doing "quick & dirty" */ + keyword = token(fp,tokenlen); keyword = token(fp,tokenlen); /* eat values */ + error = parseError; + break; + case CODEHEX: + if ((keyword = token(fp,tokenlen)) != NULL) + sscanf(keyword, "<%x>", &pos); + break; + case X0WIDTH: + (void) token(fp,tokenlen); + break; + case XWIDTH: + if ((keyword = token(fp,tokenlen)) != NULL) + if (pos >= 0) /* ignore unmapped chars */ + cwi[pos] = atoi(keyword); + break; + case ENDCHARMETRICS: + cont = false; + break; + case ENDFONTMETRICS: + cont = false; + error = normalEOF; + break; + case CHARNAME: /* eat values (so doesn't cause parseError) */ + keyword = token(fp,tokenlen); + break; + case CHARBBOX: + keyword = token(fp,tokenlen); keyword = token(fp,tokenlen); + keyword = token(fp,tokenlen); keyword = token(fp,tokenlen); + break; + case LIGATURE: + keyword = token(fp,tokenlen); keyword = token(fp,tokenlen); + break; + case VVECTOR: + keyword = token(fp,tokenlen); + keyword = token(fp,tokenlen); + break; + case NOPE: + default: + error = parseError; + break; + } /* switch */ + } /* while */ + + return(error); + +} /* parseCharWidths */ + + +/* + * number of char metrics is almost allways inaccurate, so be gentle and try to + * adapt our internal storage by adjusting the allocated list + */ + +static int +reallocFontMetrics( void **pp_fontmetrics, int *p_oldcount, int n_newcount, unsigned int n_size ) +{ + char *p_tmpmetrics = NULL; + + if ((pp_fontmetrics == NULL) || (*pp_fontmetrics == NULL)) + return storageProblem; + + if (*p_oldcount == n_newcount) + return ok; + + p_tmpmetrics = (char*)realloc(*pp_fontmetrics, n_newcount * n_size); + if (p_tmpmetrics == NULL) + return storageProblem; + + if ( n_newcount > *p_oldcount ) + { + char *p_inimetrics = p_tmpmetrics + n_size * *p_oldcount; + int n_inimetrics = n_size * (n_newcount - *p_oldcount); + memset( p_inimetrics, 0, n_inimetrics ); + } + + *pp_fontmetrics = p_tmpmetrics; + *p_oldcount = n_newcount; + + return ok; +} + +static unsigned int +enlargeCount( unsigned int n_oldcount ) +{ + unsigned int n_newcount = n_oldcount + n_oldcount / 5; + if (n_oldcount == n_newcount ) + n_newcount = n_oldcount + 5; + + return n_newcount; +} + +/************************* parseCharMetrics ************************/ + +/* This function is called by parseFile if the caller of parseFile + * requested that all character metric information be saved + * (as opposed to only the character width information). + * + * parseCharMetrics is passed in a pointer to an array of records + * to hold information on a per character basis. This function + * parses the Character Metrics section storing all character + * metric information for the ALL characters (mapped and unmapped) + * into the array. + * + * This function returns an error code specifying whether there was + * a premature EOF or a parsing error. This return value is used by + * parseFile to determine if there is more file to parse. + */ + +static int parseCharMetrics( FileInputStream* fp, register FontInfo* fi) +{ + bool cont = true, firstTime = true; + int error = ok, count = 0, tokenlen; + register CharMetricInfo *temp = fi->cmi; + register char *keyword; + + while (cont) + { + keyword = token(fp,tokenlen); + if (keyword == NULL) + { + error = earlyEOF; + break; /* get out of loop */ + } + switch(recognize(keyword,tokenlen)) + { + case COMMENT: + keyword = linetoken(fp); + break; + case CODE: + if (!(count < fi->numOfChars)) + { + reallocFontMetrics( (void**)&(fi->cmi), + &(fi->numOfChars), enlargeCount(fi->numOfChars), + sizeof(CharMetricInfo) ); + temp = &(fi->cmi[ count - 1 ]); + } + if (count < fi->numOfChars) + { + if (firstTime) firstTime = false; + else temp++; + if ((keyword = token(fp,tokenlen)) != NULL) + temp->code = atoi(keyword); + if (fi->gfi && fi->gfi->charwidth) + temp->wx = fi->gfi->charwidth; + count++; + } + else + { + error = parseError; + cont = false; + } + break; + case CODEHEX: + if (!(count < fi->numOfChars )) + { + reallocFontMetrics( (void**)&(fi->cmi), + &(fi->numOfChars), enlargeCount(fi->numOfChars), + sizeof(CharMetricInfo) ); + temp = &(fi->cmi[ count - 1 ]); + } + if (count < fi->numOfChars) { + if (firstTime) + firstTime = false; + else + temp++; + if ((keyword = token(fp,tokenlen)) != NULL) + sscanf(keyword,"<%x>", &temp->code); + if (fi->gfi && fi->gfi->charwidth) + temp->wx = fi->gfi->charwidth; + count++; + } + else { + error = parseError; + cont = false; + } + break; + case XYWIDTH: + if ((keyword = token(fp,tokenlen)) != NULL) + temp->wx = atoi(keyword); + if ((keyword = token(fp,tokenlen)) != NULL) + temp->wy = atoi(keyword); + break; + case X0WIDTH: + if ((keyword = token(fp,tokenlen)) != NULL) + temp->wx = atoi(keyword); + break; + case XWIDTH: + if ((keyword = token(fp,tokenlen)) != NULL) + temp->wx = atoi(keyword); + break; + case CHARNAME: + if ((keyword = token(fp,tokenlen)) != NULL) + temp->name = (char *)strdup(keyword); + break; + case CHARBBOX: + if ((keyword = token(fp,tokenlen)) != NULL) + temp->charBBox.llx = atoi(keyword); + if ((keyword = token(fp,tokenlen)) != NULL) + temp->charBBox.lly = atoi(keyword); + if ((keyword = token(fp,tokenlen)) != NULL) + temp->charBBox.urx = atoi(keyword); + if ((keyword = token(fp,tokenlen)) != NULL) + temp->charBBox.ury = atoi(keyword); + break; + case LIGATURE: { + Ligature **tail = &(temp->ligs); + Ligature *node = *tail; + + if (*tail != NULL) + { + while (node->next != NULL) + node = node->next; + tail = &(node->next); + } + + *tail = (Ligature *) calloc(1, sizeof(Ligature)); + if ((keyword = token(fp,tokenlen)) != NULL) + (*tail)->succ = (char *)strdup(keyword); + if ((keyword = token(fp,tokenlen)) != NULL) + (*tail)->lig = (char *)strdup(keyword); + break; } + case ENDCHARMETRICS: + cont = false;; + break; + case ENDFONTMETRICS: + cont = false; + error = normalEOF; + break; + case VVECTOR: + keyword = token(fp,tokenlen); + keyword = token(fp,tokenlen); + break; + case NOPE: + default: + error = parseError; + break; + } /* switch */ + } /* while */ + + if ((error == ok) && (count != fi->numOfChars)) + error = reallocFontMetrics( (void**)&(fi->cmi), &(fi->numOfChars), + count, sizeof(CharMetricInfo) ); + + if ((error == ok) && (count != fi->numOfChars)) + error = parseError; + + return(error); + +} /* parseCharMetrics */ + + + +/************************* parseTrackKernData ***********************/ + +/* This function is called by "parseFile". It will parse the AFM file + * up to the "EndTrackKern" or "EndKernData" keywords. It will save the + * track kerning data if requested by the caller of parseFile. + * + * parseTrackKernData is passed in a pointer to the FontInfo record. + * If data is to be saved, the FontInfo record will already contain + * a valid pointer to storage for the track kerning data. + * + * This function returns an error code specifying whether there was + * a premature EOF or a parsing error. This return value is used by + * parseFile to determine if there is more file to parse. + */ + +static int parseTrackKernData( FileInputStream* fp, register FontInfo* fi) +{ + bool cont = true, save = (fi->tkd != NULL); + int pos = 0, error = ok, tcount = 0, tokenlen; + register char *keyword; + + while (cont) + { + keyword = token(fp,tokenlen); + + if (keyword == NULL) + { + error = earlyEOF; + break; /* get out of loop */ + } + if (!save) + /* get tokens until the end of the Track Kerning Data */ + /* section without saving any of the data */ + switch(recognize(keyword,tokenlen)) + { + case ENDTRACKKERN: + case ENDKERNDATA: + cont = false; + break; + case ENDFONTMETRICS: + cont = false; + error = normalEOF; + break; + default: + break; + } /* switch */ + else + /* otherwise parse entire Track Kerning Data section, */ + /* saving the data */ + switch(recognize(keyword,tokenlen)) + { + case COMMENT: + keyword = linetoken(fp); + break; + case TRACKKERN: + if (!(tcount < fi->numOfTracks)) + { + reallocFontMetrics( (void**)&(fi->tkd), &(fi->numOfTracks), + enlargeCount(fi->numOfTracks), sizeof(TrackKernData) ); + } + + if (tcount < fi->numOfTracks) + { + if ((keyword = token(fp,tokenlen)) != NULL) + fi->tkd[pos].degree = atoi(keyword); + if ((keyword = token(fp,tokenlen)) != NULL) + fi->tkd[pos].minPtSize = StringToDouble(keyword); + if ((keyword = token(fp,tokenlen)) != NULL) + fi->tkd[pos].minKernAmt = StringToDouble(keyword); + if ((keyword = token(fp,tokenlen)) != NULL) + fi->tkd[pos].maxPtSize = StringToDouble(keyword); + if ((keyword = token(fp,tokenlen)) != NULL) + fi->tkd[pos++].maxKernAmt = StringToDouble(keyword); + tcount++; + } + else + { + error = parseError; + cont = false; + } + break; + case ENDTRACKKERN: + case ENDKERNDATA: + cont = false; + break; + case ENDFONTMETRICS: + cont = false; + error = normalEOF; + break; + case NOPE: + default: + error = parseError; + break; + } /* switch */ + } /* while */ + + if (error == ok && tcount != fi->numOfTracks) + error = reallocFontMetrics( (void**)&(fi->tkd), &(fi->numOfTracks), + tcount, sizeof(TrackKernData) ); + + if (error == ok && tcount != fi->numOfTracks) + error = parseError; + + return(error); + +} /* parseTrackKernData */ + + +/************************* parsePairKernData ************************/ + +/* This function is called by "parseFile". It will parse the AFM file + * up to the "EndKernPairs" or "EndKernData" keywords. It will save + * the pair kerning data if requested by the caller of parseFile. + * + * parsePairKernData is passed in a pointer to the FontInfo record. + * If data is to be saved, the FontInfo record will already contain + * a valid pointer to storage for the pair kerning data. + * + * This function returns an error code specifying whether there was + * a premature EOF or a parsing error. This return value is used by + * parseFile to determine if there is more file to parse. + */ + +static int parsePairKernData( FileInputStream* fp, register FontInfo* fi) +{ + bool cont = true, save = (fi->pkd != NULL); + int pos = 0, error = ok, pcount = 0, tokenlen; + register char *keyword; + + while (cont) + { + keyword = token(fp,tokenlen); + + if (keyword == NULL) + { + error = earlyEOF; + break; /* get out of loop */ + } + if (!save) + /* get tokens until the end of the Pair Kerning Data */ + /* section without saving any of the data */ + switch(recognize(keyword,tokenlen)) + { + case ENDKERNPAIRS: + case ENDKERNDATA: + cont = false; + break; + case ENDFONTMETRICS: + cont = false; + error = normalEOF; + break; + default: + break; + } /* switch */ + else + /* otherwise parse entire Pair Kerning Data section, */ + /* saving the data */ + switch(recognize(keyword,tokenlen)) + { + case COMMENT: + keyword = linetoken(fp); + break; + case KERNPAIR: + if (!(pcount < fi->numOfPairs)) + { + reallocFontMetrics( (void**)&(fi->pkd), &(fi->numOfPairs), + enlargeCount(fi->numOfPairs), sizeof(PairKernData) ); + } + if (pcount < fi->numOfPairs) + { + if ((keyword = token(fp,tokenlen)) != NULL) + fi->pkd[pos].name1 = strdup( keyword ); + if ((keyword = token(fp,tokenlen)) != NULL) + fi->pkd[pos].name2 = strdup( keyword ); + if ((keyword = token(fp,tokenlen)) != NULL) + fi->pkd[pos].xamt = atoi(keyword); + if ((keyword = token(fp,tokenlen)) != NULL) + fi->pkd[pos++].yamt = atoi(keyword); + pcount++; + } + else + { + error = parseError; + cont = false; + } + break; + case KERNPAIRXAMT: + if (!(pcount < fi->numOfPairs)) + { + reallocFontMetrics( (void**)&(fi->pkd), &(fi->numOfPairs), + enlargeCount(fi->numOfPairs), sizeof(PairKernData) ); + } + if (pcount < fi->numOfPairs) + { + if ((keyword = token(fp,tokenlen)) != NULL) + fi->pkd[pos].name1 = strdup( keyword ); + if ((keyword = token(fp,tokenlen)) != NULL) + fi->pkd[pos].name2 = strdup( keyword ); + if ((keyword = token(fp,tokenlen)) != NULL) + fi->pkd[pos++].xamt = atoi(keyword); + pcount++; + } + else + { + error = parseError; + cont = false; + } + break; + case ENDKERNPAIRS: + case ENDKERNDATA: + cont = false; + break; + case ENDFONTMETRICS: + cont = false; + error = normalEOF; + break; + case NOPE: + default: + error = parseError; + break; + } /* switch */ + } /* while */ + + if ((error == ok) && (pcount != fi->numOfPairs)) + error = reallocFontMetrics( (void**)&(fi->pkd), &(fi->numOfPairs), + pcount, sizeof(PairKernData) ); + + if (error == ok && pcount != fi->numOfPairs) + error = parseError; + + return(error); + +} /* parsePairKernData */ + + +/************************* parseCompCharData **************************/ + +/* This function is called by "parseFile". It will parse the AFM file + * up to the "EndComposites" keyword. It will save the composite + * character data if requested by the caller of parseFile. + * + * parseCompCharData is passed in a pointer to the FontInfo record, and + * a boolean representing if the data should be saved. + * + * This function will create the appropriate amount of storage for + * the composite character data and store a pointer to the storage + * in the FontInfo record. + * + * This function returns an error code specifying whether there was + * a premature EOF or a parsing error. This return value is used by + * parseFile to determine if there is more file to parse. + */ + +static int parseCompCharData( FileInputStream* fp, register FontInfo* fi) +{ + bool cont = true, firstTime = true, save = (fi->ccd != NULL); + int pos = 0, j = 0, error = ok, ccount = 0, pcount = 0, tokenlen; + register char *keyword; + + while (cont) + { + keyword = token(fp,tokenlen); + if (keyword == NULL) + /* Have reached an early and unexpected EOF. */ + /* Set flag and stop parsing */ + { + error = earlyEOF; + break; /* get out of loop */ + } + if (ccount > fi->numOfComps) + { + reallocFontMetrics( (void**)&(fi->ccd), &(fi->numOfComps), + enlargeCount(fi->numOfComps), sizeof(CompCharData) ); + } + if (ccount > fi->numOfComps) + { + error = parseError; + break; /* get out of loop */ + } + if (!save) + /* get tokens until the end of the Composite Character info */ + /* section without saving any of the data */ + switch(recognize(keyword,tokenlen)) + { + case ENDCOMPOSITES: + cont = false; + break; + case ENDFONTMETRICS: + cont = false; + error = normalEOF; + break; + case COMMENT: + case COMPCHAR: + keyword = linetoken(fp); + break; + default: + break; + } /* switch */ + else + /* otherwise parse entire Composite Character info section, */ + /* saving the data */ + switch(recognize(keyword,tokenlen)) + { + case COMMENT: + keyword = linetoken(fp); + break; + case COMPCHAR: + if (!(ccount < fi->numOfComps)) + { + reallocFontMetrics( (void**)&(fi->ccd), &(fi->numOfComps), + enlargeCount(fi->numOfComps), sizeof(CompCharData) ); + } + if (ccount < fi->numOfComps) + { + keyword = token(fp,tokenlen); + if (pcount != fi->ccd[pos].numOfPieces) + error = parseError; + pcount = 0; + if (firstTime) firstTime = false; + else pos++; + fi->ccd[pos].ccName = strdup( keyword ); + if ((keyword = token(fp,tokenlen)) != NULL) + fi->ccd[pos].numOfPieces = atoi(keyword); + fi->ccd[pos].pieces = (Pcc *) + calloc(fi->ccd[pos].numOfPieces, sizeof(Pcc)); + j = 0; + ccount++; + } + else + { + error = parseError; + cont = false; + } + break; + case COMPCHARPIECE: + if (pcount < fi->ccd[pos].numOfPieces) + { + if ((keyword = token(fp,tokenlen)) != NULL) + fi->ccd[pos].pieces[j].pccName = strdup( keyword ); + if ((keyword = token(fp,tokenlen)) != NULL) + fi->ccd[pos].pieces[j].deltax = atoi(keyword); + if ((keyword = token(fp,tokenlen)) != NULL) + fi->ccd[pos].pieces[j++].deltay = atoi(keyword); + pcount++; + } + else + error = parseError; + break; + case ENDCOMPOSITES: + cont = false; + break; + case ENDFONTMETRICS: + cont = false; + error = normalEOF; + break; + case NOPE: + default: + error = parseError; + break; + } /* switch */ + } /* while */ + + if (error == ok && ccount != fi->numOfComps) + reallocFontMetrics( (void**)&(fi->ccd), &(fi->numOfComps), + ccount, sizeof(CompCharData) ); + + if (error == ok && ccount != fi->numOfComps) + error = parseError; + + return(error); + +} /* parseCompCharData */ + + + + +/*************************** 'PUBLIC' FUNCTION ********************/ + + +/*************************** parseFile *****************************/ + +/* parseFile is the only 'public' procedure available. It is called + * from an application wishing to get information from an AFM file. + * The caller of this function is responsible for locating and opening + * an AFM file and handling all errors associated with that task. + * + * parseFile expects 3 parameters: a filename pointer, a pointer + * to a (FontInfo *) variable (for which storage will be allocated and + * the data requested filled in), and a mask specifying which + * data from the AFM file should be saved in the FontInfo structure. + * + * The file will be parsed and the requested data will be stored in + * a record of type FontInfo (refer to ParseAFM.h). + * + * parseFile returns an error code as defined in parseAFM.h. + * + * The position of the read/write pointer associated with the file + * pointer upon return of this function is undefined. + */ + +int parseFile( const char* pFilename, FontInfo** fi, FLAGS flags) +{ + FileInputStream aFile( pFilename ); + + int code = ok; /* return code from each of the parsing routines */ + int error = ok; /* used as the return code from this function */ + int tokenlen; + + register char *keyword; /* used to store a token */ + + + (*fi) = (FontInfo *) calloc(1, sizeof(FontInfo)); + if ((*fi) == NULL) {error = storageProblem; return(error);} + + if (flags & P_G) + { + (*fi)->gfi = (GlobalFontInfo *) calloc(1, sizeof(GlobalFontInfo)); + if ((*fi)->gfi == NULL) {error = storageProblem; return(error);} + } + + /* The AFM file begins with Global Font Information. This section */ + /* will be parsed whether or not information should be saved. */ + code = parseGlobals(&aFile, (*fi)->gfi); + + if (code < 0) error = code; + + /* The Global Font Information is followed by the Character Metrics */ + /* section. Which procedure is used to parse this section depends on */ + /* how much information should be saved. If all of the metrics info */ + /* is wanted, parseCharMetrics is called. If only the character widths */ + /* is wanted, parseCharWidths is called. parseCharWidths will also */ + /* be called in the case that no character data is to be saved, just */ + /* to parse through the section. */ + + if ((code != normalEOF) && (code != earlyEOF)) + { + if ((keyword = token(&aFile,tokenlen)) != NULL) + (*fi)->numOfChars = atoi(keyword); + if (flags & (P_M ^ P_W)) + { + (*fi)->cmi = (CharMetricInfo *) + calloc((*fi)->numOfChars, sizeof(CharMetricInfo)); + if ((*fi)->cmi == NULL) {error = storageProblem; return(error);} + code = parseCharMetrics(&aFile, *fi); + } + else + { + if (flags & P_W) + { + (*fi)->cwi = (int *) calloc(256, sizeof(int)); + if ((*fi)->cwi == NULL) + { + error = storageProblem; + return(error); + } + } + /* parse section regardless */ + code = parseCharWidths(&aFile, (*fi)->cwi); + } /* else */ + } /* if */ + + if ((error != earlyEOF) && (code < 0)) error = code; + + /* The remaining sections of the AFM are optional. This code will */ + /* look at the next keyword in the file to determine what section */ + /* is next, and then allocate the appropriate amount of storage */ + /* for the data (if the data is to be saved) and call the */ + /* appropriate parsing routine to parse the section. */ + + while ((code != normalEOF) && (code != earlyEOF)) + { + keyword = token(&aFile,tokenlen); + if (keyword == NULL) + /* Have reached an early and unexpected EOF. */ + /* Set flag and stop parsing */ + { + code = earlyEOF; + break; /* get out of loop */ + } + switch(recognize(keyword,tokenlen)) + { + case STARTKERNDATA: + break; + case ENDKERNDATA: + break; + case STARTTRACKKERN: + keyword = token(&aFile,tokenlen); + if ((flags & P_T) && keyword) + { + (*fi)->numOfTracks = atoi(keyword); + (*fi)->tkd = (TrackKernData *) + calloc((*fi)->numOfTracks, sizeof(TrackKernData)); + if ((*fi)->tkd == NULL) + { + error = storageProblem; + return(error); + } + } /* if */ + code = parseTrackKernData(&aFile, *fi); + break; + case STARTKERNPAIRS: + keyword = token(&aFile,tokenlen); + if ((flags & P_P) && keyword) + { + (*fi)->numOfPairs = atoi(keyword); + (*fi)->pkd = (PairKernData *) + calloc((*fi)->numOfPairs, sizeof(PairKernData)); + if ((*fi)->pkd == NULL) + { + error = storageProblem; + return(error); + } + } /* if */ + code = parsePairKernData(&aFile, *fi); + break; + case STARTCOMPOSITES: + keyword = token(&aFile,tokenlen); + if ((flags & P_C) && keyword) + { + (*fi)->numOfComps = atoi(keyword); + (*fi)->ccd = (CompCharData *) + calloc((*fi)->numOfComps, sizeof(CompCharData)); + if ((*fi)->ccd == NULL) + { + error = storageProblem; + return(error); + } + } /* if */ + code = parseCompCharData(&aFile, *fi); + break; + case ENDFONTMETRICS: + code = normalEOF; + break; + case COMMENT: + linetoken(&aFile); + break; + case NOPE: + default: + code = parseError; + break; + } /* switch */ + + if ((error != earlyEOF) && (code < 0)) error = code; + + } /* while */ + + if ((error != earlyEOF) && (code < 0)) error = code; + + return(error); + +} /* parseFile */ + +void +freeFontInfo (FontInfo *fi) +{ + int i; + + if (fi->gfi) + { + free (fi->gfi->afmVersion); + free (fi->gfi->fontName); + free (fi->gfi->fullName); + free (fi->gfi->familyName); + free (fi->gfi->weight); + free (fi->gfi->version); + free (fi->gfi->notice); + free (fi->gfi->encodingScheme); + free (fi->gfi); + } + + free (fi->cwi); + + if (fi->cmi) + { + for (i = 0; i < fi->numOfChars; i++) + { + Ligature *ligs; + free (fi->cmi[i].name); + ligs = fi->cmi[i].ligs; + while (ligs) + { + Ligature *tmp; + tmp = ligs; + ligs = ligs->next; + free (tmp->succ); + free (tmp->lig); + free (tmp); + } + } + free (fi->cmi); + } + + free (fi->tkd); + + if (fi->pkd) + { + for ( i = 0; i < fi->numOfPairs; i++) + { + free (fi->pkd[i].name1); + free (fi->pkd[i].name2); + } + free (fi->pkd); + } + + if (fi->ccd) + { + for (i = 0; i < fi->numOfComps; i++) + { + free (fi->ccd[i].ccName); + int j; + for (j = 0; j < fi->ccd[i].numOfPieces; j++) + free (fi->ccd[i].pieces[j].pccName); + + free (fi->ccd[i].pieces); + } + free (fi->ccd); + } + + free (fi); +} + +} // namspace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/fontmanager/parseAFM.hxx b/vcl/unx/generic/fontmanager/parseAFM.hxx new file mode 100644 index 000000000000..0c390bd8cf8d --- /dev/null +++ b/vcl/unx/generic/fontmanager/parseAFM.hxx @@ -0,0 +1,337 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * (C) 1988, 1989 by Adobe Systems Incorporated. All rights reserved. + * + * This file may be freely copied and redistributed as long as: + * 1) This entire notice continues to be included in the file, + * 2) If the file has been modified in any way, a notice of such + * modification is conspicuously indicated. + * + * PostScript, Display PostScript, and Adobe are registered trademarks of + * Adobe Systems Incorporated. + * + * ************************************************************************ + * THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO CHANGE WITHOUT + * NOTICE, AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY ADOBE SYSTEMS + * INCORPORATED. ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY OR + * LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO WARRANTY OF ANY + * KIND (EXPRESS, IMPLIED OR STATUTORY) WITH RESPECT TO THIS INFORMATION, + * AND EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + * ************************************************************************ + */ + +/* + * Changes made for OpenOffice.org + * + * 10/24/2000 pl - changed code to compile with c++-compilers + * - added namespace to avoid symbol clashes + * - replaced BOOL by bool + * - added function to free space allocated by parseFile + * 10/26/2000 pl - added additional keys + * - added ability to parse slightly broken files + * - added charwidth member to GlobalFontInfo + * 04/26/2001 pl - added OpenOffice header + * 10/19/2005 pl - changed parseFile to accept a file name instead of a stream + */ + +/* ParseAFM.h + * + * This header file is used in conjuction with the parseAFM.c file. + * Together these files provide the functionality to parse Adobe Font + * Metrics files and store the information in predefined data structures. + * It is intended to work with an application program that needs font metric + * information. The program can be used as is by making a procedure call to + * parse an AFM file and have the data stored, or an application developer + * may wish to customize the code. + * + * This header file defines the data structures used as well as the key + * strings that are currently recognized by this version of the AFM parser. + * This program is based on the document "Adobe Font Metrics Files, + * Specification Version 2.0". + * + * AFM files are separated into distinct sections of different data. Because + * of this, the parseAFM program can parse a specified file to only save + * certain sections of information based on the application's needs. A record + * containing the requested information will be returned to the application. + * + * AFM files are divided into five sections of data: + * 1) The Global Font Information + * 2) The Character Metrics Information + * 3) The Track Kerning Data + * 4) The Pair-Wise Kerning Data + * 5) The Composite Character Data + * + * Basically, the application can request any of these sections independent + * of what other sections are requested. In addition, in recognizing that + * many applications will want ONLY the x-width of characters and not all + * of the other character metrics information, there is a way to receive + * only the width information so as not to pay the storage cost for the + * unwanted data. An application should never request both the + * "quick and dirty" char metrics (widths only) and the Character Metrics + * Information since the Character Metrics Information will contain all + * of the character widths as well. + * + * There is a procedure in parseAFM.c, called parseFile, that can be + * called from any application wishing to get information from the AFM File. + * This procedure expects 3 parameters: a vaild file descriptor, a pointer + * to a (FontInfo *) variable (for which space will be allocated and then + * will be filled in with the data requested), and a mask specifying + * which data from the AFM File should be saved in the FontInfo structure. + * + * The flags that can be used to set the appropriate mask are defined below. + * In addition, several commonly used masks have already been defined. + * + * History: + * original: DSM Thu Oct 20 17:39:59 PDT 1988 + * modified: DSM Mon Jul 3 14:17:50 PDT 1989 + * - added 'storageProblem' return code + * - fixed typos + */ + +#include <stdio.h> + +namespace psp { + +/* your basic constants */ +#define EOL '\n' /* end-of-line indicator */ +#define MAX_NAME 4096 /* max length for identifiers */ +#define FLAGS int + + + +/* Flags that can be AND'ed together to specify exactly what + * information from the AFM file should be saved. + */ +#define P_G 0x01 /* 0000 0001 */ /* Global Font Info */ +#define P_W 0x02 /* 0000 0010 */ /* Character Widths ONLY */ +#define P_M 0x06 /* 0000 0110 */ /* All Char Metric Info */ +#define P_P 0x08 /* 0000 1000 */ /* Pair Kerning Info */ +#define P_T 0x10 /* 0001 0000 */ /* Track Kerning Info */ +#define P_C 0x20 /* 0010 0000 */ /* Composite Char Info */ + + +/* Commonly used flags + */ +#define P_GW (P_G | P_W) +#define P_GM (P_G | P_M) +#define P_GMP (P_G | P_M | P_P) +#define P_GMK (P_G | P_M | P_P | P_T) +#define P_ALL (P_G | P_M | P_P | P_T | P_C) + + + +/* Possible return codes from the parseFile procedure. + * + * ok means there were no problems parsing the file. + * + * parseError means that there was some kind of parsing error, but the + * parser went on. This could include problems like the count for any given + * section does not add up to how many entries there actually were, or + * there was a key that was not recognized. The return record may contain + * vaild data or it may not. + * + * earlyEOF means that an End of File was encountered before expected. This + * may mean that the AFM file had been truncated, or improperly formed. + * + * storageProblem means that there were problems allocating storage for + * the data structures that would have contained the AFM data. + */ + +enum afmError { ok = 0, parseError = -1, earlyEOF = -2, storageProblem = -3 }; + + +/************************* TYPES *********************************/ +/* Below are all of the data structure definitions. These structures + * try to map as closely as possible to grouping and naming of data + * in the AFM Files. + */ + + +/* Bounding box definition. Used for the Font BBox as well as the + * Character BBox. + */ +typedef struct +{ + int llx; /* lower left x-position */ + int lly; /* lower left y-position */ + int urx; /* upper right x-position */ + int ury; /* upper right y-position */ +} BBox; + + +/* Global Font information. + * The key that each field is associated with is in comments. For an + * explanation about each key and its value please refer to the AFM + * documentation (full title & version given above). + */ +typedef struct +{ + char *afmVersion; /* key: StartFontMetrics */ + char *fontName; /* key: FontName */ + char *fullName; /* key: FullName */ + char *familyName; /* key: FamilyName */ + char *weight; /* key: Weight */ + float italicAngle; /* key: ItalicAngle */ + bool isFixedPitch; /* key: IsFixedPitch */ + BBox fontBBox; /* key: FontBBox */ + int underlinePosition; /* key: UnderlinePosition */ + int underlineThickness; /* key: UnderlineThickness */ + char *version; /* key: Version */ + char *notice; /* key: Notice */ + char *encodingScheme; /* key: EncodingScheme */ + int capHeight; /* key: CapHeight */ + int xHeight; /* key: XHeight */ + int ascender; /* key: Ascender */ + int descender; /* key: Descender */ + int charwidth; /* key: CharWidth */ +} GlobalFontInfo; + + +/* Ligature definition is a linked list since any character can have + * any number of ligatures. + */ +typedef struct _t_ligature +{ + char *succ, *lig; + struct _t_ligature *next; +} Ligature; + + +/* Character Metric Information. This structure is used only if ALL + * character metric information is requested. If only the character + * widths is requested, then only an array of the character x-widths + * is returned. + * + * The key that each field is associated with is in comments. For an + * explanation about each key and its value please refer to the + * Character Metrics section of the AFM documentation (full title + * & version given above). + */ +typedef struct +{ + int code, /* key: C */ + wx, /* key: WX */ + w0x, /* key: W0X */ + wy; /* together wx and wy are associated with key: W */ + char *name; /* key: N */ + BBox charBBox; /* key: B */ + Ligature *ligs; /* key: L (linked list; not a fixed number of Ls */ +} CharMetricInfo; + + +/* Track kerning data structure. + * The fields of this record are the five values associated with every + * TrackKern entry. + * + * For an explanation about each value please refer to the + * Track Kerning section of the AFM documentation (full title + * & version given above). + */ +typedef struct +{ + int degree; + float minPtSize, + minKernAmt, + maxPtSize, + maxKernAmt; +} TrackKernData; + + +/* Pair Kerning data structure. + * The fields of this record are the four values associated with every + * KP entry. For KPX entries, the yamt will be zero. + * + * For an explanation about each value please refer to the + * Pair Kerning section of the AFM documentation (full title + * & version given above). + */ +typedef struct +{ + char *name1; + char *name2; + int xamt, + yamt; +} PairKernData; + + +/* PCC is a piece of a composite character. This is a sub structure of a + * compCharData described below. + * These fields will be filled in with the values from the key PCC. + * + * For an explanation about each key and its value please refer to the + * Composite Character section of the AFM documentation (full title + * & version given above). + */ +typedef struct +{ + char *pccName; + int deltax, + deltay; +} Pcc; + + +/* Composite Character Information data structure. + * The fields ccName and numOfPieces are filled with the values associated + * with the key CC. The field pieces points to an array (size = numOfPieces) + * of information about each of the parts of the composite character. That + * array is filled in with the values from the key PCC. + * + * For an explanation about each key and its value please refer to the + * Composite Character section of the AFM documentation (full title + * & version given above). + */ +typedef struct +{ + char *ccName; + int numOfPieces; + Pcc *pieces; +} CompCharData; + + +/* FontInfo + * Record type containing pointers to all of the other data + * structures containing information about a font. + * A a record of this type is filled with data by the + * parseFile function. + */ +typedef struct +{ + GlobalFontInfo *gfi; /* ptr to a GlobalFontInfo record */ + int *cwi; /* ptr to 256 element array of just char widths */ + int numOfChars; /* number of entries in char metrics array */ + CharMetricInfo *cmi; /* ptr to char metrics array */ + int numOfTracks; /* number to entries in track kerning array */ + TrackKernData *tkd; /* ptr to track kerning array */ + int numOfPairs; /* number to entries in pair kerning array */ + PairKernData *pkd; /* ptr to pair kerning array */ + int numOfComps; /* number to entries in comp char array */ + CompCharData *ccd; /* ptr to comp char array */ +} FontInfo; + + + +/************************* PROCEDURES ****************************/ + +/* Call this procedure to do the grunt work of parsing an AFM file. + * + * "fp" should be a valid file pointer to an AFM file. + * + * "fi" is a pointer to a pointer to a FontInfo record sturcture + * (defined above). Storage for the FontInfo structure will be + * allocated in parseFile and the structure will be filled in + * with the requested data from the AFM File. + * + * "flags" is a mask with bits set representing what data should + * be saved. Defined above are valid flags that can be used to set + * the mask, as well as a few commonly used masks. + * + * The possible return codes from parseFile are defined above. + */ + +int parseFile( const char* pFilename, FontInfo **fi, FLAGS flags ); +void freeFontInfo(FontInfo *fi); + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/gdi/cdeint.cxx b/vcl/unx/generic/gdi/cdeint.cxx new file mode 100644 index 000000000000..a6a37a178c48 --- /dev/null +++ b/vcl/unx/generic/gdi/cdeint.cxx @@ -0,0 +1,247 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <stdlib.h> +#include <ctype.h> +#include <unistd.h> + +#include <tools/stream.hxx> +#include <tools/debug.hxx> + +#include <vcl/settings.hxx> + +#include <unx/salunx.h> +#include <unx/saldisp.hxx> +#include <unx/cdeint.hxx> + +CDEIntegrator::CDEIntegrator() +{ + meType = DtCDE; +} + +CDEIntegrator::~CDEIntegrator() +{ +} + +static int getHexDigit( const char c ) +{ + if( c >= '0' && c <= '9' ) + return (int)(c-'0'); + else if( c >= 'a' && c <= 'f' ) + return (int)(c-'a'+10); + else if( c >= 'A' && c <= 'F' ) + return (int)(c-'A'+10); + return -1; +} + + +void CDEIntegrator::GetSystemLook( AllSettings& rSettings ) +{ + static Color aColors[ 8 ]; + static sal_Bool bRead = sal_False; + static sal_Bool bValid = sal_False; + + if( ! bRead ) + { + // get used palette from xrdb + char **ppStringList = 0; + int nStringCount; + XTextProperty aTextProperty; + aTextProperty.value = 0; + + static Atom nResMgrAtom = XInternAtom( mpDisplay, "RESOURCE_MANAGER", False ); + + if( XGetTextProperty( mpDisplay, + RootWindow( mpDisplay, 0 ), + &aTextProperty, + nResMgrAtom ) + && aTextProperty.value + && XTextPropertyToStringList( &aTextProperty, &ppStringList, &nStringCount ) + ) + { + // format of ColorPalette resource: + // *n*ColorPalette: palettefile + + ByteString aLines; + int i; + for( i=0; i < nStringCount; i++ ) + aLines += ppStringList[i]; + for( i = aLines.GetTokenCount( '\n' )-1; i >= 0; i-- ) + { + ByteString aLine = aLines.GetToken( i, '\n' ); + int nIndex = aLine.Search( "ColorPalette" ); + if( nIndex != STRING_NOTFOUND ) + { + int nPos = nIndex; + + nIndex+=12; + const char* pStr = aLine.GetBuffer() +nIndex; + while( *pStr && isspace( *pStr ) && *pStr != ':' ) + { + pStr++; + nIndex++; + } + if( *pStr != ':' ) + continue; + pStr++, nIndex++; + for( ; *pStr && isspace( *pStr ); pStr++, nIndex++ ) + ; + if( ! *pStr ) + continue; + int nIndex2 = nIndex; + for( ; *pStr && ! isspace( *pStr ); pStr++, nIndex2++ ) + ; + ByteString aPaletteFile( aLine.Copy( nIndex, nIndex2 - nIndex ) ); + // extract number before ColorPalette; + for( ; nPos >= 0 && aLine.GetChar( nPos ) != '*'; nPos-- ) + ; + nPos--; + for( ; nPos >= 0 && aLine.GetChar( nPos ) != '*'; nPos-- ) + ; + int nNumber = aLine.Copy( ++nPos ).ToInt32(); + + OSL_TRACE( "found palette %d in resource \"%s\"", nNumber, aLine.GetBuffer() ); + + // found no documentation what this number actually means; + // might be the screen number. 0 seems to be the right one + // in most cases. + if( nNumber ) + continue; + + OSL_TRACE( "Palette file is \"%s\".\n", aPaletteFile.GetBuffer() ); + + String aPath( aHomeDir ); + aPath.AppendAscii( "/.dt/palettes/" ); + aPath += String( aPaletteFile, gsl_getSystemTextEncoding() ); + + SvFileStream aStream( aPath, STREAM_READ ); + if( ! aStream.IsOpen() ) + { + aPath = String::CreateFromAscii( "/usr/dt/palettes/" ); + aPath += String( aPaletteFile, gsl_getSystemTextEncoding() ); + aStream.Open( aPath, STREAM_READ ); + if( ! aStream.IsOpen() ) + continue; + } + + ByteString aBuffer; + for( nIndex = 0; nIndex < 8; nIndex++ ) + { + aStream.ReadLine( aBuffer ); + // format is "#RRRRGGGGBBBB" + + OSL_TRACE( "\t\"%s\".\n", aBuffer.GetBuffer() ); + + if( aBuffer.Len() ) + { + const char* pArr = (const char*)aBuffer.GetBuffer()+1; + aColors[nIndex] = Color( + getHexDigit( pArr[1] ) + | ( getHexDigit( pArr[0] ) << 4 ), + getHexDigit( pArr[5] ) + | ( getHexDigit( pArr[4] ) << 4 ), + getHexDigit( pArr[9] ) + | ( getHexDigit( pArr[8] ) << 4 ) + ); + + OSL_TRACE( "\t\t%lx\n", aColors[nIndex].GetColor() ); + } + } + + bValid = sal_True; + break; + } + } + } + + if( ppStringList ) + XFreeStringList( ppStringList ); + if( aTextProperty.value ) + XFree( aTextProperty.value ); + } + + + StyleSettings aStyleSettings = rSettings.GetStyleSettings(); + // #i48001# set a default blink rate + aStyleSettings.SetCursorBlinkTime( 500 ); + if (bValid) + { + aStyleSettings.SetActiveColor( aColors[0] ); + aStyleSettings.SetActiveColor2( aColors[0] ); + aStyleSettings.SetActiveBorderColor( aColors[0] ); + + aStyleSettings.SetDeactiveColor( aColors[0] ); + aStyleSettings.SetDeactiveColor2( aColors[0] ); + aStyleSettings.SetDeactiveBorderColor( aColors[0] ); + + Color aActive = + aColors[ 0 ].GetBlue() < 128 || + aColors[ 0 ].GetGreen() < 128 || + aColors[ 0 ].GetRed() < 128 + ? Color( COL_WHITE ) : Color( COL_BLACK ); + Color aDeactive = + aColors[ 1 ].GetBlue() < 128 || + aColors[ 1 ].GetGreen() < 128 || + aColors[ 1 ].GetRed() < 128 + ? Color( COL_WHITE ) : Color( COL_BLACK ); + aStyleSettings.SetActiveTextColor( aActive ); + aStyleSettings.SetDeactiveTextColor( aDeactive ); + + aStyleSettings.SetDialogTextColor( aDeactive ); + aStyleSettings.SetMenuTextColor( aDeactive ); + aStyleSettings.SetMenuBarTextColor( aDeactive ); + aStyleSettings.SetButtonTextColor( aDeactive ); + aStyleSettings.SetRadioCheckTextColor( aDeactive ); + aStyleSettings.SetGroupTextColor( aDeactive ); + aStyleSettings.SetLabelTextColor( aDeactive ); + aStyleSettings.SetInfoTextColor( aDeactive ); + + aStyleSettings.Set3DColors( aColors[1] ); + aStyleSettings.SetFaceColor( aColors[1] ); + aStyleSettings.SetDialogColor( aColors[1] ); + aStyleSettings.SetMenuColor( aColors[1] ); + aStyleSettings.SetMenuBarColor( aColors[1] ); + if ( aStyleSettings.GetFaceColor() == COL_LIGHTGRAY ) + aStyleSettings.SetCheckedColor( Color( 0xCC, 0xCC, 0xCC ) ); + else + { + // calculate Checked color + Color aColor2 = aStyleSettings.GetLightColor(); + sal_uInt8 nRed = (sal_uInt8)(((sal_uInt16)aColors[1].GetRed() + (sal_uInt16)aColor2.GetRed())/2); + sal_uInt8 nGreen = (sal_uInt8)(((sal_uInt16)aColors[1].GetGreen() + (sal_uInt16)aColor2.GetGreen())/2); + sal_uInt8 nBlue = (sal_uInt8)(((sal_uInt16)aColors[1].GetBlue() + (sal_uInt16)aColor2.GetBlue())/2); + aStyleSettings.SetCheckedColor( Color( nRed, nGreen, nBlue ) ); + } + } + rSettings.SetStyleSettings( aStyleSettings ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/gdi/dtint.cxx b/vcl/unx/generic/gdi/dtint.cxx new file mode 100644 index 000000000000..df1826df90f5 --- /dev/null +++ b/vcl/unx/generic/gdi/dtint.cxx @@ -0,0 +1,143 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <dlfcn.h> + +#include "osl/file.h" +#include "osl/process.h" +#include "osl/security.h" + +#include "vcl/svapp.hxx" + +#include "unx/salunx.h" +#include <X11/Xatom.h> +#ifdef USE_CDE +#include "unx/cdeint.hxx" +#endif +#include "unx/dtint.hxx" +#include "unx/saldisp.hxx" +#include "unx/saldata.hxx" +#include "unx/wmadaptor.hxx" + +#include "dtsetenum.hxx" + +#include <set> +#include <stdio.h> + +// NETBSD has no RTLD_GLOBAL +#ifndef RTLD_GLOBAL +#define DLOPEN_MODE (RTLD_LAZY) +#else +#define DLOPEN_MODE (RTLD_GLOBAL | RTLD_LAZY) +#endif + + +using namespace vcl_sal; + +using ::rtl::OUString; + +String DtIntegrator::aHomeDir; + +DtIntegrator::DtIntegrator() : + meType( DtGeneric ), + mnSystemLookCommandProcess( -1 ) +{ + mpSalDisplay = GetX11SalData()->GetDisplay(); + mpDisplay = mpSalDisplay->GetDisplay(); + OUString aDir; + oslSecurity aCur = osl_getCurrentSecurity(); + if( aCur ) + { + osl_getHomeDir( aCur, &aDir.pData ); + osl_freeSecurityHandle( aCur ); + OUString aSysDir; + osl_getSystemPathFromFileURL( aDir.pData, &aSysDir.pData ); + aHomeDir = aSysDir; + } +} + +DtIntegrator::~DtIntegrator() +{ +} + +DtIntegrator* DtIntegrator::CreateDtIntegrator() +{ + /* + * #i22061# override desktop detection + * if environment variable OOO_FORCE_DESKTOP is set + * to one of "cde" "kde" "gnome" then autodetection + * is overridden. + */ + static const char* pOverride = getenv( "OOO_FORCE_DESKTOP" ); + if( pOverride && *pOverride ) + { + OString aOver( pOverride ); + +#if USE_CDE + if( aOver.equalsIgnoreAsciiCase( "cde" ) ) + return new CDEIntegrator(); +#endif + if( aOver.equalsIgnoreAsciiCase( "none" ) ) + return new DtIntegrator(); + } + +#ifdef USE_CDE + void* pLibrary = NULL; + + // check dt type + // CDE + SalDisplay* pSalDisplay = GetX11SalData()->GetDisplay(); + Display* pDisplay = pSalDisplay->GetDisplay(); + Atom nDtAtom = XInternAtom( pDisplay, "_DT_WM_READY", True ); + if( nDtAtom && ( pLibrary = dlopen( "/usr/dt/lib/libDtSvc.so", DLOPEN_MODE ) ) ) + { + dlclose( pLibrary ); + return new CDEIntegrator(); + } +#endif + + // default: generic implementation + return new DtIntegrator(); +} + +void DtIntegrator::GetSystemLook( AllSettings& rSettings ) +{ + // #i48001# set a default blink rate + StyleSettings aStyleSettings = rSettings.GetStyleSettings(); + aStyleSettings.SetCursorBlinkTime( 500 ); + rSettings.SetStyleSettings( aStyleSettings ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/gdi/dtsetenum.hxx b/vcl/unx/generic/gdi/dtsetenum.hxx new file mode 100644 index 000000000000..7973758d9f0d --- /dev/null +++ b/vcl/unx/generic/gdi/dtsetenum.hxx @@ -0,0 +1,149 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _VCL_DTSETENUM_HXX +#define _VCL_DTSETENUM_HXX + +enum DtSetEnum +{ + /* settings for mouse */ + MouseOptions = 1, + DoubleClickTime, + DoubleClickWidth, + DoubleClickHeight, + StartDragWidth, + StartDragHeight, + DragMoveCode, + DragCopyCode, + DragLinkCode, + ContextMenuCode, + ContextMenuClicks, + ContextMenuDown, + ScrollRepeat, + ButtonStartRepeat, + ButtonRepeat, + ActionDelay, + MenuDelay, + Follow, + MiddleButtonAction, + /* settings for keyboard */ + KeyboardOptions=64, + /* style settings */ + StyleOptions = 128, + BorderSize, + TitleHeight, + FloatTitleHeight, + TearOffTitleHeight, + MenuBarHeight, + ScrollBarSize, + SpinSize, + SplitSize, + IconHorzSpace, + IconVertSpace, + CursorSize, + CursorBlinkTime, + ScreenZoom, + ScreenFontZoom, + LogoDisplayTime, + DragFullOptions, + AnimationOptions, + SelectionOptions, + DisplayOptions, + AntialiasingMinPixelHeight, + /* style colors */ + AllTextColors, /* convenience, sets all control text colors */ + AllBackColors, /* convenience, sets all control background colors */ + ThreeDColor, + FaceColor, + CheckedColor, + LightColor, + LightBorderColor, + ShadowColor, + DarkShadowColor, + ButtonTextColor, + RadioCheckTextColor, + GroupTextColor, + LabelTextColor, + InfoTextColor, + WindowColor, + WindowTextColor, + DialogColor, + DialogTextColor, + WorkspaceColor, + FieldColor, + FieldTextColor, + ActiveColor, + ActiveColor2, + ActiveTextColor, + ActiveBorderColor, + DeactiveColor, + DeactiveColor2, + DeactiveTextColor, + DeactiveBorderColor, + HighlightColor, + HighlightTextColor, + DisableColor, + HelpColor, + HelpTextColor, + MenuColor, + MenuBarColor, + MenuTextColor, + MenuHighlightColor, + MenuHighlightTextColor, + LinkColor, + VisitedLinkColor, + HighlightLinkColor, + HighContrastMode, + /* style fonts */ + UIFont, /* convenience, sets all fonts but TitleFont and FloatTitleFont */ + AppFont, + HelpFont, + TitleFont, + FloatTitleFont, + MenuFont, + ToolFont, + GroupFont, + LabelFont, + InfoFont, + RadioCheckFont, + PushButtonFont, + FieldFont, + IconFont, + /* style numeric styles */ + RadioButtonStyle, + CheckBoxStyle, + PushButtonStyle, + TabControlStyle, + /* toolbar style */ + ToolbarIconSize + +}; + +#endif // _VCL_DTSETENUM_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/gdi/gcach_xpeer.cxx b/vcl/unx/generic/gdi/gcach_xpeer.cxx new file mode 100644 index 000000000000..9da147dbec9c --- /dev/null +++ b/vcl/unx/generic/gdi/gcach_xpeer.cxx @@ -0,0 +1,685 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "rtl/ustring.hxx" +#include "osl/module.h" +#include "osl/thread.h" + +#include "unx/saldisp.hxx" +#include "unx/saldata.hxx" +#include "unx/salgdi.h" + +#include "gcach_xpeer.hxx" +#include "xrender_peer.hxx" + +// =========================================================================== + +// all glyph specific data needed by the XGlyphPeer is quite trivial +// with one exception: if multiple screens are involved and non-antialiased +// glyph rendering is active, then we need screen specific pixmaps +struct MultiScreenGlyph +{ + const RawBitmap* mpRawBitmap; + Glyph maXRGlyphId; + Pixmap maPixmaps[1]; // [mnMaxScreens] +}; + +// =========================================================================== + +X11GlyphPeer::X11GlyphPeer() +: mpDisplay( GetX11SalData()->GetDisplay()->GetDisplay() ) +, mnMaxScreens(0) +, mnDefaultScreen(0) +, mnExtByteCount(0) +, mnForcedAA(0) +, mnUsingXRender(0) +{ + maRawBitmap.mnAllocated = 0; + maRawBitmap.mpBits = NULL; + if( !mpDisplay ) + return; + + SalDisplay& rSalDisplay = *GetX11SalData()->GetDisplay(); + mpDisplay = rSalDisplay.GetDisplay(); + mnMaxScreens = rSalDisplay.GetScreenCount(); + if( mnMaxScreens > MAX_GCACH_SCREENS ) + mnMaxScreens = MAX_GCACH_SCREENS; + // if specific glyph data has to be kept for many screens + // then prepare the allocation of MultiScreenGlyph objects + if( mnMaxScreens > 1 ) + mnExtByteCount = sizeof(MultiScreenGlyph) + sizeof(Pixmap) * (mnMaxScreens - 1); + mnDefaultScreen = rSalDisplay.GetDefaultScreenNumber(); + + InitAntialiasing(); +} + +// --------------------------------------------------------------------------- + +X11GlyphPeer::~X11GlyphPeer() +{ + SalDisplay* pSalDisp = GetX11SalData()->GetDisplay(); + Display* const pX11Disp = pSalDisp->GetDisplay(); + XRenderPeer& rRenderPeer = XRenderPeer::GetInstance(); + for( int i = 0; i < mnMaxScreens; i++ ) + { + SalDisplay::RenderEntryMap& rMap = pSalDisp->GetRenderEntries( i ); + for( SalDisplay::RenderEntryMap::iterator it = rMap.begin(); it != rMap.end(); ++it ) + { + if( it->second.m_aPixmap ) + ::XFreePixmap( pX11Disp, it->second.m_aPixmap ); + if( it->second.m_aPicture ) + rRenderPeer.FreePicture( it->second.m_aPicture ); + } + rMap.clear(); + } +} + +// --------------------------------------------------------------------------- + +void X11GlyphPeer::InitAntialiasing() +{ + int nEnvAntiAlias = 0; + const char* pEnvAntiAlias = getenv( "SAL_ANTIALIAS_DISABLE" ); + if( pEnvAntiAlias ) + { + nEnvAntiAlias = atoi( pEnvAntiAlias ); + if( nEnvAntiAlias == 0 ) + return; + } + + mnUsingXRender = 0; + mnForcedAA = 0; + + // enable XRENDER accelerated aliasing on screens that support it + // unless it explicitly disabled by an environment variable + if( (nEnvAntiAlias & 2) == 0 ) + mnUsingXRender = XRenderPeer::GetInstance().InitRenderText(); + + // else enable client side antialiasing for these screens + // unless it is explicitly disabled by an environment variable + if( (nEnvAntiAlias & 1) != 0 ) + return; + + // enable client side antialiasing for screen visuals that are suitable + // mnForcedAA is a bitmask of screens enabled for client side antialiasing + mnForcedAA = (~(~0U << mnMaxScreens)) ^ mnUsingXRender; + SalDisplay& rSalDisplay = *GetX11SalData()->GetDisplay(); + for( int nScreen = 0; nScreen < mnMaxScreens; ++nScreen) + { + Visual* pVisual = rSalDisplay.GetVisual( nScreen ).GetVisual(); + XVisualInfo aXVisualInfo; + aXVisualInfo.visualid = pVisual->visualid; + int nVisuals = 0; + XVisualInfo* pXVisualInfo = XGetVisualInfo( mpDisplay, VisualIDMask, &aXVisualInfo, &nVisuals ); + for( int i = nVisuals; --i >= 0; ) + { + if( ((pXVisualInfo[i].c_class==PseudoColor) || (pXVisualInfo[i].depth<24)) + && ((pXVisualInfo[i].c_class>GrayScale) || (pXVisualInfo[i].depth!=8) ) ) + mnForcedAA &= ~(1U << nScreen); + } + if( pXVisualInfo != NULL ) + XFree( pXVisualInfo ); + } +} + +// =========================================================================== + +enum { INFO_EMPTY=0, INFO_PIXMAP, INFO_XRENDER, INFO_RAWBMP, INFO_MULTISCREEN }; +static const Glyph NO_GLYPHID = 0; +static RawBitmap* const NO_RAWBMP = NULL; +static const Pixmap NO_PIXMAP = ~0; + +// --------------------------------------------------------------------------- + +MultiScreenGlyph* X11GlyphPeer::PrepareForMultiscreen( ExtGlyphData& rEGD ) const +{ + // prepare to store screen specific pixmaps + MultiScreenGlyph* pMSGlyph = (MultiScreenGlyph*)new char[ mnExtByteCount ]; + + // init the glyph formats + pMSGlyph->mpRawBitmap = NO_RAWBMP; + pMSGlyph->maXRGlyphId = NO_GLYPHID; + for( int i = 0; i < mnMaxScreens; ++i ) + pMSGlyph->maPixmaps[i] = NO_PIXMAP; + // reuse already available glyph formats + if( rEGD.meInfo == INFO_XRENDER ) + pMSGlyph->maXRGlyphId = reinterpret_cast<Glyph>(rEGD.mpData); + else if( rEGD.meInfo == INFO_RAWBMP ) + pMSGlyph->mpRawBitmap = reinterpret_cast<RawBitmap*>(rEGD.mpData); + else if( rEGD.meInfo == INFO_PIXMAP ) + { + Pixmap aPixmap = reinterpret_cast<Pixmap>(rEGD.mpData); + if( aPixmap != None ) + // pixmap for the default screen is available + pMSGlyph->maPixmaps[ mnDefaultScreen ] = aPixmap; + else // empty pixmap for all screens is available + for( int i = 0; i < mnMaxScreens; ++i ) + pMSGlyph->maPixmaps[ i ] = None; + } + // enable use of multiscreen glyph + rEGD.mpData = (void*)pMSGlyph; + rEGD.meInfo = INFO_MULTISCREEN; + + return pMSGlyph; + } + +// --------------------------------------------------------------------------- + +Glyph X11GlyphPeer::GetRenderGlyph( const GlyphData& rGD ) const +{ + Glyph aGlyphId = NO_GLYPHID; + const ExtGlyphData& rEGD = rGD.ExtDataRef(); + if( rEGD.meInfo == INFO_XRENDER ) + aGlyphId = reinterpret_cast<Glyph>(rEGD.mpData); + else if( rEGD.meInfo == INFO_MULTISCREEN ) + aGlyphId = reinterpret_cast<MultiScreenGlyph*>(rEGD.mpData)->maXRGlyphId; + return aGlyphId; +} + +// --------------------------------------------------------------------------- + +void X11GlyphPeer::SetRenderGlyph( GlyphData& rGD, Glyph aGlyphId ) const +{ + ExtGlyphData& rEGD = rGD.ExtDataRef(); + switch( rEGD.meInfo ) + { + case INFO_EMPTY: + rEGD.meInfo = INFO_XRENDER; + // fall through + case INFO_XRENDER: + rEGD.mpData = reinterpret_cast<void*>(aGlyphId); + break; + case INFO_PIXMAP: + case INFO_RAWBMP: + PrepareForMultiscreen( rEGD ); + // fall through + case INFO_MULTISCREEN: + reinterpret_cast<MultiScreenGlyph*>(rEGD.mpData)->maXRGlyphId = aGlyphId; + break; + default: + break; // cannot happen... + } +} + +// --------------------------------------------------------------------------- + +const RawBitmap* X11GlyphPeer::GetRawBitmap( const GlyphData& rGD ) const +{ + const RawBitmap* pRawBitmap = NO_RAWBMP; + const ExtGlyphData& rEGD = rGD.ExtDataRef(); + if( rEGD.meInfo == INFO_RAWBMP ) + pRawBitmap = reinterpret_cast<RawBitmap*>(rEGD.mpData); + else if( rEGD.meInfo == INFO_MULTISCREEN ) + pRawBitmap = reinterpret_cast<MultiScreenGlyph*>(rEGD.mpData)->mpRawBitmap; + return pRawBitmap; +} + +// --------------------------------------------------------------------------- + +void X11GlyphPeer::SetRawBitmap( GlyphData& rGD, const RawBitmap* pRawBitmap ) const +{ + ExtGlyphData& rEGD = rGD.ExtDataRef(); + switch( rEGD.meInfo ) + { + case INFO_EMPTY: + rEGD.meInfo = INFO_RAWBMP; + // fall through + case INFO_RAWBMP: + rEGD.mpData = (void*)pRawBitmap; + break; + case INFO_PIXMAP: + case INFO_XRENDER: + PrepareForMultiscreen( rEGD ); + // fall through + case INFO_MULTISCREEN: + reinterpret_cast<MultiScreenGlyph*>(rEGD.mpData)->mpRawBitmap = pRawBitmap; + break; + default: + // cannot happen... + break; + } +} + +// --------------------------------------------------------------------------- + +Pixmap X11GlyphPeer::GetPixmap( const GlyphData& rGD, int nScreen ) const +{ + Pixmap aPixmap = NO_PIXMAP; + const ExtGlyphData& rEGD = rGD.ExtDataRef(); + if( (rEGD.meInfo == INFO_PIXMAP) && (nScreen == mnDefaultScreen) ) + aPixmap = (Pixmap)rEGD.mpData; + else if( rEGD.meInfo == INFO_MULTISCREEN ) + aPixmap = (Pixmap)(reinterpret_cast<MultiScreenGlyph*>(rEGD.mpData)->maPixmaps[nScreen]); + return aPixmap; +} + +// --------------------------------------------------------------------------- + +void X11GlyphPeer::SetPixmap( GlyphData& rGD, Pixmap aPixmap, int nScreen ) const +{ + if( aPixmap == NO_PIXMAP ) + aPixmap = None; + + ExtGlyphData& rEGD = rGD.ExtDataRef(); + if( (rEGD.meInfo == INFO_EMPTY) && (nScreen == mnDefaultScreen) ) + { + rEGD.meInfo = INFO_PIXMAP; + rEGD.mpData = (void*)aPixmap; + } + else + { + MultiScreenGlyph* pMSGlyph; + if( rEGD.meInfo == INFO_MULTISCREEN ) + pMSGlyph = reinterpret_cast<MultiScreenGlyph*>(rEGD.mpData); + else + pMSGlyph = PrepareForMultiscreen( rEGD ); + + pMSGlyph->maPixmaps[ nScreen ] = aPixmap; + } +} + +// --------------------------------------------------------------------------- + +void X11GlyphPeer::RemovingFont( ServerFont& rServerFont ) +{ + void* pFontExt = rServerFont.GetExtPointer(); + switch( rServerFont.GetExtInfo() ) + { + case INFO_PIXMAP: + case INFO_RAWBMP: + // nothing to do + break; + case INFO_MULTISCREEN: + // cannot happen... + break; + + case INFO_XRENDER: + XRenderPeer::GetInstance().FreeGlyphSet( (GlyphSet)pFontExt ); + break; + } + + rServerFont.SetExtended( INFO_EMPTY, NULL ); +} + +// --------------------------------------------------------------------------- + +// notification to clean up GlyphPeer resources for this glyph +void X11GlyphPeer::RemovingGlyph( ServerFont& /*rServerFont*/, GlyphData& rGlyphData, int /*nGlyphIndex*/ ) +{ + // nothing to do if the GlyphPeer hasn't allocated resources for the glyph + if( rGlyphData.ExtDataRef().meInfo == INFO_EMPTY ) + return; + + const GlyphMetric& rGM = rGlyphData.GetMetric(); + const int nWidth = rGM.GetSize().Width(); + const int nHeight = rGM.GetSize().Height(); + + void* pGlyphExt = rGlyphData.ExtDataRef().mpData; + switch( rGlyphData.ExtDataRef().meInfo ) + { + case INFO_PIXMAP: + { + Pixmap aPixmap = (Pixmap)pGlyphExt; + if( aPixmap != None ) + { + XFreePixmap( mpDisplay, aPixmap ); + mnBytesUsed -= nHeight * ((nWidth + 7) >> 3); + } + } + break; + + case INFO_MULTISCREEN: + { + MultiScreenGlyph* pMSGlyph = reinterpret_cast<MultiScreenGlyph*>(pGlyphExt); + for( int i = 0; i < mnMaxScreens; ++i) + { + if( pMSGlyph->maPixmaps[i] == NO_PIXMAP ) + continue; + if( pMSGlyph->maPixmaps[i] == None ) + continue; + XFreePixmap( mpDisplay, pMSGlyph->maPixmaps[i] ); + mnBytesUsed -= nHeight * ((nWidth + 7) >> 3); + } + delete pMSGlyph->mpRawBitmap; + // Glyph nGlyphId = (Glyph)rGlyphData.GetExtPointer(); + // XRenderPeer::GetInstance().FreeGlyph( aGlyphSet, &nGlyphId ); + delete[] pMSGlyph; // it was allocated with new char[] + } + break; + + case INFO_RAWBMP: + { + RawBitmap* pRawBitmap = (RawBitmap*)pGlyphExt; + if( pRawBitmap != NULL ) + { + mnBytesUsed -= pRawBitmap->mnScanlineSize * pRawBitmap->mnHeight; + mnBytesUsed -= sizeof(RawBitmap); + delete pRawBitmap; + } + } + break; + + case INFO_XRENDER: + { + // Glyph nGlyphId = (Glyph)rGlyphData.GetExtPointer(); + // XRenderPeer::GetInstance().FreeGlyph( aGlyphSet, &nGlyphId ); + mnBytesUsed -= nHeight * ((nWidth + 3) & ~3); + } + break; + } + + if( mnBytesUsed < 0 ) // TODO: eliminate nBytesUsed calc mismatch + mnBytesUsed = 0; + + rGlyphData.ExtDataRef() = ExtGlyphData(); +} + +// --------------------------------------------------------------------------- + +bool X11GlyphPeer::ForcedAntialiasing( const ServerFont& rServerFont, int nScreen ) const +{ + bool bForceOk = rServerFont.GetAntialiasAdvice(); + // maximum size for antialiasing is 250 pixels + bForceOk &= (rServerFont.GetFontSelData().mnHeight < 250); + return (bForceOk && ((mnForcedAA >> nScreen) & 1)); +} + +// --------------------------------------------------------------------------- + +GlyphSet X11GlyphPeer::GetGlyphSet( ServerFont& rServerFont, int nScreen ) +{ + if( (nScreen >= 0) && ((mnUsingXRender >> nScreen) & 1) == 0 ) + return 0; + + GlyphSet aGlyphSet; + + switch( rServerFont.GetExtInfo() ) + { + case INFO_XRENDER: + aGlyphSet = (GlyphSet)rServerFont.GetExtPointer(); + break; + + case INFO_EMPTY: + { + // antialiasing for reasonable font heights only + // => prevents crashes caused by X11 requests >= 256k + // => prefer readablity of hinted glyphs at small sizes + // => prefer "grey clouds" to "black clouds" at very small sizes + int nHeight = rServerFont.GetFontSelData().mnHeight; + if( nHeight<250 && rServerFont.GetAntialiasAdvice() ) + { + aGlyphSet = XRenderPeer::GetInstance().CreateGlyphSet(); + rServerFont.SetExtended( INFO_XRENDER, (void*)aGlyphSet ); + } + else + aGlyphSet = 0; + } + break; + + default: + aGlyphSet = 0; + break; + } + + return aGlyphSet; +} + +// --------------------------------------------------------------------------- + +Pixmap X11GlyphPeer::GetPixmap( ServerFont& rServerFont, int nGlyphIndex, int nReqScreen ) +{ + if( rServerFont.IsGlyphInvisible( nGlyphIndex ) ) + return None; + + GlyphData& rGlyphData = rServerFont.GetGlyphData( nGlyphIndex ); + Pixmap aPixmap = GetPixmap( rGlyphData, nReqScreen ); + if( aPixmap == NO_PIXMAP ) + { + aPixmap = None; + if( rServerFont.GetGlyphBitmap1( nGlyphIndex, maRawBitmap ) ) + { + // #94666# circumvent bug in some X11 systems, e.g. XF410.LynxEM.v163 + sal_uLong nPixmapWidth = 8 * maRawBitmap.mnScanlineSize - 1; + nPixmapWidth = Max( nPixmapWidth, maRawBitmap.mnWidth ); + + rGlyphData.SetSize( Size( nPixmapWidth, maRawBitmap.mnHeight ) ); + rGlyphData.SetOffset( +maRawBitmap.mnXOffset, +maRawBitmap.mnYOffset ); + + const sal_uLong nBytes = maRawBitmap.mnHeight * maRawBitmap.mnScanlineSize; + if( nBytes > 0 ) + { + // conversion table LSB<->MSB (for XCreatePixmapFromData) + static const unsigned char lsb2msb[256] = + { + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, + 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, + 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, + 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, + 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, + 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, + 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, + 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, + 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, + 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, + 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, + 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, + 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, + 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, + 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, + 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, + 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF + }; + + unsigned char* pTemp = maRawBitmap.mpBits; + for( int i = nBytes; --i >= 0; ++pTemp ) + *pTemp = lsb2msb[ *pTemp ]; + + // often a glyph pixmap is only needed on the default screen + // => optimize for this common case + int nMinScreen = 0; + int nEndScreen = mnMaxScreens; + if( nReqScreen == mnDefaultScreen ) { + nMinScreen = mnDefaultScreen; + nEndScreen = mnDefaultScreen + 1; + } + // prepare glyph pixmaps for the different screens + for( int i = nMinScreen; i < nEndScreen; ++i ) + { + // don't bother if the pixmap is already there + if( GetPixmap( rGlyphData, i ) != NO_PIXMAP ) + continue; + // create the glyph pixmap + Pixmap aScreenPixmap = XCreatePixmapFromBitmapData( mpDisplay, + RootWindow( mpDisplay, i ), (char*)maRawBitmap.mpBits, + nPixmapWidth, maRawBitmap.mnHeight, 1, 0, 1 ); + // and cache it as glyph specific data + SetPixmap( rGlyphData, aScreenPixmap, i ); + mnBytesUsed += nBytes; + if( i == nReqScreen ) + aPixmap = aScreenPixmap; + } + } + } + else + { + // fall back to .notdef glyph + if( nGlyphIndex != 0 ) // recurse only once + aPixmap = GetPixmap( rServerFont, 0, nReqScreen ); + + if( aPixmap == NO_PIXMAP ) + aPixmap = None; + } + } + + return aPixmap; +} + +// --------------------------------------------------------------------------- + +const RawBitmap* X11GlyphPeer::GetRawBitmap( ServerFont& rServerFont, + int nGlyphIndex ) +{ + if( rServerFont.IsGlyphInvisible( nGlyphIndex ) ) + return NO_RAWBMP; + + GlyphData& rGlyphData = rServerFont.GetGlyphData( nGlyphIndex ); + + const RawBitmap* pRawBitmap = GetRawBitmap( rGlyphData ); + if( pRawBitmap == NO_RAWBMP ) + { + RawBitmap* pNewBitmap = new RawBitmap; + if( rServerFont.GetGlyphBitmap8( nGlyphIndex, *pNewBitmap ) ) + { + pRawBitmap = pNewBitmap; + mnBytesUsed += pNewBitmap->mnScanlineSize * pNewBitmap->mnHeight; + mnBytesUsed += sizeof(pNewBitmap); + } + else + { + delete pNewBitmap; + // fall back to .notdef glyph + if( nGlyphIndex != 0 ) // recurse only once + pRawBitmap = GetRawBitmap( rServerFont, 0 ); + } + + SetRawBitmap( rGlyphData, pRawBitmap ); + } + + return pRawBitmap; +} + +// --------------------------------------------------------------------------- + +Glyph X11GlyphPeer::GetGlyphId( ServerFont& rServerFont, int nGlyphIndex ) +{ + if( rServerFont.IsGlyphInvisible( nGlyphIndex ) ) + return NO_GLYPHID; + + GlyphData& rGlyphData = rServerFont.GetGlyphData( nGlyphIndex ); + + Glyph aGlyphId = GetRenderGlyph( rGlyphData ); + if( aGlyphId == NO_GLYPHID ) + { + // prepare GlyphInfo and Bitmap + if( rServerFont.GetGlyphBitmap8( nGlyphIndex, maRawBitmap ) ) + { + XGlyphInfo aGlyphInfo; + aGlyphInfo.width = maRawBitmap.mnWidth; + aGlyphInfo.height = maRawBitmap.mnHeight; + aGlyphInfo.x = -maRawBitmap.mnXOffset; + aGlyphInfo.y = -maRawBitmap.mnYOffset; + + rGlyphData.SetSize( Size( maRawBitmap.mnWidth, maRawBitmap.mnHeight ) ); + rGlyphData.SetOffset( +maRawBitmap.mnXOffset, +maRawBitmap.mnYOffset ); + + const GlyphMetric& rGM = rGlyphData.GetMetric(); + aGlyphInfo.xOff = +rGM.GetDelta().X(); + aGlyphInfo.yOff = +rGM.GetDelta().Y(); + + // upload glyph bitmap to server + GlyphSet aGlyphSet = GetGlyphSet( rServerFont, -1 ); + + aGlyphId = nGlyphIndex & 0x00FFFFFF; + const sal_uLong nBytes = maRawBitmap.mnScanlineSize * maRawBitmap.mnHeight; + XRenderPeer::GetInstance().AddGlyph( aGlyphSet, aGlyphId, + aGlyphInfo, (char*)maRawBitmap.mpBits, nBytes ); + mnBytesUsed += nBytes; + } + else + { + // fall back to .notdef glyph + if( nGlyphIndex != 0 ) // recurse only once + aGlyphId = GetGlyphId( rServerFont, 0 ); + } + + SetRenderGlyph( rGlyphData, aGlyphId ); + } + + return aGlyphId; +} + +// =========================================================================== + +X11GlyphCache::X11GlyphCache( X11GlyphPeer& rPeer ) +: GlyphCache( rPeer ) +{ +} + +// --------------------------------------------------------------------------- + +static X11GlyphPeer* pX11GlyphPeer = NULL; +static X11GlyphCache* pX11GlyphCache = NULL; + +X11GlyphCache& X11GlyphCache::GetInstance() +{ + if( !pX11GlyphCache ) + { + pX11GlyphPeer = new X11GlyphPeer(); + pX11GlyphCache = new X11GlyphCache( *pX11GlyphPeer ); + } + return *pX11GlyphCache; +} + +// --------------------------------------------------------------------------- + +void X11GlyphCache::KillInstance() +{ + delete pX11GlyphCache; + delete pX11GlyphPeer; + pX11GlyphCache = NULL; + pX11GlyphPeer = NULL; +} + +// =========================================================================== + +void X11SalGraphics::releaseGlyphPeer() +{ + X11GlyphCache::KillInstance(); +} + +// =========================================================================== + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/gdi/gcach_xpeer.hxx b/vcl/unx/generic/gdi/gcach_xpeer.hxx new file mode 100644 index 000000000000..412c47f82de2 --- /dev/null +++ b/vcl/unx/generic/gdi/gcach_xpeer.hxx @@ -0,0 +1,95 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_GCACH_XPEER_HXX +#define _SV_GCACH_XPEER_HXX + +#include <tools/prex.h> +#include <X11/extensions/Xrender.h> +#include <tools/postx.h> + +#include <glyphcache.hxx> + +class SalDisplay; +struct MultiScreenGlyph; + +class X11GlyphPeer +: public GlyphCachePeer +{ +public: + X11GlyphPeer(); + virtual ~X11GlyphPeer(); + + Pixmap GetPixmap( ServerFont&, int nGlyphIndex, int nScreen ); + const RawBitmap* GetRawBitmap( ServerFont&, int nGlyphIndex ); + bool ForcedAntialiasing( const ServerFont&, int nScreen ) const; + + GlyphSet GetGlyphSet( ServerFont&, int nScreen ); + Glyph GetGlyphId( ServerFont&, int nGlyphIndex ); + +protected: + void InitAntialiasing(); + + virtual void RemovingFont( ServerFont& ); + virtual void RemovingGlyph( ServerFont&, GlyphData&, int nGlyphIndex ); + + MultiScreenGlyph* PrepareForMultiscreen( ExtGlyphData& ) const; + void SetRenderGlyph( GlyphData&, Glyph ) const; + void SetRawBitmap( GlyphData&, const RawBitmap* ) const; + void SetPixmap( GlyphData&, Pixmap, int nScreen ) const; + Glyph GetRenderGlyph( const GlyphData& ) const; + const RawBitmap* GetRawBitmap( const GlyphData& ) const; + Pixmap GetPixmap( const GlyphData&, int nScreen ) const; + +private: + Display* mpDisplay; + + // thirty-two screens should be enough for everyone... + static const int MAX_GCACH_SCREENS = 32; + int mnMaxScreens; + int mnDefaultScreen; + int mnExtByteCount; + RawBitmap maRawBitmap; + sal_uInt32 mnForcedAA; + sal_uInt32 mnUsingXRender; +}; + +class X11GlyphCache : public GlyphCache +{ +public: + X11GlyphPeer& GetPeer() { return reinterpret_cast<X11GlyphPeer&>( mrPeer ); } +static X11GlyphCache& GetInstance(); +static void KillInstance(); + +private: + X11GlyphCache( X11GlyphPeer& ); +}; + +#endif // _SV_GCACH_XPEER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/gdi/pspgraphics.cxx b/vcl/unx/generic/gdi/pspgraphics.cxx new file mode 100644 index 000000000000..65c4c0ab41da --- /dev/null +++ b/vcl/unx/generic/gdi/pspgraphics.cxx @@ -0,0 +1,1504 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "unx/pspgraphics.h" + +#include "vcl/jobdata.hxx" +#include "vcl/printerinfomanager.hxx" +#include "vcl/bmpacc.hxx" +#include "vcl/svapp.hxx" +#include "vcl/sysdata.hxx" + +#include "printergfx.hxx" +#include "salbmp.hxx" +#include "glyphcache.hxx" +#include "impfont.hxx" +#include "outfont.hxx" +#include "fontsubset.hxx" +#include "salprn.hxx" +#include "region.h" + +#ifdef ENABLE_GRAPHITE +#include <graphite_layout.hxx> +#include <graphite_serverfont.hxx> +#endif + +using namespace psp; + +using ::rtl::OUString; +using ::rtl::OString; + +// ----- Implementation of PrinterBmp by means of SalBitmap/BitmapBuffer --------------- + +class SalPrinterBmp : public psp::PrinterBmp +{ + private: + BitmapBuffer* mpBmpBuffer; + + FncGetPixel mpFncGetPixel; + Scanline mpScanAccess; + sal_PtrDiff mnScanOffset; + + sal_uInt32 ColorOf (BitmapColor& rColor) const; + sal_uInt8 GrayOf (BitmapColor& rColor) const; + + SalPrinterBmp (); + + public: + + SalPrinterBmp (BitmapBuffer* pBitmap); + virtual ~SalPrinterBmp (); + virtual sal_uInt32 GetPaletteColor (sal_uInt32 nIdx) const; + virtual sal_uInt32 GetPaletteEntryCount () const; + virtual sal_uInt32 GetPixelRGB (sal_uInt32 nRow, sal_uInt32 nColumn) const; + virtual sal_uInt8 GetPixelGray (sal_uInt32 nRow, sal_uInt32 nColumn) const; + virtual sal_uInt8 GetPixelIdx (sal_uInt32 nRow, sal_uInt32 nColumn) const; + virtual sal_uInt32 GetWidth () const; + virtual sal_uInt32 GetHeight() const; + virtual sal_uInt32 GetDepth () const; +}; + +SalPrinterBmp::SalPrinterBmp (BitmapBuffer* pBuffer) : + mpBmpBuffer (pBuffer) +{ + DBG_ASSERT (mpBmpBuffer, "SalPrinterBmp::SalPrinterBmp () can't acquire Bitmap"); + + // calibrate scanline buffer + if( BMP_SCANLINE_ADJUSTMENT( mpBmpBuffer->mnFormat ) == BMP_FORMAT_TOP_DOWN ) + { + mpScanAccess = mpBmpBuffer->mpBits; + mnScanOffset = mpBmpBuffer->mnScanlineSize; + } + else + { + mpScanAccess = mpBmpBuffer->mpBits + + (mpBmpBuffer->mnHeight - 1) * mpBmpBuffer->mnScanlineSize; + mnScanOffset = - mpBmpBuffer->mnScanlineSize; + } + + // request read access to the pixels + switch( BMP_SCANLINE_FORMAT( mpBmpBuffer->mnFormat ) ) + { + case BMP_FORMAT_1BIT_MSB_PAL: + mpFncGetPixel = BitmapReadAccess::GetPixelFor_1BIT_MSB_PAL; break; + case BMP_FORMAT_1BIT_LSB_PAL: + mpFncGetPixel = BitmapReadAccess::GetPixelFor_1BIT_LSB_PAL; break; + case BMP_FORMAT_4BIT_MSN_PAL: + mpFncGetPixel = BitmapReadAccess::GetPixelFor_4BIT_MSN_PAL; break; + case BMP_FORMAT_4BIT_LSN_PAL: + mpFncGetPixel = BitmapReadAccess::GetPixelFor_4BIT_LSN_PAL; break; + case BMP_FORMAT_8BIT_PAL: + mpFncGetPixel = BitmapReadAccess::GetPixelFor_8BIT_PAL; break; + case BMP_FORMAT_8BIT_TC_MASK: + mpFncGetPixel = BitmapReadAccess::GetPixelFor_8BIT_TC_MASK; break; + case BMP_FORMAT_16BIT_TC_MSB_MASK: + mpFncGetPixel = BitmapReadAccess::GetPixelFor_16BIT_TC_MSB_MASK; break; + case BMP_FORMAT_16BIT_TC_LSB_MASK: + mpFncGetPixel = BitmapReadAccess::GetPixelFor_16BIT_TC_LSB_MASK; break; + case BMP_FORMAT_24BIT_TC_BGR: + mpFncGetPixel = BitmapReadAccess::GetPixelFor_24BIT_TC_BGR; break; + case BMP_FORMAT_24BIT_TC_RGB: + mpFncGetPixel = BitmapReadAccess::GetPixelFor_24BIT_TC_RGB; break; + case BMP_FORMAT_24BIT_TC_MASK: + mpFncGetPixel = BitmapReadAccess::GetPixelFor_24BIT_TC_MASK; break; + case BMP_FORMAT_32BIT_TC_ABGR: + mpFncGetPixel = BitmapReadAccess::GetPixelFor_32BIT_TC_ABGR; break; + case BMP_FORMAT_32BIT_TC_ARGB: + mpFncGetPixel = BitmapReadAccess::GetPixelFor_32BIT_TC_ARGB; break; + case BMP_FORMAT_32BIT_TC_BGRA: + mpFncGetPixel = BitmapReadAccess::GetPixelFor_32BIT_TC_BGRA; break; + case BMP_FORMAT_32BIT_TC_RGBA: + mpFncGetPixel = BitmapReadAccess::GetPixelFor_32BIT_TC_RGBA; break; + case BMP_FORMAT_32BIT_TC_MASK: + mpFncGetPixel = BitmapReadAccess::GetPixelFor_32BIT_TC_MASK; break; + + default: + OSL_FAIL("Error: SalPrinterBmp::SalPrinterBmp() unknown bitmap format"); + break; + } +} + +SalPrinterBmp::~SalPrinterBmp () +{ +} + +sal_uInt32 +SalPrinterBmp::GetWidth () const +{ + return mpBmpBuffer->mnWidth; +} + +sal_uInt32 +SalPrinterBmp::GetHeight () const +{ + return mpBmpBuffer->mnHeight; +} + +sal_uInt32 +SalPrinterBmp::GetDepth () const +{ + sal_uInt32 nDepth; + + switch (mpBmpBuffer->mnBitCount) + { + case 1: + nDepth = 1; + break; + + case 4: + case 8: + nDepth = 8; + break; + + case 16: + case 24: + case 32: + nDepth = 24; + break; + + default: + nDepth = 1; + OSL_FAIL("Error: unsupported bitmap depth in SalPrinterBmp::GetDepth()"); + break; + } + + return nDepth; +} + +sal_uInt32 +SalPrinterBmp::ColorOf (BitmapColor& rColor) const +{ + if (rColor.IsIndex()) + return ColorOf (mpBmpBuffer->maPalette[rColor.GetIndex()]); + else + return ((rColor.GetBlue()) & 0x000000ff) + | ((rColor.GetGreen() << 8) & 0x0000ff00) + | ((rColor.GetRed() << 16) & 0x00ff0000); +} + +sal_uInt8 +SalPrinterBmp::GrayOf (BitmapColor& rColor) const +{ + if (rColor.IsIndex()) + return GrayOf (mpBmpBuffer->maPalette[rColor.GetIndex()]); + else + return ( rColor.GetBlue() * 28UL + + rColor.GetGreen() * 151UL + + rColor.GetRed() * 77UL ) >> 8; +} + +sal_uInt32 +SalPrinterBmp::GetPaletteEntryCount () const +{ + return mpBmpBuffer->maPalette.GetEntryCount (); +} + +sal_uInt32 +SalPrinterBmp::GetPaletteColor (sal_uInt32 nIdx) const +{ + return ColorOf (mpBmpBuffer->maPalette[nIdx]); +} + +sal_uInt32 +SalPrinterBmp::GetPixelRGB (sal_uInt32 nRow, sal_uInt32 nColumn) const +{ + Scanline pScan = mpScanAccess + nRow * mnScanOffset; + BitmapColor aColor = mpFncGetPixel (pScan, nColumn, mpBmpBuffer->maColorMask); + + return ColorOf (aColor); +} + +sal_uInt8 +SalPrinterBmp::GetPixelGray (sal_uInt32 nRow, sal_uInt32 nColumn) const +{ + Scanline pScan = mpScanAccess + nRow * mnScanOffset; + BitmapColor aColor = mpFncGetPixel (pScan, nColumn, mpBmpBuffer->maColorMask); + + return GrayOf (aColor); +} + +sal_uInt8 +SalPrinterBmp::GetPixelIdx (sal_uInt32 nRow, sal_uInt32 nColumn) const +{ + Scanline pScan = mpScanAccess + nRow * mnScanOffset; + BitmapColor aColor = mpFncGetPixel (pScan, nColumn, mpBmpBuffer->maColorMask); + + if (aColor.IsIndex()) + return aColor.GetIndex(); + else + return 0; +} + +/******************************************************* + * PspGraphics * + *******************************************************/ + +PspGraphics::~PspGraphics() +{ + ReleaseFonts(); +} + +void PspGraphics::GetResolution( sal_Int32 &rDPIX, sal_Int32 &rDPIY ) +{ + if (m_pJobData != NULL) + { + int x = m_pJobData->m_aContext.getRenderResolution(); + + rDPIX = x; + rDPIY = x; + } +} + +sal_uInt16 PspGraphics::GetBitCount() const +{ + return m_pPrinterGfx->GetBitCount(); +} + +long PspGraphics::GetGraphicsWidth() const +{ + return 0; +} + +void PspGraphics::ResetClipRegion() +{ + m_pPrinterGfx->ResetClipRegion(); +} + +bool PspGraphics::setClipRegion( const Region& i_rClip ) +{ + // TODO: support polygonal clipregions here + m_pPrinterGfx->BeginSetClipRegion( i_rClip.GetRectCount() ); + + ImplRegionInfo aInfo; + long nX, nY, nW, nH; + bool bRegionRect = i_rClip.ImplGetFirstRect(aInfo, nX, nY, nW, nH ); + while( bRegionRect ) + { + if ( nW && nH ) + { + m_pPrinterGfx->UnionClipRegion( nX, nY, nW, nH ); + } + bRegionRect = i_rClip.ImplGetNextRect( aInfo, nX, nY, nW, nH ); + } + m_pPrinterGfx->EndSetClipRegion(); + return true; +} + +void PspGraphics::SetLineColor() +{ + m_pPrinterGfx->SetLineColor (); +} + +void PspGraphics::SetLineColor( SalColor nSalColor ) +{ + psp::PrinterColor aColor (SALCOLOR_RED (nSalColor), + SALCOLOR_GREEN (nSalColor), + SALCOLOR_BLUE (nSalColor)); + m_pPrinterGfx->SetLineColor (aColor); +} + +void PspGraphics::SetFillColor() +{ + m_pPrinterGfx->SetFillColor (); +} + +void PspGraphics::SetFillColor( SalColor nSalColor ) +{ + psp::PrinterColor aColor (SALCOLOR_RED (nSalColor), + SALCOLOR_GREEN (nSalColor), + SALCOLOR_BLUE (nSalColor)); + m_pPrinterGfx->SetFillColor (aColor); +} + +void PspGraphics::SetROPLineColor( SalROPColor ) +{ + DBG_ASSERT( 0, "Error: PrinterGfx::SetROPLineColor() not implemented" ); +} + +void PspGraphics::SetROPFillColor( SalROPColor ) +{ + DBG_ASSERT( 0, "Error: PrinterGfx::SetROPFillColor() not implemented" ); +} + +void PspGraphics::SetXORMode( bool bSet, bool ) +{ + (void)bSet; + DBG_ASSERT( !bSet, "Error: PrinterGfx::SetXORMode() not implemented" ); +} + +void PspGraphics::drawPixel( long nX, long nY ) +{ + m_pPrinterGfx->DrawPixel (Point(nX, nY)); +} + +void PspGraphics::drawPixel( long nX, long nY, SalColor nSalColor ) +{ + psp::PrinterColor aColor (SALCOLOR_RED (nSalColor), + SALCOLOR_GREEN (nSalColor), + SALCOLOR_BLUE (nSalColor)); + m_pPrinterGfx->DrawPixel (Point(nX, nY), aColor); +} + +void PspGraphics::drawLine( long nX1, long nY1, long nX2, long nY2 ) +{ + m_pPrinterGfx->DrawLine (Point(nX1, nY1), Point(nX2, nY2)); +} + +void PspGraphics::drawRect( long nX, long nY, long nDX, long nDY ) +{ + m_pPrinterGfx->DrawRect (Rectangle(Point(nX, nY), Size(nDX, nDY))); +} + +void PspGraphics::drawPolyLine( sal_uLong nPoints, const SalPoint *pPtAry ) +{ + m_pPrinterGfx->DrawPolyLine (nPoints, (Point*)pPtAry); +} + +void PspGraphics::drawPolygon( sal_uLong nPoints, const SalPoint* pPtAry ) +{ + // Point must be equal to SalPoint! see vcl/inc/salgtype.hxx + m_pPrinterGfx->DrawPolygon (nPoints, (Point*)pPtAry); +} + +void PspGraphics::drawPolyPolygon( sal_uInt32 nPoly, + const sal_uInt32 *pPoints, + PCONSTSALPOINT *pPtAry ) +{ + m_pPrinterGfx->DrawPolyPolygon (nPoly, pPoints, (const Point**)pPtAry); +} + +bool PspGraphics::drawPolyPolygon( const ::basegfx::B2DPolyPolygon&, double /*fTransparency*/ ) +{ + // TODO: implement and advertise OutDevSupport_B2DDraw support + return false; +} + +bool PspGraphics::drawPolyLine( const basegfx::B2DPolygon&, double /*fTranspareny*/, const basegfx::B2DVector& /*rLineWidths*/, basegfx::B2DLineJoin /*eJoin*/) +{ + // TODO: a PS printer can draw B2DPolyLines almost directly + return false; +} + +sal_Bool PspGraphics::drawPolyLineBezier( sal_uLong nPoints, const SalPoint* pPtAry, const sal_uInt8* pFlgAry ) +{ + m_pPrinterGfx->DrawPolyLineBezier (nPoints, (Point*)pPtAry, pFlgAry); + return sal_True; +} + +sal_Bool PspGraphics::drawPolygonBezier( sal_uLong nPoints, const SalPoint* pPtAry, const sal_uInt8* pFlgAry ) +{ + m_pPrinterGfx->DrawPolygonBezier (nPoints, (Point*)pPtAry, pFlgAry); + return sal_True; +} + +sal_Bool PspGraphics::drawPolyPolygonBezier( sal_uInt32 nPoly, + const sal_uInt32* pPoints, + const SalPoint* const* pPtAry, + const sal_uInt8* const* pFlgAry ) +{ + // Point must be equal to SalPoint! see vcl/inc/salgtype.hxx + m_pPrinterGfx->DrawPolyPolygonBezier (nPoly, pPoints, (Point**)pPtAry, (sal_uInt8**)pFlgAry); + return sal_True; +} + +void PspGraphics::invert( sal_uLong, + const SalPoint*, + SalInvert ) +{ + DBG_ASSERT( 0, "Error: PrinterGfx::Invert() not implemented" ); +} +sal_Bool PspGraphics::drawEPS( long nX, long nY, long nWidth, long nHeight, void* pPtr, sal_uLong nSize ) +{ + return m_pPrinterGfx->DrawEPS( Rectangle( Point( nX, nY ), Size( nWidth, nHeight ) ), pPtr, nSize ); +} + +void PspGraphics::copyBits( const SalTwoRect*, + SalGraphics* ) +{ + OSL_FAIL( "Error: PrinterGfx::CopyBits() not implemented" ); +} + +void PspGraphics::copyArea ( long,long,long,long,long,long,sal_uInt16 ) +{ + OSL_FAIL( "Error: PrinterGfx::CopyArea() not implemented" ); +} + +void PspGraphics::drawBitmap( const SalTwoRect* pPosAry, const SalBitmap& rSalBitmap ) +{ + Rectangle aSrc (Point(pPosAry->mnSrcX, pPosAry->mnSrcY), + Size(pPosAry->mnSrcWidth, pPosAry->mnSrcHeight)); + Rectangle aDst (Point(pPosAry->mnDestX, pPosAry->mnDestY), + Size(pPosAry->mnDestWidth, pPosAry->mnDestHeight)); + + BitmapBuffer* pBuffer= const_cast<SalBitmap&>(rSalBitmap).AcquireBuffer(sal_True); + + SalPrinterBmp aBmp (pBuffer); + m_pPrinterGfx->DrawBitmap (aDst, aSrc, aBmp); + + const_cast<SalBitmap&>(rSalBitmap).ReleaseBuffer (pBuffer, sal_True); +} + +void PspGraphics::drawBitmap( const SalTwoRect*, + const SalBitmap&, + const SalBitmap& ) +{ + OSL_FAIL("Error: no PrinterGfx::DrawBitmap() for transparent bitmap"); +} + +void PspGraphics::drawBitmap( const SalTwoRect*, + const SalBitmap&, + SalColor ) +{ + OSL_FAIL("Error: no PrinterGfx::DrawBitmap() for transparent color"); +} + +void PspGraphics::drawMask( const SalTwoRect*, + const SalBitmap &, + SalColor ) +{ + OSL_FAIL("Error: PrinterGfx::DrawMask() not implemented"); +} + +SalBitmap* PspGraphics::getBitmap( long, long, long, long ) +{ + DBG_WARNING ("Warning: PrinterGfx::GetBitmap() not implemented"); + return NULL; +} + +SalColor PspGraphics::getPixel( long, long ) +{ + OSL_FAIL("Warning: PrinterGfx::GetPixel() not implemented"); + return 0; +} + +void PspGraphics::invert(long,long,long,long,SalInvert) +{ + OSL_FAIL("Warning: PrinterGfx::Invert() not implemented"); +} + +//========================================================================== + +class ImplPspFontData : public ImplFontData +{ +private: + enum { PSPFD_MAGIC = 0xb5bf01f0 }; + sal_IntPtr mnFontId; + +public: + ImplPspFontData( const psp::FastPrintFontInfo& ); + virtual sal_IntPtr GetFontId() const { return mnFontId; } + virtual ImplFontData* Clone() const { return new ImplPspFontData( *this ); } + virtual ImplFontEntry* CreateFontInstance( ImplFontSelectData& ) const; + static bool CheckFontData( const ImplFontData& r ) { return r.CheckMagic( PSPFD_MAGIC ); } +}; + +//-------------------------------------------------------------------------- + +ImplPspFontData::ImplPspFontData( const psp::FastPrintFontInfo& rInfo ) +: ImplFontData( PspGraphics::Info2DevFontAttributes(rInfo), PSPFD_MAGIC ), + mnFontId( rInfo.m_nID ) +{} + +//-------------------------------------------------------------------------- + +ImplFontEntry* ImplPspFontData::CreateFontInstance( ImplFontSelectData& rFSD ) const +{ + ImplServerFontEntry* pEntry = new ImplServerFontEntry( rFSD ); + return pEntry; +} + +//========================================================================== + +class PspFontLayout : public GenericSalLayout +{ +public: + PspFontLayout( ::psp::PrinterGfx& ); + virtual bool LayoutText( ImplLayoutArgs& ); + virtual void InitFont() const; + virtual void DrawText( SalGraphics& ) const; +private: + ::psp::PrinterGfx& mrPrinterGfx; + sal_IntPtr mnFontID; + int mnFontHeight; + int mnFontWidth; + bool mbVertical; + bool mbArtItalic; + bool mbArtBold; +}; + +//-------------------------------------------------------------------------- + +PspFontLayout::PspFontLayout( ::psp::PrinterGfx& rGfx ) +: mrPrinterGfx( rGfx ) +{ + mnFontID = mrPrinterGfx.GetFontID(); + mnFontHeight = mrPrinterGfx.GetFontHeight(); + mnFontWidth = mrPrinterGfx.GetFontWidth(); + mbVertical = mrPrinterGfx.GetFontVertical(); + mbArtItalic = mrPrinterGfx.GetArtificialItalic(); + mbArtBold = mrPrinterGfx.GetArtificialBold(); +} + +//-------------------------------------------------------------------------- + +bool PspFontLayout::LayoutText( ImplLayoutArgs& rArgs ) +{ + mbVertical = ((rArgs.mnFlags & SAL_LAYOUT_VERTICAL) != 0); + + long nUnitsPerPixel = 1; + int nOldGlyphId = -1; + long nGlyphWidth = 0; + int nCharPos = -1; + Point aNewPos( 0, 0 ); + GlyphItem aPrevItem; + rtl_TextEncoding aFontEnc = mrPrinterGfx.GetFontMgr().getFontEncoding( mnFontID ); + for(;;) + { + bool bRightToLeft; + if( !rArgs.GetNextPos( &nCharPos, &bRightToLeft ) ) + break; + + sal_Unicode cChar = rArgs.mpStr[ nCharPos ]; + if( bRightToLeft ) + cChar = GetMirroredChar( cChar ); + // symbol font aliasing: 0x0020-0x00ff -> 0xf020 -> 0xf0ff + if( aFontEnc == RTL_TEXTENCODING_SYMBOL ) + if( cChar < 256 ) + cChar += 0xf000; + int nGlyphIndex = cChar; // printer glyphs = unicode + + // update fallback_runs if needed + psp::CharacterMetric aMetric; + mrPrinterGfx.GetFontMgr().getMetrics( mnFontID, cChar, cChar, &aMetric, mbVertical ); + if( aMetric.width == -1 && aMetric.height == -1 ) + rArgs.NeedFallback( nCharPos, bRightToLeft ); + + // apply pair kerning to prev glyph if requested + if( SAL_LAYOUT_KERNING_PAIRS & rArgs.mnFlags ) + { + if( nOldGlyphId > 0 ) + { + const std::list< KernPair >& rKernPairs = mrPrinterGfx.getKernPairs(mbVertical); + for( std::list< KernPair >::const_iterator it = rKernPairs.begin(); + it != rKernPairs.end(); ++it ) + { + if( it->first == nOldGlyphId && it->second == nGlyphIndex ) + { + int nTextScale = mrPrinterGfx.GetFontWidth(); + if( ! nTextScale ) + nTextScale = mrPrinterGfx.GetFontHeight(); + int nKern = (mbVertical ? it->kern_y : it->kern_x) * nTextScale; + nGlyphWidth += nKern; + aPrevItem.mnNewWidth = nGlyphWidth; + break; + } + } + } + } + + // finish previous glyph + if( nOldGlyphId >= 0 ) + AppendGlyph( aPrevItem ); + nOldGlyphId = nGlyphIndex; + aNewPos.X() += nGlyphWidth; + + // prepare GlyphItem for appending it in next round + nUnitsPerPixel = mrPrinterGfx.GetCharWidth( cChar, cChar, &nGlyphWidth ); + int nGlyphFlags = bRightToLeft ? GlyphItem::IS_RTL_GLYPH : 0; + nGlyphIndex |= GF_ISCHAR; + aPrevItem = GlyphItem( nCharPos, nGlyphIndex, aNewPos, nGlyphFlags, nGlyphWidth ); + } + + // append last glyph item if any + if( nOldGlyphId >= 0 ) + AppendGlyph( aPrevItem ); + + SetOrientation( mrPrinterGfx.GetFontAngle() ); + SetUnitsPerPixel( nUnitsPerPixel ); + return (nOldGlyphId >= 0); +} + +class PspServerFontLayout : public ServerFontLayout +{ +public: + PspServerFontLayout( psp::PrinterGfx&, ServerFont& rFont, const ImplLayoutArgs& rArgs ); + + virtual void InitFont() const; + const sal_Unicode* getTextPtr() const { return maText.getStr() - mnMinCharPos; } + int getMinCharPos() const { return mnMinCharPos; } + int getMaxCharPos() const { return mnMinCharPos+maText.getLength()-1; } +private: + ::psp::PrinterGfx& mrPrinterGfx; + sal_IntPtr mnFontID; + int mnFontHeight; + int mnFontWidth; + bool mbVertical; + bool mbArtItalic; + bool mbArtBold; + rtl::OUString maText; + int mnMinCharPos; +}; + +PspServerFontLayout::PspServerFontLayout( ::psp::PrinterGfx& rGfx, ServerFont& rFont, const ImplLayoutArgs& rArgs ) + : ServerFontLayout( rFont ), + mrPrinterGfx( rGfx ) +{ + mnFontID = mrPrinterGfx.GetFontID(); + mnFontHeight = mrPrinterGfx.GetFontHeight(); + mnFontWidth = mrPrinterGfx.GetFontWidth(); + mbVertical = mrPrinterGfx.GetFontVertical(); + mbArtItalic = mrPrinterGfx.GetArtificialItalic(); + mbArtBold = mrPrinterGfx.GetArtificialBold(); + maText = OUString( rArgs.mpStr + rArgs.mnMinCharPos, rArgs.mnEndCharPos - rArgs.mnMinCharPos+1 ); + mnMinCharPos = rArgs.mnMinCharPos; +} + +void PspServerFontLayout::InitFont() const +{ + mrPrinterGfx.SetFont( mnFontID, mnFontHeight, mnFontWidth, + mnOrientation, mbVertical, mbArtItalic, mbArtBold ); +} + +//-------------------------------------------------------------------------- + +static void DrawPrinterLayout( const SalLayout& rLayout, ::psp::PrinterGfx& rGfx, bool bIsPspServerFontLayout ) +{ + const int nMaxGlyphs = 200; + sal_uInt32 aGlyphAry[ nMaxGlyphs ]; // TODO: use sal_GlyphId + sal_Int32 aWidthAry[ nMaxGlyphs ]; + sal_Int32 aIdxAry [ nMaxGlyphs ]; + sal_Unicode aUnicodes[ nMaxGlyphs ]; + int aCharPosAry [ nMaxGlyphs ]; + + Point aPos; + long nUnitsPerPixel = rLayout.GetUnitsPerPixel(); + const sal_Unicode* pText = NULL; + int nMinCharPos = 0; + int nMaxCharPos = 0; + if (bIsPspServerFontLayout) + { + const PspServerFontLayout * pPspLayout = dynamic_cast<const PspServerFontLayout*>(&rLayout); +#ifdef ENABLE_GRAPHITE + const GraphiteServerFontLayout * pGrLayout = dynamic_cast<const GraphiteServerFontLayout*>(&rLayout); +#endif + if (pPspLayout) + { + pText = pPspLayout->getTextPtr(); + nMinCharPos = pPspLayout->getMinCharPos(); + nMaxCharPos = pPspLayout->getMaxCharPos(); + } +#ifdef ENABLE_GRAPHITE + else if (pGrLayout) + { + } +#endif + } + for( int nStart = 0;; ) + { + int nGlyphCount = rLayout.GetNextGlyphs( nMaxGlyphs, aGlyphAry, aPos, nStart, aWidthAry, pText ? aCharPosAry : NULL ); + if( !nGlyphCount ) + break; + + sal_Int32 nXOffset = 0; + for( int i = 0; i < nGlyphCount; ++i ) + { + nXOffset += aWidthAry[ i ]; + aIdxAry[ i ] = nXOffset / nUnitsPerPixel; + sal_Int32 nGlyphIdx = aGlyphAry[i] & (GF_IDXMASK | GF_ROTMASK); + if( pText ) + aUnicodes[i] = (aCharPosAry[i] >= nMinCharPos && aCharPosAry[i] <= nMaxCharPos) ? pText[ aCharPosAry[i] ] : 0; + else + aUnicodes[i] = (aGlyphAry[i] & GF_ISCHAR) ? nGlyphIdx : 0; + aGlyphAry[i] = nGlyphIdx; + } + + rGfx.DrawGlyphs( aPos, (sal_uInt32 *)aGlyphAry, aUnicodes, nGlyphCount, aIdxAry ); + } +} + +//-------------------------------------------------------------------------- + +void PspFontLayout::InitFont() const +{ + mrPrinterGfx.SetFont( mnFontID, mnFontHeight, mnFontWidth, + mnOrientation, mbVertical, mbArtItalic, mbArtBold ); +} + +//-------------------------------------------------------------------------- + +void PspFontLayout::DrawText( SalGraphics& ) const +{ + DrawPrinterLayout( *this, mrPrinterGfx, false ); +} + +void PspGraphics::DrawServerFontLayout( const ServerFontLayout& rLayout ) +{ + // print complex text + DrawPrinterLayout( rLayout, *m_pPrinterGfx, true ); +} + +const ImplFontCharMap* PspGraphics::GetImplFontCharMap() const +{ + if( !m_pServerFont[0] ) + return NULL; + + const ImplFontCharMap* pIFCMap = m_pServerFont[0]->GetImplFontCharMap(); + return pIFCMap; +} + +bool PspGraphics::GetImplFontCapabilities(vcl::FontCapabilities &rFontCapabilities) const +{ + if (!m_pServerFont[0]) + return false; + return m_pServerFont[0]->GetFontCapabilities(rFontCapabilities); +} + +sal_uInt16 PspGraphics::SetFont( ImplFontSelectData *pEntry, int nFallbackLevel ) +{ + // release all fonts that are to be overridden + for( int i = nFallbackLevel; i < MAX_FALLBACK; ++i ) + { + if( m_pServerFont[i] != NULL ) + { + // old server side font is no longer referenced + GlyphCache::GetInstance().UncacheFont( *m_pServerFont[i] ); + m_pServerFont[i] = NULL; + } + } + + // return early if there is no new font + if( !pEntry ) + return 0; + + sal_IntPtr nID = pEntry->mpFontData ? pEntry->mpFontData->GetFontId() : 0; + + // determine which font attributes need to be emulated + bool bArtItalic = false; + bool bArtBold = false; + if( pEntry->meItalic == ITALIC_OBLIQUE || pEntry->meItalic == ITALIC_NORMAL ) + { + psp::italic::type eItalic = m_pPrinterGfx->GetFontMgr().getFontItalic( nID ); + if( eItalic != psp::italic::Italic && eItalic != psp::italic::Oblique ) + bArtItalic = true; + } + int nWeight = (int)pEntry->meWeight; + int nRealWeight = (int)m_pPrinterGfx->GetFontMgr().getFontWeight( nID ); + if( nRealWeight <= (int)psp::weight::Medium && nWeight > (int)WEIGHT_MEDIUM ) + { + bArtBold = true; + } + + // also set the serverside font for layouting + m_bFontVertical = pEntry->mbVertical; + if( pEntry->mpFontData ) + { + // requesting a font provided by builtin rasterizer + ServerFont* pServerFont = GlyphCache::GetInstance().CacheFont( *pEntry ); + if( pServerFont != NULL ) + { + if( pServerFont->TestFont() ) + m_pServerFont[ nFallbackLevel ] = pServerFont; + else + GlyphCache::GetInstance().UncacheFont( *pServerFont ); + } + } + + // set the printer font + return m_pPrinterGfx->SetFont( nID, + pEntry->mnHeight, + pEntry->mnWidth, + pEntry->mnOrientation, + pEntry->mbVertical, + bArtItalic, + bArtBold + ); +} + +void PspGraphics::SetTextColor( SalColor nSalColor ) +{ + psp::PrinterColor aColor (SALCOLOR_RED (nSalColor), + SALCOLOR_GREEN (nSalColor), + SALCOLOR_BLUE (nSalColor)); + m_pPrinterGfx->SetTextColor (aColor); +} + +bool PspGraphics::AddTempDevFont( ImplDevFontList*, const String&,const String& ) +{ + return false; +} + +void RegisterFontSubstitutors( ImplDevFontList* ); + +void PspGraphics::GetDevFontList( ImplDevFontList *pList ) +{ + ::std::list< psp::fontID > aList; + psp::PrintFontManager& rMgr = psp::PrintFontManager::get(); + rMgr.getFontList( aList, m_pJobData->m_pParser, m_pInfoPrinter->m_bCompatMetrics ); + + ::std::list< psp::fontID >::iterator it; + psp::FastPrintFontInfo aInfo; + for (it = aList.begin(); it != aList.end(); ++it) + if (rMgr.getFontFastInfo (*it, aInfo)) + AnnounceFonts( pList, aInfo ); + + // register platform specific font substitutions if available + if( rMgr.hasFontconfig() ) + RegisterFontSubstitutors( pList ); +} + +void PspGraphics::GetDevFontSubstList( OutputDevice* pOutDev ) +{ + const psp::PrinterInfo& rInfo = psp::PrinterInfoManager::get().getPrinterInfo( m_pJobData->m_aPrinterName ); + if( rInfo.m_bPerformFontSubstitution ) + { + for( boost::unordered_map< rtl::OUString, rtl::OUString, rtl::OUStringHash >::const_iterator it = rInfo.m_aFontSubstitutes.begin(); it != rInfo.m_aFontSubstitutes.end(); ++it ) + pOutDev->ImplAddDevFontSubstitute( it->first, it->second, FONT_SUBSTITUTE_ALWAYS ); + } +} + +void PspGraphics::GetFontMetric( ImplFontMetricData *pMetric, int ) +{ + const psp::PrintFontManager& rMgr = psp::PrintFontManager::get(); + psp::PrintFontInfo aInfo; + + if (rMgr.getFontInfo (m_pPrinterGfx->GetFontID(), aInfo)) + { + ImplDevFontAttributes aDFA = Info2DevFontAttributes( aInfo ); + static_cast<ImplFontAttributes&>(*pMetric) = aDFA; + pMetric->mbDevice = aDFA.mbDevice; + pMetric->mbScalableFont = true; + + pMetric->mnOrientation = m_pPrinterGfx->GetFontAngle(); + pMetric->mnSlant = 0; + + sal_Int32 nTextHeight = m_pPrinterGfx->GetFontHeight(); + sal_Int32 nTextWidth = m_pPrinterGfx->GetFontWidth(); + if( ! nTextWidth ) + nTextWidth = nTextHeight; + + pMetric->mnWidth = nTextWidth; + pMetric->mnAscent = ( aInfo.m_nAscend * nTextHeight + 500 ) / 1000; + pMetric->mnDescent = ( aInfo.m_nDescend * nTextHeight + 500 ) / 1000; + pMetric->mnIntLeading = ( aInfo.m_nLeading * nTextHeight + 500 ) / 1000; + pMetric->mnExtLeading = 0; + } +} + +sal_uLong PspGraphics::GetKernPairs( sal_uLong nPairs, ImplKernPairData *pKernPairs ) +{ + const ::std::list< ::psp::KernPair >& rPairs( m_pPrinterGfx->getKernPairs() ); + sal_uLong nHavePairs = rPairs.size(); + if( pKernPairs && nPairs ) + { + ::std::list< ::psp::KernPair >::const_iterator it; + unsigned int i; + int nTextScale = m_pPrinterGfx->GetFontWidth(); + if( ! nTextScale ) + nTextScale = m_pPrinterGfx->GetFontHeight(); + for( i = 0, it = rPairs.begin(); i < nPairs && i < nHavePairs; i++, ++it ) + { + pKernPairs[i].mnChar1 = it->first; + pKernPairs[i].mnChar2 = it->second; + pKernPairs[i].mnKern = it->kern_x * nTextScale / 1000; + } + + } + return nHavePairs; +} + +sal_Bool PspGraphics::GetGlyphBoundRect( long nGlyphIndex, Rectangle& rRect ) +{ + int nLevel = nGlyphIndex >> GF_FONTSHIFT; + if( nLevel >= MAX_FALLBACK ) + return sal_False; + + ServerFont* pSF = m_pServerFont[ nLevel ]; + if( !pSF ) + return sal_False; + + nGlyphIndex &= ~GF_FONTMASK; + const GlyphMetric& rGM = pSF->GetGlyphMetric( nGlyphIndex ); + rRect = Rectangle( rGM.GetOffset(), rGM.GetSize() ); + return sal_True; +} + +sal_Bool PspGraphics::GetGlyphOutline( long nGlyphIndex, + ::basegfx::B2DPolyPolygon& rB2DPolyPoly ) +{ + int nLevel = nGlyphIndex >> GF_FONTSHIFT; + if( nLevel >= MAX_FALLBACK ) + return sal_False; + + ServerFont* pSF = m_pServerFont[ nLevel ]; + if( !pSF ) + return sal_False; + + nGlyphIndex &= ~GF_FONTMASK; + if( pSF->GetGlyphOutline( nGlyphIndex, rB2DPolyPoly ) ) + return sal_True; + + return sal_False; +} + +SalLayout* PspGraphics::GetTextLayout( ImplLayoutArgs& rArgs, int nFallbackLevel ) +{ + // workaround for printers not handling glyph indexing for non-TT fonts + int nFontId = m_pPrinterGfx->GetFontID(); + if( psp::fonttype::TrueType != psp::PrintFontManager::get().getFontType( nFontId ) ) + rArgs.mnFlags |= SAL_LAYOUT_DISABLE_GLYPH_PROCESSING; + else if( nFallbackLevel > 0 ) + rArgs.mnFlags &= ~SAL_LAYOUT_DISABLE_GLYPH_PROCESSING; + + GenericSalLayout* pLayout = NULL; + + if( m_pServerFont[ nFallbackLevel ] + && !(rArgs.mnFlags & SAL_LAYOUT_DISABLE_GLYPH_PROCESSING) ) + { +#ifdef ENABLE_GRAPHITE + // Is this a Graphite font? + if (GraphiteServerFontLayout::IsGraphiteEnabledFont(m_pServerFont[nFallbackLevel])) + { + pLayout = new GraphiteServerFontLayout(*m_pServerFont[nFallbackLevel]); + } + else +#endif + pLayout = new PspServerFontLayout( *m_pPrinterGfx, *m_pServerFont[nFallbackLevel], rArgs ); + } + else + pLayout = new PspFontLayout( *m_pPrinterGfx ); + + return pLayout; +} + +//-------------------------------------------------------------------------- + +sal_Bool PspGraphics::CreateFontSubset( + const rtl::OUString& rToFile, + const ImplFontData* pFont, + sal_Int32* pGlyphIDs, + sal_uInt8* pEncoding, + sal_Int32* pWidths, + int nGlyphCount, + FontSubsetInfo& rInfo + ) +{ + // in this context the pFont->GetFontId() is a valid PSP + // font since they are the only ones left after the PDF + // export has filtered its list of subsettable fonts (for + // which this method was created). The correct way would + // be to have the GlyphCache search for the ImplFontData pFont + psp::fontID aFont = pFont->GetFontId(); + + psp::PrintFontManager& rMgr = psp::PrintFontManager::get(); + bool bSuccess = rMgr.createFontSubset( rInfo, + aFont, + rToFile, + pGlyphIDs, + pEncoding, + pWidths, + nGlyphCount ); + return bSuccess; +} + +//-------------------------------------------------------------------------- + +const void* PspGraphics::GetEmbedFontData( const ImplFontData* pFont, const sal_Ucs* pUnicodes, sal_Int32* pWidths, FontSubsetInfo& rInfo, long* pDataLen ) +{ + // in this context the pFont->GetFontId() is a valid PSP + // font since they are the only ones left after the PDF + // export has filtered its list of subsettable fonts (for + // which this method was created). The correct way would + // be to have the GlyphCache search for the ImplFontData pFont + psp::fontID aFont = pFont->GetFontId(); + return PspGraphics::DoGetEmbedFontData( aFont, pUnicodes, pWidths, rInfo, pDataLen ); +} + +//-------------------------------------------------------------------------- + +void PspGraphics::FreeEmbedFontData( const void* pData, long nLen ) +{ + PspGraphics::DoFreeEmbedFontData( pData, nLen ); +} + +//-------------------------------------------------------------------------- + +const Ucs2SIntMap* PspGraphics::GetFontEncodingVector( const ImplFontData* pFont, const Ucs2OStrMap** pNonEncoded ) +{ + // in this context the pFont->GetFontId() is a valid PSP + // font since they are the only ones left after the PDF + // export has filtered its list of subsettable fonts (for + // which this method was created). The correct way would + // be to have the GlyphCache search for the ImplFontData pFont + psp::fontID aFont = pFont->GetFontId(); + return PspGraphics::DoGetFontEncodingVector( aFont, pNonEncoded ); +} + +//-------------------------------------------------------------------------- + +void PspGraphics::GetGlyphWidths( const ImplFontData* pFont, + bool bVertical, + Int32Vector& rWidths, + Ucs2UIntMap& rUnicodeEnc ) +{ + // in this context the pFont->GetFontId() is a valid PSP + // font since they are the only ones left after the PDF + // export has filtered its list of subsettable fonts (for + // which this method was created). The correct way would + // be to have the GlyphCache search for the ImplFontData pFont + psp::fontID aFont = pFont->GetFontId(); + PspGraphics::DoGetGlyphWidths( aFont, bVertical, rWidths, rUnicodeEnc ); +} + + +// static helpers of PspGraphics + +const void* PspGraphics::DoGetEmbedFontData( fontID aFont, const sal_Ucs* pUnicodes, sal_Int32* pWidths, FontSubsetInfo& rInfo, long* pDataLen ) +{ + psp::PrintFontManager& rMgr = psp::PrintFontManager::get(); + + psp::PrintFontInfo aFontInfo; + if( ! rMgr.getFontInfo( aFont, aFontInfo ) ) + return NULL; + + // fill in font info + rInfo.m_nAscent = aFontInfo.m_nAscend; + rInfo.m_nDescent = aFontInfo.m_nDescend; + rInfo.m_aPSName = rMgr.getPSName( aFont ); + + int xMin, yMin, xMax, yMax; + rMgr.getFontBoundingBox( aFont, xMin, yMin, xMax, yMax ); + + psp::CharacterMetric aMetrics[256]; + sal_Ucs aUnicodes[256]; + if( aFontInfo.m_aEncoding == RTL_TEXTENCODING_SYMBOL && aFontInfo.m_eType == psp::fonttype::Type1 ) + { + for( int i = 0; i < 256; i++ ) + aUnicodes[i] = pUnicodes[i] < 0x0100 ? pUnicodes[i] + 0xf000 : pUnicodes[i]; + pUnicodes = aUnicodes; + } + if( ! rMgr.getMetrics( aFont, pUnicodes, 256, aMetrics ) ) + return NULL; + + OString aSysPath = rMgr.getFontFileSysPath( aFont ); + struct stat aStat; + if( stat( aSysPath.getStr(), &aStat ) ) + return NULL; + int fd = open( aSysPath.getStr(), O_RDONLY ); + if( fd < 0 ) + return NULL; + void* pFile = mmap( NULL, aStat.st_size, PROT_READ, MAP_SHARED, fd, 0 ); + close( fd ); + if( pFile == MAP_FAILED ) + return NULL; + + *pDataLen = aStat.st_size; + + rInfo.m_aFontBBox = Rectangle( Point( xMin, yMin ), Size( xMax-xMin, yMax-yMin ) ); + rInfo.m_nCapHeight = yMax; // Well ... + + for( int i = 0; i < 256; i++ ) + pWidths[i] = (aMetrics[i].width > 0 ? aMetrics[i].width : 0); + + switch( aFontInfo.m_eType ) + { + case psp::fonttype::TrueType: + rInfo.m_nFontType = FontSubsetInfo::SFNT_TTF; + break; + case psp::fonttype::Type1: { + const bool bPFA = ((*(unsigned char*)pFile) < 0x80); + rInfo.m_nFontType = bPFA ? FontSubsetInfo::TYPE1_PFA : FontSubsetInfo::TYPE1_PFB; + } + break; + default: + return NULL; + } + + return pFile; +} + +void PspGraphics::DoFreeEmbedFontData( const void* pData, long nLen ) +{ + if( pData ) + munmap( (char*)pData, nLen ); +} + +const Ucs2SIntMap* PspGraphics::DoGetFontEncodingVector( fontID aFont, const Ucs2OStrMap** pNonEncoded ) +{ + psp::PrintFontManager& rMgr = psp::PrintFontManager::get(); + + psp::PrintFontInfo aFontInfo; + if( ! rMgr.getFontInfo( aFont, aFontInfo ) ) + { + if( pNonEncoded ) + *pNonEncoded = NULL; + return NULL; + } + + return rMgr.getEncodingMap( aFont, pNonEncoded ); +} + +void PspGraphics::DoGetGlyphWidths( psp::fontID aFont, + bool bVertical, + Int32Vector& rWidths, + Ucs2UIntMap& rUnicodeEnc ) +{ + psp::PrintFontManager& rMgr = psp::PrintFontManager::get(); + rMgr.getGlyphWidths( aFont, bVertical, rWidths, rUnicodeEnc ); +} +// ---------------------------------------------------------------------------- + +FontWidth PspGraphics::ToFontWidth (psp::width::type eWidth) +{ + switch (eWidth) + { + case psp::width::UltraCondensed: return WIDTH_ULTRA_CONDENSED; + case psp::width::ExtraCondensed: return WIDTH_EXTRA_CONDENSED; + case psp::width::Condensed: return WIDTH_CONDENSED; + case psp::width::SemiCondensed: return WIDTH_SEMI_CONDENSED; + case psp::width::Normal: return WIDTH_NORMAL; + case psp::width::SemiExpanded: return WIDTH_SEMI_EXPANDED; + case psp::width::Expanded: return WIDTH_EXPANDED; + case psp::width::ExtraExpanded: return WIDTH_EXTRA_EXPANDED; + case psp::width::UltraExpanded: return WIDTH_ULTRA_EXPANDED; + case psp::width::Unknown: return WIDTH_DONTKNOW; + default: + OSL_FAIL( "unknown width mapping" ); + break; + } + return WIDTH_DONTKNOW; +} + +FontWeight PspGraphics::ToFontWeight (psp::weight::type eWeight) +{ + switch (eWeight) + { + case psp::weight::Thin: return WEIGHT_THIN; + case psp::weight::UltraLight: return WEIGHT_ULTRALIGHT; + case psp::weight::Light: return WEIGHT_LIGHT; + case psp::weight::SemiLight: return WEIGHT_SEMILIGHT; + case psp::weight::Normal: return WEIGHT_NORMAL; + case psp::weight::Medium: return WEIGHT_MEDIUM; + case psp::weight::SemiBold: return WEIGHT_SEMIBOLD; + case psp::weight::Bold: return WEIGHT_BOLD; + case psp::weight::UltraBold: return WEIGHT_ULTRABOLD; + case psp::weight::Black: return WEIGHT_BLACK; + case psp::weight::Unknown: return WEIGHT_DONTKNOW; + default: + OSL_FAIL( "unknown weight mapping" ); + break; + } + return WEIGHT_DONTKNOW; +} + +FontPitch PspGraphics::ToFontPitch (psp::pitch::type ePitch) +{ + switch (ePitch) + { + case psp::pitch::Fixed: return PITCH_FIXED; + case psp::pitch::Variable: return PITCH_VARIABLE; + case psp::pitch::Unknown: return PITCH_DONTKNOW; + default: + OSL_FAIL( "unknown pitch mapping" ); + break; + } + return PITCH_DONTKNOW; +} + +FontItalic PspGraphics::ToFontItalic (psp::italic::type eItalic) +{ + switch (eItalic) + { + case psp::italic::Upright: return ITALIC_NONE; + case psp::italic::Oblique: return ITALIC_OBLIQUE; + case psp::italic::Italic: return ITALIC_NORMAL; + case psp::italic::Unknown: return ITALIC_DONTKNOW; + default: + OSL_FAIL( "unknown italic mapping" ); + break; + } + return ITALIC_DONTKNOW; +} + +FontFamily PspGraphics::ToFontFamily (psp::family::type eFamily) +{ + switch (eFamily) + { + case psp::family::Decorative: return FAMILY_DECORATIVE; + case psp::family::Modern: return FAMILY_MODERN; + case psp::family::Roman: return FAMILY_ROMAN; + case psp::family::Script: return FAMILY_SCRIPT; + case psp::family::Swiss: return FAMILY_SWISS; + case psp::family::System: return FAMILY_SYSTEM; + case psp::family::Unknown: return FAMILY_DONTKNOW; + default: + OSL_FAIL( "unknown family mapping" ); + break; + } + return FAMILY_DONTKNOW; +} + +ImplDevFontAttributes PspGraphics::Info2DevFontAttributes( const psp::FastPrintFontInfo& rInfo ) +{ + ImplDevFontAttributes aDFA; + aDFA.maName = rInfo.m_aFamilyName; + aDFA.maStyleName = rInfo.m_aStyleName; + aDFA.meFamily = ToFontFamily (rInfo.m_eFamilyStyle); + aDFA.meWeight = ToFontWeight (rInfo.m_eWeight); + aDFA.meItalic = ToFontItalic (rInfo.m_eItalic); + aDFA.meWidthType = ToFontWidth (rInfo.m_eWidth); + aDFA.mePitch = ToFontPitch (rInfo.m_ePitch); + aDFA.mbSymbolFlag = (rInfo.m_aEncoding == RTL_TEXTENCODING_SYMBOL); + aDFA.mbSubsettable = rInfo.m_bSubsettable; + aDFA.mbEmbeddable = rInfo.m_bEmbeddable; + + switch( rInfo.m_eType ) + { + case psp::fonttype::Builtin: + aDFA.mnQuality = 1024; + aDFA.mbDevice = true; + break; + case psp::fonttype::TrueType: + aDFA.mnQuality = 512; + aDFA.mbDevice = false; + break; + case psp::fonttype::Type1: + aDFA.mnQuality = 0; + aDFA.mbDevice = false; + break; + default: + aDFA.mnQuality = 0; + aDFA.mbDevice = false; + break; + } + + aDFA.mbOrientation = true; + + // add font family name aliases + ::std::list< OUString >::const_iterator it = rInfo.m_aAliases.begin(); + bool bHasMapNames = false; + for(; it != rInfo.m_aAliases.end(); ++it ) + { + if( bHasMapNames ) + aDFA.maMapNames.Append( ';' ); + aDFA.maMapNames.Append( (*it).getStr() ); + bHasMapNames = true; + } + +#if OSL_DEBUG_LEVEL > 2 + if( bHasMapNames ) + { + ByteString aOrigName( aDFA.maName, osl_getThreadTextEncoding() ); + ByteString aAliasNames( aDFA.maMapNames, osl_getThreadTextEncoding() ); + fprintf( stderr, "using alias names \"%s\" for font family \"%s\"\n", + aAliasNames.GetBuffer(), aOrigName.GetBuffer() ); + } +#endif + + return aDFA; +} + +// ----------------------------------------------------------------------- + +void PspGraphics::AnnounceFonts( ImplDevFontList* pFontList, const psp::FastPrintFontInfo& aInfo ) +{ + int nQuality = 0; + + if( aInfo.m_eType == psp::fonttype::TrueType ) + { + // asian type 1 fonts are not known + psp::PrintFontManager& rMgr = psp::PrintFontManager::get(); + ByteString aFileName( rMgr.getFontFileSysPath( aInfo.m_nID ) ); + int nPos = aFileName.SearchBackward( '_' ); + if( nPos == STRING_NOTFOUND || aFileName.GetChar( nPos+1 ) == '.' ) + nQuality += 5; + else + { + static const char* pLangBoost = NULL; + static bool bOnce = true; + if( bOnce ) + { + bOnce = false; + const LanguageType aLang = Application::GetSettings().GetUILanguage(); + switch( aLang ) + { + case LANGUAGE_JAPANESE: + pLangBoost = "jan"; + break; + case LANGUAGE_CHINESE: + case LANGUAGE_CHINESE_SIMPLIFIED: + case LANGUAGE_CHINESE_SINGAPORE: + pLangBoost = "zhs"; + break; + case LANGUAGE_CHINESE_TRADITIONAL: + case LANGUAGE_CHINESE_HONGKONG: + case LANGUAGE_CHINESE_MACAU: + pLangBoost = "zht"; + break; + case LANGUAGE_KOREAN: + case LANGUAGE_KOREAN_JOHAB: + pLangBoost = "kor"; + break; + } + } + + if( pLangBoost ) + if( aFileName.Copy( nPos+1, 3 ).EqualsIgnoreCaseAscii( pLangBoost ) ) + nQuality += 10; + } + } + + ImplPspFontData* pFD = new ImplPspFontData( aInfo ); + pFD->mnQuality += nQuality; + pFontList->Add( pFD ); +} + +bool PspGraphics::filterText( const String& rOrig, String& rNewText, xub_StrLen nIndex, xub_StrLen& rLen, xub_StrLen& rCutStart, xub_StrLen& rCutStop ) +{ + if( ! m_pPhoneNr ) + return false; + + rCutStop = rCutStart = STRING_NOTFOUND; + +#define FAX_PHONE_TOKEN "@@#" +#define FAX_PHONE_TOKEN_LENGTH 3 +#define FAX_END_TOKEN "@@" +#define FAX_END_TOKEN_LENGTH 2 + + bool bRet = false; + bool bStarted = false; + bool bStopped = false; + sal_uInt16 nPos; + sal_uInt16 nStart = 0; + sal_uInt16 nStop = rLen; + String aPhone = rOrig.Copy( nIndex, rLen ); + + if( ! m_bPhoneCollectionActive ) + { + if( ( nPos = aPhone.SearchAscii( FAX_PHONE_TOKEN ) ) != STRING_NOTFOUND ) + { + nStart = nPos; + m_bPhoneCollectionActive = true; + m_aPhoneCollection.Erase(); + bRet = true; + bStarted = true; + } + } + if( m_bPhoneCollectionActive ) + { + bRet = true; + nPos = bStarted ? nStart + FAX_PHONE_TOKEN_LENGTH : 0; + if( ( nPos = aPhone.SearchAscii( FAX_END_TOKEN, nPos ) ) != STRING_NOTFOUND ) + { + m_bPhoneCollectionActive = false; + nStop = nPos + FAX_END_TOKEN_LENGTH; + bStopped = true; + } + int nTokenStart = nStart + (bStarted ? FAX_PHONE_TOKEN_LENGTH : 0); + int nTokenStop = nStop - (bStopped ? FAX_END_TOKEN_LENGTH : 0); + m_aPhoneCollection += aPhone.Copy( nTokenStart, nTokenStop - nTokenStart ); + if( ! m_bPhoneCollectionActive ) + { + m_pPhoneNr->AppendAscii( "<Fax#>" ); + m_pPhoneNr->Append( m_aPhoneCollection ); + m_pPhoneNr->AppendAscii( "</Fax#>" ); + m_aPhoneCollection.Erase(); + } + } + if( m_aPhoneCollection.Len() > 1024 ) + { + m_bPhoneCollectionActive = false; + m_aPhoneCollection.Erase(); + bRet = false; + } + + if( bRet && m_bSwallowFaxNo ) + { + rLen -= nStop - nStart; + rCutStart = nStart+nIndex; + rCutStop = nStop+nIndex; + if( rCutStart ) + rNewText = rOrig.Copy( 0, rCutStart ); + rNewText += rOrig.Copy( rCutStop ); + } + + return bRet && m_bSwallowFaxNo; +} + +bool PspGraphics::drawAlphaBitmap( const SalTwoRect&, + const SalBitmap&, + const SalBitmap& ) +{ + return false; +} + +bool PspGraphics::drawAlphaRect( long, long, long, long, sal_uInt8 ) +{ + return false; +} + +SystemGraphicsData PspGraphics::GetGraphicsData() const +{ + SystemGraphicsData aRes; + aRes.nSize = sizeof(aRes); + aRes.hDrawable = 0; + aRes.pXRenderFormat = 0; + return aRes; +} + +SystemFontData PspGraphics::GetSysFontData( int nFallbacklevel ) const +{ + SystemFontData aSysFontData; + + if (nFallbacklevel >= MAX_FALLBACK) nFallbacklevel = MAX_FALLBACK - 1; + if (nFallbacklevel < 0 ) nFallbacklevel = 0; + + aSysFontData.nSize = sizeof( SystemFontData ); + aSysFontData.nFontId = 0; + aSysFontData.nFontFlags = 0; + aSysFontData.bFakeBold = false; + aSysFontData.bFakeItalic = false; + aSysFontData.bAntialias = true; + return aSysFontData; +} + +bool PspGraphics::supportsOperation( OutDevSupportType ) const +{ + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/gdi/salbmp.cxx b/vcl/unx/generic/gdi/salbmp.cxx new file mode 100644 index 000000000000..0c3babc48fdb --- /dev/null +++ b/vcl/unx/generic/gdi/salbmp.cxx @@ -0,0 +1,1160 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <string.h> +#include <stdio.h> +#include <errno.h> +#ifdef FREEBSD +#include <sys/types.h> +#endif + +#include <osl/endian.h> +#include <rtl/memory.h> + +#include <vcl/bitmap.hxx> +#include <vcl/salbtype.hxx> +#include <com/sun/star/beans/XFastPropertySet.hpp> + +#include <tools/prex.h> +#include "unx/Xproto.h" +#include <tools/postx.h> +#include <unx/salunx.h> +#include <unx/saldata.hxx> +#include <unx/saldisp.hxx> +#include <unx/salgdi.h> +#include <unx/salbmp.h> +#include <unx/salinst.h> + +// ----------- +// - Defines - +// ----------- + +#define SAL_DRAWPIXMAP_MAX_EXT 4096 + +// ------------- +// - SalBitmap - +// ------------- + +SalBitmap* X11SalInstance::CreateSalBitmap() +{ + return new X11SalBitmap(); +} + +ImplSalBitmapCache* X11SalBitmap::mpCache = NULL; +sal_uLong X11SalBitmap::mnCacheInstCount = 0; + +// ----------------------------------------------------------------------------- + +X11SalBitmap::X11SalBitmap() : + mpDIB( NULL ), + mpDDB( NULL ), + mbGrey( false ) +{ +} + +// ----------------------------------------------------------------------------- + +X11SalBitmap::~X11SalBitmap() +{ + Destroy(); +} + +// ----------------------------------------------------------------------------- + +void X11SalBitmap::ImplCreateCache() +{ + if( !mnCacheInstCount++ ) + mpCache = new ImplSalBitmapCache; +} + +// ----------------------------------------------------------------------------- + +void X11SalBitmap::ImplDestroyCache() +{ + DBG_ASSERT( mnCacheInstCount, "X11SalBitmap::ImplDestroyCache(): underflow" ); + + if( mnCacheInstCount && !--mnCacheInstCount ) + delete mpCache, mpCache = NULL; +} + +// ----------------------------------------------------------------------------- + +void X11SalBitmap::ImplRemovedFromCache() +{ + if( mpDDB ) + delete mpDDB, mpDDB = NULL; +} + +// ----------------------------------------------------------------------------- + +BitmapBuffer* X11SalBitmap::ImplCreateDIB( const Size& rSize, sal_uInt16 nBitCount, const BitmapPalette& rPal ) +{ + DBG_ASSERT( nBitCount == 1 || nBitCount == 4 || nBitCount == 8 || nBitCount == 16 || nBitCount == 24, "Unsupported BitCount!" ); + + BitmapBuffer* pDIB = NULL; + + if( rSize.Width() && rSize.Height() ) + { + try + { + pDIB = new BitmapBuffer; + } + catch( std::bad_alloc& ) + { + pDIB = NULL; + } + + if( pDIB ) + { + const sal_uInt16 nColors = ( nBitCount <= 8 ) ? ( 1 << nBitCount ) : 0; + + pDIB->mnFormat = BMP_FORMAT_BOTTOM_UP; + + switch( nBitCount ) + { + case( 1 ): pDIB->mnFormat |= BMP_FORMAT_1BIT_MSB_PAL; break; + case( 4 ): pDIB->mnFormat |= BMP_FORMAT_4BIT_MSN_PAL; break; + case( 8 ): pDIB->mnFormat |= BMP_FORMAT_8BIT_PAL; break; +#ifdef OSL_BIGENDIAN + case(16 ): + pDIB->mnFormat|= BMP_FORMAT_16BIT_TC_MSB_MASK; + pDIB->maColorMask = ColorMask( 0xf800, 0x07e0, 0x001f ); + break; +#else + case(16 ): + pDIB->mnFormat|= BMP_FORMAT_16BIT_TC_LSB_MASK; + pDIB->maColorMask = ColorMask( 0xf800, 0x07e0, 0x001f ); + break; +#endif + default: + nBitCount = 24; + //fall through + case 24: + pDIB->mnFormat |= BMP_FORMAT_24BIT_TC_BGR; + break; + } + + pDIB->mnWidth = rSize.Width(); + pDIB->mnHeight = rSize.Height(); + pDIB->mnScanlineSize = AlignedWidth4Bytes( pDIB->mnWidth * nBitCount ); + pDIB->mnBitCount = nBitCount; + + if( nColors ) + { + pDIB->maPalette = rPal; + pDIB->maPalette.SetEntryCount( nColors ); + } + + try + { + pDIB->mpBits = new sal_uInt8[ pDIB->mnScanlineSize * pDIB->mnHeight ]; + } + catch(std::bad_alloc&) + { + delete pDIB; + pDIB = NULL; + } + } + } + else + pDIB = NULL; + + return pDIB; +} + +// ----------------------------------------------------------------------------- + +BitmapBuffer* X11SalBitmap::ImplCreateDIB( Drawable aDrawable, + int nScreen, + long nDrawableDepth, + long nX, long nY, + long nWidth, long nHeight, + bool bGrey ) +{ + BitmapBuffer* pDIB = NULL; + + if( aDrawable && nWidth && nHeight && nDrawableDepth ) + { + SalDisplay* pSalDisp = GetX11SalData()->GetDisplay(); + SalXLib* pXLib = pSalDisp->GetXLib(); + Display* pXDisp = pSalDisp->GetDisplay(); + + // do not die on XError here + // alternatively one could check the coordinates for being offscreen + // but this call can actually work on servers with backing store + // defaults even if the rectangle is offscreen + // so better catch the XError + pXLib->PushXErrorLevel( true ); + XImage* pImage = XGetImage( pXDisp, aDrawable, nX, nY, nWidth, nHeight, AllPlanes, ZPixmap ); + bool bWasError = pXLib->HasXErrorOccurred() && pXLib->GetLastXErrorRequestCode() == X_GetImage; + pXLib->PopXErrorLevel(); + + if( ! bWasError && pImage && pImage->data ) + { + const SalTwoRect aTwoRect = { 0, 0, nWidth, nHeight, 0, 0, nWidth, nHeight }; + BitmapBuffer aSrcBuf; + sal_uLong nDstFormat = BMP_FORMAT_BOTTOM_UP; + const BitmapPalette* pDstPal = NULL; + + aSrcBuf.mnFormat = BMP_FORMAT_TOP_DOWN; + aSrcBuf.mnWidth = nWidth; + aSrcBuf.mnHeight = nHeight; + aSrcBuf.mnBitCount = pImage->bits_per_pixel; + aSrcBuf.mnScanlineSize = pImage->bytes_per_line; + aSrcBuf.mpBits = (sal_uInt8*) pImage->data; + + pImage->red_mask = pSalDisp->GetVisual( nScreen ).red_mask; + pImage->green_mask = pSalDisp->GetVisual( nScreen ).green_mask; + pImage->blue_mask = pSalDisp->GetVisual( nScreen ).blue_mask; + + switch( aSrcBuf.mnBitCount ) + { + case( 1 ): + { + aSrcBuf.mnFormat |= ( LSBFirst == pImage->bitmap_bit_order ? BMP_FORMAT_1BIT_LSB_PAL : BMP_FORMAT_1BIT_MSB_PAL ); + nDstFormat |= BMP_FORMAT_1BIT_MSB_PAL; + } + break; + + case( 4 ): + { + aSrcBuf.mnFormat |= ( LSBFirst == pImage->bitmap_bit_order ? BMP_FORMAT_4BIT_LSN_PAL : BMP_FORMAT_4BIT_MSN_PAL ); + nDstFormat |= BMP_FORMAT_4BIT_MSN_PAL; + } + break; + + case( 8 ): + { + aSrcBuf.mnFormat |= BMP_FORMAT_8BIT_PAL; + nDstFormat |= BMP_FORMAT_8BIT_PAL; + } + break; + + case( 16 ): + { + nDstFormat |= BMP_FORMAT_24BIT_TC_BGR; + aSrcBuf.maColorMask = ColorMask( pImage->red_mask, pImage->green_mask, pImage->blue_mask ); + + if( LSBFirst == pImage->byte_order ) + { + aSrcBuf.mnFormat |= BMP_FORMAT_16BIT_TC_LSB_MASK; + } + else + { + aSrcBuf.mnFormat |= BMP_FORMAT_16BIT_TC_MSB_MASK; + // aSrcBuf.maColorMask = ColorMask( pImage->red_mask ), SWAPSHORT( pImage->green_mask ), SWAPSHORT( pImage->blue_mask ) ); + } + } + break; + + case( 24 ): + { + if( ( LSBFirst == pImage->byte_order ) && ( pImage->red_mask == 0xFF ) ) + aSrcBuf.mnFormat |= BMP_FORMAT_24BIT_TC_RGB; + else + aSrcBuf.mnFormat |= BMP_FORMAT_24BIT_TC_BGR; + + nDstFormat |= BMP_FORMAT_24BIT_TC_BGR; + } + break; + + case( 32 ): + { + if( LSBFirst == pImage->byte_order ) + aSrcBuf.mnFormat |= ( pSalDisp->GetVisual(nScreen).red_mask == 0xFF ? BMP_FORMAT_32BIT_TC_RGBA : BMP_FORMAT_32BIT_TC_BGRA ); + else + aSrcBuf.mnFormat |= ( pSalDisp->GetVisual(nScreen).red_mask == 0xFF ? BMP_FORMAT_32BIT_TC_ABGR : BMP_FORMAT_32BIT_TC_ARGB ); + + nDstFormat |= BMP_FORMAT_24BIT_TC_BGR; + } + break; + } + + BitmapPalette& rPal = aSrcBuf.maPalette; + + if( aSrcBuf.mnBitCount == 1 ) + { + rPal.SetEntryCount( 2 ); + pDstPal = &rPal; + + rPal[ 0 ] = Color( COL_BLACK ); + rPal[ 1 ] = Color( COL_WHITE ); + } + else if( pImage->depth == 8 && bGrey ) + { + rPal.SetEntryCount( 256 ); + pDstPal = &rPal; + + for( sal_uInt16 i = 0; i < 256; i++ ) + { + BitmapColor& rBmpCol = rPal[ i ]; + + rBmpCol.SetRed( i ); + rBmpCol.SetGreen( i ); + rBmpCol.SetBlue( i ); + } + + } + else if( aSrcBuf.mnBitCount <= 8 ) + { + const SalColormap& rColMap = pSalDisp->GetColormap( nScreen ); + const sal_uInt16 nCols = Min( (sal_uLong)rColMap.GetUsed(), (sal_uLong)(1 << nDrawableDepth) ); + + rPal.SetEntryCount( nCols ); + pDstPal = &rPal; + + for( sal_uInt16 i = 0; i < nCols; i++ ) + { + const SalColor nColor( rColMap.GetColor( i ) ); + BitmapColor& rBmpCol = rPal[ i ]; + + rBmpCol.SetRed( SALCOLOR_RED( nColor ) ); + rBmpCol.SetGreen( SALCOLOR_GREEN( nColor ) ); + rBmpCol.SetBlue( SALCOLOR_BLUE( nColor ) ); + } + } + + nDstFormat = aSrcBuf.mnFormat; + pDIB = StretchAndConvert( aSrcBuf, aTwoRect, nDstFormat, + const_cast<BitmapPalette*>(pDstPal), &aSrcBuf.maColorMask ); + XDestroyImage( pImage ); + } + } + + return pDIB; +} + +// ----------------------------------------------------------------------------- + +XImage* X11SalBitmap::ImplCreateXImage( SalDisplay *pSalDisp, int nScreen, long nDepth, const SalTwoRect& rTwoRect ) const +{ + XImage* pImage = NULL; + + if( !mpDIB && mpDDB ) + { + const_cast<X11SalBitmap*>(this)->mpDIB = + ImplCreateDIB( mpDDB->ImplGetPixmap(), + mpDDB->ImplGetScreen(), + mpDDB->ImplGetDepth(), + 0, 0, + mpDDB->ImplGetWidth(), + mpDDB->ImplGetHeight(), + mbGrey ); + } + + if( mpDIB && mpDIB->mnWidth && mpDIB->mnHeight ) + { + Display* pXDisp = pSalDisp->GetDisplay(); + long nWidth = rTwoRect.mnDestWidth; + long nHeight = rTwoRect.mnDestHeight; + + if( 1 == GetBitCount() ) + nDepth = 1; + + pImage = XCreateImage( pXDisp, pSalDisp->GetVisual( nScreen ).GetVisual(), + nDepth, ( 1 == nDepth ) ? XYBitmap :ZPixmap, 0, NULL, + nWidth, nHeight, 32, 0 ); + + if( pImage ) + { + BitmapBuffer* pDstBuf; + sal_uLong nDstFormat = BMP_FORMAT_TOP_DOWN; + BitmapPalette* pPal = NULL; + ColorMask* pMask = NULL; + + switch( pImage->bits_per_pixel ) + { + case( 1 ): + nDstFormat |= ( LSBFirst == pImage->bitmap_bit_order ? BMP_FORMAT_1BIT_LSB_PAL : BMP_FORMAT_1BIT_MSB_PAL ); + break; + + case( 4 ): + nDstFormat |= ( LSBFirst == pImage->bitmap_bit_order ? BMP_FORMAT_4BIT_LSN_PAL : BMP_FORMAT_4BIT_MSN_PAL ); + break; + + case( 8 ): + nDstFormat |= BMP_FORMAT_8BIT_PAL; + break; + + case( 16 ): + { + #ifdef OSL_BIGENDIAN + + if( MSBFirst == pImage->byte_order ) + nDstFormat |= BMP_FORMAT_16BIT_TC_MSB_MASK; + else + nDstFormat |= BMP_FORMAT_16BIT_TC_LSB_MASK; + + #else /* OSL_LITENDIAN */ + + nDstFormat |= BMP_FORMAT_16BIT_TC_LSB_MASK; + if( MSBFirst == pImage->byte_order ) + pImage->byte_order = LSBFirst; + + #endif + + pMask = new ColorMask( pImage->red_mask, pImage->green_mask, pImage->blue_mask ); + } + break; + + case( 24 ): + { + if( ( LSBFirst == pImage->byte_order ) && ( pImage->red_mask == 0xFF ) ) + nDstFormat |= BMP_FORMAT_24BIT_TC_RGB; + else + nDstFormat |= BMP_FORMAT_24BIT_TC_BGR; + } + break; + + case( 32 ): + { + if( LSBFirst == pImage->byte_order ) + nDstFormat |= ( pImage->red_mask == 0xFF ? BMP_FORMAT_32BIT_TC_RGBA : BMP_FORMAT_32BIT_TC_BGRA ); + else + nDstFormat |= ( pImage->red_mask == 0xFF ? BMP_FORMAT_32BIT_TC_ABGR : BMP_FORMAT_32BIT_TC_ARGB ); + } + break; + } + + if( pImage->depth == 1 ) + { + pPal = new BitmapPalette( 2 ); + (*pPal)[ 0 ] = Color( COL_BLACK ); + (*pPal)[ 1 ] = Color( COL_WHITE ); + } + else if( pImage->depth == 8 && mbGrey ) + { + pPal = new BitmapPalette( 256 ); + + for( sal_uInt16 i = 0; i < 256; i++ ) + { + BitmapColor& rBmpCol = (*pPal)[ i ]; + + rBmpCol.SetRed( i ); + rBmpCol.SetGreen( i ); + rBmpCol.SetBlue( i ); + } + + } + else if( pImage->depth <= 8 ) + { + const SalColormap& rColMap = pSalDisp->GetColormap( nScreen ); + const sal_uInt16 nCols = Min( (sal_uLong)rColMap.GetUsed(), (sal_uLong)(1 << pImage->depth) ); + + pPal = new BitmapPalette( nCols ); + + for( sal_uInt16 i = 0; i < nCols; i++ ) + { + const SalColor nColor( rColMap.GetColor( i ) ); + BitmapColor& rBmpCol = (*pPal)[ i ]; + + rBmpCol.SetRed( SALCOLOR_RED( nColor ) ); + rBmpCol.SetGreen( SALCOLOR_GREEN( nColor ) ); + rBmpCol.SetBlue( SALCOLOR_BLUE( nColor ) ); + } + } + + pDstBuf = StretchAndConvert( *mpDIB, rTwoRect, nDstFormat, pPal, pMask ); + delete pPal; + delete pMask; + + if( pDstBuf && pDstBuf->mpBits ) + { + // set data in buffer as data member in pImage + pImage->data = (char*) pDstBuf->mpBits; + + // destroy buffer; don't destroy allocated data in buffer + delete pDstBuf; + } + else + { + XDestroyImage( pImage ); + pImage = NULL; + } + } + } + + return pImage; +} + +// ----------------------------------------------------------------------------- +bool X11SalBitmap::ImplCreateFromDrawable( Drawable aDrawable, + int nScreen, long nDrawableDepth, + long nX, long nY, long nWidth, long nHeight ) +{ + Destroy(); + + if( aDrawable && nWidth && nHeight && nDrawableDepth ) + mpDDB = new ImplSalDDB( aDrawable, nScreen, nDrawableDepth, nX, nY, nWidth, nHeight ); + + return( mpDDB != NULL ); +} +// ----------------------------------------------------------------------------- + +bool +X11SalBitmap::SnapShot (Display* pDisplay, XLIB_Window hWindow) +{ + if (hWindow != None) + { + XWindowAttributes aAttribute; + XGetWindowAttributes (pDisplay, hWindow, &aAttribute); + if (aAttribute.map_state == IsViewable) + { + // get coordinates relative to root window + XLIB_Window hPetitFleur; + int nRootX, nRootY; + + if (XTranslateCoordinates (pDisplay, hWindow, aAttribute.root, + 0, 0, &nRootX, &nRootY, &hPetitFleur)) + { + XWindowAttributes aRootAttribute; + XGetWindowAttributes (pDisplay, aAttribute.root, &aRootAttribute); + + int width = aAttribute.width; + int height = aAttribute.height; + int x = nRootX; + int y = nRootY; + + // horizontal range check + if (x < 0) + { + width = width + x; + x = 0; + } + else + if (x > aRootAttribute.width) + { + width = 0; + x = aRootAttribute.width; + } + else + if (x + width > aRootAttribute.width) + { + width = aRootAttribute.width - x; + } + + // vertical range check + if (y < 0) + { + height = height + y; + y = 0; + } + else + if (y > aRootAttribute.height) + { + height = 0; + y = aRootAttribute.height; + } + else + if (y + height > aRootAttribute.height) + { + height = aRootAttribute.height - y; + } + + if ((width > 0) && (height > 0)) + { + XImage* pImage = XGetImage( pDisplay, aAttribute.root, + x, y, width, height, AllPlanes, ZPixmap ); + bool bSnapShot = ImplCreateFromXImage( pDisplay, + aAttribute.root, + XScreenNumberOfScreen( aAttribute.screen ), + pImage ); + XDestroyImage (pImage); + + return bSnapShot; + } + } + } + } + + return False; +} + +bool +X11SalBitmap::ImplCreateFromXImage (Display* pDisplay, XLIB_Window hWindow, int nScreen, XImage* pImage) +{ + Destroy(); + + if (pImage != NULL && pImage->width != 0 && pImage->height != 0 && pImage->depth != 0) + { + mpDDB = new ImplSalDDB (pDisplay, hWindow, nScreen, pImage); + return True; + } + return False; +} + +ImplSalDDB* X11SalBitmap::ImplGetDDB( Drawable aDrawable, + int nScreen, + long nDrawableDepth, + const SalTwoRect& rTwoRect ) const +{ + if( !mpDDB || !mpDDB->ImplMatches( nScreen, nDrawableDepth, rTwoRect ) ) + { + if( mpDDB ) + { + // do we already have a DIB? if not, create aDIB from current DDB first + if( !mpDIB ) + { + const_cast<X11SalBitmap*>(this)->mpDIB = ImplCreateDIB( mpDDB->ImplGetPixmap(), + mpDDB->ImplGetScreen(), + mpDDB->ImplGetDepth(), + 0, 0, + mpDDB->ImplGetWidth(), + mpDDB->ImplGetHeight(), + mbGrey ); + } + + delete mpDDB, const_cast<X11SalBitmap*>(this)->mpDDB = NULL; + } + + if( mpCache ) + mpCache->ImplRemove( const_cast<X11SalBitmap*>(this) ); + + SalTwoRect aTwoRect( rTwoRect ); + if( aTwoRect.mnSrcX < 0 ) + { + aTwoRect.mnSrcWidth += aTwoRect.mnSrcX; + aTwoRect.mnSrcX = 0; + } + if( aTwoRect.mnSrcY < 0 ) + { + aTwoRect.mnSrcHeight += aTwoRect.mnSrcY; + aTwoRect.mnSrcY = 0; + } + + // create new DDB from DIB + const Size aSize( GetSize() ); + if( aTwoRect.mnSrcWidth == aTwoRect.mnDestWidth && + aTwoRect.mnSrcHeight == aTwoRect.mnDestHeight ) + { + aTwoRect.mnSrcX = aTwoRect.mnSrcY = aTwoRect.mnDestX = aTwoRect.mnDestY = 0; + aTwoRect.mnSrcWidth = aTwoRect.mnDestWidth = aSize.Width(); + aTwoRect.mnSrcHeight = aTwoRect.mnDestHeight = aSize.Height(); + } + else if( aTwoRect.mnSrcWidth+aTwoRect.mnSrcX > aSize.Width() || + aTwoRect.mnSrcHeight+aTwoRect.mnSrcY > aSize.Height() ) + { + // #i47823# this should not happen at all, but does nonetheless + // because BitmapEx allows for mask bitmaps of different size + // than image bitmap (broken) + if( aTwoRect.mnSrcX >= aSize.Width() || + aTwoRect.mnSrcY >= aSize.Height() ) + return NULL; // this would be a really mad case + + if( aTwoRect.mnSrcWidth+aTwoRect.mnSrcX > aSize.Width() ) + { + aTwoRect.mnSrcWidth = aSize.Width()-aTwoRect.mnSrcX; + if( aTwoRect.mnSrcWidth < 1 ) + { + aTwoRect.mnSrcX = 0; + aTwoRect.mnSrcWidth = aSize.Width(); + } + } + if( aTwoRect.mnSrcHeight+aTwoRect.mnSrcY > aSize.Height() ) + { + aTwoRect.mnSrcHeight = aSize.Height() - aTwoRect.mnSrcY; + if( aTwoRect.mnSrcHeight < 1 ) + { + aTwoRect.mnSrcY = 0; + aTwoRect.mnSrcHeight = aSize.Height(); + } + } + } + + XImage* pImage = ImplCreateXImage( GetX11SalData()->GetDisplay(), nScreen, + nDrawableDepth, aTwoRect ); + + if( pImage ) + { + const_cast<X11SalBitmap*>(this)->mpDDB = new ImplSalDDB( pImage, aDrawable, nScreen, aTwoRect ); + delete[] pImage->data, pImage->data = NULL; + XDestroyImage( pImage ); + + if( mpCache ) + mpCache->ImplAdd( const_cast<X11SalBitmap*>(this), mpDDB->ImplGetMemSize() ); + } + } + + return mpDDB; +} + +// ----------------------------------------------------------------------------- + +void X11SalBitmap::ImplDraw( Drawable aDrawable, + int nScreen, + long nDrawableDepth, + const SalTwoRect& rTwoRect, + const GC& rGC ) const +{ + ImplGetDDB( aDrawable, nScreen, nDrawableDepth, rTwoRect ); + if( mpDDB ) + mpDDB->ImplDraw( aDrawable, nDrawableDepth, rTwoRect, rGC ); +} + +// ----------------------------------------------------------------------------- + +bool X11SalBitmap::Create( const Size& rSize, sal_uInt16 nBitCount, const BitmapPalette& rPal ) +{ + Destroy(); + mpDIB = ImplCreateDIB( rSize, nBitCount, rPal ); + + return( mpDIB != NULL ); +} + +// ----------------------------------------------------------------------------- + +bool X11SalBitmap::Create( const SalBitmap& rSSalBmp ) +{ + Destroy(); + + const X11SalBitmap& rSalBmp = static_cast<const X11SalBitmap&>( rSSalBmp ); + + if( rSalBmp.mpDIB ) + { + // TODO: reference counting... + mpDIB = new BitmapBuffer( *rSalBmp.mpDIB ); + // TODO: get rid of this when BitmapBuffer gets copy constructor + try + { + mpDIB->mpBits = new sal_uInt8[ mpDIB->mnScanlineSize * mpDIB->mnHeight ]; + } + catch( std::bad_alloc& ) + { + delete mpDIB; + mpDIB = NULL; + } + + if( mpDIB ) + memcpy( mpDIB->mpBits, rSalBmp.mpDIB->mpBits, mpDIB->mnScanlineSize * mpDIB->mnHeight ); + } + else if( rSalBmp.mpDDB ) + ImplCreateFromDrawable( rSalBmp.mpDDB->ImplGetPixmap(), + rSalBmp.mpDDB->ImplGetScreen(), + rSalBmp.mpDDB->ImplGetDepth(), + 0, 0, rSalBmp.mpDDB->ImplGetWidth(), rSalBmp.mpDDB->ImplGetHeight() ); + + return( ( !rSalBmp.mpDIB && !rSalBmp.mpDDB ) || + ( rSalBmp.mpDIB && ( mpDIB != NULL ) ) || + ( rSalBmp.mpDDB && ( mpDDB != NULL ) ) ); +} + +// ----------------------------------------------------------------------------- + +bool X11SalBitmap::Create( const SalBitmap&, SalGraphics* ) +{ + return sal_False; +} + +// ----------------------------------------------------------------------------- + +bool X11SalBitmap::Create( const SalBitmap&, sal_uInt16 ) +{ + return sal_False; +} + +// ----------------------------------------------------------------------------- + +bool X11SalBitmap::Create( const ::com::sun::star::uno::Reference< ::com::sun::star::rendering::XBitmapCanvas > xBitmapCanvas, Size& rSize, bool bMask ) +{ + ::com::sun::star::uno::Reference< ::com::sun::star::beans::XFastPropertySet > xFastPropertySet( xBitmapCanvas, ::com::sun::star::uno::UNO_QUERY ); + if( xFastPropertySet.get() ) { + sal_Int32 depth; + ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any > args; + + if( xFastPropertySet->getFastPropertyValue(bMask ? 2 : 1) >>= args ) { + long pixmapHandle; + if( ( args[1] >>= pixmapHandle ) && ( args[2] >>= depth ) ) { + + mbGrey = bMask; + bool bSuccess = ImplCreateFromDrawable( pixmapHandle, 0, depth, 0, 0, (long) rSize.Width(), (long) rSize.Height() ); + bool bFreePixmap; + if( bSuccess && (args[0] >>= bFreePixmap) && bFreePixmap ) + XFreePixmap( GetX11SalData()->GetDisplay()->GetDisplay(), pixmapHandle ); + + return bSuccess; + } + } + } + + return false; +} + +// ----------------------------------------------------------------------------- + +void X11SalBitmap::Destroy() +{ + if( mpDIB ) + { + delete[] mpDIB->mpBits; + delete mpDIB, mpDIB = NULL; + } + + if( mpDDB ) + delete mpDDB, mpDDB = NULL; + + if( mpCache ) + mpCache->ImplRemove( this ); +} + +// ----------------------------------------------------------------------------- + +Size X11SalBitmap::GetSize() const +{ + Size aSize; + + if( mpDIB ) + aSize.Width() = mpDIB->mnWidth, aSize.Height() = mpDIB->mnHeight; + else if( mpDDB ) + aSize.Width() = mpDDB->ImplGetWidth(), aSize.Height() = mpDDB->ImplGetHeight(); + + return aSize; +} + +// ----------------------------------------------------------------------------- + +sal_uInt16 X11SalBitmap::GetBitCount() const +{ + sal_uInt16 nBitCount; + + if( mpDIB ) + nBitCount = mpDIB->mnBitCount; + else if( mpDDB ) + nBitCount = mpDDB->ImplGetDepth(); + else + nBitCount = 0; + + return nBitCount; +} + +// ----------------------------------------------------------------------------- + +BitmapBuffer* X11SalBitmap::AcquireBuffer( bool ) +{ + if( !mpDIB && mpDDB ) + { + mpDIB = ImplCreateDIB( mpDDB->ImplGetPixmap(), + mpDDB->ImplGetScreen(), + mpDDB->ImplGetDepth(), + 0, 0, mpDDB->ImplGetWidth(), mpDDB->ImplGetHeight(), mbGrey ); + } + + return mpDIB; +} + +// ----------------------------------------------------------------------------- + +void X11SalBitmap::ReleaseBuffer( BitmapBuffer*, bool bReadOnly ) +{ + if( !bReadOnly ) + { + if( mpDDB ) + delete mpDDB, mpDDB = NULL; + + if( mpCache ) + mpCache->ImplRemove( this ); + } +} + +// ----------------------------------------------------------------------------- + +bool X11SalBitmap::GetSystemData( BitmapSystemData& rData ) +{ + if( mpDDB ) + { + // Rename/retype pDummy to your likings (though X11 Pixmap is + // prolly not a good idea, since it's accessed from + // non-platform aware code in vcl/bitmap.hxx) + rData.aPixmap = (void*)mpDDB->ImplGetPixmap(); + rData.mnWidth = mpDDB->ImplGetWidth (); + rData.mnHeight = mpDDB->ImplGetHeight (); + return true; + } + + return false; +} + +// -------------- +// - ImplSalDDB - +// -------------- + +ImplSalDDB::ImplSalDDB( XImage* pImage, Drawable aDrawable, int nScreen, const SalTwoRect& rTwoRect ) : + maPixmap ( 0 ), + maTwoRect ( rTwoRect ), + mnDepth ( pImage->depth ), + mnScreen ( nScreen ) +{ + SalDisplay* pSalDisp = GetX11SalData()->GetDisplay(); + Display* pXDisp = pSalDisp->GetDisplay(); + + if( (maPixmap = XCreatePixmap( pXDisp, aDrawable, ImplGetWidth(), ImplGetHeight(), ImplGetDepth() )) ) + { + XGCValues aValues; + GC aGC; + int nValues = GCFunction; + + aValues.function = GXcopy; + + if( 1 == mnDepth ) + { + nValues |= ( GCForeground | GCBackground ); + aValues.foreground = 1, aValues.background = 0; + } + + aGC = XCreateGC( pXDisp, maPixmap, nValues, &aValues ); + XPutImage( pXDisp, maPixmap, aGC, pImage, 0, 0, 0, 0, maTwoRect.mnDestWidth, maTwoRect.mnDestHeight ); + XFreeGC( pXDisp, aGC ); + } +} + +// ----------------------------------------------------------------------------------------- +// create from XImage + +ImplSalDDB::ImplSalDDB (Display* pDisplay, XLIB_Window hWindow, int nScreen, XImage* pImage) : + mnScreen( nScreen ) +{ + maPixmap = XCreatePixmap (pDisplay, hWindow, pImage->width, pImage->height, pImage->depth); + if (maPixmap != 0) + { + XGCValues aValues; + GC aGC; + int nValues = GCFunction; + + aValues.function = GXcopy; + + if (pImage->depth == 1) + { + nValues |= ( GCForeground | GCBackground ); + aValues.foreground = 1; + aValues.background = 0; + } + + aGC = XCreateGC (pDisplay, maPixmap, nValues, &aValues); + XPutImage (pDisplay, maPixmap, aGC, pImage, 0, 0, 0, 0, pImage->width, pImage->height); + XFreeGC (pDisplay, aGC); + + maTwoRect.mnSrcX = 0; + maTwoRect.mnSrcY = 0; + maTwoRect.mnDestX = 0; + maTwoRect.mnDestY = 0; + maTwoRect.mnSrcWidth = pImage->width; + maTwoRect.mnDestWidth = pImage->width; + maTwoRect.mnSrcHeight = pImage->height; + maTwoRect.mnDestHeight = pImage->height; + + mnDepth = pImage->depth; + } +} + +// ----------------------------------------------------------------------------- + +ImplSalDDB::ImplSalDDB( Drawable aDrawable, int nScreen, long nDrawableDepth, long nX, long nY, long nWidth, long nHeight ) : + mnDepth( nDrawableDepth ), + mnScreen( nScreen ) +{ + SalDisplay* pSalDisp = GetX11SalData()->GetDisplay(); + Display* pXDisp = pSalDisp->GetDisplay(); + + if( (maPixmap = XCreatePixmap( pXDisp, aDrawable, nWidth, nHeight, nDrawableDepth )) ) + { + XGCValues aValues; + GC aGC; + int nValues = GCFunction; + + aValues.function = GXcopy; + + if( 1 == mnDepth ) + { + nValues |= ( GCForeground | GCBackground ); + aValues.foreground = 1, aValues.background = 0; + } + + aGC = XCreateGC( pXDisp, maPixmap, nValues, &aValues ); + ImplDraw( aDrawable, nDrawableDepth, maPixmap, mnDepth, + nX, nY, nWidth, nHeight, 0, 0, aGC ); + XFreeGC( pXDisp, aGC ); + + maTwoRect.mnSrcX = maTwoRect.mnSrcY = maTwoRect.mnDestX = maTwoRect.mnDestY = 0; + maTwoRect.mnSrcWidth = maTwoRect.mnDestWidth = nWidth; + maTwoRect.mnSrcHeight = maTwoRect.mnDestHeight = nHeight; + } +} + +// ----------------------------------------------------------------------------- + +ImplSalDDB::~ImplSalDDB() +{ + if( maPixmap && ImplGetSVData() ) + XFreePixmap( GetX11SalData()->GetDisplay()->GetDisplay(), maPixmap ); +} + +// ----------------------------------------------------------------------------- + +bool ImplSalDDB::ImplMatches( int nScreen, long nDepth, const SalTwoRect& rTwoRect ) const +{ + bool bRet = sal_False; + + if( ( maPixmap != 0 ) && ( ( mnDepth == nDepth ) || ( 1 == mnDepth ) ) && nScreen == mnScreen) + { + if( rTwoRect.mnSrcX == maTwoRect.mnSrcX && rTwoRect.mnSrcY == maTwoRect.mnSrcY && + rTwoRect.mnSrcWidth == maTwoRect.mnSrcWidth && rTwoRect.mnSrcHeight == maTwoRect.mnSrcHeight && + rTwoRect.mnDestWidth == maTwoRect.mnDestWidth && rTwoRect.mnDestHeight == maTwoRect.mnDestHeight ) + { + // absolutely indentically + bRet = sal_True; + } + else if( rTwoRect.mnSrcWidth == rTwoRect.mnDestWidth && rTwoRect.mnSrcHeight == rTwoRect.mnDestHeight && + maTwoRect.mnSrcWidth == maTwoRect.mnDestWidth && maTwoRect.mnSrcHeight == maTwoRect.mnDestHeight && + rTwoRect.mnSrcX >= maTwoRect.mnSrcX && rTwoRect.mnSrcY >= maTwoRect.mnSrcY && + ( rTwoRect.mnSrcX + rTwoRect.mnSrcWidth ) <= ( maTwoRect.mnSrcX + maTwoRect.mnSrcWidth ) && + ( rTwoRect.mnSrcY + rTwoRect.mnSrcHeight ) <= ( maTwoRect.mnSrcY + maTwoRect.mnSrcHeight ) ) + { + bRet = sal_True; + } + } + + return bRet; +} + +// ----------------------------------------------------------------------------- + +void ImplSalDDB::ImplDraw( Drawable aDrawable, long nDrawableDepth, const SalTwoRect& rTwoRect, const GC& rGC ) const +{ + ImplDraw( maPixmap, mnDepth, aDrawable, nDrawableDepth, + rTwoRect.mnSrcX - maTwoRect.mnSrcX, rTwoRect.mnSrcY - maTwoRect.mnSrcY, + rTwoRect.mnDestWidth, rTwoRect.mnDestHeight, + rTwoRect.mnDestX, rTwoRect.mnDestY, rGC ); +} + +// ----------------------------------------------------------------------------- + +void ImplSalDDB::ImplDraw( Drawable aSrcDrawable, long nSrcDrawableDepth, + Drawable aDstDrawable, long, + long nSrcX, long nSrcY, + long nDestWidth, long nDestHeight, + long nDestX, long nDestY, const GC& rGC ) +{ + SalDisplay* pSalDisp = GetX11SalData()->GetDisplay(); + Display* pXDisp = pSalDisp->GetDisplay(); + + if( 1 == nSrcDrawableDepth ) + { + XCopyPlane( pXDisp, aSrcDrawable, aDstDrawable, rGC, + nSrcX, nSrcY, nDestWidth, nDestHeight, nDestX, nDestY, 1 ); + } + else + { + XCopyArea( pXDisp, aSrcDrawable, aDstDrawable, rGC, + nSrcX, nSrcY, nDestWidth, nDestHeight, nDestX, nDestY ); + } +} + +// ---------------------- +// - ImplSalBitmapCache - +// ---------------------- + +struct ImplBmpObj +{ + X11SalBitmap* mpBmp; + sal_uLong mnMemSize; + sal_uLong mnFlags; + + ImplBmpObj( X11SalBitmap* pBmp, sal_uLong nMemSize, sal_uLong nFlags ) : + mpBmp( pBmp ), mnMemSize( nMemSize ), mnFlags( nFlags ) {} +}; + +// ----------------------------------------------------------------------------- + +ImplSalBitmapCache::ImplSalBitmapCache() : + mnTotalSize( 0UL ) +{ +} + +// ----------------------------------------------------------------------------- + +ImplSalBitmapCache::~ImplSalBitmapCache() +{ + ImplClear(); +} + +// ----------------------------------------------------------------------------- + +void ImplSalBitmapCache::ImplAdd( X11SalBitmap* pBmp, sal_uLong nMemSize, sal_uLong nFlags ) +{ + ImplBmpObj* pObj; + bool bFound = sal_False; + + for( pObj = (ImplBmpObj*) maBmpList.Last(); pObj && !bFound; pObj = (ImplBmpObj*) maBmpList.Prev() ) + if( pObj->mpBmp == pBmp ) + bFound = sal_True; + + mnTotalSize += nMemSize; + + if( bFound ) + { + mnTotalSize -= pObj->mnMemSize; + pObj->mnMemSize = nMemSize, pObj->mnFlags = nFlags; + } + else + maBmpList.Insert( new ImplBmpObj( pBmp, nMemSize, nFlags ), LIST_APPEND ); +} + +// ----------------------------------------------------------------------------- + +void ImplSalBitmapCache::ImplRemove( X11SalBitmap* pBmp ) +{ + for( ImplBmpObj* pObj = (ImplBmpObj*) maBmpList.Last(); pObj; pObj = (ImplBmpObj*) maBmpList.Prev() ) + { + if( pObj->mpBmp == pBmp ) + { + maBmpList.Remove( pObj ); + pObj->mpBmp->ImplRemovedFromCache(); + mnTotalSize -= pObj->mnMemSize; + delete pObj; + break; + } + } +} + +// ----------------------------------------------------------------------------- + +void ImplSalBitmapCache::ImplClear() +{ + for( ImplBmpObj* pObj = (ImplBmpObj*) maBmpList.First(); pObj; pObj = (ImplBmpObj*) maBmpList.Next() ) + { + pObj->mpBmp->ImplRemovedFromCache(); + delete pObj; + } + + maBmpList.Clear(); + mnTotalSize = 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/gdi/salcvt.cxx b/vcl/unx/generic/gdi/salcvt.cxx new file mode 100644 index 000000000000..f757f3fc0ab3 --- /dev/null +++ b/vcl/unx/generic/gdi/salcvt.cxx @@ -0,0 +1,343 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" +#include "salcvt.hxx" + + +SalConverterCache::SalConverterCache() +{ +} + +SalConverterCache* +SalConverterCache::GetInstance () +{ + static SalConverterCache* pCvt = NULL; + if (pCvt == NULL) + pCvt = new SalConverterCache; + + return pCvt; +} + +SalConverterCache::~SalConverterCache() +{ +} + +// ---> FIXME +#include <stdio.h> +// <--- + +rtl_UnicodeToTextConverter +SalConverterCache::GetU2TConverter( rtl_TextEncoding nEncoding ) +{ + if( rtl_isOctetTextEncoding( nEncoding ) ) + { + ConverterT& rConverter( m_aConverters[ nEncoding ] ); + if ( rConverter.mpU2T == NULL ) + { + rConverter.mpU2T = + rtl_createUnicodeToTextConverter( nEncoding ); +// ---> FIXME +if ( rConverter.mpU2T == NULL ) + fprintf( stderr, "failed to create Unicode -> %i converter\n", nEncoding); +// <--- + } + return rConverter.mpU2T; + } + return NULL; +} + +rtl_TextToUnicodeConverter +SalConverterCache::GetT2UConverter( rtl_TextEncoding nEncoding ) +{ + if( rtl_isOctetTextEncoding( nEncoding ) ) + { + ConverterT& rConverter( m_aConverters[ nEncoding ] ); + if ( rConverter.mpT2U == NULL ) + { + rConverter.mpT2U = + rtl_createTextToUnicodeConverter( nEncoding ); +// ---> FIXME +if ( rConverter.mpT2U == NULL ) + fprintf( stderr, "failed to create %i -> Unicode converter\n", nEncoding ); +// <--- + } + return rConverter.mpT2U; + } + return NULL; +} + +Bool +SalConverterCache::IsSingleByteEncoding( rtl_TextEncoding nEncoding ) +{ + if( rtl_isOctetTextEncoding( nEncoding ) ) + { + ConverterT& rConverter( m_aConverters[ nEncoding ] ); + if ( ! rConverter.mbValid ) + { + rConverter.mbValid = True; + + rtl_TextEncodingInfo aTextEncInfo; + aTextEncInfo.StructSize = sizeof( aTextEncInfo ); + rtl_getTextEncodingInfo( nEncoding, &aTextEncInfo ); + + if ( aTextEncInfo.MinimumCharSize == aTextEncInfo.MaximumCharSize + && aTextEncInfo.MinimumCharSize == 1) + rConverter.mbSingleByteEncoding = True; + else + rConverter.mbSingleByteEncoding = False; + } + + return rConverter.mbSingleByteEncoding; + } + return False; +} + +// check whether the character set nEncoding contains the unicode +// code point nChar. This list has been compiled from the according +// ttmap files in /usr/openwin/lib/X11/fonts/TrueType/ttmap/ +Bool +SalConverterCache::EncodingHasChar( rtl_TextEncoding nEncoding, + sal_Unicode nChar ) +{ + Bool bMatch = False; + + switch ( nEncoding ) + { + case RTL_TEXTENCODING_DONTKNOW: + bMatch = False; + break; + + case RTL_TEXTENCODING_MS_1252: + case RTL_TEXTENCODING_ISO_8859_1: + case RTL_TEXTENCODING_ISO_8859_15: + // handle iso8859-15 and iso8859-1 the same (and both with euro) + // handle them also like ms1252 + // this is due to the fact that so many X fonts say they are iso8859-1 + // but have the other glyphs anyway because they are really ms1252 + bMatch = ( /*nChar >= 0x0000 &&*/ nChar <= 0x00ff ) + || ( nChar == 0x20ac ) + || ( nChar == 0x201a ) + || ( nChar == 0x0192 ) + || ( nChar == 0x201e ) + || ( nChar == 0x2026 ) + || ( nChar == 0x2020 ) + || ( nChar == 0x2021 ) + || ( nChar == 0x02c6 ) + || ( nChar == 0x2030 ) + || ( nChar == 0x0160 ) + || ( nChar == 0x2039 ) + || ( nChar == 0x0152 ) + || ( nChar == 0x017d ) + || ( nChar == 0x2018 ) + || ( nChar == 0x2019 ) + || ( nChar == 0x201c ) + || ( nChar == 0x201d ) + || ( nChar == 0x2022 ) + || ( nChar == 0x2013 ) + || ( nChar == 0x2014 ) + || ( nChar == 0x02dc ) + || ( nChar == 0x2122 ) + || ( nChar == 0x0161 ) + || ( nChar == 0x203a ) + || ( nChar == 0x0153 ) + || ( nChar == 0x017e ) + || ( nChar == 0x0178 ) + ; + break; + + case RTL_TEXTENCODING_ISO_8859_2: + bMatch = ( nChar >= 0x0020 && nChar <= 0x007e ) + || ( nChar >= 0x00a0 && nChar <= 0x017e ) + || ( nChar >= 0x02c7 && nChar <= 0x02dd ); + break; + + case RTL_TEXTENCODING_ISO_8859_4: + bMatch = ( nChar >= 0x0020 && nChar <= 0x007e ) + || ( nChar >= 0x00a0 && nChar <= 0x017e ) + || ( nChar >= 0x02c7 && nChar <= 0x02db ); + break; + + case RTL_TEXTENCODING_ISO_8859_5: + bMatch = ( nChar >= 0x0020 && nChar <= 0x007e ) + || ( nChar >= 0x00a0 && nChar <= 0x00ad ) + || ( nChar >= 0x0401 && nChar <= 0x045f ) + || ( nChar == 0x2116 ); + break; + + case RTL_TEXTENCODING_ISO_8859_6: + bMatch = ( nChar >= 0x0020 && nChar <= 0x007e ) + || ( nChar >= 0x0600 && nChar <= 0x06ff ) + || ( nChar >= 0xfb50 && nChar <= 0xfffe ); + break; + + case RTL_TEXTENCODING_ISO_8859_7: + bMatch = ( nChar >= 0x0020 && nChar <= 0x007e ) + || ( nChar >= 0x00a0 && nChar <= 0x00bd ) + || ( nChar == 0x02bd ) + || ( nChar >= 0x0384 && nChar <= 0x03ce ) + || ( nChar >= 0x2014 && nChar <= 0x2019 ); + break; + + case RTL_TEXTENCODING_ISO_8859_8: + bMatch = ( nChar >= 0x0020 && nChar <= 0x007e ) + || ( nChar >= 0x00a0 && nChar <= 0x00f7 ) + || ( nChar >= 0x05d0 && nChar <= 0x05ea ) + || ( nChar == 0x2017 ); + break; + + case RTL_TEXTENCODING_ISO_8859_9: + bMatch = ( nChar >= 0x0020 && nChar <= 0x007e ) + || ( nChar >= 0x00a0 && nChar <= 0x015f ); + break; + + case RTL_TEXTENCODING_ISO_8859_13: + bMatch = ( nChar >= 0x0020 && nChar <= 0x007e ) + || ( nChar >= 0x00a0 && nChar <= 0x017e ) + || ( nChar >= 0x2019 && nChar <= 0x201e ); + break; + + /* real case for RTL_TEXTENCODING_ISO_8859_15 + case RTL_TEXTENCODING_ISO_8859_15: + bMatch = ( nChar >= 0x0020 && nChar <= 0x007e ) + || ( nChar >= 0x00a0 && nChar <= 0x00ff ) + || ( nChar >= 0x0152 && nChar <= 0x017e ) + || ( nChar == 0x20ac ); + break; + */ + + case RTL_TEXTENCODING_JIS_X_0201: + bMatch = ( nChar >= 0x0020 && nChar <= 0x007e ) + || ( nChar >= 0xff61 && nChar <= 0xff9f ); + break; + + case RTL_TEXTENCODING_MS_1251: + bMatch = ( nChar >= 0x0020 && nChar <= 0x007e ) + || ( nChar >= 0x00a0 && nChar <= 0x00bb ) + || ( nChar >= 0x0401 && nChar <= 0x045f ) + || ( nChar >= 0x0490 && nChar <= 0x0491 ) + || ( nChar >= 0x2013 && nChar <= 0x203a ) + || ( nChar >= 0x2116 && nChar <= 0x2122 ); + break; + + case RTL_TEXTENCODING_KOI8_R: + bMatch = ( nChar >= 0x0020 && nChar <= 0x007e ) + || ( nChar >= 0x00a0 && nChar <= 0x00b7 ) + || ( nChar == 0x00f7 ) + || ( nChar >= 0x0401 && nChar <= 0x0451 ) + || ( nChar >= 0x2219 && nChar <= 0x221a ) + || ( nChar >= 0x2248 && nChar <= 0x2265 ) + || ( nChar >= 0x2320 && nChar <= 0x2321 ) + || ( nChar >= 0x2500 && nChar <= 0x25a0 ); + break; + + case RTL_TEXTENCODING_UNICODE: + bMatch = True; + break; + + case RTL_TEXTENCODING_EUC_KR: + case RTL_TEXTENCODING_BIG5: + case RTL_TEXTENCODING_GBK: + case RTL_TEXTENCODING_GB_2312: + case RTL_TEXTENCODING_MS_1361: + case RTL_TEXTENCODING_JIS_X_0208: + + // XXX Big5 and Korean EUC contain Ascii chars, but Solaris + // *-big5-1 and *-ksc5601.1992-3 fonts dont, in general CJK fonts + // are monospaced, so dont trust them for latin chars + if (nChar <= 0xFF) + { + bMatch = False; + break; + } + + default: + // XXX really convert the unicode char into the encoding + // and check for conversion errors, this is expensive ! + rtl_UnicodeToTextConverter aConverter; + rtl_UnicodeToTextContext aContext; + + aConverter = GetU2TConverter(nEncoding); + aContext = rtl_createUnicodeToTextContext( aConverter ); + + // ---> FIXME + if ( aConverter == NULL ) + return False; + // <--- + + sal_Char pConversionBuffer[ 32 ]; + sal_uInt32 nConversionInfo; + sal_Size nConvertedChars; + sal_Size nSize; + + nSize = rtl_convertUnicodeToText( aConverter, aContext, + &nChar, 1, pConversionBuffer, sizeof(pConversionBuffer), + RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR + | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR, + &nConversionInfo, &nConvertedChars ); + + rtl_destroyUnicodeToTextContext( aConverter, aContext ); + + bMatch = (nConvertedChars == 1) + && (nSize == 1 || nSize == 2) // XXX Fix me this is a hack + && ((nConversionInfo & RTL_UNICODETOTEXT_INFO_ERROR) == 0); + break; + } + + return bMatch; +} + +// wrapper for rtl_convertUnicodeToText that handles the usual cases for +// textconversion in drawtext and gettextwidth routines +sal_Size +SalConverterCache::ConvertStringUTF16( const sal_Unicode *pText, int nTextLen, + sal_Char *pBuffer, sal_Size nBufferSize, rtl_TextEncoding nEncoding ) +{ + rtl_UnicodeToTextConverter aConverter = GetU2TConverter(nEncoding); + + const sal_uInt32 nCvtFlags = + RTL_UNICODETOTEXT_FLAGS_UNDEFINED_REPLACE + | RTL_UNICODETOTEXT_FLAGS_UNDEFINED_QUESTIONMARK + | RTL_UNICODETOTEXT_FLAGS_INVALID_QUESTIONMARK ; + sal_uInt32 nCvtInfo; + sal_Size nCvtChars; + + rtl_UnicodeToTextContext aContext = + rtl_createUnicodeToTextContext( aConverter ); + + sal_Size nSize = rtl_convertUnicodeToText( aConverter, aContext, + pText, nTextLen, pBuffer, nBufferSize, + nCvtFlags, &nCvtInfo, &nCvtChars ); + + rtl_destroyUnicodeToTextContext( aConverter, aContext ); + + return nSize; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/gdi/salcvt.hxx b/vcl/unx/generic/gdi/salcvt.hxx new file mode 100644 index 000000000000..896ae4f30b36 --- /dev/null +++ b/vcl/unx/generic/gdi/salcvt.hxx @@ -0,0 +1,95 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#ifndef SAL_CONVERTER_CACHE_HXX_ +#define SAL_CONVERTER_CACHE_HXX_ + +#include <rtl/tencinfo.h> +#include <rtl/textcvt.h> + +#include <unx/salunx.h> + +#include <map> + +extern "C" const char* +pGetEncodingName( rtl_TextEncoding nEncoding ); + +// +// Cache TextToUnicode and UnicodeToText converter and conversion info which is +// used in DrawXYZ routines and in the Event loop +// + +class SalConverterCache { + + public: + SalConverterCache(); + ~SalConverterCache(); + Bool EncodingHasChar( + rtl_TextEncoding nEncoding, sal_Unicode nChar ); + rtl_UnicodeToTextConverter + GetU2TConverter( rtl_TextEncoding nEncoding ); + rtl_TextToUnicodeConverter + GetT2UConverter( rtl_TextEncoding nEncoding ); + Bool IsSingleByteEncoding( rtl_TextEncoding nEncoding ); + sal_Size ConvertStringUTF16( const sal_Unicode *pText, int nTextLen, + sal_Char *pBuffer, sal_Size nBufferSize, + rtl_TextEncoding nEncoding); + + static SalConverterCache* + GetInstance (); + + private: + + struct ConverterT { + rtl_UnicodeToTextConverter mpU2T; + rtl_TextToUnicodeConverter mpT2U; + Bool mbSingleByteEncoding; + Bool mbValid; + ConverterT() : + mpU2T( NULL ), + mpT2U( NULL ), + mbSingleByteEncoding( False ), + mbValid( False ) + { + } + ~ConverterT() + { + if( mpU2T ) + rtl_destroyUnicodeToTextConverter( mpU2T ); + if( mpT2U ) + rtl_destroyTextToUnicodeConverter( mpT2U ); + } + }; + + std::map< rtl_TextEncoding, ConverterT > m_aConverters; +}; + + + +#endif /* SAL_CONVERTER_CACHE_HXX_ */ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/gdi/salgdi.cxx b/vcl/unx/generic/gdi/salgdi.cxx new file mode 100644 index 000000000000..b1b8d337430d --- /dev/null +++ b/vcl/unx/generic/gdi/salgdi.cxx @@ -0,0 +1,1247 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "tools/debug.hxx" + +#include "basegfx/polygon/b2dpolygon.hxx" +#include "basegfx/polygon/b2dpolypolygon.hxx" +#include "basegfx/polygon/b2dpolypolygontools.hxx" +#include "basegfx/polygon/b2dpolygontools.hxx" +#include "basegfx/polygon/b2dpolygonclipper.hxx" +#include "basegfx/polygon/b2dlinegeometry.hxx" +#include "basegfx/matrix/b2dhommatrix.hxx" +#include "basegfx/matrix/b2dhommatrixtools.hxx" +#include "basegfx/polygon/b2dpolypolygoncutter.hxx" +#include "basegfx/polygon/b2dtrapezoid.hxx" + +#include "vcl/jobdata.hxx" + +#include "unx/Xproto.h" +#include "unx/salunx.h" +#include "unx/saldata.hxx" +#include "unx/saldisp.hxx" +#include "unx/salgdi.h" +#include "unx/salframe.h" +#include "unx/salvd.h" + +#include "printergfx.hxx" +#include "xrender_peer.hxx" +#include "region.h" + +#include <vector> +#include <queue> +#include <set> + +// -=-= SalPolyLine =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +#define STATIC_POINTS 64 + +class SalPolyLine +{ + XPoint Points_[STATIC_POINTS]; + XPoint *pFirst_; +public: + inline SalPolyLine( sal_uLong nPoints ); + inline SalPolyLine( sal_uLong nPoints, const SalPoint *p ); + inline ~SalPolyLine(); + inline XPoint &operator [] ( sal_uLong n ) const + { return pFirst_[n]; } +}; + +inline SalPolyLine::SalPolyLine( sal_uLong nPoints ) + : pFirst_( nPoints+1 > STATIC_POINTS ? new XPoint[nPoints+1] : Points_ ) +{} + +inline SalPolyLine::SalPolyLine( sal_uLong nPoints, const SalPoint *p ) + : pFirst_( nPoints+1 > STATIC_POINTS ? new XPoint[nPoints+1] : Points_ ) +{ + for( sal_uLong i = 0; i < nPoints; i++ ) + { + pFirst_[i].x = (short)p[i].mnX; + pFirst_[i].y = (short)p[i].mnY; + } + pFirst_[nPoints] = pFirst_[0]; // close polyline +} + +inline SalPolyLine::~SalPolyLine() +{ if( pFirst_ != Points_ ) delete [] pFirst_; } + +#undef STATIC_POINTS +// -=-= X11SalGraphics =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +X11SalGraphics::X11SalGraphics() +{ + m_pFrame = NULL; + m_pVDev = NULL; + m_pDeleteColormap = NULL; + hDrawable_ = None; + m_aXRenderPicture = 0; + m_pXRenderFormat = NULL; + + mpClipRegion = NULL; + pPaintRegion_ = NULL; + + pPenGC_ = NULL; + nPenPixel_ = 0; + nPenColor_ = MAKE_SALCOLOR( 0x00, 0x00, 0x00 ); // Black + + pFontGC_ = NULL; + for( int i = 0; i < MAX_FALLBACK; ++i ) + mpServerFont[i] = NULL; + + nTextPixel_ = 0; + nTextColor_ = MAKE_SALCOLOR( 0x00, 0x00, 0x00 ); // Black + +#ifdef ENABLE_GRAPHITE + // check if graphite fonts have been disabled + static const char* pDisableGraphiteStr = getenv( "SAL_DISABLE_GRAPHITE" ); + bDisableGraphite_ = pDisableGraphiteStr ? (pDisableGraphiteStr[0]!='0') : sal_False; +#endif + + pBrushGC_ = NULL; + nBrushPixel_ = 0; + nBrushColor_ = MAKE_SALCOLOR( 0xFF, 0xFF, 0xFF ); // White + hBrush_ = None; + + pMonoGC_ = NULL; + pCopyGC_ = NULL; + pMaskGC_ = NULL; + pInvertGC_ = NULL; + pInvert50GC_ = NULL; + pStippleGC_ = NULL; + pTrackingGC_ = NULL; + + bWindow_ = sal_False; + bPrinter_ = sal_False; + bVirDev_ = sal_False; + bPenGC_ = sal_False; + bFontGC_ = sal_False; + bBrushGC_ = sal_False; + bMonoGC_ = sal_False; + bCopyGC_ = sal_False; + bInvertGC_ = sal_False; + bInvert50GC_ = sal_False; + bStippleGC_ = sal_False; + bTrackingGC_ = sal_False; + bXORMode_ = sal_False; + bDitherBrush_ = sal_False; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +X11SalGraphics::~X11SalGraphics() +{ + ReleaseFonts(); + freeResources(); +} + +// -=-= SalGraphics / X11SalGraphics =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void X11SalGraphics::freeResources() +{ + Display *pDisplay = GetXDisplay(); + + DBG_ASSERT( !pPaintRegion_, "pPaintRegion_" ); + if( mpClipRegion ) XDestroyRegion( mpClipRegion ), mpClipRegion = None; + + if( hBrush_ ) XFreePixmap( pDisplay, hBrush_ ), hBrush_ = None; + if( pPenGC_ ) XFreeGC( pDisplay, pPenGC_ ), pPenGC_ = None; + if( pFontGC_ ) XFreeGC( pDisplay, pFontGC_ ), pFontGC_ = None; + if( pBrushGC_ ) XFreeGC( pDisplay, pBrushGC_ ), pBrushGC_ = None; + if( pMonoGC_ ) XFreeGC( pDisplay, pMonoGC_ ), pMonoGC_ = None; + if( pCopyGC_ ) XFreeGC( pDisplay, pCopyGC_ ), pCopyGC_ = None; + if( pMaskGC_ ) XFreeGC( pDisplay, pMaskGC_ ), pMaskGC_ = None; + if( pInvertGC_ ) XFreeGC( pDisplay, pInvertGC_ ), pInvertGC_ = None; + if( pInvert50GC_ ) XFreeGC( pDisplay, pInvert50GC_ ), pInvert50GC_ = None; + if( pStippleGC_ ) XFreeGC( pDisplay, pStippleGC_ ), pStippleGC_ = None; + if( pTrackingGC_ ) XFreeGC( pDisplay, pTrackingGC_ ), pTrackingGC_ = None; + if( m_pDeleteColormap ) + delete m_pDeleteColormap, m_pColormap = m_pDeleteColormap = NULL; + + if( m_aXRenderPicture ) + XRenderPeer::GetInstance().FreePicture( m_aXRenderPicture ), m_aXRenderPicture = 0; + + bPenGC_ = bFontGC_ = bBrushGC_ = bMonoGC_ = bCopyGC_ = bInvertGC_ = bInvert50GC_ = bStippleGC_ = bTrackingGC_ = false; +} + +void X11SalGraphics::SetDrawable( Drawable aDrawable, int nScreen ) +{ + // shortcut if nothing changed + if( hDrawable_ == aDrawable ) + return; + + // free screen specific resources if needed + if( nScreen != m_nScreen ) + { + freeResources(); + m_pColormap = &GetX11SalData()->GetDisplay()->GetColormap( nScreen ); + m_nScreen = nScreen; + } + + hDrawable_ = aDrawable; + SetXRenderFormat( NULL ); + if( m_aXRenderPicture ) + { + XRenderPeer::GetInstance().FreePicture( m_aXRenderPicture ); + m_aXRenderPicture = 0; + } + + if( hDrawable_ ) + { + nPenPixel_ = GetPixel( nPenColor_ ); + nTextPixel_ = GetPixel( nTextColor_ ); + nBrushPixel_ = GetPixel( nBrushColor_ ); + } +} + +void X11SalGraphics::Init( SalFrame *pFrame, Drawable aTarget, int nScreen ) +{ + + m_pColormap = &GetX11SalData()->GetDisplay()->GetColormap(nScreen); + m_nScreen = nScreen; + SetDrawable( aTarget, nScreen ); + + bWindow_ = sal_True; + m_pFrame = pFrame; + m_pVDev = NULL; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalGraphics::DeInit() +{ + SetDrawable( None, m_nScreen ); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalGraphics::SetClipRegion( GC pGC, XLIB_Region pXReg ) const +{ + Display *pDisplay = GetXDisplay(); + + int n = 0; + XLIB_Region Regions[3]; + + if( mpClipRegion ) + Regions[n++] = mpClipRegion; + + if( pXReg && !XEmptyRegion( pXReg ) ) + Regions[n++] = pXReg; + + if( 0 == n ) + XSetClipMask( pDisplay, pGC, None ); + else if( 1 == n ) + XSetRegion( pDisplay, pGC, Regions[0] ); + else + { + XLIB_Region pTmpRegion = XCreateRegion(); + XIntersectRegion( Regions[0], Regions[1], pTmpRegion ); + + XSetRegion( pDisplay, pGC, pTmpRegion ); + XDestroyRegion( pTmpRegion ); + } +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +GC X11SalGraphics::SelectPen() +{ + Display *pDisplay = GetXDisplay(); + + if( !pPenGC_ ) + { + XGCValues values; + values.subwindow_mode = ClipByChildren; + values.fill_rule = EvenOddRule; // Pict import/ Gradient + values.graphics_exposures = False; + + pPenGC_ = XCreateGC( pDisplay, hDrawable_, + GCSubwindowMode | GCFillRule | GCGraphicsExposures, + &values ); + } + + if( !bPenGC_ ) + { + if( nPenColor_ != SALCOLOR_NONE ) + XSetForeground( pDisplay, pPenGC_, nPenPixel_ ); + XSetFunction ( pDisplay, pPenGC_, bXORMode_ ? GXxor : GXcopy ); + SetClipRegion( pPenGC_ ); + bPenGC_ = sal_True; + } + + return pPenGC_; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +GC X11SalGraphics::SelectBrush() +{ + Display *pDisplay = GetXDisplay(); + + DBG_ASSERT( nBrushColor_ != SALCOLOR_NONE, "Brush Transparent" ); + + if( !pBrushGC_ ) + { + XGCValues values; + values.subwindow_mode = ClipByChildren; + values.fill_rule = EvenOddRule; // Pict import/ Gradient + values.graphics_exposures = False; + + pBrushGC_ = XCreateGC( pDisplay, hDrawable_, + GCSubwindowMode | GCFillRule | GCGraphicsExposures, + &values ); + } + + if( !bBrushGC_ ) + { + if( !bDitherBrush_ ) + { + XSetFillStyle ( pDisplay, pBrushGC_, FillSolid ); + XSetForeground( pDisplay, pBrushGC_, nBrushPixel_ ); + if( bPrinter_ ) + XSetTile( pDisplay, pBrushGC_, None ); + } + else + { + // Bug in Sun Solaris 2.5.1, XFillPolygon doesn't allways reflect + // changes of the tile. PROPERTY_BUG_Tile doesn't fix this ! + if (GetDisplay()->GetProperties() & PROPERTY_BUG_FillPolygon_Tile) + XSetFillStyle ( pDisplay, pBrushGC_, FillSolid ); + + XSetFillStyle ( pDisplay, pBrushGC_, FillTiled ); + XSetTile ( pDisplay, pBrushGC_, hBrush_ ); + } + XSetFunction ( pDisplay, pBrushGC_, bXORMode_ ? GXxor : GXcopy ); + SetClipRegion( pBrushGC_ ); + + bBrushGC_ = sal_True; + } + + return pBrushGC_; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +GC X11SalGraphics::GetTrackingGC() +{ + const char dash_list[2] = {2, 2}; + + if( !pTrackingGC_ ) + { + XGCValues values; + + values.graphics_exposures = False; + values.foreground = m_pColormap->GetBlackPixel() + ^ m_pColormap->GetWhitePixel(); + values.function = GXxor; + values.line_width = 1; + values.line_style = LineOnOffDash; + + pTrackingGC_ = XCreateGC( GetXDisplay(), GetDrawable(), + GCGraphicsExposures | GCForeground | GCFunction + | GCLineWidth | GCLineStyle, + &values ); + XSetDashes( GetXDisplay(), pTrackingGC_, 0, dash_list, 2 ); + } + + if( !bTrackingGC_ ) + { + SetClipRegion( pTrackingGC_ ); + bTrackingGC_ = sal_True; + } + + return pTrackingGC_; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalGraphics::DrawLines( sal_uLong nPoints, + const SalPolyLine &rPoints, + GC pGC, + bool bClose + ) +{ + // errechne wie viele Linien XWindow auf einmal zeichnen kann + sal_uLong nMaxLines = (GetDisplay()->GetMaxRequestSize() - sizeof(xPolyPointReq)) + / sizeof(xPoint); + if( nMaxLines > nPoints ) nMaxLines = nPoints; + + // gebe alle Linien aus, die XWindows zeichnen kann. + sal_uLong n; + for( n = 0; nPoints - n > nMaxLines; n += nMaxLines - 1 ) + XDrawLines( GetXDisplay(), + GetDrawable(), + pGC, + &rPoints[n], + nMaxLines, + CoordModeOrigin ); + + if( n < nPoints ) + XDrawLines( GetXDisplay(), + GetDrawable(), + pGC, + &rPoints[n], + nPoints - n, + CoordModeOrigin ); + if( bClose ) + { + if( rPoints[nPoints-1].x != rPoints[0].x || rPoints[nPoints-1].y != rPoints[0].y ) + drawLine( rPoints[nPoints-1].x, rPoints[nPoints-1].y, rPoints[0].x, rPoints[0].y ); + } +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// Dithern: Calculate a dither-pixmap and make a brush of it +#define P_DELTA 51 +#define DMAP( v, m ) ((v % P_DELTA) > m ? (v / P_DELTA) + 1 : (v / P_DELTA)) + +BOOL X11SalGraphics::GetDitherPixmap( SalColor nSalColor ) +{ + static const short nOrdDither8Bit[ 8 ][ 8 ] = + { + { 0, 38, 9, 48, 2, 40, 12, 50}, + {25, 12, 35, 22, 28, 15, 37, 24}, + { 6, 44, 3, 41, 8, 47, 5, 44}, + {32, 19, 28, 16, 34, 21, 31, 18}, + { 1, 40, 11, 49, 0, 39, 10, 48}, + {27, 14, 36, 24, 26, 13, 36, 23}, + { 8, 46, 4, 43, 7, 45, 4, 42}, + {33, 20, 30, 17, 32, 20, 29, 16} + }; + + // test for correct depth (8bit) + if( GetColormap().GetVisual().GetDepth() != 8 ) + return sal_False; + + char pBits[64]; + char *pBitsPtr = pBits; + + // Set the pallette-entries for the dithering tile + sal_uInt8 nSalColorRed = SALCOLOR_RED ( nSalColor ); + sal_uInt8 nSalColorGreen = SALCOLOR_GREEN ( nSalColor ); + sal_uInt8 nSalColorBlue = SALCOLOR_BLUE ( nSalColor ); + + for( int nY = 0; nY < 8; nY++ ) + { + for( int nX = 0; nX < 8; nX++ ) + { + short nMagic = nOrdDither8Bit[nY][nX]; + sal_uInt8 nR = P_DELTA * DMAP( nSalColorRed, nMagic ); + sal_uInt8 nG = P_DELTA * DMAP( nSalColorGreen, nMagic ); + sal_uInt8 nB = P_DELTA * DMAP( nSalColorBlue, nMagic ); + + *pBitsPtr++ = GetColormap().GetPixel( MAKE_SALCOLOR( nR, nG, nB ) ); + } + } + + // create the tile as ximage and an according pixmap -> caching + XImage *pImage = XCreateImage( GetXDisplay(), + GetColormap().GetXVisual(), + 8, + ZPixmap, + 0, // offset + pBits, // data + 8, 8, // width & height + 8, // bitmap_pad + 0 ); // (default) bytes_per_line + + if ( GetDisplay()->GetProperties() & PROPERTY_BUG_Tile ) + { + if (hBrush_) + XFreePixmap (GetXDisplay(), hBrush_); + hBrush_ = XCreatePixmap( GetXDisplay(), GetDrawable(), 8, 8, 8 ); + } + else + if( !hBrush_ ) + hBrush_ = XCreatePixmap( GetXDisplay(), GetDrawable(), 8, 8, 8 ); + + // put the ximage to the pixmap + XPutImage( GetXDisplay(), + hBrush_, + GetDisplay()->GetCopyGC( m_nScreen ), + pImage, + 0, 0, // Source + 0, 0, // Destination + 8, 8 ); // width & height + + // destroy image-frame but not palette-data + pImage->data = NULL; + XDestroyImage( pImage ); + + return sal_True; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalGraphics::GetResolution( sal_Int32 &rDPIX, sal_Int32 &rDPIY ) // const +{ + const SalDisplay *pDisplay = GetDisplay(); + + rDPIX = pDisplay->GetResolution().A(); + rDPIY = pDisplay->GetResolution().B(); + if( !pDisplay->GetExactResolution() && rDPIY < 96 ) + { + rDPIX = Divide( rDPIX * 96, rDPIY ); + rDPIY = 96; + } + else if ( rDPIY > 200 ) + { + rDPIX = Divide( rDPIX * 200, rDPIY ); + rDPIY = 200; + } + + // #i12705# equalize x- and y-resolution if they are close enough + if( rDPIX != rDPIY ) + { + // different x- and y- resolutions are usually artifacts of + // a wrongly calculated screen size. +#ifdef DEBUG + printf("Forcing Resolution from %" SAL_PRIdINT32 "x%" SAL_PRIdINT32 " to %" SAL_PRIdINT32 "x%" SAL_PRIdINT32 "\n", + rDPIX,rDPIY,rDPIY,rDPIY); +#endif + rDPIX = rDPIY; // y-resolution is more trustworthy + } +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +sal_uInt16 X11SalGraphics::GetBitCount() const +{ + return GetVisual().GetDepth(); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +long X11SalGraphics::GetGraphicsWidth() const +{ + if( m_pFrame ) + return m_pFrame->maGeometry.nWidth; + else if( m_pVDev ) + return m_pVDev->GetWidth(); + else + return 0; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +long X11SalGraphics::GetGraphicsHeight() const +{ + if( m_pFrame ) + return m_pFrame->maGeometry.nHeight; + else if( m_pVDev ) + return m_pVDev->GetHeight(); + else + return 0; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalGraphics::ResetClipRegion() +{ + if( mpClipRegion ) + { + bPenGC_ = sal_False; + bFontGC_ = sal_False; + bBrushGC_ = sal_False; + bMonoGC_ = sal_False; + bCopyGC_ = sal_False; + bInvertGC_ = sal_False; + bInvert50GC_ = sal_False; + bStippleGC_ = sal_False; + bTrackingGC_ = sal_False; + + XDestroyRegion( mpClipRegion ); + mpClipRegion = NULL; + } +} + +bool X11SalGraphics::setClipRegion( const Region& i_rClip ) +{ + if( mpClipRegion ) + XDestroyRegion( mpClipRegion ); + mpClipRegion = XCreateRegion(); + + ImplRegionInfo aInfo; + long nX, nY, nW, nH; + bool bRegionRect = i_rClip.ImplGetFirstRect(aInfo, nX, nY, nW, nH ); + while( bRegionRect ) + { + if ( nW && nH ) + { + XRectangle aRect; + aRect.x = (short)nX; + aRect.y = (short)nY; + aRect.width = (unsigned short)nW; + aRect.height = (unsigned short)nH; + + XUnionRectWithRegion( &aRect, mpClipRegion, mpClipRegion ); + } + bRegionRect = i_rClip.ImplGetNextRect( aInfo, nX, nY, nW, nH ); + } + + // done, invalidate GCs + bPenGC_ = sal_False; + bFontGC_ = sal_False; + bBrushGC_ = sal_False; + bMonoGC_ = sal_False; + bCopyGC_ = sal_False; + bInvertGC_ = sal_False; + bInvert50GC_ = sal_False; + bStippleGC_ = sal_False; + bTrackingGC_ = sal_False; + + if( XEmptyRegion( mpClipRegion ) ) + { + XDestroyRegion( mpClipRegion ); + mpClipRegion= NULL; + } + return true; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalGraphics::SetLineColor() +{ + if( nPenColor_ != SALCOLOR_NONE ) + { + nPenColor_ = SALCOLOR_NONE; + bPenGC_ = sal_False; + } +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalGraphics::SetLineColor( SalColor nSalColor ) +{ + if( nPenColor_ != nSalColor ) + { + nPenColor_ = nSalColor; + nPenPixel_ = GetPixel( nSalColor ); + bPenGC_ = sal_False; + } +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalGraphics::SetFillColor() +{ + if( nBrushColor_ != SALCOLOR_NONE ) + { + bDitherBrush_ = sal_False; + nBrushColor_ = SALCOLOR_NONE; + bBrushGC_ = sal_False; + } +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalGraphics::SetFillColor( SalColor nSalColor ) +{ + if( nBrushColor_ != nSalColor ) + { + bDitherBrush_ = sal_False; + nBrushColor_ = nSalColor; + nBrushPixel_ = GetPixel( nSalColor ); + if( TrueColor != GetColormap().GetVisual().GetClass() + && GetColormap().GetColor( nBrushPixel_ ) != nBrushColor_ + && nSalColor != MAKE_SALCOLOR( 0x00, 0x00, 0x00 ) // black + && nSalColor != MAKE_SALCOLOR( 0x00, 0x00, 0x80 ) // blue + && nSalColor != MAKE_SALCOLOR( 0x00, 0x80, 0x00 ) // green + && nSalColor != MAKE_SALCOLOR( 0x00, 0x80, 0x80 ) // cyan + && nSalColor != MAKE_SALCOLOR( 0x80, 0x00, 0x00 ) // red + && nSalColor != MAKE_SALCOLOR( 0x80, 0x00, 0x80 ) // magenta + && nSalColor != MAKE_SALCOLOR( 0x80, 0x80, 0x00 ) // brown + && nSalColor != MAKE_SALCOLOR( 0x80, 0x80, 0x80 ) // gray + && nSalColor != MAKE_SALCOLOR( 0xC0, 0xC0, 0xC0 ) // light gray + && nSalColor != MAKE_SALCOLOR( 0x00, 0x00, 0xFF ) // light blue + && nSalColor != MAKE_SALCOLOR( 0x00, 0xFF, 0x00 ) // light green + && nSalColor != MAKE_SALCOLOR( 0x00, 0xFF, 0xFF ) // light cyan + && nSalColor != MAKE_SALCOLOR( 0xFF, 0x00, 0x00 ) // light red + && nSalColor != MAKE_SALCOLOR( 0xFF, 0x00, 0xFF ) // light magenta + && nSalColor != MAKE_SALCOLOR( 0xFF, 0xFF, 0x00 ) // light brown + && nSalColor != MAKE_SALCOLOR( 0xFF, 0xFF, 0xFF ) ) + bDitherBrush_ = GetDitherPixmap(nSalColor); + bBrushGC_ = sal_False; + } +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalGraphics::SetROPLineColor( SalROPColor nROPColor ) +{ + switch( nROPColor ) + { + case SAL_ROP_0 : // 0 + nPenPixel_ = (Pixel)0; + break; + case SAL_ROP_1 : // 1 + nPenPixel_ = (Pixel)(1 << GetVisual().GetDepth()) - 1; + break; + case SAL_ROP_INVERT : // 2 + nPenPixel_ = (Pixel)(1 << GetVisual().GetDepth()) - 1; + break; + } + nPenColor_ = GetColormap().GetColor( nPenPixel_ ); + bPenGC_ = sal_False; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalGraphics::SetROPFillColor( SalROPColor nROPColor ) +{ + switch( nROPColor ) + { + case SAL_ROP_0 : // 0 + nBrushPixel_ = (Pixel)0; + break; + case SAL_ROP_1 : // 1 + nBrushPixel_ = (Pixel)(1 << GetVisual().GetDepth()) - 1; + break; + case SAL_ROP_INVERT : // 2 + nBrushPixel_ = (Pixel)(1 << GetVisual().GetDepth()) - 1; + break; + } + bDitherBrush_ = sal_False; + nBrushColor_ = GetColormap().GetColor( nBrushPixel_ ); + bBrushGC_ = sal_False; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalGraphics::SetXORMode( bool bSet, bool ) +{ + if( !bXORMode_ == bSet ) + { + bXORMode_ = bSet; + bPenGC_ = sal_False; + bFontGC_ = sal_False; + bBrushGC_ = sal_False; + bMonoGC_ = sal_False; + bCopyGC_ = sal_False; + bInvertGC_ = sal_False; + bInvert50GC_ = sal_False; + bStippleGC_ = sal_False; + bTrackingGC_ = sal_False; + } +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalGraphics::drawPixel( long nX, long nY ) +{ + if( nPenColor_ != SALCOLOR_NONE ) + XDrawPoint( GetXDisplay(), GetDrawable(), SelectPen(), nX, nY ); +} + +void X11SalGraphics::drawPixel( long nX, long nY, SalColor nSalColor ) +{ + if( nSalColor != SALCOLOR_NONE ) + { + Display *pDisplay = GetXDisplay(); + + if( (nPenColor_ == SALCOLOR_NONE) && !bPenGC_ ) + { + SetLineColor( nSalColor ); + XDrawPoint( pDisplay, GetDrawable(), SelectPen(), nX, nY ); + nPenColor_ = SALCOLOR_NONE; + bPenGC_ = False; + } + else + { + GC pGC = SelectPen(); + + if( nSalColor != nPenColor_ ) + XSetForeground( pDisplay, pGC, GetPixel( nSalColor ) ); + + XDrawPoint( pDisplay, GetDrawable(), pGC, nX, nY ); + + if( nSalColor != nPenColor_ ) + XSetForeground( pDisplay, pGC, nPenPixel_ ); + } + } +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalGraphics::drawLine( long nX1, long nY1, long nX2, long nY2 ) +{ + if( nPenColor_ != SALCOLOR_NONE ) + { + if ( GetDisplay()->GetProperties() & PROPERTY_BUG_DrawLine ) + { + GC aGC = SelectPen(); + XDrawPoint (GetXDisplay(), GetDrawable(), aGC, (int)nX1, (int)nY1); + XDrawPoint (GetXDisplay(), GetDrawable(), aGC, (int)nX2, (int)nY2); + XDrawLine (GetXDisplay(), GetDrawable(), aGC, nX1, nY1, nX2, nY2 ); + } + else + XDrawLine( GetXDisplay(), GetDrawable(),SelectPen(), + nX1, nY1, nX2, nY2 ); + } +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalGraphics::drawRect( long nX, long nY, long nDX, long nDY ) +{ + if( nBrushColor_ != SALCOLOR_NONE ) + { + XFillRectangle( GetXDisplay(), + GetDrawable(), + SelectBrush(), + nX, nY, nDX, nDY ); + } + // Beschreibung DrawRect verkehrt, deshalb -1 + if( nPenColor_ != SALCOLOR_NONE ) + XDrawRectangle( GetXDisplay(), + GetDrawable(), + SelectPen(), + nX, nY, nDX-1, nDY-1 ); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalGraphics::drawPolyLine( sal_uLong nPoints, const SalPoint *pPtAry ) +{ + drawPolyLine( nPoints, pPtAry, false ); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalGraphics::drawPolyLine( sal_uLong nPoints, const SalPoint *pPtAry, bool bClose ) +{ + if( nPenColor_ != 0xFFFFFFFF ) + { + SalPolyLine Points( nPoints, pPtAry ); + + DrawLines( nPoints, Points, SelectPen(), bClose ); + } +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalGraphics::drawPolygon( sal_uLong nPoints, const SalPoint* pPtAry ) +{ + if( nPoints == 0 ) + return; + + if( nPoints < 3 ) + { + if( !bXORMode_ ) + { + if( 1 == nPoints ) + drawPixel( pPtAry[0].mnX, pPtAry[0].mnY ); + else + drawLine( pPtAry[0].mnX, pPtAry[0].mnY, + pPtAry[1].mnX, pPtAry[1].mnY ); + } + return; + } + + SalPolyLine Points( nPoints, pPtAry ); + + nPoints++; + + /* WORKAROUND: some Xservers (Xorg, VIA chipset in this case) + * do not draw the visible part of a polygon + * if it overlaps to the left of screen 0,y. + * This happens to be the case in the gradient drawn in the + * menubar background. workaround for the special case of + * of a rectangle overlapping to the left. + */ + if( nPoints == 5 && + Points[ 0 ].x == Points[ 1 ].x && + Points[ 1 ].y == Points[ 2 ].y && + Points[ 2 ].x == Points[ 3 ].x && + Points[ 0 ].x == Points[ 4 ].x && Points[ 0 ].y == Points[ 4 ].y + ) + { + bool bLeft = false; + bool bRight = false; + for(unsigned int i = 0; i < nPoints; i++ ) + { + if( Points[i].x < 0 ) + bLeft = true; + else + bRight= true; + } + if( bLeft && ! bRight ) + return; + if( bLeft && bRight ) + { + for( unsigned int i = 0; i < nPoints; i++ ) + if( Points[i].x < 0 ) + Points[i].x = 0; + } + } + + if( nBrushColor_ != SALCOLOR_NONE ) + XFillPolygon( GetXDisplay(), + GetDrawable(), + SelectBrush(), + &Points[0], nPoints, + Complex, CoordModeOrigin ); + + if( nPenColor_ != 0xFFFFFFFF ) + DrawLines( nPoints, Points, SelectPen(), true ); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalGraphics::drawPolyPolygon( sal_uInt32 nPoly, + const sal_uInt32 *pPoints, + PCONSTSALPOINT *pPtAry ) +{ + if( nBrushColor_ != SALCOLOR_NONE ) + { + sal_uInt32 i, n; + XLIB_Region pXRegA = NULL; + + for( i = 0; i < nPoly; i++ ) { + n = pPoints[i]; + SalPolyLine Points( n, pPtAry[i] ); + if( n > 2 ) + { + XLIB_Region pXRegB = XPolygonRegion( &Points[0], n+1, WindingRule ); + if( !pXRegA ) + pXRegA = pXRegB; + else + { + XXorRegion( pXRegA, pXRegB, pXRegA ); + XDestroyRegion( pXRegB ); + } + } + } + + if( pXRegA ) + { + XRectangle aXRect; + XClipBox( pXRegA, &aXRect ); + + GC pGC = SelectBrush(); + SetClipRegion( pGC, pXRegA ); // ??? doppelt + XDestroyRegion( pXRegA ); + bBrushGC_ = sal_False; + + XFillRectangle( GetXDisplay(), + GetDrawable(), + pGC, + aXRect.x, aXRect.y, aXRect.width, aXRect.height ); + } + } + + if( nPenColor_ != SALCOLOR_NONE ) + for( sal_uInt32 i = 0; i < nPoly; i++ ) + drawPolyLine( pPoints[i], pPtAry[i], true ); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +sal_Bool X11SalGraphics::drawPolyLineBezier( sal_uLong, const SalPoint*, const BYTE* ) +{ + return sal_False; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +sal_Bool X11SalGraphics::drawPolygonBezier( sal_uLong, const SalPoint*, const BYTE* ) +{ + return sal_False; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +sal_Bool X11SalGraphics::drawPolyPolygonBezier( sal_uInt32, const sal_uInt32*, + const SalPoint* const*, const BYTE* const* ) +{ + return sal_False; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void X11SalGraphics::invert( sal_uLong nPoints, + const SalPoint* pPtAry, + SalInvert nFlags ) +{ + SalPolyLine Points ( nPoints, pPtAry ); + + GC pGC; + if( SAL_INVERT_50 & nFlags ) + pGC = GetInvert50GC(); + else + if ( SAL_INVERT_TRACKFRAME & nFlags ) + pGC = GetTrackingGC(); + else + pGC = GetInvertGC(); + + if( SAL_INVERT_TRACKFRAME & nFlags ) + DrawLines ( nPoints, Points, pGC, true ); + else + XFillPolygon( GetXDisplay(), + GetDrawable(), + pGC, + &Points[0], nPoints, + Complex, CoordModeOrigin ); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +BOOL X11SalGraphics::drawEPS( long,long,long,long,void*,sal_uLong ) +{ + return sal_False; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +XID X11SalGraphics::GetXRenderPicture() +{ + XRenderPeer& rRenderPeer = XRenderPeer::GetInstance(); + + if( !m_aXRenderPicture ) + { + // check xrender support for matching visual + XRenderPictFormat* pXRenderFormat = GetXRenderFormat(); + if( !pXRenderFormat ) + return 0; + // get the matching xrender target for drawable + m_aXRenderPicture = rRenderPeer.CreatePicture( hDrawable_, pXRenderFormat, 0, NULL ); + } + + { + // reset clip region + // TODO: avoid clip reset if already done + XRenderPictureAttributes aAttr; + aAttr.clip_mask = None; + rRenderPeer.ChangePicture( m_aXRenderPicture, CPClipMask, &aAttr ); + } + + return m_aXRenderPicture; +} + +XRenderPictFormat* X11SalGraphics::GetXRenderFormat() const +{ + if( m_pXRenderFormat == NULL ) + m_pXRenderFormat = XRenderPeer::GetInstance().FindVisualFormat( GetVisual().visual ); + return m_pXRenderFormat; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +SystemGraphicsData X11SalGraphics::GetGraphicsData() const +{ + SystemGraphicsData aRes; + + aRes.nSize = sizeof(aRes); + aRes.pDisplay = GetXDisplay(); + aRes.hDrawable = hDrawable_; + aRes.pVisual = GetVisual().visual; + aRes.nScreen = m_nScreen; + aRes.nDepth = GetBitCount(); + aRes.aColormap = GetColormap().GetXColormap(); + aRes.pXRenderFormat = m_pXRenderFormat; + return aRes; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +// draw a poly-polygon +bool X11SalGraphics::drawPolyPolygon( const ::basegfx::B2DPolyPolygon& rOrigPolyPoly, double fTransparency ) +{ + // nothing to do for empty polypolygons + const int nOrigPolyCount = rOrigPolyPoly.count(); + if( nOrigPolyCount <= 0 ) + return sal_True; + + // nothing to do if everything is transparent + if( (nBrushColor_ == SALCOLOR_NONE) + && (nPenColor_ == SALCOLOR_NONE) ) + return sal_True; + + // cannot handle pencolor!=brushcolor yet + if( (nPenColor_ != SALCOLOR_NONE) + && (nPenColor_ != nBrushColor_) ) + return sal_False; + + // TODO: remove the env-variable when no longer needed + static const char* pRenderEnv = getenv( "SAL_DISABLE_RENDER_POLY" ); + if( pRenderEnv ) + return sal_False; + + // snap to raster if requested + basegfx::B2DPolyPolygon aPolyPoly = rOrigPolyPoly; + const bool bSnapToRaster = !getAntiAliasB2DDraw(); + if( bSnapToRaster ) + aPolyPoly = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges( aPolyPoly ); + + // don't bother with polygons outside of visible area + const basegfx::B2DRange aViewRange( 0, 0, GetGraphicsWidth(), GetGraphicsHeight() ); + aPolyPoly = basegfx::tools::clipPolyPolygonOnRange( aPolyPoly, aViewRange, true, false ); + if( !aPolyPoly.count() ) + return true; + + // tesselate the polypolygon into trapezoids + basegfx::B2DTrapezoidVector aB2DTrapVector; + basegfx::tools::trapezoidSubdivide( aB2DTrapVector, aPolyPoly ); + const int nTrapCount = aB2DTrapVector.size(); + if( !nTrapCount ) + return true; + const bool bDrawn = drawFilledTrapezoids( &aB2DTrapVector[0], nTrapCount, fTransparency ); + return bDrawn; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +bool X11SalGraphics::drawFilledTrapezoids( const ::basegfx::B2DTrapezoid* pB2DTraps, int nTrapCount, double fTransparency ) +{ + if( nTrapCount <= 0 ) + return true; + + Picture aDstPic = GetXRenderPicture(); + // check xrender support for this drawable + if( !aDstPic ) + return false; + + // convert the B2DTrapezoids into XRender-Trapezoids + typedef std::vector<XTrapezoid> TrapezoidVector; + TrapezoidVector aTrapVector( nTrapCount ); + const basegfx::B2DTrapezoid* pB2DTrap = pB2DTraps; + for( int i = 0; i < nTrapCount; ++pB2DTrap, ++i ) + { + XTrapezoid& rTrap = aTrapVector[ i ] ; + + // set y-coordinates + const double fY1 = pB2DTrap->getTopY(); + rTrap.left.p1.y = rTrap.right.p1.y = rTrap.top = XDoubleToFixed( fY1 ); + const double fY2 = pB2DTrap->getBottomY(); + rTrap.left.p2.y = rTrap.right.p2.y = rTrap.bottom = XDoubleToFixed( fY2 ); + + // set x-coordinates + const double fXL1 = pB2DTrap->getTopXLeft(); + rTrap.left.p1.x = XDoubleToFixed( fXL1 ); + const double fXR1 = pB2DTrap->getTopXRight(); + rTrap.right.p1.x = XDoubleToFixed( fXR1 ); + const double fXL2 = pB2DTrap->getBottomXLeft(); + rTrap.left.p2.x = XDoubleToFixed( fXL2 ); + const double fXR2 = pB2DTrap->getBottomXRight(); + rTrap.right.p2.x = XDoubleToFixed( fXR2 ); + } + + // get xrender Picture for polygon foreground + // TODO: cache it like the target picture which uses GetXRenderPicture() + XRenderPeer& rRenderPeer = XRenderPeer::GetInstance(); + SalDisplay::RenderEntry& rEntry = GetDisplay()->GetRenderEntries( m_nScreen )[ 32 ]; + if( !rEntry.m_aPicture ) + { + Display* pXDisplay = GetXDisplay(); + + rEntry.m_aPixmap = ::XCreatePixmap( pXDisplay, hDrawable_, 1, 1, 32 ); + XRenderPictureAttributes aAttr; + aAttr.repeat = true; + + XRenderPictFormat* pXRPF = rRenderPeer.FindStandardFormat( PictStandardARGB32 ); + rEntry.m_aPicture = rRenderPeer.CreatePicture( rEntry.m_aPixmap, pXRPF, CPRepeat, &aAttr ); + } + + // set polygon foreground color and opacity + XRenderColor aRenderColor = GetXRenderColor( nBrushColor_ , fTransparency ); + rRenderPeer.FillRectangle( PictOpSrc, rEntry.m_aPicture, &aRenderColor, 0, 0, 1, 1 ); + + // set clipping + // TODO: move into GetXRenderPicture? + if( mpClipRegion && !XEmptyRegion( mpClipRegion ) ) + rRenderPeer.SetPictureClipRegion( aDstPic, mpClipRegion ); + + // render the trapezoids + const XRenderPictFormat* pMaskFormat = rRenderPeer.GetStandardFormatA8(); + rRenderPeer.CompositeTrapezoids( PictOpOver, + rEntry.m_aPicture, aDstPic, pMaskFormat, 0, 0, &aTrapVector[0], aTrapVector.size() ); + + return true; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +bool X11SalGraphics::drawPolyLine(const ::basegfx::B2DPolygon& rPolygon, double fTransparency, const ::basegfx::B2DVector& rLineWidth, basegfx::B2DLineJoin eLineJoin) +{ + const bool bIsHairline = (rLineWidth.getX() == rLineWidth.getY()) && (rLineWidth.getX() <= 1.2); + + // #i101491# + if( !bIsHairline && (rPolygon.count() > 1000) ) + { + // the used basegfx::tools::createAreaGeometry is simply too + // expensive with very big polygons; fallback to caller (who + // should use ImplLineConverter normally) + // AW: ImplLineConverter had to be removed since it does not even + // know LineJoins, so the fallback will now prepare the line geometry + // the same way. + return false; + } + + // temporarily adjust brush color to pen color + // since the line is drawn as an area-polygon + const SalColor aKeepBrushColor = nBrushColor_; + nBrushColor_ = nPenColor_; + + // #i11575#desc5#b adjust B2D tesselation result to raster positions + basegfx::B2DPolygon aPolygon = rPolygon; + const double fHalfWidth = 0.5 * rLineWidth.getX(); + aPolygon.transform( basegfx::tools::createTranslateB2DHomMatrix(+fHalfWidth,+fHalfWidth) ); + + // shortcut for hairline drawing to improve performance + bool bDrawnOk = true; + if( bIsHairline ) + { + // hairlines can benefit from a simplified tesselation + // e.g. for hairlines the linejoin style can be ignored + basegfx::B2DTrapezoidVector aB2DTrapVector; + basegfx::tools::createLineTrapezoidFromB2DPolygon( aB2DTrapVector, aPolygon, rLineWidth.getX() ); + + // draw tesselation result + const int nTrapCount = aB2DTrapVector.size(); + if( nTrapCount > 0 ) + bDrawnOk = drawFilledTrapezoids( &aB2DTrapVector[0], nTrapCount, fTransparency ); + + // restore the original brush GC + nBrushColor_ = aKeepBrushColor; + return bDrawnOk; + } + + // get the area polygon for the line polygon + if( (rLineWidth.getX() != rLineWidth.getY()) + && !basegfx::fTools::equalZero( rLineWidth.getY() ) ) + { + // prepare for createAreaGeometry() with anisotropic linewidth + aPolygon.transform( basegfx::tools::createScaleB2DHomMatrix(1.0, rLineWidth.getX() / rLineWidth.getY())); + } + + // create the area-polygon for the line + const basegfx::B2DPolyPolygon aAreaPolyPoly( basegfx::tools::createAreaGeometry(aPolygon, fHalfWidth, eLineJoin) ); + + if( (rLineWidth.getX() != rLineWidth.getY()) + && !basegfx::fTools::equalZero( rLineWidth.getX() ) ) + { + // postprocess createAreaGeometry() for anisotropic linewidth + aPolygon.transform(basegfx::tools::createScaleB2DHomMatrix(1.0, rLineWidth.getY() / rLineWidth.getX())); + } + + // draw each area polypolygon component individually + // to emulate the polypolygon winding rule "non-zero" + const int nPolyCount = aAreaPolyPoly.count(); + for( int nPolyIdx = 0; nPolyIdx < nPolyCount; ++nPolyIdx ) + { + const ::basegfx::B2DPolyPolygon aOnePoly( aAreaPolyPoly.getB2DPolygon( nPolyIdx ) ); + bDrawnOk = drawPolyPolygon( aOnePoly, fTransparency ); + if( !bDrawnOk ) + break; + } + + // restore the original brush GC + nBrushColor_ = aKeepBrushColor; + return bDrawnOk; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/gdi/salgdi2.cxx b/vcl/unx/generic/gdi/salgdi2.cxx new file mode 100644 index 000000000000..49dd7174d583 --- /dev/null +++ b/vcl/unx/generic/gdi/salgdi2.cxx @@ -0,0 +1,1154 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <stdio.h> +#include <poll.h> + +#include "vcl/salbtype.hxx" + +#include "unx/salunx.h" +#include "unx/saldata.hxx" +#include "unx/saldisp.hxx" +#include "unx/salbmp.h" +#include "unx/salgdi.h" +#include "unx/salframe.h" +#include "unx/salvd.h" +#include "xrender_peer.hxx" + +#include "printergfx.hxx" + +#include "vcl/bmpacc.hxx" +#include "vcl/outdata.hxx" + +#undef SALGDI2_TESTTRANS + +// -=-= debugging =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +// ----------------------------------------------------------------------------- + +#if (OSL_DEBUG_LEVEL > 1) && defined SALGDI2_TESTTRANS +#define DBG_TESTTRANS( _def_drawable ) \ +{ \ + XCopyArea( pXDisp, _def_drawable, aDrawable, GetCopyGC(), \ + 0, 0, \ + pPosAry->mnDestWidth, pPosAry->mnDestHeight, \ + 0, 0 ); \ +} +#else // (OSL_DEBUG_LEVEL > 1) && defined SALGDI2_TESTTRANS +#define DBG_TESTTRANS( _def_drawable ) +#endif // (OSL_DEBUG_LEVEL > 1) && defined SALGDI2_TESTTRANS + +// -=-= X11SalGraphics =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalGraphics::CopyScreenArea( Display* pDisplay, + Drawable aSrc, int nScreenSrc, int nSrcDepth, + Drawable aDest, int nScreenDest, int nDestDepth, + GC aDestGC, + int src_x, int src_y, + unsigned int w, unsigned int h, + int dest_x, int dest_y ) +{ + if( nSrcDepth == nDestDepth ) + { + if( nScreenSrc == nScreenDest ) + XCopyArea( pDisplay, aSrc, aDest, aDestGC, + src_x, src_y, w, h, dest_x, dest_y ); + else + { + SalXLib* pLib = GetX11SalData()->GetDisplay()->GetXLib(); + pLib->PushXErrorLevel( true ); + XImage* pImage = XGetImage( pDisplay, aSrc, src_x, src_y, w, h, + AllPlanes, ZPixmap ); + if( pImage ) + { + if( pImage->data ) + { + XPutImage( pDisplay, aDest, aDestGC, pImage, + 0, 0, dest_x, dest_y, w, h ); + } + XDestroyImage( pImage ); + } + pLib->PopXErrorLevel(); + } + } + else + { + X11SalBitmap aBM; + aBM.ImplCreateFromDrawable( aSrc, nScreenSrc, nSrcDepth, src_x, src_y, w, h ); + SalTwoRect aTwoRect; + aTwoRect.mnSrcX = aTwoRect.mnSrcY = 0; + aTwoRect.mnSrcWidth = aTwoRect.mnDestWidth = w; + aTwoRect.mnSrcHeight = aTwoRect.mnDestHeight = h; + aTwoRect.mnDestX = dest_x; + aTwoRect.mnDestY = dest_y; + aBM.ImplDraw( aDest, nScreenDest, nDestDepth, aTwoRect,aDestGC ); + } +} + +GC X11SalGraphics::CreateGC( Drawable hDrawable, unsigned long nMask ) +{ + XGCValues values; + + values.graphics_exposures = False; + values.foreground = m_pColormap->GetBlackPixel() + ^ m_pColormap->GetWhitePixel(); + values.function = GXxor; + values.line_width = 1; + values.fill_style = FillStippled; + values.stipple = GetDisplay()->GetInvert50( m_nScreen ); + values.subwindow_mode = ClipByChildren; + + return XCreateGC( GetXDisplay(), hDrawable, nMask | GCSubwindowMode, &values ); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +inline GC X11SalGraphics::GetMonoGC( Pixmap hPixmap ) +{ + if( !pMonoGC_ ) + pMonoGC_ = CreateGC( hPixmap ); + + if( !bMonoGC_ ) + { + SetClipRegion( pMonoGC_ ); + bMonoGC_ = sal_True; + } + + return pMonoGC_; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +inline GC X11SalGraphics::GetCopyGC() +{ + if( bXORMode_ ) return GetInvertGC(); + + if( !pCopyGC_ ) + pCopyGC_ = CreateGC( GetDrawable() ); + + if( !bCopyGC_ ) + { + SetClipRegion( pCopyGC_ ); + bCopyGC_ = sal_True; + } + return pCopyGC_; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +GC X11SalGraphics::GetInvertGC() +{ + if( !pInvertGC_ ) + pInvertGC_ = CreateGC( GetDrawable(), + GCGraphicsExposures + | GCForeground + | GCFunction + | GCLineWidth ); + + if( !bInvertGC_ ) + { + SetClipRegion( pInvertGC_ ); + bInvertGC_ = sal_True; + } + return pInvertGC_; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +GC X11SalGraphics::GetInvert50GC() +{ + if( !pInvert50GC_ ) + { + XGCValues values; + + values.graphics_exposures = False; + values.foreground = m_pColormap->GetWhitePixel(); + values.background = m_pColormap->GetBlackPixel(); + values.function = GXinvert; + values.line_width = 1; + values.line_style = LineSolid; + unsigned long nValueMask = + GCGraphicsExposures + | GCForeground + | GCBackground + | GCFunction + | GCLineWidth + | GCLineStyle + | GCFillStyle + | GCStipple; + + char* pEnv = getenv( "SAL_DO_NOT_USE_INVERT50" ); + if( pEnv && ! strcasecmp( pEnv, "true" ) ) + { + values.fill_style = FillSolid; + nValueMask &= ~ GCStipple; + } + else + { + values.fill_style = FillStippled; + values.stipple = GetDisplay()->GetInvert50( m_nScreen ); + } + + pInvert50GC_ = XCreateGC( GetXDisplay(), GetDrawable(), + nValueMask, + &values ); + } + + if( !bInvert50GC_ ) + { + SetClipRegion( pInvert50GC_ ); + bInvert50GC_ = sal_True; + } + return pInvert50GC_; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +inline GC X11SalGraphics::GetStippleGC() +{ + if( !pStippleGC_ ) + pStippleGC_ = CreateGC( GetDrawable(), + GCGraphicsExposures + | GCFillStyle + | GCLineWidth ); + + if( !bStippleGC_ ) + { + XSetFunction( GetXDisplay(), pStippleGC_, bXORMode_ ? GXxor : GXcopy ); + SetClipRegion( pStippleGC_ ); + bStippleGC_ = sal_True; + } + + return pStippleGC_; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +int X11SalGraphics::Clip( XLIB_Region pRegion, + int &nX, + int &nY, + unsigned int &nDX, + unsigned int &nDY, + int &nSrcX, + int &nSrcY ) const +{ + XRectangle aRect; + XClipBox( pRegion, &aRect ); + + if( int(nX + nDX) <= int(aRect.x) || nX >= int(aRect.x + aRect.width) ) + return RectangleOut; + if( int(nY + nDY) <= int(aRect.y) || nY >= int(aRect.y + aRect.height) ) + return RectangleOut; + + if( nX < aRect.x ) + { + nSrcX += aRect.x - nX; + nDX -= aRect.x - nX; + nX = aRect.x; + } + else if( int(nX + nDX) > int(aRect.x + aRect.width) ) + nDX = aRect.x + aRect.width - nX; + + if( nY < aRect.y ) + { + nSrcY += aRect.y - nY; + nDY -= aRect.y - nY; + nY = aRect.y; + } + else if( int(nY + nDY) > int(aRect.y + aRect.height) ) + nDY = aRect.y + aRect.height - nY; + + return RectangleIn; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +int X11SalGraphics::Clip( int &nX, + int &nY, + unsigned int &nDX, + unsigned int &nDY, + int &nSrcX, + int &nSrcY ) const + +{ + if( pPaintRegion_ + && RectangleOut == Clip( pPaintRegion_, nX, nY, nDX, nDY, nSrcX, nSrcY ) ) + return RectangleOut; + + if( mpClipRegion + && RectangleOut == Clip( mpClipRegion, nX, nY, nDX, nDY, nSrcX, nSrcY ) ) + return RectangleOut; + + int nPaint; + if( pPaintRegion_ ) + { + nPaint = XRectInRegion( pPaintRegion_, nX, nY, nDX, nDY ); + if( RectangleOut == nPaint ) + return RectangleOut; + } + else + nPaint = RectangleIn; + + int nClip; + if( mpClipRegion ) + { + nClip = XRectInRegion( mpClipRegion, nX, nY, nDX, nDY ); + if( RectangleOut == nClip ) + return RectangleOut; + } + else + nClip = RectangleIn; + + return RectangleIn == nClip && RectangleIn == nPaint + ? RectangleIn + : RectanglePart; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +GC X11SalGraphics::SetMask( int &nX, + int &nY, + unsigned int &nDX, + unsigned int &nDY, + int &nSrcX, + int &nSrcY, + Pixmap hClipMask ) +{ + int n = Clip( nX, nY, nDX, nDY, nSrcX, nSrcY ); + if( RectangleOut == n ) + return NULL; + + Display *pDisplay = GetXDisplay(); + + if( !pMaskGC_ ) + pMaskGC_ = CreateGC( GetDrawable() ); + + if( RectangleIn == n ) + { + XSetClipMask( pDisplay, pMaskGC_, hClipMask ); + XSetClipOrigin( pDisplay, pMaskGC_, nX - nSrcX, nY - nSrcY ); + return pMaskGC_; + } + + // - - - - create alternate clip pixmap for region clipping - - - - + Pixmap hPixmap = XCreatePixmap( pDisplay, hClipMask, nDX, nDY, 1 ); + + if( !hPixmap ) + { +#if (OSL_DEBUG_LEVEL > 1) || defined DBG_UTIL + fprintf( stderr, "X11SalGraphics::SetMask !hPixmap\n" ); +#endif + return NULL; + } + + // - - - - reset pixmap; all 0 - - - - - - - - - - - - - - - - - - - + XFillRectangle( pDisplay, + hPixmap, + GetDisplay()->GetMonoGC( m_nScreen ), + 0, 0, + nDX, nDY ); + + // - - - - copy pixmap only within region - - - - - - - - - - - - - + GC pMonoGC = GetMonoGC( hPixmap ); + XSetClipOrigin( pDisplay, pMonoGC, -nX, -nY ); + XCopyArea( pDisplay, + hClipMask, // Source + hPixmap, // Destination + pMonoGC, + nSrcX, nSrcY, // Source + nDX, nDY, // Width & Height + 0, 0 ); // Destination + + XSetClipMask( pDisplay, pMaskGC_, hPixmap ); + XSetClipOrigin( pDisplay, pMaskGC_, nX, nY ); + + XFreePixmap( pDisplay, hPixmap ); + return pMaskGC_; +} + +// -=-= SalGraphics =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +extern "C" +{ + static Bool GraphicsExposePredicate( Display*, XEvent* pEvent, XPointer pFrameWindow ) + { + Bool bRet = False; + if( (pEvent->type == GraphicsExpose || pEvent->type == NoExpose) && + pEvent->xnoexpose.drawable == (Drawable)pFrameWindow ) + { + bRet = True; + } + return bRet; + } +} + + +void X11SalGraphics::YieldGraphicsExpose() +{ + // get frame if necessary + SalFrame* pFrame = m_pFrame; + Display* pDisplay = GetXDisplay(); + XLIB_Window aWindow = GetDrawable(); + if( ! pFrame ) + { + const std::list< SalFrame* >& rFrames = GetX11SalData()->GetDisplay()->getFrames(); + for( std::list< SalFrame* >::const_iterator it = rFrames.begin(); it != rFrames.end() && ! pFrame; ++it ) + { + const SystemEnvData* pEnvData = (*it)->GetSystemData(); + if( Drawable(pEnvData->aWindow) == aWindow ) + pFrame = *it; + } + if( ! pFrame ) + return; + } + + XEvent aEvent; + while( XCheckTypedWindowEvent( pDisplay, aWindow, Expose, &aEvent ) ) + { + SalPaintEvent aPEvt( aEvent.xexpose.x, aEvent.xexpose.y, aEvent.xexpose.width+1, aEvent.xexpose.height+1 ); + pFrame->CallCallback( SALEVENT_PAINT, &aPEvt ); + } + + do + { + if( ! GetDisplay()->XIfEventWithTimeout( &aEvent, (XPointer)aWindow, GraphicsExposePredicate ) ) + // this should not happen at all; still sometimes it happens + break; + + if( aEvent.type == NoExpose ) + break; + + if( pFrame ) + { + SalPaintEvent aPEvt( aEvent.xgraphicsexpose.x, aEvent.xgraphicsexpose.y, aEvent.xgraphicsexpose.width+1, aEvent.xgraphicsexpose.height+1 ); + pFrame->CallCallback( SALEVENT_PAINT, &aPEvt ); + } + } while( aEvent.xgraphicsexpose.count != 0 ); +} + +void X11SalGraphics::copyBits( const SalTwoRect *pPosAry, + SalGraphics *pSSrcGraphics ) +{ + X11SalGraphics* pSrcGraphics = pSSrcGraphics + ? static_cast<X11SalGraphics*>(pSSrcGraphics) + : this; + + if( pPosAry->mnSrcWidth <= 0 + || pPosAry->mnSrcHeight <= 0 + || pPosAry->mnDestWidth <= 0 + || pPosAry->mnDestHeight <= 0 ) + { + return; + } + + int n; + if( pSrcGraphics == this ) + { + n = 2; + } + else if( pSrcGraphics->bWindow_ ) + { + // window or compatible virtual device + if( pSrcGraphics->GetDisplay() == GetDisplay() && + pSrcGraphics->m_nScreen == m_nScreen && + pSrcGraphics->GetVisual().GetDepth() == GetVisual().GetDepth() + ) + n = 2; // same Display + else + n = 1; // printer or other display + } + else if( pSrcGraphics->bVirDev_ ) + { + // printer compatible virtual device + if( bPrinter_ ) + n = 2; // printer or compatible virtual device == same display + else + n = 1; // window or compatible virtual device + } + else + n = 0; + + if( n == 2 + && pPosAry->mnSrcWidth == pPosAry->mnDestWidth + && pPosAry->mnSrcHeight == pPosAry->mnDestHeight + ) + { + // #i60699# Need to generate graphics exposures (to repaint + // obscured areas beneath overlapping windows), src and dest + // are the same window. + const bool bNeedGraphicsExposures( pSrcGraphics == this && + !bVirDev_ && + pSrcGraphics->bWindow_ ); + + GC pCopyGC; + + if( bXORMode_ + && !pSrcGraphics->bVirDev_ + && (GetDisplay()->GetProperties() & PROPERTY_BUG_XCopyArea_GXxor) ) + { + Pixmap hPixmap = XCreatePixmap( GetXDisplay(), + pSrcGraphics->GetDrawable(), // source + pPosAry->mnSrcWidth, pPosAry->mnSrcHeight, + pSrcGraphics->GetBitCount() ); + + pCopyGC = GetDisplay()->GetCopyGC( m_nScreen ); + + if( bNeedGraphicsExposures ) + XSetGraphicsExposures( GetXDisplay(), + pCopyGC, + True ); + + XCopyArea( GetXDisplay(), + pSrcGraphics->GetDrawable(), // source + hPixmap, // destination + pCopyGC, // no clipping + pPosAry->mnSrcX, pPosAry->mnSrcY, + pPosAry->mnSrcWidth, pPosAry->mnSrcHeight, + 0, 0 ); // destination + XCopyArea( GetXDisplay(), + hPixmap, // source + GetDrawable(), // destination + GetInvertGC(), // destination clipping + 0, 0, // source + pPosAry->mnSrcWidth, pPosAry->mnSrcHeight, + pPosAry->mnDestX, pPosAry->mnDestY ); + XFreePixmap( GetXDisplay(), hPixmap ); + } + else + { + pCopyGC = GetCopyGC(); + + if( bNeedGraphicsExposures ) + XSetGraphicsExposures( GetXDisplay(), + pCopyGC, + True ); + + XCopyArea( GetXDisplay(), + pSrcGraphics->GetDrawable(), // source + GetDrawable(), // destination + pCopyGC, // destination clipping + pPosAry->mnSrcX, pPosAry->mnSrcY, + pPosAry->mnSrcWidth, pPosAry->mnSrcHeight, + pPosAry->mnDestX, pPosAry->mnDestY ); + } + + if( bNeedGraphicsExposures ) + { + YieldGraphicsExpose(); + + if( pCopyGC ) + XSetGraphicsExposures( GetXDisplay(), + pCopyGC, + False ); + } + } + else if( n ) + { + // #i60699# No chance to handle graphics exposures - we copy + // to a temp bitmap first, into which no repaints are + // technically possible. + SalBitmap *pDDB = pSrcGraphics->getBitmap( pPosAry->mnSrcX, + pPosAry->mnSrcY, + pPosAry->mnSrcWidth, + pPosAry->mnSrcHeight ); + + if( !pDDB ) + { + stderr0( "SalGraphics::CopyBits !pSrcGraphics->GetBitmap()\n" ); + return; + } + + SalTwoRect aPosAry( *pPosAry ); + + aPosAry.mnSrcX = 0, aPosAry.mnSrcY = 0; + drawBitmap( &aPosAry, *pDDB ); + + delete pDDB; + } + else { + stderr0( "X11SalGraphics::CopyBits from Printer not yet implemented\n" ); + } +} + +// -------------------------------------------------------------------------- + +void X11SalGraphics::copyArea ( long nDestX, long nDestY, + long nSrcX, long nSrcY, + long nSrcWidth, long nSrcHeight, + sal_uInt16 ) +{ + SalTwoRect aPosAry; + + aPosAry.mnDestX = nDestX; + aPosAry.mnDestY = nDestY; + aPosAry.mnDestWidth = nSrcWidth; + aPosAry.mnDestHeight = nSrcHeight; + + aPosAry.mnSrcX = nSrcX; + aPosAry.mnSrcY = nSrcY; + aPosAry.mnSrcWidth = nSrcWidth; + aPosAry.mnSrcHeight = nSrcHeight; + + copyBits ( &aPosAry, 0 ); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalGraphics::drawBitmap( const SalTwoRect* pPosAry, const SalBitmap& rSalBitmap ) +{ + const SalDisplay* pSalDisp = GetDisplay(); + Display* pXDisp = pSalDisp->GetDisplay(); + const Drawable aDrawable( GetDrawable() ); + const SalColormap& rColMap = pSalDisp->GetColormap( m_nScreen ); + const long nDepth = GetDisplay()->GetVisual( m_nScreen ).GetDepth(); + GC aGC( GetCopyGC() ); + XGCValues aOldVal, aNewVal; + int nValues = GCForeground | GCBackground; + + if( rSalBitmap.GetBitCount() == 1 ) + { + // set foreground/background values for 1Bit bitmaps + XGetGCValues( pXDisp, aGC, nValues, &aOldVal ); + + aNewVal.foreground = rColMap.GetWhitePixel(); + aNewVal.background = rColMap.GetBlackPixel(); + + //fdo#33455 handle 1 bit depth pngs with palette entries + //to set fore/back colors + if (const BitmapBuffer* pBitmapBuffer = const_cast<SalBitmap&>(rSalBitmap).AcquireBuffer(true)) + { + const BitmapPalette& rPalette = pBitmapBuffer->maPalette; + if (rPalette.GetEntryCount() == 2) + { + aNewVal.foreground = rColMap.GetPixel(ImplColorToSal(rPalette[0])); + aNewVal.background = rColMap.GetPixel(ImplColorToSal(rPalette[1])); + } + } + + XChangeGC( pXDisp, aGC, nValues, &aNewVal ); + } + + static_cast<const X11SalBitmap&>(rSalBitmap).ImplDraw( aDrawable, m_nScreen, nDepth, *pPosAry, aGC ); + + if( rSalBitmap.GetBitCount() == 1 ) + XChangeGC( pXDisp, aGC, nValues, &aOldVal ); + XFlush( pXDisp ); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void X11SalGraphics::drawBitmap( const SalTwoRect* pPosAry, + const SalBitmap& rSrcBitmap, + const SalBitmap& rMaskBitmap ) +{ + DBG_ASSERT( !bPrinter_, "Drawing of transparent bitmaps on printer devices is strictly forbidden" ); + + // decide if alpha masking or transparency masking is needed + BitmapBuffer* pAlphaBuffer = const_cast<SalBitmap&>(rMaskBitmap).AcquireBuffer( sal_True ); + if( pAlphaBuffer != NULL ) + { + int nMaskFormat = pAlphaBuffer->mnFormat; + const_cast<SalBitmap&>(rMaskBitmap).ReleaseBuffer( pAlphaBuffer, sal_True ); + if( nMaskFormat == BMP_FORMAT_8BIT_PAL ) + drawAlphaBitmap( *pPosAry, rSrcBitmap, rMaskBitmap ); + } + + drawMaskedBitmap( pPosAry, rSrcBitmap, rMaskBitmap ); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void X11SalGraphics::drawMaskedBitmap( const SalTwoRect* pPosAry, + const SalBitmap& rSalBitmap, + const SalBitmap& rTransBitmap ) +{ + const SalDisplay* pSalDisp = GetDisplay(); + Display* pXDisp = pSalDisp->GetDisplay(); + Drawable aDrawable( GetDrawable() ); + + // figure work mode depth. If this is a VDev Drawable, use its + // bitdepth to create pixmaps for, otherwise, XCopyArea will + // refuse to work. + const sal_uInt16 nDepth( m_pVDev ? + m_pVDev->GetDepth() : + pSalDisp->GetVisual( m_nScreen ).GetDepth() ); + Pixmap aFG( XCreatePixmap( pXDisp, aDrawable, pPosAry->mnDestWidth, + pPosAry->mnDestHeight, nDepth ) ); + Pixmap aBG( XCreatePixmap( pXDisp, aDrawable, pPosAry->mnDestWidth, + pPosAry->mnDestHeight, nDepth ) ); + + if( aFG && aBG ) + { + GC aTmpGC; + XGCValues aValues; + const SalColormap& rColMap = pSalDisp->GetColormap( m_nScreen ); + const int nBlack = rColMap.GetBlackPixel(), nWhite = rColMap.GetWhitePixel(); + const int nValues = GCFunction | GCForeground | GCBackground; + SalTwoRect aTmpRect( *pPosAry ); aTmpRect.mnDestX = aTmpRect.mnDestY = 0; + + // draw paint bitmap in pixmap #1 + aValues.function = GXcopy, aValues.foreground = nWhite, aValues.background = nBlack; + aTmpGC = XCreateGC( pXDisp, aFG, nValues, &aValues ); + static_cast<const X11SalBitmap&>(rSalBitmap).ImplDraw( aFG, m_nScreen, nDepth, aTmpRect, aTmpGC ); + DBG_TESTTRANS( aFG ); + + // draw background in pixmap #2 + XCopyArea( pXDisp, aDrawable, aBG, aTmpGC, + pPosAry->mnDestX, pPosAry->mnDestY, + pPosAry->mnDestWidth, pPosAry->mnDestHeight, + 0, 0 ); + + DBG_TESTTRANS( aBG ); + + // mask out paint bitmap in pixmap #1 (transparent areas 0) + aValues.function = GXand, aValues.foreground = 0x00000000, aValues.background = 0xffffffff; + XChangeGC( pXDisp, aTmpGC, nValues, &aValues ); + static_cast<const X11SalBitmap&>(rTransBitmap).ImplDraw( aFG, m_nScreen, 1, aTmpRect, aTmpGC ); + + DBG_TESTTRANS( aFG ); + + // #105055# For XOR mode, keep background behind bitmap intact + if( !bXORMode_ ) + { + // mask out background in pixmap #2 (nontransparent areas 0) + aValues.function = GXand, aValues.foreground = 0xffffffff, aValues.background = 0x00000000; + XChangeGC( pXDisp, aTmpGC, nValues, &aValues ); + static_cast<const X11SalBitmap&>(rTransBitmap).ImplDraw( aBG, m_nScreen, 1, aTmpRect, aTmpGC ); + + DBG_TESTTRANS( aBG ); + } + + // merge pixmap #1 and pixmap #2 in pixmap #2 + aValues.function = GXxor, aValues.foreground = 0xffffffff, aValues.background = 0x00000000; + XChangeGC( pXDisp, aTmpGC, nValues, &aValues ); + XCopyArea( pXDisp, aFG, aBG, aTmpGC, + 0, 0, + pPosAry->mnDestWidth, pPosAry->mnDestHeight, + 0, 0 ); + DBG_TESTTRANS( aBG ); + + // #105055# Disable XOR temporarily + sal_Bool bOldXORMode( bXORMode_ ); + bXORMode_ = sal_False; + + // copy pixmap #2 (result) to background + XCopyArea( pXDisp, aBG, aDrawable, GetCopyGC(), + 0, 0, + pPosAry->mnDestWidth, pPosAry->mnDestHeight, + pPosAry->mnDestX, pPosAry->mnDestY ); + + DBG_TESTTRANS( aBG ); + + bXORMode_ = bOldXORMode; + + XFreeGC( pXDisp, aTmpGC ); + XFlush( pXDisp ); + } + else + drawBitmap( pPosAry, rSalBitmap ); + + if( aFG ) + XFreePixmap( pXDisp, aFG ); + + if( aBG ) + XFreePixmap( pXDisp, aBG ); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +bool X11SalGraphics::drawAlphaBitmap( const SalTwoRect& rTR, + const SalBitmap& rSrcBitmap, const SalBitmap& rAlphaBmp ) +{ + // non 8-bit alpha not implemented yet + if( rAlphaBmp.GetBitCount() != 8 ) + return false; + + // horizontal mirroring not implemented yet + if( rTR.mnDestWidth < 0 ) + return false; + + // stretched conversion is not implemented yet + if( rTR.mnDestWidth != rTR.mnSrcWidth ) + return false; + if( rTR.mnDestHeight!= rTR.mnSrcHeight ) + return false; + + XRenderPeer& rPeer = XRenderPeer::GetInstance(); + if( rPeer.GetVersion() < 0x02 ) + return false; + + // create destination picture + Picture aDstPic = GetXRenderPicture(); + if( !aDstPic ) + return false; + + const SalDisplay* pSalDisp = GetDisplay(); + const SalVisual& rSalVis = pSalDisp->GetVisual( m_nScreen ); + Display* pXDisplay = pSalDisp->GetDisplay(); + + // create source Picture + int nDepth = m_pVDev ? m_pVDev->GetDepth() : rSalVis.GetDepth(); + const X11SalBitmap& rSrcX11Bmp = static_cast<const X11SalBitmap&>( rSrcBitmap ); + ImplSalDDB* pSrcDDB = rSrcX11Bmp.ImplGetDDB( hDrawable_, m_nScreen, nDepth, rTR ); + if( !pSrcDDB ) + return false; + + //#i75249# workaround for ImplGetDDB() giving us back a different depth than + // we requested. E.g. mask pixmaps are always compatible with the drawable + // TODO: find an appropriate picture format for these cases + // then remove the workaround below and the one for #i75531# + if( nDepth != pSrcDDB->ImplGetDepth() ) + return false; + + Pixmap aSrcPM = pSrcDDB->ImplGetPixmap(); + if( !aSrcPM ) + return false; + + // create source picture + // TODO: use scoped picture + Visual* pSrcXVisual = rSalVis.GetVisual(); + XRenderPictFormat* pSrcVisFmt = rPeer.FindVisualFormat( pSrcXVisual ); + if( !pSrcVisFmt ) + return false; + Picture aSrcPic = rPeer.CreatePicture( aSrcPM, pSrcVisFmt, 0, NULL ); + if( !aSrcPic ) + return false; + + // create alpha Picture + + // TODO: use SalX11Bitmap functionality and caching for the Alpha Pixmap + // problem is that they don't provide an 8bit Pixmap on a non-8bit display + BitmapBuffer* pAlphaBuffer = const_cast<SalBitmap&>(rAlphaBmp).AcquireBuffer( sal_True ); + + // an XImage needs its data top_down + // TODO: avoid wrongly oriented images in upper layers! + const int nImageSize = pAlphaBuffer->mnHeight * pAlphaBuffer->mnScanlineSize; + const char* pSrcBits = (char*)pAlphaBuffer->mpBits; + char* pAlphaBits = new char[ nImageSize ]; + if( BMP_SCANLINE_ADJUSTMENT( pAlphaBuffer->mnFormat ) == BMP_FORMAT_TOP_DOWN ) + memcpy( pAlphaBits, pSrcBits, nImageSize ); + else + { + char* pDstBits = pAlphaBits + nImageSize; + const int nLineSize = pAlphaBuffer->mnScanlineSize; + for(; (pDstBits -= nLineSize) >= pAlphaBits; pSrcBits += nLineSize ) + memcpy( pDstBits, pSrcBits, nLineSize ); + } + + // the alpha values need to be inverted for XRender + // TODO: make upper layers use standard alpha + long* pLDst = (long*)pAlphaBits; + for( int i = nImageSize/sizeof(long); --i >= 0; ++pLDst ) + *pLDst = ~*pLDst; + + char* pCDst = (char*)pLDst; + for( int i = nImageSize & (sizeof(long)-1); --i >= 0; ++pCDst ) + *pCDst = ~*pCDst; + + const XRenderPictFormat* pAlphaFormat = rPeer.GetStandardFormatA8(); + XImage* pAlphaImg = XCreateImage( pXDisplay, pSrcXVisual, 8, ZPixmap, 0, + pAlphaBits, pAlphaBuffer->mnWidth, pAlphaBuffer->mnHeight, + pAlphaFormat->depth, pAlphaBuffer->mnScanlineSize ); + + Pixmap aAlphaPM = XCreatePixmap( pXDisplay, hDrawable_, + rTR.mnDestWidth, rTR.mnDestHeight, 8 ); + + XGCValues aAlphaGCV; + aAlphaGCV.function = GXcopy; + GC aAlphaGC = XCreateGC( pXDisplay, aAlphaPM, GCFunction, &aAlphaGCV ); + XPutImage( pXDisplay, aAlphaPM, aAlphaGC, pAlphaImg, + rTR.mnSrcX, rTR.mnSrcY, 0, 0, rTR.mnDestWidth, rTR.mnDestHeight ); + XFreeGC( pXDisplay, aAlphaGC ); + XFree( pAlphaImg ); + if( pAlphaBits != (char*)pAlphaBuffer->mpBits ) + delete[] pAlphaBits; + + const_cast<SalBitmap&>(rAlphaBmp).ReleaseBuffer( pAlphaBuffer, sal_True ); + + XRenderPictureAttributes aAttr; + aAttr.repeat = true; + Picture aAlphaPic = rPeer.CreatePicture( aAlphaPM, pAlphaFormat, CPRepeat, &aAttr ); + if( !aAlphaPic ) + return false; + + // set clipping + if( mpClipRegion && !XEmptyRegion( mpClipRegion ) ) + rPeer.SetPictureClipRegion( aDstPic, mpClipRegion ); + + // paint source * mask over destination picture + rPeer.CompositePicture( PictOpOver, aSrcPic, aAlphaPic, aDstPic, + rTR.mnSrcX, rTR.mnSrcY, 0, 0, + rTR.mnDestX, rTR.mnDestY, rTR.mnDestWidth, rTR.mnDestHeight ); + + // TODO: used ScopedPic + rPeer.FreePicture( aAlphaPic ); + XFreePixmap(pXDisplay, aAlphaPM); + rPeer.FreePicture( aSrcPic ); + return true; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +bool X11SalGraphics::drawAlphaRect( long nX, long nY, long nWidth, + long nHeight, sal_uInt8 nTransparency ) +{ + if( ! m_pFrame && ! m_pVDev ) + return false; + + if( bPenGC_ || !bBrushGC_ || bXORMode_ ) + return false; // can only perform solid fills without XOR. + + if( m_pVDev && m_pVDev->GetDepth() < 8 ) + return false; + + XRenderPeer& rPeer = XRenderPeer::GetInstance(); + if( rPeer.GetVersion() < 0x02 ) // TODO: replace with better test + return false; + + Picture aDstPic = GetXRenderPicture(); + if( !aDstPic ) + return false; + + const double fTransparency = (100 - nTransparency) * (1.0/100); + const XRenderColor aRenderColor = GetXRenderColor( nBrushColor_ , fTransparency); + + rPeer.FillRectangle( PictOpOver, + aDstPic, + &aRenderColor, + nX, nY, + nWidth, nHeight ); + + return true; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalGraphics::drawBitmap( const SalTwoRect*, + const SalBitmap&, + SalColor ) +{ + OSL_FAIL( "::DrawBitmap with transparent color not supported" ); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalGraphics::drawMask( const SalTwoRect* pPosAry, + const SalBitmap &rSalBitmap, + SalColor nMaskColor ) +{ + const SalDisplay* pSalDisp = GetDisplay(); + Display* pXDisp = pSalDisp->GetDisplay(); + Drawable aDrawable( GetDrawable() ); + Pixmap aStipple( XCreatePixmap( pXDisp, aDrawable, + pPosAry->mnDestWidth, + pPosAry->mnDestHeight, 1 ) ); + + if( aStipple ) + { + SalTwoRect aTwoRect( *pPosAry ); aTwoRect.mnDestX = aTwoRect.mnDestY = 0; + GC aTmpGC; + XGCValues aValues; + + // create a stipple bitmap first (set bits are changed to unset bits and vice versa) + aValues.function = GXcopyInverted; + aValues.foreground = 1, aValues.background = 0; + aTmpGC = XCreateGC( pXDisp, aStipple, GCFunction | GCForeground | GCBackground, &aValues ); + static_cast<const X11SalBitmap&>(rSalBitmap).ImplDraw( aStipple, m_nScreen, 1, aTwoRect, aTmpGC ); + + XFreeGC( pXDisp, aTmpGC ); + + // Set stipple and draw rectangle + GC aStippleGC( GetStippleGC() ); + int nX = pPosAry->mnDestX, nY = pPosAry->mnDestY; + + XSetStipple( pXDisp, aStippleGC, aStipple ); + XSetTSOrigin( pXDisp, aStippleGC, nX, nY ); + XSetForeground( pXDisp, aStippleGC, GetPixel( nMaskColor ) ); + XFillRectangle( pXDisp, aDrawable, aStippleGC, + nX, nY, + pPosAry->mnDestWidth, pPosAry->mnDestHeight ); + XFreePixmap( pXDisp, aStipple ); + XFlush( pXDisp ); + } + else + drawBitmap( pPosAry, rSalBitmap ); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +SalBitmap *X11SalGraphics::getBitmap( long nX, long nY, long nDX, long nDY ) +{ + if( bPrinter_ && !bVirDev_ ) + return NULL; + + bool bFakeWindowBG = false; + + // normalize + if( nDX < 0 ) + { + nX += nDX; + nDX = -nDX; + } + if ( nDY < 0 ) + { + nY += nDY; + nDY = -nDY; + } + + if( bWindow_ && !bVirDev_ ) + { + XWindowAttributes aAttrib; + + XGetWindowAttributes( GetXDisplay(), GetDrawable(), &aAttrib ); + if( aAttrib.map_state != IsViewable ) + bFakeWindowBG = true; + else + { + long nOrgDX = nDX, nOrgDY = nDY; + + // clip to window size + if ( nX < 0 ) + { + nDX += nX; + nX = 0; + } + if ( nY < 0 ) + { + nDY += nY; + nY = 0; + } + if( nX + nDX > aAttrib.width ) + nDX = aAttrib.width - nX; + if( nY + nDY > aAttrib.height ) + nDY = aAttrib.height - nY; + + // inside ? + if( nDX <= 0 || nDY <= 0 ) + { + bFakeWindowBG = true; + nDX = nOrgDX; + nDY = nOrgDY; + } + } + } + + X11SalBitmap* pSalBitmap = new X11SalBitmap; + sal_uInt16 nBitCount = GetBitCount(); + + if( &GetDisplay()->GetColormap( m_nScreen ) != &GetColormap() ) + nBitCount = 1; + + if( ! bFakeWindowBG ) + pSalBitmap->ImplCreateFromDrawable( GetDrawable(), m_nScreen, nBitCount, nX, nY, nDX, nDY ); + else + pSalBitmap->Create( Size( nDX, nDY ), (nBitCount > 8) ? 24 : nBitCount, BitmapPalette( nBitCount > 8 ? nBitCount : 0 ) ); + + return pSalBitmap; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +SalColor X11SalGraphics::getPixel( long nX, long nY ) +{ + if( bWindow_ && !bVirDev_ ) + { + XWindowAttributes aAttrib; + + XGetWindowAttributes( GetXDisplay(), GetDrawable(), &aAttrib ); + if( aAttrib.map_state != IsViewable ) + { + stderr0( "X11SalGraphics::GetPixel drawable not viewable\n" ); + return 0; + } + } + + XImage *pXImage = XGetImage( GetXDisplay(), + GetDrawable(), + nX, nY, + 1, 1, + AllPlanes, + ZPixmap ); + if( !pXImage ) + { + stderr0( "X11SalGraphics::GetPixel !XGetImage()\n" ); + return 0; + } + + XColor aXColor; + + aXColor.pixel = XGetPixel( pXImage, 0, 0 ); + XDestroyImage( pXImage ); + + return GetColormap().GetColor( aXColor.pixel ); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalGraphics::invert( long nX, + long nY, + long nDX, + long nDY, + SalInvert nFlags ) +{ + GC pGC; + if( SAL_INVERT_50 & nFlags ) + { + pGC = GetInvert50GC(); + XFillRectangle( GetXDisplay(), GetDrawable(), pGC, nX, nY, nDX, nDY ); + } + else + { + if ( SAL_INVERT_TRACKFRAME & nFlags ) + { + pGC = GetTrackingGC(); + XDrawRectangle( GetXDisplay(), GetDrawable(), pGC, nX, nY, nDX, nDY ); + } + else + { + pGC = GetInvertGC(); + XFillRectangle( GetXDisplay(), GetDrawable(), pGC, nX, nY, nDX, nDY ); + } + } +} + +bool X11SalGraphics::supportsOperation( OutDevSupportType eType ) const +{ + bool bRet = false; + switch( eType ) + { + case OutDevSupport_TransparentRect: + case OutDevSupport_B2DDraw: + { + XRenderPeer& rPeer = XRenderPeer::GetInstance(); + if( rPeer.GetVersion() >= 0x02 ) + { + const SalDisplay* pSalDisp = GetDisplay(); + const SalVisual& rSalVis = pSalDisp->GetVisual( m_nScreen ); + + Visual* pDstXVisual = rSalVis.GetVisual(); + XRenderPictFormat* pDstVisFmt = rPeer.FindVisualFormat( pDstXVisual ); + if( pDstVisFmt ) + bRet = true; + } + } + break; + default: break; + } + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/gdi/salgdi3.cxx b/vcl/unx/generic/gdi/salgdi3.cxx new file mode 100644 index 000000000000..c60bf49fff0e --- /dev/null +++ b/vcl/unx/generic/gdi/salgdi3.cxx @@ -0,0 +1,1695 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "sal/alloca.h" +#include "sal/types.h" + +#include "rtl/tencinfo.h" + +#include "osl/file.hxx" + +#include "tools/string.hxx" +#include "tools/debug.hxx" +#include "tools/stream.hxx" + +#include "basegfx/polygon/b2dpolypolygon.hxx" + +#include "i18npool/mslangid.hxx" + +#include <boost/unordered_set.hpp> + +#include <vcl/sysdata.hxx> +#include "printergfx.hxx" +#include "vcl/fontmanager.hxx" +#include "vcl/jobdata.hxx" +#include "vcl/printerinfomanager.hxx" +#include "vcl/svapp.hxx" + +#include "unx/salunx.h" +#include "unx/saldata.hxx" +#include "unx/saldisp.hxx" +#include "unx/salgdi.h" +#include "unx/pspgraphics.h" +#include "unx/salvd.h" + +#include "salcvt.hxx" +#include "gcach_xpeer.hxx" +#include "xrender_peer.hxx" +#include "impfont.hxx" +#include "salframe.hxx" +#include "outdev.h" + + + +#ifdef ENABLE_GRAPHITE +#include <graphite_layout.hxx> +#include <graphite_serverfont.hxx> +#endif + +struct cairo_surface_t; +struct cairo_t; +struct cairo_font_face_t; +typedef void* FT_Face; +struct cairo_matrix_t { + double xx; double yx; + double xy; double yy; + double x0; double y0; +}; +struct cairo_glyph_t +{ + unsigned long index; + double x; + double y; +}; +struct BOX +{ + short x1, x2, y1, y2; +}; +struct _XRegion +{ + long size; + long numRects; + BOX *rects; + BOX extents; +}; +using ::rtl::OUString; +// =========================================================================== + +// PspKernInfo allows on-demand-querying of psprint provided kerning info (#i29881#) +class PspKernInfo : public ExtraKernInfo +{ +public: + PspKernInfo( int nFontId ) : ExtraKernInfo(nFontId) {} +protected: + virtual void Initialize() const; +}; + +//-------------------------------------------------------------------------- + +void PspKernInfo::Initialize() const +{ + mbInitialized = true; + + // get the kerning pairs from psprint + const psp::PrintFontManager& rMgr = psp::PrintFontManager::get(); + typedef std::list< psp::KernPair > PspKernPairs; + const PspKernPairs& rKernPairs = rMgr.getKernPairs( mnFontId ); + if( rKernPairs.empty() ) + return; + + PspKernPairs::const_iterator it = rKernPairs.begin(); + for(; it != rKernPairs.end(); ++it ) + { + ImplKernPairData aKernPair = { it->first, it->second, it->kern_x }; + maUnicodeKernPairs.insert( aKernPair ); + } +} + +// ---------------------------------------------------------------------------- +// +// X11SalGraphics +// +// ---------------------------------------------------------------------------- + +GC +X11SalGraphics::GetFontGC() +{ + Display *pDisplay = GetXDisplay(); + + if( !pFontGC_ ) + { + XGCValues values; + values.subwindow_mode = ClipByChildren; + values.fill_rule = EvenOddRule; // Pict import/ Gradient + values.graphics_exposures = False; + values.foreground = nTextPixel_; + pFontGC_ = XCreateGC( pDisplay, hDrawable_, + GCSubwindowMode | GCFillRule + | GCGraphicsExposures | GCForeground, + &values ); + } + if( !bFontGC_ ) + { + XSetForeground( pDisplay, pFontGC_, nTextPixel_ ); + SetClipRegion( pFontGC_ ); + bFontGC_ = sal_True; + } + + return pFontGC_; +} + +//-------------------------------------------------------------------------- + +bool X11SalGraphics::setFont( const ImplFontSelectData *pEntry, int nFallbackLevel ) +{ + // release all no longer needed font resources + for( int i = nFallbackLevel; i < MAX_FALLBACK; ++i ) + { + if( mpServerFont[i] != NULL ) + { + // old server side font is no longer referenced + GlyphCache::GetInstance().UncacheFont( *mpServerFont[i] ); + mpServerFont[i] = NULL; + } + } + + // return early if there is no new font + if( !pEntry ) + return false; + + bFontVertical_ = pEntry->mbVertical; + + // return early if this is not a valid font for this graphics + if( !pEntry->mpFontData ) + return false; + + // handle the request for a non-native X11-font => use the GlyphCache + ServerFont* pServerFont = GlyphCache::GetInstance().CacheFont( *pEntry ); + if( pServerFont != NULL ) + { + // ignore fonts with e.g. corrupted font files + if( !pServerFont->TestFont() ) + { + GlyphCache::GetInstance().UncacheFont( *pServerFont ); + return false; + } + + // register to use the font + mpServerFont[ nFallbackLevel ] = pServerFont; + + // apply font specific-hint settings if needed + // TODO: also disable it for reference devices + if( !bPrinter_ ) + { + ImplServerFontEntry* pSFE = static_cast<ImplServerFontEntry*>( pEntry->mpFontEntry ); + pSFE->HandleFontOptions(); + } + + return true; + } + + return false; +} + +ImplFontOptions* GetFCFontOptions( const ImplFontAttributes& rFontAttributes, int nSize); + +void ImplServerFontEntry::HandleFontOptions( void ) +{ + if( !mpServerFont ) + return; + if( !mbGotFontOptions ) + { + // get and cache the font options + mbGotFontOptions = true; + mpFontOptions.reset(GetFCFontOptions( *maFontSelData.mpFontData, + maFontSelData.mnHeight )); + } + // apply the font options + mpServerFont->SetFontOptions( mpFontOptions ); +} + +//-------------------------------------------------------------------------- + +namespace { + +class CairoWrapper +{ +private: + oslModule mpCairoLib; + + cairo_surface_t* (*mp_xlib_surface_create_with_xrender_format)(Display *, Drawable , Screen *, XRenderPictFormat *, int , int ); + void (*mp_surface_destroy)(cairo_surface_t *); + cairo_t* (*mp_create)(cairo_surface_t *); + void (*mp_destroy)(cairo_t*); + void (*mp_clip)(cairo_t*); + void (*mp_rectangle)(cairo_t*, double, double, double, double); + cairo_font_face_t * (*mp_ft_font_face_create_for_ft_face)(FT_Face, int); + cairo_font_face_t * (*mp_ft_font_face_create_for_pattern)(void*); + void (*mp_set_font_face)(cairo_t *, cairo_font_face_t *); + void (*mp_font_face_destroy)(cairo_font_face_t *); + void (*mp_matrix_init_identity)(cairo_matrix_t *); + void (*mp_matrix_scale)(cairo_matrix_t *, double, double); + void (*mp_matrix_rotate)(cairo_matrix_t *, double); + void (*mp_set_font_matrix)(cairo_t *, const cairo_matrix_t *); + void (*mp_show_glyphs)(cairo_t *, const cairo_glyph_t *, int ); + void (*mp_set_source_rgb)(cairo_t *, double , double , double ); + void (*mp_set_font_options)(cairo_t *, const void *); + void (*mp_ft_font_options_substitute)(const void*, void*); + + bool canEmbolden() const { return mp_ft_font_face_create_for_pattern != NULL; } + + CairoWrapper(); +public: + static CairoWrapper& get(); + bool isValid() const { return (mpCairoLib != NULL); } + bool isCairoRenderable(const ServerFont& rFont); + + cairo_surface_t* xlib_surface_create_with_xrender_format(Display *pDisplay, Drawable drawable, Screen *pScreen, XRenderPictFormat *pFormat, int width, int height) + { return (*mp_xlib_surface_create_with_xrender_format)(pDisplay, drawable, pScreen, pFormat, width, height); } + void surface_destroy(cairo_surface_t *surface) { (*mp_surface_destroy)(surface); } + cairo_t* create(cairo_surface_t *surface) { return (*mp_create)(surface); } + void destroy(cairo_t *cr) { (*mp_destroy)(cr); } + void clip(cairo_t *cr) { (*mp_clip)(cr); } + void rectangle(cairo_t *cr, double x, double y, double width, double height) + { (*mp_rectangle)(cr, x, y, width, height); } + cairo_font_face_t* ft_font_face_create_for_ft_face(FT_Face face, int load_flags) + { return (*mp_ft_font_face_create_for_ft_face)(face, load_flags); } + cairo_font_face_t* ft_font_face_create_for_pattern(void *pattern) + { + return mp_ft_font_face_create_for_pattern + ? (*mp_ft_font_face_create_for_pattern)(pattern) + : NULL; + } + void set_font_face(cairo_t *cr, cairo_font_face_t *font_face) + { (*mp_set_font_face)(cr, font_face); } + void font_face_destroy(cairo_font_face_t *font_face) + { (*mp_font_face_destroy)(font_face); } + void matrix_init_identity(cairo_matrix_t *matrix) + { (*mp_matrix_init_identity)(matrix); } + void matrix_scale(cairo_matrix_t *matrix, double sx, double sy) + { (*mp_matrix_scale)(matrix, sx, sy); } + void matrix_rotate(cairo_matrix_t *matrix, double radians) + { (*mp_matrix_rotate)(matrix, radians); } + void set_font_matrix(cairo_t *cr, const cairo_matrix_t *matrix) + { (*mp_set_font_matrix)(cr, matrix); } + void show_glyphs(cairo_t *cr, const cairo_glyph_t *glyphs, int no_glyphs) + { (*mp_show_glyphs)(cr, glyphs, no_glyphs); } + void set_source_rgb(cairo_t *cr, double red, double green, double blue) + { (*mp_set_source_rgb)(cr, red, green, blue); } + void set_font_options(cairo_t *cr, const void *options) + { (*mp_set_font_options)(cr, options); } + void ft_font_options_substitute(const void *options, void *pattern) + { (*mp_ft_font_options_substitute)(options, pattern); } +}; + +static CairoWrapper* pCairoInstance = NULL; + +CairoWrapper& CairoWrapper::get() +{ + if( ! pCairoInstance ) + pCairoInstance = new CairoWrapper(); + return *pCairoInstance; +} + +CairoWrapper::CairoWrapper() +: mpCairoLib( NULL ) +{ + static const char* pDisableCairoText = getenv( "SAL_DISABLE_CAIROTEXT" ); + if( pDisableCairoText && (pDisableCairoText[0] != '0') ) + return; + + int nDummy; + if( !XQueryExtension( GetX11SalData()->GetDisplay()->GetDisplay(), "RENDER", &nDummy, &nDummy, &nDummy ) ) + return; + +#ifdef MACOSX + OUString aLibName( RTL_CONSTASCII_USTRINGPARAM( "libcairo.2.dylib" )); +#else + OUString aLibName( RTL_CONSTASCII_USTRINGPARAM( "libcairo.so.2" )); +#endif + mpCairoLib = osl_loadModule( aLibName.pData, SAL_LOADMODULE_DEFAULT ); + if( !mpCairoLib ) + return; + +#ifdef DEBUG + // check cairo version + int (*p_version)(); + p_version = (int(*)()) osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_version" ); + const int nVersion = p_version ? (*p_version)() : 0; + fprintf( stderr, "CAIRO version=%d\n", nVersion ); +#endif + + mp_xlib_surface_create_with_xrender_format = (cairo_surface_t* (*)(Display *, Drawable , Screen *, XRenderPictFormat *, int , int )) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_xlib_surface_create_with_xrender_format" ); + mp_surface_destroy = (void(*)(cairo_surface_t*)) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_surface_destroy" ); + mp_create = (cairo_t*(*)(cairo_surface_t*)) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_create" ); + mp_destroy = (void(*)(cairo_t*)) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_destroy" ); + mp_clip = (void(*)(cairo_t*)) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_clip" ); + mp_rectangle = (void(*)(cairo_t*, double, double, double, double)) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_rectangle" ); + mp_ft_font_face_create_for_ft_face = (cairo_font_face_t * (*)(FT_Face, int)) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_ft_font_face_create_for_ft_face" ); + mp_ft_font_face_create_for_pattern = (cairo_font_face_t * (*)(void*)) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_ft_font_face_create_for_pattern" ); + mp_set_font_face = (void (*)(cairo_t *, cairo_font_face_t *)) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_set_font_face" ); + mp_font_face_destroy = (void (*)(cairo_font_face_t *)) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_font_face_destroy" ); + mp_matrix_init_identity = (void (*)(cairo_matrix_t *)) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_matrix_init_identity" ); + mp_matrix_scale = (void (*)(cairo_matrix_t *, double, double)) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_matrix_scale" ); + mp_matrix_rotate = (void (*)(cairo_matrix_t *, double)) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_matrix_rotate" ); + mp_set_font_matrix = (void (*)(cairo_t *, const cairo_matrix_t *)) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_set_font_matrix" ); + mp_show_glyphs = (void (*)(cairo_t *, const cairo_glyph_t *, int )) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_show_glyphs" ); + mp_set_source_rgb = (void (*)(cairo_t *, double , double , double )) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_set_source_rgb" ); + mp_set_font_options = (void (*)(cairo_t *, const void *options )) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_set_font_options" ); + mp_ft_font_options_substitute = (void (*)(const void *, void *)) + osl_getAsciiFunctionSymbol( mpCairoLib, "cairo_ft_font_options_substitute" ); + + if( !( + mp_xlib_surface_create_with_xrender_format && + mp_surface_destroy && + mp_create && + mp_destroy && + mp_clip && + mp_rectangle && + mp_ft_font_face_create_for_ft_face && + mp_set_font_face && + mp_font_face_destroy && + mp_matrix_init_identity && + mp_matrix_scale && + mp_matrix_rotate && + mp_set_font_matrix && + mp_show_glyphs && + mp_set_source_rgb && + mp_set_font_options && + mp_ft_font_options_substitute + ) ) + { + osl_unloadModule( mpCairoLib ); + mpCairoLib = NULL; +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "not all needed symbols were found\n" ); +#endif + } +} + +bool CairoWrapper::isCairoRenderable(const ServerFont& rFont) +{ + return rFont.GetFtFace() && isValid() && rFont.GetAntialiasAdvice() && + (rFont.NeedsArtificialBold() ? canEmbolden() : true); +} + +} //namespace + +CairoFontsCache::LRUFonts CairoFontsCache::maLRUFonts; +int CairoFontsCache::mnRefCount = 0; + +CairoFontsCache::CairoFontsCache() +{ + ++mnRefCount; +} + +CairoFontsCache::~CairoFontsCache() +{ + --mnRefCount; + if (!mnRefCount && !maLRUFonts.empty()) + { + CairoWrapper &rCairo = CairoWrapper::get(); + LRUFonts::iterator aEnd = maLRUFonts.end(); + for (LRUFonts::iterator aI = maLRUFonts.begin(); aI != aEnd; ++aI) + rCairo.font_face_destroy((cairo_font_face_t*)aI->first); + } +} + +void CairoFontsCache::CacheFont(void *pFont, const CairoFontsCache::CacheId &rId) +{ + maLRUFonts.push_front( std::pair<void*, CairoFontsCache::CacheId>(pFont, rId) ); + if (maLRUFonts.size() > 8) + { + CairoWrapper &rCairo = CairoWrapper::get(); + rCairo.font_face_destroy((cairo_font_face_t*)maLRUFonts.back().first); + maLRUFonts.pop_back(); + } +} + +void* CairoFontsCache::FindCachedFont(const CairoFontsCache::CacheId &rId) +{ + LRUFonts::iterator aEnd = maLRUFonts.end(); + for (LRUFonts::iterator aI = maLRUFonts.begin(); aI != aEnd; ++aI) + if (aI->second == rId) + return aI->first; + return NULL; +} + +void X11SalGraphics::DrawCairoAAFontString( const ServerFontLayout& rLayout ) +{ + std::vector<cairo_glyph_t> cairo_glyphs; + cairo_glyphs.reserve( 256 ); + + Point aPos; + sal_GlyphId aGlyphId; + for( int nStart = 0; rLayout.GetNextGlyphs( 1, &aGlyphId, aPos, nStart ); ) + { + cairo_glyph_t aGlyph; + aGlyph.index = aGlyphId & GF_IDXMASK; + aGlyph.x = aPos.X(); + aGlyph.y = aPos.Y(); + cairo_glyphs.push_back(aGlyph); + } + + if (cairo_glyphs.empty()) + return; + + // find a XRenderPictFormat compatible with the Drawable + XRenderPictFormat* pVisualFormat = GetXRenderFormat(); + DBG_ASSERT( pVisualFormat!=NULL, "no matching XRenderPictFormat for text" ); + if( !pVisualFormat ) + return; + + CairoWrapper &rCairo = CairoWrapper::get(); + + Display* pDisplay = GetXDisplay(); + + cairo_surface_t *surface = rCairo.xlib_surface_create_with_xrender_format (pDisplay, + hDrawable_, ScreenOfDisplay(pDisplay, m_nScreen), pVisualFormat, SAL_MAX_INT16, SAL_MAX_INT16); + + /* + * It might be ideal to cache surface and cairo context between calls and + * only destroy it when the drawable changes, but to do that we need to at + * least change the SalFrame etc impls to dtor the SalGraphics *before* the + * destruction of the windows they reference + */ + cairo_t *cr = rCairo.create(surface); + rCairo.surface_destroy(surface); + + if (const void *pOptions = Application::GetSettings().GetStyleSettings().GetCairoFontOptions()) + rCairo.set_font_options( cr, pOptions); + + if( mpClipRegion && !XEmptyRegion( mpClipRegion ) ) + { + for (long i = 0; i < mpClipRegion->numRects; ++i) + { + rCairo.rectangle(cr, + mpClipRegion->rects[i].x1, + mpClipRegion->rects[i].y1, + mpClipRegion->rects[i].x2 - mpClipRegion->rects[i].x1, + mpClipRegion->rects[i].y2 - mpClipRegion->rects[i].y1); + } + rCairo.clip(cr); + } + + rCairo.set_source_rgb(cr, + SALCOLOR_RED(nTextColor_)/255.0, + SALCOLOR_GREEN(nTextColor_)/255.0, + SALCOLOR_BLUE(nTextColor_)/255.0); + + ServerFont& rFont = rLayout.GetServerFont(); + + cairo_font_face_t* font_face = NULL; + + void* pFace = rFont.GetFtFace(); + CairoFontsCache::CacheId aId; + aId.mpFace = pFace; + aId.mpOptions = rFont.GetFontOptions().get(); + aId.mbEmbolden = rFont.NeedsArtificialBold(); + font_face = (cairo_font_face_t*)m_aCairoFontsCache.FindCachedFont(aId); + if (!font_face) + { + const ImplFontOptions *pOptions = rFont.GetFontOptions().get(); + void *pPattern = pOptions ? pOptions->GetPattern(pFace, aId.mbEmbolden) : NULL; + if (pPattern) + font_face = rCairo.ft_font_face_create_for_pattern(pPattern); + if (!font_face) + font_face = rCairo.ft_font_face_create_for_ft_face(pFace, rFont.GetLoadFlags()); + m_aCairoFontsCache.CacheFont(font_face, aId); + } + + rCairo.set_font_face(cr, font_face); + + cairo_matrix_t m; + const ImplFontSelectData& rFSD = rFont.GetFontSelData(); + int nWidth = rFSD.mnWidth ? rFSD.mnWidth : rFSD.mnHeight; + + rCairo.matrix_init_identity(&m); + + if (rLayout.GetOrientation()) + rCairo.matrix_rotate(&m, (3600 - rLayout.GetOrientation()) * M_PI / 1800.0); + + rCairo.matrix_scale(&m, nWidth, rFSD.mnHeight); + if (rFont.NeedsArtificialItalic()) + m.xy = -m.xx * 0x6000L / 0x10000L; + + rCairo.set_font_matrix(cr, &m); + rCairo.show_glyphs(cr, &cairo_glyphs[0], cairo_glyphs.size()); + rCairo.destroy(cr); +} + +//-------------------------------------------------------------------------- + +void X11SalGraphics::DrawServerAAFontString( const ServerFontLayout& rLayout ) +{ + // get xrender target for this drawable + Picture aDstPic = GetXRenderPicture(); + if( !aDstPic ) + return; + + // get a XRenderPicture for the font foreground + // TODO: move into own method + XRenderPeer& rRenderPeer = XRenderPeer::GetInstance(); + XRenderPictFormat* pVisualFormat = (XRenderPictFormat*)GetXRenderFormat(); + DBG_ASSERT( pVisualFormat, "we already have a render picture, but XRenderPictFormat==NULL???"); + const int nVisualDepth = pVisualFormat->depth; + SalDisplay::RenderEntry& rEntry = GetDisplay()->GetRenderEntries( m_nScreen )[ nVisualDepth ]; + if( !rEntry.m_aPicture ) + { + // create and cache XRenderPicture for the font foreground + Display* pDisplay = GetXDisplay(); +#ifdef DEBUG + int iDummy; + unsigned uDummy; + XLIB_Window wDummy; + unsigned int nDrawDepth; + ::XGetGeometry( pDisplay, hDrawable_, &wDummy, &iDummy, &iDummy, + &uDummy, &uDummy, &uDummy, &nDrawDepth ); + DBG_ASSERT( static_cast<unsigned>(nVisualDepth) == nDrawDepth, "depth messed up for XRender" ); +#endif + + rEntry.m_aPixmap = ::XCreatePixmap( pDisplay, hDrawable_, 1, 1, nVisualDepth ); + + XRenderPictureAttributes aAttr; + aAttr.repeat = true; + rEntry.m_aPicture = rRenderPeer.CreatePicture ( rEntry.m_aPixmap, pVisualFormat, CPRepeat, &aAttr ); + } + + // set font foreground color and opacity + XRenderColor aRenderColor = GetXRenderColor( nTextColor_ ); + rRenderPeer.FillRectangle( PictOpSrc, rEntry.m_aPicture, &aRenderColor, 0, 0, 1, 1 ); + + // set clipping + // TODO: move into GetXRenderPicture()? + if( mpClipRegion && !XEmptyRegion( mpClipRegion ) ) + rRenderPeer.SetPictureClipRegion( aDstPic, mpClipRegion ); + + ServerFont& rFont = rLayout.GetServerFont(); + X11GlyphPeer& rGlyphPeer = X11GlyphCache::GetInstance().GetPeer(); + GlyphSet aGlyphSet = rGlyphPeer.GetGlyphSet( rFont, m_nScreen ); + + Point aPos; + static const int MAXGLYPHS = 160; + sal_GlyphId aGlyphAry[ MAXGLYPHS ]; + int nMaxGlyphs = rLayout.GetOrientation() ? 1 : MAXGLYPHS; + for( int nStart = 0;;) + { + int nGlyphs = rLayout.GetNextGlyphs( nMaxGlyphs, aGlyphAry, aPos, nStart ); + if( !nGlyphs ) + break; + + // #i51924# avoid 32->16bit coordinate truncation problem in X11 + // TODO: reevaluate once displays with >30000 pixels are available + if( aPos.X() >= 30000 || aPos.Y() >= 30000 ) + continue; + + unsigned int aRenderAry[ MAXGLYPHS ]; + for( int i = 0; i < nGlyphs; ++i ) + aRenderAry[ i ] = rGlyphPeer.GetGlyphId( rFont, aGlyphAry[i] ); + rRenderPeer.CompositeString32( rEntry.m_aPicture, aDstPic, + aGlyphSet, aPos.X(), aPos.Y(), aRenderAry, nGlyphs ); + } +} + +//-------------------------------------------------------------------------- + +bool X11SalGraphics::DrawServerAAForcedString( const ServerFontLayout& rLayout ) +{ + ServerFont& rFont = rLayout.GetServerFont(); + + // prepare glyphs and get extent of operation + X11GlyphPeer& rGlyphPeer = X11GlyphCache::GetInstance().GetPeer(); + int nXmin = 0; + int nXmax = 0; + int nYmin = 0; + int nYmax = 0; + int nStart = 0; + Point aPos; + sal_GlyphId nGlyph; + for( bool bFirst=true; rLayout.GetNextGlyphs( 1, &nGlyph, aPos, nStart ); ) + { + const RawBitmap* const pRawBitmap = rGlyphPeer.GetRawBitmap( rFont, nGlyph ); + if( !pRawBitmap ) + continue; + + const int nX1 = aPos.X() + pRawBitmap->mnXOffset; + const int nY1 = aPos.Y() + pRawBitmap->mnYOffset; + const int nX2 = nX1 + pRawBitmap->mnWidth; + const int nY2 = nY1 + pRawBitmap->mnHeight; + + if( bFirst ) + { + bFirst = false; + nXmin = nX1; + nXmax = nX2; + nYmin = nY1; + nYmax = nY2; + } + else + { + if( nXmin > nX1 ) nXmin = nX1; + if( nXmax < nX2 ) nXmax = nX2; + if( nYmin > nY1 ) nYmin = nY1; + if( nYmax < nY2 ) nYmax = nY2; + } + } + + // get XImage + GetDisplay()->GetXLib()->PushXErrorLevel( true ); + Display* pDisplay = GetXDisplay(); + + XRectangle aXRect; + long nWidth = 1, nHeight = 1; + if( m_pFrame ) + nWidth = m_pFrame->maGeometry.nWidth, nHeight = m_pFrame->maGeometry.nHeight; + else if( m_pVDev ) + nWidth = m_pVDev->GetWidth(), nHeight = m_pVDev->GetHeight(); + + if( mpClipRegion && !XEmptyRegion( mpClipRegion ) ) + { + // get bounding box + XClipBox( mpClipRegion, &aXRect ); + // clip with window + if( aXRect.x < 0 ) aXRect.x = 0; + + if( aXRect.y < 0 ) aXRect.y = 0; + if( aXRect.width+aXRect.x > nWidth ) aXRect.width = nWidth-aXRect.x; + if( aXRect.height+aXRect.y > nHeight ) aXRect.height = nHeight-aXRect.y; + } + else + { + aXRect.x = 0; + aXRect.y = 0; + aXRect.width = nWidth; + aXRect.height = nHeight; + } + if( m_pFrame ) + { + // clip with screen + int nScreenX = m_pFrame->maGeometry.nX+aXRect.x; + int nScreenY = m_pFrame->maGeometry.nY+aXRect.y; + const Size& rScreenSize = GetDisplay()->getDataForScreen( m_nScreen ).m_aSize; + int nScreenW = rScreenSize.Width(); + int nScreenH = rScreenSize.Height(); + if( nScreenX < 0 ) + aXRect.x -= nScreenX, aXRect.width += nScreenX; + if( nScreenX+aXRect.width > nScreenW ) + aXRect.width = nScreenW-nScreenX; + if( nScreenY < 0 ) + aXRect.y -= nScreenY, aXRect.height += nScreenY; + if( nScreenY+aXRect.height > nScreenH ) + aXRect.height = nScreenH-nScreenY; + } + + + if( nXmin < aXRect.x ) nXmin = aXRect.x; + if( nYmin < aXRect.y ) nYmin = aXRect.y; + if( nXmax >= aXRect.x+aXRect.width ) nXmax = aXRect.x + aXRect.width - 1; + if( nYmax >= aXRect.y+aXRect.height ) nYmax = aXRect.y + aXRect.height - 1; + + if( nXmin > nXmax ) + return false; + if( nYmin > nYmax ) + return false; + + XImage* pImg = XGetImage( pDisplay, hDrawable_, + nXmin, nYmin, + (nXmax-nXmin+1), (nYmax-nYmin+1), + ~0, ZPixmap ); + if( pImg == NULL ) + { + if( m_pFrame ) + { + // the reason we did not get an image could be that the frame + // geometry changed in the meantime; lets get the current geometry + // and clip against the current window size as well as the screen + // with the current frame position + const Size& rScreenSize = GetDisplay()->getDataForScreen(m_nScreen).m_aSize; + int nScreenW = rScreenSize.Width(); + int nScreenH = rScreenSize.Height(); + XLIB_Window aRoot = None; + int x = 0, y = 0; + unsigned int w = 0, h = 0, bw = 0, d; + XGetGeometry( pDisplay, hDrawable_, &aRoot, &x, &y, &w, &h, &bw, &d ); + XTranslateCoordinates( pDisplay, hDrawable_, aRoot, 0, 0, &x, &y, &aRoot ); + if( nXmin + x < 0 ) // clip on left screen edge + nXmin += x-nXmin; + if( nYmin + y < 0 ) // clip on top screen edge + nYmin += y-nYmin; + if( nXmax >= int(w) ) // clip on right window egde + nXmax = w-1; + if( nYmax >= int(h) ) // clip on bottom window edge + nYmax = h-1; + if( nXmax + x >= nScreenW ) // clip on right screen edge + nXmax -= (nXmax + x - nScreenW)+1; + if( nYmax + y >= nScreenH ) // clip on bottom screen edge + nYmax -= (nYmax + y - nScreenH)+1; + if( nXmax >= nXmin && nYmax >= nYmin ) + { + // try again to get the image + pImg = XGetImage( pDisplay, hDrawable_, + nXmin, nYmin, + (nXmax-nXmin+1), (nYmax-nYmin+1), + ~0, ZPixmap ); + } + } + if( pImg == NULL ) + { + GetDisplay()->GetXLib()->PopXErrorLevel(); + return false; + } + } + + // prepare context + GC nGC = GetFontGC(); + XGCValues aGCVal; + XGetGCValues( pDisplay, nGC, GCForeground, &aGCVal ); + + unsigned long nOrigColor = XGetPixel( pImg, 0, 0 ); + XPutPixel( pImg, 0, 0, aGCVal.foreground ); + unsigned char aColor[4]; + aColor[0] = pImg->data[0]; + aColor[1] = pImg->data[1]; + aColor[2] = pImg->data[2]; + aColor[3] = pImg->data[3]; + XPutPixel( pImg, 0, 0, nOrigColor ); + + // work on XImage + const int bpp = pImg->bits_per_pixel >> 3; + for( nStart = 0; rLayout.GetNextGlyphs( 1, &nGlyph, aPos, nStart ); ) + { + const RawBitmap* const pRawBitmap = rGlyphPeer.GetRawBitmap( rFont, nGlyph ); + if( !pRawBitmap ) + continue; + + const int nX1 = aPos.X() + pRawBitmap->mnXOffset; + const int nY1 = aPos.Y() + pRawBitmap->mnYOffset; + + if( (nX1 <= nXmax) && (int(nX1 + pRawBitmap->mnWidth) > nXmin) + && (nY1 <= nYmax) && (int(nY1 + pRawBitmap->mnHeight) > nYmin) ) + { + const unsigned char* p10 = pRawBitmap->mpBits; + unsigned char* p20 = (unsigned char*)pImg->data; // dest left limit + p20 += (nY1 - nYmin) * pImg->bytes_per_line; + unsigned char* p21 = p20 + (nX1 - nXmin + pImg->xoffset) * bpp; + int y = pRawBitmap->mnHeight; + if( y > nYmax - nY1 ) + y = nYmax - nY1 + 1; + while( --y >= 0 ) + { + if( p20 >= (unsigned char*)pImg->data ) + { + unsigned char* const p22 = p20 + pImg->width * bpp; // dest right limit + unsigned char* pDst = p21; + const unsigned char* pSrc = p10; + for( int x = pRawBitmap->mnWidth; (--x >= 0) && (p22 > pDst); ++pSrc ) + { + if( (*pSrc == 0) || (p20 > pDst) ) // keep background + pDst += bpp; + else if( *pSrc == 0xFF ) // paint foreground + { + const unsigned char* pColor = aColor; + for( int z = bpp; --z >= 0; ++pColor, ++pDst ) + *pDst = *pColor; + } + else // blend fg into bg + { + const unsigned char* pColor = aColor; + for( int z = bpp; --z >= 0; ++pColor, ++pDst ) + // theoretically it should be *257) >> 16 + // but the error is <0.4% worst case and we are in + // the innermost loop of very perf-sensitive code + + *pDst += (*pSrc * ((int)*pColor - *pDst)) >> 8; + } + } + } + p10 += pRawBitmap->mnScanlineSize; + p20 += pImg->bytes_per_line; + p21 += pImg->bytes_per_line; + } + } + } + + // put XImage + XPutImage( pDisplay, hDrawable_, nGC, pImg, + 0, 0, nXmin, nYmin, (nXmax - nXmin + 1), (nYmax - nYmin + 1) ); + XDestroyImage( pImg ); + + GetDisplay()->GetXLib()->PopXErrorLevel(); + return true; +} + +//-------------------------------------------------------------------------- + +void X11SalGraphics::DrawServerSimpleFontString( const ServerFontLayout& rSalLayout ) +{ + ServerFont& rFont = rSalLayout.GetServerFont(); + X11GlyphPeer& rGlyphPeer = X11GlyphCache::GetInstance().GetPeer(); + + Display* pDisplay = GetXDisplay(); + GC nGC = GetFontGC(); + + XGCValues aGCVal; + aGCVal.fill_style = FillStippled; + aGCVal.line_width = 0; + GC tmpGC = XCreateGC( pDisplay, hDrawable_, GCFillStyle|GCLineWidth, &aGCVal ); + XCopyGC( pDisplay, nGC, (1<<GCLastBit)-(1+GCFillStyle+GCLineWidth), tmpGC ); + + Point aPos; + sal_GlyphId nGlyph; + for( int nStart = 0; rSalLayout.GetNextGlyphs( 1, &nGlyph, aPos, nStart ); ) + { + // #i51924# avoid 32->16bit coordinate truncation problem in X11 + // TODO: reevaluate once displays with >30000 pixels are available + if( aPos.X() >= 30000 || aPos.Y() >= 30000 ) + continue; + + Pixmap aStipple = rGlyphPeer.GetPixmap( rFont, nGlyph, m_nScreen ); + const GlyphMetric& rGM = rFont.GetGlyphMetric( nGlyph ); + + if( aStipple != None ) + { + const int nDestX = aPos.X() + rGM.GetOffset().X(); + const int nDestY = aPos.Y() + rGM.GetOffset().Y(); + + aGCVal.stipple = aStipple; + aGCVal.ts_x_origin = nDestX; + aGCVal.ts_y_origin = nDestY; + XChangeGC( pDisplay, tmpGC, GCStipple|GCTileStipXOrigin|GCTileStipYOrigin, &aGCVal ); + + const int nWidth = rGM.GetSize().Width(); + const int nHeight = rGM.GetSize().Height(); + XFillRectangle( pDisplay, hDrawable_, tmpGC, nDestX, nDestY, nWidth, nHeight ); + } + } + + XFreeGC( pDisplay, tmpGC ); +} + +//-------------------------------------------------------------------------- + +void X11SalGraphics::DrawServerFontLayout( const ServerFontLayout& rLayout ) +{ + // draw complex text + ServerFont& rFont = rLayout.GetServerFont(); + const bool bVertical = rFont.GetFontSelData().mbVertical; + + if( !bVertical && CairoWrapper::get().isCairoRenderable(rFont) ) + DrawCairoAAFontString( rLayout ); + else + { + X11GlyphPeer& rGlyphPeer = X11GlyphCache::GetInstance().GetPeer(); + if( rGlyphPeer.GetGlyphSet( rFont, m_nScreen ) ) + DrawServerAAFontString( rLayout ); + else if( !rGlyphPeer.ForcedAntialiasing( rFont, m_nScreen ) ) + DrawServerSimpleFontString( rLayout ); + else + DrawServerAAForcedString( rLayout ); + } +} + +//-------------------------------------------------------------------------- + +const ImplFontCharMap* X11SalGraphics::GetImplFontCharMap() const +{ + if( !mpServerFont[0] ) + return NULL; + + const ImplFontCharMap* pIFCMap = mpServerFont[0]->GetImplFontCharMap(); + return pIFCMap; +} + +bool X11SalGraphics::GetImplFontCapabilities(vcl::FontCapabilities &rGetImplFontCapabilities) const +{ + if (!mpServerFont[0]) + return false; + return mpServerFont[0]->GetFontCapabilities(rGetImplFontCapabilities); +} + +// ---------------------------------------------------------------------------- +// +// SalGraphics +// +// ---------------------------------------------------------------------------- + +sal_uInt16 X11SalGraphics::SetFont( ImplFontSelectData *pEntry, int nFallbackLevel ) +{ + sal_uInt16 nRetVal = 0; + if( !setFont( pEntry, nFallbackLevel ) ) + nRetVal |= SAL_SETFONT_BADFONT; + if( bPrinter_ || (mpServerFont[ nFallbackLevel ] != NULL) ) + nRetVal |= SAL_SETFONT_USEDRAWTEXTARRAY; + return nRetVal; +} + +// ---------------------------------------------------------------------------- + +void +X11SalGraphics::SetTextColor( SalColor nSalColor ) +{ + if( nTextColor_ != nSalColor ) + { + nTextColor_ = nSalColor; + nTextPixel_ = GetPixel( nSalColor ); + bFontGC_ = sal_False; + } +} + +// ---------------------------------------------------------------------------- + +bool X11SalGraphics::AddTempDevFont( ImplDevFontList* pFontList, + const String& rFileURL, const String& rFontName ) +{ + // inform PSP font manager + rtl::OUString aUSystemPath; + OSL_VERIFY( !osl::FileBase::getSystemPathFromFileURL( rFileURL, aUSystemPath ) ); + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + OString aOFileName( OUStringToOString( aUSystemPath, aEncoding ) ); + psp::PrintFontManager& rMgr = psp::PrintFontManager::get(); + int nFontId = rMgr.addFontFile( aOFileName, 0 ); + if( !nFontId ) + return false; + + // prepare font data + psp::FastPrintFontInfo aInfo; + rMgr.getFontFastInfo( nFontId, aInfo ); + aInfo.m_aFamilyName = rFontName; + + // inform glyph cache of new font + ImplDevFontAttributes aDFA = PspGraphics::Info2DevFontAttributes( aInfo ); + aDFA.mnQuality += 5800; + + int nFaceNum = rMgr.getFontFaceNumber( aInfo.m_nID ); + if( nFaceNum < 0 ) + nFaceNum = 0; + + GlyphCache& rGC = X11GlyphCache::GetInstance(); + const rtl::OString& rFileName = rMgr.getFontFileSysPath( aInfo.m_nID ); + rGC.AddFontFile( rFileName, nFaceNum, aInfo.m_nID, aDFA ); + + // announce new font to device's font list + rGC.AnnounceFonts( pFontList ); + return true; +} + +// ---------------------------------------------------------------------------- + +void RegisterFontSubstitutors( ImplDevFontList* ); + +void X11SalGraphics::GetDevFontList( ImplDevFontList *pList ) +{ + // prepare the GlyphCache using psprint's font infos + X11GlyphCache& rGC = X11GlyphCache::GetInstance(); + + psp::PrintFontManager& rMgr = psp::PrintFontManager::get(); + ::std::list< psp::fontID > aList; + ::std::list< psp::fontID >::iterator it; + psp::FastPrintFontInfo aInfo; + rMgr.getFontList( aList ); + for( it = aList.begin(); it != aList.end(); ++it ) + { + if( !rMgr.getFontFastInfo( *it, aInfo ) ) + continue; + + // the GlyphCache must not bother with builtin fonts because + // it cannot access or use them anyway + if( aInfo.m_eType == psp::fonttype::Builtin ) + continue; + + // normalize face number to the GlyphCache + int nFaceNum = rMgr.getFontFaceNumber( aInfo.m_nID ); + if( nFaceNum < 0 ) + nFaceNum = 0; + + // for fonts where extra kerning info can be provided on demand + // an ExtraKernInfo object is supplied + const ExtraKernInfo* pExtraKernInfo = NULL; + if( aInfo.m_eType == psp::fonttype::Type1 ) + pExtraKernInfo = new PspKernInfo( *it ); + + // inform GlyphCache about this font provided by the PsPrint subsystem + ImplDevFontAttributes aDFA = PspGraphics::Info2DevFontAttributes( aInfo ); + aDFA.mnQuality += 4096; + const rtl::OString& rFileName = rMgr.getFontFileSysPath( aInfo.m_nID ); + rGC.AddFontFile( rFileName, nFaceNum, aInfo.m_nID, aDFA, pExtraKernInfo ); + } + + // announce glyphcache fonts + rGC.AnnounceFonts( pList ); + + // register platform specific font substitutions if available + if( rMgr.hasFontconfig() ) + RegisterFontSubstitutors( pList ); + + ImplGetSVData()->maGDIData.mbNativeFontConfig = rMgr.hasFontconfig(); +} + +// ---------------------------------------------------------------------------- + +void X11SalGraphics::GetDevFontSubstList( OutputDevice* ) +{ + // no device specific font substitutions on X11 needed +} + +// ---------------------------------------------------------------------------- + +void cairosubcallback( void* pPattern ) +{ + CairoWrapper& rCairo = CairoWrapper::get(); + if( !rCairo.isValid() ) + return; + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + const void* pFontOptions = rStyleSettings.GetCairoFontOptions(); + if( !pFontOptions ) + return; + rCairo.ft_font_options_substitute( pFontOptions, pPattern ); +} + +ImplFontOptions* GetFCFontOptions( const ImplFontAttributes& rFontAttributes, int nSize) +{ + // TODO: get rid of these insane enum-conversions + // e.g. by using the classic vclenum values inside VCL + + psp::FastPrintFontInfo aInfo; + // set family name + aInfo.m_aFamilyName = rFontAttributes.GetFamilyName(); + // set italic + switch( rFontAttributes.GetSlant() ) + { + case ITALIC_NONE: + aInfo.m_eItalic = psp::italic::Upright; + break; + case ITALIC_NORMAL: + aInfo.m_eItalic = psp::italic::Italic; + break; + case ITALIC_OBLIQUE: + aInfo.m_eItalic = psp::italic::Oblique; + break; + default: + aInfo.m_eItalic = psp::italic::Unknown; + break; + } + // set weight + switch( rFontAttributes.GetWeight() ) + { + case WEIGHT_THIN: + aInfo.m_eWeight = psp::weight::Thin; + break; + case WEIGHT_ULTRALIGHT: + aInfo.m_eWeight = psp::weight::UltraLight; + break; + case WEIGHT_LIGHT: + aInfo.m_eWeight = psp::weight::Light; + break; + case WEIGHT_SEMILIGHT: + aInfo.m_eWeight = psp::weight::SemiLight; + break; + case WEIGHT_NORMAL: + aInfo.m_eWeight = psp::weight::Normal; + break; + case WEIGHT_MEDIUM: + aInfo.m_eWeight = psp::weight::Medium; + break; + case WEIGHT_SEMIBOLD: + aInfo.m_eWeight = psp::weight::SemiBold; + break; + case WEIGHT_BOLD: + aInfo.m_eWeight = psp::weight::Bold; + break; + case WEIGHT_ULTRABOLD: + aInfo.m_eWeight = psp::weight::UltraBold; + break; + case WEIGHT_BLACK: + aInfo.m_eWeight = psp::weight::Black; + break; + default: + aInfo.m_eWeight = psp::weight::Unknown; + break; + } + // set width + switch( rFontAttributes.GetWidthType() ) + { + case WIDTH_ULTRA_CONDENSED: + aInfo.m_eWidth = psp::width::UltraCondensed; + break; + case WIDTH_EXTRA_CONDENSED: + aInfo.m_eWidth = psp::width::ExtraCondensed; + break; + case WIDTH_CONDENSED: + aInfo.m_eWidth = psp::width::Condensed; + break; + case WIDTH_SEMI_CONDENSED: + aInfo.m_eWidth = psp::width::SemiCondensed; + break; + case WIDTH_NORMAL: + aInfo.m_eWidth = psp::width::Normal; + break; + case WIDTH_SEMI_EXPANDED: + aInfo.m_eWidth = psp::width::SemiExpanded; + break; + case WIDTH_EXPANDED: + aInfo.m_eWidth = psp::width::Expanded; + break; + case WIDTH_EXTRA_EXPANDED: + aInfo.m_eWidth = psp::width::ExtraExpanded; + break; + case WIDTH_ULTRA_EXPANDED: + aInfo.m_eWidth = psp::width::UltraExpanded; + break; + default: + aInfo.m_eWidth = psp::width::Unknown; + break; + } + + const psp::PrintFontManager& rPFM = psp::PrintFontManager::get(); + return rPFM.getFontOptions(aInfo, nSize, cairosubcallback); +} + +// ---------------------------------------------------------------------------- + +void +X11SalGraphics::GetFontMetric( ImplFontMetricData *pMetric, int nFallbackLevel ) +{ + if( nFallbackLevel >= MAX_FALLBACK ) + return; + + if( mpServerFont[nFallbackLevel] != NULL ) + { + long rDummyFactor; + mpServerFont[nFallbackLevel]->FetchFontMetric( *pMetric, rDummyFactor ); + } +} + +// --------------------------------------------------------------------------- + +sal_uLong +X11SalGraphics::GetKernPairs( sal_uLong nPairs, ImplKernPairData *pKernPairs ) +{ + if( ! bPrinter_ ) + { + if( mpServerFont[0] != NULL ) + { + ImplKernPairData* pTmpKernPairs; + sal_uLong nGotPairs = mpServerFont[0]->GetKernPairs( &pTmpKernPairs ); + for( unsigned int i = 0; i < nPairs && i < nGotPairs; ++i ) + pKernPairs[ i ] = pTmpKernPairs[ i ]; + delete[] pTmpKernPairs; + return nGotPairs; + } + } + return 0; +} + +// --------------------------------------------------------------------------- + +sal_Bool X11SalGraphics::GetGlyphBoundRect( long nGlyphIndex, Rectangle& rRect ) +{ + int nLevel = nGlyphIndex >> GF_FONTSHIFT; + if( nLevel < 0 || nLevel >= MAX_FALLBACK ) + return sal_False; + + ServerFont* pSF = mpServerFont[ nLevel ]; + if( !pSF ) + return sal_False; + + nGlyphIndex &= ~GF_FONTMASK; + const GlyphMetric& rGM = pSF->GetGlyphMetric( nGlyphIndex ); + rRect = Rectangle( rGM.GetOffset(), rGM.GetSize() ); + return sal_True; +} + +// --------------------------------------------------------------------------- + +sal_Bool X11SalGraphics::GetGlyphOutline( long nGlyphIndex, + ::basegfx::B2DPolyPolygon& rPolyPoly ) +{ + int nLevel = nGlyphIndex >> GF_FONTSHIFT; + if( nLevel >= MAX_FALLBACK ) + return sal_False; + + ServerFont* pSF = mpServerFont[ nLevel ]; + if( !pSF ) + return sal_False; + + nGlyphIndex &= ~GF_FONTMASK; + if( pSF->GetGlyphOutline( nGlyphIndex, rPolyPoly ) ) + return sal_True; + + return sal_False; +} + +//-------------------------------------------------------------------------- + +SalLayout* X11SalGraphics::GetTextLayout( ImplLayoutArgs& rArgs, int nFallbackLevel ) +{ + SalLayout* pLayout = NULL; + + if( mpServerFont[ nFallbackLevel ] + && !(rArgs.mnFlags & SAL_LAYOUT_DISABLE_GLYPH_PROCESSING) ) + { +#ifdef ENABLE_GRAPHITE + // Is this a Graphite font? + if (!bDisableGraphite_ && + GraphiteServerFontLayout::IsGraphiteEnabledFont(mpServerFont[nFallbackLevel])) + { + pLayout = new GraphiteServerFontLayout(*mpServerFont[nFallbackLevel]); + } + else +#endif + pLayout = new ServerFontLayout( *mpServerFont[ nFallbackLevel ] ); + } + + return pLayout; +} + +//-------------------------------------------------------------------------- + +SystemFontData X11SalGraphics::GetSysFontData( int nFallbacklevel ) const +{ + SystemFontData aSysFontData; + aSysFontData.nSize = sizeof( SystemFontData ); + aSysFontData.nFontId = 0; + + if (nFallbacklevel >= MAX_FALLBACK) nFallbacklevel = MAX_FALLBACK - 1; + if (nFallbacklevel < 0 ) nFallbacklevel = 0; + + if (mpServerFont[nFallbacklevel] != NULL) + { + ServerFont* rFont = mpServerFont[nFallbacklevel]; + aSysFontData.nFontId = rFont->GetFtFace(); + aSysFontData.nFontFlags = rFont->GetLoadFlags(); + aSysFontData.bFakeBold = rFont->NeedsArtificialBold(); + aSysFontData.bFakeItalic = rFont->NeedsArtificialItalic(); + aSysFontData.bAntialias = rFont->GetAntialiasAdvice(); + aSysFontData.bVerticalCharacterType = rFont->GetFontSelData().mbVertical; + } + + return aSysFontData; +} + +//-------------------------------------------------------------------------- + +sal_Bool X11SalGraphics::CreateFontSubset( + const rtl::OUString& rToFile, + const ImplFontData* pFont, + sal_Int32* pGlyphIDs, + sal_uInt8* pEncoding, + sal_Int32* pWidths, + int nGlyphCount, + FontSubsetInfo& rInfo + ) +{ + // in this context the pFont->GetFontId() is a valid PSP + // font since they are the only ones left after the PDF + // export has filtered its list of subsettable fonts (for + // which this method was created). The correct way would + // be to have the GlyphCache search for the ImplFontData pFont + psp::fontID aFont = pFont->GetFontId(); + + psp::PrintFontManager& rMgr = psp::PrintFontManager::get(); + bool bSuccess = rMgr.createFontSubset( rInfo, + aFont, + rToFile, + pGlyphIDs, + pEncoding, + pWidths, + nGlyphCount ); + return bSuccess; +} + +//-------------------------------------------------------------------------- + +const void* X11SalGraphics::GetEmbedFontData( const ImplFontData* pFont, const sal_Ucs* pUnicodes, sal_Int32* pWidths, FontSubsetInfo& rInfo, long* pDataLen ) +{ + // in this context the pFont->GetFontId() is a valid PSP + // font since they are the only ones left after the PDF + // export has filtered its list of subsettable fonts (for + // which this method was created). The correct way would + // be to have the GlyphCache search for the ImplFontData pFont + psp::fontID aFont = pFont->GetFontId(); + return PspGraphics::DoGetEmbedFontData( aFont, pUnicodes, pWidths, rInfo, pDataLen ); +} + +//-------------------------------------------------------------------------- + +void X11SalGraphics::FreeEmbedFontData( const void* pData, long nLen ) +{ + PspGraphics::DoFreeEmbedFontData( pData, nLen ); +} + +//-------------------------------------------------------------------------- + +const Ucs2SIntMap* X11SalGraphics::GetFontEncodingVector( const ImplFontData* pFont, const Ucs2OStrMap** pNonEncoded ) +{ + // in this context the pFont->GetFontId() is a valid PSP + // font since they are the only ones left after the PDF + // export has filtered its list of subsettable fonts (for + // which this method was created). The correct way would + // be to have the GlyphCache search for the ImplFontData pFont + psp::fontID aFont = pFont->GetFontId(); + return PspGraphics::DoGetFontEncodingVector( aFont, pNonEncoded ); +} + +//-------------------------------------------------------------------------- + +void X11SalGraphics::GetGlyphWidths( const ImplFontData* pFont, + bool bVertical, + Int32Vector& rWidths, + Ucs2UIntMap& rUnicodeEnc ) +{ + // in this context the pFont->GetFontId() is a valid PSP + // font since they are the only ones left after the PDF + // export has filtered its list of subsettable fonts (for + // which this method was created). The correct way would + // be to have the GlyphCache search for the ImplFontData pFont + psp::fontID aFont = pFont->GetFontId(); + PspGraphics::DoGetGlyphWidths( aFont, bVertical, rWidths, rUnicodeEnc ); +} + +// =========================================================================== +// platform specific font substitution hooks + +class FcPreMatchSubstititution +: public ImplPreMatchFontSubstitution +{ +public: + bool FindFontSubstitute( ImplFontSelectData& ) const; + +private: + typedef ::boost::unordered_map< ::rtl::OUString, ::rtl::OUString, ::rtl::OUStringHash > + CachedFontMapType; + mutable CachedFontMapType maCachedFontMap; +}; + +class FcGlyphFallbackSubstititution +: public ImplGlyphFallbackFontSubstitution +{ + // TODO: add a cache +public: + bool FindFontSubstitute( ImplFontSelectData&, OUString& rMissingCodes ) const; +}; + +void RegisterFontSubstitutors( ImplDevFontList* pList ) +{ + // init font substitution defaults + int nDisableBits = 0; +#ifdef SOLARIS + nDisableBits = 1; // disable "font fallback" here on default +#endif + // apply the environment variable if any + const char* pEnvStr = ::getenv( "SAL_DISABLE_FC_SUBST" ); + if( pEnvStr ) + { + if( (*pEnvStr >= '0') && (*pEnvStr <= '9') ) + nDisableBits = (*pEnvStr - '0'); + else + nDisableBits = ~0U; // no specific bits set: disable all + } + + // register font fallback substitutions (unless disabled by bit0) + if( (nDisableBits & 1) == 0 ) + { + static FcPreMatchSubstititution aSubstPreMatch; + pList->SetPreMatchHook( &aSubstPreMatch ); + } + + // register glyph fallback substitutions (unless disabled by bit1) + if( (nDisableBits & 2) == 0 ) + { + static FcGlyphFallbackSubstititution aSubstFallback; + pList->SetFallbackHook( &aSubstFallback ); + } +} + +// ----------------------------------------------------------------------- + +static ImplFontSelectData GetFcSubstitute(const ImplFontSelectData &rFontSelData, OUString& rMissingCodes ) +{ + ImplFontSelectData aRet(rFontSelData); + + const rtl::OString aLangAttrib = MsLangId::convertLanguageToIsoByteString( rFontSelData.meLanguage ); + + psp::italic::type eItalic = psp::italic::Unknown; + if( rFontSelData.GetSlant() != ITALIC_DONTKNOW ) + { + switch( rFontSelData.GetSlant() ) + { + case ITALIC_NONE: eItalic = psp::italic::Upright; break; + case ITALIC_NORMAL: eItalic = psp::italic::Italic; break; + case ITALIC_OBLIQUE: eItalic = psp::italic::Oblique; break; + default: + break; + } + } + + psp::weight::type eWeight = psp::weight::Unknown; + if( rFontSelData.GetWeight() != WEIGHT_DONTKNOW ) + { + switch( rFontSelData.GetWeight() ) + { + case WEIGHT_THIN: eWeight = psp::weight::Thin; break; + case WEIGHT_ULTRALIGHT: eWeight = psp::weight::UltraLight; break; + case WEIGHT_LIGHT: eWeight = psp::weight::Light; break; + case WEIGHT_SEMILIGHT: eWeight = psp::weight::SemiLight; break; + case WEIGHT_NORMAL: eWeight = psp::weight::Normal; break; + case WEIGHT_MEDIUM: eWeight = psp::weight::Medium; break; + case WEIGHT_SEMIBOLD: eWeight = psp::weight::SemiBold; break; + case WEIGHT_BOLD: eWeight = psp::weight::Bold; break; + case WEIGHT_ULTRABOLD: eWeight = psp::weight::UltraBold; break; + case WEIGHT_BLACK: eWeight = psp::weight::Black; break; + default: + break; + } + } + + psp::width::type eWidth = psp::width::Unknown; + if( rFontSelData.GetWidthType() != WIDTH_DONTKNOW ) + { + switch( rFontSelData.GetWidthType() ) + { + case WIDTH_ULTRA_CONDENSED: eWidth = psp::width::UltraCondensed; break; + case WIDTH_EXTRA_CONDENSED: eWidth = psp::width::ExtraCondensed; break; + case WIDTH_CONDENSED: eWidth = psp::width::Condensed; break; + case WIDTH_SEMI_CONDENSED: eWidth = psp::width::SemiCondensed; break; + case WIDTH_NORMAL: eWidth = psp::width::Normal; break; + case WIDTH_SEMI_EXPANDED: eWidth = psp::width::SemiExpanded; break; + case WIDTH_EXPANDED: eWidth = psp::width::Expanded; break; + case WIDTH_EXTRA_EXPANDED: eWidth = psp::width::ExtraExpanded; break; + case WIDTH_ULTRA_EXPANDED: eWidth = psp::width::UltraExpanded; break; + default: + break; + } + } + + psp::pitch::type ePitch = psp::pitch::Unknown; + if( rFontSelData.GetPitch() != PITCH_DONTKNOW ) + { + switch( rFontSelData.GetPitch() ) + { + case PITCH_FIXED: ePitch=psp::pitch::Fixed; break; + case PITCH_VARIABLE: ePitch=psp::pitch::Variable; break; + default: + break; + } + } + + const psp::PrintFontManager& rMgr = psp::PrintFontManager::get(); + aRet.maSearchName = rMgr.Substitute( rFontSelData.maTargetName, rMissingCodes, aLangAttrib, eItalic, eWeight, eWidth, ePitch); + + switch (eItalic) + { + case psp::italic::Upright: aRet.meItalic = ITALIC_NONE; break; + case psp::italic::Italic: aRet.meItalic = ITALIC_NORMAL; break; + case psp::italic::Oblique: aRet.meItalic = ITALIC_OBLIQUE; break; + default: + break; + } + + switch (eWeight) + { + case psp::weight::Thin: aRet.meWeight = WEIGHT_THIN; break; + case psp::weight::UltraLight: aRet.meWeight = WEIGHT_ULTRALIGHT; break; + case psp::weight::Light: aRet.meWeight = WEIGHT_LIGHT; break; + case psp::weight::SemiLight: aRet.meWeight = WEIGHT_SEMILIGHT; break; + case psp::weight::Normal: aRet.meWeight = WEIGHT_NORMAL; break; + case psp::weight::Medium: aRet.meWeight = WEIGHT_MEDIUM; break; + case psp::weight::SemiBold: aRet.meWeight = WEIGHT_SEMIBOLD; break; + case psp::weight::Bold: aRet.meWeight = WEIGHT_BOLD; break; + case psp::weight::UltraBold: aRet.meWeight = WEIGHT_ULTRABOLD; break; + case psp::weight::Black: aRet.meWeight = WEIGHT_BLACK; break; + default: + break; + } + + switch (eWidth) + { + case psp::width::UltraCondensed: aRet.meWidthType = WIDTH_ULTRA_CONDENSED; break; + case psp::width::ExtraCondensed: aRet.meWidthType = WIDTH_EXTRA_CONDENSED; break; + case psp::width::Condensed: aRet.meWidthType = WIDTH_CONDENSED; break; + case psp::width::SemiCondensed: aRet.meWidthType = WIDTH_SEMI_CONDENSED; break; + case psp::width::Normal: aRet.meWidthType = WIDTH_NORMAL; break; + case psp::width::SemiExpanded: aRet.meWidthType = WIDTH_SEMI_EXPANDED; break; + case psp::width::Expanded: aRet.meWidthType = WIDTH_EXPANDED; break; + case psp::width::ExtraExpanded: aRet.meWidthType = WIDTH_EXTRA_EXPANDED; break; + case psp::width::UltraExpanded: aRet.meWidthType = WIDTH_ULTRA_EXPANDED; break; + default: + break; + } + + switch (ePitch) + { + case psp::pitch::Fixed: aRet.mePitch = PITCH_FIXED; break; + case psp::pitch::Variable: aRet.mePitch = PITCH_VARIABLE; break; + default: + break; + } + + return aRet; +} + +namespace +{ + bool uselessmatch(const ImplFontSelectData &rOrig, const ImplFontSelectData &rNew) + { + return + ( + rOrig.maTargetName == rNew.maSearchName && + rOrig.meWeight == rNew.meWeight && + rOrig.meItalic == rNew.meItalic && + rOrig.mePitch == rNew.mePitch && + rOrig.meWidthType == rNew.meWidthType + ); + } +} + +//-------------------------------------------------------------------------- + +bool FcPreMatchSubstititution::FindFontSubstitute( ImplFontSelectData &rFontSelData ) const +{ + // We dont' actually want to talk to Fontconfig at all for symbol fonts + if( rFontSelData.IsSymbolFont() ) + return false; + // StarSymbol is a unicode font, but it still deserves the symbol flag + if( 0 == rFontSelData.maSearchName.CompareIgnoreCaseToAscii( "starsymbol", 10) + || 0 == rFontSelData.maSearchName.CompareIgnoreCaseToAscii( "opensymbol", 10) ) + return false; + + CachedFontMapType::const_iterator itr = maCachedFontMap.find(rFontSelData.maTargetName); + if (itr != maCachedFontMap.end()) + { + // Cached substitution pair + rFontSelData.maSearchName = itr->second; + return true; + } + + rtl::OUString aDummy; + const ImplFontSelectData aOut = GetFcSubstitute( rFontSelData, aDummy ); + + maCachedFontMap.insert( + CachedFontMapType::value_type(rFontSelData.maTargetName, aOut.maSearchName)); + + if( !aOut.maSearchName.Len() ) + return false; + + const bool bHaveSubstitute = !uselessmatch( rFontSelData, aOut ); + +#ifdef DEBUG + const ByteString aOrigName( rFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 ); + const ByteString aSubstName( aOut.maSearchName, RTL_TEXTENCODING_UTF8 ); + printf( "FcPreMatchSubstititution \"%s\" bipw=%d%d%d%d -> ", + aOrigName.GetBuffer(), rFontSelData.meWeight, rFontSelData.meItalic, + rFontSelData.mePitch, rFontSelData.meWidthType ); + if( !bHaveSubstitute ) + printf( "no substitute available\n" ); + else + printf( "\"%s\" bipw=%d%d%d%d\n", aSubstName.GetBuffer(), + aOut.meWeight, aOut.meItalic, aOut.mePitch, aOut.meWidthType ); +#endif + + if( bHaveSubstitute ) + rFontSelData = aOut; + + return bHaveSubstitute; +} + +// ----------------------------------------------------------------------- + +bool FcGlyphFallbackSubstititution::FindFontSubstitute( ImplFontSelectData& rFontSelData, + rtl::OUString& rMissingCodes ) const +{ + // We dont' actually want to talk to Fontconfig at all for symbol fonts + if( rFontSelData.IsSymbolFont() ) + return false; + // StarSymbol is a unicode font, but it still deserves the symbol flag + if( 0 == rFontSelData.maSearchName.CompareIgnoreCaseToAscii( "starsymbol", 10) + || 0 == rFontSelData.maSearchName.CompareIgnoreCaseToAscii( "opensymbol", 10) ) + return false; + + const ImplFontSelectData aOut = GetFcSubstitute( rFontSelData, rMissingCodes ); + // TODO: cache the unicode + srcfont specific result + // FC doing it would be preferable because it knows the invariables + // e.g. FC knows the FC rule that all Arial gets replaced by LiberationSans + // whereas we would have to check for every size or attribute + if( !aOut.maSearchName.Len() ) + return false; + + const bool bHaveSubstitute = !uselessmatch( rFontSelData, aOut ); + +#ifdef DEBUG + const ByteString aOrigName( rFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 ); + const ByteString aSubstName( aOut.maSearchName, RTL_TEXTENCODING_UTF8 ); + printf( "FcGFSubstititution \"%s\" bipw=%d%d%d%d ->", + aOrigName.GetBuffer(), rFontSelData.meWeight, rFontSelData.meItalic, + rFontSelData.mePitch, rFontSelData.meWidthType ); + if( !bHaveSubstitute ) + printf( "no substitute available\n" ); + else + printf( "\"%s\" bipw=%d%d%d%d\n", aSubstName.GetBuffer(), + aOut.meWeight, aOut.meItalic, aOut.mePitch, aOut.meWidthType ); +#endif + + if( bHaveSubstitute ) + rFontSelData = aOut; + + return bHaveSubstitute; +} + +// =========================================================================== + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/gdi/salprnpsp.cxx b/vcl/unx/generic/gdi/salprnpsp.cxx new file mode 100644 index 000000000000..009621a9e22e --- /dev/null +++ b/vcl/unx/generic/gdi/salprnpsp.cxx @@ -0,0 +1,1489 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +/** + this file implements the sal printer interface ( SalPrinter, SalInfoPrinter + and some printer relevant methods of SalInstance and SalGraphicsData ) + + as aunderlying library the printer features of psprint are used. + + The query methods of a SalInfoPrinter are implemented by querying psprint + + The job methods of a SalPrinter are implemented by calling psprint + printer job functions. + */ + +#include <unistd.h> +#include <sys/wait.h> +#include <sys/stat.h> + +#include "rtl/ustring.hxx" + +#include "osl/module.h" + +#include "vcl/svapp.hxx" +#include "vcl/print.hxx" +#include "vcl/pdfwriter.hxx" +#include "vcl/printerinfomanager.hxx" + +#include <unx/salunx.h> +#include "unx/saldisp.hxx" +#include "unx/salinst.h" +#include "unx/salprn.h" +#include "unx/salframe.h" +#include "unx/pspgraphics.h" +#include "unx/saldata.hxx" + +#include "jobset.h" +#include "print.h" +#include "salptype.hxx" + +#include <com/sun/star/beans/PropertyValue.hpp> + +using namespace psp; +using namespace com::sun::star; + +using ::rtl::OUString; +using ::rtl::OUStringHash; +using ::rtl::OUStringToOString; + +/* + * static helpers + */ + +static oslModule driverLib = NULL; +extern "C" +{ +typedef int(*setupFunction)(PrinterInfo&); +static setupFunction pSetupFunction = NULL; +typedef int(*faxFunction)(String&); +static faxFunction pFaxNrFunction = NULL; +} + +static String getPdfDir( const PrinterInfo& rInfo ) +{ + String aDir; + sal_Int32 nIndex = 0; + while( nIndex != -1 ) + { + OUString aToken( rInfo.m_aFeatures.getToken( 0, ',', nIndex ) ); + if( ! aToken.compareToAscii( "pdf=", 4 ) ) + { + sal_Int32 nPos = 0; + aDir = aToken.getToken( 1, '=', nPos ); + if( ! aDir.Len() ) + aDir = String( ByteString( getenv( "HOME" ) ), osl_getThreadTextEncoding() ); + break; + } + } + return aDir; +} + +static void getPaLib() +{ + if( ! driverLib ) + { + OUString aLibName( RTL_CONSTASCII_USTRINGPARAM( _XSALSET_LIBNAME ) ); + driverLib = osl_loadModuleRelative( (oslGenericFunction)getPaLib, aLibName.pData, SAL_LOADMODULE_DEFAULT ); + if ( !driverLib ) + { + return; + } + + pSetupFunction = (setupFunction)osl_getAsciiFunctionSymbol( driverLib, "Sal_SetupPrinterDriver" ); + if ( !pSetupFunction ) + fprintf( stderr, "could not resolve Sal_SetupPrinterDriver\n" ); + + pFaxNrFunction = (faxFunction)osl_getAsciiFunctionSymbol( driverLib, "Sal_queryFaxNumber" ); + if ( !pFaxNrFunction ) + fprintf( stderr, "could not resolve Sal_queryFaxNumber\n" ); + } +} + +inline int PtTo10Mu( int nPoints ) { return (int)((((double)nPoints)*35.27777778)+0.5); } + +inline int TenMuToPt( int nUnits ) { return (int)((((double)nUnits)/35.27777778)+0.5); } + +static void copyJobDataToJobSetup( ImplJobSetup* pJobSetup, JobData& rData ) +{ + pJobSetup->meOrientation = (Orientation)(rData.m_eOrientation == orientation::Landscape ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT); + + // copy page size + String aPaper; + int width, height; + + rData.m_aContext.getPageSize( aPaper, width, height ); + pJobSetup->mePaperFormat = PaperInfo::fromPSName(OUStringToOString( aPaper, RTL_TEXTENCODING_ISO_8859_1 )); + + pJobSetup->mnPaperWidth = 0; + pJobSetup->mnPaperHeight = 0; + if( pJobSetup->mePaperFormat == PAPER_USER ) + { + // transform to 100dth mm + width = PtTo10Mu( width ); + height = PtTo10Mu( height ); + + if( rData.m_eOrientation == psp::orientation::Portrait ) + { + pJobSetup->mnPaperWidth = width; + pJobSetup->mnPaperHeight= height; + } + else + { + pJobSetup->mnPaperWidth = height; + pJobSetup->mnPaperHeight= width; + } + } + + // copy input slot + const PPDKey* pKey = NULL; + const PPDValue* pValue = NULL; + + pJobSetup->mnPaperBin = 0; + if( rData.m_pParser ) + pKey = rData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "InputSlot" ) ) ); + if( pKey ) + pValue = rData.m_aContext.getValue( pKey ); + if( pKey && pValue ) + { + for( pJobSetup->mnPaperBin = 0; + pValue != pKey->getValue( pJobSetup->mnPaperBin ) && + pJobSetup->mnPaperBin < pKey->countValues(); + pJobSetup->mnPaperBin++ ) + ; + if( pJobSetup->mnPaperBin >= pKey->countValues() ) + pJobSetup->mnPaperBin = 0; + } + + // copy duplex + pKey = NULL; + pValue = NULL; + + pJobSetup->meDuplexMode = DUPLEX_UNKNOWN; + if( rData.m_pParser ) + pKey = rData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Duplex" ) ) ); + if( pKey ) + pValue = rData.m_aContext.getValue( pKey ); + if( pKey && pValue ) + { + if( pValue->m_aOption.EqualsIgnoreCaseAscii( "None" ) || + pValue->m_aOption.EqualsIgnoreCaseAscii( "Simplex", 0, 7 ) + ) + { + pJobSetup->meDuplexMode = DUPLEX_OFF; + } + else if( pValue->m_aOption.EqualsIgnoreCaseAscii( "DuplexNoTumble" ) ) + { + pJobSetup->meDuplexMode = DUPLEX_LONGEDGE; + } + else if( pValue->m_aOption.EqualsIgnoreCaseAscii( "DuplexTumble" ) ) + { + pJobSetup->meDuplexMode = DUPLEX_SHORTEDGE; + } + } + + // copy the whole context + if( pJobSetup->mpDriverData ) + rtl_freeMemory( pJobSetup->mpDriverData ); + + int nBytes; + void* pBuffer = NULL; + if( rData.getStreamBuffer( pBuffer, nBytes ) ) + { + pJobSetup->mnDriverDataLen = nBytes; + pJobSetup->mpDriverData = (sal_uInt8*)pBuffer; + } + else + { + pJobSetup->mnDriverDataLen = 0; + pJobSetup->mpDriverData = NULL; + } +} + +static bool passFileToCommandLine( const String& rFilename, const String& rCommandLine, bool bRemoveFile = true ) +{ + bool bSuccess = false; + + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + ByteString aCmdLine( rCommandLine, aEncoding ); + ByteString aFilename( rFilename, aEncoding ); + + bool bPipe = aCmdLine.Search( "(TMP)" ) != STRING_NOTFOUND ? false : true; + + // setup command line for exec + if( ! bPipe ) + while( aCmdLine.SearchAndReplace( "(TMP)", aFilename ) != STRING_NOTFOUND ) + ; + +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "%s commandline: \"%s\"\n", + bPipe ? "piping to" : "executing", + aCmdLine.GetBuffer() ); + struct stat aStat; + if( stat( aFilename.GetBuffer(), &aStat ) ) + fprintf( stderr, "stat( %s ) failed\n", aFilename.GetBuffer() ); + fprintf( stderr, "Tmp file %s has modes: 0%03lo\n", aFilename.GetBuffer(), (long)aStat.st_mode ); +#endif + const char* argv[4]; + if( ! ( argv[ 0 ] = getenv( "SHELL" ) ) ) + argv[ 0 ] = "/bin/sh"; + argv[ 1 ] = "-c"; + argv[ 2 ] = aCmdLine.GetBuffer(); + argv[ 3 ] = 0; + + bool bHavePipes = false; + int pid, fd[2]; + + if( bPipe ) + bHavePipes = pipe( fd ) ? false : true; + if( ( pid = fork() ) > 0 ) + { + if( bPipe && bHavePipes ) + { + close( fd[0] ); + char aBuffer[ 2048 ]; + FILE* fp = fopen( aFilename.GetBuffer(), "r" ); + while( fp && ! feof( fp ) ) + { + int nBytes = fread( aBuffer, 1, sizeof( aBuffer ), fp ); + if( nBytes ) + write( fd[ 1 ], aBuffer, nBytes ); + } + fclose( fp ); + close( fd[ 1 ] ); + } + int status = 0; + waitpid( pid, &status, 0 ); + if( ! status ) + bSuccess = true; + } + else if( ! pid ) + { + if( bPipe && bHavePipes ) + { + close( fd[1] ); + if( fd[0] != STDIN_FILENO ) // not probable, but who knows :) + dup2( fd[0], STDIN_FILENO ); + } + execv( argv[0], const_cast<char**>(argv) ); + fprintf( stderr, "failed to execute \"%s\"\n", aCmdLine.GetBuffer() ); + _exit( 1 ); + } + else + fprintf( stderr, "failed to fork\n" ); + + // clean up the mess + if( bRemoveFile ) + unlink( aFilename.GetBuffer() ); + + return bSuccess; +} + +static bool sendAFax( const String& rFaxNumber, const String& rFileName, const String& rCommand ) +{ + std::list< OUString > aFaxNumbers; + + if( ! rFaxNumber.Len() ) + { + getPaLib(); + if( pFaxNrFunction ) + { + String aNewNr; + if( pFaxNrFunction( aNewNr ) ) + aFaxNumbers.push_back( OUString( aNewNr ) ); + } + } + else + { + sal_Int32 nIndex = 0; + OUString aFaxes( rFaxNumber ); + OUString aBeginToken( RTL_CONSTASCII_USTRINGPARAM("<Fax#>") ); + OUString aEndToken( RTL_CONSTASCII_USTRINGPARAM("</Fax#>") ); + while( nIndex != -1 ) + { + nIndex = aFaxes.indexOf( aBeginToken, nIndex ); + if( nIndex != -1 ) + { + sal_Int32 nBegin = nIndex + aBeginToken.getLength(); + nIndex = aFaxes.indexOf( aEndToken, nIndex ); + if( nIndex != -1 ) + { + aFaxNumbers.push_back( aFaxes.copy( nBegin, nIndex-nBegin ) ); + nIndex += aEndToken.getLength(); + } + } + } + } + + bool bSuccess = true; + if( aFaxNumbers.begin() != aFaxNumbers.end() ) + { + while( aFaxNumbers.begin() != aFaxNumbers.end() && bSuccess ) + { + String aCmdLine( rCommand ); + String aFaxNumber( aFaxNumbers.front() ); + aFaxNumbers.pop_front(); + while( aCmdLine.SearchAndReplace( String( RTL_CONSTASCII_USTRINGPARAM( "(PHONE)" ) ), aFaxNumber ) != STRING_NOTFOUND ) + ; +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "sending fax to \"%s\"\n", OUStringToOString( aFaxNumber, osl_getThreadTextEncoding() ).getStr() ); +#endif + bSuccess = passFileToCommandLine( rFileName, aCmdLine, false ); + } + } + else + bSuccess = false; + + // clean up temp file + unlink( ByteString( rFileName, osl_getThreadTextEncoding() ).GetBuffer() ); + + return bSuccess; +} + +static bool createPdf( const String& rToFile, const String& rFromFile, const String& rCommandLine ) +{ + String aCommandLine( rCommandLine ); + while( aCommandLine.SearchAndReplace( String( RTL_CONSTASCII_USTRINGPARAM( "(OUTFILE)" ) ), rToFile ) != STRING_NOTFOUND ) + ; + return passFileToCommandLine( rFromFile, aCommandLine ); +} + +/* + * SalInstance + */ + +// ----------------------------------------------------------------------- + +SalInfoPrinter* X11SalInstance::CreateInfoPrinter( SalPrinterQueueInfo* pQueueInfo, + ImplJobSetup* pJobSetup ) +{ + mbPrinterInit = true; + // create and initialize SalInfoPrinter + PspSalInfoPrinter* pPrinter = new PspSalInfoPrinter; + + if( pJobSetup ) + { + PrinterInfoManager& rManager( PrinterInfoManager::get() ); + PrinterInfo aInfo( rManager.getPrinterInfo( pQueueInfo->maPrinterName ) ); + pPrinter->m_aJobData = aInfo; + pPrinter->m_aPrinterGfx.Init( pPrinter->m_aJobData ); + + if( pJobSetup->mpDriverData ) + JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aInfo ); + + pJobSetup->mnSystem = JOBSETUP_SYSTEM_UNIX; + pJobSetup->maPrinterName = pQueueInfo->maPrinterName; + pJobSetup->maDriver = aInfo.m_aDriverName; + copyJobDataToJobSetup( pJobSetup, aInfo ); + + // set/clear backwards compatibility flag + bool bStrictSO52Compatibility = false; + boost::unordered_map<rtl::OUString, rtl::OUString, rtl::OUStringHash >::const_iterator compat_it = + pJobSetup->maValueMap.find( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "StrictSO52Compatibility" ) ) ); + + if( compat_it != pJobSetup->maValueMap.end() ) + { + if( compat_it->second.equalsIgnoreAsciiCaseAscii( "true" ) ) + bStrictSO52Compatibility = true; + } + pPrinter->m_aPrinterGfx.setStrictSO52Compatibility( bStrictSO52Compatibility ); + } + + + return pPrinter; +} + +// ----------------------------------------------------------------------- + +void X11SalInstance::DestroyInfoPrinter( SalInfoPrinter* pPrinter ) +{ + delete pPrinter; +} + +// ----------------------------------------------------------------------- + +SalPrinter* X11SalInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter ) +{ + mbPrinterInit = true; + // create and initialize SalPrinter + PspSalPrinter* pPrinter = new PspSalPrinter( pInfoPrinter ); + pPrinter->m_aJobData = static_cast<PspSalInfoPrinter*>(pInfoPrinter)->m_aJobData; + + return pPrinter; +} + +// ----------------------------------------------------------------------- + +void X11SalInstance::DestroyPrinter( SalPrinter* pPrinter ) +{ + delete pPrinter; +} + +// ----------------------------------------------------------------------- + +void X11SalInstance::GetPrinterQueueInfo( ImplPrnQueueList* pList ) +{ + mbPrinterInit = true; + PrinterInfoManager& rManager( PrinterInfoManager::get() ); + static const char* pNoSyncDetection = getenv( "SAL_DISABLE_SYNCHRONOUS_PRINTER_DETECTION" ); + if( ! pNoSyncDetection || ! *pNoSyncDetection ) + { + // #i62663# synchronize possible asynchronouse printer detection now + rManager.checkPrintersChanged( true ); + } + ::std::list< OUString > aPrinters; + rManager.listPrinters( aPrinters ); + + for( ::std::list< OUString >::iterator it = aPrinters.begin(); it != aPrinters.end(); ++it ) + { + const PrinterInfo& rInfo( rManager.getPrinterInfo( *it ) ); + // Neuen Eintrag anlegen + SalPrinterQueueInfo* pInfo = new SalPrinterQueueInfo; + pInfo->maPrinterName = *it; + pInfo->maDriver = rInfo.m_aDriverName; + pInfo->maLocation = rInfo.m_aLocation; + pInfo->maComment = rInfo.m_aComment; + pInfo->mpSysData = NULL; + + sal_Int32 nIndex = 0; + while( nIndex != -1 ) + { + String aToken( rInfo.m_aFeatures.getToken( 0, ',', nIndex ) ); + if( aToken.CompareToAscii( "pdf=", 4 ) == COMPARE_EQUAL ) + { + pInfo->maLocation = getPdfDir( rInfo ); + break; + } + } + + pList->Add( pInfo ); + } +} + +// ----------------------------------------------------------------------- + +void X11SalInstance::DeletePrinterQueueInfo( SalPrinterQueueInfo* pInfo ) +{ + delete pInfo; +} + +// ----------------------------------------------------------------------- + +void X11SalInstance::GetPrinterQueueState( SalPrinterQueueInfo* ) +{ + mbPrinterInit = true; +} + +// ----------------------------------------------------------------------- + +String X11SalInstance::GetDefaultPrinter() +{ + mbPrinterInit = true; + PrinterInfoManager& rManager( PrinterInfoManager::get() ); + return rManager.getDefaultPrinter(); +} + +// ======================================================================= + +PspSalInfoPrinter::PspSalInfoPrinter() +{ + m_pGraphics = NULL; + m_bPapersInit = false; +} + +// ----------------------------------------------------------------------- + +PspSalInfoPrinter::~PspSalInfoPrinter() +{ + if( m_pGraphics ) + { + delete m_pGraphics; + m_pGraphics = NULL; + } +} + +// ----------------------------------------------------------------------- + +void PspSalInfoPrinter::InitPaperFormats( const ImplJobSetup* ) +{ + m_aPaperFormats.clear(); + m_bPapersInit = true; + + if( m_aJobData.m_pParser ) + { + const PPDKey* pKey = m_aJobData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) ); + if( pKey ) + { + int nValues = pKey->countValues(); + for( int i = 0; i < nValues; i++ ) + { + const PPDValue* pValue = pKey->getValue( i ); + int nWidth = 0, nHeight = 0; + m_aJobData.m_pParser->getPaperDimension( pValue->m_aOption, nWidth, nHeight ); + PaperInfo aInfo(PtTo10Mu( nWidth ), PtTo10Mu( nHeight )); + m_aPaperFormats.push_back( aInfo ); + } + } + } +} + +// ----------------------------------------------------------------------- + +int PspSalInfoPrinter::GetLandscapeAngle( const ImplJobSetup* ) +{ + return 900; +} + +// ----------------------------------------------------------------------- + +SalGraphics* PspSalInfoPrinter::GetGraphics() +{ + // return a valid pointer only once + // the reasoning behind this is that we could have different + // SalGraphics that can run in multiple threads + // (future plans) + SalGraphics* pRet = NULL; + if( ! m_pGraphics ) + { + m_pGraphics = new PspGraphics( &m_aJobData, &m_aPrinterGfx, NULL, false, this ); + m_pGraphics->SetLayout( 0 ); + pRet = m_pGraphics; + } + return pRet; +} + +// ----------------------------------------------------------------------- + +void PspSalInfoPrinter::ReleaseGraphics( SalGraphics* pGraphics ) +{ + if( pGraphics == m_pGraphics ) + { + delete pGraphics; + m_pGraphics = NULL; + } + return; +} + +// ----------------------------------------------------------------------- + +sal_Bool PspSalInfoPrinter::Setup( SalFrame* pFrame, ImplJobSetup* pJobSetup ) +{ + if( ! pFrame || ! pJobSetup ) + return sal_False; + + getPaLib(); + + if( ! pSetupFunction ) + return sal_False; + + PrinterInfoManager& rManager = PrinterInfoManager::get(); + + PrinterInfo aInfo( rManager.getPrinterInfo( pJobSetup->maPrinterName ) ); + if ( pJobSetup->mpDriverData ) + { + SetData( ~0, pJobSetup ); + JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aInfo ); + } + + if( pSetupFunction( aInfo ) ) + { + rtl_freeMemory( pJobSetup->mpDriverData ); + pJobSetup->mpDriverData = NULL; + + int nBytes; + void* pBuffer = NULL; + aInfo.getStreamBuffer( pBuffer, nBytes ); + pJobSetup->mnDriverDataLen = nBytes; + pJobSetup->mpDriverData = (sal_uInt8*)pBuffer; + + // copy everything to job setup + copyJobDataToJobSetup( pJobSetup, aInfo ); + JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, m_aJobData ); + return sal_True; + } + return sal_False; +} + +// ----------------------------------------------------------------------- + +// This function gets the driver data and puts it into pJobSetup +// If pJobSetup->mpDriverData is NOT NULL, then the independend +// data should be merged into the driver data +// If pJobSetup->mpDriverData IS NULL, then the driver defaults +// should be merged into the independent data +sal_Bool PspSalInfoPrinter::SetPrinterData( ImplJobSetup* pJobSetup ) +{ + // set/clear backwards compatibility flag + bool bStrictSO52Compatibility = false; + boost::unordered_map<rtl::OUString, rtl::OUString, rtl::OUStringHash >::const_iterator compat_it = + pJobSetup->maValueMap.find( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "StrictSO52Compatibility" ) ) ); + + if( compat_it != pJobSetup->maValueMap.end() ) + { + if( compat_it->second.equalsIgnoreAsciiCaseAscii( "true" ) ) + bStrictSO52Compatibility = true; + } + m_aPrinterGfx.setStrictSO52Compatibility( bStrictSO52Compatibility ); + + if( pJobSetup->mpDriverData ) + return SetData( ~0, pJobSetup ); + + copyJobDataToJobSetup( pJobSetup, m_aJobData ); + + return sal_True; +} + +// ----------------------------------------------------------------------- + +// This function merges the independ driver data +// and sets the new independ data in pJobSetup +// Only the data must be changed, where the bit +// in nGetDataFlags is set +sal_Bool PspSalInfoPrinter::SetData( + sal_uLong nSetDataFlags, + ImplJobSetup* pJobSetup ) +{ + JobData aData; + JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData ); + + if( aData.m_pParser ) + { + const PPDKey* pKey; + const PPDValue* pValue; + + // merge papersize if necessary + if( nSetDataFlags & SAL_JOBSET_PAPERSIZE ) + { + String aPaper; + + if( pJobSetup->mePaperFormat == PAPER_USER ) + aPaper = aData.m_pParser->matchPaper( + TenMuToPt( pJobSetup->mnPaperWidth ), + TenMuToPt( pJobSetup->mnPaperHeight ) ); + else + aPaper = rtl::OStringToOUString(PaperInfo::toPSName(pJobSetup->mePaperFormat), RTL_TEXTENCODING_ISO_8859_1); + + pKey = aData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) ); + pValue = pKey ? pKey->getValueCaseInsensitive( aPaper ) : NULL; + + // some PPD files do not specify the standard paper names (e.g. C5 instead of EnvC5) + // try to find the correct paper anyway using the size + if( pKey && ! pValue && pJobSetup->mePaperFormat != PAPER_USER ) + { + PaperInfo aInfo( pJobSetup->mePaperFormat ); + aPaper = aData.m_pParser->matchPaper( + TenMuToPt( aInfo.getWidth() ), + TenMuToPt( aInfo.getHeight() ) ); + pValue = pKey->getValueCaseInsensitive( aPaper ); + } + + if( ! ( pKey && pValue && aData.m_aContext.setValue( pKey, pValue, false ) == pValue ) ) + return sal_False; + } + + // merge paperbin if necessary + if( nSetDataFlags & SAL_JOBSET_PAPERBIN ) + { + pKey = aData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "InputSlot" ) ) ); + if( pKey ) + { + int nPaperBin = pJobSetup->mnPaperBin; + if( nPaperBin >= pKey->countValues() ) + pValue = pKey->getDefaultValue(); + else + pValue = pKey->getValue( pJobSetup->mnPaperBin ); + + // may fail due to constraints; + // real paper bin is copied back to jobsetup in that case + aData.m_aContext.setValue( pKey, pValue ); + } + // if printer has no InputSlot key simply ignore this setting + // (e.g. SGENPRT has no InputSlot) + } + + // merge orientation if necessary + if( nSetDataFlags & SAL_JOBSET_ORIENTATION ) + aData.m_eOrientation = pJobSetup->meOrientation == ORIENTATION_LANDSCAPE ? orientation::Landscape : orientation::Portrait; + + // merge duplex if necessary + if( nSetDataFlags & SAL_JOBSET_DUPLEXMODE ) + { + pKey = aData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Duplex" ) ) ); + if( pKey ) + { + pValue = NULL; + switch( pJobSetup->meDuplexMode ) + { + case DUPLEX_OFF: + pValue = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "None" ) ) ); + if( pValue == NULL ) + pValue = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "SimplexNoTumble" ) ) ); + break; + case DUPLEX_SHORTEDGE: + pValue = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "DuplexTumble" ) ) ); + break; + case DUPLEX_LONGEDGE: + pValue = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "DuplexNoTumble" ) ) ); + break; + case DUPLEX_UNKNOWN: + default: + pValue = 0; + break; + } + if( ! pValue ) + pValue = pKey->getDefaultValue(); + aData.m_aContext.setValue( pKey, pValue ); + } + } + + m_aJobData = aData; + copyJobDataToJobSetup( pJobSetup, aData ); + return sal_True; + } + + return sal_False; +} + +// ----------------------------------------------------------------------- + +void PspSalInfoPrinter::GetPageInfo( + const ImplJobSetup* pJobSetup, + long& rOutWidth, long& rOutHeight, + long& rPageOffX, long& rPageOffY, + long& rPageWidth, long& rPageHeight ) +{ + if( ! pJobSetup ) + return; + + JobData aData; + JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData ); + + // get the selected page size + if( aData.m_pParser ) + { + + String aPaper; + int width, height; + int left = 0, top = 0, right = 0, bottom = 0; + int nDPI = aData.m_aContext.getRenderResolution(); + + + if( aData.m_eOrientation == psp::orientation::Portrait ) + { + aData.m_aContext.getPageSize( aPaper, width, height ); + aData.m_pParser->getMargins( aPaper, left, right, top, bottom ); + } + else + { + aData.m_aContext.getPageSize( aPaper, height, width ); + aData.m_pParser->getMargins( aPaper, top, bottom, right, left ); + } + + rPageWidth = width * nDPI / 72; + rPageHeight = height * nDPI / 72; + rPageOffX = left * nDPI / 72; + rPageOffY = top * nDPI / 72; + rOutWidth = ( width - left - right ) * nDPI / 72; + rOutHeight = ( height - top - bottom ) * nDPI / 72; + } +} + +// ----------------------------------------------------------------------- + +sal_uLong PspSalInfoPrinter::GetPaperBinCount( const ImplJobSetup* pJobSetup ) +{ + if( ! pJobSetup ) + return 0; + + JobData aData; + JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData ); + + const PPDKey* pKey = aData.m_pParser ? aData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "InputSlot" ) ) ): NULL; + return pKey ? pKey->countValues() : 0; +} + +// ----------------------------------------------------------------------- + +String PspSalInfoPrinter::GetPaperBinName( const ImplJobSetup* pJobSetup, sal_uLong nPaperBin ) +{ + JobData aData; + JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData ); + + String aRet; + if( aData.m_pParser ) + { + const PPDKey* pKey = aData.m_pParser ? aData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "InputSlot" ) ) ): NULL; + if( ! pKey || nPaperBin >= (sal_uLong)pKey->countValues() ) + aRet = aData.m_pParser->getDefaultInputSlot(); + else + { + const PPDValue* pValue = pKey->getValue( nPaperBin ); + if( pValue ) + aRet = aData.m_pParser->translateOption( pKey->getKey(), pValue->m_aOption ); + } + } + + return aRet; +} + +// ----------------------------------------------------------------------- + +sal_uLong PspSalInfoPrinter::GetCapabilities( const ImplJobSetup* pJobSetup, sal_uInt16 nType ) +{ + switch( nType ) + { + case PRINTER_CAPABILITIES_SUPPORTDIALOG: + return 1; + case PRINTER_CAPABILITIES_COPIES: + return 0xffff; + case PRINTER_CAPABILITIES_COLLATECOPIES: + { + // see if the PPD contains a value to set Collate to True + JobData aData; + JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData ); + + const PPDKey* pKey = aData.m_pParser ? aData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Collate" ) ) ) : NULL; + const PPDValue* pVal = pKey ? pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "True" ) ) ) : NULL; + + // PPDs don't mention the number of possible collated copies. + // so let's guess as many as we want ? + return pVal ? 0xffff : 0; + } + case PRINTER_CAPABILITIES_SETORIENTATION: + return 1; + case PRINTER_CAPABILITIES_SETDUPLEX: + return 1; + case PRINTER_CAPABILITIES_SETPAPERBIN: + return 1; + case PRINTER_CAPABILITIES_SETPAPERSIZE: + return 1; + case PRINTER_CAPABILITIES_SETPAPER: + return 0; + case PRINTER_CAPABILITIES_FAX: + return PrinterInfoManager::get().checkFeatureToken( pJobSetup->maPrinterName, "fax" ) ? 1 : 0; + case PRINTER_CAPABILITIES_PDF: + if( PrinterInfoManager::get().checkFeatureToken( pJobSetup->maPrinterName, "pdf" ) ) + return 1; + else + { + // see if the PPD contains a value to set Collate to True + JobData aData = PrinterInfoManager::get().getPrinterInfo( pJobSetup->maPrinterName ); + if( pJobSetup->mpDriverData ) + JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData ); + return aData.m_nPDFDevice > 0 ? 1 : 0; + } + case PRINTER_CAPABILITIES_EXTERNALDIALOG: + return PrinterInfoManager::get().checkFeatureToken( pJobSetup->maPrinterName, "external_dialog" ) ? 1 : 0; + case PRINTER_CAPABILITIES_USEPULLMODEL: + { + // see if the PPD contains a value to set Collate to True + JobData aData = PrinterInfoManager::get().getPrinterInfo( pJobSetup->maPrinterName ); + if( pJobSetup->mpDriverData ) + JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData ); + return aData.m_nPDFDevice > 0 ? 1 : 0; + } + default: break; + }; + return 0; +} + +// ======================================================================= + +/* + * SalPrinter + */ + + PspSalPrinter::PspSalPrinter( SalInfoPrinter* pInfoPrinter ) + : m_bFax( false ), + m_bPdf( false ), + m_bSwallowFaxNo( false ), + m_bIsPDFWriterJob( false ), + m_pGraphics( NULL ), + m_nCopies( 1 ), + m_bCollate( false ), + m_pInfoPrinter( pInfoPrinter ) +{ +} + +// ----------------------------------------------------------------------- + +PspSalPrinter::~PspSalPrinter() +{ +} + +// ----------------------------------------------------------------------- + +static String getTmpName() +{ + rtl::OUString aTmp, aSys; + osl_createTempFile( NULL, NULL, &aTmp.pData ); + osl_getSystemPathFromFileURL( aTmp.pData, &aSys.pData ); + + return aSys; +} + +sal_Bool PspSalPrinter::StartJob( + const XubString* pFileName, + const XubString& rJobName, + const XubString& rAppName, + sal_uLong nCopies, + bool bCollate, + bool bDirect, + ImplJobSetup* pJobSetup ) +{ + GetSalData()->m_pInstance->jobStartedPrinterUpdate(); + + m_bFax = false; + m_bPdf = false; + m_aFileName = pFileName ? *pFileName : String(); + m_aTmpFile = String(); + m_nCopies = nCopies; + m_bCollate = bCollate; + + JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, m_aJobData ); + if( m_nCopies > 1 ) + { + // in case user did not do anything (m_nCopies=1) + // take the default from jobsetup + m_aJobData.m_nCopies = m_nCopies; + m_aJobData.setCollate( bCollate ); + } + + // check wether this printer is configured as fax + int nMode = 0; + const PrinterInfo& rInfo( PrinterInfoManager::get().getPrinterInfo( m_aJobData.m_aPrinterName ) ); + sal_Int32 nIndex = 0; + while( nIndex != -1 ) + { + OUString aToken( rInfo.m_aFeatures.getToken( 0, ',', nIndex ) ); + if( ! aToken.compareToAscii( "fax", 3 ) ) + { + m_bFax = true; + m_aTmpFile = getTmpName(); + nMode = S_IRUSR | S_IWUSR; + + ::boost::unordered_map< ::rtl::OUString, ::rtl::OUString, ::rtl::OUStringHash >::const_iterator it; + it = pJobSetup->maValueMap.find( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("FAX#")) ); + if( it != pJobSetup->maValueMap.end() ) + m_aFaxNr = it->second; + + sal_Int32 nPos = 0; + m_bSwallowFaxNo = ! aToken.getToken( 1, '=', nPos ).compareToAscii( "swallow", 7 ) ? true : false; + + break; + } + if( ! aToken.compareToAscii( "pdf=", 4 ) ) + { + m_bPdf = true; + m_aTmpFile = getTmpName(); + nMode = S_IRUSR | S_IWUSR; + + if( ! m_aFileName.Len() ) + { + m_aFileName = getPdfDir( rInfo ); + m_aFileName.Append( '/' ); + m_aFileName.Append( rJobName ); + m_aFileName.AppendAscii( ".pdf" ); + } + break; + } + } + m_aPrinterGfx.Init( m_aJobData ); + + // set/clear backwards compatibility flag + bool bStrictSO52Compatibility = false; + boost::unordered_map<rtl::OUString, rtl::OUString, rtl::OUStringHash >::const_iterator compat_it = + pJobSetup->maValueMap.find( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "StrictSO52Compatibility" ) ) ); + + if( compat_it != pJobSetup->maValueMap.end() ) + { + if( compat_it->second.equalsIgnoreAsciiCaseAscii( "true" ) ) + bStrictSO52Compatibility = true; + } + m_aPrinterGfx.setStrictSO52Compatibility( bStrictSO52Compatibility ); + + return m_aPrintJob.StartJob( m_aTmpFile.Len() ? m_aTmpFile : m_aFileName, nMode, rJobName, rAppName, m_aJobData, &m_aPrinterGfx, bDirect ) ? sal_True : sal_False; +} + +// ----------------------------------------------------------------------- + +sal_Bool PspSalPrinter::EndJob() +{ + sal_Bool bSuccess = sal_False; + if( m_bIsPDFWriterJob ) + bSuccess = sal_True; + else + { + bSuccess = m_aPrintJob.EndJob(); + + if( bSuccess ) + { + // check for fax + if( m_bFax ) + { + + const PrinterInfo& rInfo( PrinterInfoManager::get().getPrinterInfo( m_aJobData.m_aPrinterName ) ); + // sendAFax removes the file after use + bSuccess = sendAFax( m_aFaxNr, m_aTmpFile, rInfo.m_aCommand ); + } + else if( m_bPdf ) + { + const PrinterInfo& rInfo( PrinterInfoManager::get().getPrinterInfo( m_aJobData.m_aPrinterName ) ); + bSuccess = createPdf( m_aFileName, m_aTmpFile, rInfo.m_aCommand ); + } + } + } + GetSalData()->m_pInstance->jobEndedPrinterUpdate(); + return bSuccess; +} + +// ----------------------------------------------------------------------- + +sal_Bool PspSalPrinter::AbortJob() +{ + sal_Bool bAbort = m_aPrintJob.AbortJob() ? sal_True : sal_False; + GetSalData()->m_pInstance->jobEndedPrinterUpdate(); + return bAbort; +} + +// ----------------------------------------------------------------------- + +SalGraphics* PspSalPrinter::StartPage( ImplJobSetup* pJobSetup, sal_Bool ) +{ + JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, m_aJobData ); + m_pGraphics = new PspGraphics( &m_aJobData, &m_aPrinterGfx, m_bFax ? &m_aFaxNr : NULL, m_bSwallowFaxNo, m_pInfoPrinter ); + m_pGraphics->SetLayout( 0 ); + if( m_nCopies > 1 ) + { + // in case user did not do anything (m_nCopies=1) + // take the default from jobsetup + m_aJobData.m_nCopies = m_nCopies; + m_aJobData.setCollate( m_nCopies > 1 && m_bCollate ); + } + + m_aPrintJob.StartPage( m_aJobData ); + m_aPrinterGfx.Init( m_aPrintJob ); + + return m_pGraphics; +} + +// ----------------------------------------------------------------------- + +sal_Bool PspSalPrinter::EndPage() +{ + sal_Bool bResult = m_aPrintJob.EndPage(); + m_aPrinterGfx.Clear(); + return bResult ? sal_True : sal_False; +} + +// ----------------------------------------------------------------------- + +sal_uLong PspSalPrinter::GetErrorCode() +{ + return 0; +} + +// ----------------------------------------------------------------------- + +struct PDFNewJobParameters +{ + Size maPageSize; + sal_uInt16 mnPaperBin; + + PDFNewJobParameters( const Size& i_rSize = Size(), + sal_uInt16 i_nPaperBin = 0xffff ) + : maPageSize( i_rSize ), mnPaperBin( i_nPaperBin ) {} + + bool operator!=(const PDFNewJobParameters& rComp ) const + { + Size aCompLSSize( rComp.maPageSize.Height(), rComp.maPageSize.Width() ); + return + (maPageSize != rComp.maPageSize && maPageSize != aCompLSSize) + || mnPaperBin != rComp.mnPaperBin + ; + } + + bool operator==(const PDFNewJobParameters& rComp) const + { + return ! this->operator!=(rComp); + } +}; + +struct PDFPrintFile +{ + rtl::OUString maTmpURL; + PDFNewJobParameters maParameters; + + PDFPrintFile( const rtl::OUString& i_rURL, const PDFNewJobParameters& i_rNewParameters ) + : maTmpURL( i_rURL ) + , maParameters( i_rNewParameters ) {} +}; + +sal_Bool PspSalPrinter::StartJob( const String* i_pFileName, const String& i_rJobName, const String& i_rAppName, + ImplJobSetup* i_pSetupData, vcl::PrinterController& i_rController ) +{ + OSL_TRACE( "StartJob with controller: pFilename = %s", i_pFileName ? rtl::OUStringToOString( *i_pFileName, RTL_TEXTENCODING_UTF8 ).getStr() : "<nil>" ); + // mark for endjob + m_bIsPDFWriterJob = true; + // reset IsLastPage + i_rController.setLastPage( sal_False ); + + // update job data + if( i_pSetupData ) + JobData::constructFromStreamBuffer( i_pSetupData->mpDriverData, i_pSetupData->mnDriverDataLen, m_aJobData ); + + OSL_ASSERT( m_aJobData.m_nPDFDevice > 0 ); + m_aJobData.m_nPDFDevice = 1; + + // possibly create one job for collated output + sal_Bool bSinglePrintJobs = sal_False; + beans::PropertyValue* pSingleValue = i_rController.getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PrintCollateAsSingleJobs" ) ) ); + if( pSingleValue ) + { + pSingleValue->Value >>= bSinglePrintJobs; + } + + int nCopies = i_rController.getPrinter()->GetCopyCount(); + bool bCollate = i_rController.getPrinter()->IsCollateCopy(); + + // notify start of real print job + i_rController.jobStarted(); + + // setup PDFWriter context + vcl::PDFWriter::PDFWriterContext aContext; + aContext.Version = vcl::PDFWriter::PDF_1_4; + aContext.Tagged = false; + aContext.EmbedStandardFonts = true; + aContext.DocumentLocale = Application::GetSettings().GetLocale(); + aContext.ColorMode = i_rController.getPrinter()->GetPrinterOptions().IsConvertToGreyscales() + ? vcl::PDFWriter::DrawGreyscale : vcl::PDFWriter::DrawColor; + + // prepare doc info + aContext.DocumentInfo.Title = i_rJobName; + aContext.DocumentInfo.Creator = i_rAppName; + aContext.DocumentInfo.Producer = i_rAppName; + + // define how we handle metafiles in PDFWriter + vcl::PDFWriter::PlayMetafileContext aMtfContext; + aMtfContext.m_bOnlyLosslessCompression = true; + + boost::shared_ptr<vcl::PDFWriter> pWriter; + std::vector< PDFPrintFile > aPDFFiles; + boost::shared_ptr<Printer> pPrinter( i_rController.getPrinter() ); + int nAllPages = i_rController.getFilteredPageCount(); + i_rController.createProgressDialog(); + bool bAborted = false; + PDFNewJobParameters aLastParm; + + aContext.DPIx = pPrinter->ImplGetDPIX(); + aContext.DPIy = pPrinter->ImplGetDPIY(); + for( int nPage = 0; nPage < nAllPages && ! bAborted; nPage++ ) + { + if( nPage == nAllPages-1 ) + i_rController.setLastPage( sal_True ); + + // get the page's metafile + GDIMetaFile aPageFile; + vcl::PrinterController::PageSize aPageSize = i_rController.getFilteredPageFile( nPage, aPageFile ); + if( i_rController.isProgressCanceled() ) + { + bAborted = true; + if( nPage != nAllPages-1 ) + { + i_rController.createProgressDialog(); + i_rController.setLastPage( sal_True ); + i_rController.getFilteredPageFile( nPage, aPageFile ); + } + } + else + { + pPrinter->SetMapMode( MapMode( MAP_100TH_MM ) ); + pPrinter->SetPaperSizeUser( aPageSize.aSize, true ); + PDFNewJobParameters aNewParm( pPrinter->GetPaperSize(), pPrinter->GetPaperBin() ); + + // create PDF writer on demand + // either on first page + // or on paper format change - cups does not support multiple paper formats per job (yet?) + // so we need to start a new job to get a new paper format from the printer + // orientation switches (that is switch of height and width) is handled transparently by CUPS + if( ! pWriter || + (aNewParm != aLastParm && ! i_pFileName ) ) + { + if( pWriter ) + { + pWriter->Emit(); + } + // produce PDF file + OUString aPDFUrl; + if( i_pFileName ) + aPDFUrl = *i_pFileName; + else + osl_createTempFile( NULL, NULL, &aPDFUrl.pData ); + // normalize to file URL + if( aPDFUrl.compareToAscii( "file:", 5 ) != 0 ) + { + // this is not a file URL, but it should + // form it into a osl friendly file URL + rtl::OUString aTmp; + osl_getFileURLFromSystemPath( aPDFUrl.pData, &aTmp.pData ); + aPDFUrl = aTmp; + } + // save current file and paper format + aLastParm = aNewParm; + aPDFFiles.push_back( PDFPrintFile( aPDFUrl, aNewParm ) ); + // update context + aContext.URL = aPDFUrl; + + // create and initialize PDFWriter + #if defined __SUNPRO_CC + #pragma disable_warn + #endif + pWriter.reset( new vcl::PDFWriter( aContext, uno::Reference< beans::XMaterialHolder >() ) ); + #if defined __SUNPRO_CC + #pragma enable_warn + #endif + } + + pWriter->NewPage( TenMuToPt( aNewParm.maPageSize.Width() ), + TenMuToPt( aNewParm.maPageSize.Height() ), + vcl::PDFWriter::Portrait ); + + pWriter->PlayMetafile( aPageFile, aMtfContext, NULL ); + } + } + + // emit the last file + if( pWriter ) + pWriter->Emit(); + + // handle collate, copy count and multiple jobs correctly + int nOuterJobs = 1; + if( bSinglePrintJobs ) + { + nOuterJobs = nCopies; + m_aJobData.m_nCopies = 1; + } + else + { + if( bCollate ) + { + if( aPDFFiles.size() == 1 && pPrinter->HasSupport( SUPPORT_COLLATECOPY ) ) + { + m_aJobData.setCollate( true ); + m_aJobData.m_nCopies = nCopies; + } + else + { + nOuterJobs = nCopies; + m_aJobData.m_nCopies = 1; + } + } + else + { + m_aJobData.setCollate( false ); + m_aJobData.m_nCopies = nCopies; + } + } + + // spool files + if( ! i_pFileName && ! bAborted ) + { + bool bFirstJob = true; + for( int nCurJob = 0; nCurJob < nOuterJobs; nCurJob++ ) + { + for( size_t i = 0; i < aPDFFiles.size(); i++ ) + { + oslFileHandle pFile = NULL; + osl_openFile( aPDFFiles[i].maTmpURL.pData, &pFile, osl_File_OpenFlag_Read ); + if( pFile ) + { + osl_setFilePos( pFile, osl_Pos_Absolut, 0 ); + std::vector< char > buffer( 0x10000, 0 ); + // update job data with current page size + Size aPageSize( aPDFFiles[i].maParameters.maPageSize ); + m_aJobData.setPaper( TenMuToPt( aPageSize.Width() ), TenMuToPt( aPageSize.Height() ) ); + // update job data with current paperbin + m_aJobData.setPaperBin( aPDFFiles[i].maParameters.mnPaperBin ); + + // spool current file + FILE* fp = PrinterInfoManager::get().startSpool( pPrinter->GetName(), i_rController.isDirectPrint() ); + if( fp ) + { + sal_uInt64 nBytesRead = 0; + do + { + osl_readFile( pFile, &buffer[0], buffer.size(), &nBytesRead ); + if( nBytesRead > 0 ) + fwrite( &buffer[0], 1, nBytesRead, fp ); + } while( nBytesRead == buffer.size() ); + rtl::OUStringBuffer aBuf( i_rJobName.Len() + 8 ); + aBuf.append( i_rJobName ); + if( i > 0 || nCurJob > 0 ) + { + aBuf.append( sal_Unicode(' ') ); + aBuf.append( sal_Int32( i + nCurJob * aPDFFiles.size() ) ); + } + PrinterInfoManager::get().endSpool( pPrinter->GetName(), aBuf.makeStringAndClear(), fp, m_aJobData, bFirstJob ); + bFirstJob = false; + } + } + osl_closeFile( pFile ); + } + } + } + + // job has been spooled + i_rController.setJobState( bAborted ? view::PrintableState_JOB_ABORTED : view::PrintableState_JOB_SPOOLED ); + + // clean up the temporary PDF files + if( ! i_pFileName || bAborted ) + { + for( size_t i = 0; i < aPDFFiles.size(); i++ ) + { + osl_removeFile( aPDFFiles[i].maTmpURL.pData ); + OSL_TRACE( "removed print PDF file %s\n", rtl::OUStringToOString( aPDFFiles[i].maTmpURL, RTL_TEXTENCODING_UTF8 ).getStr() ); + } + } + + return sal_True; +} + + +namespace x11 +{ + class PrinterUpdate + { + static Timer* pPrinterUpdateTimer; + static int nActiveJobs; + + static void doUpdate(); + DECL_STATIC_LINK( PrinterUpdate, UpdateTimerHdl, void* ); + public: + static void update(X11SalInstance &rInstance); + static void jobStarted() { nActiveJobs++; } + static void jobEnded(); + }; +} + +/* + * x11::PrinterUpdate + */ + +Timer* x11::PrinterUpdate::pPrinterUpdateTimer = NULL; +int x11::PrinterUpdate::nActiveJobs = 0; + +void x11::PrinterUpdate::doUpdate() +{ + ::psp::PrinterInfoManager& rManager( ::psp::PrinterInfoManager::get() ); + if( rManager.checkPrintersChanged( false ) ) + { + SalDisplay* pDisp = GetX11SalData()->GetDisplay(); + const std::list< SalFrame* >& rList = pDisp->getFrames(); + for( std::list< SalFrame* >::const_iterator it = rList.begin(); + it != rList.end(); ++it ) + pDisp->SendInternalEvent( *it, NULL, SALEVENT_PRINTERCHANGED ); + } +} + +// ----------------------------------------------------------------------- + +IMPL_STATIC_LINK_NOINSTANCE( x11::PrinterUpdate, UpdateTimerHdl, void*, EMPTYARG ) +{ + if( nActiveJobs < 1 ) + { + doUpdate(); + delete pPrinterUpdateTimer; + pPrinterUpdateTimer = NULL; + } + else + pPrinterUpdateTimer->Start(); + + return 0; +} + +// ----------------------------------------------------------------------- + +void x11::PrinterUpdate::update(X11SalInstance &rInstance) +{ + if( Application::GetSettings().GetMiscSettings().GetDisablePrinting() ) + return; + + if( ! rInstance.isPrinterInit() ) + { + // #i45389# start background printer detection + psp::PrinterInfoManager::get(); + return; + } + + if( nActiveJobs < 1 ) + doUpdate(); + else if( ! pPrinterUpdateTimer ) + { + pPrinterUpdateTimer = new Timer(); + pPrinterUpdateTimer->SetTimeout( 500 ); + pPrinterUpdateTimer->SetTimeoutHdl( STATIC_LINK( NULL, x11::PrinterUpdate, UpdateTimerHdl ) ); + pPrinterUpdateTimer->Start(); + } +} + +void X11SalInstance::updatePrinterUpdate() +{ + x11::PrinterUpdate::update(*this); +} + +void X11SalInstance::jobStartedPrinterUpdate() +{ + x11::PrinterUpdate::jobStarted(); +} + +// ----------------------------------------------------------------------- + +void x11::PrinterUpdate::jobEnded() +{ + nActiveJobs--; + if( nActiveJobs < 1 ) + { + if( pPrinterUpdateTimer ) + { + pPrinterUpdateTimer->Stop(); + delete pPrinterUpdateTimer; + pPrinterUpdateTimer = NULL; + doUpdate(); + } + } +} + +void X11SalInstance::jobEndedPrinterUpdate() +{ + x11::PrinterUpdate::jobEnded(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/gdi/salvd.cxx b/vcl/unx/generic/gdi/salvd.cxx new file mode 100644 index 000000000000..0bb34519bd8f --- /dev/null +++ b/vcl/unx/generic/gdi/salvd.cxx @@ -0,0 +1,276 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <vcl/sysdata.hxx> + +#include <tools/prex.h> +#include <X11/extensions/Xrender.h> +#include <tools/postx.h> + +#include <unx/salunx.h> +#include <unx/saldata.hxx> +#include <unx/saldisp.hxx> +#include <unx/salgdi.h> +#include <unx/salvd.h> + +#include <salinst.hxx> + +// -=-= SalInstance =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +SalVirtualDevice* X11SalInstance::CreateVirtualDevice( SalGraphics* pGraphics, + long nDX, long nDY, + sal_uInt16 nBitCount, const SystemGraphicsData *pData ) +{ + X11SalVirtualDevice *pVDev = new X11SalVirtualDevice(); + if( !nBitCount && pGraphics ) + nBitCount = pGraphics->GetBitCount(); + + if( pData && pData->hDrawable != None ) + { + XLIB_Window aRoot; + int x, y; + unsigned int w = 0, h = 0, bw, d; + Display* pDisp = GetX11SalData()->GetDisplay()->GetDisplay(); + XGetGeometry( pDisp, pData->hDrawable, + &aRoot, &x, &y, &w, &h, &bw, &d ); + int nScreen = 0; + while( nScreen < ScreenCount( pDisp ) ) + { + if( RootWindow( pDisp, nScreen ) == aRoot ) + break; + nScreen++; + } + nDX = (long)w; + nDY = (long)h; + if( !pVDev->Init( GetX11SalData()->GetDisplay(), nDX, nDY, nBitCount, nScreen, pData->hDrawable, + static_cast< XRenderPictFormat* >( pData->pXRenderFormat )) ) + { + delete pVDev; + return NULL; + } + } + else if( !pVDev->Init( GetX11SalData()->GetDisplay(), nDX, nDY, nBitCount, + pGraphics ? static_cast<X11SalGraphics*>(pGraphics)->GetScreenNumber() : + GetX11SalData()->GetDisplay()->GetDefaultScreenNumber() ) ) + { + delete pVDev; + return NULL; + } + + pVDev->InitGraphics( pVDev ); + return pVDev; +} + +void X11SalInstance::DestroyVirtualDevice( SalVirtualDevice* pDevice ) +{ + delete pDevice; +} + +// -=-= SalGraphicsData =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalGraphics::Init( X11SalVirtualDevice *pDevice, SalColormap* pColormap, bool bDeleteColormap ) +{ + SalColormap *pOrigDeleteColormap = m_pDeleteColormap; + + SalDisplay *pDisplay = pDevice->GetDisplay(); + m_nScreen = pDevice->GetScreenNumber(); + + int nVisualDepth = pDisplay->GetColormap( m_nScreen ).GetVisual().GetDepth(); + int nDeviceDepth = pDevice->GetDepth(); + + if( pColormap ) + { + m_pColormap = pColormap; + if( bDeleteColormap ) + m_pDeleteColormap = pColormap; + } + else + if( nDeviceDepth == nVisualDepth ) + m_pColormap = &pDisplay->GetColormap( m_nScreen ); + else + if( nDeviceDepth == 1 ) + m_pColormap = m_pDeleteColormap = new SalColormap(); + + if (m_pDeleteColormap != pOrigDeleteColormap) + delete pOrigDeleteColormap; + + const Drawable aVdevDrawable = pDevice->GetDrawable(); + SetDrawable( aVdevDrawable, m_nScreen ); + + m_pVDev = pDevice; + m_pFrame = NULL; + + bWindow_ = pDisplay->IsDisplay(); + bVirDev_ = sal_True; +} + +// -=-= SalVirDevData / SalVirtualDevice -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +sal_Bool X11SalVirtualDevice::Init( SalDisplay *pDisplay, + long nDX, long nDY, + sal_uInt16 nBitCount, + int nScreen, + Pixmap hDrawable, + XRenderPictFormat* pXRenderFormat ) +{ + SalColormap* pColormap = NULL; + bool bDeleteColormap = false; + + pDisplay_ = pDisplay; + pGraphics_ = new X11SalGraphics(); + m_nScreen = nScreen; + if( pXRenderFormat ) { + pGraphics_->SetXRenderFormat( pXRenderFormat ); + if( pXRenderFormat->colormap ) + pColormap = new SalColormap( pDisplay, pXRenderFormat->colormap, m_nScreen ); + else + pColormap = new SalColormap( nBitCount ); + bDeleteColormap = true; + } + else if( nBitCount != pDisplay->GetVisual( m_nScreen ).GetDepth() ) + { + pColormap = new SalColormap( nBitCount ); + bDeleteColormap = true; + } + pGraphics_->SetLayout( 0 ); // by default no! mirroring for VirtualDevices, can be enabled with EnableRTL() + nDX_ = nDX; + nDY_ = nDY; + nDepth_ = nBitCount; + + if( hDrawable == None ) + hDrawable_ = XCreatePixmap( GetXDisplay(), + pDisplay_->GetDrawable( m_nScreen ), + nDX_, nDY_, + GetDepth() ); + else + { + hDrawable_ = hDrawable; + bExternPixmap_ = sal_True; + } + + pGraphics_->Init( this, pColormap, bDeleteColormap ); + + return hDrawable_ != None ? sal_True : sal_False; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +X11SalVirtualDevice::X11SalVirtualDevice() +{ + pDisplay_ = (SalDisplay*)ILLEGAL_POINTER; + pGraphics_ = NULL; + hDrawable_ = None; + nDX_ = 0; + nDY_ = 0; + nDepth_ = 0; + bGraphics_ = sal_False; + bExternPixmap_ = sal_False; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +X11SalVirtualDevice::~X11SalVirtualDevice() +{ + if( pGraphics_ ) + delete pGraphics_; + pGraphics_ = NULL; + + if( GetDrawable() && !bExternPixmap_ ) + XFreePixmap( GetXDisplay(), GetDrawable() ); +} + +SalGraphics* X11SalVirtualDevice::GetGraphics() +{ + if( bGraphics_ ) + return NULL; + + if( pGraphics_ ) + bGraphics_ = sal_True; + + return pGraphics_; +} + +void X11SalVirtualDevice::ReleaseGraphics( SalGraphics* ) +{ bGraphics_ = sal_False; } + +sal_Bool X11SalVirtualDevice::SetSize( long nDX, long nDY ) +{ + if( bExternPixmap_ ) + return sal_False; + + // #144688# + // the X protocol request CreatePixmap puts an upper bound + // of 16 bit to the size. Beyond that there may be implementation + // limits of the Xserver; which we should catch by a failed XCreatePixmap + // call. However extra large values should be caught here since we'd run into + // 16 bit truncation here without noticing. + if( nDX < 0 || nDX > 65535 || + nDY < 0 || nDY > 65535 ) + return sal_False; + + if( !nDX ) nDX = 1; + if( !nDY ) nDY = 1; + + Pixmap h = XCreatePixmap( GetXDisplay(), + pDisplay_->GetDrawable( m_nScreen ), + nDX, nDY, nDepth_ ); + + if( !h ) + { + if( !GetDrawable() ) + { + hDrawable_ = XCreatePixmap( GetXDisplay(), + pDisplay_->GetDrawable( m_nScreen ), + 1, 1, nDepth_ ); + nDX_ = 1; + nDY_ = 1; + } + return sal_False; + } + + if( GetDrawable() ) + XFreePixmap( GetXDisplay(), GetDrawable() ); + hDrawable_ = h; + + nDX_ = nDX; + nDY_ = nDY; + + if( pGraphics_ ) + InitGraphics( this ); + + return sal_True; +} + +void X11SalVirtualDevice::GetSize( long& rWidth, long& rHeight ) +{ + rWidth = GetWidth(); + rHeight = GetHeight(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/gdi/xrender_peer.cxx b/vcl/unx/generic/gdi/xrender_peer.cxx new file mode 100644 index 000000000000..eda3254958f0 --- /dev/null +++ b/vcl/unx/generic/gdi/xrender_peer.cxx @@ -0,0 +1,246 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <stdio.h> + +#include <rtl/ustring.hxx> +#include <osl/module.h> + +#include <unx/salunx.h> +#include <unx/saldata.hxx> +#include <unx/saldisp.hxx> + +using ::rtl::OUString; +using ::rtl::OUStringToOString; +#include <xrender_peer.hxx> + +using namespace rtl; + +// --------------------------------------------------------------------------- + +XRenderPeer::XRenderPeer() +: mpDisplay( GetX11SalData()->GetDisplay()->GetDisplay() ), + mpStandardFormatA8( NULL ), + mnRenderVersion( 0 ), + mpRenderLib( NULL ) +#ifndef XRENDER_LINK +, mpXRenderCompositeTrapezoids( NULL ) +, mpXRenderAddTraps( NULL ) +#endif // XRENDER_LINK +{ + InitRenderLib(); +} + +// --------------------------------------------------------------------------- + +XRenderPeer::~XRenderPeer() +{ + osl_unloadModule( mpRenderLib ); +} + +// --------------------------------------------------------------------------- + +XRenderPeer& XRenderPeer::GetInstance() +{ + static XRenderPeer aPeer; + return aPeer; +} + +// --------------------------------------------------------------------------- + +void XRenderPeer::InitRenderLib() +{ + int nDummy; + if( !XQueryExtension( mpDisplay, "RENDER", &nDummy, &nDummy, &nDummy ) ) + return; + +#ifndef XRENDER_LINK + // we don't know if we are running on a system with xrender library + // we don't want to install system libraries ourselves + // => load them dynamically when they are there + const OUString aLibName( RTL_CONSTASCII_USTRINGPARAM( "libXrender.so.1" )); + mpRenderLib = osl_loadModule( aLibName.pData, SAL_LOADMODULE_DEFAULT ); + if( !mpRenderLib ) { +#ifdef DEBUG + fprintf( stderr, "Display can do XRender, but no %s installed.\n" + "Please install for improved display performance\n", OUStringToOString( aLibName.getStr(), + osl_getThreadTextEncoding() ).getStr() ); +#endif + return; + } + + oslGenericFunction pFunc; + pFunc = osl_getAsciiFunctionSymbol( mpRenderLib, "XRenderQueryExtension" ); + if( !pFunc ) return; + mpXRenderQueryExtension = (Bool(*)(Display*,int*,int*))pFunc; + + pFunc = osl_getAsciiFunctionSymbol( mpRenderLib, "XRenderQueryVersion" ); + if( !pFunc ) return; + mpXRenderQueryVersion = (void(*)(Display*,int*,int*))pFunc; + + pFunc = osl_getAsciiFunctionSymbol( mpRenderLib, "XRenderFindVisualFormat" ); + if( !pFunc ) return; + mpXRenderFindVisualFormat = (XRenderPictFormat*(*)(Display*,Visual*))pFunc; + + pFunc = osl_getAsciiFunctionSymbol( mpRenderLib, "XRenderFindStandardFormat" ); + if( !pFunc ) return; + mpXRenderFindStandardFormat = (XRenderPictFormat*(*)(Display*,int))pFunc; + + pFunc = osl_getAsciiFunctionSymbol( mpRenderLib, "XRenderFindFormat" ); + if( !pFunc ) return; + mpXRenderFindFormat = (XRenderPictFormat*(*)(Display*,unsigned long, + const XRenderPictFormat*,int))pFunc; + + pFunc = osl_getAsciiFunctionSymbol( mpRenderLib, "XRenderCreateGlyphSet" ); + if( !pFunc ) return; + mpXRenderCreateGlyphSet = (GlyphSet(*)(Display*,const XRenderPictFormat*))pFunc; + + pFunc = osl_getAsciiFunctionSymbol( mpRenderLib, "XRenderFreeGlyphSet" ); + if( !pFunc ) return; + mpXRenderFreeGlyphSet = (void(*)(Display*,GlyphSet))pFunc; + + pFunc = osl_getAsciiFunctionSymbol( mpRenderLib, "XRenderAddGlyphs" ); + if( !pFunc ) return; + mpXRenderAddGlyphs = (void(*)(Display*,GlyphSet,Glyph*,const XGlyphInfo*, + int,const char*,int))pFunc; + + pFunc = osl_getAsciiFunctionSymbol( mpRenderLib, "XRenderFreeGlyphs" ); + if( !pFunc ) return; + mpXRenderFreeGlyphs = (void(*)(Display*,GlyphSet,Glyph*,int))pFunc; + + pFunc = osl_getAsciiFunctionSymbol( mpRenderLib, "XRenderCompositeString32" ); + if( !pFunc ) return; + mpXRenderCompositeString32 = (void(*)(Display*,int,Picture,Picture, + const XRenderPictFormat*,GlyphSet,int,int,int,int,const unsigned*,int))pFunc; + + pFunc = osl_getAsciiFunctionSymbol( mpRenderLib, "XRenderCreatePicture" ); + if( !pFunc ) return; + mpXRenderCreatePicture = (Picture(*)(Display*,Drawable,const XRenderPictFormat*, + unsigned long,const XRenderPictureAttributes*))pFunc; + + pFunc = osl_getAsciiFunctionSymbol( mpRenderLib, "XRenderChangePicture" ); + if( !pFunc ) return; + mpXRenderChangePicture = (void(*)(Display*,Picture,unsigned long,const XRenderPictureAttributes*))pFunc; + + pFunc = osl_getAsciiFunctionSymbol( mpRenderLib, "XRenderSetPictureClipRegion" ); + if( !pFunc ) return; + mpXRenderSetPictureClipRegion = (void(*)(Display*,Picture,XLIB_Region))pFunc; + + pFunc = osl_getAsciiFunctionSymbol( mpRenderLib, "XRenderFreePicture" ); + if( !pFunc ) return; + mpXRenderFreePicture = (void(*)(Display*,Picture))pFunc; + + pFunc = osl_getAsciiFunctionSymbol( mpRenderLib, "XRenderComposite" ); + if( !pFunc ) return; + mpXRenderComposite = (void(*)(Display*,int,Picture,Picture,Picture, + int,int,int,int,int,int,unsigned,unsigned))pFunc; + + pFunc = osl_getAsciiFunctionSymbol( mpRenderLib, "XRenderFillRectangle" ); + if( !pFunc ) return; + mpXRenderFillRectangle = (void(*)(Display*,int,Picture,const XRenderColor*, + int,int,unsigned int,unsigned int))pFunc; + + pFunc = osl_getAsciiFunctionSymbol( mpRenderLib, "XRenderCompositeTrapezoids" ); + mpXRenderCompositeTrapezoids = (void(*)(Display*,int,Picture,Picture, + const XRenderPictFormat*,int,int,const XTrapezoid*,int))pFunc; + + pFunc = osl_getAsciiFunctionSymbol( mpRenderLib, "XRenderAddTraps" ); + mpXRenderAddTraps = (void(*)(Display*,Picture,int,int,const _XTrap*,int))pFunc; + +#endif // XRENDER_LINK + + // needed to initialize libXrender internals, we already know its there +#ifdef XRENDER_LINK + XRenderQueryExtension( mpDisplay, &nDummy, &nDummy ); +#else + (*mpXRenderQueryExtension)( mpDisplay, &nDummy, &nDummy ); +#endif + + int nMajor, nMinor; +#ifdef XRENDER_LINK + XRenderQueryVersion( mpDisplay, &nMajor, &nMinor ); +#else + (*mpXRenderQueryVersion)( mpDisplay, &nMajor, &nMinor ); +#endif + mnRenderVersion = 16*nMajor + nMinor; + + // the 8bit alpha mask format must be there + XRenderPictFormat aPictFormat={0,0,8,{0,0,0,0,0,0,0,0xFF},0}; + mpStandardFormatA8 = FindPictureFormat( PictFormatAlphaMask|PictFormatDepth, aPictFormat ); +} + +// --------------------------------------------------------------------------- + +// return mask of screens capable of XRENDER text +sal_uInt32 XRenderPeer::InitRenderText() +{ + if( mnRenderVersion < 0x01 ) + return 0; + + // #93033# disable XRENDER for old RENDER versions if XINERAMA is present + int nDummy; + if( XQueryExtension( mpDisplay, "XINERAMA", &nDummy, &nDummy, &nDummy ) ) + if( mnRenderVersion < 0x02 ) + return 0; + + if( !mpStandardFormatA8 ) + return 0; + + // and the visual must be supported too on at least one screen + sal_uInt32 nRetMask = 0; + SalDisplay* pSalDisp = GetX11SalData()->GetDisplay(); + const int nScreenCount = pSalDisp->GetScreenCount(); + XRenderPictFormat* pVisualFormat = NULL; + int nMaxDepth = 0; + for( int nScreen = 0; nScreen < nScreenCount; ++nScreen ) + { + Visual* pXVisual = pSalDisp->GetVisual( nScreen ).GetVisual(); + pVisualFormat = FindVisualFormat( pXVisual ); + if( pVisualFormat != NULL ) + { + int nVDepth = pSalDisp->GetVisual( nScreen ).GetDepth(); + if( nVDepth > nMaxDepth ) + nMaxDepth = nVDepth; + nRetMask |= 1U << nScreen; + } + } + + // #97763# disable XRENDER on <15bit displays for XFree<=4.2.0 + if( mnRenderVersion <= 0x02 ) + if( nMaxDepth < 15 ) + nRetMask = 0; + + return nRetMask; +} + +// --------------------------------------------------------------------------- + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/gdi/xrender_peer.hxx b/vcl/unx/generic/gdi/xrender_peer.hxx new file mode 100644 index 000000000000..f9417bb2be95 --- /dev/null +++ b/vcl/unx/generic/gdi/xrender_peer.hxx @@ -0,0 +1,380 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _SV_XRENDER_PEER_HXX +#define _SV_XRENDER_PEER_HXX + +#include <tools/prex.h> +struct _XTrap; // on some older systems this is not declared within Xrender.h +#include <X11/extensions/Xrender.h> +#include <tools/postx.h> + +#include <vcl/salgtype.hxx> +#include <osl/module.h> + +class XRenderPeer +{ +public: + static XRenderPeer& GetInstance(); + int GetVersion() const; + + sal_uInt32 InitRenderText(); + +protected: + XRenderPeer(); + ~XRenderPeer(); + void InitRenderLib(); + + Display* mpDisplay; + XRenderPictFormat* mpStandardFormatA8; + int mnRenderVersion; + oslModule mpRenderLib; + +public: + XRenderPictFormat* GetStandardFormatA8() const; + XRenderPictFormat* FindStandardFormat(int nFormat) const; + + // the methods below are thin wrappers for the XRENDER API + XRenderPictFormat* FindVisualFormat( Visual* ) const; + XRenderPictFormat* FindPictureFormat( unsigned long nMask, + const XRenderPictFormat& ) const; + Picture CreatePicture( Drawable, const XRenderPictFormat*, + unsigned long nDrawable, const XRenderPictureAttributes* ) const; + void ChangePicture( Picture, unsigned long nValueMask, + const XRenderPictureAttributes* ) const; + void SetPictureClipRegion( Picture, XLIB_Region ) const; + void CompositePicture( int nOp, Picture aSrc, Picture aMask, Picture aDst, + int nXSrc, int nYSrc, int nXMask, int nYMask, + int nXDst, int nYDst, unsigned nWidth, unsigned nHeight ) const; + void FreePicture( Picture ) const; + + GlyphSet CreateGlyphSet() const; + void FreeGlyphSet( GlyphSet ) const; + void AddGlyph( GlyphSet, Glyph nGlyphId, const XGlyphInfo&, + const char* pBuffer, int nBufSize ) const; + void FreeGlyph( GlyphSet, Glyph nGlyphId ) const; + void CompositeString32( Picture aSrc, Picture aDst, GlyphSet, + int nDstX, int nDstY, const unsigned* pText, int nTextLen ) const; + void FillRectangle( int nOp, Picture aDst, const XRenderColor*, + int nX, int nY, unsigned nW, unsigned nH ) const; + void CompositeTrapezoids( int nOp, Picture aSrc, Picture aDst, + const XRenderPictFormat*, int nXSrc, int nYSrc, + const XTrapezoid*, int nCount ) const; + bool AddTraps( Picture aDst, int nXOfs, int nYOfs, + const _XTrap*, int nCount ) const; + + bool AreTrapezoidsSupported() const +#ifdef XRENDER_LINK + { return true; } +#else + { return mpXRenderCompositeTrapezoids!=NULL; } + +private: + XRenderPictFormat* (*mpXRenderFindFormat)(Display*,unsigned long, + const XRenderPictFormat*,int); + XRenderPictFormat* (*mpXRenderFindVisualFormat)(Display*,Visual*); + XRenderPictFormat* (*mpXRenderFindStandardFormat)(Display*,int); + Bool (*mpXRenderQueryExtension)(Display*,int*,int*); + void (*mpXRenderQueryVersion)(Display*,int*,int*); + + Picture (*mpXRenderCreatePicture)(Display*,Drawable, const XRenderPictFormat*, + unsigned long,const XRenderPictureAttributes*); + void (*mpXRenderChangePicture)(Display*,Picture, + unsigned long,const XRenderPictureAttributes*); + void (*mpXRenderSetPictureClipRegion)(Display*,Picture,XLIB_Region); + void (*mpXRenderFreePicture)(Display*,Picture); + void (*mpXRenderComposite)(Display*,int,Picture,Picture,Picture, + int,int,int,int,int,int,unsigned,unsigned); + + GlyphSet (*mpXRenderCreateGlyphSet)(Display*, const XRenderPictFormat*); + void (*mpXRenderFreeGlyphSet)(Display*,GlyphSet); + void (*mpXRenderAddGlyphs)(Display*,GlyphSet,Glyph*, + const XGlyphInfo*,int,const char*,int); + void (*mpXRenderFreeGlyphs)(Display*,GlyphSet,Glyph*,int); + void (*mpXRenderCompositeString32)(Display*,int,Picture,Picture, + const XRenderPictFormat*,GlyphSet,int,int,int,int,const unsigned*,int); + void (*mpXRenderFillRectangle)(Display*,int,Picture, + const XRenderColor*,int,int,unsigned int,unsigned int); + void (*mpXRenderCompositeTrapezoids)(Display*,int,Picture,Picture, + const XRenderPictFormat*,int,int,const XTrapezoid*,int); + void (*mpXRenderAddTraps)(Display*,Picture,int,int,const _XTrap*,int); +#endif // XRENDER_LINK +}; + +//===================================================================== + +class ScopedPic +{ +public: + ScopedPic( XRenderPeer& rPeer, Picture& rPic ); + ~ScopedPic(); + Picture& Get(); + +private: + XRenderPeer& mrRenderPeer; + Picture maPicture; + +private: // prevent copy and assignmet + ScopedPic( const ScopedPic& ); + void operator=( const ScopedPic& ); +}; + +//===================================================================== + +inline int XRenderPeer::GetVersion() const +{ + return mnRenderVersion; +} + +inline XRenderPictFormat* XRenderPeer::GetStandardFormatA8() const +{ + return mpStandardFormatA8; +} + +inline XRenderPictFormat* XRenderPeer::FindStandardFormat(int nFormat) const +{ +#ifdef XRENDER_LINK + return XRenderFindStandardFormat(mpDisplay, nFormat); +#else + return (*mpXRenderFindStandardFormat)(mpDisplay, nFormat); +#endif +} + +inline XRenderPictFormat* XRenderPeer::FindVisualFormat( Visual* pVisual ) const +{ +#ifdef XRENDER_LINK + return XRenderFindVisualFormat ( mpDisplay, pVisual ); +#else + return (*mpXRenderFindVisualFormat)( mpDisplay, pVisual ); +#endif +} + +inline XRenderPictFormat* XRenderPeer::FindPictureFormat( unsigned long nFormatMask, + const XRenderPictFormat& rFormatAttr ) const +{ +#ifdef XRENDER_LINK + return XRenderFindFormat( mpDisplay, nFormatMask, &rFormatAttr, 0 ); +#else + return (*mpXRenderFindFormat)( mpDisplay, nFormatMask, &rFormatAttr, 0 ); +#endif +} + +inline Picture XRenderPeer::CreatePicture( Drawable aDrawable, + const XRenderPictFormat* pVisFormat, unsigned long nValueMask, + const XRenderPictureAttributes* pRenderAttr ) const +{ +#ifdef XRENDER_LINK + return XRenderCreatePicture( mpDisplay, aDrawable, pVisFormat, + nValueMask, pRenderAttr ); +#else + return (*mpXRenderCreatePicture)( mpDisplay, aDrawable, pVisFormat, + nValueMask, pRenderAttr ); +#endif +} + +inline void XRenderPeer::ChangePicture( Picture aPicture, + unsigned long nValueMask, const XRenderPictureAttributes* pRenderAttr ) const +{ +#ifdef XRENDER_LINK + XRenderChangePicture( mpDisplay, aPicture, nValueMask, pRenderAttr ); +#else + (*mpXRenderChangePicture)( mpDisplay, aPicture, nValueMask, pRenderAttr ); +#endif +} + +inline void XRenderPeer::SetPictureClipRegion( Picture aPicture, + XLIB_Region aXlibRegion ) const +{ +#ifdef XRENDER_LINK + XRenderSetPictureClipRegion( mpDisplay, aPicture, aXlibRegion ); +#else + (*mpXRenderSetPictureClipRegion)( mpDisplay, aPicture, aXlibRegion ); +#endif +} + +inline void XRenderPeer::CompositePicture( int nXRenderOp, + Picture aSrcPic, Picture aMaskPic, Picture aDstPic, + int nSrcX, int nSrcY, int nMaskX, int nMaskY, int nDstX, int nDstY, + unsigned nWidth, unsigned nHeight ) const +{ +#ifdef XRENDER_LINK + XRenderComposite( mpDisplay, nXRenderOp, aSrcPic, aMaskPic, aDstPic, + nSrcX, nSrcY, nMaskX, nMaskY, nDstX, nDstY, nWidth, nHeight ); +#else + (*mpXRenderComposite)( mpDisplay, nXRenderOp, aSrcPic, aMaskPic, aDstPic, + nSrcX, nSrcY, nMaskX, nMaskY, nDstX, nDstY, nWidth, nHeight ); +#endif +} + +inline void XRenderPeer::FreePicture( Picture aPicture ) const +{ +#ifdef XRENDER_LINK + XRenderFreePicture( mpDisplay, aPicture ); +#else + (*mpXRenderFreePicture)( mpDisplay, aPicture ); +#endif +} + +inline GlyphSet XRenderPeer::CreateGlyphSet() const +{ +#ifdef XRENDER_LINK + return XRenderCreateGlyphSet( mpDisplay, mpStandardFormatA8 ); +#else + return (*mpXRenderCreateGlyphSet)( mpDisplay, mpStandardFormatA8 ); +#endif +} + +inline void XRenderPeer::FreeGlyphSet( GlyphSet aGS ) const +{ +#ifdef XRENDER_LINK + XRenderFreeGlyphSet( mpDisplay, aGS ); +#else + (*mpXRenderFreeGlyphSet)( mpDisplay, aGS ); +#endif +} + +inline void XRenderPeer::AddGlyph( GlyphSet aGS, Glyph nGlyphId, + const XGlyphInfo& rGI, const char* pBuffer, int nBufSize ) const +{ +#ifdef XRENDER_LINK + XRenderAddGlyphs( mpDisplay, aGS, &nGlyphId, &rGI, 1, + const_cast<char*>(pBuffer), nBufSize ); +#else + (*mpXRenderAddGlyphs)( mpDisplay, aGS, &nGlyphId, &rGI, 1, + const_cast<char*>(pBuffer), nBufSize ); +#endif +} + +inline void XRenderPeer::FreeGlyph( GlyphSet aGS, Glyph nGlyphId ) const +{ + (void)aGS; (void)nGlyphId; + + // XRenderFreeGlyphs not implemented yet for version<=0.2 + // #108209# disabled because of crash potential, + // the glyph leak is not too bad because they will + // be cleaned up when the glyphset is released +} + +inline void XRenderPeer::CompositeString32( Picture aSrc, Picture aDst, + GlyphSet aGlyphSet, int nDstX, int nDstY, + const unsigned* pText, int nTextLen ) const +{ +#ifdef XRENDER_LINK + XRenderCompositeString32( mpDisplay, PictOpOver, aSrc, aDst, NULL, + aGlyphSet, 0, 0, nDstX, nDstY, pText, nTextLen ); +#else + (*mpXRenderCompositeString32)( mpDisplay, PictOpOver, aSrc, aDst, NULL, + aGlyphSet, 0, 0, nDstX, nDstY, pText, nTextLen ); +#endif +} + +inline void XRenderPeer::FillRectangle( int a, Picture b, const XRenderColor* c, + int d, int e, unsigned int f, unsigned int g) const +{ +#ifdef XRENDER_LINK + XRenderFillRectangle( mpDisplay, a, b, c, d, e, f, g ); +#else + (*mpXRenderFillRectangle)( mpDisplay, a, b, c, d, e, f, g ); +#endif +} + + +inline void XRenderPeer::CompositeTrapezoids( int nOp, + Picture aSrc, Picture aDst, const XRenderPictFormat* pXRPF, + int nXSrc, int nYSrc, const XTrapezoid* pXT, int nCount ) const +{ +#ifdef XRENDER_LINK + XRenderCompositeTrapezoids( mpDisplay, nOp, aSrc, aDst, pXRPF, + nXSrc, nYSrc, pXT, nCount ); +#else + (*mpXRenderCompositeTrapezoids)( mpDisplay, nOp, aSrc, aDst, pXRPF, + nXSrc, nYSrc, pXT, nCount ); +#endif +} + +inline bool XRenderPeer::AddTraps( Picture aDst, int nXOfs, int nYOfs, + const _XTrap* pTraps, int nCount ) const +{ +#ifdef XRENDER_LINK + XRenderAddTraps( mpDisplay, aDst, nXOfs, nYOfs, pTraps, nCount ); +#else + if( !mpXRenderAddTraps ) + return false; + (*mpXRenderAddTraps)( mpDisplay, aDst, nXOfs, nYOfs, pTraps, nCount ); +#endif + return true; +} + +//===================================================================== + +inline ScopedPic::ScopedPic( XRenderPeer& rPeer, Picture& rPic ) +: mrRenderPeer( rPeer) +, maPicture( rPic ) +{} + +inline ScopedPic::~ScopedPic() +{ + if( maPicture ) + mrRenderPeer.FreePicture( maPicture ); +} + +inline Picture& ScopedPic::Get() +{ + return maPicture; +} + +//===================================================================== + +inline XRenderColor GetXRenderColor( const SalColor& rSalColor, double fTransparency = 0.0 ) +{ + XRenderColor aRetVal; + // convert the SalColor + aRetVal.red = SALCOLOR_RED( rSalColor ); aRetVal.red |= (aRetVal.red << 8); + aRetVal.green = SALCOLOR_GREEN( rSalColor ); aRetVal.green |= (aRetVal.green << 8); + aRetVal.blue = SALCOLOR_BLUE( rSalColor ); aRetVal.blue |= (aRetVal.blue << 8); + + // handle transparency + aRetVal.alpha = 0xFFFF; // default to opaque + if( fTransparency != 0 ) + { + const double fAlpha = 1.0 - fTransparency; + aRetVal.alpha = static_cast<sal_uInt16>(fAlpha * 0xFFFF + 0.5); + // xrender wants pre-multiplied colors + aRetVal.red = static_cast<sal_uInt16>(fAlpha * aRetVal.red + 0.5); + aRetVal.green = static_cast<sal_uInt16>(fAlpha * aRetVal.green + 0.5); + aRetVal.blue = static_cast<sal_uInt16>(fAlpha * aRetVal.blue + 0.5); + } + + return aRetVal; +} + +//===================================================================== + +#endif // _SV_XRENDER_PEER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/plugadapt/salplug.cxx b/vcl/unx/generic/plugadapt/salplug.cxx new file mode 100644 index 000000000000..476ea135e570 --- /dev/null +++ b/vcl/unx/generic/plugadapt/salplug.cxx @@ -0,0 +1,309 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "osl/module.h" +#include "osl/process.h" + +#include "rtl/ustrbuf.hxx" + +#include "salinst.hxx" +#include "unx/saldata.hxx" +#include "vcl/printerinfomanager.hxx" + +#include <cstdio> +#include <unistd.h> + +using ::rtl::OUString; +using ::rtl::OUStringBuffer; +extern "C" { +typedef SalInstance*(*salFactoryProc)( oslModule pModule); +} + +static oslModule pCloseModule = NULL; + +enum { + DESKTOP_NONE = 0, + DESKTOP_UNKNOWN, + DESKTOP_GNOME, + DESKTOP_KDE, + DESKTOP_KDE4, + DESKTOP_CDE +}; + +static const char * desktop_strings[] = { "none", "unknown", "GNOME", "KDE", "KDE4", "CDE" }; + +static SalInstance* tryInstance( const OUString& rModuleBase ) +{ + SalInstance* pInst = NULL; + + OUStringBuffer aModName( 128 ); + aModName.appendAscii( SAL_DLLPREFIX"vclplug_" ); + aModName.append( rModuleBase ); + aModName.appendAscii( SAL_DLLPOSTFIX ); + OUString aModule = aModName.makeStringAndClear(); + + oslModule aMod = osl_loadModuleRelative( + reinterpret_cast< oslGenericFunction >( &tryInstance ), aModule.pData, + SAL_LOADMODULE_DEFAULT ); + if( aMod ) + { + salFactoryProc aProc = (salFactoryProc)osl_getAsciiFunctionSymbol( aMod, "create_SalInstance" ); + if( aProc ) + { + pInst = aProc( aMod ); +#if OSL_DEBUG_LEVEL > 1 + std::fprintf( stderr, "sal plugin %s produced instance %p\n", + OUStringToOString( aModule, RTL_TEXTENCODING_ASCII_US ).getStr(), + pInst ); +#endif + if( pInst ) + { + pCloseModule = aMod; + + /* + * Recent GTK+ versions load their modules with RTLD_LOCAL, so we can + * not access the 'gnome_accessibility_module_shutdown' anymore. + * So make sure libgtk+ & co are still mapped into memory when + * atk-bridge's atexit handler gets called. + */ + if( rModuleBase.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("gtk")) ) + { + pCloseModule = NULL; + } + /* + * #i109007# KDE3 seems to have the same problem; an atexit cleanup + * handler, which cannot be resolved anymore if the plugin is already unloaded. + */ + else if( rModuleBase.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("kde")) ) + { + pCloseModule = NULL; + } + + GetSalData()->m_pPlugin = aMod; + } + else + osl_unloadModule( aMod ); + } + else + { +#if OSL_DEBUG_LEVEL > 1 + std::fprintf( stderr, "could not load symbol %s from shared object %s\n", + "create_SalInstance", + OUStringToOString( aModule, RTL_TEXTENCODING_ASCII_US ).getStr() ); +#endif + osl_unloadModule( aMod ); + } + } +#if OSL_DEBUG_LEVEL > 1 + else + std::fprintf( stderr, "could not load shared object %s\n", + OUStringToOString( aModule, RTL_TEXTENCODING_ASCII_US ).getStr() ); +#endif + + return pInst; +} + +static const rtl::OUString& get_desktop_environment() +{ + static rtl::OUString aRet; + if( ! aRet.getLength() ) + { + OUStringBuffer aModName( 128 ); + aModName.appendAscii( SAL_DLLPREFIX"desktop_detector" ); + aModName.appendAscii( SAL_DLLPOSTFIX ); + aModName.appendAscii( SAL_DLLEXTENSION ); + OUString aModule = aModName.makeStringAndClear(); + + oslModule aMod = osl_loadModuleRelative( + reinterpret_cast< oslGenericFunction >( &tryInstance ), aModule.pData, + SAL_LOADMODULE_DEFAULT ); + if( aMod ) + { + rtl::OUString (*pSym)() = (rtl::OUString(*)()) + osl_getAsciiFunctionSymbol( aMod, "get_desktop_environment" ); + if( pSym ) + aRet = pSym(); + } + osl_unloadModule( aMod ); + } + return aRet; +} + +static SalInstance* autodetect_plugin() +{ + static const char* pKDEFallbackList[] = + { + "kde4", "kde", "gtk", "gen", 0 + }; + + static const char* pStandardFallbackList[] = + { + "gtk", "gen", 0 + }; + + static const char* pHeadlessFallbackList[] = + { + "svp", 0 + }; + + const rtl::OUString& desktop( get_desktop_environment() ); + const char ** pList = pStandardFallbackList; + int nListEntry = 0; + + // no server at all: dummy plugin + if ( desktop.equalsAscii( desktop_strings[DESKTOP_NONE] ) ) + pList = pHeadlessFallbackList; + else if ( desktop.equalsAscii( desktop_strings[DESKTOP_GNOME] ) ) + pList = pStandardFallbackList; + else if( desktop.equalsAscii( desktop_strings[DESKTOP_KDE] ) ) + { + pList = pKDEFallbackList; + nListEntry = 1; + } + else if( desktop.equalsAscii( desktop_strings[DESKTOP_KDE4] ) ) + pList = pKDEFallbackList; + + SalInstance* pInst = NULL; + while( pList[nListEntry] && pInst == NULL ) + { + rtl::OUString aTry( rtl::OUString::createFromAscii( pList[nListEntry] ) ); + pInst = tryInstance( aTry ); + #if OSL_DEBUG_LEVEL > 1 + if( pInst ) + std::fprintf( stderr, "plugin autodetection: %s\n", pList[nListEntry] ); + #endif + nListEntry++; + } + + return pInst; +} + +static SalInstance* check_headless_plugin() +{ + int nParams = osl_getCommandArgCount(); + OUString aParam; + for( int i = 0; i < nParams; i++ ) + { + osl_getCommandArg( i, &aParam.pData ); + if( aParam.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("-headless")) || + aParam.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("--headless")) ) + { + return tryInstance( OUString( RTL_CONSTASCII_USTRINGPARAM( "svp" ) ) ); + } + } + return NULL; +} + +SalInstance *CreateSalInstance() +{ + SalInstance* pInst = NULL; + + static const char* pUsePlugin = getenv( "SAL_USE_VCLPLUGIN" ); + + if( !(pUsePlugin && *pUsePlugin) ) + pInst = check_headless_plugin(); + else + pInst = tryInstance( OUString::createFromAscii( pUsePlugin ) ); + + if( ! pInst ) + pInst = autodetect_plugin(); + + // fallback, try everything + const char* pPlugin[] = { "gtk", "kde", "gen", 0 }; + + for ( int i = 0; !pInst && pPlugin[ i ]; ++i ) + pInst = tryInstance( OUString::createFromAscii( pPlugin[ i ] ) ); + + if( ! pInst ) + { + std::fprintf( stderr, "no suitable windowing system found, exiting.\n" ); + _exit( 1 ); + } + + // acquire SolarMutex + pInst->AcquireYieldMutex( 1 ); + + return pInst; +} + +void DestroySalInstance( SalInstance *pInst ) +{ + // release SolarMutex + pInst->ReleaseYieldMutex(); + + delete pInst; + if( pCloseModule ) + osl_unloadModule( pCloseModule ); +} + +void InitSalData() +{ +} + +void DeInitSalData() +{ +} + +void InitSalMain() +{ +} + +void DeInitSalMain() +{ +} + +void SalAbort( const XubString& rErrorText ) +{ + if( !rErrorText.Len() ) + std::fprintf( stderr, "Application Error" ); + else + std::fprintf( stderr, "%s", ByteString( rErrorText, gsl_getSystemTextEncoding() ).GetBuffer() ); + exit(-1); +} + +const OUString& SalGetDesktopEnvironment() +{ + return get_desktop_environment(); +} + +SalData::SalData() : + m_pInstance(NULL), + m_pPlugin(NULL), + m_pPIManager(NULL) +{ +} + +SalData::~SalData() +{ + psp::PrinterInfoManager::release(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/printer/cupsmgr.cxx b/vcl/unx/generic/printer/cupsmgr.cxx new file mode 100644 index 000000000000..a850ec14bb28 --- /dev/null +++ b/vcl/unx/generic/printer/cupsmgr.cxx @@ -0,0 +1,1181 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#ifdef ENABLE_CUPS +#include <cups/cups.h> +#include <cups/ppd.h> + +#else // !ENABLE_CUPS +typedef void ppd_file_t; +typedef void cups_dest_t; +typedef void cups_option_t; +#endif + +#include <unistd.h> + +#include "cupsmgr.hxx" + +#include "osl/thread.h" +#include "osl/diagnose.h" +#include "osl/conditn.hxx" + +#include "rtl/ustrbuf.hxx" + +#include <algorithm> +#include <setjmp.h> +#include <signal.h> + +#define CUPS_LIB_NAME "libcups.so.2" + +namespace psp +{ +class CUPSWrapper +{ + oslModule m_pLib; + osl::Mutex m_aGetPPDMutex; + bool m_bPPDThreadRunning; + + int (*m_pcupsPrintFile)(const char*, const char*, const char*, int, cups_option_t*); + int (*m_pcupsGetDests)(cups_dest_t**); + void (*m_pcupsSetDests)(int,cups_dest_t*); + void (*m_pcupsFreeDests)(int,cups_dest_t*); + const char* (*m_pcupsGetPPD)(const char*); + int (*m_pcupsMarkOptions)(ppd_file_t*,int,cups_option_t*); + int (*m_pcupsAddOption)(const char*,const char*,int,cups_option_t**); + void (*m_pcupsFreeOptions)(int,cups_option_t*); + ppd_file_t* (*m_pppdOpenFile)(const char* pFile); + void (*m_pppdClose)(ppd_file_t*); + const char* (*m_pcupsServer)(); + void (*m_pcupsSetPasswordCB)(const char*(cb)(const char*)); + const char* (*m_pcupsUser)(); + void (*m_pcupsSetUser)(const char*); + const char* (*m_pcupsGetOption)(const char*,int,cups_option_t*); + + oslGenericFunction loadSymbol( const char* ); +public: + CUPSWrapper(); + ~CUPSWrapper(); + + bool isValid(); + + int cupsGetDests(cups_dest_t** pDests) + { return m_pcupsGetDests(pDests); } + + void cupsSetDests( int nDests, cups_dest_t* pDests ) + { m_pcupsSetDests( nDests, pDests ); } + + void cupsFreeDests(int nDests, cups_dest_t* pDests) + { m_pcupsFreeDests(nDests, pDests); } + + int cupsPrintFile( const char* pPrinter, + const char* pFileName, + const char* pTitle, + int nOptions, + cups_option_t* pOptions ) + { return m_pcupsPrintFile( pPrinter, pFileName, pTitle, nOptions, pOptions ); } + + rtl::OString cupsGetPPD( const char* pPrinter ); + + int cupsMarkOptions(ppd_file_t* pPPD, int nOptions, cups_option_t* pOptions ) + { return m_pcupsMarkOptions(pPPD, nOptions, pOptions); } + + int cupsAddOption( const char* pName, const char* pValue, int nOptions, cups_option_t** pOptions ) + { return m_pcupsAddOption( pName, pValue, nOptions, pOptions ); } + + void cupsFreeOptions( int nOptions, cups_option_t* pOptions ) + { m_pcupsFreeOptions( nOptions, pOptions ); } + + ppd_file_t* ppdOpenFile( const char* pFileName ) + { return m_pppdOpenFile( pFileName ); } + + void ppdClose( ppd_file_t* pPPD ) + { m_pppdClose( pPPD ); } + + const char *cupsServer(void) + { return m_pcupsServer(); } + + const char *cupsUser(void) + { return m_pcupsUser(); } + + void cupsSetPasswordCB(const char *(*cb)(const char *)) + { m_pcupsSetPasswordCB( cb ); } + + void cupsSetUser(const char *user) + { m_pcupsSetUser( user ); } + + const char* cupsGetOption(const char* name, int num_options, cups_option_t* options) + { return m_pcupsGetOption( name, num_options, options ); } + +}; +} + +using namespace psp; +using namespace osl; + +using ::rtl::OUString; +using ::rtl::OUStringBuffer; +using ::rtl::OUStringToOString; +using ::rtl::OStringToOUString; +using ::rtl::OUStringHash; +using ::rtl::OString; + +/* + * CUPSWrapper class + */ + +oslGenericFunction CUPSWrapper::loadSymbol( const char* pSymbol ) +{ + OUString aSym( OUString::createFromAscii( pSymbol ) ); + oslGenericFunction pSym = osl_getFunctionSymbol( m_pLib, aSym.pData ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "%s %s\n", pSymbol, pSym ? "found" : "not found" ); +#endif + return pSym; +} + +CUPSWrapper::CUPSWrapper() + : m_pLib( NULL ), + m_bPPDThreadRunning( false ) +{ +#ifdef ENABLE_CUPS + OUString aLib( RTL_CONSTASCII_USTRINGPARAM( CUPS_LIB_NAME ) ); + m_pLib = osl_loadModule( aLib.pData, SAL_LOADMODULE_LAZY ); + if( ! m_pLib ) + { + aLib = OUString( RTL_CONSTASCII_USTRINGPARAM( SAL_MODULENAME( "cups" ) ) ); + m_pLib = osl_loadModule( aLib.pData, SAL_LOADMODULE_LAZY ); + } +#endif + + if( ! m_pLib ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "no cups library found\n" ); +#endif + return; + } + + m_pcupsPrintFile = (int(*)(const char*,const char*,const char*,int,cups_option_t*)) + loadSymbol( "cupsPrintFile" ); + m_pcupsGetDests = (int(*)(cups_dest_t**)) + loadSymbol( "cupsGetDests" ); + m_pcupsSetDests = (void(*)(int,cups_dest_t*)) + loadSymbol( "cupsSetDests" ); + m_pcupsFreeDests = (void(*)(int,cups_dest_t*)) + loadSymbol( "cupsFreeDests" ); + m_pcupsGetPPD = (const char*(*)(const char*)) + loadSymbol( "cupsGetPPD" ); + m_pcupsMarkOptions = (int(*)(ppd_file_t*,int,cups_option_t*)) + loadSymbol( "cupsMarkOptions" ); + m_pcupsAddOption = (int(*)(const char*,const char*,int,cups_option_t**)) + loadSymbol( "cupsAddOption" ); + m_pcupsFreeOptions = (void(*)(int,cups_option_t*)) + loadSymbol( "cupsFreeOptions" ); + m_pppdOpenFile = (ppd_file_t*(*)(const char*)) + loadSymbol( "ppdOpenFile" ); + m_pppdClose = (void(*)(ppd_file_t*)) + loadSymbol( "ppdClose" ); + m_pcupsServer = (const char*(*)()) + loadSymbol( "cupsServer" ); + m_pcupsUser = (const char*(*)()) + loadSymbol( "cupsUser" ); + m_pcupsSetPasswordCB = (void(*)(const char*(*)(const char*))) + loadSymbol( "cupsSetPasswordCB" ); + m_pcupsSetUser = (void(*)(const char*)) + loadSymbol( "cupsSetUser" ); + m_pcupsGetOption = (const char*(*)(const char*,int,cups_option_t*)) + loadSymbol( "cupsGetOption" ); + + if( ! ( + m_pcupsPrintFile && + m_pcupsGetDests && + m_pcupsSetDests && + m_pcupsFreeDests && + m_pcupsGetPPD && + m_pcupsMarkOptions && + m_pcupsAddOption && + m_pcupsServer && + m_pcupsUser && + m_pcupsSetPasswordCB && + m_pcupsSetUser && + m_pcupsFreeOptions && + m_pppdOpenFile && + m_pppdClose && + m_pcupsGetOption + ) ) + { + osl_unloadModule( m_pLib ); + m_pLib = NULL; + } +} + +CUPSWrapper::~CUPSWrapper() +{ + if( m_pLib ) + osl_unloadModule( m_pLib ); +} + +bool CUPSWrapper::isValid() +{ + return m_pLib != NULL; +} + +typedef const char*(*PPDFunction)(const char*); +struct GetPPDAttribs +{ + PPDFunction m_pFunction; + osl::Condition m_aCondition; + OString m_aParameter; + OString m_aResult; + oslThread m_aThread; + int m_nRefs; + bool* m_pResetRunning; + osl::Mutex* m_pSyncMutex; + + GetPPDAttribs( PPDFunction pFn, const char * m_pParameter, + bool* pResetRunning, osl::Mutex* pSyncMutex ) + : m_pFunction( pFn ), + m_aParameter( m_pParameter ), + m_pResetRunning( pResetRunning ), + m_pSyncMutex( pSyncMutex ) + { + m_nRefs = 2; + m_aCondition.reset(); + } + + ~GetPPDAttribs() + { + if( m_aResult.getLength() ) + unlink( m_aResult.getStr() ); + } + + void unref() + { + if( --m_nRefs == 0 ) + { + *m_pResetRunning = false; + delete this; + } + } + + void executeCall() + { + // This CUPS method is not at all thread-safe we need + // to dup the pointer to a static buffer it returns ASAP + OString aResult = m_pFunction( m_aParameter ); + MutexGuard aGuard( *m_pSyncMutex ); + m_aResult = aResult; + m_aCondition.set(); + unref(); + } + + OString waitResult( TimeValue *pDelay ) + { + m_pSyncMutex->release(); + + if (m_aCondition.wait( pDelay ) != Condition::result_ok + ) + { + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "cupsGetPPD %s timed out\n", + (const sal_Char *) m_aParameter + ); + #endif + } + m_pSyncMutex->acquire(); + + OString aRetval = m_aResult; + m_aResult = OString(); + unref(); + + return aRetval; + } +}; + +extern "C" { + static void getPPDWorker(void* pData) + { + GetPPDAttribs* pAttribs = (GetPPDAttribs*)pData; + pAttribs->executeCall(); + } +} + +OString CUPSWrapper::cupsGetPPD( const char* pPrinter ) +{ + OString aResult; + + m_aGetPPDMutex.acquire(); + // if one thread hangs in cupsGetPPD already, don't start another + if( ! m_bPPDThreadRunning ) + { + m_bPPDThreadRunning = true; + GetPPDAttribs* pAttribs = new GetPPDAttribs( m_pcupsGetPPD, + pPrinter, + &m_bPPDThreadRunning, + &m_aGetPPDMutex ); + + oslThread aThread = osl_createThread( getPPDWorker, pAttribs ); + + TimeValue aValue; + aValue.Seconds = 5; + aValue.Nanosec = 0; + + // NOTE: waitResult release and acquires the GetPPD mutex + aResult = pAttribs->waitResult( &aValue ); + osl_destroyThread( aThread ); + } + m_aGetPPDMutex.release(); + + return aResult; +} + +#ifdef ENABLE_CUPS +static const char* setPasswordCallback( const char* pIn ) +{ + const char* pRet = NULL; + + PrinterInfoManager& rMgr = PrinterInfoManager::get(); + if( rMgr.getType() == PrinterInfoManager::CUPS ) // sanity check + pRet = static_cast<CUPSManager&>(rMgr).authenticateUser( pIn ); + return pRet; +} +#endif + +/* + * CUPSManager class + */ + +CUPSManager* CUPSManager::tryLoadCUPS() +{ + CUPSManager* pManager = NULL; +#ifdef ENABLE_CUPS + static const char* pEnv = getenv( "SAL_DISABLE_CUPS" ); + + if( ! pEnv || ! *pEnv ) + { + // try to load CUPS + CUPSWrapper* pWrapper = new CUPSWrapper(); + if( pWrapper->isValid() ) + pManager = new CUPSManager( pWrapper ); + else + delete pWrapper; + } +#endif + return pManager; +} + +extern "C" +{ +static void run_dest_thread_stub( void* pThis ) +{ + CUPSManager::runDestThread( pThis ); +} +} + +CUPSManager::CUPSManager( CUPSWrapper* pWrapper ) : + PrinterInfoManager( CUPS ), + m_pCUPSWrapper( pWrapper ), + m_nDests( 0 ), + m_pDests( NULL ), + m_bNewDests( false ) +{ + m_aDestThread = osl_createThread( run_dest_thread_stub, this ); +} + +CUPSManager::~CUPSManager() +{ + if( m_aDestThread ) + { + // if the thread is still running here, then + // cupsGetDests is hung; terminate the thread instead of joining + osl_terminateThread( m_aDestThread ); + osl_destroyThread( m_aDestThread ); + } + + if( m_nDests && m_pDests ) + m_pCUPSWrapper->cupsFreeDests( m_nDests, (cups_dest_t*)m_pDests ); + delete m_pCUPSWrapper; +} + +void CUPSManager::runDestThread( void* pThis ) +{ + ((CUPSManager*)pThis)->runDests(); +} + +static sigjmp_buf aViolationBuffer; + +extern "C" +{ + static void lcl_signal_action(int nSignal) + { + fprintf( stderr, "Signal %d during fontconfig initialization called, ignoring fontconfig\n", nSignal ); + siglongjmp( aViolationBuffer, 1 ); + } +} + +void CUPSManager::runDests() +{ +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "starting cupsGetDests\n" ); +#endif + cups_dest_t* pDests = NULL; + + // #i86306# prepare against really broken CUPS installations / missing servers + + // install signal handler for SEGV, BUS and ABRT + struct sigaction act; + struct sigaction oact[3]; + + act.sa_handler = lcl_signal_action; + act.sa_flags = 0; + sigemptyset(&(act.sa_mask)); + + int nSegvSignalInstalled = sigaction(SIGSEGV, &act, &oact[0]); + int nBusSignalInstalled = sigaction(SIGBUS, &act, &oact[1]); + int nAbortSignalInstalled = sigaction(SIGABRT, &act, &oact[2]); + + // prepare against a signal during FcInit or FcConfigGetCurrent + if( sigsetjmp( aViolationBuffer, ~0 ) == 0 ) + { + int nDests = m_pCUPSWrapper->cupsGetDests( &pDests ); + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "came out of cupsGetDests\n" ); + #endif + + osl::MutexGuard aGuard( m_aCUPSMutex ); + m_nDests = nDests; + m_pDests = pDests; + m_bNewDests = true; + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "finished cupsGetDests\n" ); + #endif + } + else + { + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "cupsGetDests crashed, not using CUPS\n" ); + #endif + } + + // restore old signal handlers + if( nSegvSignalInstalled == 0 ) + sigaction( SIGSEGV, &oact[0], NULL ); + if( nBusSignalInstalled == 0 ) + sigaction( SIGBUS, &oact[1], NULL ); + if( nAbortSignalInstalled == 0 ) + sigaction( SIGABRT, &oact[2], NULL ); +} + +void CUPSManager::initialize() +{ + // get normal printers, clear printer list + PrinterInfoManager::initialize(); + +#ifdef ENABLE_CUPS + // check whether thread has completed + // if not behave like old printing system + osl::MutexGuard aGuard( m_aCUPSMutex ); + + if( ! m_bNewDests ) + return; + + // dest thread has run, clean up + if( m_aDestThread ) + { + osl_joinWithThread( m_aDestThread ); + osl_destroyThread( m_aDestThread ); + m_aDestThread = NULL; + } + m_bNewDests = false; + + // clear old stuff + m_aCUPSDestMap.clear(); + + if( ! (m_nDests && m_pDests ) ) + return; + + if( isCUPSDisabled() ) + return; + + // check for CUPS server(?) > 1.2 + // since there is no API to query, check for options that were + // introduced in dests with 1.2 + // this is needed to check for %%IncludeFeature support + // (#i65684#, #i65491#) + bool bUsePDF = false; + cups_dest_t* pDest = ((cups_dest_t*)m_pDests); + const char* pOpt = m_pCUPSWrapper->cupsGetOption( "printer-info", + pDest->num_options, + pDest->options ); + if( pOpt ) + { + m_bUseIncludeFeature = true; + bUsePDF = true; + if( m_aGlobalDefaults.m_nPSLevel == 0 && m_aGlobalDefaults.m_nPDFDevice == 0 ) + m_aGlobalDefaults.m_nPDFDevice = 1; + } + // do not send include JobPatch; CUPS will insert that itself + // TODO: currently unknwon which versions of CUPS insert JobPatches + // so currently it is assumed CUPS = don't insert JobPatch files + m_bUseJobPatch = false; + + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + int nPrinter = m_nDests; + + // reset global default PPD options; these are queried on demand from CUPS + m_aGlobalDefaults.m_pParser = NULL; + m_aGlobalDefaults.m_aContext = PPDContext(); + + // add CUPS printers, should there be a printer + // with the same name as a CUPS printer, overwrite it + while( nPrinter-- ) + { + pDest = ((cups_dest_t*)m_pDests)+nPrinter; + OUString aPrinterName = OStringToOUString( pDest->name, aEncoding ); + if( pDest->instance && *pDest->instance ) + { + OUStringBuffer aBuf( 256 ); + aBuf.append( aPrinterName ); + aBuf.append( sal_Unicode( '/' ) ); + aBuf.append( OStringToOUString( pDest->instance, aEncoding ) ); + aPrinterName = aBuf.makeStringAndClear(); + } + + // initialize printer with possible configuration from psprint.conf + bool bSetToGlobalDefaults = m_aPrinters.find( aPrinterName ) == m_aPrinters.end(); + Printer aPrinter = m_aPrinters[ aPrinterName ]; + if( bSetToGlobalDefaults ) + aPrinter.m_aInfo = m_aGlobalDefaults; + aPrinter.m_aInfo.m_aPrinterName = aPrinterName; + if( pDest->is_default ) + m_aDefaultPrinter = aPrinterName; + + for( int k = 0; k < pDest->num_options; k++ ) + { + if(!strcmp(pDest->options[k].name, "printer-info")) + aPrinter.m_aInfo.m_aComment=OStringToOUString(pDest->options[k].value, aEncoding); + if(!strcmp(pDest->options[k].name, "printer-location")) + aPrinter.m_aInfo.m_aLocation=OStringToOUString(pDest->options[k].value, aEncoding); + } + + + OUStringBuffer aBuf( 256 ); + aBuf.appendAscii( "CUPS:" ); + aBuf.append( aPrinterName ); + // note: the parser that goes with the PrinterInfo + // is created implicitly by the JobData::operator=() + // when it detects the NULL ptr m_pParser. + // if we wanted to fill in the parser here this + // would mean we'd have to download PPDs for each and + // every printer - which would be really bad runtime + // behaviour + aPrinter.m_aInfo.m_pParser = NULL; + aPrinter.m_aInfo.m_aContext.setParser( NULL ); + boost::unordered_map< OUString, PPDContext, OUStringHash >::const_iterator c_it = m_aDefaultContexts.find( aPrinterName ); + if( c_it != m_aDefaultContexts.end() ) + { + aPrinter.m_aInfo.m_pParser = c_it->second.getParser(); + aPrinter.m_aInfo.m_aContext = c_it->second; + } + if( bUsePDF && aPrinter.m_aInfo.m_nPSLevel == 0 && aPrinter.m_aInfo.m_nPDFDevice == 0 ) + aPrinter.m_aInfo.m_nPDFDevice = 1; + aPrinter.m_aInfo.m_aDriverName = aBuf.makeStringAndClear(); + aPrinter.m_bModified = false; + + m_aPrinters[ aPrinter.m_aInfo.m_aPrinterName ] = aPrinter; + m_aCUPSDestMap[ aPrinter.m_aInfo.m_aPrinterName ] = nPrinter; + } + + // remove everything that is not a CUPS printer and not + // a special purpose printer (PDF, Fax) + std::list< OUString > aRemovePrinters; + for( boost::unordered_map< OUString, Printer, OUStringHash >::iterator it = m_aPrinters.begin(); + it != m_aPrinters.end(); ++it ) + { + if( m_aCUPSDestMap.find( it->first ) != m_aCUPSDestMap.end() ) + continue; + + if( it->second.m_aInfo.m_aFeatures.getLength() > 0 ) + continue; + aRemovePrinters.push_back( it->first ); + } + while( aRemovePrinters.begin() != aRemovePrinters.end() ) + { + m_aPrinters.erase( aRemovePrinters.front() ); + aRemovePrinters.pop_front(); + } + + m_pCUPSWrapper->cupsSetPasswordCB( setPasswordCallback ); +#endif // ENABLE_CUPS +} + +#ifdef ENABLE_CUPS +static void updatePrinterContextInfo( ppd_group_t* pPPDGroup, PPDContext& rContext ) +{ + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + for( int i = 0; i < pPPDGroup->num_options; i++ ) + { + ppd_option_t* pOption = pPPDGroup->options + i; + for( int n = 0; n < pOption->num_choices; n++ ) + { + ppd_choice_t* pChoice = pOption->choices + n; + if( pChoice->marked ) + { + const PPDKey* pKey = rContext.getParser()->getKey( OStringToOUString( pOption->keyword, aEncoding ) ); + if( pKey ) + { + const PPDValue* pValue = pKey->getValue( OStringToOUString( pChoice->choice, aEncoding ) ); + if( pValue ) + { + if( pValue != pKey->getDefaultValue() ) + { + rContext.setValue( pKey, pValue, true ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "key %s is set to %s\n", pOption->keyword, pChoice->choice ); +#endif + + } +#if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "key %s is defaulted to %s\n", pOption->keyword, pChoice->choice ); +#endif + } +#if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "caution: value %s not found in key %s\n", pChoice->choice, pOption->keyword ); +#endif + } +#if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "caution: key %s not found in parser\n", pOption->keyword ); +#endif + } + } + } + + // recurse through subgroups + for( int g = 0; g < pPPDGroup->num_subgroups; g++ ) + { + updatePrinterContextInfo( pPPDGroup->subgroups + g, rContext ); + } +} +#endif // ENABLE_CUPS + +const PPDParser* CUPSManager::createCUPSParser( const OUString& rPrinter ) +{ + const PPDParser* pNewParser = NULL; + OUString aPrinter; + + if( rPrinter.compareToAscii( "CUPS:", 5 ) == 0 ) + aPrinter = rPrinter.copy( 5 ); + else + aPrinter = rPrinter; + +#ifdef ENABLE_CUPS + if( m_aCUPSMutex.tryToAcquire() ) + { + if( m_nDests && m_pDests && ! isCUPSDisabled() ) + { + boost::unordered_map< OUString, int, OUStringHash >::iterator dest_it = + m_aCUPSDestMap.find( aPrinter ); + if( dest_it != m_aCUPSDestMap.end() ) + { + cups_dest_t* pDest = ((cups_dest_t*)m_pDests) + dest_it->second; + OString aPPDFile = m_pCUPSWrapper->cupsGetPPD( pDest->name ); + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "PPD for %s is %s\n", OUStringToOString( aPrinter, osl_getThreadTextEncoding() ).getStr(), aPPDFile.getStr() ); + #endif + if( aPPDFile.getLength() ) + { + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + OUString aFileName( OStringToOUString( aPPDFile, aEncoding ) ); + // update the printer info with context information + ppd_file_t* pPPD = m_pCUPSWrapper->ppdOpenFile( aPPDFile.getStr() ); + if( pPPD ) + { + // create the new parser + PPDParser* pCUPSParser = new PPDParser( aFileName ); + pCUPSParser->m_aFile = rPrinter; + pNewParser = pCUPSParser; + + /*int nConflicts =*/ m_pCUPSWrapper->cupsMarkOptions( pPPD, pDest->num_options, pDest->options ); + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "processing the following options for printer %s (instance %s):\n", + pDest->name, pDest->instance ); + for( int k = 0; k < pDest->num_options; k++ ) + fprintf( stderr, " \"%s\" = \"%s\"\n", + pDest->options[k].name, + pDest->options[k].value ); + #endif + PrinterInfo& rInfo = m_aPrinters[ aPrinter ].m_aInfo; + + // remember the default context for later use + PPDContext& rContext = m_aDefaultContexts[ aPrinter ]; + rContext.setParser( pNewParser ); + // set system default paper; printer CUPS PPD options + // may overwrite it + setDefaultPaper( rContext ); + for( int i = 0; i < pPPD->num_groups; i++ ) + updatePrinterContextInfo( pPPD->groups + i, rContext ); + + rInfo.m_pParser = pNewParser; + rInfo.m_aContext = rContext; + + // clean up the mess + m_pCUPSWrapper->ppdClose( pPPD ); + } + #if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "ppdOpenFile failed, falling back to generic driver\n" ); + #endif + + // remove temporary PPD file + unlink( aPPDFile.getStr() ); + } + #if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "cupsGetPPD failed, falling back to generic driver\n" ); + #endif + } + #if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "no dest found for printer %s\n", OUStringToOString( aPrinter, osl_getThreadTextEncoding() ).getStr() ); + #endif + } + m_aCUPSMutex.release(); + } + #if OSL_DEBUG_LEVEL >1 + else + fprintf( stderr, "could not acquire CUPS mutex !!!\n" ); + #endif + #endif // ENABLE_CUPS + + if( ! pNewParser ) + { + // get the default PPD + pNewParser = PPDParser::getParser( String( RTL_CONSTASCII_USTRINGPARAM( "SGENPRT" ) ) ); + + PrinterInfo& rInfo = m_aPrinters[ aPrinter ].m_aInfo; + + rInfo.m_pParser = pNewParser; + rInfo.m_aContext.setParser( pNewParser ); + } + + return pNewParser; +} + +void CUPSManager::setupJobContextData( + JobData& +#ifdef ENABLE_CUPS + rData +#endif +) +{ +#ifdef ENABLE_CUPS + boost::unordered_map< OUString, int, OUStringHash >::iterator dest_it = + m_aCUPSDestMap.find( rData.m_aPrinterName ); + + if( dest_it == m_aCUPSDestMap.end() ) + return PrinterInfoManager::setupJobContextData( rData ); + + boost::unordered_map< OUString, Printer, OUStringHash >::iterator p_it = + m_aPrinters.find( rData.m_aPrinterName ); + if( p_it == m_aPrinters.end() ) // huh ? + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "CUPS printer list in disorder, no dest for printer %s !\n", OUStringToOString( rData.m_aPrinterName, osl_getThreadTextEncoding() ).getStr() ); +#endif + return; + } + + if( p_it->second.m_aInfo.m_pParser == NULL ) + { + // in turn calls createCUPSParser + // which updates the printer info + p_it->second.m_aInfo.m_pParser = PPDParser::getParser( p_it->second.m_aInfo.m_aDriverName ); + } + if( p_it->second.m_aInfo.m_aContext.getParser() == NULL ) + { + OUString aPrinter; + if( p_it->second.m_aInfo.m_aDriverName.compareToAscii( "CUPS:", 5 ) == 0 ) + aPrinter = p_it->second.m_aInfo.m_aDriverName.copy( 5 ); + else + aPrinter = p_it->second.m_aInfo.m_aDriverName; + + p_it->second.m_aInfo.m_aContext = m_aDefaultContexts[ aPrinter ]; + } + + rData.m_pParser = p_it->second.m_aInfo.m_pParser; + rData.m_aContext = p_it->second.m_aInfo.m_aContext; +#endif +} + +FILE* CUPSManager::startSpool( const OUString& rPrintername, bool bQuickCommand ) +{ + OSL_TRACE( "endSpool: %s, %s", + rtl::OUStringToOString( rPrintername, RTL_TEXTENCODING_UTF8 ).getStr(), + bQuickCommand ? "true" : "false" ); + + if( m_aCUPSDestMap.find( rPrintername ) == m_aCUPSDestMap.end() ) + { + OSL_TRACE( "defer to PrinterInfoManager::startSpool" ); + return PrinterInfoManager::startSpool( rPrintername, bQuickCommand ); + } + +#ifdef ENABLE_CUPS + OUString aTmpURL, aTmpFile; + osl_createTempFile( NULL, NULL, &aTmpURL.pData ); + osl_getSystemPathFromFileURL( aTmpURL.pData, &aTmpFile.pData ); + OString aSysFile = OUStringToOString( aTmpFile, osl_getThreadTextEncoding() ); + FILE* fp = fopen( aSysFile.getStr(), "w" ); + if( fp ) + m_aSpoolFiles[fp] = aSysFile; + + return fp; +#else + return NULL; +#endif +} + +struct less_ppd_key : public ::std::binary_function<double, double, bool> +{ + bool operator()(const PPDKey* left, const PPDKey* right) + { return left->getOrderDependency() < right->getOrderDependency(); } +}; + +void CUPSManager::getOptionsFromDocumentSetup( const JobData& rJob, bool bBanner, int& rNumOptions, void** rOptions ) const +{ + rNumOptions = 0; + *rOptions = NULL; + + // emit features ordered to OrderDependency + // ignore features that are set to default + + // sanity check + if( rJob.m_pParser == rJob.m_aContext.getParser() && rJob.m_pParser ) + { + int i; + int nKeys = rJob.m_aContext.countValuesModified(); + ::std::vector< const PPDKey* > aKeys( nKeys ); + for( i = 0; i < nKeys; i++ ) + aKeys[i] = rJob.m_aContext.getModifiedKey( i ); + ::std::sort( aKeys.begin(), aKeys.end(), less_ppd_key() ); + + for( i = 0; i < nKeys; i++ ) + { + const PPDKey* pKey = aKeys[i]; + const PPDValue* pValue = rJob.m_aContext.getValue( pKey ); + if(pValue && pValue->m_eType == eInvocation && pValue->m_aValue.Len() ) + { + OString aKey = OUStringToOString( pKey->getKey(), RTL_TEXTENCODING_ASCII_US ); + OString aValue = OUStringToOString( pValue->m_aOption, RTL_TEXTENCODING_ASCII_US ); + rNumOptions = m_pCUPSWrapper->cupsAddOption( aKey.getStr(), aValue.getStr(), rNumOptions, (cups_option_t**)rOptions ); + } + } + } + + if( rJob.m_nPDFDevice > 0 && rJob.m_nCopies > 1 ) + { + rtl::OString aVal( rtl::OString::valueOf( sal_Int32( rJob.m_nCopies ) ) ); + rNumOptions = m_pCUPSWrapper->cupsAddOption( "copies", aVal.getStr(), rNumOptions, (cups_option_t**)rOptions ); + } + if( ! bBanner ) + { + rNumOptions = m_pCUPSWrapper->cupsAddOption( "job-sheets", "none", rNumOptions, (cups_option_t**)rOptions ); + } +} + +int CUPSManager::endSpool( const OUString& rPrintername, const OUString& rJobTitle, FILE* pFile, const JobData& rDocumentJobData, bool bBanner ) +{ + OSL_TRACE( "endSpool: %s, %s, copy count = %d", + rtl::OUStringToOString( rPrintername, RTL_TEXTENCODING_UTF8 ).getStr(), + rtl::OUStringToOString( rJobTitle, RTL_TEXTENCODING_UTF8 ).getStr(), + rDocumentJobData.m_nCopies + ); + + int nJobID = 0; + + osl::MutexGuard aGuard( m_aCUPSMutex ); + + boost::unordered_map< OUString, int, OUStringHash >::iterator dest_it = + m_aCUPSDestMap.find( rPrintername ); + if( dest_it == m_aCUPSDestMap.end() ) + { + OSL_TRACE( "defer to PrinterInfoManager::endSpool" ); + return PrinterInfoManager::endSpool( rPrintername, rJobTitle, pFile, rDocumentJobData, bBanner ); + } + + #ifdef ENABLE_CUPS + boost::unordered_map< FILE*, OString, FPtrHash >::const_iterator it = m_aSpoolFiles.find( pFile ); + if( it != m_aSpoolFiles.end() ) + { + fclose( pFile ); + rtl_TextEncoding aEnc = osl_getThreadTextEncoding(); + + // setup cups options + int nNumOptions = 0; + cups_option_t* pOptions = NULL; + getOptionsFromDocumentSetup( rDocumentJobData, bBanner, nNumOptions, (void**)&pOptions ); + + cups_dest_t* pDest = ((cups_dest_t*)m_pDests) + dest_it->second; + nJobID = m_pCUPSWrapper->cupsPrintFile( pDest->name, + it->second.getStr(), + OUStringToOString( rJobTitle, aEnc ).getStr(), + nNumOptions, pOptions ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "cupsPrintFile( %s, %s, %s, %d, %p ) returns %d\n", + pDest->name, + it->second.getStr(), + OUStringToOString( rJobTitle, aEnc ).getStr(), + nNumOptions, + pOptions, + nJobID + ); + for( int n = 0; n < nNumOptions; n++ ) + fprintf( stderr, " option %s=%s\n", pOptions[n].name, pOptions[n].value ); + OString aCmd( "cp " ); + aCmd = aCmd + it->second; + aCmd = aCmd + OString( " $HOME/cupsprint.ps" ); + system( aCmd.getStr() ); +#endif + + unlink( it->second.getStr() ); + m_aSpoolFiles.erase( pFile ); + if( pOptions ) + m_pCUPSWrapper->cupsFreeOptions( nNumOptions, pOptions ); + } +#endif // ENABLE_CUPS + + return nJobID; +} + + +void CUPSManager::changePrinterInfo( const OUString& rPrinter, const PrinterInfo& rNewInfo ) +{ + PrinterInfoManager::changePrinterInfo( rPrinter, rNewInfo ); +} + +bool CUPSManager::checkPrintersChanged( bool bWait ) +{ + bool bChanged = false; + if( bWait ) + { + if( m_aDestThread ) + { + // initial asynchronous detection still running + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "syncing cups discovery thread\n" ); + #endif + osl_joinWithThread( m_aDestThread ); + osl_destroyThread( m_aDestThread ); + m_aDestThread = NULL; + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "done: syncing cups discovery thread\n" ); + #endif + } + else + { + // #i82321# check for cups printer updates + // with this change the whole asynchronous detection in a thread is + // almost useless. The only relevance left is for some stalled systems + // where the user can set SAL_DISABLE_SYNCHRONOUS_PRINTER_DETECTION + // (see vcl/unx/source/gdi/salprnpsp.cxx) + // so that checkPrintersChanged( true ) will never be called + + // there is no way to query CUPS whether the printer list has changed + // so get the dest list anew + if( m_nDests && m_pDests ) + m_pCUPSWrapper->cupsFreeDests( m_nDests, (cups_dest_t*)m_pDests ); + m_nDests = 0; + m_pDests = NULL; + runDests(); + } + } + if( m_aCUPSMutex.tryToAcquire() ) + { + bChanged = m_bNewDests; + m_aCUPSMutex.release(); + } + + if( ! bChanged ) + { + bChanged = PrinterInfoManager::checkPrintersChanged( bWait ); + // #i54375# ensure new merging with CUPS list in :initialize + if( bChanged ) + m_bNewDests = true; + } + + if( bChanged ) + initialize(); + + return bChanged; +} + +bool CUPSManager::addPrinter( const OUString& rName, const OUString& rDriver ) +{ + // don't touch the CUPS printers + if( m_aCUPSDestMap.find( rName ) != m_aCUPSDestMap.end() || + rDriver.compareToAscii( "CUPS:", 5 ) == 0 + ) + return false; + return PrinterInfoManager::addPrinter( rName, rDriver ); +} + +bool CUPSManager::removePrinter( const OUString& rName, bool bCheck ) +{ + // don't touch the CUPS printers + if( m_aCUPSDestMap.find( rName ) != m_aCUPSDestMap.end() ) + return false; + return PrinterInfoManager::removePrinter( rName, bCheck ); +} + +bool CUPSManager::setDefaultPrinter( const OUString& rName ) +{ + bool bSuccess = false; +#ifdef ENABLE_CUPS + boost::unordered_map< OUString, int, OUStringHash >::iterator nit = + m_aCUPSDestMap.find( rName ); + if( nit != m_aCUPSDestMap.end() && m_aCUPSMutex.tryToAcquire() ) + { + cups_dest_t* pDests = (cups_dest_t*)m_pDests; + for( int i = 0; i < m_nDests; i++ ) + pDests[i].is_default = 0; + pDests[ nit->second ].is_default = 1; + m_pCUPSWrapper->cupsSetDests( m_nDests, (cups_dest_t*)m_pDests ); + m_aDefaultPrinter = rName; + m_aCUPSMutex.release(); + bSuccess = true; + } + else +#endif + bSuccess = PrinterInfoManager::setDefaultPrinter( rName ); + + return bSuccess; +} + +bool CUPSManager::writePrinterConfig() +{ +#ifdef ENABLE_CUPS + bool bDestModified = false; + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + + for( boost::unordered_map< OUString, Printer, OUStringHash >::iterator prt = + m_aPrinters.begin(); prt != m_aPrinters.end(); ++prt ) + { + boost::unordered_map< OUString, int, OUStringHash >::iterator nit = + m_aCUPSDestMap.find( prt->first ); + if( nit == m_aCUPSDestMap.end() ) + continue; + + if( ! prt->second.m_bModified ) + continue; + + if( m_aCUPSMutex.tryToAcquire() ) + { + bDestModified = true; + cups_dest_t* pDest = ((cups_dest_t*)m_pDests) + nit->second; + PrinterInfo& rInfo = prt->second.m_aInfo; + + // create new option list + int nNewOptions = 0; + cups_option_t* pNewOptions = NULL; + int nValues = rInfo.m_aContext.countValuesModified(); + for( int i = 0; i < nValues; i++ ) + { + const PPDKey* pKey = rInfo.m_aContext.getModifiedKey( i ); + const PPDValue* pValue = rInfo.m_aContext.getValue( pKey ); + if( pKey && pValue ) // sanity check + { + OString aName = OUStringToOString( pKey->getKey(), aEncoding ); + OString aValue = OUStringToOString( pValue->m_aOption, aEncoding ); + nNewOptions = m_pCUPSWrapper->cupsAddOption( aName.getStr(), aValue.getStr(), nNewOptions, &pNewOptions ); + } + } + // set PPD options on CUPS dest + m_pCUPSWrapper->cupsFreeOptions( pDest->num_options, pDest->options ); + pDest->num_options = nNewOptions; + pDest->options = pNewOptions; + m_aCUPSMutex.release(); + } + } + if( bDestModified && m_aCUPSMutex.tryToAcquire() ) + { + m_pCUPSWrapper->cupsSetDests( m_nDests, (cups_dest_t*)m_pDests ); + m_aCUPSMutex.release(); + } +#endif // ENABLE_CUPS + + return PrinterInfoManager::writePrinterConfig(); +} + +bool CUPSManager::addOrRemovePossible() const +{ + return (m_nDests && m_pDests && ! isCUPSDisabled())? false : PrinterInfoManager::addOrRemovePossible(); +} + +const char* CUPSManager::authenticateUser( const char* /*pIn*/ ) +{ + const char* pRet = NULL; + +#ifdef ENABLE_CUPS + OUString aLib(RTL_CONSTASCII_USTRINGPARAM( _XSALSET_LIBNAME )); + oslModule pLib = osl_loadModule( aLib.pData, SAL_LOADMODULE_LAZY ); + if( pLib ) + { + OUString aSym( RTL_CONSTASCII_USTRINGPARAM( "Sal_authenticateQuery" ) ); + bool (*getpw)( const OString& rServer, OString& rUser, OString& rPw) = + (bool(*)(const OString&,OString&,OString&))osl_getFunctionSymbol( pLib, aSym.pData ); + if( getpw ) + { + osl::MutexGuard aGuard( m_aCUPSMutex ); + + OString aUser = m_pCUPSWrapper->cupsUser(); + OString aServer = m_pCUPSWrapper->cupsServer(); + OString aPassword; + if( getpw( aServer, aUser, aPassword ) ) + { + m_aPassword = aPassword; + m_aUser = aUser; + m_pCUPSWrapper->cupsSetUser( m_aUser.getStr() ); + pRet = m_aPassword.getStr(); + } + } + osl_unloadModule( pLib ); + } +#if OSL_DEBUG_LEVEL > 1 + else fprintf( stderr, "loading of module %s failed\n", OUStringToOString( aLib, osl_getThreadTextEncoding() ).getStr() ); +#endif +#endif // ENABLE_CUPS + + return pRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/printer/jobdata.cxx b/vcl/unx/generic/printer/jobdata.cxx new file mode 100644 index 000000000000..0982b219bade --- /dev/null +++ b/vcl/unx/generic/printer/jobdata.cxx @@ -0,0 +1,267 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "vcl/jobdata.hxx" +#include "vcl/printerinfomanager.hxx" + +#include "tools/stream.hxx" + +#include "sal/alloca.h" + +using namespace psp; + +JobData& JobData::operator=(const JobData& rRight) +{ + m_nCopies = rRight.m_nCopies; + m_nLeftMarginAdjust = rRight.m_nLeftMarginAdjust; + m_nRightMarginAdjust = rRight.m_nRightMarginAdjust; + m_nTopMarginAdjust = rRight.m_nTopMarginAdjust; + m_nBottomMarginAdjust = rRight.m_nBottomMarginAdjust; + m_nColorDepth = rRight.m_nColorDepth; + m_eOrientation = rRight.m_eOrientation; + m_aPrinterName = rRight.m_aPrinterName; + m_pParser = rRight.m_pParser; + m_aContext = rRight.m_aContext; + m_nPSLevel = rRight.m_nPSLevel; + m_nPDFDevice = rRight.m_nPDFDevice; + m_nColorDevice = rRight.m_nColorDevice; + + if( ! m_pParser && m_aPrinterName.getLength() ) + { + PrinterInfoManager& rMgr = PrinterInfoManager::get(); + rMgr.setupJobContextData( *this ); + } + return *this; +} + +void JobData::setCollate( bool bCollate ) +{ + const PPDParser* pParser = m_aContext.getParser(); + if( pParser ) + { + const PPDKey* pKey = pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Collate" ) ) ); + if( pKey ) + { + const PPDValue* pVal = NULL; + if( bCollate ) + pVal = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "True" ) ) ); + else + { + pVal = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "False" ) ) ); + if( ! pVal ) + pVal = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "None" ) ) ); + } + m_aContext.setValue( pKey, pVal ); + } + } +} + +bool JobData::setPaper( int i_nWidth, int i_nHeight ) +{ + bool bSuccess = false; + if( m_pParser ) + { + rtl::OUString aPaper( m_pParser->matchPaper( i_nWidth, i_nHeight ) ); + + const PPDKey* pKey = m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) ); + const PPDValue* pValue = pKey ? pKey->getValueCaseInsensitive( aPaper ) : NULL; + + bSuccess = pKey && pValue && m_aContext.setValue( pKey, pValue, false ); + } + return bSuccess; +} + +bool JobData::setPaperBin( int i_nPaperBin ) +{ + bool bSuccess = false; + if( m_pParser ) + { + const PPDKey* pKey = m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "InputSlot" ) ) ); + const PPDValue* pValue = pKey ? pKey->getValue( i_nPaperBin ) : NULL; + + bSuccess = pKey && pValue && m_aContext.setValue( pKey, pValue, false ); + } + return bSuccess; +} + +bool JobData::getStreamBuffer( void*& pData, int& bytes ) +{ + // consistency checks + if( ! m_pParser ) + m_pParser = m_aContext.getParser(); + if( m_pParser != m_aContext.getParser() || + ! m_pParser ) + return false; + + SvMemoryStream aStream; + ByteString aLine; + + // write header job data + aStream.WriteLine( "JobData 1" ); + + aLine = "printer="; + aLine += ByteString( String( m_aPrinterName ), RTL_TEXTENCODING_UTF8 ); + aStream.WriteLine( aLine ); + + aLine = "orientation="; + aLine += m_eOrientation == orientation::Landscape ? "Landscape" : "Portrait"; + aStream.WriteLine( aLine ); + + aLine = "copies="; + aLine += ByteString::CreateFromInt32( m_nCopies ); + aStream.WriteLine( aLine ); + + aLine = "margindajustment="; + aLine += ByteString::CreateFromInt32( m_nLeftMarginAdjust ); + aLine += ','; + aLine += ByteString::CreateFromInt32( m_nRightMarginAdjust ); + aLine += ','; + aLine += ByteString::CreateFromInt32( m_nTopMarginAdjust ); + aLine += ','; + aLine += ByteString::CreateFromInt32( m_nBottomMarginAdjust ); + aStream.WriteLine( aLine ); + + aLine = "colordepth="; + aLine += ByteString::CreateFromInt32( m_nColorDepth ); + aStream.WriteLine( aLine ); + + aLine = "pslevel="; + aLine += ByteString::CreateFromInt32( m_nPSLevel ); + aStream.WriteLine( aLine ); + + aLine = "pdfdevice="; + aLine += ByteString::CreateFromInt32( m_nPDFDevice ); + aStream.WriteLine( aLine ); + + aLine = "colordevice="; + aLine += ByteString::CreateFromInt32( m_nColorDevice ); + aStream.WriteLine( aLine ); + + // now append the PPDContext stream buffer + aStream.WriteLine( "PPDContexData" ); + sal_uLong nBytes; + void* pContextBuffer = m_aContext.getStreamableBuffer( nBytes ); + if( nBytes ) + aStream.Write( pContextBuffer, nBytes ); + + // success + pData = rtl_allocateMemory( bytes = aStream.Tell() ); + memcpy( pData, aStream.GetData(), bytes ); + return true; +} + +bool JobData::constructFromStreamBuffer( void* pData, int bytes, JobData& rJobData ) +{ + SvMemoryStream aStream( pData, bytes, STREAM_READ ); + ByteString aLine; + bool bVersion = false; + bool bPrinter = false; + bool bOrientation = false; + bool bCopies = false; + bool bContext = false; + bool bMargin = false; + bool bColorDepth = false; + bool bColorDevice = false; + bool bPSLevel = false; + bool bPDFDevice = false; + while( ! aStream.IsEof() ) + { + aStream.ReadLine( aLine ); + if( aLine.CompareTo( "JobData", 7 ) == COMPARE_EQUAL ) + bVersion = true; + else if( aLine.CompareTo( "printer=", 8 ) == COMPARE_EQUAL ) + { + bPrinter = true; + rJobData.m_aPrinterName = String( aLine.Copy( 8 ), RTL_TEXTENCODING_UTF8 ); + } + else if( aLine.CompareTo( "orientation=", 12 ) == COMPARE_EQUAL ) + { + bOrientation = true; + rJobData.m_eOrientation = aLine.Copy( 12 ).EqualsIgnoreCaseAscii( "landscape" ) ? orientation::Landscape : orientation::Portrait; + } + else if( aLine.CompareTo( "copies=", 7 ) == COMPARE_EQUAL ) + { + bCopies = true; + rJobData.m_nCopies = aLine.Copy( 7 ).ToInt32(); + } + else if( aLine.CompareTo( "margindajustment=",17 ) == COMPARE_EQUAL ) + { + bMargin = true; + ByteString aValues( aLine.Copy( 17 ) ); + rJobData.m_nLeftMarginAdjust = aValues.GetToken( 0, ',' ).ToInt32(); + rJobData.m_nRightMarginAdjust = aValues.GetToken( 1, ',' ).ToInt32(); + rJobData.m_nTopMarginAdjust = aValues.GetToken( 2, ',' ).ToInt32(); + rJobData.m_nBottomMarginAdjust = aValues.GetToken( 3, ',' ).ToInt32(); + } + else if( aLine.CompareTo( "colordepth=", 11 ) == COMPARE_EQUAL ) + { + bColorDepth = true; + rJobData.m_nColorDepth = aLine.Copy( 11 ).ToInt32(); + } + else if( aLine.CompareTo( "colordevice=", 12 ) == COMPARE_EQUAL ) + { + bColorDevice = true; + rJobData.m_nColorDevice = aLine.Copy( 12 ).ToInt32(); + } + else if( aLine.CompareTo( "pslevel=", 8 ) == COMPARE_EQUAL ) + { + bPSLevel = true; + rJobData.m_nPSLevel = aLine.Copy( 8 ).ToInt32(); + } + else if( aLine.CompareTo( "pdfdevice=", 10 ) == COMPARE_EQUAL ) + { + bPDFDevice = true; + rJobData.m_nPDFDevice = aLine.Copy( 10 ).ToInt32(); + } + else if( aLine.Equals( "PPDContexData" ) ) + { + if( bPrinter ) + { + PrinterInfoManager& rManager = PrinterInfoManager::get(); + const PrinterInfo& rInfo = rManager.getPrinterInfo( rJobData.m_aPrinterName ); + rJobData.m_pParser = PPDParser::getParser( rInfo.m_aDriverName ); + if( rJobData.m_pParser ) + { + rJobData.m_aContext.setParser( rJobData.m_pParser ); + int nBytes = bytes - aStream.Tell(); + void* pRemain = alloca( bytes - aStream.Tell() ); + aStream.Read( pRemain, nBytes ); + rJobData.m_aContext.rebuildFromStreamBuffer( pRemain, nBytes ); + bContext = true; + } + } + } + } + + return bVersion && bPrinter && bOrientation && bCopies && bContext && bMargin && bPSLevel && bPDFDevice && bColorDevice && bColorDepth; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/printer/ppdparser.cxx b/vcl/unx/generic/printer/ppdparser.cxx new file mode 100644 index 000000000000..30d4598568d2 --- /dev/null +++ b/vcl/unx/generic/printer/ppdparser.cxx @@ -0,0 +1,2187 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <stdlib.h> +#include <stdio.h> + +#include <boost/unordered_map.hpp> + +#include "vcl/ppdparser.hxx" +#include "vcl/strhelper.hxx" +#include "vcl/helper.hxx" +#include "vcl/svapp.hxx" +#include "cupsmgr.hxx" +#include "tools/debug.hxx" +#include "tools/urlobj.hxx" +#include "tools/stream.hxx" +#include "tools/zcodec.hxx" +#include "osl/mutex.hxx" +#include "osl/file.hxx" +#include "osl/process.h" +#include "osl/thread.h" +#include "rtl/strbuf.hxx" +#include "rtl/ustrbuf.hxx" +#include "rtl/instance.hxx" +#include <sal/macros.h> + +#include "com/sun/star/lang/Locale.hpp" + +namespace psp +{ + class PPDTranslator + { + struct LocaleEqual + { + bool operator()(const com::sun::star::lang::Locale& i_rLeft, + const com::sun::star::lang::Locale& i_rRight) const + { + return i_rLeft.Language.equals( i_rRight.Language ) && + i_rLeft.Country.equals( i_rRight.Country ) && + i_rLeft.Variant.equals( i_rRight.Variant ); + } + }; + + struct LocaleHash + { + size_t operator()(const com::sun::star::lang::Locale& rLocale) const + { return + (size_t)rLocale.Language.hashCode() + ^ (size_t)rLocale.Country.hashCode() + ^ (size_t)rLocale.Variant.hashCode() + ; + } + }; + + typedef boost::unordered_map< com::sun::star::lang::Locale, rtl::OUString, LocaleHash, LocaleEqual > translation_map; + typedef boost::unordered_map< rtl::OUString, translation_map, rtl::OUStringHash > key_translation_map; + + key_translation_map m_aTranslations; + public: + PPDTranslator() {} + ~PPDTranslator() {} + + + void insertValue( + const rtl::OUString& i_rKey, + const rtl::OUString& i_rOption, + const rtl::OUString& i_rValue, + const rtl::OUString& i_rTranslation, + const com::sun::star::lang::Locale& i_rLocale = com::sun::star::lang::Locale() + ); + + void insertOption( const rtl::OUString& i_rKey, + const rtl::OUString& i_rOption, + const rtl::OUString& i_rTranslation, + const com::sun::star::lang::Locale& i_rLocale = com::sun::star::lang::Locale() ) + { + insertValue( i_rKey, i_rOption, rtl::OUString(), i_rTranslation, i_rLocale ); + } + + void insertKey( const rtl::OUString& i_rKey, + const rtl::OUString& i_rTranslation, + const com::sun::star::lang::Locale& i_rLocale = com::sun::star::lang::Locale() ) + { + insertValue( i_rKey, rtl::OUString(), rtl::OUString(), i_rTranslation, i_rLocale ); + } + + rtl::OUString translateValue( + const rtl::OUString& i_rKey, + const rtl::OUString& i_rOption, + const rtl::OUString& i_rValue, + const com::sun::star::lang::Locale& i_rLocale = com::sun::star::lang::Locale() + ) const; + + rtl::OUString translateOption( const rtl::OUString& i_rKey, + const rtl::OUString& i_rOption, + const com::sun::star::lang::Locale& i_rLocale = com::sun::star::lang::Locale() ) const + { + return translateValue( i_rKey, i_rOption, rtl::OUString(), i_rLocale ); + } + + rtl::OUString translateKey( const rtl::OUString& i_rKey, + const com::sun::star::lang::Locale& i_rLocale = com::sun::star::lang::Locale() ) const + { + return translateValue( i_rKey, rtl::OUString(), rtl::OUString(), i_rLocale ); + } + }; + + static com::sun::star::lang::Locale normalizeInputLocale( + const com::sun::star::lang::Locale& i_rLocale, + bool bInsertDefault = false + ) + { + com::sun::star::lang::Locale aLoc( i_rLocale ); + if( bInsertDefault && aLoc.Language.getLength() == 0 ) + { + // empty locale requested, fill in application UI locale + aLoc = Application::GetSettings().GetUILocale(); + + #if OSL_DEBUG_LEVEL > 1 + static const char* pEnvLocale = getenv( "SAL_PPDPARSER_LOCALE" ); + if( pEnvLocale && *pEnvLocale ) + { + rtl::OString aStr( pEnvLocale ); + sal_Int32 nLen = aStr.getLength(); + aLoc.Language = rtl::OStringToOUString( aStr.copy( 0, nLen > 2 ? 2 : nLen ), RTL_TEXTENCODING_MS_1252 ); + if( nLen >=5 && aStr.getStr()[2] == '_' ) + aLoc.Country = rtl::OStringToOUString( aStr.copy( 3, 2 ), RTL_TEXTENCODING_MS_1252 ); + else + aLoc.Country = rtl::OUString(); + aLoc.Variant = rtl::OUString(); + } + #endif + } + aLoc.Language = aLoc.Language.toAsciiLowerCase(); + aLoc.Country = aLoc.Country.toAsciiUpperCase(); + aLoc.Variant = aLoc.Variant.toAsciiUpperCase(); + + return aLoc; + } + + void PPDTranslator::insertValue( + const rtl::OUString& i_rKey, + const rtl::OUString& i_rOption, + const rtl::OUString& i_rValue, + const rtl::OUString& i_rTranslation, + const com::sun::star::lang::Locale& i_rLocale + ) + { + rtl::OUStringBuffer aKey( i_rKey.getLength() + i_rOption.getLength() + i_rValue.getLength() + 2 ); + aKey.append( i_rKey ); + if( i_rOption.getLength() || i_rValue.getLength() ) + { + aKey.append( sal_Unicode( ':' ) ); + aKey.append( i_rOption ); + } + if( i_rValue.getLength() ) + { + aKey.append( sal_Unicode( ':' ) ); + aKey.append( i_rValue ); + } + if( aKey.getLength() && i_rTranslation.getLength() ) + { + rtl::OUString aK( aKey.makeStringAndClear() ); + com::sun::star::lang::Locale aLoc; + aLoc.Language = i_rLocale.Language.toAsciiLowerCase(); + aLoc.Country = i_rLocale.Country.toAsciiUpperCase(); + aLoc.Variant = i_rLocale.Variant.toAsciiUpperCase(); + m_aTranslations[ aK ][ aLoc ] = i_rTranslation; + } + } + + rtl::OUString PPDTranslator::translateValue( + const rtl::OUString& i_rKey, + const rtl::OUString& i_rOption, + const rtl::OUString& i_rValue, + const com::sun::star::lang::Locale& i_rLocale + ) const + { + rtl::OUString aResult; + + rtl::OUStringBuffer aKey( i_rKey.getLength() + i_rOption.getLength() + i_rValue.getLength() + 2 ); + aKey.append( i_rKey ); + if( i_rOption.getLength() || i_rValue.getLength() ) + { + aKey.append( sal_Unicode( ':' ) ); + aKey.append( i_rOption ); + } + if( i_rValue.getLength() ) + { + aKey.append( sal_Unicode( ':' ) ); + aKey.append( i_rValue ); + } + if( aKey.getLength() ) + { + rtl::OUString aK( aKey.makeStringAndClear() ); + key_translation_map::const_iterator it = m_aTranslations.find( aK ); + if( it != m_aTranslations.end() ) + { + const translation_map& rMap( it->second ); + + com::sun::star::lang::Locale aLoc( normalizeInputLocale( i_rLocale, true ) ); + for( int nTry = 0; nTry < 4; nTry++ ) + { + translation_map::const_iterator tr = rMap.find( aLoc ); + if( tr != rMap.end() ) + { + aResult = tr->second; + break; + } + switch( nTry ) + { + case 0: aLoc.Variant = rtl::OUString();break; + case 1: aLoc.Country = rtl::OUString();break; + case 2: aLoc.Language = rtl::OUString();break; + } + } + } + } + return aResult; + } + + class PPDCache + { + public: + std::list< PPDParser* > aAllParsers; + boost::unordered_map< rtl::OUString, rtl::OUString, rtl::OUStringHash >* pAllPPDFiles; + PPDCache() + : pAllPPDFiles(NULL) + {} + ~PPDCache() + { + while( aAllParsers.begin() != aAllParsers.end() ) + { + delete aAllParsers.front(); + aAllParsers.pop_front(); + } + delete pAllPPDFiles; + pAllPPDFiles = NULL; + } + }; +} + +using namespace psp; + +using ::rtl::OUString; +using ::rtl::OStringBuffer; +using ::rtl::OUStringHash; + + +#undef DBG_ASSERT +#if defined DBG_UTIL || (OSL_DEBUG_LEVEL > 1) +#define BSTRING(x) ByteString( x, osl_getThreadTextEncoding() ) +#define DBG_ASSERT( x, y ) { if( ! (x) ) fprintf( stderr, (y) ); } +#else +#define DBG_ASSERT( x, y ) +#endif + +namespace +{ + struct thePPDCache : public rtl::Static<PPDCache, thePPDCache> {}; +} + +class PPDDecompressStream +{ + SvFileStream* mpFileStream; + SvMemoryStream* mpMemStream; + rtl::OUString maFileName; + + // forbid copying + PPDDecompressStream( const PPDDecompressStream& ); + PPDDecompressStream& operator=(const PPDDecompressStream& ); + + public: + PPDDecompressStream( const rtl::OUString& rFile ); + ~PPDDecompressStream(); + + bool IsOpen() const; + bool IsEof() const; + void ReadLine( ByteString& o_rLine); + void Open( const rtl::OUString& i_rFile ); + void Close(); + const rtl::OUString& GetFileName() const { return maFileName; } +}; + +PPDDecompressStream::PPDDecompressStream( const rtl::OUString& i_rFile ) : + mpFileStream( NULL ), + mpMemStream( NULL ) +{ + Open( i_rFile ); +} + +PPDDecompressStream::~PPDDecompressStream() +{ + Close(); +} + +void PPDDecompressStream::Open( const rtl::OUString& i_rFile ) +{ + Close(); + + mpFileStream = new SvFileStream( i_rFile, STREAM_READ ); + maFileName = mpFileStream->GetFileName(); + + if( ! mpFileStream->IsOpen() ) + { + Close(); + return; + } + + ByteString aLine; + mpFileStream->ReadLine( aLine ); + mpFileStream->Seek( 0 ); + + // check for compress'ed or gzip'ed file + sal_uLong nCompressMethod = 0; + if( aLine.Len() > 1 && static_cast<unsigned char>(aLine.GetChar( 0 )) == 0x1f ) + { + if( static_cast<unsigned char>(aLine.GetChar( 1 )) == 0x8b ) // check for gzip + nCompressMethod = ZCODEC_DEFAULT | ZCODEC_GZ_LIB; + } + + if( nCompressMethod != 0 ) + { + // so let's try to decompress the stream + mpMemStream = new SvMemoryStream( 4096, 4096 ); + ZCodec aCodec; + aCodec.BeginCompression( nCompressMethod ); + long nComp = aCodec.Decompress( *mpFileStream, *mpMemStream ); + aCodec.EndCompression(); + if( nComp < 0 ) + { + // decompression failed, must be an uncompressed stream after all + delete mpMemStream, mpMemStream = NULL; + mpFileStream->Seek( 0 ); + } + else + { + // compression successfull, can get rid of file stream + delete mpFileStream, mpFileStream = NULL; + mpMemStream->Seek( 0 ); + } + } +} + +void PPDDecompressStream::Close() +{ + delete mpMemStream, mpMemStream = NULL; + delete mpFileStream, mpFileStream = NULL; +} + +bool PPDDecompressStream::IsOpen() const +{ + return (mpMemStream || (mpFileStream && mpFileStream->IsOpen())); +} + +bool PPDDecompressStream::IsEof() const +{ + return ( mpMemStream ? mpMemStream->IsEof() : ( mpFileStream ? mpFileStream->IsEof() : true ) ); +} + +void PPDDecompressStream::ReadLine( ByteString& o_rLine ) +{ + if( mpMemStream ) + mpMemStream->ReadLine( o_rLine ); + else if( mpFileStream ) + mpFileStream->ReadLine( o_rLine ); +} + +static osl::FileBase::RC resolveLink( const rtl::OUString& i_rURL, rtl::OUString& o_rResolvedURL, rtl::OUString& o_rBaseName, osl::FileStatus::Type& o_rType, int nLinkLevel = 10 ) +{ + osl::DirectoryItem aLinkItem; + osl::FileBase::RC aRet = osl::FileBase::E_None; + + if( ( aRet = osl::DirectoryItem::get( i_rURL, aLinkItem ) ) == osl::FileBase::E_None ) + { + osl::FileStatus aStatus( FileStatusMask_FileName | FileStatusMask_Type | FileStatusMask_LinkTargetURL ); + if( ( aRet = aLinkItem.getFileStatus( aStatus ) ) == osl::FileBase::E_None ) + { + if( aStatus.getFileType() == osl::FileStatus::Link ) + { + if( nLinkLevel > 0 ) + aRet = resolveLink( aStatus.getLinkTargetURL(), o_rResolvedURL, o_rBaseName, o_rType, nLinkLevel-1 ); + else + aRet = osl::FileBase::E_MULTIHOP; + } + else + { + o_rResolvedURL = i_rURL; + o_rBaseName = aStatus.getFileName(); + o_rType = aStatus.getFileType(); + } + } + } + return aRet; +} + +void PPDParser::scanPPDDir( const String& rDir ) +{ + static struct suffix_t + { + const sal_Char* pSuffix; + const sal_Int32 nSuffixLen; + } const pSuffixes[] = + { { ".PS", 3 }, { ".PPD", 4 }, { ".PS.GZ", 6 }, { ".PPD.GZ", 7 } }; + + const int nSuffixes = SAL_N_ELEMENTS(pSuffixes); + + PPDCache &rPPDCache = thePPDCache::get(); + + osl::Directory aDir( rDir ); + if ( aDir.open() == osl::FileBase::E_None ) + { + osl::DirectoryItem aItem; + + INetURLObject aPPDDir(rDir); + while( aDir.getNextItem( aItem ) == osl::FileBase::E_None ) + { + osl::FileStatus aStatus( FileStatusMask_FileName ); + if( aItem.getFileStatus( aStatus ) == osl::FileBase::E_None ) + { + rtl::OUStringBuffer aURLBuf( rDir.Len() + 64 ); + aURLBuf.append( rDir ); + aURLBuf.append( sal_Unicode( '/' ) ); + aURLBuf.append( aStatus.getFileName() ); + + rtl::OUString aFileURL, aFileName; + osl::FileStatus::Type eType = osl::FileStatus::Unknown; + + if( resolveLink( aURLBuf.makeStringAndClear(), aFileURL, aFileName, eType ) == osl::FileBase::E_None ) + { + if( eType == osl::FileStatus::Regular ) + { + INetURLObject aPPDFile = aPPDDir; + aPPDFile.Append( aFileName ); + + // match extension + for( int nSuffix = 0; nSuffix < nSuffixes; nSuffix++ ) + { + if( aFileName.getLength() > pSuffixes[nSuffix].nSuffixLen ) + { + if( aFileName.endsWithIgnoreAsciiCaseAsciiL( pSuffixes[nSuffix].pSuffix, pSuffixes[nSuffix].nSuffixLen ) ) + { + (*rPPDCache.pAllPPDFiles)[ aFileName.copy( 0, aFileName.getLength() - pSuffixes[nSuffix].nSuffixLen ) ] = aPPDFile.PathToFileName(); + break; + } + } + } + } + else if( eType == osl::FileStatus::Directory ) + { + scanPPDDir( aFileURL ); + } + } + } + } + aDir.close(); + } +} + +void PPDParser::initPPDFiles() +{ + PPDCache &rPPDCache = thePPDCache::get(); + if( rPPDCache.pAllPPDFiles ) + return; + + rPPDCache.pAllPPDFiles = new boost::unordered_map< OUString, OUString, OUStringHash >(); + + // check installation directories + std::list< OUString > aPathList; + psp::getPrinterPathList( aPathList, PRINTER_PPDDIR ); + for( std::list< OUString >::const_iterator ppd_it = aPathList.begin(); ppd_it != aPathList.end(); ++ppd_it ) + { + INetURLObject aPPDDir( *ppd_it, INET_PROT_FILE, INetURLObject::ENCODE_ALL ); + scanPPDDir( aPPDDir.GetMainURL( INetURLObject::NO_DECODE ) ); + } + if( rPPDCache.pAllPPDFiles->find( OUString( RTL_CONSTASCII_USTRINGPARAM( "SGENPRT" ) ) ) == rPPDCache.pAllPPDFiles->end() ) + { + // last try: search in directory of executable (mainly for setup) + OUString aExe; + if( osl_getExecutableFile( &aExe.pData ) == osl_Process_E_None ) + { + INetURLObject aDir( aExe ); + aDir.removeSegment(); +#ifdef DEBUG + fprintf( stderr, "scanning last chance dir: %s\n", OUStringToOString( aDir.GetMainURL( INetURLObject::NO_DECODE ), osl_getThreadTextEncoding() ).getStr() ); +#endif + scanPPDDir( aDir.GetMainURL( INetURLObject::NO_DECODE ) ); +#ifdef DEBUG + fprintf( stderr, "SGENPRT %s\n", rPPDCache.pAllPPDFiles->find( OUString( RTL_CONSTASCII_USTRINGPARAM( "SGENPRT" ) ) ) == rPPDCache.pAllPPDFiles->end() ? "not found" : "found" ); +#endif + } + } +} + +void PPDParser::getKnownPPDDrivers( std::list< rtl::OUString >& o_rDrivers, bool bRefresh ) +{ + PPDCache &rPPDCache = thePPDCache::get(); + + if( bRefresh ) + { + delete rPPDCache.pAllPPDFiles; + rPPDCache.pAllPPDFiles = NULL; + } + + initPPDFiles(); + o_rDrivers.clear(); + + boost::unordered_map< OUString, OUString, OUStringHash >::const_iterator it; + for( it = rPPDCache.pAllPPDFiles->begin(); it != rPPDCache.pAllPPDFiles->end(); ++it ) + o_rDrivers.push_back( it->first ); +} + +String PPDParser::getPPDFile( const String& rFile ) +{ + INetURLObject aPPD( rFile, INET_PROT_FILE, INetURLObject::ENCODE_ALL ); + // someone might enter a full qualified name here + PPDDecompressStream aStream( aPPD.PathToFileName() ); + if( ! aStream.IsOpen() ) + { + boost::unordered_map< OUString, OUString, OUStringHash >::const_iterator it; + PPDCache &rPPDCache = thePPDCache::get(); + + bool bRetry = true; + do + { + initPPDFiles(); + // some PPD files contain dots beside the extension, so try name first + // and cut of points after that + rtl::OUString aBase( rFile ); + sal_Int32 nLastIndex = aBase.lastIndexOf( sal_Unicode( '/' ) ); + if( nLastIndex >= 0 ) + aBase = aBase.copy( nLastIndex+1 ); + do + { + it = rPPDCache.pAllPPDFiles->find( aBase ); + nLastIndex = aBase.lastIndexOf( sal_Unicode( '.' ) ); + if( nLastIndex > 0 ) + aBase = aBase.copy( 0, nLastIndex ); + } while( it == rPPDCache.pAllPPDFiles->end() && nLastIndex > 0 ); + + if( it == rPPDCache.pAllPPDFiles->end() && bRetry ) + { + // a new file ? rehash + delete rPPDCache.pAllPPDFiles; rPPDCache.pAllPPDFiles = NULL; + bRetry = false; + // note this is optimized for office start where + // no new files occur and initPPDFiles is called only once + } + } while( ! rPPDCache.pAllPPDFiles ); + + if( it != rPPDCache.pAllPPDFiles->end() ) + aStream.Open( it->second ); + } + + String aRet; + if( aStream.IsOpen() ) + { + ByteString aLine; + aStream.ReadLine( aLine ); + if( aLine.Search( "*PPD-Adobe" ) == 0 ) + aRet = aStream.GetFileName(); + else + { + // our *Include hack does usually not begin + // with *PPD-Adobe, so try some lines for *Include + int nLines = 10; + while( aLine.Search( "*Include" ) != 0 && --nLines ) + aStream.ReadLine( aLine ); + if( nLines ) + aRet = aStream.GetFileName(); + } + } + + return aRet; +} + +String PPDParser::getPPDPrinterName( const String& rFile ) +{ + String aPath = getPPDFile( rFile ); + String aName; + + // read in the file + PPDDecompressStream aStream( aPath ); + if( aStream.IsOpen() ) + { + String aCurLine; + while( ! aStream.IsEof() && aStream.IsOpen() ) + { + ByteString aByteLine; + aStream.ReadLine( aByteLine ); + aCurLine = String( aByteLine, RTL_TEXTENCODING_MS_1252 ); + if( aCurLine.CompareIgnoreCaseToAscii( "*include:", 9 ) == COMPARE_EQUAL ) + { + aCurLine.Erase( 0, 9 ); + aCurLine.EraseLeadingChars( ' ' ); + aCurLine.EraseTrailingChars( ' ' ); + aCurLine.EraseLeadingChars( '\t' ); + aCurLine.EraseTrailingChars( '\t' ); + aCurLine.EraseTrailingChars( '\r' ); + aCurLine.EraseTrailingChars( '\n' ); + aCurLine.EraseLeadingChars( '"' ); + aCurLine.EraseTrailingChars( '"' ); + aStream.Close(); + aStream.Open( getPPDFile( aCurLine ) ); + continue; + } + if( aCurLine.CompareToAscii( "*ModelName:", 11 ) == COMPARE_EQUAL ) + { + aName = aCurLine.GetToken( 1, '"' ); + break; + } + else if( aCurLine.CompareToAscii( "*NickName:", 10 ) == COMPARE_EQUAL ) + aName = aCurLine.GetToken( 1, '"' ); + } + } + return aName; +} + +const PPDParser* PPDParser::getParser( const String& rFile ) +{ + static ::osl::Mutex aMutex; + ::osl::Guard< ::osl::Mutex > aGuard( aMutex ); + + String aFile = rFile; + if( rFile.CompareToAscii( "CUPS:", 5 ) != COMPARE_EQUAL ) + aFile = getPPDFile( rFile ); + if( ! aFile.Len() ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "Could not get printer PPD file \"%s\" !\n", OUStringToOString( rFile, osl_getThreadTextEncoding() ).getStr() ); +#endif + return NULL; + } + + PPDCache &rPPDCache = thePPDCache::get(); + for( ::std::list< PPDParser* >::const_iterator it = rPPDCache.aAllParsers.begin(); it != rPPDCache.aAllParsers.end(); ++it ) + if( (*it)->m_aFile == aFile ) + return *it; + + PPDParser* pNewParser = NULL; + if( aFile.CompareToAscii( "CUPS:", 5 ) != COMPARE_EQUAL ) + pNewParser = new PPDParser( aFile ); + else + { + PrinterInfoManager& rMgr = PrinterInfoManager::get(); + if( rMgr.getType() == PrinterInfoManager::CUPS ) + { + pNewParser = const_cast<PPDParser*>(static_cast<CUPSManager&>(rMgr).createCUPSParser( aFile )); + } + } + if( pNewParser ) + { + // this may actually be the SGENPRT parser, + // so ensure uniquness here + rPPDCache.aAllParsers.remove( pNewParser ); + // insert new parser to list + rPPDCache.aAllParsers.push_front( pNewParser ); + } + return pNewParser; +} + +PPDParser::PPDParser( const String& rFile ) : + m_aFile( rFile ), + m_bType42Capable( false ), + m_aFileEncoding( RTL_TEXTENCODING_MS_1252 ), + m_pDefaultImageableArea( NULL ), + m_pImageableAreas( NULL ), + m_pDefaultPaperDimension( NULL ), + m_pPaperDimensions( NULL ), + m_pDefaultInputSlot( NULL ), + m_pInputSlots( NULL ), + m_pDefaultResolution( NULL ), + m_pResolutions( NULL ), + m_pDefaultDuplexType( NULL ), + m_pDuplexTypes( NULL ), + m_pFontList( NULL ), + m_pTranslator( new PPDTranslator() ) +{ + // read in the file + std::list< ByteString > aLines; + PPDDecompressStream aStream( m_aFile ); + bool bLanguageEncoding = false; + if( aStream.IsOpen() ) + { + ByteString aCurLine; + while( ! aStream.IsEof() ) + { + aStream.ReadLine( aCurLine ); + if( aCurLine.GetChar( 0 ) == '*' ) + { + if( aCurLine.CompareIgnoreCaseToAscii( "*include:", 9 ) == COMPARE_EQUAL ) + { + aCurLine.Erase( 0, 9 ); + aCurLine.EraseLeadingChars( ' ' ); + aCurLine.EraseTrailingChars( ' ' ); + aCurLine.EraseLeadingChars( '\t' ); + aCurLine.EraseTrailingChars( '\t' ); + aCurLine.EraseTrailingChars( '\r' ); + aCurLine.EraseTrailingChars( '\n' ); + aCurLine.EraseLeadingChars( '"' ); + aCurLine.EraseTrailingChars( '"' ); + aStream.Close(); + aStream.Open( getPPDFile( String( aCurLine, m_aFileEncoding ) ) ); + continue; + } + else if( ! bLanguageEncoding && + aCurLine.CompareIgnoreCaseToAscii( "*languageencoding", 17 ) == COMPARE_EQUAL ) + { + bLanguageEncoding = true; // generally only the first one counts + ByteString aLower = aCurLine; + aLower.ToLowerAscii(); + if( aLower.Search( "isolatin1", 17 ) != STRING_NOTFOUND || + aLower.Search( "windowsansi", 17 ) != STRING_NOTFOUND ) + m_aFileEncoding = RTL_TEXTENCODING_MS_1252; + else if( aLower.Search( "isolatin2", 17 ) != STRING_NOTFOUND ) + m_aFileEncoding = RTL_TEXTENCODING_ISO_8859_2; + else if( aLower.Search( "isolatin5", 17 ) != STRING_NOTFOUND ) + m_aFileEncoding = RTL_TEXTENCODING_ISO_8859_5; + else if( aLower.Search( "jis83-rksj", 17 ) != STRING_NOTFOUND ) + m_aFileEncoding = RTL_TEXTENCODING_SHIFT_JIS; + else if( aLower.Search( "macstandard", 17 ) != STRING_NOTFOUND ) + m_aFileEncoding = RTL_TEXTENCODING_APPLE_ROMAN; + else if( aLower.Search( "utf-8", 17 ) != STRING_NOTFOUND ) + m_aFileEncoding = RTL_TEXTENCODING_UTF8; + } + } + aLines.push_back( aCurLine ); + } + } + aStream.Close(); + + // now get the Values + parse( aLines ); +#if OSL_DEBUG_LEVEL > 2 + fprintf( stderr, "acquired %d Keys from PPD %s:\n", m_aKeys.size(), BSTRING( m_aFile ).GetBuffer() ); + for( PPDParser::hash_type::const_iterator it = m_aKeys.begin(); it != m_aKeys.end(); ++it ) + { + const PPDKey* pKey = it->second; + char* pSetupType = "<unknown>"; + switch( pKey->m_eSetupType ) + { + case PPDKey::ExitServer: pSetupType = "ExitServer";break; + case PPDKey::Prolog: pSetupType = "Prolog";break; + case PPDKey::DocumentSetup: pSetupType = "DocumentSetup";break; + case PPDKey::PageSetup: pSetupType = "PageSetup";break; + case PPDKey::JCLSetup: pSetupType = "JCLSetup";break; + case PPDKey::AnySetup: pSetupType = "AnySetup";break; + default: break; + }; + fprintf( stderr, "\t\"%s\" (%d values) OrderDependency: %d %s\n", + BSTRING( pKey->getKey() ).GetBuffer(), + pKey->countValues(), + pKey->m_nOrderDependency, + pSetupType ); + for( int j = 0; j < pKey->countValues(); j++ ) + { + fprintf( stderr, "\t\t" ); + const PPDValue* pValue = pKey->getValue( j ); + if( pValue == pKey->m_pDefaultValue ) + fprintf( stderr, "(Default:) " ); + char* pVType = "<unknown>"; + switch( pValue->m_eType ) + { + case eInvocation: pVType = "invocation";break; + case eQuoted: pVType = "quoted";break; + case eString: pVType = "string";break; + case eSymbol: pVType = "symbol";break; + case eNo: pVType = "no";break; + default: break; + }; + fprintf( stderr, "option: \"%s\", value: type %s \"%s\"\n", + BSTRING( pValue->m_aOption ).GetBuffer(), + pVType, + BSTRING( pValue->m_aValue ).GetBuffer() ); + } + } + fprintf( stderr, "constraints: (%d found)\n", m_aConstraints.size() ); + for( std::list< PPDConstraint >::const_iterator cit = m_aConstraints.begin(); cit != m_aConstraints.end(); ++cit ) + { + fprintf( stderr, "*\"%s\" \"%s\" *\"%s\" \"%s\"\n", + BSTRING( cit->m_pKey1->getKey() ).GetBuffer(), + cit->m_pOption1 ? BSTRING( cit->m_pOption1->m_aOption ).GetBuffer() : "<nil>", + BSTRING( cit->m_pKey2->getKey() ).GetBuffer(), + cit->m_pOption2 ? BSTRING( cit->m_pOption2->m_aOption ).GetBuffer() : "<nil>" + ); + } +#endif + + // fill in shortcuts + const PPDKey* pKey; + + m_pImageableAreas = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "ImageableArea" ) ) ); + if( m_pImageableAreas ) + m_pDefaultImageableArea = m_pImageableAreas->getDefaultValue(); + DBG_ASSERT( m_pImageableAreas, "Warning: no ImageableArea in PPD\n" ); + DBG_ASSERT( m_pDefaultImageableArea, "Warning: no DefaultImageableArea in PPD\n" ); + + m_pPaperDimensions = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PaperDimension" ) ) ); + if( m_pPaperDimensions ) + m_pDefaultPaperDimension = m_pPaperDimensions->getDefaultValue(); + DBG_ASSERT( m_pPaperDimensions, "Warning: no PaperDimension in PPD\n" ); + DBG_ASSERT( m_pDefaultPaperDimension, "Warning: no DefaultPaperDimension in PPD\n" ); + + m_pResolutions = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Resolution" ) ) ); + if( m_pResolutions ) + m_pDefaultResolution = m_pResolutions->getDefaultValue(); + DBG_ASSERT( m_pResolutions, "Warning: no Resolution in PPD\n" ); + DBG_ASSERT( m_pDefaultResolution, "Warning: no DefaultResolution in PPD\n" ); + + m_pInputSlots = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "InputSlot" ) ) ); + if( m_pInputSlots ) + m_pDefaultInputSlot = m_pInputSlots->getDefaultValue(); + DBG_ASSERT( m_pPaperDimensions, "Warning: no InputSlot in PPD\n" ); + DBG_ASSERT( m_pDefaultPaperDimension, "Warning: no DefaultInputSlot in PPD\n" ); + + m_pDuplexTypes = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Duplex" ) ) ); + if( m_pDuplexTypes ) + m_pDefaultDuplexType = m_pDuplexTypes->getDefaultValue(); + + m_pFontList = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Font" ) ) ); + DBG_ASSERT( m_pFontList, "Warning: no Font in PPD\n" ); + + // fill in direct values + if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "ModelName" ) ) )) ) + m_aPrinterName = pKey->getValue( 0 )->m_aValue; + if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "NickName" ) ) )) ) + m_aNickName = pKey->getValue( 0 )->m_aValue; + if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "ColorDevice" ) ) )) ) + m_bColorDevice = pKey->getValue( 0 )->m_aValue.CompareIgnoreCaseToAscii( "true", 4 ) == COMPARE_EQUAL ? true : false; + + if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "LanguageLevel" ) ) )) ) + m_nLanguageLevel = pKey->getValue( 0 )->m_aValue.ToInt32(); + if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "TTRasterizer" ) ) )) ) + m_bType42Capable = pKey->getValue( 0 )->m_aValue.EqualsIgnoreCaseAscii( "Type42" ) ? true : false; +} + +PPDParser::~PPDParser() +{ + for( PPDParser::hash_type::iterator it = m_aKeys.begin(); it != m_aKeys.end(); ++it ) + delete it->second; + delete m_pTranslator; +} + +void PPDParser::insertKey( const String& rKey, PPDKey* pKey ) +{ + m_aKeys[ rKey ] = pKey; + m_aOrderedKeys.push_back( pKey ); +} + +const PPDKey* PPDParser::getKey( int n ) const +{ + return ((unsigned int)n < m_aOrderedKeys.size() && n >= 0) ? m_aOrderedKeys[n] : NULL; +} + +const PPDKey* PPDParser::getKey( const String& rKey ) const +{ + PPDParser::hash_type::const_iterator it = m_aKeys.find( rKey ); + return it != m_aKeys.end() ? it->second : NULL; +} + +bool PPDParser::hasKey( const PPDKey* pKey ) const +{ + return + pKey ? + ( m_aKeys.find( pKey->getKey() ) != m_aKeys.end() ? true : false ) : + false; +} + +static sal_uInt8 getNibble( sal_Char cChar ) +{ + sal_uInt8 nRet = 0; + if( cChar >= '0' && cChar <= '9' ) + nRet = sal_uInt8( cChar - '0' ); + else if( cChar >= 'A' && cChar <= 'F' ) + nRet = 10 + sal_uInt8( cChar - 'A' ); + else if( cChar >= 'a' && cChar <= 'f' ) + nRet = 10 + sal_uInt8( cChar - 'a' ); + return nRet; +} + +String PPDParser::handleTranslation( const ByteString& i_rString, bool bIsGlobalized ) +{ + int nOrigLen = i_rString.Len(); + OStringBuffer aTrans( nOrigLen ); + const sal_Char* pStr = i_rString.GetBuffer(); + const sal_Char* pEnd = pStr + nOrigLen; + while( pStr < pEnd ) + { + if( *pStr == '<' ) + { + pStr++; + sal_Char cChar; + while( *pStr != '>' && pStr < pEnd-1 ) + { + cChar = getNibble( *pStr++ ) << 4; + cChar |= getNibble( *pStr++ ); + aTrans.append( cChar ); + } + pStr++; + } + else + aTrans.append( *pStr++ ); + } + return OStringToOUString( aTrans.makeStringAndClear(), bIsGlobalized ? RTL_TEXTENCODING_UTF8 : m_aFileEncoding ); +} + +void PPDParser::parse( ::std::list< ByteString >& rLines ) +{ + std::list< ByteString >::iterator line = rLines.begin(); + PPDParser::hash_type::const_iterator keyit; + while( line != rLines.end() ) + { + ByteString aCurrentLine( *line ); + ++line; + if( aCurrentLine.GetChar(0) != '*' ) + continue; + if( aCurrentLine.GetChar(1) == '%' ) + continue; + + ByteString aKey = GetCommandLineToken( 0, aCurrentLine.GetToken( 0, ':' ) ); + int nPos = aKey.Search( '/' ); + if( nPos != STRING_NOTFOUND ) + aKey.Erase( nPos ); + aKey.Erase( 0, 1 ); // remove the '*' + + if( aKey.Equals( "CloseUI" ) || aKey.Equals( "OpenGroup" ) || aKey.Equals( "CloseGroup" ) || aKey.Equals( "End" ) || aKey.Equals( "OpenSubGroup" ) || aKey.Equals( "CloseSubGroup" ) ) + continue; + + if( aKey.Equals( "OpenUI" ) ) + { + parseOpenUI( aCurrentLine ); + continue; + } + else if( aKey.Equals( "OrderDependency" ) ) + { + parseOrderDependency( aCurrentLine ); + continue; + } + else if( aKey.Equals( "UIConstraints" ) || aKey.Equals( "NonUIConstraints" ) ) + continue; // parsed in pass 2 + else if( aKey.Equals( "CustomPageSize" ) ) // currently not handled + continue; + + // default values are parsed in pass 2 + if( aKey.CompareTo( "Default", 7 ) == COMPARE_EQUAL ) + continue; + + bool bQuery = false; + if( aKey.GetChar( 0 ) == '?' ) + { + aKey.Erase( 0, 1 ); + bQuery = true; + } + + String aUniKey( aKey, RTL_TEXTENCODING_MS_1252 ); + // handle CUPS extension for globalized PPDs + bool bIsGlobalizedLine = false; + com::sun::star::lang::Locale aTransLocale; + if( ( aUniKey.Len() > 3 && aUniKey.GetChar( 2 ) == '.' ) || + ( aUniKey.Len() > 5 && aUniKey.GetChar( 2 ) == '_' && aUniKey.GetChar( 5 ) == '.' ) ) + { + if( aUniKey.GetChar( 2 ) == '.' ) + { + aTransLocale.Language = aUniKey.Copy( 0, 2 ); + aUniKey = aUniKey.Copy( 3 ); + } + else + { + aTransLocale.Language = aUniKey.Copy( 0, 2 ); + aTransLocale.Country = aUniKey.Copy( 3, 2 ); + aUniKey = aUniKey.Copy( 6 ); + } + bIsGlobalizedLine = true; + } + + String aOption; + nPos = aCurrentLine.Search( ':' ); + if( nPos != STRING_NOTFOUND ) + { + aOption = String( aCurrentLine.Copy( 1, nPos-1 ), RTL_TEXTENCODING_MS_1252 ); + aOption = GetCommandLineToken( 1, aOption ); + int nTransPos = aOption.Search( '/' ); + if( nTransPos != STRING_NOTFOUND ) + aOption.Erase( nTransPos ); + } + + PPDValueType eType = eNo; + String aValue; + rtl::OUString aOptionTranslation; + rtl::OUString aValueTranslation; + if( nPos != STRING_NOTFOUND ) + { + // found a colon, there may be an option + ByteString aLine = aCurrentLine.Copy( 1, nPos-1 ); + aLine = WhitespaceToSpace( aLine ); + int nTransPos = aLine.Search( '/' ); + if( nTransPos != STRING_NOTFOUND ) + aOptionTranslation = handleTranslation( aLine.Copy( nTransPos+1 ), bIsGlobalizedLine ); + + // read in more lines if necessary for multiline values + aLine = aCurrentLine.Copy( nPos+1 ); + if( aLine.Len() ) + { + while( ! ( aLine.GetTokenCount( '"' ) & 1 ) && + line != rLines.end() ) + // while there is an even number of tokens; that means + // an odd number of doubleqoutes + { + // copy the newlines also + aLine += '\n'; + aLine += *line; + ++line; + } + } + aLine = WhitespaceToSpace( aLine ); + + // #i100644# handle a missing value (actually a broken PPD) + if( ! aLine.Len() ) + { + if( aOption.Len() && + aUniKey.CompareToAscii( "JCL", 3 ) != COMPARE_EQUAL ) + eType = eInvocation; + else + eType = eQuoted; + } + // check for invocation or quoted value + else if( aLine.GetChar(0) == '"' ) + { + aLine.Erase( 0, 1 ); + nTransPos = aLine.Search( '"' ); + aValue = String( aLine.Copy( 0, nTransPos ), RTL_TEXTENCODING_MS_1252 ); + // after the second doublequote can follow a / and a translation + aValueTranslation = handleTranslation( aLine.Copy( nTransPos+2 ), bIsGlobalizedLine ); + // check for quoted value + if( aOption.Len() && + aUniKey.CompareToAscii( "JCL", 3 ) != COMPARE_EQUAL ) + eType = eInvocation; + else + eType = eQuoted; + } + // check for symbol value + else if( aLine.GetChar(0) == '^' ) + { + aLine.Erase( 0, 1 ); + aValue = String( aLine, RTL_TEXTENCODING_MS_1252 ); + eType = eSymbol; + } + else + { + // must be a string value then + // strictly this is false because string values + // can contain any whitespace which is reduced + // to one space by now + // who cares ... + nTransPos = aLine.Search( '/' ); + if( nTransPos == STRING_NOTFOUND ) + nTransPos = aLine.Len(); + aValue = String( aLine.Copy( 0, nTransPos ), RTL_TEXTENCODING_MS_1252 ); + aValueTranslation = handleTranslation( aLine.Copy( nTransPos+1 ), bIsGlobalizedLine ); + eType = eString; + } + } + + // handle globalized PPD entries + if( bIsGlobalizedLine ) + { + // handle main key translations of form: + // *ll_CC.Translation MainKeyword/translated text: "" + if( aUniKey.EqualsAscii( "Translation" ) ) + { + m_pTranslator->insertKey( aOption, aOptionTranslation, aTransLocale ); + } + // handle options translations of for: + // *ll_CC.MainKeyword OptionKeyword/translated text: "" + else + { + m_pTranslator->insertOption( aUniKey, aOption, aOptionTranslation, aTransLocale ); + } + continue; + } + + PPDKey* pKey = NULL; + keyit = m_aKeys.find( aUniKey ); + if( keyit == m_aKeys.end() ) + { + pKey = new PPDKey( aUniKey ); + insertKey( aUniKey, pKey ); + } + else + pKey = keyit->second; + + if( eType == eNo && bQuery ) + continue; + + PPDValue* pValue = pKey->insertValue( aOption ); + if( ! pValue ) + continue; + pValue->m_eType = eType; + pValue->m_aValue = aValue; + + if( aOptionTranslation.getLength() ) + m_pTranslator->insertOption( aUniKey, aOption, aOptionTranslation, aTransLocale ); + if( aValueTranslation.getLength() ) + m_pTranslator->insertValue( aUniKey, aOption, aValue, aValueTranslation, aTransLocale ); + + // eventually update query and remove from option list + if( bQuery && pKey->m_bQueryValue == sal_False ) + { + pKey->m_aQueryValue = *pValue; + pKey->m_bQueryValue = true; + pKey->eraseValue( pValue->m_aOption ); + } + } + + // second pass: fill in defaults + for( line = rLines.begin(); line != rLines.end(); ++line ) + { + ByteString aLine( *line ); + if( aLine.CompareTo( "*Default", 8 ) == COMPARE_EQUAL ) + { + String aKey( aLine.Copy( 8 ), RTL_TEXTENCODING_MS_1252 ); + sal_uInt16 nPos = aKey.Search( ':' ); + if( nPos != STRING_NOTFOUND ) + { + aKey.Erase( nPos ); + String aOption( WhitespaceToSpace( aLine.Copy( nPos+9 ) ), RTL_TEXTENCODING_MS_1252 ); + keyit = m_aKeys.find( aKey ); + if( keyit != m_aKeys.end() ) + { + PPDKey* pKey = keyit->second; + const PPDValue* pDefValue = pKey->getValue( aOption ); + if( pKey->m_pDefaultValue == NULL ) + pKey->m_pDefaultValue = pDefValue; + } + else + { + // some PPDs contain defaults for keys that + // do not exist otherwise + // (example: DefaultResolution) + // so invent that key here and have a default value + PPDKey* pKey = new PPDKey( aKey ); + PPDValue* pNewValue = pKey->insertValue( aOption ); + pNewValue->m_eType = eInvocation; // or what ? + insertKey( aKey, pKey ); + } + } + } + else if( aLine.CompareTo( "*UIConstraints", 14 ) == COMPARE_EQUAL || + aLine.CompareTo( "*NonUIConstraints", 17 ) == COMPARE_EQUAL ) + parseConstraint( aLine ); + + } +} + +void PPDParser::parseOpenUI( const ByteString& rLine ) +{ + String aTranslation; + ByteString aKey = rLine; + + int nPos = aKey.Search( ':' ); + if( nPos != STRING_NOTFOUND ) + aKey.Erase( nPos ); + nPos = aKey.Search( '/' ); + if( nPos != STRING_NOTFOUND ) + { + aTranslation = handleTranslation( aKey.Copy( nPos + 1 ), false ); + aKey.Erase( nPos ); + } + aKey = GetCommandLineToken( 1, aKey ); + aKey.Erase( 0, 1 ); + + String aUniKey( aKey, RTL_TEXTENCODING_MS_1252 ); + PPDParser::hash_type::const_iterator keyit = m_aKeys.find( aUniKey ); + PPDKey* pKey; + if( keyit == m_aKeys.end() ) + { + pKey = new PPDKey( aUniKey ); + insertKey( aUniKey, pKey ); + } + else + pKey = keyit->second; + + pKey->m_bUIOption = true; + m_pTranslator->insertKey( pKey->getKey(), aTranslation ); + + ByteString aValue = WhitespaceToSpace( rLine.GetToken( 1, ':' ) ); + if( aValue.CompareIgnoreCaseToAscii( "boolean" ) == COMPARE_EQUAL ) + pKey->m_eUIType = PPDKey::Boolean; + else if( aValue.CompareIgnoreCaseToAscii( "pickmany" ) == COMPARE_EQUAL ) + pKey->m_eUIType = PPDKey::PickMany; + else + pKey->m_eUIType = PPDKey::PickOne; +} + +void PPDParser::parseOrderDependency( const ByteString& rLine ) +{ + ByteString aLine( rLine ); + int nPos = aLine.Search( ':' ); + if( nPos != STRING_NOTFOUND ) + aLine.Erase( 0, nPos+1 ); + + int nOrder = GetCommandLineToken( 0, aLine ).ToInt32(); + ByteString aSetup = GetCommandLineToken( 1, aLine ); + String aKey( GetCommandLineToken( 2, aLine ), RTL_TEXTENCODING_MS_1252 ); + if( aKey.GetChar( 0 ) != '*' ) + return; // invalid order depency + aKey.Erase( 0, 1 ); + + PPDKey* pKey; + PPDParser::hash_type::const_iterator keyit = m_aKeys.find( aKey ); + if( keyit == m_aKeys.end() ) + { + pKey = new PPDKey( aKey ); + insertKey( aKey, pKey ); + } + else + pKey = keyit->second; + + pKey->m_nOrderDependency = nOrder; + if( aSetup.Equals( "ExitServer" ) ) + pKey->m_eSetupType = PPDKey::ExitServer; + else if( aSetup.Equals( "Prolog" ) ) + pKey->m_eSetupType = PPDKey::Prolog; + else if( aSetup.Equals( "DocumentSetup" ) ) + pKey->m_eSetupType = PPDKey::DocumentSetup; + else if( aSetup.Equals( "PageSetup" ) ) + pKey->m_eSetupType = PPDKey::PageSetup; + else if( aSetup.Equals( "JCLSetup" ) ) + pKey->m_eSetupType = PPDKey::JCLSetup; + else + pKey->m_eSetupType = PPDKey::AnySetup; +} + +void PPDParser::parseConstraint( const ByteString& rLine ) +{ + bool bFailed = false; + + String aLine( rLine, RTL_TEXTENCODING_MS_1252 ); + aLine.Erase( 0, rLine.Search( ':' )+1 ); + PPDConstraint aConstraint; + int nTokens = GetCommandLineTokenCount( aLine ); + for( int i = 0; i < nTokens; i++ ) + { + String aToken = GetCommandLineToken( i, aLine ); + if( aToken.GetChar( 0 ) == '*' ) + { + aToken.Erase( 0, 1 ); + if( aConstraint.m_pKey1 ) + aConstraint.m_pKey2 = getKey( aToken ); + else + aConstraint.m_pKey1 = getKey( aToken ); + } + else + { + if( aConstraint.m_pKey2 ) + { + if( ! ( aConstraint.m_pOption2 = aConstraint.m_pKey2->getValue( aToken ) ) ) + bFailed = true; + } + else if( aConstraint.m_pKey1 ) + { + if( ! ( aConstraint.m_pOption1 = aConstraint.m_pKey1->getValue( aToken ) ) ) + bFailed = true; + } + else + // constraint for nonexistent keys; this happens + // e.g. in HP4PLUS3 + bFailed = true; + } + } + // there must be two keywords + if( ! aConstraint.m_pKey1 || ! aConstraint.m_pKey2 || bFailed ) + { +#ifdef __DEBUG + fprintf( stderr, "Warning: constraint \"%s\" is invalid\n", rLine.GetStr() ); +#endif + } + else + m_aConstraints.push_back( aConstraint ); +} + +String PPDParser::getDefaultPaperDimension() const +{ + if( m_pDefaultPaperDimension ) + return m_pDefaultPaperDimension->m_aOption; + + return String(); +} + +bool PPDParser::getMargins( + const String& rPaperName, + int& rLeft, int& rRight, + int& rUpper, int& rLower ) const +{ + if( ! m_pImageableAreas || ! m_pPaperDimensions ) + return false; + + int nPDim=-1, nImArea=-1, i; + for( i = 0; i < m_pImageableAreas->countValues(); i++ ) + if( rPaperName == m_pImageableAreas->getValue( i )->m_aOption ) + nImArea = i; + for( i = 0; i < m_pPaperDimensions->countValues(); i++ ) + if( rPaperName == m_pPaperDimensions->getValue( i )->m_aOption ) + nPDim = i; + if( nPDim == -1 || nImArea == -1 ) + return false; + + double ImLLx, ImLLy, ImURx, ImURy; + double PDWidth, PDHeight; + String aArea = m_pImageableAreas->getValue( nImArea )->m_aValue; + ImLLx = StringToDouble( GetCommandLineToken( 0, aArea ) ); + ImLLy = StringToDouble( GetCommandLineToken( 1, aArea ) ); + ImURx = StringToDouble( GetCommandLineToken( 2, aArea ) ); + ImURy = StringToDouble( GetCommandLineToken( 3, aArea ) ); + aArea = m_pPaperDimensions->getValue( nPDim )->m_aValue; + PDWidth = StringToDouble( GetCommandLineToken( 0, aArea ) ); + PDHeight = StringToDouble( GetCommandLineToken( 1, aArea ) ); + rLeft = (int)(ImLLx + 0.5); + rLower = (int)(ImLLy + 0.5); + rUpper = (int)(PDHeight - ImURy + 0.5); + rRight = (int)(PDWidth - ImURx + 0.5); + + return true; +} + +bool PPDParser::getPaperDimension( + const String& rPaperName, + int& rWidth, int& rHeight ) const +{ + if( ! m_pPaperDimensions ) + return false; + + int nPDim=-1; + for( int i = 0; i < m_pPaperDimensions->countValues(); i++ ) + if( rPaperName == m_pPaperDimensions->getValue( i )->m_aOption ) + nPDim = i; + if( nPDim == -1 ) + return false; + + double PDWidth, PDHeight; + String aArea = m_pPaperDimensions->getValue( nPDim )->m_aValue; + PDWidth = StringToDouble( GetCommandLineToken( 0, aArea ) ); + PDHeight = StringToDouble( GetCommandLineToken( 1, aArea ) ); + rHeight = (int)(PDHeight + 0.5); + rWidth = (int)(PDWidth + 0.5); + + return true; +} + +String PPDParser::matchPaper( int nWidth, int nHeight ) const +{ + if( ! m_pPaperDimensions ) + return String(); + + int nPDim = -1; + double PDWidth, PDHeight; + double fSort = 2e36, fNewSort; + + for( int i = 0; i < m_pPaperDimensions->countValues(); i++ ) + { + String aArea = m_pPaperDimensions->getValue( i )->m_aValue; + PDWidth = StringToDouble( GetCommandLineToken( 0, aArea ) ); + PDHeight = StringToDouble( GetCommandLineToken( 1, aArea ) ); + PDWidth /= (double)nWidth; + PDHeight /= (double)nHeight; + if( PDWidth >= 0.9 && PDWidth <= 1.1 && + PDHeight >= 0.9 && PDHeight <= 1.1 ) + { + fNewSort = + (1.0-PDWidth)*(1.0-PDWidth) + (1.0-PDHeight)*(1.0-PDHeight); + if( fNewSort == 0.0 ) // perfect match + return m_pPaperDimensions->getValue( i )->m_aOption; + + if( fNewSort < fSort ) + { + fSort = fNewSort; + nPDim = i; + } + } + } + + static bool bDontSwap = false; + if( nPDim == -1 && ! bDontSwap ) + { + // swap portrait/landscape and try again + bDontSwap = true; + String rRet = matchPaper( nHeight, nWidth ); + bDontSwap = false; + return rRet; + } + + return nPDim != -1 ? m_pPaperDimensions->getValue( nPDim )->m_aOption : String(); +} + +String PPDParser::getDefaultInputSlot() const +{ + if( m_pDefaultInputSlot ) + return m_pDefaultInputSlot->m_aValue; + return String(); +} + +String PPDParser::getSlot( int nSlot ) const +{ + if( ! m_pInputSlots ) + return String(); + + if( nSlot > 0 && nSlot < m_pInputSlots->countValues() ) + return m_pInputSlots->getValue( nSlot )->m_aOption; + else if( m_pInputSlots->countValues() > 0 ) + return m_pInputSlots->getValue( (sal_uLong)0 )->m_aOption; + + return String(); +} + +String PPDParser::getSlotCommand( int nSlot ) const +{ + if( ! m_pInputSlots ) + return String(); + + if( nSlot > 0 && nSlot < m_pInputSlots->countValues() ) + return m_pInputSlots->getValue( nSlot )->m_aValue; + else if( m_pInputSlots->countValues() > 0 ) + return m_pInputSlots->getValue( (sal_uLong)0 )->m_aValue; + + return String(); +} + +String PPDParser::getSlotCommand( const String& rSlot ) const +{ + if( ! m_pInputSlots ) + return String(); + + for( int i=0; i < m_pInputSlots->countValues(); i++ ) + { + const PPDValue* pValue = m_pInputSlots->getValue( i ); + if( pValue->m_aOption == rSlot ) + return pValue->m_aValue; + } + return String(); +} + +String PPDParser::getPaperDimension( int nPaperDimension ) const +{ + if( ! m_pPaperDimensions ) + return String(); + + if( nPaperDimension > 0 && nPaperDimension < m_pPaperDimensions->countValues() ) + return m_pPaperDimensions->getValue( nPaperDimension )->m_aOption; + else if( m_pPaperDimensions->countValues() > 0 ) + return m_pPaperDimensions->getValue( (sal_uLong)0 )->m_aOption; + + return String(); +} + +String PPDParser::getPaperDimensionCommand( int nPaperDimension ) const +{ + if( ! m_pPaperDimensions ) + return String(); + + if( nPaperDimension > 0 && nPaperDimension < m_pPaperDimensions->countValues() ) + return m_pPaperDimensions->getValue( nPaperDimension )->m_aValue; + else if( m_pPaperDimensions->countValues() > 0 ) + return m_pPaperDimensions->getValue( (sal_uLong)0 )->m_aValue; + + return String(); +} + +String PPDParser::getPaperDimensionCommand( const String& rPaperDimension ) const +{ + if( ! m_pPaperDimensions ) + return String(); + + for( int i=0; i < m_pPaperDimensions->countValues(); i++ ) + { + const PPDValue* pValue = m_pPaperDimensions->getValue( i ); + if( pValue->m_aOption == rPaperDimension ) + return pValue->m_aValue; + } + return String(); +} + +void PPDParser::getResolutionFromString( + const String& rString, + int& rXRes, int& rYRes ) const +{ + int nDPIPos; + + rXRes = rYRes = 300; + + nDPIPos = rString.SearchAscii( "dpi" ); + if( nDPIPos != STRING_NOTFOUND ) + { + int nPos = 0; + if( ( nPos = rString.Search( 'x' ) ) != STRING_NOTFOUND ) + { + rXRes = rString.Copy( 0, nPos ).ToInt32(); + rYRes = rString.GetToken( 1, 'x' ).Erase( nDPIPos - nPos - 1 ).ToInt32(); + } + else + rXRes = rYRes = rString.Copy( 0, nDPIPos ).ToInt32(); + } +} + +void PPDParser::getDefaultResolution( int& rXRes, int& rYRes ) const +{ + if( m_pDefaultResolution ) + { + getResolutionFromString( m_pDefaultResolution->m_aValue, rXRes, rYRes ); + return; + } + + rXRes = 300; + rYRes = 300; +} + +int PPDParser::getResolutions() const +{ + if( ( ! m_pResolutions || m_pResolutions->countValues() == 0 ) && + m_pDefaultResolution ) + return 1; + return m_pResolutions ? m_pResolutions->countValues() : 0; +} + +void PPDParser::getResolution( int nNr, int& rXRes, int& rYRes ) const +{ + if( ( ! m_pResolutions || m_pResolutions->countValues() == 0 ) && m_pDefaultResolution && nNr == 0 ) + { + getDefaultResolution( rXRes, rYRes ); + return; + } + if( ! m_pResolutions ) + return; + + getResolutionFromString( m_pResolutions->getValue( nNr )->m_aOption, + rXRes, rYRes ); +} + +String PPDParser::getResolutionCommand( int nXRes, int nYRes ) const +{ + if( ( ! m_pResolutions || m_pResolutions->countValues() == 0 ) && m_pDefaultResolution ) + return m_pDefaultResolution->m_aValue; + + if( ! m_pResolutions ) + return String(); + + int nX, nY; + for( int i = 0; i < m_pResolutions->countValues(); i++ ) + { + getResolutionFromString( m_pResolutions->getValue( i )->m_aOption, + nX, nY ); + if( nX == nXRes && nY == nYRes ) + return m_pResolutions->getValue( i )->m_aValue; + } + return String(); +} + +String PPDParser::getDefaultDuplexType() const +{ + if( m_pDefaultDuplexType ) + return m_pDefaultDuplexType->m_aValue; + return String(); +} + +String PPDParser::getDuplex( int nDuplex ) const +{ + if( ! m_pDuplexTypes ) + return String(); + + if( nDuplex > 0 && nDuplex < m_pDuplexTypes->countValues() ) + return m_pDuplexTypes->getValue( nDuplex )->m_aOption; + else if( m_pDuplexTypes->countValues() > 0 ) + return m_pDuplexTypes->getValue( (sal_uLong)0 )->m_aOption; + + return String(); +} + +String PPDParser::getDuplexCommand( int nDuplex ) const +{ + if( ! m_pDuplexTypes ) + return String(); + + if( nDuplex > 0 && nDuplex < m_pDuplexTypes->countValues() ) + return m_pDuplexTypes->getValue( nDuplex )->m_aValue; + else if( m_pDuplexTypes->countValues() > 0 ) + return m_pDuplexTypes->getValue( (sal_uLong)0 )->m_aValue; + + return String(); +} + +String PPDParser::getDuplexCommand( const String& rDuplex ) const +{ + if( ! m_pDuplexTypes ) + return String(); + + for( int i=0; i < m_pDuplexTypes->countValues(); i++ ) + { + const PPDValue* pValue = m_pDuplexTypes->getValue( i ); + if( pValue->m_aOption == rDuplex ) + return pValue->m_aValue; + } + return String(); +} + +void PPDParser::getFontAttributes( + int nFont, + String& rEncoding, + String& rCharset ) const +{ + if( m_pFontList && nFont >= 0 && nFont < m_pFontList->countValues() ) + { + String aAttribs = + WhitespaceToSpace( m_pFontList->getValue( nFont )->m_aValue ); + rEncoding = GetCommandLineToken( 0, aAttribs ); + rCharset = GetCommandLineToken( 2, aAttribs ); + } +} + +void PPDParser::getFontAttributes( + const String& rFont, + String& rEncoding, + String& rCharset ) const +{ + if( m_pFontList ) + { + for( int i = 0; i < m_pFontList->countValues(); i++ ) + if( m_pFontList->getValue( i )->m_aOption == rFont ) + getFontAttributes( i, rEncoding, rCharset ); + } +} + +String PPDParser::getFont( int nFont ) const +{ + if( ! m_pFontList ) + return String(); + + if( nFont >=0 && nFont < m_pFontList->countValues() ) + return m_pFontList->getValue( nFont )->m_aOption; + return String(); +} + +rtl::OUString PPDParser::translateKey( const rtl::OUString& i_rKey, + const com::sun::star::lang::Locale& i_rLocale ) const +{ + rtl::OUString aResult( m_pTranslator->translateKey( i_rKey, i_rLocale ) ); + if( aResult.getLength() == 0 ) + aResult = i_rKey; + return aResult; +} + +rtl::OUString PPDParser::translateOption( const rtl::OUString& i_rKey, + const rtl::OUString& i_rOption, + const com::sun::star::lang::Locale& i_rLocale ) const +{ + rtl::OUString aResult( m_pTranslator->translateOption( i_rKey, i_rOption, i_rLocale ) ); + if( aResult.getLength() == 0 ) + aResult = i_rOption; + return aResult; +} + +rtl::OUString PPDParser::translateValue( const rtl::OUString& i_rKey, + const rtl::OUString& i_rOption, + const rtl::OUString& i_rValue, + const com::sun::star::lang::Locale& i_rLocale ) const +{ + rtl::OUString aResult( m_pTranslator->translateValue( i_rKey, i_rOption, i_rValue, i_rLocale ) ); + if( aResult.getLength() == 0 ) + aResult = i_rValue; + return aResult; +} + +/* + * PPDKey + */ + +PPDKey::PPDKey( const String& rKey ) : + m_aKey( rKey ), + m_pDefaultValue( NULL ), + m_bQueryValue( false ), + m_bUIOption( false ), + m_eUIType( PickOne ), + m_nOrderDependency( 100 ), + m_eSetupType( AnySetup ) +{ +} + +// ------------------------------------------------------------------- + +PPDKey::~PPDKey() +{ +} + +// ------------------------------------------------------------------- + +const PPDValue* PPDKey::getValue( int n ) const +{ + return ((unsigned int)n < m_aOrderedValues.size() && n >= 0) ? m_aOrderedValues[n] : NULL; +} + +// ------------------------------------------------------------------- + +const PPDValue* PPDKey::getValue( const String& rOption ) const +{ + PPDKey::hash_type::const_iterator it = m_aValues.find( rOption ); + return it != m_aValues.end() ? &it->second : NULL; +} + +// ------------------------------------------------------------------- + +const PPDValue* PPDKey::getValueCaseInsensitive( const String& rOption ) const +{ + const PPDValue* pValue = getValue( rOption ); + if( ! pValue ) + { + for( size_t n = 0; n < m_aOrderedValues.size() && ! pValue; n++ ) + if( m_aOrderedValues[n]->m_aOption.EqualsIgnoreCaseAscii( rOption ) ) + pValue = m_aOrderedValues[n]; + } + + return pValue; +} + +// ------------------------------------------------------------------- + +void PPDKey::eraseValue( const String& rOption ) +{ + PPDKey::hash_type::iterator it = m_aValues.find( rOption ); + if( it == m_aValues.end() ) + return; + + for( PPDKey::value_type::iterator vit = m_aOrderedValues.begin(); vit != m_aOrderedValues.end(); ++vit ) + { + if( *vit == &(it->second ) ) + { + m_aOrderedValues.erase( vit ); + break; + } + } + m_aValues.erase( it ); +} + +// ------------------------------------------------------------------- + +PPDValue* PPDKey::insertValue( const String& rOption ) +{ + if( m_aValues.find( rOption ) != m_aValues.end() ) + return NULL; + + PPDValue aValue; + aValue.m_aOption = rOption; + m_aValues[ rOption ] = aValue; + PPDValue* pValue = &m_aValues[rOption]; + m_aOrderedValues.push_back( pValue ); + return pValue; +} + +// ------------------------------------------------------------------- + +/* + * PPDContext + */ + +PPDContext::PPDContext( const PPDParser* pParser ) : + m_pParser( pParser ) +{ +} + +// ------------------------------------------------------------------- + +PPDContext& PPDContext::operator=( const PPDContext& rCopy ) +{ + m_pParser = rCopy.m_pParser; + m_aCurrentValues = rCopy.m_aCurrentValues; + return *this; +} + +// ------------------------------------------------------------------- + +PPDContext::~PPDContext() +{ +} + +// ------------------------------------------------------------------- + +const PPDKey* PPDContext::getModifiedKey( int n ) const +{ + hash_type::const_iterator it; + for( it = m_aCurrentValues.begin(); it != m_aCurrentValues.end() && n--; ++it ) + ; + return it != m_aCurrentValues.end() ? it->first : NULL; +} + +// ------------------------------------------------------------------- + +void PPDContext::setParser( const PPDParser* pParser ) +{ + if( pParser != m_pParser ) + { + m_aCurrentValues.clear(); + m_pParser = pParser; + } +} + +// ------------------------------------------------------------------- + +const PPDValue* PPDContext::getValue( const PPDKey* pKey ) const +{ + if( ! m_pParser ) + return NULL; + + hash_type::const_iterator it; + it = m_aCurrentValues.find( pKey ); + if( it != m_aCurrentValues.end() ) + return it->second; + + if( ! m_pParser->hasKey( pKey ) ) + return NULL; + + const PPDValue* pValue = pKey->getDefaultValue(); + if( ! pValue ) + pValue = pKey->getValue( 0 ); + + return pValue; +} + +// ------------------------------------------------------------------- + +const PPDValue* PPDContext::setValue( const PPDKey* pKey, const PPDValue* pValue, bool bDontCareForConstraints ) +{ + if( ! m_pParser || ! pKey ) + return NULL; + + // pValue can be NULL - it means ignore this option + + if( ! m_pParser->hasKey( pKey ) ) + return NULL; + + // check constraints + if( pValue ) + { + if( bDontCareForConstraints ) + { + m_aCurrentValues[ pKey ] = pValue; + } + else if( checkConstraints( pKey, pValue, true ) ) + { + m_aCurrentValues[ pKey ] = pValue; + + // after setting this value, check all constraints ! + hash_type::iterator it = m_aCurrentValues.begin(); + while( it != m_aCurrentValues.end() ) + { + if( it->first != pKey && + ! checkConstraints( it->first, it->second, false ) ) + { +#ifdef __DEBUG + fprintf( stderr, "PPDContext::setValue: option %s (%s) is constrained after setting %s to %s\n", + it->first->getKey().GetStr(), + it->second->m_aOption.GetStr(), + pKey->getKey().GetStr(), + pValue->m_aOption.GetStr() ); +#endif + resetValue( it->first, true ); + it = m_aCurrentValues.begin(); + } + else + ++it; + } + } + } + else + m_aCurrentValues[ pKey ] = NULL; + + return pValue; +} + +// ------------------------------------------------------------------- + +bool PPDContext::checkConstraints( const PPDKey* pKey, const PPDValue* pValue ) +{ + if( ! m_pParser || ! pKey || ! pValue ) + return false; + + // ensure that this key is already in the list if it exists at all + if( m_aCurrentValues.find( pKey ) != m_aCurrentValues.end() ) + return checkConstraints( pKey, pValue, false ); + + // it is not in the list, insert it temporarily + bool bRet = false; + if( m_pParser->hasKey( pKey ) ) + { + const PPDValue* pDefValue = pKey->getDefaultValue(); + m_aCurrentValues[ pKey ] = pDefValue; + bRet = checkConstraints( pKey, pValue, false ); + m_aCurrentValues.erase( pKey ); + } + + return bRet; +} + +// ------------------------------------------------------------------- + +bool PPDContext::resetValue( const PPDKey* pKey, bool bDefaultable ) +{ + if( ! pKey || ! m_pParser || ! m_pParser->hasKey( pKey ) ) + return false; + + const PPDValue* pResetValue = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "None" ) ) ); + if( ! pResetValue ) + pResetValue = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "False" ) ) ); + if( ! pResetValue && bDefaultable ) + pResetValue = pKey->getDefaultValue(); + + bool bRet = pResetValue ? ( setValue( pKey, pResetValue ) == pResetValue ? true : false ) : false; + + return bRet; +} + +// ------------------------------------------------------------------- + +bool PPDContext::checkConstraints( const PPDKey* pKey, const PPDValue* pNewValue, bool bDoReset ) +{ + if( ! pNewValue ) + return true; + + // sanity checks + if( ! m_pParser ) + return false; + + if( pKey->getValue( pNewValue->m_aOption ) != pNewValue ) + return false; + + // None / False and the default can always be set, but be careful ! + // setting them might influence constrained values + if( pNewValue->m_aOption.EqualsAscii( "None" ) || pNewValue->m_aOption.EqualsAscii( "False" ) || + pNewValue == pKey->getDefaultValue() ) + return true; + + const ::std::list< PPDParser::PPDConstraint >& rConstraints( m_pParser->getConstraints() ); + for( ::std::list< PPDParser::PPDConstraint >::const_iterator it = rConstraints.begin(); it != rConstraints.end(); ++it ) + { + const PPDKey* pLeft = it->m_pKey1; + const PPDKey* pRight = it->m_pKey2; + if( ! pLeft || ! pRight || ( pKey != pLeft && pKey != pRight ) ) + continue; + + const PPDKey* pOtherKey = pKey == pLeft ? pRight : pLeft; + const PPDValue* pOtherKeyOption = pKey == pLeft ? it->m_pOption2 : it->m_pOption1; + const PPDValue* pKeyOption = pKey == pLeft ? it->m_pOption1 : it->m_pOption2; + + // syntax *Key1 option1 *Key2 option2 + if( pKeyOption && pOtherKeyOption ) + { + if( pNewValue != pKeyOption ) + continue; + if( pOtherKeyOption == getValue( pOtherKey ) ) + { + return false; + } + } + // syntax *Key1 option *Key2 or *Key1 *Key2 option + else if( pOtherKeyOption || pKeyOption ) + { + if( pKeyOption ) + { + if( ! ( pOtherKeyOption = getValue( pOtherKey ) ) ) + continue; // this should not happen, PPD broken + + if( pKeyOption == pNewValue && + ! pOtherKeyOption->m_aOption.EqualsAscii( "None" ) && + ! pOtherKeyOption->m_aOption.EqualsAscii( "False" ) ) + { + // check if the other value can be reset and + // do so if possible + if( bDoReset && resetValue( pOtherKey ) ) + continue; + + return false; + } + } + else if( pOtherKeyOption ) + { + if( getValue( pOtherKey ) == pOtherKeyOption && + ! pNewValue->m_aOption.EqualsAscii( "None" ) && + ! pNewValue->m_aOption.EqualsAscii( "False" ) ) + return false; + } + else + { + // this should not happen, PPD is broken + } + } + // syntax *Key1 *Key2 + else + { + const PPDValue* pOtherValue = getValue( pOtherKey ); + if( ! pOtherValue->m_aOption.EqualsAscii( "None" ) && + ! pOtherValue->m_aOption.EqualsAscii( "False" ) && + ! pNewValue->m_aOption.EqualsAscii( "None" ) && + ! pNewValue->m_aOption.EqualsAscii( "False" ) ) + return false; + } + } + return true; +} + +// ------------------------------------------------------------------- + +void PPDContext::getUnconstrainedValues( const PPDKey* pKey, ::std::list< const PPDValue* >& rValues ) +{ + rValues.clear(); + + if( ! m_pParser || ! pKey || ! m_pParser->hasKey( pKey ) ) + return; + + int nValues = pKey->countValues(); + for( int i = 0; i < nValues; i++ ) + { + const PPDValue* pValue = pKey->getValue( i ); + if( checkConstraints( pKey, pValue ) ) + rValues.push_back( pValue ); + } +} + + +// ------------------------------------------------------------------- + +void* PPDContext::getStreamableBuffer( sal_uLong& rBytes ) const +{ + rBytes = 0; + if( ! m_aCurrentValues.size() ) + return NULL; + hash_type::const_iterator it; + for( it = m_aCurrentValues.begin(); it != m_aCurrentValues.end(); ++it ) + { + ByteString aCopy( it->first->getKey(), RTL_TEXTENCODING_MS_1252 ); + rBytes += aCopy.Len(); + rBytes += 1; // for ':' + if( it->second ) + { + aCopy = ByteString( it->second->m_aOption, RTL_TEXTENCODING_MS_1252 ); + rBytes += aCopy.Len(); + } + else + rBytes += 4; + rBytes += 1; // for '\0' + } + rBytes += 1; + void* pBuffer = new char[ rBytes ]; + memset( pBuffer, 0, rBytes ); + char* pRun = (char*)pBuffer; + for( it = m_aCurrentValues.begin(); it != m_aCurrentValues.end(); ++it ) + { + ByteString aCopy( it->first->getKey(), RTL_TEXTENCODING_MS_1252 ); + int nBytes = aCopy.Len(); + memcpy( pRun, aCopy.GetBuffer(), nBytes ); + pRun += nBytes; + *pRun++ = ':'; + if( it->second ) + aCopy = ByteString( it->second->m_aOption, RTL_TEXTENCODING_MS_1252 ); + else + aCopy = "*nil"; + nBytes = aCopy.Len(); + memcpy( pRun, aCopy.GetBuffer(), nBytes ); + pRun += nBytes; + + *pRun++ = 0; + } + return pBuffer; +} + +// ------------------------------------------------------------------- + +void PPDContext::rebuildFromStreamBuffer( void* pBuffer, sal_uLong nBytes ) +{ + if( ! m_pParser ) + return; + + m_aCurrentValues.clear(); + + char* pRun = (char*)pBuffer; + while( nBytes && *pRun ) + { + ByteString aLine( pRun ); + int nPos = aLine.Search( ':' ); + if( nPos != STRING_NOTFOUND ) + { + const PPDKey* pKey = m_pParser->getKey( String( aLine.Copy( 0, nPos ), RTL_TEXTENCODING_MS_1252 ) ); + if( pKey ) + { + const PPDValue* pValue = NULL; + String aOption( aLine.Copy( nPos+1 ), RTL_TEXTENCODING_MS_1252 ); + if( ! aOption.EqualsAscii( "*nil" ) ) + pValue = pKey->getValue( aOption ); + m_aCurrentValues[ pKey ] = pValue; +#ifdef __DEBUG + fprintf( stderr, "PPDContext::rebuildFromStreamBuffer: read PPDKeyValue { %s, %s }\n", pKV->m_pKey->getKey().GetStr(), pKV->m_pCurrentValue ? pKV->m_pCurrentValue->m_aOption.GetStr() : "<nil>" ); +#endif + } + } + nBytes -= aLine.Len()+1; + pRun += aLine.Len()+1; + } +} + +// ------------------------------------------------------------------- + +int PPDContext::getRenderResolution() const +{ + // initialize to reasonable default, if parser is not set + int nDPI = 300; + if( m_pParser ) + { + int nDPIx = 300, nDPIy = 300; + const PPDKey* pKey = m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Resolution" ) ) ); + if( pKey ) + { + const PPDValue* pValue = getValue( pKey ); + if( pValue ) + m_pParser->getResolutionFromString( pValue->m_aOption, nDPIx, nDPIy ); + else + m_pParser->getDefaultResolution( nDPIx, nDPIy ); + } + else + m_pParser->getDefaultResolution( nDPIx, nDPIy ); + + nDPI = (nDPIx > nDPIy) ? nDPIx : nDPIy; + } + return nDPI; +} + +// ------------------------------------------------------------------- + +void PPDContext::getPageSize( String& rPaper, int& rWidth, int& rHeight ) const +{ + // initialize to reasonable default, if parser is not set + rPaper = String( RTL_CONSTASCII_USTRINGPARAM( "A4" ) ); + rWidth = 595; + rHeight = 842; + if( m_pParser ) + { + const PPDKey* pKey = m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) ); + if( pKey ) + { + const PPDValue* pValue = getValue( pKey ); + if( pValue ) + { + rPaper = pValue->m_aOption; + m_pParser->getPaperDimension( rPaper, rWidth, rHeight ); + } + else + { + rPaper = m_pParser->getDefaultPaperDimension(); + m_pParser->getDefaultPaperDimension( rWidth, rHeight ); + } + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/printer/printerinfomanager.cxx b/vcl/unx/generic/printer/printerinfomanager.cxx new file mode 100644 index 000000000000..4a7374c41b7b --- /dev/null +++ b/vcl/unx/generic/printer/printerinfomanager.cxx @@ -0,0 +1,1438 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <unistd.h> +#include <sys/wait.h> +#include <signal.h> + +#include "cupsmgr.hxx" +#include "vcl/fontmanager.hxx" +#include "vcl/strhelper.hxx" + +#include "unx/saldata.hxx" + +#include "tools/urlobj.hxx" +#include "tools/stream.hxx" +#include "tools/debug.hxx" +#include "tools/config.hxx" + +#include "i18npool/paper.hxx" + +#include "rtl/strbuf.hxx" +#include <sal/macros.h> + +#include "osl/thread.hxx" +#include "osl/mutex.hxx" +#include "osl/process.h" + +#include <boost/scoped_ptr.hpp> + +// filename of configuration files +#define PRINT_FILENAME "psprint.conf" +// the group of the global defaults +#define GLOBAL_DEFAULTS_GROUP "__Global_Printer_Defaults__" + +#include <boost/unordered_set.hpp> + +using namespace psp; +using namespace osl; + +using ::rtl::OUString; +using ::rtl::OString; +using ::rtl::OStringToOUString; +using ::rtl::OUStringHash; + +namespace psp +{ + class SystemQueueInfo : public Thread + { + mutable Mutex m_aMutex; + bool m_bChanged; + std::list< PrinterInfoManager::SystemPrintQueue > + m_aQueues; + OUString m_aCommand; + + virtual void run(); + + public: + SystemQueueInfo(); + ~SystemQueueInfo(); + + bool hasChanged() const; + OUString getCommand() const; + + // sets changed status to false; therefore not const + void getSystemQueues( std::list< PrinterInfoManager::SystemPrintQueue >& rQueues ); + }; +} // namespace + +/* +* class PrinterInfoManager +*/ + +// ----------------------------------------------------------------- + +PrinterInfoManager& PrinterInfoManager::get() +{ + SalData* pSalData = GetSalData(); + + if( ! pSalData->m_pPIManager ) + { + pSalData->m_pPIManager = CUPSManager::tryLoadCUPS(); + if( ! pSalData->m_pPIManager ) + pSalData->m_pPIManager = new PrinterInfoManager(); + + pSalData->m_pPIManager->initialize(); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "PrinterInfoManager::get create Manager of type %d\n", pSalData->m_pPIManager->getType() ); +#endif + } + + return *pSalData->m_pPIManager; +} + +void PrinterInfoManager::release() +{ + SalData* pSalData = GetSalData(); + delete pSalData->m_pPIManager; + pSalData->m_pPIManager = NULL; +} + +// ----------------------------------------------------------------- + +PrinterInfoManager::PrinterInfoManager( Type eType ) : + m_pQueueInfo( NULL ), + m_eType( eType ), + m_bUseIncludeFeature( false ), + m_bUseJobPatch( true ), + m_aSystemDefaultPaper( RTL_CONSTASCII_USTRINGPARAM( "A4" ) ), + m_bDisableCUPS( false ) +{ + if( eType == Default ) + m_pQueueInfo = new SystemQueueInfo(); + initSystemDefaultPaper(); +} + +// ----------------------------------------------------------------- + +PrinterInfoManager::~PrinterInfoManager() +{ + delete m_pQueueInfo; + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "PrinterInfoManager: destroyed Manager of type %d\n", getType() ); + #endif +} + +// ----------------------------------------------------------------- + +bool PrinterInfoManager::isCUPSDisabled() const +{ + return m_bDisableCUPS; +} + +// ----------------------------------------------------------------- + +void PrinterInfoManager::setCUPSDisabled( bool bDisable ) +{ + m_bDisableCUPS = bDisable; + writePrinterConfig(); + // actually we know the printers changed + // however this triggers reinitialization the right way + checkPrintersChanged( true ); +} + +// ----------------------------------------------------------------- + +void PrinterInfoManager::initSystemDefaultPaper() +{ + m_aSystemDefaultPaper = rtl::OStringToOUString( + PaperInfo::toPSName(PaperInfo::getSystemDefaultPaper().getPaper()), + RTL_TEXTENCODING_UTF8); +} + +// ----------------------------------------------------------------- + +bool PrinterInfoManager::checkPrintersChanged( bool bWait ) +{ + // check if files were created, deleted or modified since initialize() + ::std::list< WatchFile >::const_iterator it; + bool bChanged = false; + for( it = m_aWatchFiles.begin(); it != m_aWatchFiles.end() && ! bChanged; ++it ) + { + DirectoryItem aItem; + if( DirectoryItem::get( it->m_aFilePath, aItem ) ) + { + if( it->m_aModified.Seconds != 0 ) + bChanged = true; // file probably has vanished + } + else + { + FileStatus aStatus( FileStatusMask_ModifyTime ); + if( aItem.getFileStatus( aStatus ) ) + bChanged = true; // unlikely but not impossible + else + { + TimeValue aModified = aStatus.getModifyTime(); + if( aModified.Seconds != it->m_aModified.Seconds ) + bChanged = true; + } + } + } + + if( bWait && m_pQueueInfo ) + { + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "syncing printer discovery thread\n" ); + #endif + m_pQueueInfo->join(); + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "done: syncing printer discovery thread\n" ); + #endif + } + + if( ! bChanged && m_pQueueInfo ) + bChanged = m_pQueueInfo->hasChanged(); + if( bChanged ) + { + initialize(); + } + + return bChanged; +} + +// ----------------------------------------------------------------- + +void PrinterInfoManager::initialize() +{ + m_bUseIncludeFeature = false; + rtl_TextEncoding aEncoding = gsl_getSystemTextEncoding(); + m_aPrinters.clear(); + m_aWatchFiles.clear(); + OUString aDefaultPrinter; + + // first initialize the global defaults + // have to iterate over all possible files + // there should be only one global setup section in all + // available config files + m_aGlobalDefaults = PrinterInfo(); + + // need a parser for the PPDContext. generic printer should do. + m_aGlobalDefaults.m_pParser = PPDParser::getParser( String( RTL_CONSTASCII_USTRINGPARAM( "SGENPRT" ) ) ); + m_aGlobalDefaults.m_aContext.setParser( m_aGlobalDefaults.m_pParser ); + m_aGlobalDefaults.m_bPerformFontSubstitution = true; + m_bDisableCUPS = false; + + if( ! m_aGlobalDefaults.m_pParser ) + { + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "Error: no default PPD file SGENPRT available, shutting down psprint...\n" ); + #endif + return; + } + + std::list< OUString > aDirList; + psp::getPrinterPathList( aDirList, NULL ); + std::list< OUString >::const_iterator print_dir_it; + for( print_dir_it = aDirList.begin(); print_dir_it != aDirList.end(); ++print_dir_it ) + { + INetURLObject aFile( *print_dir_it, INET_PROT_FILE, INetURLObject::ENCODE_ALL ); + aFile.Append( String( RTL_CONSTASCII_USTRINGPARAM( PRINT_FILENAME ) ) ); + Config aConfig( aFile.PathToFileName() ); + if( aConfig.HasGroup( GLOBAL_DEFAULTS_GROUP ) ) + { + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "found global defaults in %s\n", OUStringToOString( aFile.PathToFileName(), RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); + #endif + aConfig.SetGroup( GLOBAL_DEFAULTS_GROUP ); + + ByteString aValue( aConfig.ReadKey( "Copies" ) ); + if( aValue.Len() ) + m_aGlobalDefaults.m_nCopies = aValue.ToInt32(); + + aValue = aConfig.ReadKey( "Orientation" ); + if( aValue.Len() ) + m_aGlobalDefaults.m_eOrientation = aValue.EqualsIgnoreCaseAscii( "Landscape" ) ? orientation::Landscape : orientation::Portrait; + + aValue = aConfig.ReadKey( "MarginAdjust" ); + if( aValue.Len() ) + { + m_aGlobalDefaults.m_nLeftMarginAdjust = aValue.GetToken( 0, ',' ).ToInt32(); + m_aGlobalDefaults.m_nRightMarginAdjust = aValue.GetToken( 1, ',' ).ToInt32(); + m_aGlobalDefaults.m_nTopMarginAdjust = aValue.GetToken( 2, ',' ).ToInt32(); + m_aGlobalDefaults.m_nBottomMarginAdjust = aValue.GetToken( 3, ',' ).ToInt32(); + } + + aValue = aConfig.ReadKey( "ColorDepth", "24" ); + if( aValue.Len() ) + m_aGlobalDefaults.m_nColorDepth = aValue.ToInt32(); + + aValue = aConfig.ReadKey( "ColorDevice" ); + if( aValue.Len() ) + m_aGlobalDefaults.m_nColorDevice = aValue.ToInt32(); + + aValue = aConfig.ReadKey( "PSLevel" ); + if( aValue.Len() ) + m_aGlobalDefaults.m_nPSLevel = aValue.ToInt32(); + + aValue = aConfig.ReadKey( "PDFDevice" ); + if( aValue.Len() ) + m_aGlobalDefaults.m_nPDFDevice = aValue.ToInt32(); + + aValue = aConfig.ReadKey( "PerformFontSubstitution" ); + if( aValue.Len() ) + { + if( ! aValue.Equals( "0" ) && ! aValue.EqualsIgnoreCaseAscii( "false" ) ) + m_aGlobalDefaults.m_bPerformFontSubstitution = true; + else + m_aGlobalDefaults.m_bPerformFontSubstitution = false; + } + + aValue = aConfig.ReadKey( "DisableCUPS" ); + if( aValue.Len() ) + { + if( aValue.Equals( "1" ) || aValue.EqualsIgnoreCaseAscii( "true" ) ) + m_bDisableCUPS = true; + else + m_bDisableCUPS = false; + } + + // get the PPDContext of global JobData + for( int nKey = 0; nKey < aConfig.GetKeyCount(); nKey++ ) + { + ByteString aKey( aConfig.GetKeyName( nKey ) ); + if( aKey.CompareTo( "PPD_", 4 ) == COMPARE_EQUAL ) + { + aValue = aConfig.ReadKey( aKey ); + const PPDKey* pKey = m_aGlobalDefaults.m_pParser->getKey( String( aKey.Copy( 4 ), RTL_TEXTENCODING_ISO_8859_1 ) ); + if( pKey ) + { + m_aGlobalDefaults.m_aContext. + setValue( pKey, + aValue.Equals( "*nil" ) ? NULL : pKey->getValue( String( aValue, RTL_TEXTENCODING_ISO_8859_1 ) ), + sal_True ); + } + } + else if( aKey.Len() > 10 && aKey.CompareTo("SubstFont_", 10 ) == COMPARE_EQUAL ) + { + aValue = aConfig.ReadKey( aKey ); + m_aGlobalDefaults.m_aFontSubstitutes[ OStringToOUString( aKey.Copy( 10 ), RTL_TEXTENCODING_ISO_8859_1 ) ] = OStringToOUString( aValue, RTL_TEXTENCODING_ISO_8859_1 ); + } + } + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "global settings: fontsubst = %s, %" SAL_PRI_SIZET "u substitutes\n", m_aGlobalDefaults.m_bPerformFontSubstitution ? "true" : "false", m_aGlobalDefaults.m_aFontSubstitutes.size() ); + #endif + } + } + setDefaultPaper( m_aGlobalDefaults.m_aContext ); + fillFontSubstitutions( m_aGlobalDefaults ); + + // now collect all available printers + for( print_dir_it = aDirList.begin(); print_dir_it != aDirList.end(); ++print_dir_it ) + { + INetURLObject aDir( *print_dir_it, INET_PROT_FILE, INetURLObject::ENCODE_ALL ); + INetURLObject aFile( aDir ); + aFile.Append( String( RTL_CONSTASCII_USTRINGPARAM( PRINT_FILENAME ) ) ); + + // check directory validity + OUString aUniPath; + FileBase::getFileURLFromSystemPath( aDir.PathToFileName(), aUniPath ); + Directory aDirectory( aUniPath ); + if( aDirectory.open() ) + continue; + aDirectory.close(); + + + FileBase::getFileURLFromSystemPath( aFile.PathToFileName(), aUniPath ); + FileStatus aStatus( FileStatusMask_ModifyTime ); + DirectoryItem aItem; + + // setup WatchFile list + WatchFile aWatchFile; + aWatchFile.m_aFilePath = aUniPath; + if( ! DirectoryItem::get( aUniPath, aItem ) && + ! aItem.getFileStatus( aStatus ) ) + { + aWatchFile.m_aModified = aStatus.getModifyTime(); + } + else + { + aWatchFile.m_aModified.Seconds = 0; + aWatchFile.m_aModified.Nanosec = 0; + } + m_aWatchFiles.push_back( aWatchFile ); + + Config aConfig( aFile.PathToFileName() ); + for( int nGroup = 0; nGroup < aConfig.GetGroupCount(); nGroup++ ) + { + aConfig.SetGroup( aConfig.GetGroupName( nGroup ) ); + ByteString aValue = aConfig.ReadKey( "Printer" ); + if( aValue.Len() ) + { + OUString aPrinterName; + + int nNamePos = aValue.Search( '/' ); + // check for valid value of "Printer" + if( nNamePos == STRING_NOTFOUND ) + continue; + + Printer aPrinter; + // initialize to global defaults + aPrinter.m_aInfo = m_aGlobalDefaults; + // global settings do not default the printer substitution + // list ! the substitution list in there is only used for + // newly created printers + aPrinter.m_aInfo.m_aFontSubstitutes.clear(); + aPrinter.m_aInfo.m_aFontSubstitutions.clear(); + + aPrinterName = String( aValue.Copy( nNamePos+1 ), RTL_TEXTENCODING_UTF8 ); + aPrinter.m_aInfo.m_aPrinterName = aPrinterName; + aPrinter.m_aInfo.m_aDriverName = String( aValue.Copy( 0, nNamePos ), RTL_TEXTENCODING_UTF8 ); + + // set parser, merge settings + // don't do this for CUPS printers as this is done + // by the CUPS system itself + if( aPrinter.m_aInfo.m_aDriverName.compareToAscii( "CUPS:", 5 ) != 0 ) + { + aPrinter.m_aInfo.m_pParser = PPDParser::getParser( aPrinter.m_aInfo.m_aDriverName ); + aPrinter.m_aInfo.m_aContext.setParser( aPrinter.m_aInfo.m_pParser ); + // note: setParser also purges the context + + // ignore this printer if its driver is not found + if( ! aPrinter.m_aInfo.m_pParser ) + continue; + + // merge the ppd context keys if the printer has the same keys and values + // this is a bit tricky, since it involves mixing two PPDs + // without constraints which might end up badly + // this feature should be use with caution + // it is mainly to select default paper sizes for new printers + for( int nPPDValueModified = 0; nPPDValueModified < m_aGlobalDefaults.m_aContext.countValuesModified(); nPPDValueModified++ ) + { + const PPDKey* pDefKey = m_aGlobalDefaults.m_aContext.getModifiedKey( nPPDValueModified ); + const PPDValue* pDefValue = m_aGlobalDefaults.m_aContext.getValue( pDefKey ); + const PPDKey* pPrinterKey = pDefKey ? aPrinter.m_aInfo.m_pParser->getKey( pDefKey->getKey() ) : NULL; + if( pDefKey && pPrinterKey ) + // at least the options exist in both PPDs + { + if( pDefValue ) + { + const PPDValue* pPrinterValue = pPrinterKey->getValue( pDefValue->m_aOption ); + if( pPrinterValue ) + // the printer has a corresponding option for the key + aPrinter.m_aInfo.m_aContext.setValue( pPrinterKey, pPrinterValue ); + } + else + aPrinter.m_aInfo.m_aContext.setValue( pPrinterKey, NULL ); + } + } + + aValue = aConfig.ReadKey( "Command" ); + // no printer without a command + if( ! aValue.Len() ) + { + /* TODO: + * porters: please append your platform to the Solaris + * case if your platform has SystemV printing per default. + */ + #if defined SOLARIS + aValue = "lp"; + #else + aValue = "lpr"; + #endif + } + aPrinter.m_aInfo.m_aCommand = String( aValue, RTL_TEXTENCODING_UTF8 ); + } + + aValue = aConfig.ReadKey( "QuickCommand" ); + aPrinter.m_aInfo.m_aQuickCommand = String( aValue, RTL_TEXTENCODING_UTF8 ); + + aValue = aConfig.ReadKey( "Features" ); + aPrinter.m_aInfo.m_aFeatures = String( aValue, RTL_TEXTENCODING_UTF8 ); + + // override the settings in m_aGlobalDefaults if keys exist + aValue = aConfig.ReadKey( "DefaultPrinter" ); + if( ! aValue.Equals( "0" ) && ! aValue.EqualsIgnoreCaseAscii( "false" ) ) + aDefaultPrinter = aPrinterName; + + aValue = aConfig.ReadKey( "Location" ); + aPrinter.m_aInfo.m_aLocation = String( aValue, RTL_TEXTENCODING_UTF8 ); + + aValue = aConfig.ReadKey( "Comment" ); + aPrinter.m_aInfo.m_aComment = String( aValue, RTL_TEXTENCODING_UTF8 ); + + aValue = aConfig.ReadKey( "Copies" ); + if( aValue.Len() ) + aPrinter.m_aInfo.m_nCopies = aValue.ToInt32(); + + aValue = aConfig.ReadKey( "Orientation" ); + if( aValue.Len() ) + aPrinter.m_aInfo.m_eOrientation = aValue.EqualsIgnoreCaseAscii( "Landscape" ) ? orientation::Landscape : orientation::Portrait; + + aValue = aConfig.ReadKey( "MarginAdjust" ); + if( aValue.Len() ) + { + aPrinter.m_aInfo.m_nLeftMarginAdjust = aValue.GetToken( 0, ',' ).ToInt32(); + aPrinter.m_aInfo.m_nRightMarginAdjust = aValue.GetToken( 1, ',' ).ToInt32(); + aPrinter.m_aInfo.m_nTopMarginAdjust = aValue.GetToken( 2, ',' ).ToInt32(); + aPrinter.m_aInfo.m_nBottomMarginAdjust = aValue.GetToken( 3, ',' ).ToInt32(); + } + + aValue = aConfig.ReadKey( "ColorDepth" ); + if( aValue.Len() ) + aPrinter.m_aInfo.m_nColorDepth = aValue.ToInt32(); + + aValue = aConfig.ReadKey( "ColorDevice" ); + if( aValue.Len() ) + aPrinter.m_aInfo.m_nColorDevice = aValue.ToInt32(); + + aValue = aConfig.ReadKey( "PSLevel" ); + if( aValue.Len() ) + aPrinter.m_aInfo.m_nPSLevel = aValue.ToInt32(); + + aValue = aConfig.ReadKey( "PDFDevice" ); + if( aValue.Len() ) + aPrinter.m_aInfo.m_nPDFDevice = aValue.ToInt32(); + + aValue = aConfig.ReadKey( "PerformFontSubstitution" ); + if( ! aValue.Equals( "0" ) && ! aValue.EqualsIgnoreCaseAscii( "false" ) ) + aPrinter.m_aInfo.m_bPerformFontSubstitution = true; + else + aPrinter.m_aInfo.m_bPerformFontSubstitution = false; + + // now iterate over all keys to extract multi key information: + // 1. PPDContext information + // 2. Font substitution table + for( int nKey = 0; nKey < aConfig.GetKeyCount(); nKey++ ) + { + ByteString aKey( aConfig.GetKeyName( nKey ) ); + if( aKey.CompareTo( "PPD_", 4 ) == COMPARE_EQUAL && aPrinter.m_aInfo.m_pParser ) + { + aValue = aConfig.ReadKey( aKey ); + const PPDKey* pKey = aPrinter.m_aInfo.m_pParser->getKey( String( aKey.Copy( 4 ), RTL_TEXTENCODING_ISO_8859_1 ) ); + if( pKey ) + { + aPrinter.m_aInfo.m_aContext. + setValue( pKey, + aValue.Equals( "*nil" ) ? NULL : pKey->getValue( String( aValue, RTL_TEXTENCODING_ISO_8859_1 ) ), + sal_True ); + } + } + else if( aKey.Len() > 10 && aKey.CompareTo("SubstFont_", 10 ) == COMPARE_EQUAL ) + { + aValue = aConfig.ReadKey( aKey ); + aPrinter.m_aInfo.m_aFontSubstitutes[ OStringToOUString( aKey.Copy( 10 ), RTL_TEXTENCODING_ISO_8859_1 ) ] = OStringToOUString( aValue, RTL_TEXTENCODING_ISO_8859_1 ); + } + } + + setDefaultPaper( aPrinter.m_aInfo.m_aContext ); + fillFontSubstitutions( aPrinter.m_aInfo ); + + // finally insert printer + FileBase::getFileURLFromSystemPath( aFile.PathToFileName(), aPrinter.m_aFile ); + aPrinter.m_bModified = false; + aPrinter.m_aGroup = aConfig.GetGroupName( nGroup ); + boost::unordered_map< OUString, Printer, OUStringHash >::const_iterator find_it = + m_aPrinters.find( aPrinterName ); + if( find_it != m_aPrinters.end() ) + { + aPrinter.m_aAlternateFiles = find_it->second.m_aAlternateFiles; + aPrinter.m_aAlternateFiles.push_front( find_it->second.m_aFile ); + } + m_aPrinters[ aPrinterName ] = aPrinter; + } + } + } + + // set default printer + if( m_aPrinters.size() ) + { + if( m_aPrinters.find( aDefaultPrinter ) == m_aPrinters.end() ) + aDefaultPrinter = m_aPrinters.begin()->first; + } + else + aDefaultPrinter = OUString(); + m_aDefaultPrinter = aDefaultPrinter; + + if( m_eType != Default ) + return; + + // add a default printer for every available print queue + // merge paper and font substitution from default printer, + // all else from global defaults + PrinterInfo aMergeInfo( m_aGlobalDefaults ); + aMergeInfo.m_aDriverName = String( RTL_CONSTASCII_USTRINGPARAM( "SGENPRT" ) ); + aMergeInfo.m_aFeatures = String( RTL_CONSTASCII_USTRINGPARAM( "autoqueue" ) ); + + if( m_aDefaultPrinter.getLength() ) + { + PrinterInfo aDefaultInfo( getPrinterInfo( m_aDefaultPrinter ) ); + aMergeInfo.m_bPerformFontSubstitution = aDefaultInfo.m_bPerformFontSubstitution; + fillFontSubstitutions( aMergeInfo ); + + const PPDKey* pDefKey = aDefaultInfo.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) ); + const PPDKey* pMergeKey = aMergeInfo.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) ); + const PPDValue* pDefValue = aDefaultInfo.m_aContext.getValue( pDefKey ); + const PPDValue* pMergeValue = pMergeKey ? pMergeKey->getValue( pDefValue->m_aOption ) : NULL; + if( pMergeKey && pMergeValue ) + aMergeInfo.m_aContext.setValue( pMergeKey, pMergeValue ); + } + + getSystemPrintQueues(); + for( ::std::list< SystemPrintQueue >::iterator it = m_aSystemPrintQueues.begin(); it != m_aSystemPrintQueues.end(); ++it ) + { + String aPrinterName( RTL_CONSTASCII_USTRINGPARAM( "<" ) ); + aPrinterName += String( it->m_aQueue ); + aPrinterName.Append( '>' ); + + if( m_aPrinters.find( aPrinterName ) != m_aPrinters.end() ) + // probably user made this one permanent in padmin + continue; + + String aCmd( m_aSystemPrintCommand ); + aCmd.SearchAndReplace( String( RTL_CONSTASCII_USTRINGPARAM( "(PRINTER)" ) ), it->m_aQueue ); + + Printer aPrinter; + + // initialize to merged defaults + aPrinter.m_aInfo = aMergeInfo; + aPrinter.m_aInfo.m_aPrinterName = aPrinterName; + aPrinter.m_aInfo.m_aCommand = aCmd; + aPrinter.m_aInfo.m_aComment = it->m_aComment; + aPrinter.m_aInfo.m_aLocation = it->m_aLocation; + aPrinter.m_bModified = false; + aPrinter.m_aGroup = ByteString( aPrinterName, aEncoding ); //provide group name in case user makes this one permanent in padmin + + m_aPrinters[ aPrinterName ] = aPrinter; + } +} + +// ----------------------------------------------------------------- + +void PrinterInfoManager::listPrinters( ::std::list< OUString >& rList ) const +{ + ::boost::unordered_map< OUString, Printer, OUStringHash >::const_iterator it; + rList.clear(); + for( it = m_aPrinters.begin(); it != m_aPrinters.end(); ++it ) + rList.push_back( it->first ); +} + +// ----------------------------------------------------------------- + +const PrinterInfo& PrinterInfoManager::getPrinterInfo( const OUString& rPrinter ) const +{ + static PrinterInfo aEmptyInfo; + ::boost::unordered_map< OUString, Printer, OUStringHash >::const_iterator it = m_aPrinters.find( rPrinter ); + + DBG_ASSERT( it != m_aPrinters.end(), "Do not ask for info about nonexistent printers" ); + + return it != m_aPrinters.end() ? it->second.m_aInfo : aEmptyInfo; +} + +// ----------------------------------------------------------------- + +void PrinterInfoManager::changePrinterInfo( const OUString& rPrinter, const PrinterInfo& rNewInfo ) +{ + ::boost::unordered_map< OUString, Printer, OUStringHash >::iterator it = m_aPrinters.find( rPrinter ); + + DBG_ASSERT( it != m_aPrinters.end(), "Do not change nonexistant printers" ); + + if( it != m_aPrinters.end() ) + { + it->second.m_aInfo = rNewInfo; + // recalculate font substitutions + fillFontSubstitutions( it->second.m_aInfo ); + it->second.m_bModified = true; + writePrinterConfig(); + } +} + +// ----------------------------------------------------------------- + +// need to check writeability / creatability of config files +static bool checkWriteability( const OUString& rUniPath ) +{ + bool bRet = false; + OUString aSysPath; + FileBase::getSystemPathFromFileURL( rUniPath, aSysPath ); + SvFileStream aStream( aSysPath, STREAM_READ | STREAM_WRITE ); + if( aStream.IsOpen() && aStream.IsWritable() ) + bRet = true; + return bRet; +} + +bool PrinterInfoManager::writePrinterConfig() +{ + // find at least one writeable config + ::boost::unordered_map< OUString, Config*, OUStringHash > files; + ::boost::unordered_map< OUString, int, OUStringHash > rofiles; + ::boost::unordered_map< OUString, Config*, OUStringHash >::iterator file_it; + + for( ::std::list< WatchFile >::const_iterator wit = m_aWatchFiles.begin(); wit != m_aWatchFiles.end(); ++wit ) + { + if( checkWriteability( wit->m_aFilePath ) ) + { + files[ wit->m_aFilePath ] = new Config( wit->m_aFilePath ); + break; + } + } + + if( files.empty() ) + return false; + + Config* pGlobal = files.begin()->second; + pGlobal->SetGroup( GLOBAL_DEFAULTS_GROUP ); + pGlobal->WriteKey( "DisableCUPS", m_bDisableCUPS ? "true" : "false" ); + + ::boost::unordered_map< OUString, Printer, OUStringHash >::iterator it; + for( it = m_aPrinters.begin(); it != m_aPrinters.end(); ++it ) + { + if( ! it->second.m_bModified ) + // printer was not changed, do nothing + continue; + + // don't save autoqueue printers + sal_Int32 nIndex = 0; + bool bAutoQueue = false; + while( nIndex != -1 && ! bAutoQueue ) + { + OUString aToken( it->second.m_aInfo.m_aFeatures.getToken( 0, ',', nIndex ) ); + if( aToken.getLength() && aToken.compareToAscii( "autoqueue" ) == 0 ) + bAutoQueue = true; + } + if( bAutoQueue ) + continue; + + if( it->second.m_aFile.getLength() ) + { + // check if file is writable + if( files.find( it->second.m_aFile ) == files.end() ) + { + bool bInsertToNewFile = false; + // maybe it is simply not inserted yet + if( rofiles.find( it->second.m_aFile ) == rofiles.end() ) + { + if( checkWriteability( it->second.m_aFile ) ) + files[ it->second.m_aFile ] = new Config( it->second.m_aFile ); + else + bInsertToNewFile = true; + } + else + bInsertToNewFile = true; + // original file is read only, insert printer in a new writeable file + if( bInsertToNewFile ) + { + rofiles[ it->second.m_aFile ] = 1; + // update alternate file list + // the remove operation ensures uniqueness of each alternate + it->second.m_aAlternateFiles.remove( it->second.m_aFile ); + it->second.m_aAlternateFiles.remove( files.begin()->first ); + it->second.m_aAlternateFiles.push_front( it->second.m_aFile ); + // update file + it->second.m_aFile = files.begin()->first; + } + } + } + else // a new printer, write it to the first file available + it->second.m_aFile = files.begin()->first; + + if( ! it->second.m_aGroup.getLength() ) // probably a new printer + it->second.m_aGroup = OString( it->first.getStr(), it->first.getLength(), RTL_TEXTENCODING_UTF8 ); + + if( files.find( it->second.m_aFile ) != files.end() ) + { + Config* pConfig = files[ it->second.m_aFile ]; + pConfig->DeleteGroup( it->second.m_aGroup ); // else some old keys may remain + pConfig->SetGroup( it->second.m_aGroup ); + + ByteString aValue( String( it->second.m_aInfo.m_aDriverName ), RTL_TEXTENCODING_UTF8 ); + aValue += '/'; + aValue += ByteString( String( it->first ), RTL_TEXTENCODING_UTF8 ); + pConfig->WriteKey( "Printer", aValue ); + pConfig->WriteKey( "DefaultPrinter", it->first == m_aDefaultPrinter ? "1" : "0" ); + pConfig->WriteKey( "Location", ByteString( String( it->second.m_aInfo.m_aLocation ), RTL_TEXTENCODING_UTF8 ) ); + pConfig->WriteKey( "Comment", ByteString( String( it->second.m_aInfo.m_aComment ), RTL_TEXTENCODING_UTF8 ) ); + pConfig->WriteKey( "Command", ByteString( String( it->second.m_aInfo.m_aCommand ), RTL_TEXTENCODING_UTF8 ) ); + pConfig->WriteKey( "QuickCommand", ByteString( String( it->second.m_aInfo.m_aQuickCommand ), RTL_TEXTENCODING_UTF8 ) ); + pConfig->WriteKey( "Features", ByteString( String( it->second.m_aInfo.m_aFeatures ), RTL_TEXTENCODING_UTF8 ) ); + pConfig->WriteKey( "Copies", ByteString::CreateFromInt32( it->second.m_aInfo.m_nCopies ) ); + pConfig->WriteKey( "Orientation", it->second.m_aInfo.m_eOrientation == orientation::Landscape ? "Landscape" : "Portrait" ); + pConfig->WriteKey( "PSLevel", ByteString::CreateFromInt32( it->second.m_aInfo.m_nPSLevel ) ); + pConfig->WriteKey( "PDFDevice", ByteString::CreateFromInt32( it->second.m_aInfo.m_nPDFDevice ) ); + pConfig->WriteKey( "ColorDevice", ByteString::CreateFromInt32( it->second.m_aInfo.m_nColorDevice ) ); + pConfig->WriteKey( "ColorDepth", ByteString::CreateFromInt32( it->second.m_aInfo.m_nColorDepth ) ); + aValue = ByteString::CreateFromInt32( it->second.m_aInfo.m_nLeftMarginAdjust ); + aValue += ','; + aValue += ByteString::CreateFromInt32( it->second.m_aInfo.m_nRightMarginAdjust ); + aValue += ','; + aValue += ByteString::CreateFromInt32( it->second.m_aInfo.m_nTopMarginAdjust ); + aValue += ','; + aValue += ByteString::CreateFromInt32( it->second.m_aInfo.m_nBottomMarginAdjust ); + pConfig->WriteKey( "MarginAdjust", aValue ); + + if( it->second.m_aInfo.m_aDriverName.compareToAscii( "CUPS:", 5 ) != 0 ) + { + // write PPDContext (not for CUPS) + for( int i = 0; i < it->second.m_aInfo.m_aContext.countValuesModified(); i++ ) + { + const PPDKey* pKey = it->second.m_aInfo.m_aContext.getModifiedKey( i ); + ByteString aKey( "PPD_" ); + aKey += ByteString( pKey->getKey(), RTL_TEXTENCODING_ISO_8859_1 ); + + const PPDValue* pValue = it->second.m_aInfo.m_aContext.getValue( pKey ); + aValue = pValue ? ByteString( pValue->m_aOption, RTL_TEXTENCODING_ISO_8859_1 ) : ByteString( "*nil" ); + pConfig->WriteKey( aKey, aValue ); + } + } + + // write font substitution table + pConfig->WriteKey( "PerformFontSubstitution", it->second.m_aInfo.m_bPerformFontSubstitution ? "true" : "false" ); + for( ::boost::unordered_map< OUString, OUString, OUStringHash >::const_iterator subst = it->second.m_aInfo.m_aFontSubstitutes.begin(); + subst != it->second.m_aInfo.m_aFontSubstitutes.end(); ++subst ) + { + ByteString aKey( "SubstFont_" ); + aKey.Append( OUStringToOString( subst->first, RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); + pConfig->WriteKey( aKey, OUStringToOString( subst->second, RTL_TEXTENCODING_ISO_8859_1 ) ); + } + } + } + + // get rid of Config objects. this also writes any changes + for( file_it = files.begin(); file_it != files.end(); ++file_it ) + delete file_it->second; + + return true; +} + +// ----------------------------------------------------------------- + +bool PrinterInfoManager::addPrinter( const OUString& rPrinterName, const OUString& rDriverName ) +{ + bool bSuccess = false; + + const PPDParser* pParser = NULL; + if( m_aPrinters.find( rPrinterName ) == m_aPrinters.end() && ( pParser = PPDParser::getParser( rDriverName ) ) ) + { + Printer aPrinter; + aPrinter.m_bModified = true; + aPrinter.m_aInfo = m_aGlobalDefaults; + aPrinter.m_aInfo.m_aDriverName = rDriverName; + aPrinter.m_aInfo.m_pParser = pParser; + aPrinter.m_aInfo.m_aContext.setParser( pParser ); + aPrinter.m_aInfo.m_aPrinterName = rPrinterName; + + fillFontSubstitutions( aPrinter.m_aInfo ); + // merge PPD values with global defaults + for( int nPPDValueModified = 0; nPPDValueModified < m_aGlobalDefaults.m_aContext.countValuesModified(); nPPDValueModified++ ) + { + const PPDKey* pDefKey = m_aGlobalDefaults.m_aContext.getModifiedKey( nPPDValueModified ); + const PPDValue* pDefValue = m_aGlobalDefaults.m_aContext.getValue( pDefKey ); + const PPDKey* pPrinterKey = pDefKey ? aPrinter.m_aInfo.m_pParser->getKey( pDefKey->getKey() ) : NULL; + if( pDefKey && pPrinterKey ) + // at least the options exist in both PPDs + { + if( pDefValue ) + { + const PPDValue* pPrinterValue = pPrinterKey->getValue( pDefValue->m_aOption ); + if( pPrinterValue ) + // the printer has a corresponding option for the key + aPrinter.m_aInfo.m_aContext.setValue( pPrinterKey, pPrinterValue ); + } + else + aPrinter.m_aInfo.m_aContext.setValue( pPrinterKey, NULL ); + } + } + + m_aPrinters[ rPrinterName ] = aPrinter; + bSuccess = true; + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "new printer %s, level = %d, pdfdevice = %d, colordevice = %d, depth = %d\n", + OUStringToOString( rPrinterName, osl_getThreadTextEncoding() ).getStr(), + m_aPrinters[rPrinterName].m_aInfo.m_nPSLevel, + m_aPrinters[rPrinterName].m_aInfo.m_nPDFDevice, + m_aPrinters[rPrinterName].m_aInfo.m_nColorDevice, + m_aPrinters[rPrinterName].m_aInfo.m_nColorDepth ); + #endif + // comment: logically one should writePrinterConfig() here + // but immediately after addPrinter() a changePrinterInfo() + // will follow (see padmin code), which writes it again, + // so we can currently save some performance here + } + return bSuccess; +} + +// ----------------------------------------------------------------- + +bool PrinterInfoManager::removePrinter( const OUString& rPrinterName, bool bCheckOnly ) +{ + bool bSuccess = true; + + ::boost::unordered_map< OUString, Printer, OUStringHash >::iterator it = m_aPrinters.find( rPrinterName ); + if( it != m_aPrinters.end() ) + { + if( it->second.m_aFile.getLength() ) + { + // this printer already exists in a config file + + + // check writeability of config file(s) + if( ! checkWriteability( it->second.m_aFile ) ) + bSuccess = false; + else + { + for( std::list< OUString >::const_iterator file_it = it->second.m_aAlternateFiles.begin(); + file_it != it->second.m_aAlternateFiles.end() && bSuccess; ++file_it ) + { + if( ! checkWriteability( *file_it ) ) + bSuccess = false; + } + } + if( bSuccess && ! bCheckOnly ) + { + + Config aConfig( it->second.m_aFile ); + aConfig.DeleteGroup( it->second.m_aGroup ); + aConfig.Flush(); + for( std::list< OUString >::const_iterator file_it = it->second.m_aAlternateFiles.begin(); + file_it != it->second.m_aAlternateFiles.end() && bSuccess; ++file_it ) + { + Config aAltConfig( *file_it ); + aAltConfig.DeleteGroup( it->second.m_aGroup ); + aAltConfig.Flush(); + } + } + } + if( bSuccess && ! bCheckOnly ) + { + m_aPrinters.erase( it ); + // need this here because someone may call + // checkPrintersChanged after the removal + // but then other added printers were not flushed + // to disk, so they are discarded + writePrinterConfig(); + } + } + return bSuccess; +} + +// ----------------------------------------------------------------- + +bool PrinterInfoManager::setDefaultPrinter( const OUString& rPrinterName ) +{ + bool bSuccess = false; + + ::boost::unordered_map< OUString, Printer, OUStringHash >::iterator it = m_aPrinters.find( rPrinterName ); + if( it != m_aPrinters.end() ) + { + bSuccess = true; + it->second.m_bModified = true; + if( ( it = m_aPrinters.find( m_aDefaultPrinter ) ) != m_aPrinters.end() ) + it->second.m_bModified = true; + m_aDefaultPrinter = rPrinterName; + writePrinterConfig(); + } + return bSuccess; +} + +// ----------------------------------------------------------------- +bool PrinterInfoManager::addOrRemovePossible() const +{ + return true; +} + +// ----------------------------------------------------------------- + +void PrinterInfoManager::fillFontSubstitutions( PrinterInfo& rInfo ) const +{ + PrintFontManager& rFontManager( PrintFontManager::get() ); + rInfo.m_aFontSubstitutions.clear(); + + if( ! rInfo.m_bPerformFontSubstitution || + ! rInfo.m_aFontSubstitutes.size() ) + return; + + ::std::list< FastPrintFontInfo > aFonts; + ::boost::unordered_map< OUString, ::std::list< FastPrintFontInfo >, OUStringHash > aPrinterFonts; + rFontManager.getFontListWithFastInfo( aFonts, rInfo.m_pParser ); + + // get builtin fonts + ::std::list< FastPrintFontInfo >::const_iterator it; + for( it = aFonts.begin(); it != aFonts.end(); ++it ) + if( it->m_eType == fonttype::Builtin ) + aPrinterFonts[ it->m_aFamilyName.toAsciiLowerCase() ].push_back( *it ); + + // map lower case, so build a local copy of the font substitutions + ::boost::unordered_map< OUString, OUString, OUStringHash > aSubstitutions; + ::boost::unordered_map< OUString, OUString, OUStringHash >::const_iterator subst; + for( subst = rInfo.m_aFontSubstitutes.begin(); subst != rInfo.m_aFontSubstitutes.end(); ++subst ) + { + OUString aFamily( subst->first.toAsciiLowerCase() ); + // first look if there is a builtin of this family + // in this case override the substitution table + if( aPrinterFonts.find( aFamily ) != aPrinterFonts.end() ) + aSubstitutions[ aFamily ] = aFamily; + else + aSubstitutions[ aFamily ] = subst->second.toAsciiLowerCase(); + } + + + // now find substitutions + for( it = aFonts.begin(); it != aFonts.end(); ++it ) + { + if( it->m_eType != fonttype::Builtin ) + { + OUString aFamily( it->m_aFamilyName.toAsciiLowerCase() ); + subst = aSubstitutions.find( aFamily ); + if( subst != aSubstitutions.end() ) + { + // search a substitution + const ::std::list< FastPrintFontInfo >& rBuiltins( aPrinterFonts[ aSubstitutions[ aFamily ] ] ); + ::std::list< FastPrintFontInfo >::const_iterator builtin; + int nLastMatch = -10000; + fontID nSubstitute = -1; + for( builtin = rBuiltins.begin(); builtin != rBuiltins.end(); ++builtin ) + { + int nMatch = 0; + int nDiff; + if( builtin->m_eItalic == it->m_eItalic ) + nMatch += 8000; + + nDiff = builtin->m_eWeight - it->m_eWeight; + nDiff = nDiff < 0 ? -nDiff : nDiff; + nMatch += 4000 - 1000*nDiff; + + nDiff = builtin->m_eWidth - it->m_eWidth; + nDiff = nDiff < 0 ? -nDiff : nDiff; + nMatch += 2000 - 500*nDiff; + + if( nMatch > nLastMatch ) + { + nLastMatch = nMatch; + nSubstitute = builtin->m_nID; + } + } + if( nSubstitute != -1 ) + { + rInfo.m_aFontSubstitutions[ it->m_nID ] = nSubstitute; + #if OSL_DEBUG_LEVEL > 2 + FastPrintFontInfo aInfo; + rFontManager.getFontFastInfo( nSubstitute, aInfo ); + fprintf( stderr, + "substitute %s %s %d %d\n" + " -> %s %s %d %d\n", + OUStringToOString( it->m_aFamilyName, RTL_TEXTENCODING_ISO_8859_1 ).getStr(), + it->m_eItalic == italic::Upright ? "r" : it->m_eItalic == italic::Oblique ? "o" : it->m_eItalic == italic::Italic ? "i" : "u", + it->m_eWeight, + it->m_eWidth, + + OUStringToOString( aInfo.m_aFamilyName, RTL_TEXTENCODING_ISO_8859_1 ).getStr(), + aInfo.m_eItalic == italic::Upright ? "r" : aInfo.m_eItalic == italic::Oblique ? "o" : aInfo.m_eItalic == italic::Italic ? "i" : "u", + aInfo.m_eWeight, + aInfo.m_eWidth + ); + #endif + } + } + } + } +} + +// ----------------------------------------------------------------- + +void PrinterInfoManager::getSystemPrintCommands( std::list< OUString >& rCommands ) +{ + if( m_pQueueInfo && m_pQueueInfo->hasChanged() ) + { + m_aSystemPrintCommand = m_pQueueInfo->getCommand(); + m_pQueueInfo->getSystemQueues( m_aSystemPrintQueues ); + delete m_pQueueInfo, m_pQueueInfo = NULL; + } + + std::list< SystemPrintQueue >::const_iterator it; + rCommands.clear(); + String aPrinterConst( RTL_CONSTASCII_USTRINGPARAM( "(PRINTER)" ) ); + for( it = m_aSystemPrintQueues.begin(); it != m_aSystemPrintQueues.end(); ++it ) + { + String aCmd( m_aSystemPrintCommand ); + aCmd.SearchAndReplace( aPrinterConst, it->m_aQueue ); + rCommands.push_back( aCmd ); + } +} + +const std::list< PrinterInfoManager::SystemPrintQueue >& PrinterInfoManager::getSystemPrintQueues() +{ + if( m_pQueueInfo && m_pQueueInfo->hasChanged() ) + { + m_aSystemPrintCommand = m_pQueueInfo->getCommand(); + m_pQueueInfo->getSystemQueues( m_aSystemPrintQueues ); + delete m_pQueueInfo, m_pQueueInfo = NULL; + } + + return m_aSystemPrintQueues; +} + +bool PrinterInfoManager::checkFeatureToken( const rtl::OUString& rPrinterName, const char* pToken ) const +{ + const PrinterInfo& rPrinterInfo( getPrinterInfo( rPrinterName ) ); + sal_Int32 nIndex = 0; + while( nIndex != -1 ) + { + OUString aOuterToken = rPrinterInfo.m_aFeatures.getToken( 0, ',', nIndex ); + sal_Int32 nInnerIndex = 0; + OUString aInnerToken = aOuterToken.getToken( 0, '=', nInnerIndex ); + if( aInnerToken.equalsIgnoreAsciiCaseAscii( pToken ) ) + return true; + } + return false; +} + +FILE* PrinterInfoManager::startSpool( const OUString& rPrintername, bool bQuickCommand ) +{ + const PrinterInfo& rPrinterInfo = getPrinterInfo (rPrintername); + const rtl::OUString& rCommand = (bQuickCommand && rPrinterInfo.m_aQuickCommand.getLength() ) ? + rPrinterInfo.m_aQuickCommand : rPrinterInfo.m_aCommand; + rtl::OString aShellCommand = OUStringToOString (rCommand, RTL_TEXTENCODING_ISO_8859_1); + aShellCommand += rtl::OString( " 2>/dev/null" ); + + return popen (aShellCommand.getStr(), "w"); +} + +int PrinterInfoManager::endSpool( const OUString& /*rPrintername*/, const OUString& /*rJobTitle*/, FILE* pFile, const JobData& /*rDocumentJobData*/, bool /*bBanner*/ ) +{ + return (0 == pclose( pFile )); +} + +void PrinterInfoManager::setupJobContextData( JobData& rData ) +{ + boost::unordered_map< OUString, Printer, OUStringHash >::iterator it = + m_aPrinters.find( rData.m_aPrinterName ); + if( it != m_aPrinters.end() ) + { + rData.m_pParser = it->second.m_aInfo.m_pParser; + rData.m_aContext = it->second.m_aInfo.m_aContext; + } +} + +void PrinterInfoManager::setDefaultPaper( PPDContext& rContext ) const +{ + if( ! rContext.getParser() ) + return; + + const PPDKey* pPageSizeKey = rContext.getParser()->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) ); + if( ! pPageSizeKey ) + return; + + int nModified = rContext.countValuesModified(); + while( nModified-- && + rContext.getModifiedKey( nModified ) != pPageSizeKey ) + ; + + if( nModified >= 0 ) // paper was set already, do not modify + { + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "not setting default paper, already set %s\n", + OUStringToOString( rContext.getValue( pPageSizeKey )->m_aOption, RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); + #endif + return; + } + + // paper not set, fill in default value + const PPDValue* pPaperVal = NULL; + int nValues = pPageSizeKey->countValues(); + for( int i = 0; i < nValues && ! pPaperVal; i++ ) + { + const PPDValue* pVal = pPageSizeKey->getValue( i ); + if( pVal->m_aOption.EqualsIgnoreCaseAscii( m_aSystemDefaultPaper.getStr() ) ) + pPaperVal = pVal; + } + if( pPaperVal ) + { + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "setting default paper %s\n", OUStringToOString( pPaperVal->m_aOption, RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); + #endif + rContext.setValue( pPageSizeKey, pPaperVal ); + #if OSL_DEBUG_LEVEL > 1 + pPaperVal = rContext.getValue( pPageSizeKey ); + fprintf( stderr, "-> got paper %s\n", OUStringToOString( pPaperVal->m_aOption, RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); + #endif + } +} + +// ----------------------------------------------------------------- + +SystemQueueInfo::SystemQueueInfo() : + m_bChanged( false ) +{ + create(); +} + +SystemQueueInfo::~SystemQueueInfo() +{ + static const char* pNoSyncDetection = getenv( "SAL_DISABLE_SYNCHRONOUS_PRINTER_DETECTION" ); + if( ! pNoSyncDetection || !*pNoSyncDetection ) + join(); + else + terminate(); +} + +bool SystemQueueInfo::hasChanged() const +{ + MutexGuard aGuard( m_aMutex ); + bool bChanged = m_bChanged; + return bChanged; +} + +void SystemQueueInfo::getSystemQueues( std::list< PrinterInfoManager::SystemPrintQueue >& rQueues ) +{ + MutexGuard aGuard( m_aMutex ); + rQueues = m_aQueues; + m_bChanged = false; +} + +OUString SystemQueueInfo::getCommand() const +{ + MutexGuard aGuard( m_aMutex ); + OUString aRet = m_aCommand; + return aRet; +} + +struct SystemCommandParameters; +typedef void(* tokenHandler)(const std::list< rtl::OString >&, + std::list< PrinterInfoManager::SystemPrintQueue >&, + const SystemCommandParameters*); + +struct SystemCommandParameters +{ + const char* pQueueCommand; + const char* pPrintCommand; + const char* pForeToken; + const char* pAftToken; + unsigned int nForeTokenCount; + tokenHandler pHandler; +}; + +#if ! (defined(LINUX) || defined(NETBSD) || defined(FREEBSD) || defined(OPENBSD)) +static void lpgetSysQueueTokenHandler( + const std::list< rtl::OString >& i_rLines, + std::list< PrinterInfoManager::SystemPrintQueue >& o_rQueues, + const SystemCommandParameters* ) +{ + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + boost::unordered_set< OUString, OUStringHash > aUniqueSet; + boost::unordered_set< OUString, OUStringHash > aOnlySet; + aUniqueSet.insert( OUString( RTL_CONSTASCII_USTRINGPARAM( "_all" ) ) ); + aUniqueSet.insert( OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) ); + + // the eventual "all" attribute of the "_all" queue tells us, which + // printers are to be used for this user at all + + // find _all: line + rtl::OString aAllLine( "_all:" ); + rtl::OString aAllAttr( "all=" ); + for( std::list< rtl::OString >::const_iterator it = i_rLines.begin(); + it != i_rLines.end(); ++it ) + { + if( it->indexOf( aAllLine, 0 ) == 0 ) + { + // now find the "all" attribute + ++it; + while( it != i_rLines.end() ) + { + rtl::OString aClean( WhitespaceToSpace( *it ) ); + if( aClean.indexOf( aAllAttr, 0 ) == 0 ) + { + // insert the comma separated entries into the set of printers to use + sal_Int32 nPos = aAllAttr.getLength(); + while( nPos != -1 ) + { + OString aTok( aClean.getToken( 0, ',', nPos ) ); + if( aTok.getLength() > 0 ) + aOnlySet.insert( rtl::OStringToOUString( aTok, aEncoding ) ); + } + break; + } + } + break; + } + } + + bool bInsertAttribute = false; + rtl::OString aDescrStr( "description=" ); + rtl::OString aLocStr( "location=" ); + for( std::list< rtl::OString >::const_iterator it = i_rLines.begin(); + it != i_rLines.end(); ++it ) + { + sal_Int32 nPos = 0; + // find the begin of a new printer section + nPos = it->indexOf( ':', 0 ); + if( nPos != -1 ) + { + OUString aSysQueue( rtl::OStringToOUString( it->copy( 0, nPos ), aEncoding ) ); + // do not insert duplicates (e.g. lpstat tends to produce such lines) + // in case there was a "_all" section, insert only those printer explicitly + // set in the "all" attribute + if( aUniqueSet.find( aSysQueue ) == aUniqueSet.end() && + ( aOnlySet.empty() || aOnlySet.find( aSysQueue ) != aOnlySet.end() ) + ) + { + o_rQueues.push_back( PrinterInfoManager::SystemPrintQueue() ); + o_rQueues.back().m_aQueue = aSysQueue; + o_rQueues.back().m_aLocation = aSysQueue; + aUniqueSet.insert( aSysQueue ); + bInsertAttribute = true; + } + else + bInsertAttribute = false; + continue; + } + if( bInsertAttribute && ! o_rQueues.empty() ) + { + // look for "description" attribute, insert as comment + nPos = it->indexOf( aDescrStr, 0 ); + if( nPos != -1 ) + { + ByteString aComment( WhitespaceToSpace( it->copy(nPos+12) ) ); + if( aComment.Len() > 0 ) + o_rQueues.back().m_aComment = String( aComment, aEncoding ); + continue; + } + // look for "location" attribute, inser as location + nPos = it->indexOf( aLocStr, 0 ); + if( nPos != -1 ) + { + ByteString aLoc( WhitespaceToSpace( it->copy(nPos+9) ) ); + if( aLoc.Len() > 0 ) + o_rQueues.back().m_aLocation = String( aLoc, aEncoding ); + continue; + } + } + } +} +#endif +static void standardSysQueueTokenHandler( + const std::list< rtl::OString >& i_rLines, + std::list< PrinterInfoManager::SystemPrintQueue >& o_rQueues, + const SystemCommandParameters* i_pParms) +{ + rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); + boost::unordered_set< OUString, OUStringHash > aUniqueSet; + rtl::OString aForeToken( i_pParms->pForeToken ); + rtl::OString aAftToken( i_pParms->pAftToken ); + /* Normal Unix print queue discovery, also used for Darwin 5 LPR printing + */ + for( std::list< rtl::OString >::const_iterator it = i_rLines.begin(); + it != i_rLines.end(); ++it ) + { + sal_Int32 nPos = 0; + + // search for a line describing a printer: + // find if there are enough tokens before the name + for( unsigned int i = 0; i < i_pParms->nForeTokenCount && nPos != -1; i++ ) + { + nPos = it->indexOf( aForeToken, nPos ); + if( nPos != -1 && it->getLength() >= nPos+aForeToken.getLength() ) + nPos += aForeToken.getLength(); + } + if( nPos != -1 ) + { + // find if there is the token after the queue + sal_Int32 nAftPos = it->indexOf( aAftToken, nPos ); + if( nAftPos != -1 ) + { + // get the queue name between fore and aft tokens + OUString aSysQueue( rtl::OStringToOUString( it->copy( nPos, nAftPos - nPos ), aEncoding ) ); + // do not insert duplicates (e.g. lpstat tends to produce such lines) + if( aUniqueSet.find( aSysQueue ) == aUniqueSet.end() ) + { + o_rQueues.push_back( PrinterInfoManager::SystemPrintQueue() ); + o_rQueues.back().m_aQueue = aSysQueue; + o_rQueues.back().m_aLocation = aSysQueue; + aUniqueSet.insert( aSysQueue ); + } + } + } + } +} + +static const struct SystemCommandParameters aParms[] = +{ + #if defined(LINUX) || defined(NETBSD) || defined(FREEBSD) || defined(OPENBSD) + { "/usr/sbin/lpc status", "lpr -P \"(PRINTER)\"", "", ":", 0, standardSysQueueTokenHandler }, + { "lpc status", "lpr -P \"(PRINTER)\"", "", ":", 0, standardSysQueueTokenHandler }, + { "LANG=C;LC_ALL=C;export LANG LC_ALL;lpstat -s", "lp -d \"(PRINTER)\"", "system for ", ": ", 1, standardSysQueueTokenHandler } + #else + { "LANG=C;LC_ALL=C;export LANG LC_ALL;lpget list", "lp -d \"(PRINTER)\"", "", ":", 0, lpgetSysQueueTokenHandler }, + { "LANG=C;LC_ALL=C;export LANG LC_ALL;lpstat -s", "lp -d \"(PRINTER)\"", "system for ", ": ", 1, standardSysQueueTokenHandler }, + { "/usr/sbin/lpc status", "lpr -P \"(PRINTER)\"", "", ":", 0, standardSysQueueTokenHandler }, + { "lpc status", "lpr -P \"(PRINTER)\"", "", ":", 0, standardSysQueueTokenHandler } + #endif +}; + +void SystemQueueInfo::run() +{ + char pBuffer[1024]; + FILE *pPipe; + std::list< rtl::OString > aLines; + + /* Discover which command we can use to get a list of all printer queues */ + for( unsigned int i = 0; i < SAL_N_ELEMENTS(aParms); i++ ) + { + aLines.clear(); + rtl::OStringBuffer aCmdLine( 128 ); + aCmdLine.append( aParms[i].pQueueCommand ); + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "trying print queue command \"%s\" ... ", aParms[i].pQueueCommand ); + #endif + aCmdLine.append( " 2>/dev/null" ); + if( (pPipe = popen( aCmdLine.getStr(), "r" )) ) + { + while( fgets( pBuffer, 1024, pPipe ) ) + aLines.push_back( rtl::OString( pBuffer ) ); + if( ! pclose( pPipe ) ) + { + std::list< PrinterInfoManager::SystemPrintQueue > aSysPrintQueues; + aParms[i].pHandler( aLines, aSysPrintQueues, &(aParms[i]) ); + MutexGuard aGuard( m_aMutex ); + m_bChanged = true; + m_aQueues = aSysPrintQueues; + m_aCommand = rtl::OUString::createFromAscii( aParms[i].pPrintCommand ); + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "success\n" ); + #endif + break; + } + } + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "failed\n" ); + #endif + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/printergfx/bitmap_gfx.cxx b/vcl/unx/generic/printergfx/bitmap_gfx.cxx new file mode 100644 index 000000000000..0c139678f8ab --- /dev/null +++ b/vcl/unx/generic/printergfx/bitmap_gfx.cxx @@ -0,0 +1,735 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "psputil.hxx" + +#include "printergfx.hxx" +#include "vcl/strhelper.hxx" + +namespace psp { + +const sal_uInt32 nLineLength = 80; +const sal_uInt32 nBufferSize = 16384; + +/* + * + * Bitmap compression / Hex encoding / Ascii85 Encoding + * + */ + +PrinterBmp::~PrinterBmp () +{ /* dont need this, but C50 does */ } + +/* virtual base class */ + +class ByteEncoder +{ +private: + +public: + + virtual void EncodeByte (sal_uInt8 nByte) = 0; + virtual ~ByteEncoder () = 0; +}; + +ByteEncoder::~ByteEncoder () +{ /* dont need this, but the C50 does */ } + +/* HexEncoder */ + +class HexEncoder : public ByteEncoder +{ +private: + + osl::File* mpFile; + sal_uInt32 mnColumn; + sal_uInt32 mnOffset; + sal_Char mpFileBuffer[nBufferSize + 16]; + + HexEncoder (); /* dont use */ + +public: + + HexEncoder (osl::File* pFile); + virtual ~HexEncoder (); + void WriteAscii (sal_uInt8 nByte); + virtual void EncodeByte (sal_uInt8 nByte); + void FlushLine (); +}; + +HexEncoder::HexEncoder (osl::File* pFile) : + mpFile (pFile), + mnColumn (0), + mnOffset (0) +{} + +HexEncoder::~HexEncoder () +{ + FlushLine (); + if (mnColumn > 0) + WritePS (mpFile, "\n"); +} + +void +HexEncoder::WriteAscii (sal_uInt8 nByte) +{ + sal_uInt32 nOff = psp::getHexValueOf (nByte, mpFileBuffer + mnOffset); + mnColumn += nOff; + mnOffset += nOff; + + if (mnColumn >= nLineLength) + { + mnOffset += psp::appendStr ("\n", mpFileBuffer + mnOffset); + mnColumn = 0; + } + if (mnOffset >= nBufferSize) + FlushLine (); +} + +void +HexEncoder::EncodeByte (sal_uInt8 nByte) +{ + WriteAscii (nByte); +} + +void +HexEncoder::FlushLine () +{ + if (mnOffset > 0) + { + WritePS (mpFile, mpFileBuffer, mnOffset); + mnOffset = 0; + } +} + +/* Ascii85 encoder, is abi compatible with HexEncoder but writes a ~> to + indicate end of data EOD */ + +class Ascii85Encoder : public ByteEncoder +{ +private: + + osl::File* mpFile; + sal_uInt32 mnByte; + sal_uInt8 mpByteBuffer[4]; + + sal_uInt32 mnColumn; + sal_uInt32 mnOffset; + sal_Char mpFileBuffer[nBufferSize + 16]; + + Ascii85Encoder (); /* dont use */ + + inline void PutByte (sal_uInt8 nByte); + inline void PutEOD (); + void ConvertToAscii85 (); + void FlushLine (); + +public: + + Ascii85Encoder (osl::File* pFile); + virtual ~Ascii85Encoder (); + virtual void EncodeByte (sal_uInt8 nByte); + void WriteAscii (sal_uInt8 nByte); +}; + +Ascii85Encoder::Ascii85Encoder (osl::File* pFile) : + mpFile (pFile), + mnByte (0), + mnColumn (0), + mnOffset (0) +{} + +inline void +Ascii85Encoder::PutByte (sal_uInt8 nByte) +{ + mpByteBuffer [mnByte++] = nByte; +} + +inline void +Ascii85Encoder::PutEOD () +{ + WritePS (mpFile, "~>\n"); +} + +void +Ascii85Encoder::ConvertToAscii85 () +{ + if (mnByte < 4) + std::memset (mpByteBuffer + mnByte, 0, (4 - mnByte) * sizeof(sal_uInt8)); + + sal_uInt32 nByteValue = mpByteBuffer[0] * 256 * 256 * 256 + + mpByteBuffer[1] * 256 * 256 + + mpByteBuffer[2] * 256 + + mpByteBuffer[3]; + + if (nByteValue == 0 && mnByte == 4) + { + /* special case of 4 Bytes in row */ + mpFileBuffer [mnOffset] = 'z'; + + mnOffset += 1; + mnColumn += 1; + } + else + { + /* real ascii85 encoding */ + mpFileBuffer [mnOffset + 4] = (nByteValue % 85) + 33; + nByteValue /= 85; + mpFileBuffer [mnOffset + 3] = (nByteValue % 85) + 33; + nByteValue /= 85; + mpFileBuffer [mnOffset + 2] = (nByteValue % 85) + 33; + nByteValue /= 85; + mpFileBuffer [mnOffset + 1] = (nByteValue % 85) + 33; + nByteValue /= 85; + mpFileBuffer [mnOffset + 0] = (nByteValue % 85) + 33; + + mnColumn += (mnByte + 1); + mnOffset += (mnByte + 1); + + /* insert a newline if necessary */ + if (mnColumn > nLineLength) + { + sal_uInt32 nEolOff = mnColumn - nLineLength; + sal_uInt32 nBufOff = mnOffset - nEolOff; + + std::memmove (mpFileBuffer + nBufOff + 1, mpFileBuffer + nBufOff, nEolOff); + mpFileBuffer[ nBufOff ] = '\n'; + + mnOffset++; + mnColumn = nEolOff; + } + } + + mnByte = 0; +} + +void +Ascii85Encoder::WriteAscii (sal_uInt8 nByte) +{ + PutByte (nByte); + if (mnByte == 4) + ConvertToAscii85 (); + + if (mnColumn >= nLineLength) + { + mnOffset += psp::appendStr ("\n", mpFileBuffer + mnOffset); + mnColumn = 0; + } + if (mnOffset >= nBufferSize) + FlushLine (); +} + +void +Ascii85Encoder::EncodeByte (sal_uInt8 nByte) +{ + WriteAscii (nByte); +} + +void +Ascii85Encoder::FlushLine () +{ + if (mnOffset > 0) + { + WritePS (mpFile, mpFileBuffer, mnOffset); + mnOffset = 0; + } +} + +Ascii85Encoder::~Ascii85Encoder () +{ + if (mnByte > 0) + ConvertToAscii85 (); + if (mnOffset > 0) + FlushLine (); + PutEOD (); +} + +/* LZW encoder */ + +class LZWEncoder : public Ascii85Encoder +{ +private: + + struct LZWCTreeNode + { + LZWCTreeNode* mpBrother; // next node with same parent + LZWCTreeNode* mpFirstChild; // first son + sal_uInt16 mnCode; // code for the string + sal_uInt16 mnValue; // pixelvalue + }; + + LZWCTreeNode* mpTable; // LZW compression data + LZWCTreeNode* mpPrefix; // the compression is as same as the TIFF compression + sal_uInt16 mnDataSize; + sal_uInt16 mnClearCode; + sal_uInt16 mnEOICode; + sal_uInt16 mnTableSize; + sal_uInt16 mnCodeSize; + sal_uInt32 mnOffset; + sal_uInt32 mdwShift; + + LZWEncoder (); + void WriteBits (sal_uInt16 nCode, sal_uInt16 nCodeLen); + +public: + + LZWEncoder (osl::File* pOutputFile); + ~LZWEncoder (); + + virtual void EncodeByte (sal_uInt8 nByte); +}; + +LZWEncoder::LZWEncoder(osl::File* pOutputFile) : + Ascii85Encoder (pOutputFile) +{ + mnDataSize = 8; + + mnClearCode = 1 << mnDataSize; + mnEOICode = mnClearCode + 1; + mnTableSize = mnEOICode + 1; + mnCodeSize = mnDataSize + 1; + + mnOffset = 32; // free bits in dwShift + mdwShift = 0; + + mpTable = new LZWCTreeNode[ 4096 ]; + + for (sal_uInt32 i = 0; i < 4096; i++) + { + mpTable[i].mpBrother = NULL; + mpTable[i].mpFirstChild = NULL; + mpTable[i].mnCode = i; + mpTable[i].mnValue = (sal_uInt8)mpTable[i].mnCode; + } + + mpPrefix = NULL; + + WriteBits( mnClearCode, mnCodeSize ); +} + +LZWEncoder::~LZWEncoder() +{ + if (mpPrefix) + WriteBits (mpPrefix->mnCode, mnCodeSize); + + WriteBits (mnEOICode, mnCodeSize); + + delete[] mpTable; +} + +void +LZWEncoder::WriteBits (sal_uInt16 nCode, sal_uInt16 nCodeLen) +{ + mdwShift |= (nCode << (mnOffset - nCodeLen)); + mnOffset -= nCodeLen; + while (mnOffset < 24) + { + WriteAscii ((sal_uInt8)(mdwShift >> 24)); + mdwShift <<= 8; + mnOffset += 8; + } + if (nCode == 257 && mnOffset != 32) + WriteAscii ((sal_uInt8)(mdwShift >> 24)); +} + +void +LZWEncoder::EncodeByte (sal_uInt8 nByte ) +{ + LZWCTreeNode* p; + sal_uInt16 i; + sal_uInt8 nV; + + if (!mpPrefix) + { + mpPrefix = mpTable + nByte; + } + else + { + nV = nByte; + for (p = mpPrefix->mpFirstChild; p != NULL; p = p->mpBrother) + { + if (p->mnValue == nV) + break; + } + + if (p != NULL) + { + mpPrefix = p; + } + else + { + WriteBits (mpPrefix->mnCode, mnCodeSize); + + if (mnTableSize == 409) + { + WriteBits (mnClearCode, mnCodeSize); + + for (i = 0; i < mnClearCode; i++) + mpTable[i].mpFirstChild = NULL; + + mnCodeSize = mnDataSize + 1; + mnTableSize = mnEOICode + 1; + } + else + { + if(mnTableSize == (sal_uInt16)((1 << mnCodeSize) - 1)) + mnCodeSize++; + + p = mpTable + (mnTableSize++); + p->mpBrother = mpPrefix->mpFirstChild; + mpPrefix->mpFirstChild = p; + p->mnValue = nV; + p->mpFirstChild = NULL; + } + + mpPrefix = mpTable + nV; + } + } +} + +/* + * + * bitmap handling routines + * + */ + +void +PrinterGfx::DrawBitmap (const Rectangle& rDest, const Rectangle& rSrc, + const PrinterBmp& rBitmap) +{ + double fScaleX = (double)rDest.GetWidth() / (double)rSrc.GetWidth(); + double fScaleY = (double)rDest.GetHeight() / (double)rSrc.GetHeight(); + + PSGSave (); + PSTranslate (rDest.BottomLeft()); + PSScale (fScaleX, fScaleY); + + if (mnPSLevel >= 2) + { + if (rBitmap.GetDepth() == 1) + { + DrawPS2MonoImage (rBitmap, rSrc); + } + else + if (rBitmap.GetDepth() == 8 && mbColor) + { + // if the palette is larger than the image itself print it as a truecolor + // image to save diskspace. This is important for printing transparent + // bitmaps that are disassembled into small pieces + sal_Int32 nImageSz = rSrc.GetWidth() * rSrc.GetHeight(); + sal_Int32 nPaletteSz = rBitmap.GetPaletteEntryCount(); + if ((nImageSz < nPaletteSz) || (nImageSz < 24) ) + DrawPS2TrueColorImage (rBitmap, rSrc); + else + DrawPS2PaletteImage (rBitmap, rSrc); + } + else + if (rBitmap.GetDepth() == 24 && mbColor) + { + DrawPS2TrueColorImage (rBitmap, rSrc); + } + else + { + DrawPS2GrayImage (rBitmap, rSrc); + } + } + else + { + DrawPS1GrayImage (rBitmap, rSrc); + } + + PSGRestore (); +} + +/* XXX does not work XXX */ +void +PrinterGfx::DrawBitmap (const Rectangle& rDest, const Rectangle& rSrc, + const PrinterBmp& /*rBitmap*/, const PrinterBmp& /*rTransBitmap*/) +{ + double fScaleX = (double)rDest.GetWidth() / (double)rSrc.GetWidth(); + double fScaleY = (double)rDest.GetHeight() / (double)rSrc.GetHeight(); + + PSGSave (); + PSTranslate (rDest.BottomLeft()); + PSScale (fScaleX, fScaleY); + PSGRestore (); +} + +/* XXX does not work XXX */ +void +PrinterGfx::DrawMask (const Rectangle& rDest, const Rectangle& rSrc, + const PrinterBmp &/*rBitmap*/, PrinterColor& /*rMaskColor*/) +{ + double fScaleX = (double)rDest.GetWidth() / (double)rSrc.GetWidth(); + double fScaleY = (double)rDest.GetHeight() / (double)rSrc.GetHeight(); + + PSGSave (); + PSTranslate (rDest.BottomLeft()); + PSScale (fScaleX, fScaleY); + PSGRestore (); +} + +/* + * + * Implementation: PS Level 1 + * + */ + +void +PrinterGfx::DrawPS1GrayImage (const PrinterBmp& rBitmap, const Rectangle& rArea) +{ + sal_uInt32 nWidth = rArea.GetWidth(); + sal_uInt32 nHeight = rArea.GetHeight(); + + sal_Char pGrayImage [512]; + sal_Int32 nChar = 0; + + // image header + nChar += psp::getValueOf (nWidth, pGrayImage + nChar); + nChar += psp::appendStr (" ", pGrayImage + nChar); + nChar += psp::getValueOf (nHeight, pGrayImage + nChar); + nChar += psp::appendStr (" 8 ", pGrayImage + nChar); + nChar += psp::appendStr ("[ 1 0 0 1 0 ", pGrayImage + nChar); + nChar += psp::getValueOf (nHeight, pGrayImage + nChar); + nChar += psp::appendStr ("]", pGrayImage + nChar); + nChar += psp::appendStr (" {currentfile ", pGrayImage + nChar); + nChar += psp::getValueOf (nWidth, pGrayImage + nChar); + nChar += psp::appendStr (" string readhexstring pop}\n", pGrayImage + nChar); + nChar += psp::appendStr ("image\n", pGrayImage + nChar); + + WritePS (mpPageBody, pGrayImage); + + // image body + HexEncoder* pEncoder = new HexEncoder (mpPageBody); + + for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++) + { + for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++) + { + sal_uChar nByte = rBitmap.GetPixelGray (nRow, nColumn); + pEncoder->EncodeByte (nByte); + } + } + + delete pEncoder; + + WritePS (mpPageBody, "\n"); +} + +/* + * + * Implementation: PS Level 2 + * + */ + +void +PrinterGfx::writePS2ImageHeader (const Rectangle& rArea, psp::ImageType nType) +{ + sal_Int32 nChar = 0; + sal_Char pImage [512]; + + sal_Int32 nDictType = 0; + switch (nType) + { + case psp::TrueColorImage: nDictType = 0; break; + case psp::PaletteImage: nDictType = 1; break; + case psp::GrayScaleImage: nDictType = 2; break; + case psp::MonochromeImage: nDictType = 3; break; + default: break; + } + sal_Int32 nCompressType = mbCompressBmp ? 1 : 0; + + nChar += psp::getValueOf (rArea.GetWidth(), pImage + nChar); + nChar += psp::appendStr (" ", pImage + nChar); + nChar += psp::getValueOf (rArea.GetHeight(), pImage + nChar); + nChar += psp::appendStr (" ", pImage + nChar); + nChar += psp::getValueOf (nDictType, pImage + nChar); + nChar += psp::appendStr (" ", pImage + nChar); + nChar += psp::getValueOf (nCompressType, pImage + nChar); + nChar += psp::appendStr (" psp_imagedict image\n", pImage + nChar); + + WritePS (mpPageBody, pImage); +} + +void +PrinterGfx::writePS2Colorspace(const PrinterBmp& rBitmap, psp::ImageType nType) +{ + switch (nType) + { + case psp::GrayScaleImage: + + WritePS (mpPageBody, "/DeviceGray setcolorspace\n"); + break; + + case psp::TrueColorImage: + + WritePS (mpPageBody, "/DeviceRGB setcolorspace\n"); + break; + + case psp::MonochromeImage: + case psp::PaletteImage: + { + + sal_Int32 nChar = 0; + sal_Char pImage [4096]; + + const sal_uInt32 nSize = rBitmap.GetPaletteEntryCount(); + + nChar += psp::appendStr ("[/Indexed /DeviceRGB ", pImage + nChar); + nChar += psp::getValueOf (nSize - 1, pImage + nChar); + if (mbCompressBmp) + nChar += psp::appendStr ("\npsp_lzwstring\n", pImage + nChar); + else + nChar += psp::appendStr ("\npsp_ascii85string\n", pImage + nChar); + WritePS (mpPageBody, pImage); + + ByteEncoder* pEncoder = mbCompressBmp ? new LZWEncoder(mpPageBody) + : new Ascii85Encoder(mpPageBody); + for (sal_uInt32 i = 0; i < nSize; i++) + { + PrinterColor aColor = rBitmap.GetPaletteColor(i); + + pEncoder->EncodeByte (aColor.GetRed()); + pEncoder->EncodeByte (aColor.GetGreen()); + pEncoder->EncodeByte (aColor.GetBlue()); + } + delete pEncoder; + + WritePS (mpPageBody, "pop ] setcolorspace\n"); + } + break; + default: break; + } +} + +void +PrinterGfx::DrawPS2GrayImage (const PrinterBmp& rBitmap, const Rectangle& rArea) +{ + writePS2Colorspace(rBitmap, psp::GrayScaleImage); + writePS2ImageHeader(rArea, psp::GrayScaleImage); + + ByteEncoder* pEncoder = mbCompressBmp ? new LZWEncoder(mpPageBody) + : new Ascii85Encoder(mpPageBody); + + for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++) + { + for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++) + { + sal_uChar nByte = rBitmap.GetPixelGray (nRow, nColumn); + pEncoder->EncodeByte (nByte); + } + } + + delete pEncoder; +} + +void +PrinterGfx::DrawPS2MonoImage (const PrinterBmp& rBitmap, const Rectangle& rArea) +{ + writePS2Colorspace(rBitmap, psp::MonochromeImage); + writePS2ImageHeader(rArea, psp::MonochromeImage); + + ByteEncoder* pEncoder = mbCompressBmp ? new LZWEncoder(mpPageBody) + : new Ascii85Encoder(mpPageBody); + + for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++) + { + long nBitPos = 0; + sal_uChar nBit = 0; + sal_uChar nByte = 0; + + for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++) + { + nBit = rBitmap.GetPixelIdx (nRow, nColumn); + nByte |= nBit << (7 - nBitPos); + + if (++nBitPos == 8) + { + pEncoder->EncodeByte (nByte); + nBitPos = 0; + nByte = 0; + } + } + // keep the row byte aligned + if (nBitPos != 0) + pEncoder->EncodeByte (nByte); + } + + delete pEncoder; +} + +void +PrinterGfx::DrawPS2PaletteImage (const PrinterBmp& rBitmap, const Rectangle& rArea) +{ + writePS2Colorspace(rBitmap, psp::PaletteImage); + writePS2ImageHeader(rArea, psp::PaletteImage); + + ByteEncoder* pEncoder = mbCompressBmp ? new LZWEncoder(mpPageBody) + : new Ascii85Encoder(mpPageBody); + + for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++) + { + for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++) + { + sal_uChar nByte = rBitmap.GetPixelIdx (nRow, nColumn); + pEncoder->EncodeByte (nByte); + } + } + + delete pEncoder; +} + +void +PrinterGfx::DrawPS2TrueColorImage (const PrinterBmp& rBitmap, const Rectangle& rArea) +{ + writePS2Colorspace(rBitmap, psp::TrueColorImage); + writePS2ImageHeader(rArea, psp::TrueColorImage); + + ByteEncoder* pEncoder = mbCompressBmp ? new LZWEncoder(mpPageBody) + : new Ascii85Encoder(mpPageBody); + + for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++) + { + for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++) + { + PrinterColor aColor = rBitmap.GetPixelRGB (nRow, nColumn); + pEncoder->EncodeByte (aColor.GetRed()); + pEncoder->EncodeByte (aColor.GetGreen()); + pEncoder->EncodeByte (aColor.GetBlue()); + } + } + + delete pEncoder; +} + +} /* namespace psp */ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/printergfx/common_gfx.cxx b/vcl/unx/generic/printergfx/common_gfx.cxx new file mode 100644 index 000000000000..7cae4586bf6e --- /dev/null +++ b/vcl/unx/generic/printergfx/common_gfx.cxx @@ -0,0 +1,1287 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "psputil.hxx" +#include "glyphset.hxx" + +#include "printergfx.hxx" +#include "printerjob.hxx" +#include "vcl/fontmanager.hxx" +#include "vcl/strhelper.hxx" +#include "vcl/printerinfomanager.hxx" + +#include "tools/debug.hxx" +#include "tools/color.hxx" +#include "tools/poly.hxx" + +using namespace psp ; + +static const sal_Int32 nMaxTextColumn = 80; + +GraphicsStatus::GraphicsStatus() : + mbArtItalic( false ), + mbArtBold( false ), + mnTextHeight( 0 ), + mnTextWidth( 0 ), + mfLineWidth( -1 ) +{ +} + +/* + * non graphics graphics routines + */ + +sal_Bool +PrinterGfx::Init (PrinterJob &rPrinterJob) +{ + mpPageHeader = rPrinterJob.GetCurrentPageHeader (); + mpPageBody = rPrinterJob.GetCurrentPageBody (); + mnDepth = rPrinterJob.GetDepth (); + mnPSLevel = rPrinterJob.GetPostscriptLevel (); + mbColor = rPrinterJob.IsColorPrinter (); + + mnDpi = rPrinterJob.GetResolution(); + rPrinterJob.GetScale (mfScaleX, mfScaleY); + const PrinterInfo& rInfo( PrinterInfoManager::get().getPrinterInfo( rPrinterJob.GetPrinterName() ) ); + if( mpFontSubstitutes ) + delete const_cast< ::boost::unordered_map<fontID,fontID>* >(mpFontSubstitutes); + if( rInfo.m_bPerformFontSubstitution ) + mpFontSubstitutes = new ::boost::unordered_map< fontID, fontID >( rInfo.m_aFontSubstitutions ); + else + mpFontSubstitutes = NULL; + mbUploadPS42Fonts = rInfo.m_pParser ? ( rInfo.m_pParser->isType42Capable() ? sal_True : sal_False ) : sal_False; + + return sal_True; +} + +sal_Bool +PrinterGfx::Init (const JobData& rData) +{ + mpPageHeader = NULL; + mpPageBody = NULL; + mnDepth = rData.m_nColorDepth; + mnPSLevel = rData.m_nPSLevel ? rData.m_nPSLevel : (rData.m_pParser ? rData.m_pParser->getLanguageLevel() : 2 ); + mbColor = rData.m_nColorDevice ? ( rData.m_nColorDevice == -1 ? sal_False : sal_True ) : (( rData.m_pParser ? (rData.m_pParser->isColorDevice() ? sal_True : sal_False ) : sal_True ) ); + int nRes = rData.m_aContext.getRenderResolution(); + mnDpi = nRes; + mfScaleX = (double)72.0 / (double)mnDpi; + mfScaleY = (double)72.0 / (double)mnDpi; + const PrinterInfo& rInfo( PrinterInfoManager::get().getPrinterInfo( rData.m_aPrinterName ) ); + if( mpFontSubstitutes ) + delete const_cast< ::boost::unordered_map<fontID,fontID>* >(mpFontSubstitutes); + if( rInfo.m_bPerformFontSubstitution ) + mpFontSubstitutes = new ::boost::unordered_map< fontID, fontID >( rInfo.m_aFontSubstitutions ); + else + mpFontSubstitutes = NULL; + mbUploadPS42Fonts = rInfo.m_pParser ? ( rInfo.m_pParser->isType42Capable() ? sal_True : sal_False ) : sal_False; + + return sal_True; +} + +void +PrinterGfx::GetResolution (sal_Int32 &rDpiX, sal_Int32 &rDpiY) const +{ + rDpiX = mnDpi; + rDpiY = mnDpi; +} + +sal_uInt16 +PrinterGfx::GetBitCount () +{ + return mnDepth; +} + +PrinterGfx::PrinterGfx() : + mpPageHeader (NULL), + mpPageBody (NULL), + mnFontID (0), + mnFallbackID (0), + mnTextAngle (0), + mbTextVertical (false), + mrFontMgr (PrintFontManager::get()), + mbCompressBmp (sal_True), + maFillColor (0xff,0,0), + maTextColor (0,0,0), + maLineColor (0, 0xff, 0), + mpFontSubstitutes( NULL ), + mbStrictSO52Compatibility( false ) +{ + maVirtualStatus.mfLineWidth = 1.0; + maVirtualStatus.mnTextHeight = 12; + maVirtualStatus.mnTextWidth = 0; + + maGraphicsStack.push_back( GraphicsStatus() ); +} + +PrinterGfx::~PrinterGfx() +{ + /* + * the original reasoning why mpFontSubstitutes is a pointer was + * that applications should release all PrinterGfx when printers change + * because they are really invalid; the corresponding printers may have + * changed their settings or even not exist anymore. + * + * Alas, this is not always done real time. So we keep a local copy of + * the font substitutes now in case of bad timing. + */ + delete const_cast< ::boost::unordered_map<fontID,fontID>* >(mpFontSubstitutes); +} + +void +PrinterGfx::Clear() +{ + mpPageHeader = NULL; + mpPageBody = NULL; + mnFontID = 0; + maVirtualStatus = GraphicsStatus(); + maVirtualStatus.mnTextHeight = 12; + maVirtualStatus.mnTextWidth = 0; + maVirtualStatus.mfLineWidth = 1.0; + mbTextVertical = false; + maLineColor = PrinterColor(); + maFillColor = PrinterColor(); + maTextColor = PrinterColor(); + mbCompressBmp = sal_True; + mnDpi = 300; + mnDepth = 24; + mnPSLevel = 2; + mbColor = sal_True; + mnTextAngle = 0; + + maClipRegion.clear(); + maGraphicsStack.clear(); + maGraphicsStack.push_back( GraphicsStatus() ); +} + +/* + * clip region handling + */ + +void +PrinterGfx::ResetClipRegion() +{ + maClipRegion.clear(); + PSGRestore (); + PSGSave (); // get "clean" clippath +} + +void +PrinterGfx::BeginSetClipRegion( sal_uInt32 ) +{ + maClipRegion.clear(); +} + +sal_Bool +PrinterGfx::UnionClipRegion (sal_Int32 nX,sal_Int32 nY,sal_Int32 nDX,sal_Int32 nDY) +{ + if( nDX && nDY ) + maClipRegion.push_back (Rectangle(Point(nX,nY ), Size(nDX,nDY))); + return sal_True; +} + +sal_Bool +PrinterGfx::JoinVerticalClipRectangles( std::list< Rectangle >::iterator& it, + Point& rOldPoint, sal_Int32& rColumn ) +{ + sal_Bool bSuccess = sal_False; + + std::list< Rectangle >::iterator tempit, nextit; + nextit = it; + ++nextit; + std::list< Point > leftside, rightside; + + Rectangle aLastRect( *it ); + leftside.push_back( Point( it->Left(), it->Top() ) ); + rightside.push_back( Point( it->Right()+1, it->Top() ) ); + while( nextit != maClipRegion.end() ) + { + tempit = nextit; + ++tempit; + if( nextit->Top() == aLastRect.Bottom()+1 ) + { + if( + ( nextit->Left() >= aLastRect.Left() && nextit->Left() <= aLastRect.Right() ) // left endpoint touches last rectangle + || + ( nextit->Right() >= aLastRect.Left() && nextit->Right() <= aLastRect.Right() ) // right endpoint touches last rectangle + || + ( nextit->Left() <= aLastRect.Left() && nextit->Right() >= aLastRect.Right() ) // whole line touches last rectangle + ) + { + if( aLastRect.GetHeight() > 1 || + abs( aLastRect.Left() - nextit->Left() ) > 2 || + abs( aLastRect.Right() - nextit->Right() ) > 2 + ) + { + leftside.push_back( Point( aLastRect.Left(), aLastRect.Bottom()+1 ) ); + rightside.push_back( Point( aLastRect.Right()+1, aLastRect.Bottom()+1 ) ); + } + aLastRect = *nextit; + leftside.push_back( aLastRect.TopLeft() ); + rightside.push_back( aLastRect.TopRight() ); + maClipRegion.erase( nextit ); + } + } + nextit = tempit; + } + if( leftside.size() > 1 ) + { + // push the last coordinates + leftside.push_back( Point( aLastRect.Left(), aLastRect.Bottom()+1 ) ); + rightside.push_back( Point( aLastRect.Right()+1, aLastRect.Bottom()+1 ) ); + + // cool, we can concatenate rectangles + int nDX = -65536, nDY = 65536; + int nNewDX = 0, nNewDY = 0; + + Point aLastPoint = leftside.front(); + PSBinMoveTo (aLastPoint, rOldPoint, rColumn); + leftside.pop_front(); + while( leftside.begin() != leftside.end() ) + { + Point aPoint (leftside.front()); + leftside.pop_front(); + // may have been the last one + if( leftside.begin() != leftside.end() ) + { + nNewDX = aPoint.X() - aLastPoint.X(); + nNewDY = aPoint.Y() - aLastPoint.Y(); + if( nNewDX == 0 && nDX == 0 ) + continue; + if( nDX != 0 && nNewDX != 0 && + (double)nNewDY/(double)nNewDX == (double)nDY/(double)nDX ) + continue; + } + PSBinLineTo (aPoint, rOldPoint, rColumn); + aLastPoint = aPoint; + } + + aLastPoint = rightside.back(); + nDX = -65536; + nDY = 65536; + PSBinLineTo (aLastPoint, rOldPoint, rColumn); + rightside.pop_back(); + while( rightside.begin() != rightside.end() ) + { + Point aPoint (rightside.back()); + rightside.pop_back(); + if( rightside.begin() != rightside.end() ) + { + nNewDX = aPoint.X() - aLastPoint.X(); + nNewDY = aPoint.Y() - aLastPoint.Y(); + if( nNewDX == 0 && nDX == 0 ) + continue; + if( nDX != 0 && nNewDX != 0 && + (double)nNewDY/(double)nNewDX == (double)nDY/(double)nDX ) + continue; + } + PSBinLineTo (aPoint, rOldPoint, rColumn); + } + + tempit = it; + ++tempit; + maClipRegion.erase( it ); + it = tempit; + bSuccess = sal_True; + } + return bSuccess; +} + +void +PrinterGfx::EndSetClipRegion() +{ + PSGRestore (); + PSGSave (); // get "clean" clippath + + PSBinStartPath (); + Point aOldPoint (0, 0); + sal_Int32 nColumn = 0; + + std::list< Rectangle >::iterator it = maClipRegion.begin(); + while( it != maClipRegion.end() ) + { + // try to concatenate adjacent rectangles + // first try in y direction, then in x direction + if( ! JoinVerticalClipRectangles( it, aOldPoint, nColumn ) ) + { + // failed, so it is a single rectangle + PSBinMoveTo (it->TopLeft(), aOldPoint, nColumn ); + PSBinLineTo (Point( it->Left(), it->Bottom()+1 ), aOldPoint, nColumn ); + PSBinLineTo (Point( it->Right()+1, it->Bottom()+1 ), aOldPoint, nColumn ); + PSBinLineTo (Point( it->Right()+1, it->Top() ), aOldPoint, nColumn ); + ++it; + } + } + + PSBinEndPath (); + + WritePS (mpPageBody, "closepath clip newpath\n"); + maClipRegion.clear(); +} + +/* + * draw graphic primitives + */ + +void +PrinterGfx::DrawRect (const Rectangle& rRectangle ) +{ + char pRect [128]; + sal_Int32 nChar = 0; + + nChar = psp::getValueOf (rRectangle.TopLeft().X(), pRect); + nChar += psp::appendStr (" ", pRect + nChar); + nChar += psp::getValueOf (rRectangle.TopLeft().Y(), pRect + nChar); + nChar += psp::appendStr (" ", pRect + nChar); + nChar += psp::getValueOf (rRectangle.GetWidth(), pRect + nChar); + nChar += psp::appendStr (" ", pRect + nChar); + nChar += psp::getValueOf (rRectangle.GetHeight(), pRect + nChar); + nChar += psp::appendStr (" ", pRect + nChar); + + if( maFillColor.Is() ) + { + PSSetColor (maFillColor); + PSSetColor (); + WritePS (mpPageBody, pRect, nChar); + WritePS (mpPageBody, "rectfill\n"); + } + if( maLineColor.Is() ) + { + PSSetColor (maLineColor); + PSSetColor (); + PSSetLineWidth (); + WritePS (mpPageBody, pRect, nChar); + WritePS (mpPageBody, "rectstroke\n"); + } +} + +void +PrinterGfx::DrawLine (const Point& rFrom, const Point& rTo) +{ + if( maLineColor.Is() ) + { + PSSetColor (maLineColor); + PSSetColor (); + PSSetLineWidth (); + + PSMoveTo (rFrom); + PSLineTo (rTo); + WritePS (mpPageBody, "stroke\n" ); + } +} + +void +PrinterGfx::DrawPixel (const Point& rPoint, const PrinterColor& rPixelColor) +{ + if( rPixelColor.Is() ) + { + PSSetColor (rPixelColor); + PSSetColor (); + + PSMoveTo (rPoint); + PSLineTo (Point (rPoint.X ()+1, rPoint.Y ())); + PSLineTo (Point (rPoint.X ()+1, rPoint.Y ()+1)); + PSLineTo (Point (rPoint.X (), rPoint.Y ()+1)); + WritePS (mpPageBody, "fill\n" ); + } +} + +void +PrinterGfx::DrawPolyLine (sal_uInt32 nPoints, const Point* pPath) +{ + if( maLineColor.Is() && nPoints && pPath ) + { + PSSetColor (maLineColor); + PSSetColor (); + PSSetLineWidth (); + + PSBinCurrentPath (nPoints, pPath); + + WritePS (mpPageBody, "stroke\n" ); + } +} + +void +PrinterGfx::DrawPolygon (sal_uInt32 nPoints, const Point* pPath) +{ + // premature end of operation + if (!(nPoints > 1) || (pPath == NULL) || !(maFillColor.Is() || maLineColor.Is())) + return; + + // setup closed path + Point aPoint( 0, 0 ); + sal_Int32 nColumn( 0 ); + + PSBinStartPath(); + PSBinMoveTo( pPath[0], aPoint, nColumn ); + for( unsigned int n = 1; n < nPoints; n++ ) + PSBinLineTo( pPath[n], aPoint, nColumn ); + if( pPath[0] != pPath[nPoints-1] ) + PSBinLineTo( pPath[0], aPoint, nColumn ); + PSBinEndPath(); + + // fill the polygon first, then draw the border, note that fill and + // stroke reset the currentpath + + // if fill and stroke, save the current path + if( maFillColor.Is() && maLineColor.Is()) + PSGSave(); + + if (maFillColor.Is ()) + { + PSSetColor (maFillColor); + PSSetColor (); + WritePS (mpPageBody, "eofill\n"); + } + + // restore the current path + if( maFillColor.Is() && maLineColor.Is()) + PSGRestore(); + + if (maLineColor.Is ()) + { + PSSetColor (maLineColor); + PSSetColor (); + PSSetLineWidth (); + WritePS (mpPageBody, "stroke\n"); + } +} + +void +PrinterGfx::DrawPolyPolygon (sal_uInt32 nPoly, const sal_uInt32* pSizes, const Point** pPaths ) +{ + // sanity check + if ( !nPoly || !pPaths || !(maFillColor.Is() || maLineColor.Is())) + return; + + + // setup closed path + for( unsigned int i = 0; i < nPoly; i++ ) + { + Point aPoint( 0, 0 ); + sal_Int32 nColumn( 0 ); + + PSBinStartPath(); + PSBinMoveTo( pPaths[i][0], aPoint, nColumn ); + for( unsigned int n = 1; n < pSizes[i]; n++ ) + PSBinLineTo( pPaths[i][n], aPoint, nColumn ); + if( pPaths[i][0] != pPaths[i][pSizes[i]-1] ) + PSBinLineTo( pPaths[i][0], aPoint, nColumn ); + PSBinEndPath(); + } + + // if eofill and stroke, save the current path + if( maFillColor.Is() && maLineColor.Is()) + PSGSave(); + + // first draw area + if( maFillColor.Is() ) + { + PSSetColor (maFillColor); + PSSetColor (); + WritePS (mpPageBody, "eofill\n"); + } + + // restore the current path + if( maFillColor.Is() && maLineColor.Is()) + PSGRestore(); + + // now draw outlines + if( maLineColor.Is() ) + { + PSSetColor (maLineColor); + PSSetColor (); + PSSetLineWidth (); + WritePS (mpPageBody, "stroke\n"); + } +} + +/* + * Bezier Polygon Drawing methods. + */ + +void +PrinterGfx::DrawPolyLineBezier (sal_uInt32 nPoints, const Point* pPath, const sal_uInt8* pFlgAry) +{ + const sal_uInt32 nBezString= 1024; + sal_Char pString[nBezString]; + + if ( nPoints > 1 && maLineColor.Is() && pPath ) + { + PSSetColor (maLineColor); + PSSetColor (); + PSSetLineWidth (); + + snprintf(pString, nBezString, "%li %li moveto\n", pPath[0].X(), pPath[0].Y()); + WritePS(mpPageBody, pString); + + // Handle the drawing of mixed lines mixed with curves + // - a normal point followed by a normal point is a line + // - a normal point followed by 2 control points and a normal point is a curve + for (unsigned int i=1; i<nPoints;) + { + if (pFlgAry[i] != POLY_CONTROL) //If the next point is a POLY_NORMAL, we're drawing a line + { + snprintf(pString, nBezString, "%li %li lineto\n", pPath[i].X(), pPath[i].Y()); + i++; + } + else //Otherwise we're drawing a spline + { + if (i+2 >= nPoints) + return; //Error: wrong sequence of contol/normal points somehow + if ((pFlgAry[i] == POLY_CONTROL) && (pFlgAry[i+1] == POLY_CONTROL) && + (pFlgAry[i+2] != POLY_CONTROL)) + { + snprintf(pString, nBezString, "%li %li %li %li %li %li curveto\n", + pPath[i].X(), pPath[i].Y(), + pPath[i+1].X(), pPath[i+1].Y(), + pPath[i+2].X(), pPath[i+2].Y()); + } + else + { + OSL_FAIL( "PrinterGfx::DrawPolyLineBezier: Strange output" ); + } + i+=3; + } + WritePS(mpPageBody, pString); + } + + // now draw outlines + WritePS (mpPageBody, "stroke\n"); + } +} + +void +PrinterGfx::DrawPolygonBezier (sal_uInt32 nPoints, const Point* pPath, const sal_uInt8* pFlgAry) +{ + const sal_uInt32 nBezString = 1024; + sal_Char pString[nBezString]; + // premature end of operation + if (!(nPoints > 1) || (pPath == NULL) || !(maFillColor.Is() || maLineColor.Is())) + return; + + snprintf(pString, nBezString, "%li %li moveto\n", pPath[0].X(), pPath[0].Y()); + WritePS(mpPageBody, pString); //Move to the starting point for the PolyPoygon + for (unsigned int i=1; i < nPoints;) + { + if (pFlgAry[i] != POLY_CONTROL) + { + snprintf(pString, nBezString, "%li %li lineto\n", pPath[i].X(), pPath[i].Y()); + WritePS(mpPageBody, pString); + i++; + } + else + { + if (i+2 >= nPoints) + return; //Error: wrong sequence of contol/normal points somehow + if ((pFlgAry[i] == POLY_CONTROL) && (pFlgAry[i+1] == POLY_CONTROL) && + (pFlgAry[i+2] != POLY_CONTROL)) + { + snprintf(pString, nBezString, "%li %li %li %li %li %li curveto\n", + pPath[i].X(), pPath[i].Y(), + pPath[i+1].X(), pPath[i+1].Y(), + pPath[i+2].X(), pPath[i+2].Y()); + WritePS(mpPageBody, pString); + } + else + { + OSL_FAIL( "PrinterGfx::DrawPolygonBezier: Strange output" ); + } + i+=3; + } + } + + // if fill and stroke, save the current path + if( maFillColor.Is() && maLineColor.Is()) + PSGSave(); + + if (maFillColor.Is ()) + { + PSSetColor (maFillColor); + PSSetColor (); + WritePS (mpPageBody, "eofill\n"); + } + + // restore the current path + if( maFillColor.Is() && maLineColor.Is()) + PSGRestore(); +} + +void +PrinterGfx::DrawPolyPolygonBezier (sal_uInt32 nPoly, const sal_uInt32 * pPoints, const Point* const * pPtAry, const sal_uInt8* const* pFlgAry) +{ + const sal_uInt32 nBezString = 1024; + sal_Char pString[nBezString]; + if ( !nPoly || !pPtAry || !pPoints || !(maFillColor.Is() || maLineColor.Is())) + return; + + + for (unsigned int i=0; i<nPoly;i++) + { + sal_uInt32 nPoints = pPoints[i]; + // sanity check + if( nPoints == 0 || pPtAry[i] == NULL ) + continue; + + snprintf(pString, nBezString, "%li %li moveto\n", pPtAry[i][0].X(), pPtAry[i][0].Y()); //Move to the starting point + WritePS(mpPageBody, pString); + for (unsigned int j=1; j < nPoints;) + { + // if no flag array exists for this polygon, then it must be a regular + // polygon without beziers + if ( ! pFlgAry[i] || pFlgAry[i][j] != POLY_CONTROL) + { + snprintf(pString, nBezString, "%li %li lineto\n", pPtAry[i][j].X(), pPtAry[i][j].Y()); + WritePS(mpPageBody, pString); + j++; + } + else + { + if (j+2 >= nPoints) + break; //Error: wrong sequence of contol/normal points somehow + if ((pFlgAry[i][j] == POLY_CONTROL) && (pFlgAry[i][j+1] == POLY_CONTROL) && (pFlgAry[i][j+2] != POLY_CONTROL)) + { + snprintf(pString, nBezString, "%li %li %li %li %li %li curveto\n", + pPtAry[i][j].X(), pPtAry[i][j].Y(), + pPtAry[i][j+1].X(), pPtAry[i][j+1].Y(), + pPtAry[i][j+2].X(), pPtAry[i][j+2].Y()); + WritePS(mpPageBody, pString); + } + else + { + OSL_FAIL( "PrinterGfx::DrawPolyPolygonBezier: Strange output" ); + } + j+=3; + } + } + } + + // if fill and stroke, save the current path + if( maFillColor.Is() && maLineColor.Is()) + PSGSave(); + + if (maFillColor.Is ()) + { + PSSetColor (maFillColor); + PSSetColor (); + WritePS (mpPageBody, "eofill\n"); + } + + // restore the current path + if( maFillColor.Is() && maLineColor.Is()) + PSGRestore(); +} + + +/* + * postscript generating routines + */ +void +PrinterGfx::PSGSave () +{ + WritePS (mpPageBody, "gsave\n" ); + GraphicsStatus aNewState; + if( maGraphicsStack.begin() != maGraphicsStack.end() ) + aNewState = maGraphicsStack.front(); + maGraphicsStack.push_front( aNewState ); +} + +void +PrinterGfx::PSGRestore () +{ + WritePS (mpPageBody, "grestore\n" ); + if( maGraphicsStack.begin() == maGraphicsStack.end() ) + WritePS (mpPageBody, "Error: too many grestores\n" ); + else + maGraphicsStack.pop_front(); +} + +void +PrinterGfx::PSSetLineWidth () +{ + if( currentState().mfLineWidth != maVirtualStatus.mfLineWidth ) + { + char pBuffer[128]; + sal_Int32 nChar = 0; + + currentState().mfLineWidth = maVirtualStatus.mfLineWidth; + nChar = psp::getValueOfDouble (pBuffer, maVirtualStatus.mfLineWidth, 5); + nChar += psp::appendStr (" setlinewidth\n", pBuffer + nChar); + WritePS (mpPageBody, pBuffer, nChar); + } +} + +void +PrinterGfx::PSSetColor () +{ + PrinterColor& rColor( maVirtualStatus.maColor ); + + if( currentState().maColor != rColor ) + { + currentState().maColor = rColor; + + char pBuffer[128]; + sal_Int32 nChar = 0; + + if( mbColor ) + { + nChar = psp::getValueOfDouble (pBuffer, + (double)rColor.GetRed() / 255.0, 5); + nChar += psp::appendStr (" ", pBuffer + nChar); + nChar += psp::getValueOfDouble (pBuffer + nChar, + (double)rColor.GetGreen() / 255.0, 5); + nChar += psp::appendStr (" ", pBuffer + nChar); + nChar += psp::getValueOfDouble (pBuffer + nChar, + (double)rColor.GetBlue() / 255.0, 5); + nChar += psp::appendStr (" setrgbcolor\n", pBuffer + nChar ); + } + else + { + Color aColor( rColor.GetRed(), rColor.GetGreen(), rColor.GetBlue() ); + sal_uInt8 nCol = aColor.GetLuminance(); + nChar = psp::getValueOfDouble( pBuffer, (double)nCol / 255.0, 5 ); + nChar += psp::appendStr( " setgray\n", pBuffer + nChar ); + } + + WritePS (mpPageBody, pBuffer, nChar); + } +} + +void +PrinterGfx::PSSetFont () +{ + GraphicsStatus& rCurrent( currentState() ); + if( maVirtualStatus.maFont != rCurrent.maFont || + maVirtualStatus.mnTextHeight != rCurrent.mnTextHeight || + maVirtualStatus.maEncoding != rCurrent.maEncoding || + maVirtualStatus.mnTextWidth != rCurrent.mnTextWidth || + maVirtualStatus.mbArtBold != rCurrent.mbArtBold || + maVirtualStatus.mbArtItalic != rCurrent.mbArtItalic + ) + { + rCurrent.maFont = maVirtualStatus.maFont; + rCurrent.maEncoding = maVirtualStatus.maEncoding; + rCurrent.mnTextWidth = maVirtualStatus.mnTextWidth; + rCurrent.mnTextHeight = maVirtualStatus.mnTextHeight; + rCurrent.mbArtItalic = maVirtualStatus.mbArtItalic; + rCurrent.mbArtBold = maVirtualStatus.mbArtBold; + + sal_Int32 nTextHeight = rCurrent.mnTextHeight; + sal_Int32 nTextWidth = rCurrent.mnTextWidth ? rCurrent.mnTextWidth + : rCurrent.mnTextHeight; + + sal_Char pSetFont [256]; + sal_Int32 nChar = 0; + + // postscript based fonts need reencoding + if ( ( rCurrent.maEncoding == RTL_TEXTENCODING_MS_1252) + || ( rCurrent.maEncoding == RTL_TEXTENCODING_ISO_8859_1) + || ( rCurrent.maEncoding >= RTL_TEXTENCODING_USER_START + && rCurrent.maEncoding <= RTL_TEXTENCODING_USER_END) + ) + { + rtl::OString aReencodedFont = + psp::GlyphSet::GetReencodedFontName (rCurrent.maEncoding, + rCurrent.maFont); + + nChar += psp::appendStr ("(", pSetFont + nChar); + nChar += psp::appendStr (aReencodedFont.getStr(), + pSetFont + nChar); + nChar += psp::appendStr (") cvn findfont ", + pSetFont + nChar); + } + else + // tt based fonts mustn't reencode, the encoding is implied by the fontname + // same for symbol type1 fonts, dont try to touch them + { + nChar += psp::appendStr ("(", pSetFont + nChar); + nChar += psp::appendStr (rCurrent.maFont.getStr(), + pSetFont + nChar); + nChar += psp::appendStr (") cvn findfont ", + pSetFont + nChar); + } + + if( ! rCurrent.mbArtItalic ) + { + nChar += psp::getValueOf (nTextWidth, pSetFont + nChar); + nChar += psp::appendStr (" ", pSetFont + nChar); + nChar += psp::getValueOf (-nTextHeight, pSetFont + nChar); + nChar += psp::appendStr (" matrix scale makefont setfont\n", pSetFont + nChar); + } + else // skew 15 degrees to right + { + nChar += psp::appendStr ( " [", pSetFont + nChar); + nChar += psp::getValueOf (nTextWidth, pSetFont + nChar); + nChar += psp::appendStr (" 0 ", pSetFont + nChar); + nChar += psp::getValueOfDouble (pSetFont + nChar, 0.27*(double)nTextWidth, 3 ); + nChar += psp::appendStr ( " ", pSetFont + nChar); + nChar += psp::getValueOf (-nTextHeight, pSetFont + nChar); + + nChar += psp::appendStr (" 0 0] makefont setfont\n", pSetFont + nChar); + } + + WritePS (mpPageBody, pSetFont); + } +} + +void +PrinterGfx::PSRotate (sal_Int32 nAngle) +{ + sal_Int32 nPostScriptAngle = -nAngle; + while( nPostScriptAngle < 0 ) + nPostScriptAngle += 3600; + + if (nPostScriptAngle == 0) + return; + + sal_Int32 nFullAngle = nPostScriptAngle / 10; + sal_Int32 nTenthAngle = nPostScriptAngle % 10; + + sal_Char pRotate [48]; + sal_Int32 nChar = 0; + + nChar = psp::getValueOf (nFullAngle, pRotate); + nChar += psp::appendStr (".", pRotate + nChar); + nChar += psp::getValueOf (nTenthAngle, pRotate + nChar); + nChar += psp::appendStr (" rotate\n", pRotate + nChar); + + WritePS (mpPageBody, pRotate); +} + +void +PrinterGfx::PSPointOp (const Point& rPoint, const sal_Char* pOperator) +{ + sal_Char pPSCommand [48]; + sal_Int32 nChar = 0; + + nChar = psp::getValueOf (rPoint.X(), pPSCommand); + nChar += psp::appendStr (" ", pPSCommand + nChar); + nChar += psp::getValueOf (rPoint.Y(), pPSCommand + nChar); + nChar += psp::appendStr (" ", pPSCommand + nChar); + nChar += psp::appendStr (pOperator, pPSCommand + nChar); + nChar += psp::appendStr ("\n", pPSCommand + nChar); + + DBG_ASSERT (nChar < 48, "Buffer overflow in PSPointOp"); + + WritePS (mpPageBody, pPSCommand); +} + +void +PrinterGfx::PSTranslate (const Point& rPoint) +{ + PSPointOp (rPoint, "translate"); +} + +void +PrinterGfx::PSMoveTo (const Point& rPoint) +{ + PSPointOp (rPoint, "moveto"); +} + +void +PrinterGfx::PSLineTo (const Point& rPoint) +{ + PSPointOp (rPoint, "lineto"); +} + +void +PrinterGfx::PSRMoveTo (sal_Int32 nDx, sal_Int32 nDy) +{ + Point aPoint(nDx, nDy); + PSPointOp (aPoint, "rmoveto"); +} + +/* get a compressed representation of the path information */ + +#define DEBUG_BINPATH 0 + +void +PrinterGfx::PSBinLineTo (const Point& rCurrent, Point& rOld, sal_Int32& nColumn) +{ +#if (DEBUG_BINPATH == 1) + PSLineTo (rCurrent); +#else + PSBinPath (rCurrent, rOld, lineto, nColumn); +#endif +} + +void +PrinterGfx::PSBinMoveTo (const Point& rCurrent, Point& rOld, sal_Int32& nColumn) +{ +#if (DEBUG_BINPATH == 1) + PSMoveTo (rCurrent); +#else + PSBinPath (rCurrent, rOld, moveto, nColumn); +#endif +} + +void +PrinterGfx::PSBinStartPath () +{ +#if (DEBUG_BINPATH == 1) + WritePS (mpPageBody, "% PSBinStartPath\n"); +#else + WritePS (mpPageBody, "readpath\n" ); +#endif +} + +void +PrinterGfx::PSBinEndPath () +{ +#if (DEBUG_BINPATH == 1) + WritePS (mpPageBody, "% PSBinEndPath\n"); +#else + WritePS (mpPageBody, "~\n"); +#endif +} + +void +PrinterGfx::PSBinCurrentPath (sal_uInt32 nPoints, const Point* pPath) +{ + // create the path + Point aPoint (0, 0); + sal_Int32 nColumn = 0; + + PSBinStartPath (); + PSBinMoveTo (*pPath, aPoint, nColumn); + for (unsigned int i = 1; i < nPoints; i++) + PSBinLineTo (pPath[i], aPoint, nColumn); + PSBinEndPath (); +} + +void +PrinterGfx::PSBinPath (const Point& rCurrent, Point& rOld, + pspath_t eType, sal_Int32& nColumn) +{ + sal_Char pPath[48]; + sal_Int32 nChar; + + // create the hex representation of the dx and dy path shift, store the field + // width as it is needed for the building the command + sal_Int32 nXPrec = getAlignedHexValueOf (rCurrent.X() - rOld.X(), pPath + 1); + sal_Int32 nYPrec = getAlignedHexValueOf (rCurrent.Y() - rOld.Y(), pPath + 1 + nXPrec); + pPath [ 1 + nXPrec + nYPrec ] = 0; + + // build the command, it is a char with bit represention 000cxxyy + // c represents the char, xx and yy repr. the field width of the dx and dy shift, + // dx and dy represent the number of bytes to read after the opcode + sal_Char cCmd = (eType == lineto ? (sal_Char)0x00 : (sal_Char)0x10); + switch (nYPrec) + { + case 2: break; + case 4: cCmd |= 0x01; break; + case 6: cCmd |= 0x02; break; + case 8: cCmd |= 0x03; break; + default: OSL_FAIL("invalid x precision in binary path"); + } + switch (nXPrec) + { + case 2: break; + case 4: cCmd |= 0x04; break; + case 6: cCmd |= 0x08; break; + case 8: cCmd |= 0x0c; break; + default: OSL_FAIL("invalid y precision in binary path"); + } + cCmd += 'A'; + pPath[0] = cCmd; + + // write the command to file, + // line breaking at column nMaxTextColumn (80) + nChar = 1 + nXPrec + nYPrec; + if ((nColumn + nChar) > nMaxTextColumn) + { + sal_Int32 nSegment = nMaxTextColumn - nColumn; + + WritePS (mpPageBody, pPath, nSegment); + WritePS (mpPageBody, "\n", 1); + WritePS (mpPageBody, pPath + nSegment, nChar - nSegment); + + nColumn = nChar - nSegment; + } + else + { + WritePS (mpPageBody, pPath, nChar); + + nColumn += nChar; + } + + rOld = rCurrent; +} + +void +PrinterGfx::PSScale (double fScaleX, double fScaleY) +{ + sal_Char pScale [48]; + sal_Int32 nChar = 0; + + nChar = psp::getValueOfDouble (pScale, fScaleX, 5); + nChar += psp::appendStr (" ", pScale + nChar); + nChar += psp::getValueOfDouble (pScale + nChar, fScaleY, 5); + nChar += psp::appendStr (" scale\n", pScale + nChar); + + WritePS (mpPageBody, pScale); +} + +/* psshowtext helper routines: draw an hex string for show/xshow */ +void +PrinterGfx::PSHexString (const sal_uChar* pString, sal_Int16 nLen) +{ + sal_Char pHexString [128]; + sal_Int32 nChar = 0; + + nChar = psp::appendStr ("<", pHexString); + for (int i = 0; i < nLen; i++) + { + if (nChar >= (nMaxTextColumn - 1)) + { + nChar += psp::appendStr ("\n", pHexString + nChar); + WritePS (mpPageBody, pHexString, nChar); + nChar = 0; + } + nChar += psp::getHexValueOf ((sal_Int32)pString[i], pHexString + nChar); + } + + nChar += psp::appendStr (">\n", pHexString + nChar); + WritePS (mpPageBody, pHexString, nChar); +} + +/* psshowtext helper routines: draw an array for xshow ps operator */ +void +PrinterGfx::PSDeltaArray (const sal_Int32 *pArray, sal_Int16 nEntries) +{ + sal_Char pPSArray [128]; + sal_Int32 nChar = 0; + + nChar = psp::appendStr ("[", pPSArray + nChar); + nChar += psp::getValueOf (pArray[0], pPSArray + nChar); + + for (int i = 1; i < nEntries; i++) + { + if (nChar >= (nMaxTextColumn - 1)) + { + nChar += psp::appendStr ("\n", pPSArray + nChar); + WritePS (mpPageBody, pPSArray, nChar); + nChar = 0; + } + + nChar += psp::appendStr (" ", pPSArray + nChar); + nChar += psp::getValueOf (pArray[i] - pArray[i-1], pPSArray + nChar); + } + + nChar += psp::appendStr (" 0]\n", pPSArray + nChar); + WritePS (mpPageBody, pPSArray); +} + +/* the DrawText equivalent, pDeltaArray may be NULL. For Type1 fonts or single byte + * fonts in general nBytes and nGlyphs is the same. For printer resident Composite + * fonts it may be different (these fonts may be SJIS encoded for example) */ +void +PrinterGfx::PSShowText (const sal_uChar* pStr, sal_Int16 nGlyphs, sal_Int16 nBytes, + const sal_Int32* pDeltaArray) +{ + PSSetColor (maTextColor); + PSSetColor (); + PSSetFont (); + // rotate the user coordinate system + if (mnTextAngle != 0) + { + PSGSave (); + PSRotate (mnTextAngle); + } + + sal_Char pBuffer[256]; + if( maVirtualStatus.mbArtBold ) + { + sal_Int32 nLW = maVirtualStatus.mnTextWidth; + if( nLW == 0 ) + nLW = maVirtualStatus.mnTextHeight; + else + nLW = nLW < maVirtualStatus.mnTextHeight ? nLW : maVirtualStatus.mnTextHeight; + psp::getValueOfDouble( pBuffer, (double)nLW / 30.0 ); + } + // dispatch to the drawing method + if (pDeltaArray == NULL) + { + PSHexString (pStr, nBytes); + + if( maVirtualStatus.mbArtBold ) + { + WritePS( mpPageBody, pBuffer ); + WritePS( mpPageBody, " bshow\n" ); + } + else + WritePS (mpPageBody, "show\n"); + } + else + { + PSHexString (pStr, nBytes); + PSDeltaArray (pDeltaArray, nGlyphs - 1); + if( maVirtualStatus.mbArtBold ) + { + WritePS( mpPageBody, pBuffer ); + WritePS( mpPageBody, " bxshow\n" ); + } + else + WritePS (mpPageBody, "xshow\n"); + } + + // restore the user coordinate system + if (mnTextAngle != 0) + PSGRestore (); +} + +void +PrinterGfx::PSComment( const sal_Char* pComment ) +{ + const sal_Char* pLast = pComment; + while( pComment && *pComment ) + { + while( *pComment && *pComment != '\n' && *pComment != '\r' ) + pComment++; + if( pComment - pLast > 1 ) + { + WritePS( mpPageBody, "% ", 2 ); + WritePS( mpPageBody, pLast, pComment - pLast ); + WritePS( mpPageBody, "\n", 1 ); + } + if( *pComment ) + pLast = ++pComment; + } +} + +sal_Bool +PrinterGfx::DrawEPS( const Rectangle& rBoundingBox, void* pPtr, sal_uInt32 nSize ) +{ + if( nSize == 0 ) + return sal_True; + if( ! mpPageBody ) + return sal_False; + + sal_Bool bSuccess = sal_False; + + // first search the BoundingBox of the EPS data + SvMemoryStream aStream( pPtr, nSize, STREAM_READ ); + aStream.Seek( STREAM_SEEK_TO_BEGIN ); + ByteString aLine; + + ByteString aDocTitle; + double fLeft = 0, fRight = 0, fTop = 0, fBottom = 0; + bool bEndComments = false; + while( ! aStream.IsEof() + && ( ( fLeft == 0 && fRight == 0 && fTop == 0 && fBottom == 0 ) || + ( aDocTitle.Len() == 0 && bEndComments == false ) ) + ) + { + aStream.ReadLine( aLine ); + if( aLine.Len() > 1 && aLine.GetChar( 0 ) == '%' ) + { + char cChar = aLine.GetChar(1); + if( cChar == '%' ) + { + if( aLine.CompareIgnoreCaseToAscii( "%%BoundingBox:", 14 ) == COMPARE_EQUAL ) + { + aLine = WhitespaceToSpace( aLine.GetToken( 1, ':' ) ); + if( aLine.Len() && aLine.Search( "atend" ) == STRING_NOTFOUND ) + { + fLeft = StringToDouble( GetCommandLineToken( 0, aLine ) ); + fBottom = StringToDouble( GetCommandLineToken( 1, aLine ) ); + fRight = StringToDouble( GetCommandLineToken( 2, aLine ) ); + fTop = StringToDouble( GetCommandLineToken( 3, aLine ) ); + } + } + else if( aLine.CompareIgnoreCaseToAscii( "%%Title:", 8 ) == COMPARE_EQUAL ) + aDocTitle = WhitespaceToSpace( aLine.Copy( 8 ) ); + else if( aLine.CompareIgnoreCaseToAscii( "%%EndComments", 13 ) == COMPARE_EQUAL ) + bEndComments = true; + } + else if( cChar == ' ' || cChar == '\t' || cChar == '\r' || cChar == '\n' ) + bEndComments = true; + } + else + bEndComments = true; + } + + static sal_uInt16 nEps = 0; + if( ! aDocTitle.Len() ) + aDocTitle = ByteString::CreateFromInt32( (sal_Int32)(nEps++) ); + + if( fLeft != fRight && fTop != fBottom ) + { + double fScaleX = (double)rBoundingBox.GetWidth()/(fRight-fLeft); + double fScaleY = -(double)rBoundingBox.GetHeight()/(fTop-fBottom); + Point aTranslatePoint( (int)(rBoundingBox.Left()-fLeft*fScaleX), + (int)(rBoundingBox.Bottom()+1-fBottom*fScaleY) ); + // prepare EPS + WritePS( mpPageBody, + "/b4_Inc_state save def\n" + "/dict_count countdictstack def\n" + "/op_count count 1 sub def\n" + "userdict begin\n" + "/showpage {} def\n" + "0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin\n" + "10 setmiterlimit [] 0 setdash newpath\n" + "/languagelevel where\n" + "{pop languagelevel\n" + "1 ne\n" + " {false setstrokeadjust false setoverprint\n" + " } if\n" + "}if\n" ); + // set up clip path and scale + BeginSetClipRegion( 1 ); + UnionClipRegion( rBoundingBox.Left(), rBoundingBox.Top(), rBoundingBox.GetWidth(), rBoundingBox.GetHeight() ); + EndSetClipRegion(); + PSTranslate( aTranslatePoint ); + PSScale( fScaleX, fScaleY ); + + // DSC requires BeginDocument + WritePS( mpPageBody, "%%BeginDocument: " ); + WritePS( mpPageBody, aDocTitle ); + WritePS( mpPageBody, "\n" ); + + // write the EPS data + sal_uInt64 nOutLength; + mpPageBody->write( pPtr, nSize, nOutLength ); + bSuccess = nOutLength == nSize; + + // corresponding EndDocument + if( ((char*)pPtr)[ nSize-1 ] != '\n' ) + WritePS( mpPageBody, "\n" ); + WritePS( mpPageBody, "%%EndDocument\n" ); + + // clean up EPS + WritePS( mpPageBody, + "count op_count sub {pop} repeat\n" + "countdictstack dict_count sub {end} repeat\n" + "b4_Inc_state restore\n" ); + } + return bSuccess; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/printergfx/glyphset.cxx b/vcl/unx/generic/printergfx/glyphset.cxx new file mode 100644 index 000000000000..967c9f2a175f --- /dev/null +++ b/vcl/unx/generic/printergfx/glyphset.cxx @@ -0,0 +1,949 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include "glyphset.hxx" +#include "psputil.hxx" + +#include "sft.hxx" + +#include "printergfx.hxx" +#include "fontsubset.hxx" +#include "vcl/fontmanager.hxx" + +#include "osl/thread.h" + +#include "sal/alloca.h" + +#include "rtl/ustring.hxx" +#include "rtl/strbuf.hxx" + +#include <set> +#include <map> +#include <algorithm> + +using namespace vcl; +using namespace psp; + +using ::rtl::OUString; +using ::rtl::OString; +using ::rtl::OStringBuffer; +using ::rtl::OUStringToOString; + +GlyphSet::GlyphSet () + : mnFontID (-1), + mbVertical (0), + mbUseFontEncoding (false) +{} + +GlyphSet::GlyphSet (sal_Int32 nFontID, sal_Bool bVertical) + : mnFontID (nFontID), + mbVertical (bVertical) +{ + PrintFontManager &rMgr = PrintFontManager::get(); + meBaseType = rMgr.getFontType (mnFontID); + maBaseName = OUStringToOString (rMgr.getPSName(mnFontID), + RTL_TEXTENCODING_ASCII_US); + mnBaseEncoding = rMgr.getFontEncoding(mnFontID); + mbUseFontEncoding = rMgr.getUseOnlyFontEncoding(mnFontID); +} + +GlyphSet::~GlyphSet () +{ + /* FIXME delete the glyphlist ??? */ +} + +sal_Int32 +GlyphSet::GetFontID () +{ + return mnFontID; +} + +fonttype::type +GlyphSet::GetFontType () +{ + return meBaseType; +} + +sal_Bool +GlyphSet::IsVertical () +{ + return mbVertical; +} + +sal_Bool +GlyphSet::SetFont (sal_Int32 nFontID, sal_Bool bVertical) +{ + if (mnFontID != -1) + return sal_False; + + mnFontID = nFontID; + mbVertical = bVertical; + + PrintFontManager &rMgr = PrintFontManager::get(); + meBaseType = rMgr.getFontType (mnFontID); + maBaseName = OUStringToOString (rMgr.getPSName(mnFontID), + RTL_TEXTENCODING_ASCII_US); + mnBaseEncoding = rMgr.getFontEncoding(mnFontID); + mbUseFontEncoding = rMgr.getUseOnlyFontEncoding(mnFontID); + + return sal_True; +} + +sal_Bool +GlyphSet::GetCharID ( + sal_Unicode nChar, + sal_uChar* nOutGlyphID, + sal_Int32* nOutGlyphSetID + ) +{ + return LookupCharID (nChar, nOutGlyphID, nOutGlyphSetID) + || AddCharID (nChar, nOutGlyphID, nOutGlyphSetID); +} + +sal_Bool +GlyphSet::GetGlyphID ( + sal_uInt32 nGlyph, + sal_Unicode nUnicode, + sal_uChar* nOutGlyphID, + sal_Int32* nOutGlyphSetID + ) +{ + return LookupGlyphID (nGlyph, nOutGlyphID, nOutGlyphSetID) + || AddGlyphID (nGlyph, nUnicode, nOutGlyphID, nOutGlyphSetID); +} + +sal_Bool +GlyphSet::LookupCharID ( + sal_Unicode nChar, + sal_uChar* nOutGlyphID, + sal_Int32* nOutGlyphSetID + ) +{ + char_list_t::iterator aGlyphSet; + sal_Int32 nGlyphSetID; + + // loop thru all the font subsets + for (aGlyphSet = maCharList.begin(), nGlyphSetID = 1; + aGlyphSet != maCharList.end(); + ++aGlyphSet, nGlyphSetID++) + { + // check every subset if it contains the queried unicode char + char_map_t::const_iterator aGlyph = (*aGlyphSet).find (nChar); + if (aGlyph != (*aGlyphSet).end()) + { + // success: found the unicode char, return the glyphid and the glyphsetid + *nOutGlyphSetID = nGlyphSetID; + *nOutGlyphID = (*aGlyph).second; + return sal_True; + } + } + + *nOutGlyphSetID = -1; + *nOutGlyphID = 0; + return sal_False; +} + +sal_Bool +GlyphSet::LookupGlyphID ( + sal_uInt32 nGlyph, + sal_uChar* nOutGlyphID, + sal_Int32* nOutGlyphSetID + ) +{ + glyph_list_t::iterator aGlyphSet; + sal_Int32 nGlyphSetID; + + // loop thru all the font subsets + for (aGlyphSet = maGlyphList.begin(), nGlyphSetID = 1; + aGlyphSet != maGlyphList.end(); + ++aGlyphSet, nGlyphSetID++) + { + // check every subset if it contains the queried unicode char + glyph_map_t::const_iterator aGlyph = (*aGlyphSet).find (nGlyph); + if (aGlyph != (*aGlyphSet).end()) + { + // success: found the glyph id, return the mapped glyphid and the glyphsetid + *nOutGlyphSetID = nGlyphSetID; + *nOutGlyphID = (*aGlyph).second; + return sal_True; + } + } + + *nOutGlyphSetID = -1; + *nOutGlyphID = 0; + return sal_False; +} + +sal_uChar +GlyphSet::GetAnsiMapping (sal_Unicode nUnicodeChar) +{ + static rtl_UnicodeToTextConverter aConverter = + rtl_createUnicodeToTextConverter(RTL_TEXTENCODING_MS_1252); + static rtl_UnicodeToTextContext aContext = + rtl_createUnicodeToTextContext( aConverter ); + + sal_Char nAnsiChar; + sal_uInt32 nCvtInfo; + sal_Size nCvtChars; + const sal_uInt32 nCvtFlags = RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR + | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR; + + sal_Size nSize = rtl_convertUnicodeToText( aConverter, aContext, + &nUnicodeChar, 1, &nAnsiChar, 1, + nCvtFlags, &nCvtInfo, &nCvtChars ); + + return nSize == 1 ? (sal_uChar)nAnsiChar : (sal_uChar)0; +} + +sal_uChar +GlyphSet::GetSymbolMapping (sal_Unicode nUnicodeChar) +{ + if (0x0000 < nUnicodeChar && nUnicodeChar < 0x0100) + return (sal_uChar)nUnicodeChar; + if (0xf000 < nUnicodeChar && nUnicodeChar < 0xf100) + return (sal_uChar)nUnicodeChar; + + return 0; +} + +void +GlyphSet::AddNotdef (char_map_t &rCharMap) +{ + if (rCharMap.size() == 0) + rCharMap[0] = 0; +} + +void +GlyphSet::AddNotdef (glyph_map_t &rGlyphMap) +{ + if (rGlyphMap.size() == 0) + rGlyphMap[0] = 0; +} +sal_Bool +GlyphSet::AddCharID ( + sal_Unicode nChar, + sal_uChar* nOutGlyphID, + sal_Int32* nOutGlyphSetID + ) +{ + sal_uChar nMappedChar; + + // XXX important: avoid to reencode type1 symbol fonts + if (mnBaseEncoding == RTL_TEXTENCODING_SYMBOL) + nMappedChar = GetSymbolMapping (nChar); + else + nMappedChar = GetAnsiMapping (nChar); + + // create an empty glyphmap that is reserved for iso1252 encoded glyphs + // (or -- unencoded -- symbol glyphs) and a second map that takes any other + if (maCharList.empty()) + { + char_map_t aMap, aMapp; + + maCharList.push_back (aMap); + maCharList.push_back (aMapp); + } + // if the last map is full, create a new one + if ((!nMappedChar) && (maCharList.back().size() == 255)) + { + char_map_t aMap; + maCharList.push_back (aMap); + } + + // insert a new glyph in the font subset + if (nMappedChar) + { + // always put iso1252 chars into the first map, map them on itself + char_map_t& aGlyphSet = maCharList.front(); + AddNotdef (aGlyphSet); + + aGlyphSet [nChar] = nMappedChar; + *nOutGlyphSetID = 1; + *nOutGlyphID = nMappedChar; + } + else + { + // other chars are just appended to the list + char_map_t& aGlyphSet = maCharList.back(); + AddNotdef (aGlyphSet); + + int nSize = aGlyphSet.size(); + + aGlyphSet [nChar] = nSize; + *nOutGlyphSetID = maCharList.size(); + *nOutGlyphID = aGlyphSet [nChar]; + } + + return sal_True; +} + +sal_Bool +GlyphSet::AddGlyphID ( + sal_uInt32 nGlyph, + sal_Unicode nUnicode, + sal_uChar* nOutGlyphID, + sal_Int32* nOutGlyphSetID + ) +{ + sal_uChar nMappedChar; + + // XXX important: avoid to reencode type1 symbol fonts + if (mnBaseEncoding == RTL_TEXTENCODING_SYMBOL) + nMappedChar = GetSymbolMapping (nUnicode); + else + nMappedChar = GetAnsiMapping (nUnicode); + + // create an empty glyphmap that is reserved for iso1252 encoded glyphs + // (or -- unencoded -- symbol glyphs) and a second map that takes any other + if (maGlyphList.empty()) + { + glyph_map_t aMap, aMapp; + + maGlyphList.push_back (aMap); + maGlyphList.push_back (aMapp); + } + // if the last map is full, create a new one + if ((!nMappedChar) && (maGlyphList.back().size() == 255)) + { + glyph_map_t aMap; + maGlyphList.push_back (aMap); + } + + // insert a new glyph in the font subset + if (nMappedChar) + { + // always put iso1252 chars into the first map, map them on itself + glyph_map_t& aGlyphSet = maGlyphList.front(); + AddNotdef (aGlyphSet); + + aGlyphSet [nGlyph] = nMappedChar; + *nOutGlyphSetID = 1; + *nOutGlyphID = nMappedChar; + } + else + { + // other chars are just appended to the list + glyph_map_t& aGlyphSet = maGlyphList.back(); + AddNotdef (aGlyphSet); + + int nSize = aGlyphSet.size(); + + aGlyphSet [nGlyph] = nSize; + *nOutGlyphSetID = maGlyphList.size(); + *nOutGlyphID = aGlyphSet [nGlyph]; + } + + return sal_True; +} + +OString +GlyphSet::GetCharSetName (sal_Int32 nGlyphSetID) +{ + if (meBaseType == fonttype::TrueType) + { + OStringBuffer aSetName( maBaseName.getLength() + 32 ); + aSetName.append( maBaseName ); + aSetName.append( "FID" ); + aSetName.append( mnFontID ); + aSetName.append( mbVertical ? "VCSet" : "HCSet" ); + aSetName.append( nGlyphSetID ); + return aSetName.makeStringAndClear(); + } + else + /* (meBaseType == fonttype::Type1 || meBaseType == fonttype::Builtin) */ + { + return maBaseName; + } +} + +OString +GlyphSet::GetGlyphSetName (sal_Int32 nGlyphSetID) +{ + if (meBaseType == fonttype::TrueType) + { + OStringBuffer aSetName( maBaseName.getLength() + 32 ); + aSetName.append( maBaseName ); + aSetName.append( "FID" ); + aSetName.append( mnFontID ); + aSetName.append( mbVertical ? "VGSet" : "HGSet" ); + aSetName.append( nGlyphSetID ); + return aSetName.makeStringAndClear(); + } + else + /* (meBaseType == fonttype::Type1 || meBaseType == fonttype::Builtin) */ + { + return maBaseName; + } +} + +sal_Int32 +GlyphSet::GetGlyphSetEncoding (sal_Int32 nGlyphSetID) +{ + if (meBaseType == fonttype::TrueType) + return RTL_TEXTENCODING_DONTKNOW; + else + { + /* (meBaseType == fonttype::Type1 || meBaseType == fonttype::Builtin) */ + if (mnBaseEncoding == RTL_TEXTENCODING_SYMBOL) + return RTL_TEXTENCODING_SYMBOL; + else + return nGlyphSetID == 1 ? RTL_TEXTENCODING_MS_1252 + : RTL_TEXTENCODING_USER_START + nGlyphSetID; + } +} + +OString +GlyphSet::GetGlyphSetEncodingName (rtl_TextEncoding nEnc, const OString &rFontName) +{ + if ( nEnc == RTL_TEXTENCODING_MS_1252 + || nEnc == RTL_TEXTENCODING_ISO_8859_1) + { + return OString("ISO1252Encoding"); + } + else + if (nEnc >= RTL_TEXTENCODING_USER_START && nEnc <= RTL_TEXTENCODING_USER_END) + { + return rFontName + + OString("Enc") + + OString::valueOf ((sal_Int32)(nEnc - RTL_TEXTENCODING_USER_START)); + } + else + { + return OString(); + } +} + +OString +GlyphSet::GetGlyphSetEncodingName (sal_Int32 nGlyphSetID) +{ + return GetGlyphSetEncodingName (GetGlyphSetEncoding(nGlyphSetID), maBaseName); +} + +void +GlyphSet::PSDefineReencodedFont (osl::File* pOutFile, sal_Int32 nGlyphSetID) +{ + // only for ps fonts + if ((meBaseType != fonttype::Builtin) && (meBaseType != fonttype::Type1)) + return; + + sal_Char pEncodingVector [256]; + sal_Int32 nSize = 0; + + nSize += psp::appendStr ("(", pEncodingVector + nSize); + nSize += psp::appendStr (GetReencodedFontName(nGlyphSetID), + pEncodingVector + nSize); + nSize += psp::appendStr (") cvn (", pEncodingVector + nSize); + nSize += psp::appendStr (maBaseName.getStr(), + pEncodingVector + nSize); + nSize += psp::appendStr (") cvn ", pEncodingVector + nSize); + nSize += psp::appendStr (GetGlyphSetEncodingName(nGlyphSetID), + pEncodingVector + nSize); + nSize += psp::appendStr (" psp_definefont\n", + pEncodingVector + nSize); + + psp::WritePS (pOutFile, pEncodingVector); +} + +OString +GlyphSet::GetReencodedFontName (rtl_TextEncoding nEnc, const OString &rFontName) +{ + if ( nEnc == RTL_TEXTENCODING_MS_1252 + || nEnc == RTL_TEXTENCODING_ISO_8859_1) + { + return rFontName + + OString("-iso1252"); + } + else + if (nEnc >= RTL_TEXTENCODING_USER_START && nEnc <= RTL_TEXTENCODING_USER_END) + { + return rFontName + + OString("-enc") + + OString::valueOf ((sal_Int32)(nEnc - RTL_TEXTENCODING_USER_START)); + } + else + { + return OString(); + } +} + +OString +GlyphSet::GetReencodedFontName (sal_Int32 nGlyphSetID) +{ + return GetReencodedFontName (GetGlyphSetEncoding(nGlyphSetID), maBaseName); +} + +void GlyphSet::DrawGlyphs( + PrinterGfx& rGfx, + const Point& rPoint, + const sal_uInt32* pGlyphIds, + const sal_Unicode* pUnicodes, + sal_Int16 nLen, + const sal_Int32* pDeltaArray ) +{ + sal_uChar *pGlyphID = (sal_uChar*)alloca (nLen * sizeof(sal_uChar)); + sal_Int32 *pGlyphSetID = (sal_Int32*)alloca (nLen * sizeof(sal_Int32)); + std::set< sal_Int32 > aGlyphSet; + + // convert unicode to font glyph id and font subset + for (int nChar = 0; nChar < nLen; nChar++) + { + GetGlyphID (pGlyphIds[nChar], pUnicodes[nChar], pGlyphID + nChar, pGlyphSetID + nChar); + aGlyphSet.insert (pGlyphSetID[nChar]); + } + + // loop over all glyph sets to detect substrings that can be xshown together + // without changing the postscript font + sal_Int32 *pDeltaSubset = (sal_Int32*)alloca (nLen * sizeof(sal_Int32)); + sal_uChar *pGlyphSubset = (sal_uChar*)alloca (nLen * sizeof(sal_uChar)); + + std::set< sal_Int32 >::iterator aSet; + for (aSet = aGlyphSet.begin(); aSet != aGlyphSet.end(); ++aSet) + { + Point aPoint = rPoint; + sal_Int32 nOffset = 0; + sal_Int32 nGlyphs = 0; + sal_Int32 nChar; + + // get offset to first glyph + for (nChar = 0; (nChar < nLen) && (pGlyphSetID[nChar] != *aSet); nChar++) + { + nOffset = pDeltaArray [nChar]; + } + + // loop over all chars to extract those that share the current glyph set + for (nChar = 0; nChar < nLen; nChar++) + { + if (pGlyphSetID[nChar] == *aSet) + { + pGlyphSubset [nGlyphs] = pGlyphID [nChar]; + // the offset to the next glyph is determined by the glyph in + // front of the next glyph with the same glyphset id + // most often, this will be the current glyph + while ((nChar + 1) < nLen) + { + if (pGlyphSetID[nChar + 1] == *aSet) + break; + else + nChar += 1; + } + pDeltaSubset [nGlyphs] = pDeltaArray[nChar] - nOffset; + + nGlyphs += 1; + } + } + + // show the text using the PrinterGfx text api + aPoint.Move (nOffset, 0); + + OString aGlyphSetName(GetGlyphSetName(*aSet)); + rGfx.PSSetFont (aGlyphSetName, GetGlyphSetEncoding(*aSet)); + rGfx.PSMoveTo (aPoint); + rGfx.PSShowText (pGlyphSubset, nGlyphs, nGlyphs, nGlyphs > 1 ? pDeltaSubset : NULL); + } +} + +void +GlyphSet::DrawText (PrinterGfx &rGfx, const Point& rPoint, + const sal_Unicode* pStr, sal_Int16 nLen, const sal_Int32* pDeltaArray) +{ + // dispatch to the impl method + if (pDeltaArray == NULL) + ImplDrawText (rGfx, rPoint, pStr, nLen); + else + ImplDrawText (rGfx, rPoint, pStr, nLen, pDeltaArray); +} + +void +GlyphSet::ImplDrawText (PrinterGfx &rGfx, const Point& rPoint, + const sal_Unicode* pStr, sal_Int16 nLen) +{ + rGfx.PSMoveTo (rPoint); + + if( mbUseFontEncoding ) + { + OString aPSName( OUStringToOString( rGfx.GetFontMgr().getPSName( mnFontID ), RTL_TEXTENCODING_ISO_8859_1 ) ); + OString aBytes( OUStringToOString( OUString( pStr, nLen ), mnBaseEncoding ) ); + rGfx.PSSetFont( aPSName, mnBaseEncoding ); + rGfx.PSShowText( (const unsigned char*)aBytes.getStr(), nLen, aBytes.getLength() ); + return; + } + + int nChar; + sal_uChar *pGlyphID = (sal_uChar*)alloca (nLen * sizeof(sal_uChar)); + sal_Int32 *pGlyphSetID = (sal_Int32*)alloca (nLen * sizeof(sal_Int32)); + + // convert unicode to glyph id and char set (font subset) + for (nChar = 0; nChar < nLen; nChar++) + GetCharID (pStr[nChar], pGlyphID + nChar, pGlyphSetID + nChar); + + // loop over the string to draw subsequent pieces of chars + // with the same postscript font + for (nChar = 0; nChar < nLen; /* atend */) + { + sal_Int32 nGlyphSetID = pGlyphSetID [nChar]; + sal_Int32 nGlyphs = 1; + for (int nNextChar = nChar + 1; nNextChar < nLen; nNextChar++) + { + if (pGlyphSetID[nNextChar] == nGlyphSetID) + nGlyphs++; + else + break; + } + + // show the text using the PrinterGfx text api + OString aGlyphSetName(GetCharSetName(nGlyphSetID)); + rGfx.PSSetFont (aGlyphSetName, GetGlyphSetEncoding(nGlyphSetID)); + rGfx.PSShowText (pGlyphID + nChar, nGlyphs, nGlyphs); + + nChar += nGlyphs; + } +} + +void +GlyphSet::ImplDrawText (PrinterGfx &rGfx, const Point& rPoint, + const sal_Unicode* pStr, sal_Int16 nLen, const sal_Int32* pDeltaArray) +{ + if( mbUseFontEncoding ) + { + OString aPSName( OUStringToOString( rGfx.GetFontMgr().getPSName( mnFontID ), RTL_TEXTENCODING_ISO_8859_1 ) ); + OString aBytes( OUStringToOString( OUString( pStr, nLen ), mnBaseEncoding ) ); + rGfx.PSMoveTo( rPoint ); + rGfx.PSSetFont( aPSName, mnBaseEncoding ); + rGfx.PSShowText( (const unsigned char*)aBytes.getStr(), nLen, aBytes.getLength(), pDeltaArray ); + return; + } + + sal_uChar *pGlyphID = (sal_uChar*)alloca (nLen * sizeof(sal_uChar)); + sal_Int32 *pGlyphSetID = (sal_Int32*)alloca (nLen * sizeof(sal_Int32)); + std::set< sal_Int32 > aGlyphSet; + + // convert unicode to font glyph id and font subset + for (int nChar = 0; nChar < nLen; nChar++) + { + GetCharID (pStr[nChar], pGlyphID + nChar, pGlyphSetID + nChar); + aGlyphSet.insert (pGlyphSetID[nChar]); + } + + // loop over all glyph sets to detect substrings that can be xshown together + // without changing the postscript font + sal_Int32 *pDeltaSubset = (sal_Int32*)alloca (nLen * sizeof(sal_Int32)); + sal_uChar *pGlyphSubset = (sal_uChar*)alloca (nLen * sizeof(sal_uChar)); + + std::set< sal_Int32 >::iterator aSet; + for (aSet = aGlyphSet.begin(); aSet != aGlyphSet.end(); ++aSet) + { + Point aPoint = rPoint; + sal_Int32 nOffset = 0; + sal_Int32 nGlyphs = 0; + sal_Int32 nChar; + + // get offset to first glyph + for (nChar = 0; (nChar < nLen) && (pGlyphSetID[nChar] != *aSet); nChar++) + { + nOffset = pDeltaArray [nChar]; + } + + // loop over all chars to extract those that share the current glyph set + for (nChar = 0; nChar < nLen; nChar++) + { + if (pGlyphSetID[nChar] == *aSet) + { + pGlyphSubset [nGlyphs] = pGlyphID [nChar]; + // the offset to the next glyph is determined by the glyph in + // front of the next glyph with the same glyphset id + // most often, this will be the current glyph + while ((nChar + 1) < nLen) + { + if (pGlyphSetID[nChar + 1] == *aSet) + break; + else + nChar += 1; + } + pDeltaSubset [nGlyphs] = pDeltaArray[nChar] - nOffset; + + nGlyphs += 1; + } + } + + // show the text using the PrinterGfx text api + aPoint.Move (nOffset, 0); + + OString aGlyphSetName(GetCharSetName(*aSet)); + rGfx.PSSetFont (aGlyphSetName, GetGlyphSetEncoding(*aSet)); + rGfx.PSMoveTo (aPoint); + rGfx.PSShowText (pGlyphSubset, nGlyphs, nGlyphs, nGlyphs > 1 ? pDeltaSubset : NULL); + } +} + +sal_Bool +GlyphSet::PSUploadEncoding(osl::File* pOutFile, PrinterGfx &rGfx) +{ + // only for ps fonts + if ((meBaseType != fonttype::Builtin) && (meBaseType != fonttype::Type1)) + return sal_False; + if (mnBaseEncoding == RTL_TEXTENCODING_SYMBOL) + return sal_False; + + PrintFontManager &rMgr = rGfx.GetFontMgr(); + + // loop thru all the font subsets + sal_Int32 nGlyphSetID = 0; + char_list_t::iterator aGlyphSet; + for (aGlyphSet = maCharList.begin(); aGlyphSet != maCharList.end(); ++aGlyphSet) + { + ++nGlyphSetID; + + if (nGlyphSetID == 1) // latin1 page uses global reencoding table + { + PSDefineReencodedFont (pOutFile, nGlyphSetID); + continue; + } + if ((*aGlyphSet).size() == 0) // empty set, doesn't need reencoding + { + continue; + } + + // create reencoding table + + sal_Char pEncodingVector [256]; + sal_Int32 nSize = 0; + + nSize += psp::appendStr ("/", + pEncodingVector + nSize); + nSize += psp::appendStr (GetGlyphSetEncodingName(nGlyphSetID), + pEncodingVector + nSize); + nSize += psp::appendStr (" [ ", + pEncodingVector + nSize); + + // need a list of glyphs, sorted by glyphid + typedef std::map< sal_uInt8, sal_Unicode > ps_mapping_t; + typedef ps_mapping_t::value_type ps_value_t; + ps_mapping_t aSortedGlyphSet; + + char_map_t::const_iterator aUnsortedGlyph; + for (aUnsortedGlyph = (*aGlyphSet).begin(); + aUnsortedGlyph != (*aGlyphSet).end(); + ++aUnsortedGlyph) + { + aSortedGlyphSet.insert(ps_value_t((*aUnsortedGlyph).second, + (*aUnsortedGlyph).first)); + } + + ps_mapping_t::const_iterator aSortedGlyph; + // loop thru all the glyphs in the subset + for (aSortedGlyph = (aSortedGlyphSet).begin(); + aSortedGlyph != (aSortedGlyphSet).end(); + ++aSortedGlyph) + { + nSize += psp::appendStr ("/", + pEncodingVector + nSize); + + std::list< OString > aName( rMgr.getAdobeNameFromUnicode((*aSortedGlyph).second) ); + + if( aName.begin() != aName.end() ) + nSize += psp::appendStr ( aName.front(), pEncodingVector + nSize); + else + nSize += psp::appendStr (".notdef", pEncodingVector + nSize ); + nSize += psp::appendStr (" ", pEncodingVector + nSize); + // flush line + if (nSize >= 70) + { + nSize += psp::appendStr ("\n", pEncodingVector + nSize); + psp::WritePS (pOutFile, pEncodingVector); + nSize = 0; + } + } + + nSize += psp::appendStr ("] def\n", pEncodingVector + nSize); + psp::WritePS (pOutFile, pEncodingVector); + + PSDefineReencodedFont (pOutFile, nGlyphSetID); + } + + return sal_True; +} + +struct EncEntry +{ + sal_uChar aEnc; + long aGID; + + EncEntry() : aEnc( 0 ), aGID( 0 ) {} + + bool operator<( const EncEntry& rRight ) const + { return aEnc < rRight.aEnc; } +}; + +static void CreatePSUploadableFont( TrueTypeFont* pSrcFont, FILE* pTmpFile, + const char* pGlyphSetName, int nGlyphCount, + /*const*/ sal_uInt16* pRequestedGlyphs, /*const*/ sal_uChar* pEncoding, + bool bAllowType42, bool /*bAllowCID*/ ) +{ + // match the font-subset to the printer capabilities + // TODO: allow CFF for capable printers + int nTargetMask = FontSubsetInfo::TYPE1_PFA | FontSubsetInfo::TYPE3_FONT; + if( bAllowType42 ) + nTargetMask |= FontSubsetInfo::TYPE42_FONT; + + std::vector< EncEntry > aSorted( nGlyphCount, EncEntry() ); + for( int i = 0; i < nGlyphCount; i++ ) + { + aSorted[i].aEnc = pEncoding[i]; + aSorted[i].aGID = pRequestedGlyphs[i]; + } + + std::stable_sort( aSorted.begin(), aSorted.end() ); + + std::vector< sal_uChar > aEncoding( nGlyphCount ); + std::vector< long > aRequestedGlyphs( nGlyphCount ); + + for( int i = 0; i < nGlyphCount; i++ ) + { + aEncoding[i] = aSorted[i].aEnc; + aRequestedGlyphs[i] = aSorted[i].aGID; + } + + FontSubsetInfo aInfo; + aInfo.LoadFont( pSrcFont ); + + aInfo.CreateFontSubset( nTargetMask, pTmpFile, pGlyphSetName, + &aRequestedGlyphs[0], &aEncoding[0], nGlyphCount, NULL ); +} + +sal_Bool +GlyphSet::PSUploadFont (osl::File& rOutFile, PrinterGfx &rGfx, bool bAllowType42, std::list< OString >& rSuppliedFonts ) +{ + // only for truetype fonts + if (meBaseType != fonttype::TrueType) + return sal_False; + + TrueTypeFont *pTTFont; + OString aTTFileName (rGfx.GetFontMgr().getFontFileSysPath(mnFontID)); + int nFace = rGfx.GetFontMgr().getFontFaceNumber(mnFontID); + sal_Int32 nSuccess = OpenTTFontFile(aTTFileName.getStr(), nFace < 0 ? 0 : nFace, &pTTFont); + if (nSuccess != SF_OK) + return sal_False; + FILE* pTmpFile = tmpfile(); + if (pTmpFile == NULL) + return sal_False; + + // array of unicode source characters + sal_Unicode pUChars[256]; + + // encoding vector maps character encoding to the ordinal number + // of the glyph in the output file + sal_uChar pEncoding[256]; + sal_uInt16 pTTGlyphMapping[256]; + const bool bAllowCID = false; // TODO: nPSLanguageLevel>=3 + + // loop thru all the font subsets + sal_Int32 nCharSetID; + char_list_t::iterator aCharSet; + for (aCharSet = maCharList.begin(), nCharSetID = 1; + aCharSet != maCharList.end(); + ++aCharSet, nCharSetID++) + { + if ((*aCharSet).size() == 0) + continue; + + // loop thru all the chars in the subset + char_map_t::const_iterator aChar; + sal_Int32 n = 0; + for (aChar = (*aCharSet).begin(); aChar != (*aCharSet).end(); ++aChar) + { + pUChars [n] = (*aChar).first; + pEncoding [n] = (*aChar).second; + n++; + } + // create a mapping from the unicode chars to the char encoding in + // source TrueType font + MapString (pTTFont, pUChars, (*aCharSet).size(), pTTGlyphMapping, mbVertical); + + // create the current subset + OString aCharSetName = GetCharSetName(nCharSetID); + fprintf( pTmpFile, "%%%%BeginResource: font %s\n", aCharSetName.getStr() ); + CreatePSUploadableFont( pTTFont, pTmpFile, aCharSetName.getStr(), (*aCharSet).size(), + pTTGlyphMapping, pEncoding, bAllowType42, bAllowCID ); + fprintf( pTmpFile, "%%%%EndResource\n" ); + rSuppliedFonts.push_back( aCharSetName ); + } + + // loop thru all the font glyph subsets + sal_Int32 nGlyphSetID; + glyph_list_t::iterator aGlyphSet; + for (aGlyphSet = maGlyphList.begin(), nGlyphSetID = 1; + aGlyphSet != maGlyphList.end(); + ++aGlyphSet, nGlyphSetID++) + { + if ((*aGlyphSet).size() == 0) + continue; + + // loop thru all the glyphs in the subset + glyph_map_t::const_iterator aGlyph; + sal_Int32 n = 0; + for (aGlyph = (*aGlyphSet).begin(); aGlyph != (*aGlyphSet).end(); ++aGlyph) + { + pTTGlyphMapping [n] = (*aGlyph).first; + pEncoding [n] = (*aGlyph).second; + n++; + } + + // create the current subset + OString aGlyphSetName = GetGlyphSetName(nGlyphSetID); + fprintf( pTmpFile, "%%%%BeginResource: font %s\n", aGlyphSetName.getStr() ); + CreatePSUploadableFont( pTTFont, pTmpFile, aGlyphSetName.getStr(), (*aGlyphSet).size(), + pTTGlyphMapping, pEncoding, bAllowType42, bAllowCID ); + fprintf( pTmpFile, "%%%%EndResource\n" ); + rSuppliedFonts.push_back( aGlyphSetName ); + } + + // copy the file into the page header + rewind(pTmpFile); + fflush(pTmpFile); + + sal_uChar pBuffer[0x2000]; + sal_uInt64 nIn; + sal_uInt64 nOut; + do + { + nIn = fread(pBuffer, 1, sizeof(pBuffer), pTmpFile); + rOutFile.write (pBuffer, nIn, nOut); + } + while ((nIn == nOut) && !feof(pTmpFile)); + + // cleanup + CloseTTFont (pTTFont); + fclose (pTmpFile); + + return sal_True; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/printergfx/glyphset.hxx b/vcl/unx/generic/printergfx/glyphset.hxx new file mode 100644 index 000000000000..c8b009c91155 --- /dev/null +++ b/vcl/unx/generic/printergfx/glyphset.hxx @@ -0,0 +1,137 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _PSPRINT_GLYPHSET_HXX_ +#define _PSPRINT_GLYPHSET_HXX_ + +#include "vcl/fontmanager.hxx" + +#include "osl/file.hxx" + +#include "rtl/string.hxx" + +#include "tools/gen.hxx" + +#include <list> +#include <boost/unordered_map.hpp> + +namespace psp { + +class PrinterGfx; +class PrintFontManager; + +class GlyphSet +{ +private: + + sal_Int32 mnFontID; + sal_Bool mbVertical; + rtl::OString maBaseName; + fonttype::type meBaseType; + rtl_TextEncoding mnBaseEncoding; + bool mbUseFontEncoding; + + typedef boost::unordered_map< sal_Unicode, sal_uInt8 > char_map_t; + typedef std::list< char_map_t > char_list_t; + typedef boost::unordered_map< sal_uInt32, sal_uInt8 > glyph_map_t; + typedef std::list< glyph_map_t > glyph_list_t; + + char_list_t maCharList; + glyph_list_t maGlyphList; + + rtl::OString GetGlyphSetName (sal_Int32 nGlyphSetID); + rtl::OString GetCharSetName (sal_Int32 nGlyphSetID); + sal_Int32 GetGlyphSetEncoding (sal_Int32 nGlyphSetID); + rtl::OString GetGlyphSetEncodingName (sal_Int32 nGlyphSetID); + + rtl::OString GetReencodedFontName (sal_Int32 nGlyphSetID); + void PSDefineReencodedFont (osl::File* pOutFile, + sal_Int32 nGlyphSetID); + + sal_Bool GetCharID (sal_Unicode nChar, + sal_uChar* nOutGlyphID, sal_Int32* nOutGlyphSetID); + sal_Bool LookupCharID (sal_Unicode nChar, + sal_uChar* nOutGlyphID, sal_Int32* nOutGlyphSetID); + sal_Bool AddCharID (sal_Unicode nChar, + sal_uChar* nOutGlyphID, + sal_Int32* nOutGlyphSetID); + sal_Bool GetGlyphID (sal_uInt32 nGlyph, sal_Unicode nUnicode, + sal_uChar* nOutGlyphID, sal_Int32* nOutGlyphSetID); + sal_Bool LookupGlyphID (sal_uInt32 nGlyph, + sal_uChar* nOutGlyphID, sal_Int32* nOutGlyphSetID); + sal_Bool AddGlyphID (sal_uInt32 nGlyph, sal_Unicode nUnicode, + sal_uChar* nOutGlyphID, + sal_Int32* nOutGlyphSetID); + void AddNotdef (char_map_t &rCharMap); + void AddNotdef (glyph_map_t &rGlyphMap); + sal_uChar GetAnsiMapping (sal_Unicode nUnicodeChar); + sal_uChar GetSymbolMapping (sal_Unicode nUnicodeChar); + + void ImplDrawText (PrinterGfx &rGfx, const Point& rPoint, + const sal_Unicode* pStr, sal_Int16 nLen); + void ImplDrawText (PrinterGfx &rGfx, const Point& rPoint, + const sal_Unicode* pStr, sal_Int16 nLen, + const sal_Int32* pDeltaArray); + +public: + + GlyphSet (); + GlyphSet (sal_Int32 nFontID, sal_Bool bVertical); + ~GlyphSet (); + + sal_Int32 GetFontID (); + fonttype::type GetFontType (); + static rtl::OString + GetReencodedFontName (rtl_TextEncoding nEnc, + const rtl::OString &rFontName); + static rtl::OString + GetGlyphSetEncodingName (rtl_TextEncoding nEnc, + const rtl::OString &rFontName); + sal_Bool IsVertical (); + + sal_Bool SetFont (sal_Int32 nFontID, sal_Bool bVertical); + + void DrawText (PrinterGfx &rGfx, const Point& rPoint, + const sal_Unicode* pStr, sal_Int16 nLen, + const sal_Int32* pDeltaArray = NULL); + void DrawGlyphs (PrinterGfx& rGfx, + const Point& rPoint, + const sal_uInt32* pGlyphIds, + const sal_Unicode* pUnicodes, + sal_Int16 nLen, + const sal_Int32* pDeltaArray ); + sal_Bool PSUploadEncoding(osl::File* pOutFile, PrinterGfx &rGfx); + sal_Bool PSUploadFont (osl::File& rOutFile, PrinterGfx &rGfx, bool bAsType42, std::list< rtl::OString >& rSuppliedFonts ); +}; + + +} /* namespace psp */ + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/printergfx/printerjob.cxx b/vcl/unx/generic/printergfx/printerjob.cxx new file mode 100644 index 000000000000..827b5c7cf269 --- /dev/null +++ b/vcl/unx/generic/printergfx/printerjob.cxx @@ -0,0 +1,1212 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <pwd.h> + +#include "psputil.hxx" +#include "glyphset.hxx" + +#include "printerjob.hxx" +#include "printergfx.hxx" +#include "vcl/ppdparser.hxx" +#include "vcl/strhelper.hxx" +#include "vcl/printerinfomanager.hxx" + +#include "rtl/ustring.hxx" +#include "rtl/strbuf.hxx" +#include "rtl/ustrbuf.hxx" + +#include "osl/thread.h" +#include "sal/alloca.h" +#include <sal/macros.h> + +#include <algorithm> +#include <vector> + +using namespace psp; + +using ::rtl::OUString; +using ::rtl::OUStringToOString; +using ::rtl::OString; +using ::rtl::OStringBuffer; + +// forward declaration + +#define nBLOCKSIZE 0x2000 + +namespace psp +{ + +sal_Bool +AppendPS (FILE* pDst, osl::File* pSrc, sal_uChar* pBuffer, + sal_uInt32 nBlockSize = nBLOCKSIZE) +{ + if ((pDst == NULL) || (pSrc == NULL)) + return sal_False; + + if (nBlockSize == 0) + nBlockSize = nBLOCKSIZE; + if (pBuffer == NULL) + pBuffer = (sal_uChar*)alloca (nBlockSize); + + pSrc->setPos (osl_Pos_Absolut, 0); + + sal_uInt64 nIn = 0; + sal_uInt64 nOut = 0; + do + { + pSrc->read (pBuffer, nBlockSize, nIn); + if (nIn > 0) + nOut = fwrite (pBuffer, 1, sal::static_int_cast<sal_uInt32>(nIn), pDst); + } + while ((nIn > 0) && (nIn == nOut)); + + return sal_True; +} + +} // namespace psp + +/* + * private convenience routines for file handling + */ + +osl::File* +PrinterJob::CreateSpoolFile (const rtl::OUString& rName, const rtl::OUString& rExtension) +{ + osl::File::RC nError = osl::File::E_None; + osl::File* pFile = NULL; + + rtl::OUString aFile = rName + rExtension; + rtl::OUString aFileURL; + nError = osl::File::getFileURLFromSystemPath( aFile, aFileURL ); + if (nError != osl::File::E_None) + return NULL; + aFileURL = maSpoolDirName + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM ("/")) + aFileURL; + + pFile = new osl::File (aFileURL); + nError = pFile->open (osl_File_OpenFlag_Read | osl_File_OpenFlag_Write | osl_File_OpenFlag_Create); + if (nError != osl::File::E_None) + { + delete pFile; + return NULL; + } + + pFile->setAttributes (aFileURL, + osl_File_Attribute_OwnWrite | osl_File_Attribute_OwnRead); + return pFile; +} + +/* + * public methods of PrinterJob: for use in PrinterGfx + */ + +void +PrinterJob::GetScale (double &rXScale, double &rYScale) const +{ + rXScale = mfXScale; + rYScale = mfYScale; +} + +sal_uInt16 +PrinterJob::GetDepth () const +{ + sal_Int32 nLevel = GetPostscriptLevel(); + sal_Bool bColor = IsColorPrinter (); + + return nLevel > 1 && bColor ? 24 : 8; +} + +sal_uInt16 +PrinterJob::GetPostscriptLevel (const JobData *pJobData) const +{ + sal_uInt16 nPSLevel = 2; + + if( pJobData == NULL ) + pJobData = &m_aLastJobData; + + if( pJobData->m_nPSLevel ) + nPSLevel = pJobData->m_nPSLevel; + else + if( pJobData->m_pParser ) + nPSLevel = pJobData->m_pParser->getLanguageLevel(); + + return nPSLevel; +} + +sal_Bool +PrinterJob::IsColorPrinter () const +{ + sal_Bool bColor = sal_False; + + if( m_aLastJobData.m_nColorDevice ) + bColor = m_aLastJobData.m_nColorDevice == -1 ? sal_False : sal_True; + else if( m_aLastJobData.m_pParser ) + bColor = m_aLastJobData.m_pParser->isColorDevice() ? sal_True : sal_False; + + return bColor; +} + +osl::File* +PrinterJob::GetDocumentHeader () +{ + return mpJobHeader; +} + +osl::File* +PrinterJob::GetDocumentTrailer () +{ + return mpJobTrailer; +} + +osl::File* +PrinterJob::GetCurrentPageHeader () +{ + return maHeaderList.back(); +} + +osl::File* +PrinterJob::GetCurrentPageBody () +{ + return maPageList.back(); +} + +/* + * public methods of PrinterJob: the actual job / spool handling + */ + +PrinterJob::PrinterJob () : + mpJobHeader( NULL ), + mpJobTrailer( NULL ), + m_bQuickJob( false ) +{ +} + +namespace psp +{ + +/* check whether the given name points to a directory which is + usable for the user */ +sal_Bool +existsTmpDir (const char* pName) +{ + struct stat aFileStatus; + + if (pName == NULL) + return sal_False; + if (stat(pName, &aFileStatus) != 0) + return sal_False; + if (! S_ISDIR(aFileStatus.st_mode)) + return sal_False; + + return access(pName, W_OK | R_OK) == 0 ? sal_True : sal_False; +} + +/* return the username in the given buffer */ +sal_Bool +getUserName (char* pName, int nSize) +{ + struct passwd *pPWEntry; + struct passwd aPWEntry; + sal_Char pPWBuffer[256]; + + sal_Bool bSuccess = sal_False; + +#ifdef FREEBSD + pPWEntry = getpwuid( getuid()); +#else + if (getpwuid_r(getuid(), &aPWEntry, pPWBuffer, sizeof(pPWBuffer), &pPWEntry) != 0) + pPWEntry = NULL; +#endif + + if (pPWEntry != NULL && pPWEntry->pw_name != NULL) + { + sal_Int32 nLen = strlen(pPWEntry->pw_name); + if (nLen > 0 && nLen < nSize) + { + memcpy (pName, pPWEntry->pw_name, nLen); + pName[nLen] = '\0'; + + bSuccess = sal_True; + } + } + + // wipe the passwd off the stack + memset (pPWBuffer, 0, sizeof(pPWBuffer)); + + return bSuccess; +} + +/* remove all our temporary files, uses external program "rm", since + osl functionality is inadequate */ +void +removeSpoolDir (const rtl::OUString& rSpoolDir) +{ + rtl::OUString aSysPath; + if( osl::File::E_None != osl::File::getSystemPathFromFileURL( rSpoolDir, aSysPath ) ) + { + // Conversion did not work, as this is quite a dangerous action, + // we should abort here .... + OSL_FAIL( "psprint: couldn't remove spool directory" ); + return; + } + rtl::OString aSysPathByte = + rtl::OUStringToOString (aSysPath, osl_getThreadTextEncoding()); + sal_Char pSystem [128]; + sal_Int32 nChar = 0; + + nChar = psp::appendStr ("rm -rf ", pSystem); + nChar += psp::appendStr (aSysPathByte.getStr(), pSystem + nChar); + + if (system (pSystem) == -1) + OSL_FAIL( "psprint: couldn't remove spool directory" ); +} + +/* creates a spool directory with a "pidgin random" value based on + current system time */ +rtl::OUString +createSpoolDir () +{ + TimeValue aCur; + osl_getSystemTime( &aCur ); + sal_Int32 nRand = aCur.Seconds ^ (aCur.Nanosec/1000); + + rtl::OUString aTmpDir; + osl_getTempDirURL( &aTmpDir.pData ); + + do + { + rtl::OUStringBuffer aDir( aTmpDir.getLength() + 16 ); + aDir.append( aTmpDir ); + aDir.appendAscii( "/psp" ); + aDir.append(nRand); + rtl::OUString aResult = aDir.makeStringAndClear(); + if( osl::Directory::create( aResult ) == osl::FileBase::E_None ) + { + osl::File::setAttributes( aResult, + osl_File_Attribute_OwnWrite + | osl_File_Attribute_OwnRead + | osl_File_Attribute_OwnExe ); + return aResult; + } + nRand++; + } while( nRand ); + return rtl::OUString(); +} + +} // namespace psp + +PrinterJob::~PrinterJob () +{ + std::list< osl::File* >::iterator pPage; + for (pPage = maPageList.begin(); pPage != maPageList.end(); ++pPage) + { + //(*pPage)->remove(); + delete *pPage; + } + for (pPage = maHeaderList.begin(); pPage != maHeaderList.end(); ++pPage) + { + //(*pPage)->remove(); + delete *pPage; + } + // mpJobHeader->remove(); + delete mpJobHeader; + // mpJobTrailer->remove(); + delete mpJobTrailer; + + // XXX should really call osl::remove routines + if( maSpoolDirName.getLength() ) + removeSpoolDir (maSpoolDirName); + + // osl::Directory::remove (maSpoolDirName); +} + +namespace psp +{ + +// get locale invariant, 7bit clean current local time string +sal_Char* +getLocalTime(sal_Char* pBuffer) +{ + time_t nTime = time (NULL); + struct tm aTime; + struct tm *pLocalTime = localtime_r (&nTime, &aTime); + + return asctime_r(pLocalTime, pBuffer); +} + +} + +static bool isAscii( const rtl::OUString& rStr ) +{ + const sal_Unicode* pStr = rStr; + sal_Int32 nLen = rStr.getLength(); + for( sal_Int32 i = 0; i < nLen; i++ ) + if( pStr[i] > 127 ) + return false; + return true; +} + +sal_Bool +PrinterJob::StartJob ( + const rtl::OUString& rFileName, + int nMode, + const rtl::OUString& rJobName, + const rtl::OUString& rAppName, + const JobData& rSetupData, + PrinterGfx* pGraphics, + bool bIsQuickJob + ) +{ + m_bQuickJob = bIsQuickJob; + mnMaxWidthPt = mnMaxHeightPt = 0; + mnLandscapes = mnPortraits = 0; + m_pGraphics = pGraphics; + InitPaperSize (rSetupData); + + // create file container for document header and trailer + maFileName = rFileName; + mnFileMode = nMode; + maSpoolDirName = createSpoolDir (); + maJobTitle = rJobName; + + rtl::OUString aExt(RTL_CONSTASCII_USTRINGPARAM (".ps")); + mpJobHeader = CreateSpoolFile (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("psp_head")), aExt); + mpJobTrailer = CreateSpoolFile (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("psp_tail")), aExt); + if( ! (mpJobHeader && mpJobTrailer) ) // existing files are removed in destructor + return sal_False; + + // write document header according to Document Structuring Conventions (DSC) + WritePS (mpJobHeader, + "%!PS-Adobe-3.0\n" + "%%BoundingBox: (atend)\n" ); + + rtl::OUString aFilterWS; + + // Creator (this application) + aFilterWS = WhitespaceToSpace( rAppName, sal_False ); + WritePS (mpJobHeader, "%%Creator: ("); + WritePS (mpJobHeader, aFilterWS); + WritePS (mpJobHeader, ")\n"); + + // For (user name) + sal_Char pUserName[64]; + if (getUserName(pUserName, sizeof(pUserName))) + { + WritePS (mpJobHeader, "%%For: ("); + WritePS (mpJobHeader, pUserName); + WritePS (mpJobHeader, ")\n"); + } + + // Creation Date (locale independent local time) + sal_Char pCreationDate [256]; + WritePS (mpJobHeader, "%%CreationDate: ("); + getLocalTime(pCreationDate); + for( unsigned int i = 0; i < SAL_N_ELEMENTS(pCreationDate); i++ ) + { + if( pCreationDate[i] == '\n' ) + { + pCreationDate[i] = 0; + break; + } + } + WritePS (mpJobHeader, pCreationDate ); + WritePS (mpJobHeader, ")\n"); + + // Document Title + /* #i74335# + * The title should be clean ascii; rJobName however may + * contain any Unicode character. So implement the following + * algorithm: + * use rJobName, if it contains only ascii + * use the filename, if it contains only ascii + * else omit %%Title + */ + aFilterWS = WhitespaceToSpace( rJobName, sal_False ); + rtl::OUString aTitle( aFilterWS ); + if( ! isAscii( aTitle ) ) + { + sal_Int32 nIndex = 0; + while( nIndex != -1 ) + aTitle = rFileName.getToken( 0, '/', nIndex ); + aTitle = WhitespaceToSpace( aTitle, sal_False ); + if( ! isAscii( aTitle ) ) + aTitle = rtl::OUString(); + } + + maJobTitle = aFilterWS; + if( aTitle.getLength() ) + { + WritePS (mpJobHeader, "%%Title: ("); + WritePS (mpJobHeader, aTitle); + WritePS (mpJobHeader, ")\n"); + } + + // Language Level + sal_Char pLevel[16]; + sal_Int32 nSz = getValueOf(GetPostscriptLevel(&rSetupData), pLevel); + pLevel[nSz++] = '\n'; + pLevel[nSz ] = '\0'; + WritePS (mpJobHeader, "%%LanguageLevel: "); + WritePS (mpJobHeader, pLevel); + + // Other + WritePS (mpJobHeader, "%%DocumentData: Clean7Bit\n"); + WritePS (mpJobHeader, "%%Pages: (atend)\n"); + WritePS (mpJobHeader, "%%Orientation: (atend)\n"); + WritePS (mpJobHeader, "%%PageOrder: Ascend\n"); + WritePS (mpJobHeader, "%%EndComments\n"); + + // write Prolog + writeProlog (mpJobHeader, rSetupData); + + // mark last job setup as not set + m_aLastJobData.m_pParser = NULL; + m_aLastJobData.m_aContext.setParser( NULL ); + + return sal_True; +} + +sal_Bool +PrinterJob::EndJob () +{ + // no pages ? that really means no print job + if( maPageList.empty() ) + return sal_False; + + // write document setup (done here because it + // includes the accumulated fonts + if( mpJobHeader ) + writeSetup( mpJobHeader, m_aDocumentJobData ); + m_pGraphics->OnEndJob(); + if( ! (mpJobHeader && mpJobTrailer) ) + return sal_False; + + // write document trailer according to Document Structuring Conventions (DSC) + rtl::OStringBuffer aTrailer(512); + aTrailer.append( "%%Trailer\n" ); + aTrailer.append( "%%BoundingBox: 0 0 " ); + aTrailer.append( (sal_Int32)mnMaxWidthPt ); + aTrailer.append( " " ); + aTrailer.append( (sal_Int32)mnMaxHeightPt ); + if( mnLandscapes > mnPortraits ) + aTrailer.append("\n%%Orientation: Landscape"); + else + aTrailer.append("\n%%Orientation: Portrait"); + aTrailer.append( "\n%%Pages: " ); + aTrailer.append( (sal_Int32)maPageList.size() ); + aTrailer.append( "\n%%EOF\n" ); + WritePS (mpJobTrailer, aTrailer.getStr()); + + /* + * spool the set of files to their final destination, this is U**X dependent + */ + + FILE* pDestFILE = NULL; + + /* create a destination either as file or as a pipe */ + sal_Bool bSpoolToFile = maFileName.getLength() > 0 ? sal_True : sal_False; + if (bSpoolToFile) + { + const rtl::OString aFileName = rtl::OUStringToOString (maFileName, + osl_getThreadTextEncoding()); + if( mnFileMode ) + { + int nFile = open( aFileName.getStr(), O_CREAT | O_EXCL | O_RDWR, mnFileMode ); + if( nFile != -1 ) + { + pDestFILE = fdopen( nFile, "w" ); + if( pDestFILE == NULL ) + { + close( nFile ); + unlink( aFileName.getStr() ); + return sal_False; + } + } + else + chmod( aFileName.getStr(), mnFileMode ); + } + if (pDestFILE == NULL) + pDestFILE = fopen (aFileName.getStr(), "w"); + + if (pDestFILE == NULL) + return sal_False; + } + else + { + PrinterInfoManager& rPrinterInfoManager = PrinterInfoManager::get (); + pDestFILE = rPrinterInfoManager.startSpool( m_aLastJobData.m_aPrinterName, m_bQuickJob ); + if (pDestFILE == NULL) + return sal_False; + } + + /* spool the document parts to the destination */ + + sal_uChar pBuffer[ nBLOCKSIZE ]; + + AppendPS (pDestFILE, mpJobHeader, pBuffer); + mpJobHeader->close(); + + sal_Bool bSuccess = sal_True; + std::list< osl::File* >::iterator pPageBody; + std::list< osl::File* >::iterator pPageHead; + for (pPageBody = maPageList.begin(), pPageHead = maHeaderList.begin(); + pPageBody != maPageList.end() && pPageHead != maHeaderList.end(); + ++pPageBody, ++pPageHead) + { + if( *pPageHead ) + { + osl::File::RC nError = (*pPageHead)->open(osl_File_OpenFlag_Read); + if (nError == osl::File::E_None) + { + AppendPS (pDestFILE, *pPageHead, pBuffer); + (*pPageHead)->close(); + } + } + else + bSuccess = sal_False; + if( *pPageBody ) + { + osl::File::RC nError = (*pPageBody)->open(osl_File_OpenFlag_Read); + if (nError == osl::File::E_None) + { + AppendPS (pDestFILE, *pPageBody, pBuffer); + (*pPageBody)->close(); + } + } + else + bSuccess = sal_False; + } + + AppendPS (pDestFILE, mpJobTrailer, pBuffer); + mpJobTrailer->close(); + + /* well done */ + + if (bSpoolToFile) + fclose (pDestFILE); + else + { + PrinterInfoManager& rPrinterInfoManager = PrinterInfoManager::get(); + if (0 == rPrinterInfoManager.endSpool( m_aLastJobData.m_aPrinterName, + maJobTitle, pDestFILE, m_aDocumentJobData, true )) + { + bSuccess = sal_False; + } + } + + return bSuccess; +} + +sal_Bool +PrinterJob::AbortJob () +{ + m_pGraphics->OnEndJob(); + return sal_False; +} + +void +PrinterJob::InitPaperSize (const JobData& rJobSetup) +{ + int nRes = rJobSetup.m_aContext.getRenderResolution (); + + String aPaper; + int nWidth, nHeight; + rJobSetup.m_aContext.getPageSize (aPaper, nWidth, nHeight); + + int nLeft = 0, nRight = 0, nUpper = 0, nLower = 0; + const PPDParser* pParser = rJobSetup.m_aContext.getParser(); + if (pParser != NULL) + pParser->getMargins (aPaper, nLeft, nRight, nUpper, nLower); + + mnResolution = nRes; + + mnWidthPt = nWidth; + mnHeightPt = nHeight; + + if( mnWidthPt > mnMaxWidthPt ) + mnMaxWidthPt = mnWidthPt; + if( mnHeightPt > mnMaxHeightPt ) + mnMaxHeightPt = mnHeightPt; + + mnLMarginPt = nLeft; + mnRMarginPt = nRight; + mnTMarginPt = nUpper; + mnBMarginPt = nLower; + + mfXScale = (double)72.0 / (double)mnResolution; + mfYScale = -1.0 * (double)72.0 / (double)mnResolution; +} + + +sal_Bool +PrinterJob::StartPage (const JobData& rJobSetup) +{ + InitPaperSize (rJobSetup); + + rtl::OUString aPageNo = rtl::OUString::valueOf ((sal_Int32)maPageList.size()+1); // sequential page number must start with 1 + rtl::OUString aExt = aPageNo + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (".ps")); + + osl::File* pPageHeader = CreateSpoolFile ( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("psp_pghead")), aExt); + osl::File* pPageBody = CreateSpoolFile ( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("psp_pgbody")), aExt); + + maHeaderList.push_back (pPageHeader); + maPageList.push_back (pPageBody); + + if( ! (pPageHeader && pPageBody) ) + return sal_False; + + // write page header according to Document Structuring Conventions (DSC) + WritePS (pPageHeader, "%%Page: "); + WritePS (pPageHeader, aPageNo); + WritePS (pPageHeader, " "); + WritePS (pPageHeader, aPageNo); + WritePS (pPageHeader, "\n"); + + if( rJobSetup.m_eOrientation == orientation::Landscape ) + { + WritePS (pPageHeader, "%%PageOrientation: Landscape\n"); + mnLandscapes++; + } + else + { + WritePS (pPageHeader, "%%PageOrientation: Portrait\n"); + mnPortraits++; + } + + sal_Char pBBox [256]; + sal_Int32 nChar = 0; + + nChar = psp::appendStr ("%%PageBoundingBox: ", pBBox); + nChar += psp::getValueOf (mnLMarginPt, pBBox + nChar); + nChar += psp::appendStr (" ", pBBox + nChar); + nChar += psp::getValueOf (mnBMarginPt, pBBox + nChar); + nChar += psp::appendStr (" ", pBBox + nChar); + nChar += psp::getValueOf (mnWidthPt - mnRMarginPt, pBBox + nChar); + nChar += psp::appendStr (" ", pBBox + nChar); + nChar += psp::getValueOf (mnHeightPt - mnTMarginPt, pBBox + nChar); + nChar += psp::appendStr ("\n", pBBox + nChar); + + WritePS (pPageHeader, pBBox); + + /* #i7262# #i65491# write setup only before first page + * (to %%Begin(End)Setup, instead of %%Begin(End)PageSetup) + * don't do this in StartJob since the jobsetup there may be + * different. + */ + bool bWriteFeatures = true; + if( 1 == maPageList.size() ) + { + m_aDocumentJobData = rJobSetup; + bWriteFeatures = false; + } + + if ( writePageSetup( pPageHeader, rJobSetup, bWriteFeatures ) ) + { + m_aLastJobData = rJobSetup; + return true; + } + + return false; +} + +sal_Bool +PrinterJob::EndPage () +{ + m_pGraphics->OnEndPage(); + + osl::File* pPageHeader = maHeaderList.back(); + osl::File* pPageBody = maPageList.back(); + + if( ! (pPageBody && pPageHeader) ) + return sal_False; + + // copy page to paper and write page trailer according to DSC + + sal_Char pTrailer[256]; + sal_Int32 nChar = 0; + nChar = psp::appendStr ("grestore grestore\n", pTrailer); + nChar += psp::appendStr ("showpage\n", pTrailer + nChar); + nChar += psp::appendStr ("%%PageTrailer\n\n", pTrailer + nChar); + WritePS (pPageBody, pTrailer); + + // this page is done for now, close it to avoid having too many open fd's + + pPageHeader->close(); + pPageBody->close(); + + return sal_True; +} + +sal_uInt32 +PrinterJob::GetErrorCode () +{ + /* TODO */ + return 0; +} + +struct less_ppd_key : public ::std::binary_function<double, double, bool> +{ + bool operator()(const PPDKey* left, const PPDKey* right) + { return left->getOrderDependency() < right->getOrderDependency(); } +}; + +static bool writeFeature( osl::File* pFile, const PPDKey* pKey, const PPDValue* pValue, bool bUseIncluseFeature ) +{ + if( ! pKey || ! pValue ) + return true; + + OStringBuffer aFeature(256); + aFeature.append( "[{\n" ); + if( bUseIncluseFeature ) + aFeature.append( "%%IncludeFeature:" ); + else + aFeature.append( "%%BeginFeature:" ); + aFeature.append( " *" ); + aFeature.append( OUStringToOString( pKey->getKey(), RTL_TEXTENCODING_ASCII_US ) ); + aFeature.append( ' ' ); + aFeature.append( OUStringToOString( pValue->m_aOption, RTL_TEXTENCODING_ASCII_US ) ); + if( !bUseIncluseFeature ) + { + aFeature.append( '\n' ); + aFeature.append( OUStringToOString( pValue->m_aValue, RTL_TEXTENCODING_ASCII_US ) ); + aFeature.append( "\n%%EndFeature" ); + } + aFeature.append( "\n} stopped cleartomark\n" ); + sal_uInt64 nWritten = 0; + return pFile->write( aFeature.getStr(), aFeature.getLength(), nWritten ) + || nWritten != (sal_uInt64)aFeature.getLength() ? false : true; +} + +bool PrinterJob::writeFeatureList( osl::File* pFile, const JobData& rJob, bool bDocumentSetup ) +{ + bool bSuccess = true; + + // emit features ordered to OrderDependency + // ignore features that are set to default + + // sanity check + if( rJob.m_pParser == rJob.m_aContext.getParser() && + rJob.m_pParser && + ( m_aLastJobData.m_pParser == rJob.m_pParser || m_aLastJobData.m_pParser == NULL ) + ) + { + int i; + int nKeys = rJob.m_aContext.countValuesModified(); + ::std::vector< const PPDKey* > aKeys( nKeys ); + for( i = 0; i < nKeys; i++ ) + aKeys[i] = rJob.m_aContext.getModifiedKey( i ); + ::std::sort( aKeys.begin(), aKeys.end(), less_ppd_key() ); + + for( i = 0; i < nKeys && bSuccess; i++ ) + { + const PPDKey* pKey = aKeys[i]; + bool bEmit = false; + if( bDocumentSetup ) + { + if( pKey->getSetupType() == PPDKey::DocumentSetup ) + bEmit = true; + } + if( pKey->getSetupType() == PPDKey::PageSetup || + pKey->getSetupType() == PPDKey::AnySetup ) + bEmit = true; + if( bEmit ) + { + const PPDValue* pValue = rJob.m_aContext.getValue( pKey ); + if( pValue + && pValue->m_eType == eInvocation + && ( m_aLastJobData.m_pParser == NULL + || m_aLastJobData.m_aContext.getValue( pKey ) != pValue + || bDocumentSetup + ) + ) + { + // try to avoid PS level 2 feature commands if level is set to 1 + if( GetPostscriptLevel( &rJob ) == 1 ) + { + bool bHavePS2 = + ( pValue->m_aValue.SearchAscii( "<<" ) != STRING_NOTFOUND ) + || + ( pValue->m_aValue.SearchAscii( ">>" ) != STRING_NOTFOUND ); + if( bHavePS2 ) + continue; + } + bSuccess = writeFeature( pFile, pKey, pValue, PrinterInfoManager::get().getUseIncludeFeature() ); + } + } + } + } + else + bSuccess = false; + + return bSuccess; +} + +bool PrinterJob::writePageSetup( osl::File* pFile, const JobData& rJob, bool bWriteFeatures ) +{ + bool bSuccess = true; + + WritePS (pFile, "%%BeginPageSetup\n%\n"); + if ( bWriteFeatures ) + bSuccess = writeFeatureList( pFile, rJob, false ); + WritePS (pFile, "%%EndPageSetup\n"); + + sal_Char pTranslate [128]; + sal_Int32 nChar = 0; + + if( rJob.m_eOrientation == orientation::Portrait ) + { + nChar = psp::appendStr ("gsave\n[", pTranslate); + nChar += psp::getValueOfDouble ( pTranslate + nChar, mfXScale, 5); + nChar += psp::appendStr (" 0 0 ", pTranslate + nChar); + nChar += psp::getValueOfDouble ( pTranslate + nChar, mfYScale, 5); + nChar += psp::appendStr (" ", pTranslate + nChar); + nChar += psp::getValueOf (mnRMarginPt, pTranslate + nChar); + nChar += psp::appendStr (" ", pTranslate + nChar); + nChar += psp::getValueOf (mnHeightPt-mnTMarginPt, + pTranslate + nChar); + nChar += psp::appendStr ("] concat\ngsave\n", + pTranslate + nChar); + } + else + { + nChar = psp::appendStr ("gsave\n", pTranslate); + nChar += psp::appendStr ("[ 0 ", pTranslate + nChar); + nChar += psp::getValueOfDouble ( pTranslate + nChar, -mfYScale, 5); + nChar += psp::appendStr (" ", pTranslate + nChar); + nChar += psp::getValueOfDouble ( pTranslate + nChar, mfXScale, 5); + nChar += psp::appendStr (" 0 ", pTranslate + nChar ); + nChar += psp::getValueOfDouble ( pTranslate + nChar, mnLMarginPt, 5 ); + nChar += psp::appendStr (" ", pTranslate + nChar); + nChar += psp::getValueOf (mnBMarginPt, pTranslate + nChar ); + nChar += psp::appendStr ("] concat\ngsave\n", + pTranslate + nChar); + } + + WritePS (pFile, pTranslate); + + return bSuccess; +} + +void PrinterJob::writeJobPatch( osl::File* pFile, const JobData& rJobData ) +{ + if( ! PrinterInfoManager::get().getUseJobPatch() ) + return; + + const PPDKey* pKey = NULL; + + if( rJobData.m_pParser ) + pKey = rJobData.m_pParser->getKey( OUString( RTL_CONSTASCII_USTRINGPARAM( "JobPatchFile" ) ) ); + if( ! pKey ) + return; + + // order the patch files + // according to PPD spec the JobPatchFile options must be int + // and should be emitted in order + std::list< sal_Int32 > patch_order; + int nValueCount = pKey->countValues(); + for( int i = 0; i < nValueCount; i++ ) + { + const PPDValue* pVal = pKey->getValue( i ); + patch_order.push_back( pVal->m_aOption.ToInt32() ); + if( patch_order.back() == 0 && ! pVal->m_aOption.EqualsAscii( "0" ) ) + { + WritePS( pFile, "% Warning: left out JobPatchFile option \"" ); + OString aOption = OUStringToOString( pVal->m_aOption, RTL_TEXTENCODING_ASCII_US ); + WritePS( pFile, aOption.getStr() ); + WritePS( pFile, + "\"\n% as it violates the PPD spec;\n" + "% JobPatchFile options need to be numbered for ordering.\n" ); + } + } + + patch_order.sort(); + patch_order.unique(); + + while( patch_order.begin() != patch_order.end() ) + { + // note: this discards patch files not adhering to the "int" scheme + // as there won't be a value for them + writeFeature( pFile, pKey, pKey->getValue( OUString::valueOf( patch_order.front() ) ), false ); + patch_order.pop_front(); + } +} + +bool PrinterJob::writeProlog (osl::File* pFile, const JobData& rJobData ) +{ + WritePS( pFile, "%%BeginProlog\n" ); + + // JobPatchFile feature needs to be emitted at begin of prolog + writeJobPatch( pFile, rJobData ); + + static const sal_Char pProlog[] = { + "%%BeginResource: procset PSPrint-Prolog 1.0 0\n" + "/ISO1252Encoding [\n" + "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n" + "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n" + "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n" + "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n" + "/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle\n" + "/parenleft /parenright /asterisk /plus /comma /hyphen /period /slash\n" + "/zero /one /two /three /four /five /six /seven\n" + "/eight /nine /colon /semicolon /less /equal /greater /question\n" + "/at /A /B /C /D /E /F /G\n" + "/H /I /J /K /L /M /N /O\n" + "/P /Q /R /S /T /U /V /W\n" + "/X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore\n" + "/grave /a /b /c /d /e /f /g\n" + "/h /i /j /k /l /m /n /o\n" + "/p /q /r /s /t /u /v /w\n" + "/x /y /z /braceleft /bar /braceright /asciitilde /unused\n" + "/Euro /unused /quotesinglbase /florin /quotedblbase /ellipsis /dagger /daggerdbl\n" + "/circumflex /perthousand /Scaron /guilsinglleft /OE /unused /Zcaron /unused\n" + "/unused /quoteleft /quoteright /quotedblleft /quotedblright /bullet /endash /emdash\n" + "/tilde /trademark /scaron /guilsinglright /oe /unused /zcaron /Ydieresis\n" + "/space /exclamdown /cent /sterling /currency /yen /brokenbar /section\n" + "/dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron\n" + "/degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered\n" + "/cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown\n" + "/Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla\n" + "/Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis\n" + "/Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply\n" + "/Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls\n" + "/agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla\n" + "/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis\n" + "/eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide\n" + "/oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] def\n" + "\n" + "/psp_definefont { exch dup findfont dup length dict begin { 1 index /FID ne\n" + "{ def } { pop pop } ifelse } forall /Encoding 3 -1 roll def\n" + "currentdict end exch pop definefont pop } def\n" + "\n" + "/pathdict dup 8 dict def load begin\n" + "/rcmd { { currentfile 1 string readstring pop 0 get dup 32 gt { exit }\n" + "{ pop } ifelse } loop dup 126 eq { pop exit } if 65 sub dup 16#3 and 1\n" + "add exch dup 16#C and -2 bitshift 16#3 and 1 add exch 16#10 and 16#10\n" + "eq 3 1 roll exch } def\n" + "/rhex { dup 1 sub exch currentfile exch string readhexstring pop dup 0\n" + "get dup 16#80 and 16#80 eq dup 3 1 roll { 16#7f and } if 2 index 0 3\n" + "-1 roll put 3 1 roll 0 0 1 5 -1 roll { 2 index exch get add 256 mul }\n" + "for 256 div exch pop exch { neg } if } def\n" + "/xcmd { rcmd exch rhex exch rhex exch 5 -1 roll add exch 4 -1 roll add\n" + "1 index 1 index 5 -1 roll { moveto } { lineto } ifelse } def end\n" + "/readpath { 0 0 pathdict begin { xcmd } loop end pop pop } def\n" + "\n" + "systemdict /languagelevel known not {\n" + "/xshow { exch dup length 0 1 3 -1 roll 1 sub { dup 3 index exch get\n" + "exch 2 index exch get 1 string dup 0 4 -1 roll put currentpoint 3 -1\n" + "roll show moveto 0 rmoveto } for pop pop } def\n" + "/rectangle { 4 -2 roll moveto 1 index 0 rlineto 0 exch rlineto neg 0\n" + "rlineto closepath } def\n" + "/rectfill { rectangle fill } def\n" + "/rectstroke { rectangle stroke } def } if\n" + "/bshow { currentlinewidth 3 1 roll currentpoint 3 index show moveto\n" + "setlinewidth false charpath stroke setlinewidth } def\n" + "/bxshow { currentlinewidth 4 1 roll setlinewidth exch dup length 1 sub\n" + "0 1 3 -1 roll { 1 string 2 index 2 index get 1 index exch 0 exch put dup\n" + "currentpoint 3 -1 roll show moveto currentpoint 3 -1 roll false charpath\n" + "stroke moveto 2 index exch get 0 rmoveto } for pop pop setlinewidth } def\n" + "\n" + "/psp_lzwfilter { currentfile /ASCII85Decode filter /LZWDecode filter } def\n" + "/psp_ascii85filter { currentfile /ASCII85Decode filter } def\n" + "/psp_lzwstring { psp_lzwfilter 1024 string readstring } def\n" + "/psp_ascii85string { psp_ascii85filter 1024 string readstring } def\n" + "/psp_imagedict {\n" + "/psp_bitspercomponent { 3 eq { 1 }{ 8 } ifelse } def\n" + "/psp_decodearray { [ [0 1 0 1 0 1] [0 255] [0 1] [0 255] ] exch get }\n" + "def 7 dict dup\n" + "/ImageType 1 put dup\n" + "/Width 7 -1 roll put dup\n" + "/Height 5 index put dup\n" + "/BitsPerComponent 4 index psp_bitspercomponent put dup\n" + "/Decode 5 -1 roll psp_decodearray put dup\n" + "/ImageMatrix [1 0 0 1 0 0] dup 5 8 -1 roll put put dup\n" + "/DataSource 4 -1 roll 1 eq { psp_lzwfilter } { psp_ascii85filter } ifelse put\n" + "} def\n" + "%%EndResource\n" + "%%EndProlog\n" + }; + static const sal_Char pSO52CompatProlog[] = { + "%%BeginResource: procset PSPrint-Prolog 1.0 0\n" + "/ISO1252Encoding [\n" + "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n" + "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n" + "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n" + "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n" + "/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quoteright\n" + "/parenleft /parenright /asterisk /plus /comma /minus /period /slash\n" + "/zero /one /two /three /four /five /six /seven\n" + "/eight /nine /colon /semicolon /less /equal /greater /question\n" + "/at /A /B /C /D /E /F /G\n" + "/H /I /J /K /L /M /N /O\n" + "/P /Q /R /S /T /U /V /W\n" + "/X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore\n" + "/grave /a /b /c /d /e /f /g\n" + "/h /i /j /k /l /m /n /o\n" + "/p /q /r /s /t /u /v /w\n" + "/x /y /z /braceleft /bar /braceright /asciitilde /unused\n" + "/Euro /unused /quotesinglbase /florin /quotedblbase /ellipsis /dagger /daggerdbl\n" + "/circumflex /perthousand /Scaron /guilsinglleft /OE /unused /Zcaron /unused\n" + "/unused /quoteleft /quoteright /quotedblleft /quotedblright /bullet /endash /emdash\n" + "/tilde /trademark /scaron /guilsinglright /oe /unused /zcaron /Ydieresis\n" + "/space /exclamdown /cent /sterling /currency /yen /brokenbar /section\n" + "/dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron\n" + "/degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered\n" + "/cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown\n" + "/Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla\n" + "/Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis\n" + "/Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply\n" + "/Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls\n" + "/agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla\n" + "/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis\n" + "/eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide\n" + "/oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] def\n" + "\n" + "/psp_definefont { exch dup findfont dup length dict begin { 1 index /FID ne\n" + "{ def } { pop pop } ifelse } forall /Encoding 3 -1 roll def\n" + "currentdict end exch pop definefont pop } def\n" + "\n" + "/pathdict dup 8 dict def load begin\n" + "/rcmd { { currentfile 1 string readstring pop 0 get dup 32 gt { exit }\n" + "{ pop } ifelse } loop dup 126 eq { pop exit } if 65 sub dup 16#3 and 1\n" + "add exch dup 16#C and -2 bitshift 16#3 and 1 add exch 16#10 and 16#10\n" + "eq 3 1 roll exch } def\n" + "/rhex { dup 1 sub exch currentfile exch string readhexstring pop dup 0\n" + "get dup 16#80 and 16#80 eq dup 3 1 roll { 16#7f and } if 2 index 0 3\n" + "-1 roll put 3 1 roll 0 0 1 5 -1 roll { 2 index exch get add 256 mul }\n" + "for 256 div exch pop exch { neg } if } def\n" + "/xcmd { rcmd exch rhex exch rhex exch 5 -1 roll add exch 4 -1 roll add\n" + "1 index 1 index 5 -1 roll { moveto } { lineto } ifelse } def end\n" + "/readpath { 0 0 pathdict begin { xcmd } loop end pop pop } def\n" + "\n" + "systemdict /languagelevel known not {\n" + "/xshow { exch dup length 0 1 3 -1 roll 1 sub { dup 3 index exch get\n" + "exch 2 index exch get 1 string dup 0 4 -1 roll put currentpoint 3 -1\n" + "roll show moveto 0 rmoveto } for pop pop } def\n" + "/rectangle { 4 -2 roll moveto 1 index 0 rlineto 0 exch rlineto neg 0\n" + "rlineto closepath } def\n" + "/rectfill { rectangle fill } def\n" + "/rectstroke { rectangle stroke } def } if\n" + "/bshow { currentlinewidth 3 1 roll currentpoint 3 index show moveto\n" + "setlinewidth false charpath stroke setlinewidth } def\n" + "/bxshow { currentlinewidth 4 1 roll setlinewidth exch dup length 1 sub\n" + "0 1 3 -1 roll { 1 string 2 index 2 index get 1 index exch 0 exch put dup\n" + "currentpoint 3 -1 roll show moveto currentpoint 3 -1 roll false charpath\n" + "stroke moveto 2 index exch get 0 rmoveto } for pop pop setlinewidth } def\n" + "\n" + "/psp_lzwfilter { currentfile /ASCII85Decode filter /LZWDecode filter } def\n" + "/psp_ascii85filter { currentfile /ASCII85Decode filter } def\n" + "/psp_lzwstring { psp_lzwfilter 1024 string readstring } def\n" + "/psp_ascii85string { psp_ascii85filter 1024 string readstring } def\n" + "/psp_imagedict {\n" + "/psp_bitspercomponent { 3 eq { 1 }{ 8 } ifelse } def\n" + "/psp_decodearray { [ [0 1 0 1 0 1] [0 255] [0 1] [0 255] ] exch get }\n" + "def 7 dict dup\n" + "/ImageType 1 put dup\n" + "/Width 7 -1 roll put dup\n" + "/Height 5 index put dup\n" + "/BitsPerComponent 4 index psp_bitspercomponent put dup\n" + "/Decode 5 -1 roll psp_decodearray put dup\n" + "/ImageMatrix [1 0 0 1 0 0] dup 5 8 -1 roll put put dup\n" + "/DataSource 4 -1 roll 1 eq { psp_lzwfilter } { psp_ascii85filter } ifelse put\n" + "} def\n" + "%%EndResource\n" + "%%EndProlog\n" + }; + WritePS (pFile, m_pGraphics && m_pGraphics->getStrictSO52Compatibility() ? pSO52CompatProlog : pProlog); + + return true; +} + +bool PrinterJob::writeSetup( osl::File* pFile, const JobData& rJob ) +{ + WritePS (pFile, "%%BeginSetup\n%\n"); + + // download fonts + std::list< rtl::OString > aFonts[2]; + m_pGraphics->writeResources( pFile, aFonts[0], aFonts[1] ); + + for( int i = 0; i < 2; i++ ) + { + if( !aFonts[i].empty() ) + { + std::list< rtl::OString >::const_iterator it = aFonts[i].begin(); + rtl::OStringBuffer aLine( 256 ); + if( i == 0 ) + aLine.append( "%%DocumentSuppliedResources: font " ); + else + aLine.append( "%%DocumentNeededResources: font " ); + aLine.append( *it ); + aLine.append( "\n" ); + WritePS ( pFile, aLine.getStr() ); + while( (++it) != aFonts[i].end() ) + { + aLine.setLength(0); + aLine.append( "%%+ font " ); + aLine.append( *it ); + aLine.append( "\n" ); + WritePS ( pFile, aLine.getStr() ); + } + } + } + + bool bSuccess = true; + // in case of external print dialog the number of copies is prepended + // to the job, let us not complicate things by emitting our own copy count + bool bExternalDialog = PrinterInfoManager::get().checkFeatureToken( GetPrinterName(), "external_dialog" ); + if( ! bExternalDialog && rJob.m_nCopies > 1 ) + { + // setup code + ByteString aLine( "/#copies " ); + aLine += ByteString::CreateFromInt32( rJob.m_nCopies ); + aLine += " def\n"; + sal_uInt64 nWritten = 0; + bSuccess = pFile->write( aLine.GetBuffer(), aLine.Len(), nWritten ) + || nWritten != aLine.Len() ? false : true; + + if( bSuccess && GetPostscriptLevel( &rJob ) >= 2 ) + WritePS (pFile, "<< /NumCopies null /Policies << /NumCopies 1 >> >> setpagedevice\n" ); + } + + bool bFeatureSuccess = writeFeatureList( pFile, rJob, true ); + + WritePS (pFile, "%%EndSetup\n"); + + return bSuccess && bFeatureSuccess; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/printergfx/psheader.ps b/vcl/unx/generic/printergfx/psheader.ps new file mode 100644 index 000000000000..6a0e350d9ddc --- /dev/null +++ b/vcl/unx/generic/printergfx/psheader.ps @@ -0,0 +1,368 @@ +%************************************************************************* +% +% DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +% +% Copyright 2000, 2010 Oracle and/or its affiliates. +% +% OpenOffice.org - a multi-platform office productivity suite +% +% This file is part of OpenOffice.org. +% +% OpenOffice.org is free software: you can redistribute it and/or modify +% it under the terms of the GNU Lesser General Public License version 3 +% only, as published by the Free Software Foundation. +% +% OpenOffice.org is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU Lesser General Public License version 3 for more details +% (a copy is included in the LICENSE file that accompanied this code). +% +% You should have received a copy of the GNU Lesser General Public License +% version 3 along with OpenOffice.org. If not, see +% <http://www.openoffice.org/license.html> +% for a copy of the LGPLv3 License. +% +%************************************************************************* + +% +% +% readpath +% +% The intention of readpath is to save disk space since the vcl clip region routines +% produce a huge amount of lineto/moveto commands +% +% The principal idea is to maintain the current point on stack and to provide only deltas +% in the command. These deltas are added to the current point. The new point is used for +% the lineto and moveto command and saved on stack for the next command. +% +% pathdict implements binary/hex representation of lineto and moveto commands. +% The command consists of a 1byte opcode to switch between lineto and moveto and the size +% of the following delta-x and delta-y values. The opcode is read with /rcmd, the two +% coordinates are read with /rhex. The whole command is executed with /xcmd +% +% + +/pathdict dup 8 dict def load +begin + + % the command is of the bit format cxxyy + % with c=0 meaning lineto + % c=1 meaning moveto + % xx is a 2bit value for the number of bytes for x position + % yy is the same for y, values are off by one: 00 means 1; 11 means 4 ! + % the command has been added to 'A' to be always in the ascii character + % range. the command is followed by 2*xx + 2*yy hexchars. + % '~' denotes the special case of EOD + /rcmd { + { + currentfile 1 string readstring % s bool + pop % s + 0 get % s[0] + % --- check wether s[0] is CR, LF ... + dup 32 gt % s > ' ' ? then read on + { exit } + { pop } + ifelse + } + loop + + dup 126 eq { pop exit } if % -- Exit loop if cmd is '~' + 65 sub % cmd=s[0]-'A' + % -- Separate yy bits + dup 16#3 and 1 add % cmd yy + % -- Separate xx bits + exch % yy cmd + dup 16#C and -2 bitshift + 16#3 and 1 add exch % yy xx cmd + % -- Separate command bit + 16#10 and 16#10 eq % yy xx bool + 3 1 roll exch % bool xx yy + } def + + % length rhex -- reads a signed hex value of given length + % the left most bit of char 0 is considered as the sign (0 means '+', 1 means '-') + % the rest of the bits is considered to be the abs value. Please note that this + % does not match the C binary representation of integers + /rhex { + dup 1 sub exch % l-1 l + currentfile exch string readhexstring % l-1 substring[l] bool + pop + dup 0 get dup % l-1 s s[0] s[0] + % -- Extract the sign + 16#80 and 16#80 eq dup % l-1 s s[0] sign=- sign=- + % -- Mask out the sign bit and put value back + 3 1 roll % l-1 s sign=- s[0] sign=- + { 16#7f and } if % l-1 s sign=- +s[0] + 2 index 0 % l-1 s sign=- +s[0] s 0 + 3 -1 roll put % l-1 s sign=- s 0 +s[0] + % -- Read loop: add to prev sum, mul with 256 + 3 1 roll 0 % sign=- l-1 s Sum=0 + 0 1 5 -1 roll % sign=- s Sum=0 0 1 l-1 + { % sign=- s Sum idx + 2 index exch % sign=- s Sum s idx + get % sign=- s Sum s[idx] + add 256 mul % sign=- s Sum=(s[idx]+Sum)*256 + } + for + % -- mul was once too often, weave in the sign + 256 div % sign=- s Sum/256 + exch pop % sign=- Sum/256 + exch { neg } if % (sign=- ? -Sum : Sum) + } def + + % execute a single command, the former x and y position is already on stack + % only offsets are read from cmdstring + /xcmd { % x y + rcmd % x y bool wx wy + exch rhex % x y bool wy Dx + exch rhex % x y bool Dx Dy + exch 5 -1 roll % y bool Dy Dx x + add exch % y bool X Dy + 4 -1 roll add % bool X Y + 1 index 1 index % bool X Y X Y + 5 -1 roll % X Y X Y bool + { moveto } + { lineto } + ifelse % X Y + } def +end + +/readpath +{ + 0 0 % push initial-x initial-y + pathdict begin + { xcmd } loop + end + pop pop % pop final-x final-y +} def + +% +% +% if languagelevel is not in the systemdict then its level 1 interpreter: +% provide compatibility routines +% +% + +systemdict /languagelevel known not +{ + % string numarray xxshow - + % does only work for single byte fonts + /xshow { + exch dup % a s s + length 0 1 % a s l(s) 1 1 + 3 -1 roll 1 sub % a s 0 1 l(s)-1 + { % a s idx + dup % a s idx idx + % -- extract the delta offset + 3 index exch get % a s idx a[idx] + % -- extract the character + exch % a s a[idx] idx + 2 index exch get % a s a[idx] s[idx] + % -- create a tmp string for show + 1 string dup 0 % a s a[idx] s[idx] s1 s1 0 + 4 -1 roll % a s a[idx] s1 s1 0 s[idx] + put % a s a[idx] s1 + % -- store the current point + currentpoint 3 -1 roll % a s a[idx] x y s1 + % -- draw the character + show % a s a[idx] x y + % -- move to the offset + moveto 0 rmoveto % a s + } + for + pop pop % - + } def + + % x y width height rectfill + % x y width height rectshow + % in contrast to the languagelevel 2 operator + % they use and change the currentpath + /rectangle { + 4 -2 roll % width height x y + moveto % width height + 1 index 0 rlineto % width height % rmoveto(width, 0) + 0 exch rlineto % width % rmoveto(0, height) + neg 0 rlineto % - % rmoveto(-width, 0) + closepath + } def + + /rectfill { rectangle fill } def + /rectstroke { rectangle stroke } def +} +if + +% -- small test program +% 75 75 moveto /Times-Roman findfont 12 scalefont setfont +% <292a2b2c2d2e2f30313233343536373839> +% [5 5 6 6 6 6 6 6 6 6 6 6 7 7 7 7 5] xshow <21>[0] xshow +% showpage + +% +% +% shortcuts for image header with compression +% +% + +/psp_lzwfilter { + currentfile /ASCII85Decode filter /LZWDecode filter +} def +/psp_ascii85filter { + currentfile /ASCII85Decode filter +} def +/psp_lzwstring { + psp_lzwfilter 1024 string readstring +} def +/psp_ascii85string { + psp_ascii85filter 1024 string readstring +} def +/psp_imagedict { + /psp_bitspercomponent { + 3 eq + { 1 } + { 8 } + ifelse + } def + /psp_decodearray { + [ [0 1 0 1 0 1] [0 255] [0 1] [0 255] ] exch get + } def + + 7 dict dup + /ImageType 1 put dup + /Width 7 -1 roll put dup + /Height 5 index put dup + /BitsPerComponent 4 index + psp_bitspercomponent put dup + /Decode 5 -1 roll + psp_decodearray put dup + /ImageMatrix [1 0 0 1 0 0] dup + 5 8 -1 roll put put dup + /DataSource 4 -1 roll + 1 eq + { psp_lzwfilter } + { psp_ascii85filter } + ifelse put +} def + + +% +% +% font encoding and reencoding +% +% + +/ISO1252Encoding [ + /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef + /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef + /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef + /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef + /space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle + /parenleft /parenright /asterisk /plus /comma /hyphen /period /slash + /zero /one /two /three /four /five /six /seven + /eight /nine /colon /semicolon /less /equal /greater /question + /at /A /B /C /D /E /F /G + /H /I /J /K /L /M /N /O + /P /Q /R /S /T /U /V /W + /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore + /grave /a /b /c /d /e /f /g + /h /i /j /k /l /m /n /o + /p /q /r /s /t /u /v /w + /x /y /z /braceleft /bar /braceright /asciitilde /unused + /Euro /unused /quotesinglbase /florin /quotedblbase /ellipsis /dagger /daggerdbl + /circumflex /perthousand /Scaron /guilsinglleft /OE /unused /Zcaron /unused + /unused /quoteleft /quoteright /quotedblleft /quotedblright /bullet /endash /emdash + /tilde /trademark /scaron /guilsinglright /oe /unused /zcaron /Ydieresis + /space /exclamdown /cent /sterling /currency /yen /brokenbar /section + /dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron + /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered + /cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown + /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla + /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis + /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply + /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls + /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla + /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis + /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide + /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis +] def + +% /fontname /encoding psp_findfont +/psp_findfont { + exch dup % encoding fontname fontname + findfont % encoding fontname + dup length dict + begin + { + 1 index /FID ne + { def } + { pop pop } + ifelse + } forall + /Encoding 3 -1 roll def + currentdict + end + /psp_reencodedfont exch definefont +} def + +% bshow shows a text in artificial bold +% this is achieved by first showing the text +% then stroking its outline over it with +% the linewidth set to the second parameter +% usage: (string) num bshow + +/bshow { + currentlinewidth % save current linewidth + 3 1 roll % move it to the last stack position + currentpoint % save the current point + 3 index % copy the string to show + show % show it + moveto % move to the original coordinates again + setlinewidth % set the linewidth + false charpath % create the outline path of the shown string + stroke % and stroke it + setlinewidth % reset the stored linewidth +} def + +% bxshow shows a text with a delta array in artificial bold +% that is it does what bshow does for show +% usage: (string) [deltaarray] num bxshow + +/bxshow { + currentlinewidth % save linewidth + 4 1 roll % move it to the last stack position + setlinewidth % set the new linewidth + exch % exchange string and delta array + dup + length % get length of string + 1 sub % prepare parameters for {} for + 0 1 + 3 -1 roll + { + 1 string % create a string object length 1 + 2 index % get the text + 2 index % get charpos (for index variable) + get % have char value at charpos + 1 index % prepare string for put + exch + 0 + exch + put % put into string of length 1 + dup % duplicate the it + currentpoint % save current position + 3 -1 roll % prepare show + show % show the character + moveto % move back to beginning + currentpoint % save current position + 3 -1 roll % prepare outline path of character + false charpath + stroke % stroke it + moveto % move back + % now move to next point + 2 index % get advance array + exch % get charpos + get % get advance element + 0 rmoveto % advance current position + } for + pop pop % remove string and delta array + setlinewidth % restore linewidth +} def diff --git a/vcl/unx/generic/printergfx/psputil.cxx b/vcl/unx/generic/printergfx/psputil.cxx new file mode 100644 index 000000000000..9a5de44960e6 --- /dev/null +++ b/vcl/unx/generic/printergfx/psputil.cxx @@ -0,0 +1,271 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <string.h> + +#include "psputil.hxx" + +#include "tools/debug.hxx" + +namespace psp { + +/* + * string convenience routines + */ + +sal_Int32 +getHexValueOf (sal_Int32 nValue, sal_Char* pBuffer) +{ + const static sal_Char pHex [0x10] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + + pBuffer[0] = pHex [(nValue & 0xF0) >> 4]; + pBuffer[1] = pHex [(nValue & 0x0F) ]; + + return 2; +} + +sal_Int32 +getAlignedHexValueOf (sal_Int32 nValue, sal_Char* pBuffer) +{ + // get sign + sal_Bool bNegative = nValue < 0; + nValue = bNegative ? -nValue : nValue; + + // get required buffer size, must be a multiple of two + sal_Int32 nPrecision; + if (nValue < 0x80) + nPrecision = 2; + else + if (nValue < 0x8000) + nPrecision = 4; + else + if (nValue < 0x800000) + nPrecision = 6; + else + nPrecision = 8; + + // convert the int into its hex representation, write it into the buffer + sal_Int32 nRet = nPrecision; + while (nPrecision) + { + nPrecision -= getHexValueOf (nValue % 256, pBuffer + nPrecision - 2 ); + nValue /= 256; + } + + // set sign bit + if (bNegative) + { + switch (pBuffer[0]) + { + case '0' : pBuffer[0] = '8'; break; + case '1' : pBuffer[0] = '9'; break; + case '2' : pBuffer[0] = 'A'; break; + case '3' : pBuffer[0] = 'B'; break; + case '4' : pBuffer[0] = 'C'; break; + case '5' : pBuffer[0] = 'D'; break; + case '6' : pBuffer[0] = 'E'; break; + case '7' : pBuffer[0] = 'F'; break; + default: OSL_FAIL("Already a signed value"); + } + } + + // report precision + return nRet; +} + + +sal_Int32 +getValueOf (sal_Int32 nValue, sal_Char* pBuffer) +{ + sal_Int32 nChar = 0; + if (nValue < 0) + { + pBuffer [nChar++] = '-'; + nValue *= -1; + } + else + if (nValue == 0) + { + pBuffer [nChar++] = '0'; + return nChar; + } + + sal_Char pInvBuffer [32]; + sal_Int32 nInvChar = 0; + while (nValue > 0) + { + pInvBuffer [nInvChar++] = '0' + nValue % 10; + nValue /= 10; + } + while (nInvChar > 0) + { + pBuffer [nChar++] = pInvBuffer [--nInvChar]; + } + + return nChar; +} + +sal_Int32 +appendStr (const sal_Char* pSrc, sal_Char* pDst) +{ + sal_Int32 nBytes = strlen (pSrc); + strncpy (pDst, pSrc, nBytes + 1); + + return nBytes; +} + +sal_Int32 +appendStr (const sal_Char* pSrc, sal_Char* pDst, sal_Int32 nBytes) +{ + strncpy (pDst, pSrc, nBytes); + pDst [nBytes] = '\0'; + return nBytes; +} + +/* + * copy strings to file + */ + +sal_Bool +WritePS (osl::File* pFile, const sal_Char* pString) +{ + sal_uInt64 nInLength = rtl_str_getLength (pString); + sal_uInt64 nOutLength = 0; + + if (nInLength > 0 && pFile) + pFile->write (pString, nInLength, nOutLength); + + return nInLength == nOutLength; +} + +sal_Bool +WritePS (osl::File* pFile, const sal_Char* pString, sal_uInt64 nInLength) +{ + sal_uInt64 nOutLength = 0; + + if (nInLength > 0 && pFile) + pFile->write (pString, nInLength, nOutLength); + + return nInLength == nOutLength; +} + +sal_Bool +WritePS (osl::File* pFile, const rtl::OString &rString) +{ + sal_uInt64 nInLength = rString.getLength(); + sal_uInt64 nOutLength = 0; + + if (nInLength > 0 && pFile) + pFile->write (rString, nInLength, nOutLength); + + return nInLength == nOutLength; +} + +sal_Bool +WritePS (osl::File* pFile, const rtl::OUString &rString) +{ + return WritePS (pFile, rtl::OUStringToOString(rString, RTL_TEXTENCODING_ASCII_US)); +} + +/* + * cache converter for use in postscript drawing routines + */ + +ConverterFactory::ConverterFactory() +{ +} + +ConverterFactory::~ConverterFactory () +{ + for( std::map< rtl_TextEncoding, rtl_UnicodeToTextConverter >::const_iterator it = m_aConverters.begin(); it != m_aConverters.end(); ++it ) + rtl_destroyUnicodeToTextConverter (it->second); +} + +rtl_UnicodeToTextConverter +ConverterFactory::Get (rtl_TextEncoding nEncoding) +{ + if (rtl_isOctetTextEncoding( nEncoding )) + { + std::map< rtl_TextEncoding, rtl_UnicodeToTextConverter >::const_iterator it = + m_aConverters.find( nEncoding ); + rtl_UnicodeToTextConverter aConverter; + if (it == m_aConverters.end()) + { + aConverter = rtl_createUnicodeToTextConverter (nEncoding); + m_aConverters[nEncoding] = aConverter; + } + else + aConverter = it->second; + return aConverter; + } + return NULL; +} + +// wrapper for rtl_convertUnicodeToText that handles the usual cases for +// textconversion in drawtext +sal_Size +ConverterFactory::Convert (const sal_Unicode *pText, int nTextLen, + sal_uChar *pBuffer, sal_Size nBufferSize, rtl_TextEncoding nEncoding) +{ + const sal_uInt32 nCvtFlags = RTL_UNICODETOTEXT_FLAGS_UNDEFINED_QUESTIONMARK + | RTL_UNICODETOTEXT_FLAGS_INVALID_QUESTIONMARK ; + sal_uInt32 nCvtInfo; + sal_Size nCvtChars; + + rtl_UnicodeToTextConverter aConverter = Get (nEncoding); + rtl_UnicodeToTextContext aContext = rtl_createUnicodeToTextContext (aConverter); + + sal_Size nSize = rtl_convertUnicodeToText (aConverter, aContext, + pText, nTextLen, (sal_Char*)pBuffer, nBufferSize, + nCvtFlags, &nCvtInfo, &nCvtChars); + + rtl_destroyUnicodeToTextContext (aConverter, aContext); + + return nSize; +} + +ConverterFactory* +GetConverterFactory () +{ + static ConverterFactory* pCvt = NULL; + + if (pCvt == NULL) + pCvt = new ConverterFactory; + + return pCvt; +} + + +} /* namespace psp */ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/printergfx/psputil.hxx b/vcl/unx/generic/printergfx/psputil.hxx new file mode 100644 index 000000000000..878fd9c7791a --- /dev/null +++ b/vcl/unx/generic/printergfx/psputil.hxx @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _PSPRINT_PRINTERUTIL_HXX_ +#define _PSPRINT_PRINTERUTIL_HXX_ + +#include "osl/file.hxx" + +#include "rtl/ustring.hxx" +#include "rtl/string.hxx" +#include "rtl/tencinfo.h" +#include "rtl/textcvt.h" + +#include <map> + +namespace psp { + +/* + * string convenience routines + * sizeof(pBuffer) must be at least 2 Bytes, 0x00 <= nValue <= 0xFF, + * effective buffer of get*ValueOf() is NOT NULL-terminated + */ +sal_Int32 getHexValueOf (sal_Int32 nValue, sal_Char* pBuffer); +sal_Int32 getAlignedHexValueOf (sal_Int32 nValue, sal_Char* pBuffer); +sal_Int32 getValueOf (sal_Int32 nValue, sal_Char* pBuffer); +sal_Int32 appendStr (const sal_Char* pSrc, sal_Char* pDst); +sal_Int32 appendStr (const sal_Char* pSrc, sal_Char* pDst, sal_Int32 nBytes); + +sal_Bool WritePS (osl::File* pFile, const sal_Char* pString); +sal_Bool WritePS (osl::File* pFile, const sal_Char* pString, sal_uInt64 nInLength); +sal_Bool WritePS (osl::File* pFile, const rtl::OString &rString); +sal_Bool WritePS (osl::File* pFile, const rtl::OUString &rString); + +class ConverterFactory +{ + +public: + ConverterFactory(); + ~ConverterFactory(); + rtl_UnicodeToTextConverter Get (rtl_TextEncoding nEncoding); + sal_Size Convert (const sal_Unicode *pText, int nTextLen, + sal_uChar *pBuffer, sal_Size nBufferSize, + rtl_TextEncoding nEncoding); +private: + + std::map< rtl_TextEncoding, rtl_UnicodeToTextConverter > m_aConverters; +}; + +ConverterFactory* GetConverterFactory (); + +} /* namespace psp */ + +#endif /* _PSPRINT_PRINTERUTIL_HXX_ */ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/printergfx/text_gfx.cxx b/vcl/unx/generic/printergfx/text_gfx.cxx new file mode 100644 index 000000000000..263e0b4c7995 --- /dev/null +++ b/vcl/unx/generic/printergfx/text_gfx.cxx @@ -0,0 +1,865 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <math.h> + +#include "psputil.hxx" +#include "glyphset.hxx" + +#include "printergfx.hxx" +#include "vcl/fontmanager.hxx" +#include "vcl/helper.hxx" + +#include "osl/thread.h" + +#include "sal/alloca.h" + +using namespace psp ; + +namespace psp { +/* + container for a font and its helper fonts: + 1st font is the font substitute e.g. helvetica substitutes arial on the printer + 2nd is the font itself + 3rd is a fallback font, usually a font with unicode glyph repertoir (e.g. andale) + symbol fonts (adobe-fontspecific) may need special glyphmapping + (symbol page vc. latin page) +*/ +class Font3 +{ + private: + + #define Font3Size 3 + + fontID mpFont [Font3Size]; + bool mbSymbol; + + public: + + fontID GetFont (int nIdx) const + { return nIdx < Font3Size ? mpFont[nIdx] : -1 ; } + bool IsSymbolFont () const + { return mbSymbol; } + + Font3 (const PrinterGfx &rGfx); + ~Font3 () {} +}; + +Font3::Font3(const PrinterGfx &rGfx) +{ + mpFont[0] = rGfx.getFontSubstitute(); + mpFont[1] = rGfx.GetFontID(); + mpFont[2] = rGfx.getFallbackID(); + // mpFont[2] = rGfx.GetFontID(); + + PrintFontManager &rMgr = PrintFontManager::get(); + mbSymbol = mpFont[1] != -1 ? + rMgr.getFontEncoding(mpFont[1]) == RTL_TEXTENCODING_SYMBOL : false; +} + +} // namespace psp + +static int getVerticalDeltaAngle( sal_Unicode nChar ) +{ + int nAngle = 0; + if( ( nChar >= 0x1100 && nChar < 0x11fa ) || + ( nChar >= 0x3000 && nChar < 0xfb00 ) || + ( nChar >= 0xfe20 && nChar < 0xfe70 ) || + ( nChar >= 0xff00 && nChar < 0xff64 ) + ) + { + /* #i52932# remember: + nChar == 0x2010 || nChar == 0x2015 + nChar == 0x2016 || nChar == 0x2026 + + are nAngle = 0 also, but already handled in the first if + */ + if( ( nChar >= 0x3008 && nChar < 0x3019 && nChar != 0x3012 ) || + nChar == 0xff3b || nChar == 0xff3d || + (nChar >= 0xff6b && nChar < 0xff64 ) || + nChar == 0xffe3 + ) + nAngle = 0; + else if( nChar == 0x30fc ) + nAngle = -900; + else + nAngle = 900; + } + return nAngle; +} + +void +PrinterGfx::PSUploadPS1Font (sal_Int32 nFontID) +{ + std::list< sal_Int32 >::iterator aFont; + // already in the document header ? + for (aFont = maPS1Font.begin(); aFont != maPS1Font.end(); ++aFont ) + if( nFontID == *aFont ) + return; + + // no occurrenc yet, mark for download + // add the fontid to the list + maPS1Font.push_back (nFontID); +} + +/* + * implement text handling printer routines, + */ + +sal_uInt16 +PrinterGfx::SetFont( + sal_Int32 nFontID, + sal_Int32 nHeight, + sal_Int32 nWidth, + sal_Int32 nAngle, + bool bVertical, + bool bArtItalic, + bool bArtBold + ) +{ + // font and encoding will be set by drawText again immediately + // before PSShowText + mnFontID = nFontID; + maVirtualStatus.maFont = rtl::OString(); + maVirtualStatus.maEncoding = RTL_TEXTENCODING_DONTKNOW; + maVirtualStatus.mnTextHeight = nHeight; + maVirtualStatus.mnTextWidth = nWidth; + maVirtualStatus.mbArtItalic = bArtItalic; + maVirtualStatus.mbArtBold = bArtBold; + mnTextAngle = nAngle; + mbTextVertical = bVertical; + + return 0; +} + +sal_uInt16 +PrinterGfx::SetFallbackFont ( sal_Int32 nFontID ) +{ + mnFallbackID = nFontID; + return 0; +} + +void PrinterGfx::drawGlyphs( + const Point& rPoint, + sal_uInt32* pGlyphIds, + sal_Unicode* pUnicodes, + sal_Int16 nLen, + sal_Int32* pDeltaArray + ) +{ + + // draw the string + // search for a glyph set matching the set font + std::list< GlyphSet >::iterator aIter; + for (aIter = maPS3Font.begin(); aIter != maPS3Font.end(); ++aIter) + if ( ((*aIter).GetFontID() == mnFontID) + && ((*aIter).IsVertical() == mbTextVertical)) + { + (*aIter).DrawGlyphs (*this, rPoint, pGlyphIds, pUnicodes, nLen, pDeltaArray); + break; + } + + // not found ? create a new one + if (aIter == maPS3Font.end()) + { + maPS3Font.push_back (GlyphSet(mnFontID, mbTextVertical)); + maPS3Font.back().DrawGlyphs (*this, rPoint, pGlyphIds, pUnicodes, nLen, pDeltaArray); + } +} + +void PrinterGfx::DrawGlyphs( + const Point& rPoint, + sal_GlyphId* pGlyphIds, + sal_Unicode* pUnicodes, + sal_Int16 nLen, + sal_Int32* pDeltaArray + ) +{ + if( nLen <= 0 ) + return; + + if ( !mrFontMgr.isFontDownloadingAllowed( mnFontID ) ) + { + LicenseWarning(rPoint, pUnicodes, nLen, pDeltaArray); + return; + } + + if( mrFontMgr.getFontType( mnFontID ) != fonttype::TrueType ) + { + DrawText( rPoint, pUnicodes, nLen, pDeltaArray ); + return; + } + + // move and rotate the user coordinate system + // avoid the gsave/grestore for the simple cases since it allows + // reuse of the current font if it hasn't changed + sal_Int32 nCurrentTextAngle = mnTextAngle; + Point aPoint( rPoint ); + + if (nCurrentTextAngle != 0) + { + PSGSave (); + PSTranslate (rPoint); + PSRotate (nCurrentTextAngle); + mnTextAngle = 0; + aPoint = Point( 0, 0 ); + } + + if( mbTextVertical ) + { + // vertical glyphs can have an additional rotation ... sigh. + // so break up text in chunks of normal glyphs and print out + // specially rotated glyphs extra + sal_uInt32* pTempGlyphIds = (sal_uInt32*)alloca(sizeof(sal_Int32)*nLen); + sal_Int32* pTempDelta = (sal_Int32*)alloca(sizeof(sal_Int32)*nLen); + sal_Unicode* pTempUnicodes = (sal_Unicode*)alloca(sizeof(sal_Unicode)*nLen); + sal_Int16 nTempLen = 0; + sal_Int32 nTempFirstDelta = 0; + Point aRotPoint; + sal_Int32 nTextHeight = maVirtualStatus.mnTextHeight; + sal_Int32 nTextWidth = maVirtualStatus.mnTextWidth ? maVirtualStatus.mnTextWidth : maVirtualStatus.mnTextHeight; + sal_Int32 nAscend = mrFontMgr.getFontAscend( mnFontID ); + sal_Int32 nDescend = mrFontMgr.getFontDescend( mnFontID ); + + nDescend = nDescend * nTextHeight / 1000; + nAscend = nAscend * nTextHeight / 1000; + + for( sal_Int16 i = 0; i < nLen; i++ ) + { + const sal_GlyphId nRot = pGlyphIds[i] & GF_ROTMASK; + if( nRot == GF_NONE ) + { + pTempUnicodes[nTempLen] = pUnicodes[i]; + pTempGlyphIds[nTempLen] = pGlyphIds[i]; + if( nTempLen > 0 ) + pTempDelta[nTempLen-1] = pDeltaArray[i-1]-nTempFirstDelta; + else + { + // the first element in pDeltaArray shows + // the offset of the second character + // so if the first glyph is normal + // then we do not need to move the delta indices + // else we have to move them down by one and + // recalculate aPoint and all deltas + if( i != 0 ) + nTempFirstDelta = pDeltaArray[ i-1 ]; + } + nTempLen++; + } + else + { + sal_Int32 nOffset = i > 0 ? pDeltaArray[i-1] : 0; + sal_Int32 nRotAngle = 0; + switch( nRot ) + { + case GF_ROTR: + nRotAngle = 2700; + aRotPoint = Point( -nAscend*nTextWidth/nTextHeight, -nDescend*nTextWidth/nTextHeight - nOffset ); + break; + case GF_VERT: + nRotAngle = 1800; + aRotPoint = Point( -nOffset, (nAscend+nDescend) ); + break; + case GF_ROTL: + nRotAngle = 900; + aRotPoint = Point( -nDescend*nTextWidth/nTextHeight, nOffset + nAscend*nTextWidth/nTextHeight ); + break; + } + sal_GlyphId nRotGlyphId = pGlyphIds[i]; + sal_Unicode nRotUnicode = pUnicodes[i]; + sal_Int32 nRotDelta = 0; + + // transform matrix to new individual direction + PSGSave (); + GraphicsStatus aSaveStatus = maVirtualStatus; + if( nRot != 2 ) // switch font aspect + { + maVirtualStatus.mnTextWidth = nTextHeight; + maVirtualStatus.mnTextHeight = nTextWidth; + } + if( aPoint.X() || aPoint.Y() ) + PSTranslate( aPoint ); + PSRotate (nRotAngle); + // draw the rotated glyph + drawGlyphs( aRotPoint, &nRotGlyphId, &nRotUnicode, 1, &nRotDelta ); + + // restore previous state + maVirtualStatus = aSaveStatus; + PSGRestore(); + } + } + + pGlyphIds = pTempGlyphIds; + pUnicodes = pTempUnicodes; + pDeltaArray = pTempDelta; + nLen = nTempLen; + + aPoint.X() += nTempFirstDelta; + } + + if( nLen > 0 ) + drawGlyphs( aPoint, pGlyphIds, pUnicodes, nLen, pDeltaArray ); + + // restore the user coordinate system + if (nCurrentTextAngle != 0) + { + PSGRestore (); + mnTextAngle = nCurrentTextAngle; + } +} + +void +PrinterGfx::DrawText ( + const Point& rPoint, + const sal_Unicode* pStr, + sal_Int16 nLen, + const sal_Int32* pDeltaArray + ) +{ + fontID nRestoreFont = mnFontID; + + // setup font[substitutes] and map the string into the symbol area in case of + // symbol font + Font3 aFont(*this); + sal_Unicode *pEffectiveStr; + if ( aFont.IsSymbolFont() ) + { + pEffectiveStr = (sal_Unicode*)alloca(nLen * sizeof(pStr[0])); + for (int i = 0; i < nLen; i++) + pEffectiveStr[i] = pStr[i] < 256 ? pStr[i] + 0xF000 : pStr[i]; + } + else + { + pEffectiveStr = const_cast<sal_Unicode*>(pStr); + } + + fontID *pFontMap = (fontID*) alloca(nLen * sizeof(fontID)); + sal_Int32 *pCharWidth = (sal_Int32*) alloca(nLen * sizeof(sal_Int32)); + + for( int n = 0; n < nLen; n++ ) + { + CharacterMetric aBBox; + pFontMap[n] = getCharMetric (aFont, pEffectiveStr[n], &aBBox); + pCharWidth[n] = getCharWidth (mbTextVertical, pEffectiveStr[n], &aBBox); + } + + // setup a new delta array, use virtual resolution of 1000 + sal_Int32* pNewDeltaArray = (sal_Int32*)alloca( sizeof( sal_Int32 )*nLen ); + if ( pDeltaArray != 0) + { + for (int i = 0; i < nLen - 1; i++) + pNewDeltaArray[i] = 1000 * pDeltaArray[i]; + pNewDeltaArray[nLen - 1] = 0; + } + else + { + pNewDeltaArray[0] = pCharWidth[0]; + for (int i = 1; i < nLen; i++) + pNewDeltaArray[i] = pNewDeltaArray[i-1] + pCharWidth[i]; + } + + // move and rotate the user coordinate system + // avoid the gsave/grestore for the simple cases since it allows + // reuse of the current font if it hasn't changed + sal_Int32 nCurrentTextAngle = mnTextAngle; + sal_Int32 nCurrentPointX; + sal_Int32 nCurrentPointY; + + if (nCurrentTextAngle != 0) + { + PSGSave (); + PSTranslate (rPoint); + PSRotate (nCurrentTextAngle); + mnTextAngle = 0; + + nCurrentPointX = 0; + nCurrentPointY = 0; + } + else + { + nCurrentPointX = rPoint.X(); + nCurrentPointY = rPoint.Y(); + } + + // draw the string + sal_Int32 nDelta = 0; + for (int nTo = 0; nTo < nLen; ) + { + int nFrom = nTo; + fontID nFont = pFontMap[ nFrom ]; + + while ((nTo < nLen) && (nFont == pFontMap[nTo])) + { + pNewDeltaArray[ nTo ] = (sal_Int32)(((0.5 + pNewDeltaArray[ nTo ]) / 1000.0) - nDelta); + nTo++ ; + } + + SetFont( nFont, + maVirtualStatus.mnTextHeight, maVirtualStatus.mnTextWidth, + mnTextAngle, + mbTextVertical, + maVirtualStatus.mbArtItalic, + maVirtualStatus.mbArtBold + ); + + if (mbTextVertical) + { + drawVerticalizedText( + Point(nCurrentPointX + nDelta, nCurrentPointY), + pEffectiveStr + nFrom, nTo - nFrom, + pNewDeltaArray + nFrom ); + } + else + { + drawText( + Point(nCurrentPointX + nDelta, nCurrentPointY), + pEffectiveStr + nFrom, nTo - nFrom, + pDeltaArray == NULL ? NULL : pNewDeltaArray + nFrom ); + } + nDelta += pNewDeltaArray[ nTo - 1 ]; + } + + // restore the user coordinate system + if (nCurrentTextAngle != 0) + { + PSGRestore (); + mnTextAngle = nCurrentTextAngle; + } + + // restore the original font settings + SetFont( nRestoreFont, + maVirtualStatus.mnTextHeight, maVirtualStatus.mnTextWidth, + mnTextAngle, mbTextVertical, + maVirtualStatus.mbArtItalic, + maVirtualStatus.mbArtBold + ); +} + +void PrinterGfx::drawVerticalizedText( + const Point& rPoint, + const sal_Unicode* pStr, + sal_Int16 nLen, + const sal_Int32* pDeltaArray + ) +{ + sal_Int32* pDelta = (sal_Int32*)alloca( nLen * sizeof(sal_Int32) ); + + int nTextScale = maVirtualStatus.mnTextWidth ? maVirtualStatus.mnTextWidth : maVirtualStatus.mnTextHeight; + int nNormalAngle = mnTextAngle; + int nDeltaAngle, nLastPos = 0; + + double fSin = sin( -2.0*M_PI*nNormalAngle/3600 ); + double fCos = cos( -2.0*M_PI*nNormalAngle/3600 ); + + PrintFontManager &rMgr = PrintFontManager::get(); + PrintFontInfo aInfo; + rMgr.getFontInfo( mnFontID, aInfo ); + + bool* pGsubFlags = (bool*)alloca( nLen * sizeof(bool) ); + rMgr.hasVerticalSubstitutions( mnFontID, pStr, nLen, pGsubFlags ); + + Point aPoint( rPoint ); + for( int i = 0; i < nLen; ) + { + while( ( nDeltaAngle = getVerticalDeltaAngle( pStr[i] ) ) == 0 && i < nLen ) + i++; + if( i <= nLen && i > nLastPos ) + { + for( int n = nLastPos; n < i; n++ ) + pDelta[n] = pDeltaArray[n] - (aPoint.X() - rPoint.X() ); + + SetFont( mnFontID, + maVirtualStatus.mnTextHeight, maVirtualStatus.mnTextWidth, + nNormalAngle, mbTextVertical, + maVirtualStatus.mbArtItalic, + maVirtualStatus.mbArtBold ); + drawText( aPoint, pStr + nLastPos, i - nLastPos, pDelta + nLastPos ); + + aPoint.X() = (sal_Int32)(rPoint.X() + ((double)pDeltaArray[i-1] * fCos)); + aPoint.Y() = (sal_Int32)(rPoint.Y() + ((double)pDeltaArray[i-1] * fSin)); + } + if( i < nLen ) + { + int nOldWidth = maVirtualStatus.mnTextWidth; + int nOldHeight = maVirtualStatus.mnTextHeight; + SetFont( mnFontID, + nTextScale, + maVirtualStatus.mnTextHeight, + nNormalAngle + nDeltaAngle, + mbTextVertical, + maVirtualStatus.mbArtItalic, + maVirtualStatus.mbArtBold ); + + double nA = nTextScale * aInfo.m_nAscend / 1000.0; + double nD = nTextScale * aInfo.m_nDescend / 1000.0; + double fStretch = (double)maVirtualStatus.mnTextWidth / maVirtualStatus.mnTextHeight; + if( !pGsubFlags[i] ) + nD *= fStretch; + + Point aPos( aPoint ); + switch( nDeltaAngle ) + { + case +900: + aPos.X() += (sal_Int32)(+nA * fCos + nD * fSin); + aPos.Y() += (sal_Int32)(-nA * fSin + nD * fCos); + break; + case -900: + aPos.X() += (sal_Int32)(+nA * fSin + nD * fCos); + aPos.Y() += (sal_Int32)(-(nTextScale*fStretch - nD) * fCos); + break; + } + drawText( aPos, pStr+i, 1, NULL ); + if( i < nLen-1 && pDeltaArray ) + { + aPoint.X() = (sal_Int32)(rPoint.X() + ((double)pDeltaArray[i] * fCos)); + aPoint.Y() = (sal_Int32)(rPoint.Y() + ((double)pDeltaArray[i] * fSin)); + } + + // swap text width/height again + SetFont( mnFontID, + nOldHeight, + nOldWidth, + nNormalAngle, + mbTextVertical, + maVirtualStatus.mbArtItalic, + maVirtualStatus.mbArtBold ); + } + i++; + nLastPos = i; + } + mnTextAngle = nNormalAngle; +} + +void +PrinterGfx::LicenseWarning(const Point& rPoint, const sal_Unicode* pStr, + sal_Int16 nLen, const sal_Int32* pDeltaArray) +{ + // treat it like a builtin font in case a user has that font also in the + // printer. This is not so unlikely as it may seem; no print embedding + // licensed fonts are often used (or so they say) in companies: + // they are installed on displays and printers, but get not embedded in + // they are installed on displays and printers, but get not embedded in + // print files or documents because they are not licensed for use outside + // the company. + rtl::OString aMessage( "The font " ); + aMessage += rtl::OUStringToOString( mrFontMgr.getPSName(mnFontID), + RTL_TEXTENCODING_ASCII_US ); + aMessage += " could not be downloaded\nbecause its license does not allow for that"; + PSComment( aMessage.getStr() ); + + rtl::OString aFontName = rtl::OUStringToOString( + mrFontMgr.getPSName(mnFontID), + RTL_TEXTENCODING_ASCII_US); + PSSetFont (aFontName, RTL_TEXTENCODING_ISO_8859_1); + + sal_Size nSize = 4 * nLen; + sal_uChar* pBuffer = (sal_uChar*)alloca (nSize* sizeof(sal_uChar)); + + ConverterFactory* pCvt = GetConverterFactory (); + nSize = pCvt->Convert (pStr, nLen, pBuffer, nSize, RTL_TEXTENCODING_ISO_8859_1); + + PSMoveTo (rPoint); + PSShowText (pBuffer, nLen, nSize, pDeltaArray); +} + +void +PrinterGfx::drawText( + const Point& rPoint, + const sal_Unicode* pStr, + sal_Int16 nLen, + const sal_Int32* pDeltaArray + ) +{ + if (!(nLen > 0)) + return; + + fonttype::type eType = mrFontMgr.getFontType (mnFontID); + + if (eType == fonttype::Type1) + PSUploadPS1Font (mnFontID); + + if ( eType == fonttype::TrueType + && !mrFontMgr.isFontDownloadingAllowed(mnFontID)) + { + LicenseWarning(rPoint, pStr, nLen, pDeltaArray); + return; + } + + if( mrFontMgr.getUseOnlyFontEncoding( mnFontID ) ) + { + GlyphSet aGSet( mnFontID, mbTextVertical ); + aGSet.DrawText( *this, rPoint, pStr, nLen, pDeltaArray ); + return; + } + + // search for a glyph set matching the set font + std::list< GlyphSet >::iterator aIter; + for (aIter = maPS3Font.begin(); aIter != maPS3Font.end(); ++aIter) + if ( ((*aIter).GetFontID() == mnFontID) + && ((*aIter).IsVertical() == mbTextVertical)) + { + (*aIter).DrawText (*this, rPoint, pStr, nLen, pDeltaArray); + break; + } + + // not found ? create a new one + if (aIter == maPS3Font.end()) + { + maPS3Font.push_back (GlyphSet(mnFontID, mbTextVertical)); + maPS3Font.back().DrawText (*this, rPoint, pStr, nLen, pDeltaArray); + } +} + +int +PrinterGfx::getCharWidth (sal_Bool b_vert, sal_Unicode n_char, CharacterMetric *p_bbox) +{ + b_vert = b_vert && (getVerticalDeltaAngle(n_char) != 0); + int w = b_vert ? p_bbox->height : p_bbox->width; + w *= maVirtualStatus.mnTextWidth ? maVirtualStatus.mnTextWidth : maVirtualStatus.mnTextHeight; + return w; +} + +fontID +PrinterGfx::getCharMetric (const Font3 &rFont, sal_Unicode n_char, CharacterMetric *p_bbox) +{ + p_bbox->width = -1; + p_bbox->height = -1; + + for (fontID n = 0; n < 3; n++) + { + fontID n_font = rFont.GetFont(n); + if (n_font != -1) + { + if( mbStrictSO52Compatibility ) + { + fonttype::type eType = mrFontMgr.getFontType( n_font ); + if( (eType == fonttype::Builtin || eType == fonttype::Type1) ) + { + // note: any character exchanged here MUST also be changed + // in the compatibility ISO encoding vector in the prolog + // in printerjob.cxx + sal_Unicode aRepl = 0; + if( n_char == 0x2d ) + aRepl = 0x2212; + else if( n_char == 0x27 ) + aRepl = 0x2019; + /* + additional characters that may need backwards compatibility: + ISO5589 StdEnc Unicode suggested n_char -> aRepl + 0264 0302 0x00B4 0x00B4 (acute) -> 0x2019 (quiteright) + 0246 - 0x00A6 0x00A6 (brokenbar) -> 0x007C (bar) + 0225 0267 0x0095 0x0095 () -> 0x2022 (bullet) + 0140 0301 0x0060 0x0060 (grave) -> ? + */ + if( aRepl ) + { + mrFontMgr.getMetrics( n_font, aRepl, aRepl, p_bbox ); + if (p_bbox->width >= 0 && p_bbox->height >= 0) + return n_font; + } + } + } + mrFontMgr.getMetrics( n_font, n_char, n_char, p_bbox ); + } + if (p_bbox->width >= 0 && p_bbox->height >= 0) + return n_font; + } + if (n_char != '?') + return getCharMetric (rFont, '?', p_bbox); + + return rFont.GetFont(0) != -1 ? rFont.GetFont(0) : rFont.GetFont(1); +} + +fontID +PrinterGfx::getFontSubstitute () const +{ + if( mpFontSubstitutes ) + { + ::boost::unordered_map< fontID, fontID >::const_iterator it = + mpFontSubstitutes->find( mnFontID ); + if( it != mpFontSubstitutes->end() ) + return it->second; + } + + return -1; +} + +sal_Int32 +PrinterGfx::GetCharWidth (sal_Unicode nFrom, sal_Unicode nTo, long *pWidthArray) +{ + Font3 aFont(*this); + if (aFont.IsSymbolFont() && (nFrom < 256) && (nTo < 256)) + { + nFrom += 0xF000; + nTo += 0xF000; + } + + for( int n = 0; n < (nTo - nFrom + 1); n++ ) + { + CharacterMetric aBBox; + getCharMetric (aFont, n + nFrom, &aBBox); + pWidthArray[n] = getCharWidth (mbTextVertical, n + nFrom, &aBBox); + } + + // returned metrics have postscript precision + return 1000; +} + +const ::std::list< KernPair >& PrinterGfx::getKernPairs( bool bVertical ) const +{ + /* + * Note: this is only a 80% solution: if a font is only + * partially substituted in a string due to missing glyphs + * the results may not be perfect; the more so the more the + * substitution differs from the original metricwise. But + * vcl only asks for KernPairs for each font once and NOT + * in a string context this is the best we can do. + * In future the kerning should be done on a per string basis. + */ + fontID nFont = mnFontID; + if( mpFontSubstitutes ) + { + ::boost::unordered_map< fontID, fontID >::const_iterator it = + mpFontSubstitutes->find( mnFontID ); + if( it != mpFontSubstitutes->end() ) + nFont = it->second; + } + return mrFontMgr.getKernPairs( nFont, bVertical ); +} + +/* + * advanced glyph handling + */ + +sal_Bool +PrinterGfx::GetGlyphBoundRect (sal_Unicode /*c*/, Rectangle& /*rOutRect*/) +{ + return 0; +} + +sal_uInt32 +PrinterGfx::GetGlyphOutline (sal_Unicode /*c*/, + sal_uInt16 **/*ppPolySizes*/, Point **/*ppPoints*/, sal_uInt8 **/*ppFlags*/) +{ + return 0; +} + +/* + * spool the converted truetype fonts to the page header after the page body is + * complete + * for Type1 fonts spool additional reencoding vectors that are necessary to access the + * whole font + */ + +void +PrinterGfx::OnEndPage () +{ +} + +void +PrinterGfx::OnEndJob () +{ + maPS3Font.clear(); + maPS1Font.clear(); +} + +void +PrinterGfx::writeResources( osl::File* pFile, std::list< rtl::OString >& rSuppliedFonts, std::list< rtl::OString >& rNeededFonts ) +{ + // write all type 1 fonts + std::list< sal_Int32 >::iterator aFont; + // already in the document header ? + for (aFont = maPS1Font.begin(); aFont != maPS1Font.end(); ++aFont) + { + const rtl::OString& rSysPath (mrFontMgr.getFontFileSysPath(*aFont) ); + rtl::OUString aUNCPath; + osl::File::getFileURLFromSystemPath (OStringToOUString (rSysPath, osl_getThreadTextEncoding()), aUNCPath); + osl::File aFontFile (aUNCPath); + + // provide the pfb or pfa font as a (pfa-)font resource + rtl::OString aPostScriptName = + rtl::OUStringToOString ( mrFontMgr.getPSName(*aFont), + RTL_TEXTENCODING_ASCII_US ); + + WritePS (pFile, "%%BeginResource: font "); + WritePS (pFile, aPostScriptName.getStr()); + WritePS (pFile, "\n"); + + osl::File::RC nError = aFontFile.open(osl_File_OpenFlag_Read); + if (nError == osl::File::E_None) + { + convertPfbToPfa (aFontFile, *pFile); + aFontFile.close (); + + pFile->setPos(osl_Pos_Current, -1); + char lastchar = '\n'; + sal_uInt64 uBytes(1); + pFile->read((void *)(&lastchar), uBytes, uBytes); + if (lastchar != '\n') + WritePS (pFile, "\n"); + } + WritePS (pFile, "%%EndResource\n"); + rSuppliedFonts.push_back( aPostScriptName ); + } + + // write glyphsets and reencodings + std::list< GlyphSet >::iterator aIter; + for (aIter = maPS3Font.begin(); aIter != maPS3Font.end(); ++aIter) + { + if (aIter->GetFontType() == fonttype::TrueType) + { + aIter->PSUploadFont (*pFile, *this, mbUploadPS42Fonts ? true : false, rSuppliedFonts ); + } + else + // ( aIter->GetFontType() == fonttype::Type1 + // || aIter->GetFontType() == fonttype::Builtin ) + { + aIter->PSUploadEncoding (pFile, *this); + if( aIter->GetFontType() == fonttype::Builtin ) + rNeededFonts.push_back( + rtl::OUStringToOString( + mrFontMgr.getPSName( aIter->GetFontID() ), + RTL_TEXTENCODING_ASCII_US ) ); + } + } +} + +bool PrinterGfx::getStrictSO52Compatibility() const +{ + return mbStrictSO52Compatibility; +} + +void PrinterGfx::setStrictSO52Compatibility( bool bCompat) +{ + mbStrictSO52Compatibility = bCompat; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/window/FWS.cxx b/vcl/unx/generic/window/FWS.cxx new file mode 100644 index 000000000000..a90f76af90c2 --- /dev/null +++ b/vcl/unx/generic/window/FWS.cxx @@ -0,0 +1,282 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> +#include "FWS.hxx" + +static Atom fwsIconAtom; + +static Atom FWS_CLIENT; +static Atom FWS_COMM_WINDOW; +static Atom FWS_PROTOCOLS; +static Atom FWS_STACK_UNDER; +static Atom FWS_PARK_ICONS; +static Atom FWS_PASS_ALL_INPUT; +static Atom FWS_PASSES_INPUT; +static Atom FWS_HANDLES_FOCUS; + +static Atom FWS_REGISTER_WINDOW; +static Atom FWS_STATE_CHANGE; +static Atom FWS_UNSEEN_STATE; +static Atom FWS_NORMAL_STATE; +static Atom WM_PROTOCOLS; +static Atom WM_CHANGE_STATE; + +static Bool fwsStackUnder; +static Bool fwsParkIcons; +static Bool fwsPassesInput; +static Bool fwsHandlesFocus; + +static Window fwsCommWindow; + +/*************************************<->*********************************** + * + * WMSupportsFWS() - + * + * Initialize our atoms and determine if the current window manager is + * providing FWS extension support. + * + *************************************<->***********************************/ + +Bool +WMSupportsFWS (Display *display, int screen) +{ + unsigned int i; + Atom protocol; + Atom propType; + int propFormat; + unsigned long propItems; + unsigned long propBytesAfter; + unsigned char *propData; + char propName[64]; + + FWS_CLIENT = XInternAtom(display, "_SUN_FWS_CLIENT", False); + FWS_COMM_WINDOW = XInternAtom(display, "_SUN_FWS_COMM_WINDOW", False); + FWS_PROTOCOLS = XInternAtom(display, "_SUN_FWS_PROTOCOLS", False); + FWS_STACK_UNDER = XInternAtom(display, "_SUN_FWS_STACK_UNDER", False); + FWS_PARK_ICONS = XInternAtom(display, "_SUN_FWS_PARK_ICONS", False); + FWS_PASS_ALL_INPUT = XInternAtom(display, "_SUN_FWS_PASS_ALL_INPUT", False); + FWS_PASSES_INPUT = XInternAtom(display, "_SUN_FWS_PASSES_INPUT", False); + FWS_HANDLES_FOCUS = XInternAtom(display, "_SUN_FWS_HANDLES_FOCUS", False); + FWS_REGISTER_WINDOW= XInternAtom(display, "_SUN_FWS_REGISTER_WINDOW",False); + FWS_STATE_CHANGE = XInternAtom(display, "_SUN_FWS_STATE_CHANGE", False); + FWS_UNSEEN_STATE = XInternAtom(display, "_SUN_FWS_UNSEEN_STATE", False); + FWS_NORMAL_STATE = XInternAtom(display, "_SUN_FWS_NORMAL_STATE", False); + WM_PROTOCOLS = XInternAtom(display, "WM_PROTOCOLS", False); + WM_CHANGE_STATE = XInternAtom(display, "WM_CHANGE_STATE", False); + + snprintf (propName, sizeof(propName), "_SUN_FWS_NEXT_ICON_%d", screen); + fwsIconAtom = XInternAtom(display, propName, False); + + if (XGetWindowProperty (display, DefaultRootWindow (display), + FWS_COMM_WINDOW, 0, 1, + False, AnyPropertyType, &propType, + &propFormat, &propItems, + &propBytesAfter, &propData) != Success) + return False; + + if (propFormat != 32 || + propItems != 1 || + propBytesAfter != 0) + { + #if OSL_DEBUG_LEVEL > 1 + fprintf (stderr, "Bad FWS_COMM_WINDOW property on root window.\n"); + #endif + XFree (propData); + return False; + } + + fwsCommWindow = *(Window *) propData; + #if OSL_DEBUG_LEVEL > 1 + fprintf (stderr, "Using fwsCommWindow = 0x%lx.\n", fwsCommWindow); + #endif + XFree (propData); + + + if (XGetWindowProperty (display, DefaultRootWindow (display), + FWS_PROTOCOLS, 0, 10, + False, AnyPropertyType, &propType, + &propFormat, &propItems, + &propBytesAfter, &propData) != Success) + { + return False; + } + + if (propFormat != 32 || + propBytesAfter != 0) + { + #if OSL_DEBUG_LEVEL > 1 + fprintf (stderr, "Bad FWS_PROTOCOLS property on root window.\n"); + #endif + XFree (propData); + return False; + } + + for (i = 0; i < propItems; ++i) + { + protocol = ((Atom *) propData)[i]; + if (protocol == FWS_STACK_UNDER) + { + fwsStackUnder = True; + #if OSL_DEBUG_LEVEL > 1 + fprintf (stderr, "Using fwsStackUnder.\n"); + #endif + } + else + if (protocol == FWS_PARK_ICONS) + { + fwsParkIcons = True; + #if OSL_DEBUG_LEVEL > 1 + fprintf (stderr, "Using fwsParkIcons.\n"); + #endif + } + else + if (protocol == FWS_PASSES_INPUT) + { + fwsPassesInput = True; + #if OSL_DEBUG_LEVEL > 1 + fprintf (stderr, "Using fwsPassesInput.\n"); + #endif + } + else + if (protocol == FWS_HANDLES_FOCUS) + { + fwsHandlesFocus = True; + #if OSL_DEBUG_LEVEL > 1 + fprintf (stderr, "Using fwsHandlesFocus.\n"); + #endif + } + } + + XFree (propData); + return True; +} + +/*************************************<->*********************************** + * + * newHandler() - + * + * Handle X errors (temporarily) to record the occurrence of BadWindow + * errors without crashing. Used to detect the FWS_COMM_WINDOW root window + * property containing an old or obsolete window id. + * + *************************************<->***********************************/ + +extern "C" { + +static Bool badWindowFound; +static int (* oldHandler) (Display *, XErrorEvent *); + +static int +newHandler (Display *display, XErrorEvent *xerror) +{ + if (xerror->error_code != BadWindow) + (*oldHandler)(display, xerror); + else + badWindowFound = True; + + return 0; +} + +} + +/*************************************<->*********************************** + * + * RegisterFwsWindow() - + * + * Send a client message to the FWS_COMM_WINDOW indicating the existance + * of a new FWS client window. Be careful to avoid BadWindow errors on + * the XSendEvent in case the FWS_COMM_WINDOW root window property had + * old/obsolete junk in it. + * + *************************************<->***********************************/ + +Bool +RegisterFwsWindow (Display *display, Window window) +{ + XClientMessageEvent msg; + + msg.type = ClientMessage; + msg.window = fwsCommWindow; + msg.message_type = FWS_REGISTER_WINDOW; + msg.format = 32; + msg.data.l[0] = window; + + XSync (display, False); + badWindowFound = False; + oldHandler = XSetErrorHandler (newHandler); + + XSendEvent (display, fwsCommWindow, False, NoEventMask, + (XEvent *) &msg); + XSync (display, False); + + XSetErrorHandler (oldHandler); + #if OSL_DEBUG_LEVEL > 1 + if (badWindowFound) + fprintf (stderr, "No FWS client window to register with.\n"); + #endif + + return !badWindowFound; +} + +/*************************************<->*********************************** + * + * AddFwsProtocols - + * + * Add the FWS protocol atoms to the WMProtocols property for the window. + * + *************************************<->***********************************/ + +void +AddFwsProtocols (Display *display, Window window) +{ + #define MAX_FWS_PROTOS 10 + + Atom fwsProtocols[ MAX_FWS_PROTOS ]; + int nProtos = 0; + + fwsProtocols[ nProtos++ ] = FWS_CLIENT; + fwsProtocols[ nProtos++ ] = FWS_STACK_UNDER; + fwsProtocols[ nProtos++ ] = FWS_STATE_CHANGE; + fwsProtocols[ nProtos++ ] = FWS_PASS_ALL_INPUT; + XChangeProperty (display, window, WM_PROTOCOLS, + XA_ATOM, 32, PropModeAppend, + (unsigned char *) fwsProtocols, nProtos); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/window/FWS.hxx b/vcl/unx/generic/window/FWS.hxx new file mode 100644 index 000000000000..36b4b16a9214 --- /dev/null +++ b/vcl/unx/generic/window/FWS.hxx @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _FOREIGN_WINDOW_SYSTEM_HXX +#define _FOREIGN_WINDOW_SYSTEM_HXX + +#include <X11/Xlib.h> + +#if defined(__cplusplus) +extern "C" { +#endif + +/* Initialize our atoms and determine if the current window manager is + * providing FWS extension support. + */ + +Bool +WMSupportsFWS (Display *display, int screen); + +/* Send a client message to the FWS_COMM_WINDOW indicating the existance + * of a new FWS client window. Be careful to avoid BadWindow errors on + * the XSendEvent in case the FWS_COMM_WINDOW root window property had + * old/obsolete junk in it. + */ + +Bool +RegisterFwsWindow (Display *display, Window window); + +/* Add the FWS protocol atoms to the WMProtocols property for the window. + */ + +void +AddFwsProtocols (Display *display, Window window); + +#if defined(__cplusplus) +} /* extern "C" */ +#endif + +#endif // _FOREIGN_WINDOW_SYSTEM_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/window/salframe.cxx b/vcl/unx/generic/window/salframe.cxx new file mode 100644 index 000000000000..1fdfc02c3916 --- /dev/null +++ b/vcl/unx/generic/window/salframe.cxx @@ -0,0 +1,4567 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "tools/debug.hxx" + +#include "sal/alloca.h" + +#include "vcl/floatwin.hxx" +#include "vcl/svapp.hxx" +#include "vcl/keycodes.hxx" +#include "vcl/printerinfomanager.hxx" +#include "vcl/settings.hxx" + +#include <tools/prex.h> +#include <X11/Xatom.h> +#include <X11/keysym.h> +#include "FWS.hxx" +#include <X11/extensions/shape.h> +#if !defined(SOLARIS) && !defined(AIX) +#include <X11/extensions/dpms.h> +#endif +#include <tools/postx.h> + +#include "unx/salunx.h" +#include "unx/saldata.hxx" +#include "unx/saldisp.hxx" +#include "unx/salgdi.h" +#include "unx/salframe.h" +#include "unx/soicon.hxx" +#include "unx/dtint.hxx" +#include "unx/sm.hxx" +#include "unx/wmadaptor.hxx" +#include "unx/salprn.h" +#include "unx/salbmp.h" +#include "unx/i18n_ic.hxx" +#include "unx/i18n_keysym.hxx" +#include "unx/i18n_status.hxx" + +#include "salinst.hxx" +#include "sallayout.hxx" + +#include <sal/macros.h> +#include <com/sun/star/uno/Exception.hpp> + +#include <algorithm> + +#ifndef Button6 +# define Button6 6 +#endif +#ifndef Button7 +# define Button7 7 +#endif + +using namespace vcl_sal; +using namespace vcl; + +// -=-= #defines -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +#define CLIENT_EVENTS StructureNotifyMask \ + | SubstructureNotifyMask \ + | KeyPressMask \ + | KeyReleaseMask \ + | ButtonPressMask \ + | ButtonReleaseMask \ + | PointerMotionMask \ + | EnterWindowMask \ + | LeaveWindowMask \ + | FocusChangeMask \ + | ExposureMask \ + | VisibilityChangeMask \ + | PropertyChangeMask \ + | ColormapChangeMask + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +static XLIB_Window hPresentationWindow = None, hPresFocusWindow = None; +static ::std::list< XLIB_Window > aPresentationReparentList; +static int nVisibleFloats = 0; + +X11SalFrame* X11SalFrame::s_pSaveYourselfFrame = NULL; + +// -=-= C++ statics =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +static void doReparentPresentationDialogues( SalDisplay* pDisplay ) +{ + pDisplay->GetXLib()->PushXErrorLevel( true ); + while( aPresentationReparentList.begin() != aPresentationReparentList.end() ) + { + int x, y; + XLIB_Window aRoot, aChild; + unsigned int w, h, bw, d; + XGetGeometry( pDisplay->GetDisplay(), + aPresentationReparentList.front(), + &aRoot, + &x, &y, &w, &h, &bw, &d ); + XTranslateCoordinates( pDisplay->GetDisplay(), + hPresentationWindow, + aRoot, + x, y, + &x, &y, + &aChild ); + XReparentWindow( pDisplay->GetDisplay(), + aPresentationReparentList.front(), + aRoot, + x, y ); + aPresentationReparentList.pop_front(); + } + if( hPresFocusWindow ) + XSetInputFocus( pDisplay->GetDisplay(), hPresFocusWindow, PointerRoot, CurrentTime ); + XSync( pDisplay->GetDisplay(), False ); + pDisplay->GetXLib()->PopXErrorLevel(); +} + +// -=-= SalFrame / X11SalFrame =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +bool X11SalFrame::IsOverrideRedirect() const +{ + return + ((nStyle_ & SAL_FRAME_STYLE_INTRO) && !pDisplay_->getWMAdaptor()->supportsSplash()) + || + (!( nStyle_ & ~SAL_FRAME_STYLE_DEFAULT ) && !pDisplay_->getWMAdaptor()->supportsFullScreen()) + ; +} + +bool X11SalFrame::IsFloatGrabWindow() const +{ + static const char* pDisableGrab = getenv( "SAL_DISABLE_FLOATGRAB" ); + + return + ( ( !pDisableGrab || !*pDisableGrab ) && + ( + (nStyle_ & SAL_FRAME_STYLE_FLOAT) && + ! (nStyle_ & SAL_FRAME_STYLE_TOOLTIP) && + ! (nStyle_ & SAL_FRAME_STYLE_OWNERDRAWDECORATION) + ) + ); +} + +void X11SalFrame::setXEmbedInfo() +{ + if( m_bXEmbed ) + { + long aInfo[2]; + aInfo[0] = 1; // XEMBED protocol version + aInfo[1] = (bMapped_ ? 1 : 0); // XEMBED_MAPPED + XChangeProperty( pDisplay_->GetDisplay(), + mhWindow, + pDisplay_->getWMAdaptor()->getAtom( WMAdaptor::XEMBED_INFO ), + pDisplay_->getWMAdaptor()->getAtom( WMAdaptor::XEMBED_INFO ), + 32, + PropModeReplace, + reinterpret_cast<unsigned char*>(aInfo), + SAL_N_ELEMENTS(aInfo) ); + } +} + +void X11SalFrame::askForXEmbedFocus( sal_Int32 i_nTimeCode ) +{ + XEvent aEvent; + + rtl_zeroMemory( &aEvent, sizeof(aEvent) ); + aEvent.xclient.window = mhForeignParent; + aEvent.xclient.type = ClientMessage; + aEvent.xclient.message_type = pDisplay_->getWMAdaptor()->getAtom( WMAdaptor::XEMBED ); + aEvent.xclient.format = 32; + aEvent.xclient.data.l[0] = i_nTimeCode ? i_nTimeCode : CurrentTime; + aEvent.xclient.data.l[1] = 3; // XEMBED_REQUEST_FOCUS + aEvent.xclient.data.l[2] = 0; + aEvent.xclient.data.l[3] = 0; + aEvent.xclient.data.l[4] = 0; + + GetDisplay()->GetXLib()->PushXErrorLevel( true ); + XSendEvent( pDisplay_->GetDisplay(), + mhForeignParent, + False, NoEventMask, &aEvent ); + XSync( pDisplay_->GetDisplay(), False ); + GetDisplay()->GetXLib()->PopXErrorLevel(); +} + + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void X11SalFrame::Init( sal_uLong nSalFrameStyle, int nScreen, SystemParentData* pParentData, bool bUseGeometry ) +{ + if( nScreen < 0 || nScreen >= GetDisplay()->GetScreenCount() ) + nScreen = GetDisplay()->GetDefaultScreenNumber(); + if( mpParent ) + nScreen = mpParent->m_nScreen; + + m_nScreen = nScreen; + nStyle_ = nSalFrameStyle; + XWMHints Hints; + Hints.flags = InputHint; + Hints.input = (nSalFrameStyle & SAL_FRAME_STYLE_OWNERDRAWDECORATION) ? False : True; + + int x = 0, y = 0; + unsigned int w = 500, h = 500; + XSetWindowAttributes Attributes; + + int nAttrMask = CWBorderPixel + | CWBackPixmap + | CWColormap + | CWOverrideRedirect + | CWEventMask + ; + Attributes.border_pixel = 0; + Attributes.background_pixmap = None; + Attributes.colormap = GetDisplay()->GetColormap( m_nScreen ).GetXColormap(); + Attributes.override_redirect = False; + Attributes.event_mask = CLIENT_EVENTS; + + const SalVisual& rVis = GetDisplay()->GetVisual( m_nScreen ); + XLIB_Window aFrameParent = pParentData ? pParentData->aWindow : GetDisplay()->GetRootWindow( m_nScreen ); + XLIB_Window aClientLeader = None; + + if( bUseGeometry ) + { + x = maGeometry.nX; + y = maGeometry.nY; + w = maGeometry.nWidth; + h = maGeometry.nHeight; + } + + if( (nSalFrameStyle & SAL_FRAME_STYLE_FLOAT) && + ! (nSalFrameStyle & SAL_FRAME_STYLE_OWNERDRAWDECORATION) + ) + { + if( nShowState_ == SHOWSTATE_UNKNOWN ) + { + w = 10; + h = 10; + } + Attributes.override_redirect = True; + } + else if( (nSalFrameStyle & SAL_FRAME_STYLE_SYSTEMCHILD ) ) + { + DBG_ASSERT( mpParent, "SAL_FRAME_STYLE_SYSTEMCHILD window without parent" ); + if( mpParent ) + { + aFrameParent = mpParent->mhWindow; + // FIXME: since with SAL_FRAME_STYLE_SYSTEMCHILD + // multiple X11SalFrame objects can have the same shell window + // dispatching events in saldisp.cxx is unclear (the first frame) + // wins. HTH this correctly is unclear yet + // for the time being, treat set the shell window to own window + // like for a normal frame + // mhShellWindow = mpParent->GetShellWindow(); + } + } + else if( pParentData ) + { + // plugin parent may be killed unexpectedly by + // plugging process; ignore XErrors in that case + GetDisplay()->setHaveSystemChildFrame(); + + nStyle_ |= SAL_FRAME_STYLE_PLUG; + Attributes.override_redirect = True; + if( pParentData->nSize >= sizeof(SystemParentData) ) + m_bXEmbed = pParentData->bXEmbedSupport; + + int x_ret, y_ret; + unsigned int bw, d; + XLIB_Window aRoot, aParent; + + XGetGeometry( GetXDisplay(), pParentData->aWindow, + &aRoot, &x_ret, &y_ret, &w, &h, &bw, &d ); + mhForeignParent = pParentData->aWindow; + + mhShellWindow = aParent = mhForeignParent; + XLIB_Window* pChildren; + unsigned int nChildren; + bool bBreak = false; + do + { + XQueryTree( GetDisplay()->GetDisplay(), mhShellWindow, + &aRoot, &aParent, &pChildren, &nChildren ); + XFree( pChildren ); + if( aParent != aRoot ) + mhShellWindow = aParent; + int nCount = 0; + Atom* pProps = XListProperties( GetDisplay()->GetDisplay(), + mhShellWindow, + &nCount ); + for( int i = 0; i < nCount && ! bBreak; ++i ) + bBreak = (pProps[i] == XA_WM_HINTS); + if( pProps ) + XFree( pProps ); + } while( aParent != aRoot && ! bBreak ); + + // check if this is really one of our own frames + // do not change the input mask in that case + const std::list< SalFrame* >& rFrames = GetDisplay()->getFrames(); + std::list< SalFrame* >::const_iterator it = rFrames.begin(); + while( it != rFrames.end() && mhForeignParent != static_cast<const X11SalFrame*>(*it)->GetWindow() ) + ++it; + + if( it == rFrames.end() ) + { + XSelectInput( GetDisplay()->GetDisplay(), mhForeignParent, StructureNotifyMask | FocusChangeMask ); + XSelectInput( GetDisplay()->GetDisplay(), mhShellWindow, StructureNotifyMask | FocusChangeMask ); + } + } + else + { + if( ! bUseGeometry ) + { + Size aScreenSize( GetDisplay()->getDataForScreen( m_nScreen ).m_aSize ); + w = aScreenSize.Width(); + h = aScreenSize.Height(); + if( nSalFrameStyle & SAL_FRAME_STYLE_SIZEABLE && + nSalFrameStyle & SAL_FRAME_STYLE_MOVEABLE ) + { + // fill in holy default values brought to us by product management + if( aScreenSize.Width() >= 800 ) + w = 785; + if( aScreenSize.Width() >= 1024 ) + w = 920; + + if( aScreenSize.Height() >= 600 ) + h = 550; + if( aScreenSize.Height() >= 768 ) + h = 630; + if( aScreenSize.Height() >= 1024 ) + h = 875; + } + if( ! mpParent ) + { + // find the last document window (if any) + const X11SalFrame* pFrame = NULL; + const std::list< SalFrame* >& rFrames = GetDisplay()->getFrames(); + std::list< SalFrame* >::const_iterator it = rFrames.begin(); + while( it != rFrames.end() ) + { + pFrame = static_cast< const X11SalFrame* >(*it); + if( ! ( pFrame->mpParent + || pFrame->mbFullScreen + || ! ( pFrame->nStyle_ & SAL_FRAME_STYLE_SIZEABLE ) + || ! pFrame->GetUnmirroredGeometry().nWidth + || ! pFrame->GetUnmirroredGeometry().nHeight + ) + ) + break; + ++it; + } + + if( it != rFrames.end() ) + { + // set a document position and size + // the first frame gets positioned by the window manager + const SalFrameGeometry& rGeom( pFrame->GetUnmirroredGeometry() ); + x = rGeom.nX; + y = rGeom.nY; + if( x+(int)w+40 <= (int)aScreenSize.Width() && + y+(int)h+40 <= (int)aScreenSize.Height() + ) + { + y += 40; + x += 40; + } + else + { + x = 10; // leave some space for decoration + y = 20; + } + } + else if( GetDisplay()->IsXinerama() ) + { + // place frame on same screen as mouse pointer + XLIB_Window aRoot, aChild; + int root_x = 0, root_y = 0, lx, ly; + unsigned int mask; + XQueryPointer( GetXDisplay(), + GetDisplay()->GetRootWindow( m_nScreen ), + &aRoot, &aChild, + &root_x, &root_y, &lx, &ly, &mask ); + const std::vector< Rectangle >& rScreens = GetDisplay()->GetXineramaScreens(); + for( unsigned int i = 0; i < rScreens.size(); i++ ) + if( rScreens[i].IsInside( Point( root_x, root_y ) ) ) + { + x = rScreens[i].Left(); + y = rScreens[i].Top(); + break; + } + } + } + } + Attributes.win_gravity = pDisplay_->getWMAdaptor()->getInitWinGravity(); + nAttrMask |= CWWinGravity; + if( mpParent ) + { + Attributes.save_under = True; + nAttrMask |= CWSaveUnder; + } + if( IsOverrideRedirect() ) + Attributes.override_redirect = True; + // default icon + if( (nStyle_ & SAL_FRAME_STYLE_INTRO) == 0 ) + { + bool bOk=false; + try + { + bOk=SelectAppIconPixmap( pDisplay_, m_nScreen, + mnIconID != 1 ? mnIconID : + (mpParent ? mpParent->mnIconID : 1), 32, + Hints.icon_pixmap, Hints.icon_mask ); + } + catch( com::sun::star::uno::Exception& ) + { + // can happen - no ucb during early startup + } + if( bOk ) + { + Hints.flags |= IconPixmapHint; + if( Hints.icon_mask ) + Hints.flags |= IconMaskHint; + } + } + + // find the top level frame of the transience hierarchy + X11SalFrame* pFrame = this; + while( pFrame->mpParent ) + pFrame = pFrame->mpParent; + if( (pFrame->nStyle_ & SAL_FRAME_STYLE_PLUG ) ) + { + // if the top level window is a plugin window, + // then we should place us in the same window group as + // the parent application (or none if there is no window group + // hint in the parent). + if( pFrame->GetShellWindow() ) + { + XWMHints* pWMHints = XGetWMHints( pDisplay_->GetDisplay(), + pFrame->GetShellWindow() ); + if( pWMHints ) + { + if( (pWMHints->flags & WindowGroupHint) ) + { + Hints.flags |= WindowGroupHint; + Hints.window_group = pWMHints->window_group; + } + XFree( pWMHints ); + } + } + } + else + { + Hints.flags |= WindowGroupHint; + Hints.window_group = pFrame->GetShellWindow(); + // note: for a normal document window this will produce None + // as the window is not yet created and the shell window is + // initialized to None. This must be corrected after window creation. + aClientLeader = GetDisplay()->GetDrawable( m_nScreen ); + } + } + + nShowState_ = SHOWSTATE_UNKNOWN; + bViewable_ = sal_True; + bMapped_ = sal_False; + nVisibility_ = VisibilityFullyObscured; + mhWindow = XCreateWindow( GetXDisplay(), + aFrameParent, + x, y, + w, h, + 0, + rVis.GetDepth(), + InputOutput, + rVis.GetVisual(), + nAttrMask, + &Attributes ); + // FIXME: see above: fake shell window for now to own window + if( pParentData == NULL ) + { + mhShellWindow = mhWindow; + } + + // correct window group if necessary + if( (Hints.flags & WindowGroupHint) == WindowGroupHint ) + { + if( Hints.window_group == None ) + Hints.window_group = GetShellWindow(); + } + + maGeometry.nX = x; + maGeometry.nY = y; + maGeometry.nWidth = w; + maGeometry.nHeight = h; + updateScreenNumber(); + + XSync( GetXDisplay(), False ); + setXEmbedInfo(); + + XLIB_Time nUserTime = (nStyle_ & (SAL_FRAME_STYLE_OWNERDRAWDECORATION | SAL_FRAME_STYLE_TOOLWINDOW) ) == 0 ? + pDisplay_->GetLastUserEventTime() : 0; + pDisplay_->getWMAdaptor()->setUserTime( this, nUserTime ); + + if( ! pParentData && ! IsChildWindow() && ! Attributes.override_redirect ) + { + XSetWMHints( GetXDisplay(), mhWindow, &Hints ); + // WM Protocols && internals + Atom a[4]; + int n = 0; + a[n++] = pDisplay_->getWMAdaptor()->getAtom( WMAdaptor::WM_DELETE_WINDOW ); + if( pDisplay_->getWMAdaptor()->getAtom( WMAdaptor::NET_WM_PING ) ) + a[n++] = pDisplay_->getWMAdaptor()->getAtom( WMAdaptor::NET_WM_PING ); + if( ! s_pSaveYourselfFrame && ! mpParent) + { + // at all times have only one frame with SaveYourself + a[n++] = pDisplay_->getWMAdaptor()->getAtom( WMAdaptor::WM_SAVE_YOURSELF ); + s_pSaveYourselfFrame = this; + } + if( (nSalFrameStyle & SAL_FRAME_STYLE_OWNERDRAWDECORATION) ) + a[n++] = pDisplay_->getWMAdaptor()->getAtom( WMAdaptor::WM_TAKE_FOCUS ); + XSetWMProtocols( GetXDisplay(), GetShellWindow(), a, n ); + + // force wm class hint + mnExtStyle = ~0; + if (mpParent) + m_sWMClass = mpParent->m_sWMClass; + SetExtendedFrameStyle( 0 ); + + XSizeHints* pHints = XAllocSizeHints(); + pHints->flags = PWinGravity | PPosition; + pHints->win_gravity = GetDisplay()->getWMAdaptor()->getPositionWinGravity(); + pHints->x = 0; + pHints->y = 0; + if( mbFullScreen ) + { + pHints->flags |= PMaxSize | PMinSize; + pHints->max_width = w+100; + pHints->max_height = h+100; + pHints->min_width = w; + pHints->min_height = h; + } + XSetWMNormalHints( GetXDisplay(), + GetShellWindow(), + pHints ); + XFree (pHints); + + // set PID and WM_CLIENT_MACHINE + pDisplay_->getWMAdaptor()->setClientMachine( this ); + pDisplay_->getWMAdaptor()->setPID( this ); + + // set client leader + if( aClientLeader ) + { + XChangeProperty( GetXDisplay(), + mhWindow, + pDisplay_->getWMAdaptor()->getAtom( WMAdaptor::WM_CLIENT_LEADER), + XA_WINDOW, + 32, + PropModeReplace, + (unsigned char*)&aClientLeader, + 1 + ); + } +#define DECOFLAGS (SAL_FRAME_STYLE_MOVEABLE | SAL_FRAME_STYLE_SIZEABLE | SAL_FRAME_STYLE_CLOSEABLE) + int nDecoFlags = WMAdaptor::decoration_All; + if( (nStyle_ & SAL_FRAME_STYLE_PARTIAL_FULLSCREEN) || + (nStyle_ & SAL_FRAME_STYLE_OWNERDRAWDECORATION) + ) + nDecoFlags = 0; + else if( (nStyle_ & DECOFLAGS ) != DECOFLAGS || (nStyle_ & SAL_FRAME_STYLE_TOOLWINDOW) ) + { + if( nStyle_ & DECOFLAGS ) + // if any decoration, then show a border + nDecoFlags = WMAdaptor::decoration_Border; + else + nDecoFlags = 0; + + if( ! mpParent && (nStyle_ & DECOFLAGS) ) + // don't add a min button if window should be decorationless + nDecoFlags |= WMAdaptor::decoration_MinimizeBtn; + if( nStyle_ & SAL_FRAME_STYLE_CLOSEABLE ) + nDecoFlags |= WMAdaptor::decoration_CloseBtn; + if( nStyle_ & SAL_FRAME_STYLE_SIZEABLE ) + { + nDecoFlags |= WMAdaptor::decoration_Resize; + if( ! (nStyle_ & SAL_FRAME_STYLE_TOOLWINDOW) ) + nDecoFlags |= WMAdaptor::decoration_MaximizeBtn; + } + if( nStyle_ & SAL_FRAME_STYLE_MOVEABLE ) + nDecoFlags |= WMAdaptor::decoration_Title; + } + + WMAdaptor::WMWindowType eType = WMAdaptor::windowType_Normal; + if( nStyle_ & SAL_FRAME_STYLE_INTRO ) + eType = WMAdaptor::windowType_Splash; + if( (nStyle_ & SAL_FRAME_STYLE_DIALOG) && hPresentationWindow == None ) + eType = WMAdaptor::windowType_ModelessDialogue; + if( nStyle_ & SAL_FRAME_STYLE_TOOLWINDOW ) + eType = WMAdaptor::windowType_Utility; + if( nStyle_ & SAL_FRAME_STYLE_OWNERDRAWDECORATION ) + eType = WMAdaptor::windowType_Toolbar; + if( (nStyle_ & SAL_FRAME_STYLE_PARTIAL_FULLSCREEN) + && GetDisplay()->getWMAdaptor()->isLegacyPartialFullscreen() ) + eType = WMAdaptor::windowType_Dock; + + GetDisplay()->getWMAdaptor()-> + setFrameTypeAndDecoration( this, + eType, + nDecoFlags, + hPresentationWindow ? NULL : mpParent ); + + if( (nStyle_ & (SAL_FRAME_STYLE_DEFAULT | + SAL_FRAME_STYLE_OWNERDRAWDECORATION| + SAL_FRAME_STYLE_FLOAT | + SAL_FRAME_STYLE_INTRO | + SAL_FRAME_STYLE_PARTIAL_FULLSCREEN) ) + == SAL_FRAME_STYLE_DEFAULT ) + pDisplay_->getWMAdaptor()->maximizeFrame( this, true, true ); + } + + m_nWorkArea = GetDisplay()->getWMAdaptor()->getCurrentWorkArea(); + + // Pointer + SetPointer( POINTER_ARROW ); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +X11SalFrame::X11SalFrame( SalFrame *pParent, sal_uLong nSalFrameStyle, SystemParentData* pSystemParent ) +{ + X11SalData* pSalData = GetX11SalData(); + + // initialize frame geometry + memset( &maGeometry, 0, sizeof(maGeometry) ); + + mpParent = static_cast< X11SalFrame* >( pParent ); + + mbTransientForRoot = false; + + pDisplay_ = pSalData->GetDisplay(); + // insert frame in framelist + pDisplay_->registerFrame( this ); + + mhWindow = None; + mhShellWindow = None; + mhStackingWindow = None; + mhForeignParent = None; + mhBackgroundPixmap = None; + m_bSetFocusOnMap = false; + + pGraphics_ = NULL; + pFreeGraphics_ = NULL; + + hCursor_ = None; + nCaptured_ = 0; + + nReleaseTime_ = 0; + nKeyCode_ = 0; + nKeyState_ = 0; + nCompose_ = -1; + mbSendExtKeyModChange = false; + mnExtKeyMod = 0; + + nShowState_ = SHOWSTATE_UNKNOWN; + nWidth_ = 0; + nHeight_ = 0; + nStyle_ = 0; + mnExtStyle = 0; + bAlwaysOnTop_ = sal_False; + + // set bViewable_ to sal_True: hack GetClientSize to report something + // different to 0/0 before first map + bViewable_ = sal_True; + bMapped_ = sal_False; + bDefaultPosition_ = sal_True; + nVisibility_ = VisibilityFullyObscured; + m_nWorkArea = 0; + mbInShow = sal_False; + m_bXEmbed = false; + + nScreenSaversTimeout_ = 0; + + mpInputContext = NULL; + mbInputFocus = False; + + maAlwaysOnTopRaiseTimer.SetTimeoutHdl( LINK( this, X11SalFrame, HandleAlwaysOnTopRaise ) ); + maAlwaysOnTopRaiseTimer.SetTimeout( 100 ); + + meWindowType = WMAdaptor::windowType_Normal; + mnDecorationFlags = WMAdaptor::decoration_All; + mbMaximizedVert = false; + mbMaximizedHorz = false; + mbShaded = false; + mbFullScreen = false; + + mnIconID = 1; // ICON_DEFAULT + + m_pClipRectangles = NULL; + m_nCurClipRect = 0; + m_nMaxClipRect = 0; + + if( mpParent ) + mpParent->maChildren.push_back( this ); + + Init( nSalFrameStyle, GetDisplay()->GetDefaultScreenNumber(), pSystemParent ); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void X11SalFrame::passOnSaveYourSelf() +{ + if( this == s_pSaveYourselfFrame ) + { + // pass on SaveYourself + const X11SalFrame* pFrame = NULL; + const std::list< SalFrame* >& rFrames = GetDisplay()->getFrames(); + std::list< SalFrame* >::const_iterator it = rFrames.begin(); + while( it != rFrames.end() ) + { + pFrame = static_cast< const X11SalFrame* >(*it); + if( ! ( IsChildWindow() || pFrame->mpParent ) + && pFrame != s_pSaveYourselfFrame ) + break; + ++it; + } + + s_pSaveYourselfFrame = (it != rFrames.end() ) ? const_cast<X11SalFrame*>(pFrame) : NULL; + if( s_pSaveYourselfFrame ) + { + Atom a[4]; + int n = 0; + a[n++] = pDisplay_->getWMAdaptor()->getAtom( WMAdaptor::WM_DELETE_WINDOW ); + a[n++] = pDisplay_->getWMAdaptor()->getAtom( WMAdaptor::WM_SAVE_YOURSELF ); + if( pDisplay_->getWMAdaptor()->getAtom( WMAdaptor::NET_WM_PING ) ) + a[n++] = pDisplay_->getWMAdaptor()->getAtom( WMAdaptor::NET_WM_PING ); + XSetWMProtocols( GetXDisplay(), s_pSaveYourselfFrame->GetShellWindow(), a, n ); + } + } +} + +X11SalFrame::~X11SalFrame() +{ + notifyDelete(); + + if( m_pClipRectangles ) + { + delete [] m_pClipRectangles; + m_pClipRectangles = NULL; + m_nCurClipRect = m_nMaxClipRect = 0; + } + + if( mhBackgroundPixmap ) + { + XSetWindowBackgroundPixmap( GetXDisplay(), GetWindow(), None ); + XFreePixmap( GetXDisplay(), mhBackgroundPixmap ); + } + + if( mhStackingWindow ) + aPresentationReparentList.remove( mhStackingWindow ); + + // remove from parent's list + if( mpParent ) + mpParent->maChildren.remove( this ); + + // deregister on SalDisplay + pDisplay_->deregisterFrame( this ); + + // unselect all events, some may be still in the queue anyway + if( ! IsSysChildWindow() ) + XSelectInput( GetXDisplay(), GetShellWindow(), 0 ); + XSelectInput( GetXDisplay(), GetWindow(), 0 ); + + ShowFullScreen( sal_False, 0 ); + + if( bMapped_ ) + Show( sal_False ); + + if( mpInputContext ) + { + mpInputContext->UnsetICFocus( this ); + mpInputContext->Unmap( this ); + delete mpInputContext; + } + + if( GetWindow() == hPresentationWindow ) + { + hPresentationWindow = None; + doReparentPresentationDialogues( GetDisplay() ); + } + + if( pGraphics_ ) + { + pGraphics_->DeInit(); + delete pGraphics_; + } + + if( pFreeGraphics_ ) + { + pFreeGraphics_->DeInit(); + delete pFreeGraphics_; + } + + + XDestroyWindow( GetXDisplay(), mhWindow ); + + /* + * check if there is only the status frame left + * if so, free it + */ + if( ! GetDisplay()->getFrames().empty() && I18NStatus::exists() ) + { + SalFrame* pStatusFrame = I18NStatus::get().getStatusFrame(); + std::list< SalFrame* >::const_iterator sit = GetDisplay()->getFrames().begin(); + if( pStatusFrame + && *sit == pStatusFrame + && ++sit == GetDisplay()->getFrames().end() ) + vcl::I18NStatus::free(); + } + + passOnSaveYourSelf(); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void X11SalFrame::SetExtendedFrameStyle( SalExtStyle nStyle ) +{ + if( nStyle != mnExtStyle && ! IsChildWindow() ) + { + mnExtStyle = nStyle; + updateWMClass(); + } +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void X11SalFrame::SetBackgroundBitmap( SalBitmap* pBitmap ) +{ + if( mhBackgroundPixmap ) + { + XSetWindowBackgroundPixmap( GetXDisplay(), GetWindow(), None ); + XFreePixmap( GetXDisplay(), mhBackgroundPixmap ); + mhBackgroundPixmap = None; + } + if( pBitmap ) + { + X11SalBitmap* pBM = static_cast<X11SalBitmap*>(pBitmap); + Size aSize = pBM->GetSize(); + if( aSize.Width() && aSize.Height() ) + { + mhBackgroundPixmap = + XCreatePixmap( GetXDisplay(), + GetWindow(), + aSize.Width(), + aSize.Height(), + GetDisplay()->GetVisual( m_nScreen ).GetDepth() ); + if( mhBackgroundPixmap ) + { + SalTwoRect aTwoRect; + aTwoRect.mnSrcX = aTwoRect.mnSrcY = aTwoRect.mnDestX = aTwoRect.mnDestY = 0; + aTwoRect.mnSrcWidth = aTwoRect.mnDestWidth = aSize.Width(); + aTwoRect.mnSrcHeight = aTwoRect.mnDestHeight = aSize.Height(); + pBM->ImplDraw( mhBackgroundPixmap, + m_nScreen, + GetDisplay()->GetVisual( m_nScreen ).GetDepth(), + aTwoRect, GetDisplay()->GetCopyGC( m_nScreen ) ); + XSetWindowBackgroundPixmap( GetXDisplay(), GetWindow(), mhBackgroundPixmap ); + } + } + } +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +const SystemChildData* X11SalFrame::GetSystemData() const +{ + X11SalFrame *pFrame = const_cast<X11SalFrame*>(this); + pFrame->maSystemChildData.nSize = sizeof( SystemChildData ); + pFrame->maSystemChildData.pDisplay = GetXDisplay(); + pFrame->maSystemChildData.aWindow = pFrame->GetWindow(); + pFrame->maSystemChildData.pSalFrame = pFrame; + pFrame->maSystemChildData.pWidget = NULL; + pFrame->maSystemChildData.pVisual = GetDisplay()->GetVisual( m_nScreen ).GetVisual(); + pFrame->maSystemChildData.nScreen = m_nScreen; + pFrame->maSystemChildData.nDepth = GetDisplay()->GetVisual( m_nScreen ).GetDepth(); + pFrame->maSystemChildData.aColormap = GetDisplay()->GetColormap( m_nScreen ).GetXColormap(); + pFrame->maSystemChildData.pAppContext = NULL; + pFrame->maSystemChildData.aShellWindow = pFrame->GetShellWindow(); + pFrame->maSystemChildData.pShellWidget = NULL; + return &maSystemChildData; +} + +SalGraphics *X11SalFrame::GetGraphics() +{ + if( pGraphics_ ) + return NULL; + + if( pFreeGraphics_ ) + { + pGraphics_ = pFreeGraphics_; + pFreeGraphics_ = NULL; + } + else + { + pGraphics_ = new X11SalGraphics(); + pGraphics_->Init( this, GetWindow(), m_nScreen ); + } + + return pGraphics_; +} + +void X11SalFrame::ReleaseGraphics( SalGraphics *pGraphics ) +{ + DBG_ASSERT( pGraphics == pGraphics_, "SalFrame::ReleaseGraphics pGraphics!=pGraphics_" ); + + if( pGraphics != pGraphics_ ) + return; + + pFreeGraphics_ = pGraphics_; + pGraphics_ = NULL; +} + +void X11SalFrame::updateGraphics( bool bClear ) +{ + Drawable aDrawable = bClear ? None : GetWindow(); + if( pGraphics_ ) + pGraphics_->SetDrawable( aDrawable, m_nScreen ); + if( pFreeGraphics_ ) + pFreeGraphics_->SetDrawable( aDrawable, m_nScreen ); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void X11SalFrame::Enable( sal_Bool /*bEnable*/ ) +{ + // NYI: enable/disable frame +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void X11SalFrame::SetIcon( sal_uInt16 nIcon ) +{ + if ( ! IsChildWindow() ) + { + // 0 == default icon -> #1 + if ( nIcon == 0 ) + nIcon = 1; + + mnIconID = nIcon; + + XIconSize *pIconSize = NULL; + int nSizes = 0; + int iconSize = 32; + if ( XGetIconSizes( GetXDisplay(), GetDisplay()->GetRootWindow( m_nScreen ), &pIconSize, &nSizes ) ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf(stderr, "X11SalFrame::SetIcon(): found %d IconSizes:\n", nSizes); +#endif + + const int ourLargestIconSize = 48; + bool bFoundIconSize = false; + + int i; + for( i=0; i<nSizes; i++) + { + // select largest supported icon + + // Note: olwm/olvwm reports a huge max icon size of + // 160x160 pixels; always choosing the max as the + // preferred icon size is apparently wrong under olvwm + // - so we keep the safe default |iconSize| when we see + // unreasonable large max icon sizes (> twice of our + // largest available icon) reported by XGetIconSizes. + if( pIconSize[i].max_width > iconSize + && pIconSize[i].max_width <= 2*ourLargestIconSize ) + { + iconSize = pIconSize[i].max_width; + bFoundIconSize = true; + } + iconSize = pIconSize[i].max_width; + +#if OSL_DEBUG_LEVEL > 1 + fprintf(stderr, "min: %d, %d\nmax: %d, %d\ninc: %d, %d\n\n", + pIconSize[i].min_width, pIconSize[i].min_height, + pIconSize[i].max_width, pIconSize[i].max_height, + pIconSize[i].width_inc, pIconSize[i].height_inc); +#endif + } + + if ( !bFoundIconSize ) + { + // Unless someone has fixed olwm/olvwm, we have rejected + // the max icon size from |XGetIconSizes()|. Provide a + // better icon size default value, in case our window manager + // is olwm/olvwm. + const String& rWM( pDisplay_->getWMAdaptor()->getWindowManagerName() ); + + if ( rWM.EqualsAscii( "Olwm" ) ) + iconSize = 48; + } + + XFree( pIconSize ); + } + else + { + const String& rWM( pDisplay_->getWMAdaptor()->getWindowManagerName() ); + if( rWM.EqualsAscii( "KWin" ) ) // assume KDE is running + iconSize = 48; + static bool bGnomeIconSize = false; + static bool bGnomeChecked = false; + if( ! bGnomeChecked ) + { + bGnomeChecked=true; + int nCount = 0; + Atom* pProps = XListProperties( GetXDisplay(), + GetDisplay()->GetRootWindow( m_nScreen ), + &nCount ); + for( int i = 0; i < nCount && !bGnomeIconSize; i++ ) + { + char* pName = XGetAtomName( GetXDisplay(), pProps[i] ); + if( !strcmp( pName, "GNOME_PANEL_DESKTOP_AREA" ) ) + bGnomeIconSize = true; + if( pName ) + XFree( pName ); + } + if( pProps ) + XFree( pProps ); + } + if( bGnomeIconSize ) + iconSize = 48; + } + + XWMHints Hints; + Hints.flags = 0; + XWMHints *pHints = XGetWMHints( GetXDisplay(), GetShellWindow() ); + if( pHints ) + { + memcpy(&Hints, pHints, sizeof( XWMHints )); + XFree( pHints ); + } + pHints = &Hints; + + sal_Bool bOk = SelectAppIconPixmap( GetDisplay(), m_nScreen, + nIcon, iconSize, + pHints->icon_pixmap, pHints->icon_mask ); + if ( !bOk ) + { + // load default icon (0) + bOk = SelectAppIconPixmap( GetDisplay(), m_nScreen, + 0, iconSize, + pHints->icon_pixmap, pHints->icon_mask ); + } + if( bOk ) + { + pHints->flags |= IconPixmapHint; + if( pHints->icon_mask ) + pHints->flags |= IconMaskHint; + + XSetWMHints( GetXDisplay(), GetShellWindow(), pHints ); + } + } +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void X11SalFrame::SetMaxClientSize( long nWidth, long nHeight ) +{ + if( ! IsChildWindow() ) + { + if( GetShellWindow() && (nStyle_ & (SAL_FRAME_STYLE_FLOAT|SAL_FRAME_STYLE_OWNERDRAWDECORATION) ) != SAL_FRAME_STYLE_FLOAT ) + { + XSizeHints* pHints = XAllocSizeHints(); + long nSupplied = 0; + XGetWMNormalHints( GetXDisplay(), + GetShellWindow(), + pHints, + &nSupplied + ); + pHints->max_width = nWidth; + pHints->max_height = nHeight; + pHints->flags |= PMaxSize; + XSetWMNormalHints( GetXDisplay(), + GetShellWindow(), + pHints ); + XFree( pHints ); + } + } +} + +void X11SalFrame::SetMinClientSize( long nWidth, long nHeight ) +{ + if( ! IsChildWindow() ) + { + if( GetShellWindow() && (nStyle_ & (SAL_FRAME_STYLE_FLOAT|SAL_FRAME_STYLE_OWNERDRAWDECORATION) ) != SAL_FRAME_STYLE_FLOAT ) + { + XSizeHints* pHints = XAllocSizeHints(); + long nSupplied = 0; + XGetWMNormalHints( GetXDisplay(), + GetShellWindow(), + pHints, + &nSupplied + ); + pHints->min_width = nWidth; + pHints->min_height = nHeight; + pHints->flags |= PMinSize; + XSetWMNormalHints( GetXDisplay(), + GetShellWindow(), + pHints ); + XFree( pHints ); + } + } +} + +// Show + Pos (x,y,z) + Size (width,height) +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalFrame::Show( sal_Bool bVisible, sal_Bool bNoActivate ) +{ + if( ( bVisible && bMapped_ ) + || ( !bVisible && !bMapped_ ) ) + return; + + // HACK: this is a workaround for (at least) kwin + // even though transient frames should be kept above their parent + // this does not necessarily hold true for DOCK type windows + // so artificially set ABOVE and remove it again on hide + if( mpParent && (mpParent->nStyle_ & SAL_FRAME_STYLE_PARTIAL_FULLSCREEN ) && pDisplay_->getWMAdaptor()->isLegacyPartialFullscreen()) + pDisplay_->getWMAdaptor()->enableAlwaysOnTop( this, bVisible ); + + bMapped_ = bVisible; + bViewable_ = bVisible; + setXEmbedInfo(); + if( bVisible ) + { + mbInShow = sal_True; + if( ! (nStyle_ & SAL_FRAME_STYLE_INTRO) ) + { + // hide all INTRO frames + const std::list< SalFrame* >& rFrames = GetDisplay()->getFrames(); + for( std::list< SalFrame* >::const_iterator it = rFrames.begin(); it != rFrames.end(); ++it ) + { + const X11SalFrame* pFrame = static_cast< const X11SalFrame* >(*it); + // look for intro bit map; if present, hide it + if( pFrame->nStyle_ & SAL_FRAME_STYLE_INTRO ) + { + if( pFrame->bMapped_ ) + const_cast<X11SalFrame*>(pFrame)->Show( sal_False ); + } + } + } + + // update NET_WM_STATE which may have been deleted due to earlier Show(sal_False) + if( nShowState_ == SHOWSTATE_HIDDEN ) + GetDisplay()->getWMAdaptor()->frameIsMapping( this ); + + /* + * Actually this is rather exotic and currently happens only in conjunction + * with the basic dialogue editor, + * which shows a frame and instantly hides it again. After that the + * editor window is shown and the WM takes this as an opportunity + * to show our hidden transient frame also. So Show( sal_False ) must + * withdraw the frame AND delete the WM_TRANSIENT_FOR property. + * In case the frame is shown again, the transient hint must be restored here. + */ + if( ! IsChildWindow() + && ! IsOverrideRedirect() + && ! IsFloatGrabWindow() + && mpParent + ) + { + GetDisplay()->getWMAdaptor()->changeReferenceFrame( this, mpParent ); + } + + // #i45160# switch to desktop where a dialog with parent will appear + if( mpParent && mpParent->m_nWorkArea != m_nWorkArea ) + GetDisplay()->getWMAdaptor()->switchToWorkArea( mpParent->m_nWorkArea ); + + if( IsFloatGrabWindow() && + mpParent && + nVisibleFloats == 0 && + ! GetDisplay()->GetCaptureFrame() ) + { + /* #i39420# + * outsmart KWin's "focus strictly under mouse" mode + * which insists on taking the focus from the document + * to the new float. Grab focus to parent frame BEFORE + * showing the float (cannot grab it to the float + * before show). + */ + XGrabPointer( GetXDisplay(), + mpParent->GetWindow(), + True, + PointerMotionMask | ButtonPressMask | ButtonReleaseMask, + GrabModeAsync, + GrabModeAsync, + None, + mpParent ? mpParent->GetCursor() : None, + CurrentTime + ); + } + + XLIB_Time nUserTime = 0; + if( ! bNoActivate && (nStyle_ & (SAL_FRAME_STYLE_OWNERDRAWDECORATION)) == 0 ) + nUserTime = pDisplay_->GetLastUserEventTime( true ); + GetDisplay()->getWMAdaptor()->setUserTime( this, nUserTime ); + if( ! bNoActivate && (nStyle_ & SAL_FRAME_STYLE_TOOLWINDOW) ) + m_bSetFocusOnMap = true; + + // actually map the window + if( m_bXEmbed ) + askForXEmbedFocus( 0 ); + else + { + if( GetWindow() != GetShellWindow() && ! IsSysChildWindow() ) + { + if( IsChildWindow() ) + XMapWindow( GetXDisplay(), GetShellWindow() ); + XSelectInput( GetXDisplay(), GetShellWindow(), CLIENT_EVENTS ); + } + if( nStyle_ & SAL_FRAME_STYLE_FLOAT ) + XMapRaised( GetXDisplay(), GetWindow() ); + else + XMapWindow( GetXDisplay(), GetWindow() ); + } + XSelectInput( GetXDisplay(), GetWindow(), CLIENT_EVENTS ); + + if( maGeometry.nWidth > 0 + && maGeometry.nHeight > 0 + && ( nWidth_ != (int)maGeometry.nWidth + || nHeight_ != (int)maGeometry.nHeight ) ) + { + nWidth_ = maGeometry.nWidth; + nHeight_ = maGeometry.nHeight; + } + + XSync( GetXDisplay(), False ); + + if( IsFloatGrabWindow() ) + { + /* + * Sawfish and twm can be switched to enter-exit focus behaviour. In this case + * we must grab the pointer else the dumb WM will put the focus to the + * override-redirect float window. The application window will be deactivated + * which causes that the floats are destroyed, so the user can never click on + * a menu because it vanishes as soon as he enters it. + */ + nVisibleFloats++; + if( nVisibleFloats == 1 && ! GetDisplay()->GetCaptureFrame() ) + { + /* #i39420# now move grab to the new float window */ + XGrabPointer( GetXDisplay(), + GetWindow(), + True, + PointerMotionMask | ButtonPressMask | ButtonReleaseMask, + GrabModeAsync, + GrabModeAsync, + None, + mpParent ? mpParent->GetCursor() : None, + CurrentTime + ); + } + } + CallCallback( SALEVENT_RESIZE, NULL ); + + /* + * sometimes a message box/dialogue is brought up when a frame is not mapped + * the corresponding TRANSIENT_FOR hint is then set to the root window + * so that the dialogue shows in all cases. Correct it here if the + * frame is shown afterwards. + */ + if( ! IsChildWindow() + && ! IsOverrideRedirect() + && ! IsFloatGrabWindow() + ) + { + for( std::list< X11SalFrame* >::const_iterator it = maChildren.begin(); + it != maChildren.end(); ++it ) + { + if( (*it)->mbTransientForRoot ) + GetDisplay()->getWMAdaptor()->changeReferenceFrame( *it, this ); + } + } + /* + * leave SHOWSTATE_UNKNOWN as this indicates first mapping + * and is only reset int HandleSizeEvent + */ + if( nShowState_ != SHOWSTATE_UNKNOWN ) + nShowState_ = SHOWSTATE_NORMAL; + + /* + * plugged windows don't necessarily get the + * focus on show because the parent may already be mapped + * and have the focus. So try to set the focus + * to the child on Show(sal_True) + */ + if( (nStyle_ & SAL_FRAME_STYLE_PLUG) && ! m_bXEmbed ) + XSetInputFocus( GetXDisplay(), + GetWindow(), + RevertToParent, + CurrentTime ); + + if( mpParent ) + { + // push this frame so it will be in front of its siblings + // only necessary for insane transient behaviour of Dtwm/olwm + mpParent->maChildren.remove( this ); + mpParent->maChildren.push_front(this); + } + } + else + { + if( getInputContext() ) + getInputContext()->Unmap( this ); + + if( ! IsChildWindow() ) + { + /* FIXME: Is deleting the property really necessary ? It hurts + * owner drawn windows at least. + */ + if( mpParent && ! (nStyle_ & SAL_FRAME_STYLE_OWNERDRAWDECORATION) ) + XDeleteProperty( GetXDisplay(), GetShellWindow(), GetDisplay()->getWMAdaptor()->getAtom( WMAdaptor::WM_TRANSIENT_FOR ) ); + XWithdrawWindow( GetXDisplay(), GetShellWindow(), m_nScreen ); + } + else if( ! m_bXEmbed ) + XUnmapWindow( GetXDisplay(), GetWindow() ); + + nShowState_ = SHOWSTATE_HIDDEN; + if( IsFloatGrabWindow() && nVisibleFloats ) + { + nVisibleFloats--; + if( nVisibleFloats == 0 && ! GetDisplay()->GetCaptureFrame() ) + XUngrabPointer( GetXDisplay(), + CurrentTime ); + } + // flush here; there may be a very seldom race between + // the display connection used for clipboard and our connection + Flush(); + } +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalFrame::ToTop( sal_uInt16 nFlags ) +{ + if( ( nFlags & SAL_FRAME_TOTOP_RESTOREWHENMIN ) + && ! ( nStyle_ & SAL_FRAME_STYLE_FLOAT ) + && nShowState_ != SHOWSTATE_HIDDEN + && nShowState_ != SHOWSTATE_UNKNOWN + ) + { + GetDisplay()->getWMAdaptor()->frameIsMapping( this ); + if( GetWindow() != GetShellWindow() && ! IsSysChildWindow() ) + XMapWindow( GetXDisplay(), GetShellWindow() ); + XMapWindow( GetXDisplay(), GetWindow() ); + } + + XLIB_Window aToTopWindow = IsSysChildWindow() ? GetWindow() : GetShellWindow(); + if( ! (nFlags & SAL_FRAME_TOTOP_GRABFOCUS_ONLY) ) + { + XRaiseWindow( GetXDisplay(), aToTopWindow ); + if( ! GetDisplay()->getWMAdaptor()->isTransientBehaviourAsExpected() ) + for( std::list< X11SalFrame* >::const_iterator it = maChildren.begin(); + it != maChildren.end(); ++it ) + (*it)->ToTop( nFlags & ~SAL_FRAME_TOTOP_GRABFOCUS ); + } + + if( ( ( nFlags & SAL_FRAME_TOTOP_GRABFOCUS ) || ( nFlags & SAL_FRAME_TOTOP_GRABFOCUS_ONLY ) ) + && bMapped_ ) + { + if( m_bXEmbed ) + askForXEmbedFocus( 0 ); + else + XSetInputFocus( GetXDisplay(), aToTopWindow, RevertToParent, CurrentTime ); + } +} +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalFrame::GetWorkArea( Rectangle& rWorkArea ) +{ + rWorkArea = pDisplay_->getWMAdaptor()->getWorkArea( 0 ); +} +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalFrame::GetClientSize( long &rWidth, long &rHeight ) +{ + if( ! bViewable_ ) + { + rWidth = rHeight = 0; + return; + } + + rWidth = maGeometry.nWidth; + rHeight = maGeometry.nHeight; + + if( !rWidth || !rHeight ) + { + XWindowAttributes aAttrib; + + XGetWindowAttributes( GetXDisplay(), GetWindow(), &aAttrib ); + + maGeometry.nWidth = rWidth = aAttrib.width; + maGeometry.nHeight = rHeight = aAttrib.height; + } +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void X11SalFrame::SetWindowGravity (int nGravity) const +{ + if( ! IsChildWindow() ) + { + XSizeHints* pHint = XAllocSizeHints(); + long nFlag; + + XGetWMNormalHints (GetXDisplay(), GetShellWindow(), pHint, &nFlag); + pHint->flags |= PWinGravity; + pHint->win_gravity = nGravity; + + XSetWMNormalHints (GetXDisplay(), GetShellWindow(), pHint); + XSync (GetXDisplay(), False); + + XFree (pHint); + } +} + +void X11SalFrame::Center( ) +{ + int nX, nY, nScreenWidth, nScreenHeight; + int nRealScreenWidth, nRealScreenHeight; + int nScreenX = 0, nScreenY = 0; + + const Size& aScreenSize = GetDisplay()->getDataForScreen( m_nScreen ).m_aSize; + nScreenWidth = aScreenSize.Width(); + nScreenHeight = aScreenSize.Height(); + nRealScreenWidth = nScreenWidth; + nRealScreenHeight = nScreenHeight; + + if( GetDisplay()->IsXinerama() ) + { + // get xinerama screen we are on + // if there is a parent, use its center for screen determination + // else use the pointer + XLIB_Window aRoot, aChild; + int root_x, root_y, x, y; + unsigned int mask; + if( mpParent ) + { + root_x = mpParent->maGeometry.nX + mpParent->maGeometry.nWidth/2; + root_y = mpParent->maGeometry.nY + mpParent->maGeometry.nHeight/2; + } + else + XQueryPointer( GetXDisplay(), + GetShellWindow(), + &aRoot, &aChild, + &root_x, &root_y, + &x, &y, + &mask ); + const std::vector< Rectangle >& rScreens = GetDisplay()->GetXineramaScreens(); + for( unsigned int i = 0; i < rScreens.size(); i++ ) + if( rScreens[i].IsInside( Point( root_x, root_y ) ) ) + { + nScreenX = rScreens[i].Left(); + nScreenY = rScreens[i].Top(); + nRealScreenWidth = rScreens[i].GetWidth(); + nRealScreenHeight = rScreens[i].GetHeight(); + break; + } + } + + if( mpParent ) + { + X11SalFrame* pFrame = mpParent; + while( pFrame->mpParent ) + pFrame = pFrame->mpParent; + if( pFrame->maGeometry.nWidth < 1 || pFrame->maGeometry.nHeight < 1 ) + { + Rectangle aRect; + pFrame->GetPosSize( aRect ); + pFrame->maGeometry.nX = aRect.Left(); + pFrame->maGeometry.nY = aRect.Top(); + pFrame->maGeometry.nWidth = aRect.GetWidth(); + pFrame->maGeometry.nHeight = aRect.GetHeight(); + } + + if( pFrame->nStyle_ & SAL_FRAME_STYLE_PLUG ) + { + XLIB_Window aRoot; + unsigned int bw, depth; + XGetGeometry( GetXDisplay(), + pFrame->GetShellWindow(), + &aRoot, + &nScreenX, &nScreenY, + (unsigned int*)&nScreenWidth, + (unsigned int*)&nScreenHeight, + &bw, &depth ); + } + else + { + nScreenX = pFrame->maGeometry.nX; + nScreenY = pFrame->maGeometry.nY; + nScreenWidth = pFrame->maGeometry.nWidth; + nScreenHeight = pFrame->maGeometry.nHeight; + } + } + + if( mpParent && mpParent->nShowState_ == SHOWSTATE_NORMAL ) + { + if( maGeometry.nWidth >= mpParent->maGeometry.nWidth && + maGeometry.nHeight >= mpParent->maGeometry.nHeight ) + { + nX = nScreenX + 40; + nY = nScreenY + 40; + } + else + { + // center the window relative to the top level frame + nX = (nScreenWidth - (int)maGeometry.nWidth ) / 2 + nScreenX; + nY = (nScreenHeight - (int)maGeometry.nHeight) / 2 + nScreenY; + } + } + else + { + // center the window relative to screen + nX = (nRealScreenWidth - (int)maGeometry.nWidth ) / 2 + nScreenX; + nY = (nRealScreenHeight - (int)maGeometry.nHeight) / 2 + nScreenY; + } + nX = nX < 0 ? 0 : nX; + nY = nY < 0 ? 0 : nY; + + bDefaultPosition_ = False; + if( mpParent ) + { + nX -= mpParent->maGeometry.nX; + nY -= mpParent->maGeometry.nY; + } + + Point aPoint(nX, nY); + SetPosSize( Rectangle( aPoint, Size( maGeometry.nWidth, maGeometry.nHeight ) ) ); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalFrame::updateScreenNumber() +{ + if( GetDisplay()->IsXinerama() && GetDisplay()->GetXineramaScreens().size() > 1 ) + { + Point aPoint( maGeometry.nX, maGeometry.nY ); + const std::vector<Rectangle>& rScreenRects( GetDisplay()->GetXineramaScreens() ); + size_t nScreens = rScreenRects.size(); + for( size_t i = 0; i < nScreens; i++ ) + { + if( rScreenRects[i].IsInside( aPoint ) ) + { + maGeometry.nScreenNumber = static_cast<unsigned int>(i); + break; + } + } + } + else + maGeometry.nScreenNumber = static_cast<unsigned int>(m_nScreen); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalFrame::SetPosSize( long nX, long nY, long nWidth, long nHeight, sal_uInt16 nFlags ) +{ + if( nStyle_ & SAL_FRAME_STYLE_PLUG ) + return; + + // relative positioning in X11SalFrame::SetPosSize + Rectangle aPosSize( Point( maGeometry.nX, maGeometry.nY ), Size( maGeometry.nWidth, maGeometry.nHeight ) ); + aPosSize.Justify(); + + if( ! ( nFlags & SAL_FRAME_POSSIZE_X ) ) + { + nX = aPosSize.Left(); + if( mpParent ) + nX -= mpParent->maGeometry.nX; + } + if( ! ( nFlags & SAL_FRAME_POSSIZE_Y ) ) + { + nY = aPosSize.Top(); + if( mpParent ) + nY -= mpParent->maGeometry.nY; + } + if( ! ( nFlags & SAL_FRAME_POSSIZE_WIDTH ) ) + nWidth = aPosSize.GetWidth(); + if( ! ( nFlags & SAL_FRAME_POSSIZE_HEIGHT ) ) + nHeight = aPosSize.GetHeight(); + + aPosSize = Rectangle( Point( nX, nY ), Size( nWidth, nHeight ) ); + + if( ! ( nFlags & ( SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y ) ) ) + { + if( bDefaultPosition_ ) + { + maGeometry.nWidth = aPosSize.GetWidth(); + maGeometry.nHeight = aPosSize.GetHeight(); + Center(); + } + else + SetSize( Size( nWidth, nHeight ) ); + } + else + SetPosSize( aPosSize ); + + bDefaultPosition_ = False; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalFrame::SetAlwaysOnTop( sal_Bool bOnTop ) +{ + if( ! IsOverrideRedirect() ) + { + bAlwaysOnTop_ = bOnTop; + pDisplay_->getWMAdaptor()->enableAlwaysOnTop( this, bOnTop ); + } +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +#define _FRAMESTATE_MASK_GEOMETRY \ + (SAL_FRAMESTATE_MASK_X | SAL_FRAMESTATE_MASK_Y | \ + SAL_FRAMESTATE_MASK_WIDTH | SAL_FRAMESTATE_MASK_HEIGHT) +#define _FRAMESTATE_MASK_MAXIMIZED_GEOMETRY \ + (SAL_FRAMESTATE_MASK_MAXIMIZED_X | SAL_FRAMESTATE_MASK_MAXIMIZED_Y | \ + SAL_FRAMESTATE_MASK_MAXIMIZED_WIDTH | SAL_FRAMESTATE_MASK_MAXIMIZED_HEIGHT) + +void X11SalFrame::SetWindowState( const SalFrameState *pState ) +{ + if (pState == NULL) + return; + + // Request for position or size change + if (pState->mnMask & _FRAMESTATE_MASK_GEOMETRY) + { + Rectangle aPosSize; + bool bDoAdjust = false; + + /* #i44325# + * if maximized, set restore size and guess maximized size from last time + * in state change below maximize window + */ + if( ! IsChildWindow() && + (pState->mnMask & SAL_FRAMESTATE_MASK_STATE) && + (pState->mnState & SAL_FRAMESTATE_MAXIMIZED) && + (pState->mnMask & _FRAMESTATE_MASK_GEOMETRY) == _FRAMESTATE_MASK_GEOMETRY && + (pState->mnMask & _FRAMESTATE_MASK_MAXIMIZED_GEOMETRY) == _FRAMESTATE_MASK_MAXIMIZED_GEOMETRY + ) + { + XSizeHints* pHints = XAllocSizeHints(); + long nSupplied = 0; + XGetWMNormalHints( GetXDisplay(), + GetShellWindow(), + pHints, + &nSupplied ); + pHints->flags |= PPosition | PWinGravity; + pHints->x = pState->mnX; + pHints->y = pState->mnY; + pHints->win_gravity = pDisplay_->getWMAdaptor()->getPositionWinGravity(); + XSetWMNormalHints( GetXDisplay(), + GetShellWindow(), + pHints ); + XFree( pHints ); + + XMoveResizeWindow( GetXDisplay(), GetShellWindow(), + pState->mnX, pState->mnY, + pState->mnWidth, pState->mnHeight ); + // guess maximized geometry from last time + maGeometry.nX = pState->mnMaximizedX; + maGeometry.nY = pState->mnMaximizedY; + maGeometry.nWidth = pState->mnMaximizedWidth; + maGeometry.nHeight = pState->mnMaximizedHeight; + updateScreenNumber(); + } + else + { + // initialize with current geometry + if ((pState->mnMask & _FRAMESTATE_MASK_GEOMETRY) != _FRAMESTATE_MASK_GEOMETRY) + GetPosSize (aPosSize); + + // change requested properties + if (pState->mnMask & SAL_FRAMESTATE_MASK_X) + { + aPosSize.setX (pState->mnX); + } + if (pState->mnMask & SAL_FRAMESTATE_MASK_Y) + { + aPosSize.setY (pState->mnY); + } + if (pState->mnMask & SAL_FRAMESTATE_MASK_WIDTH) + { + long nWidth = pState->mnWidth > 0 ? pState->mnWidth - 1 : 0; + aPosSize.setWidth (nWidth); + bDoAdjust = true; + } + if (pState->mnMask & SAL_FRAMESTATE_MASK_HEIGHT) + { + int nHeight = pState->mnHeight > 0 ? pState->mnHeight - 1 : 0; + aPosSize.setHeight (nHeight); + bDoAdjust = true; + } + + const Size& aScreenSize = pDisplay_->getDataForScreen( m_nScreen ).m_aSize; + const WMAdaptor *pWM = GetDisplay()->getWMAdaptor(); + + if( bDoAdjust && aPosSize.GetWidth() <= aScreenSize.Width() + && aPosSize.GetHeight() <= aScreenSize.Height() ) + { + SalFrameGeometry aGeom = maGeometry; + + if( ! (nStyle_ & ( SAL_FRAME_STYLE_FLOAT | SAL_FRAME_STYLE_PLUG ) ) && + mpParent && + aGeom.nLeftDecoration == 0 && + aGeom.nTopDecoration == 0 ) + { + aGeom = mpParent->maGeometry; + if( aGeom.nLeftDecoration == 0 && + aGeom.nTopDecoration == 0 ) + { + aGeom.nLeftDecoration = 5; + aGeom.nTopDecoration = 20; + aGeom.nRightDecoration = 5; + aGeom.nBottomDecoration = 5; + } + } + + // adjust position so that frame fits onto screen + if( aPosSize.Right()+(long)aGeom.nRightDecoration > aScreenSize.Width()-1 ) + aPosSize.Move( (long)aScreenSize.Width() - (long)aPosSize.Right() - (long)aGeom.nRightDecoration, 0 ); + if( aPosSize.Bottom()+(long)aGeom.nBottomDecoration > aScreenSize.Height()-1 ) + aPosSize.Move( 0, (long)aScreenSize.Height() - (long)aPosSize.Bottom() - (long)aGeom.nBottomDecoration ); + if( aPosSize.Left() < (long)aGeom.nLeftDecoration ) + aPosSize.Move( (long)aGeom.nLeftDecoration - (long)aPosSize.Left(), 0 ); + if( aPosSize.Top() < (long)aGeom.nTopDecoration ) + aPosSize.Move( 0, (long)aGeom.nTopDecoration - (long)aPosSize.Top() ); + } + + // resize with new args + if (pWM->supportsICCCMPos()) + { + if( mpParent ) + aPosSize.Move( -mpParent->maGeometry.nX, + -mpParent->maGeometry.nY ); + SetPosSize( aPosSize ); + bDefaultPosition_ = False; + } + else + SetPosSize( 0, 0, aPosSize.GetWidth(), aPosSize.GetHeight(), SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT ); + } + } + + // request for status change + if (pState->mnMask & SAL_FRAMESTATE_MASK_STATE) + { + if (pState->mnState & SAL_FRAMESTATE_MAXIMIZED) + { + nShowState_ = SHOWSTATE_NORMAL; + if( ! (pState->mnState & (SAL_FRAMESTATE_MAXIMIZED_HORZ|SAL_FRAMESTATE_MAXIMIZED_VERT) ) ) + Maximize(); + else + { + bool bHorz = (pState->mnState & SAL_FRAMESTATE_MAXIMIZED_HORZ) ? true : false; + bool bVert = (pState->mnState & SAL_FRAMESTATE_MAXIMIZED_VERT) ? true : false; + GetDisplay()->getWMAdaptor()->maximizeFrame( this, bHorz, bVert ); + } + maRestorePosSize.Left() = pState->mnX; + maRestorePosSize.Top() = pState->mnY; + maRestorePosSize.Right() = maRestorePosSize.Left() + pState->mnWidth; + maRestorePosSize.Right() = maRestorePosSize.Left() + pState->mnHeight; + } + else if( mbMaximizedHorz || mbMaximizedVert ) + GetDisplay()->getWMAdaptor()->maximizeFrame( this, false, false ); + + if (pState->mnState & SAL_FRAMESTATE_MINIMIZED) + { + if (nShowState_ == SHOWSTATE_UNKNOWN) + nShowState_ = SHOWSTATE_NORMAL; + Minimize(); + } + if (pState->mnState & SAL_FRAMESTATE_NORMAL) + { + if (nShowState_ != SHOWSTATE_NORMAL) + Restore(); + } + if (pState->mnState & SAL_FRAMESTATE_ROLLUP) + GetDisplay()->getWMAdaptor()->shade( this, true ); + } +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +sal_Bool X11SalFrame::GetWindowState( SalFrameState* pState ) +{ + if( SHOWSTATE_MINIMIZED == nShowState_ ) + pState->mnState = SAL_FRAMESTATE_MINIMIZED; + else + pState->mnState = SAL_FRAMESTATE_NORMAL; + + Rectangle aPosSize; + if( maRestorePosSize.IsEmpty() ) + GetPosSize( aPosSize ); + else + aPosSize = maRestorePosSize; + + if( mbMaximizedHorz ) + pState->mnState |= SAL_FRAMESTATE_MAXIMIZED_HORZ; + if( mbMaximizedVert ) + pState->mnState |= SAL_FRAMESTATE_MAXIMIZED_VERT; + if( mbShaded ) + pState->mnState |= SAL_FRAMESTATE_ROLLUP; + + pState->mnX = aPosSize.Left(); + pState->mnY = aPosSize.Top(); + pState->mnWidth = aPosSize.GetWidth(); + pState->mnHeight = aPosSize.GetHeight(); + + pState->mnMask = _FRAMESTATE_MASK_GEOMETRY | SAL_FRAMESTATE_MASK_STATE; + + + if (! maRestorePosSize.IsEmpty() ) + { + GetPosSize( aPosSize ); + pState->mnState |= SAL_FRAMESTATE_MAXIMIZED; + pState->mnMaximizedX = aPosSize.Left(); + pState->mnMaximizedY = aPosSize.Top(); + pState->mnMaximizedWidth = aPosSize.GetWidth(); + pState->mnMaximizedHeight = aPosSize.GetHeight(); + pState->mnMask |= _FRAMESTATE_MASK_MAXIMIZED_GEOMETRY; + } + + return sal_True; +} + +// ---------------------------------------------------------------------------- +// get a screenshot of the current frame including window manager decoration +SalBitmap* X11SalFrame::SnapShot() +{ + Display* pDisplay = GetXDisplay(); + + // make sure the frame has been reparented and all paint timer have been + // expired + do + { + XSync(pDisplay, False); + Application::Reschedule (); + } + while (XPending(pDisplay)); + TimeValue aVal; + aVal.Seconds = 0; + aVal.Nanosec = 50000000; + osl_waitThread( &aVal ); + do + { + XSync(pDisplay, False); + Application::Reschedule (); + } + while (XPending(pDisplay)); + + // get the most outer window, usually the window manager decoration + Drawable hWindow = None; + if (IsOverrideRedirect()) + hWindow = GetDrawable(); + else + if (hPresentationWindow != None) + hWindow = hPresentationWindow; + else + hWindow = GetStackingWindow(); + + // query the contents of the window + if (hWindow != None) + { + X11SalBitmap *pBmp = new X11SalBitmap; + if (pBmp->SnapShot (pDisplay, hWindow)) + return pBmp; + else + delete pBmp; + } + + return NULL; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +// native menu implementation - currently empty +void X11SalFrame::DrawMenuBar() +{ +} + +void X11SalFrame::SetMenu( SalMenu* ) +{ +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalFrame::GetPosSize( Rectangle &rPosSize ) +{ + if( maGeometry.nWidth < 1 || maGeometry.nHeight < 1 ) + { + const Size& aScreenSize = pDisplay_->getDataForScreen( m_nScreen ).m_aSize; + long w = aScreenSize.Width() - maGeometry.nLeftDecoration - maGeometry.nRightDecoration; + long h = aScreenSize.Height() - maGeometry.nTopDecoration - maGeometry.nBottomDecoration; + + rPosSize = Rectangle( Point( maGeometry.nX, maGeometry.nY ), Size( w, h ) ); + } + else + rPosSize = Rectangle( Point( maGeometry.nX, maGeometry.nY ), + Size( maGeometry.nWidth, maGeometry.nHeight ) ); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalFrame::SetSize( const Size &rSize ) +{ + if( rSize.Width() > 0 && rSize.Height() > 0 ) + { + if( ! ( nStyle_ & SAL_FRAME_STYLE_SIZEABLE ) + && ! IsChildWindow() + && ( nStyle_ & (SAL_FRAME_STYLE_FLOAT|SAL_FRAME_STYLE_OWNERDRAWDECORATION) ) != SAL_FRAME_STYLE_FLOAT ) + { + XSizeHints* pHints = XAllocSizeHints(); + long nSupplied = 0; + XGetWMNormalHints( GetXDisplay(), + GetShellWindow(), + pHints, + &nSupplied + ); + pHints->min_width = rSize.Width(); + pHints->min_height = rSize.Height(); + pHints->max_width = rSize.Width(); + pHints->max_height = rSize.Height(); + pHints->flags |= PMinSize | PMaxSize; + XSetWMNormalHints( GetXDisplay(), + GetShellWindow(), + pHints ); + XFree( pHints ); + } + XResizeWindow( GetXDisplay(), IsSysChildWindow() ? GetWindow() : GetShellWindow(), rSize.Width(), rSize.Height() ); + if( GetWindow() != GetShellWindow() ) + { + if( (nStyle_ & SAL_FRAME_STYLE_PLUG ) ) + XMoveResizeWindow( GetXDisplay(), GetWindow(), 0, 0, rSize.Width(), rSize.Height() ); + else + XResizeWindow( GetXDisplay(), GetWindow(), rSize.Width(), rSize.Height() ); + } + + maGeometry.nWidth = rSize.Width(); + maGeometry.nHeight = rSize.Height(); + + // allow the external status window to reposition + if (mbInputFocus && mpInputContext != NULL) + mpInputContext->SetICFocus ( this ); + } +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void X11SalFrame::SetPosSize( const Rectangle &rPosSize ) +{ + XWindowChanges values; + values.x = rPosSize.Left(); + values.y = rPosSize.Top(); + values.width = rPosSize.GetWidth(); + values.height = rPosSize.GetHeight(); + + if( !values.width || !values.height ) + return; + + if( mpParent && ! IsSysChildWindow() ) + { + // --- RTL --- (mirror window pos) + if( Application::GetSettings().GetLayoutRTL() ) + values.x = mpParent->maGeometry.nWidth-values.width-1-values.x; + + XLIB_Window aChild; + // coordinates are relative to parent, so translate to root coordinates + XTranslateCoordinates( GetDisplay()->GetDisplay(), + mpParent->GetWindow(), + GetDisplay()->GetRootWindow( m_nScreen ), + values.x, values.y, + &values.x, &values.y, + & aChild ); + } + + bool bMoved = false; + bool bSized = false; + if( values.x != maGeometry.nX || values.y != maGeometry.nY ) + bMoved = true; + if( values.width != (int)maGeometry.nWidth || values.height != (int)maGeometry.nHeight ) + bSized = true; + + if( ! ( nStyle_ & ( SAL_FRAME_STYLE_PLUG | SAL_FRAME_STYLE_FLOAT ) ) + && !(pDisplay_->GetProperties() & PROPERTY_SUPPORT_WM_ClientPos) ) + { + values.x -= maGeometry.nLeftDecoration; + values.y -= maGeometry.nTopDecoration; + } + + // do net set WMNormalHints for .. + if( + // child windows + ! IsChildWindow() + // popups (menu, help window, etc.) + && (nStyle_ & (SAL_FRAME_STYLE_FLOAT|SAL_FRAME_STYLE_OWNERDRAWDECORATION) ) != SAL_FRAME_STYLE_FLOAT + // shown, sizeable windows + && ( nShowState_ == SHOWSTATE_UNKNOWN || + nShowState_ == SHOWSTATE_HIDDEN || + ! ( nStyle_ & SAL_FRAME_STYLE_SIZEABLE ) + ) + ) + { + XSizeHints* pHints = XAllocSizeHints(); + long nSupplied = 0; + XGetWMNormalHints( GetXDisplay(), + GetShellWindow(), + pHints, + &nSupplied + ); + if( ! ( nStyle_ & SAL_FRAME_STYLE_SIZEABLE ) ) + { + pHints->min_width = rPosSize.GetWidth(); + pHints->min_height = rPosSize.GetHeight(); + pHints->max_width = rPosSize.GetWidth(); + pHints->max_height = rPosSize.GetHeight(); + pHints->flags |= PMinSize | PMaxSize; + } + if( nShowState_ == SHOWSTATE_UNKNOWN || nShowState_ == SHOWSTATE_HIDDEN ) + { + pHints->flags |= PPosition | PWinGravity; + pHints->x = values.x; + pHints->y = values.y; + pHints->win_gravity = pDisplay_->getWMAdaptor()->getPositionWinGravity(); + } + if( mbFullScreen ) + { + pHints->max_width = 10000; + pHints->max_height = 10000; + pHints->flags |= PMaxSize; + } + XSetWMNormalHints( GetXDisplay(), + GetShellWindow(), + pHints ); + XFree( pHints ); + } + + XMoveResizeWindow( GetXDisplay(), IsSysChildWindow() ? GetWindow() : GetShellWindow(), values.x, values.y, values.width, values.height ); + if( GetShellWindow() != GetWindow() ) + { + if( (nStyle_ & SAL_FRAME_STYLE_PLUG ) ) + XMoveResizeWindow( GetXDisplay(), GetWindow(), 0, 0, values.width, values.height ); + else + XMoveResizeWindow( GetXDisplay(), GetWindow(), values.x, values.y, values.width, values.height ); + } + + maGeometry.nX = values.x; + maGeometry.nY = values.y; + maGeometry.nWidth = values.width; + maGeometry.nHeight = values.height; + if( IsSysChildWindow() && mpParent ) + { + // translate back to root coordinates + maGeometry.nX += mpParent->maGeometry.nX; + maGeometry.nY += mpParent->maGeometry.nY; + } + + updateScreenNumber(); + if( bSized && ! bMoved ) + CallCallback( SALEVENT_RESIZE, NULL ); + else if( bMoved && ! bSized ) + CallCallback( SALEVENT_MOVE, NULL ); + else + CallCallback( SALEVENT_MOVERESIZE, NULL ); + + // allow the external status window to reposition + if (mbInputFocus && mpInputContext != NULL) + mpInputContext->SetICFocus ( this ); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalFrame::Minimize() +{ + if( IsSysChildWindow() ) + return; + + if( SHOWSTATE_UNKNOWN == nShowState_ || SHOWSTATE_HIDDEN == nShowState_ ) + { + stderr0( "X11SalFrame::Minimize on withdrawn window\n" ); + return; + } + + if( XIconifyWindow( GetXDisplay(), + GetShellWindow(), + pDisplay_->GetDefaultScreenNumber() ) ) + nShowState_ = SHOWSTATE_MINIMIZED; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalFrame::Maximize() +{ + if( IsSysChildWindow() ) + return; + + if( SHOWSTATE_MINIMIZED == nShowState_ ) + { + GetDisplay()->getWMAdaptor()->frameIsMapping( this ); + XMapWindow( GetXDisplay(), GetShellWindow() ); + nShowState_ = SHOWSTATE_NORMAL; + } + + pDisplay_->getWMAdaptor()->maximizeFrame( this, true, true ); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalFrame::Restore() +{ + if( IsSysChildWindow() ) + return; + + if( SHOWSTATE_UNKNOWN == nShowState_ || SHOWSTATE_HIDDEN == nShowState_ ) + { + stderr0( "X11SalFrame::Restore on withdrawn window\n" ); + return; + } + + if( SHOWSTATE_MINIMIZED == nShowState_ ) + { + GetDisplay()->getWMAdaptor()->frameIsMapping( this ); + XMapWindow( GetXDisplay(), GetShellWindow() ); + nShowState_ = SHOWSTATE_NORMAL; + } + + pDisplay_->getWMAdaptor()->maximizeFrame( this, false, false ); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void X11SalFrame::SetScreenNumber( unsigned int nNewScreen ) +{ + if( nNewScreen == maGeometry.nScreenNumber ) + return; + + if( GetDisplay()->IsXinerama() && GetDisplay()->GetXineramaScreens().size() > 1 ) + { + if( nNewScreen >= GetDisplay()->GetXineramaScreens().size() ) + return; + + Rectangle aOldScreenRect( GetDisplay()->GetXineramaScreens()[maGeometry.nScreenNumber] ); + Rectangle aNewScreenRect( GetDisplay()->GetXineramaScreens()[nNewScreen] ); + bool bVisible = bMapped_; + if( bVisible ) + Show( sal_False ); + maGeometry.nX = aNewScreenRect.Left() + (maGeometry.nX - aOldScreenRect.Left()); + maGeometry.nY = aNewScreenRect.Top() + (maGeometry.nY - aOldScreenRect.Top()); + createNewWindow( None, m_nScreen ); + if( bVisible ) + Show( sal_True ); + maGeometry.nScreenNumber = nNewScreen; + } + else if( sal_Int32(nNewScreen) < GetDisplay()->GetScreenCount() ) + { + bool bVisible = bMapped_; + if( bVisible ) + Show( sal_False ); + createNewWindow( None, nNewScreen ); + if( bVisible ) + Show( sal_True ); + maGeometry.nScreenNumber = nNewScreen; + } +} + +void X11SalFrame::SetApplicationID( const rtl::OUString &rWMClass ) +{ + if( rWMClass != m_sWMClass && ! IsChildWindow() ) + { + m_sWMClass = rWMClass; + updateWMClass(); + std::list< X11SalFrame* >::const_iterator it; + for( it = maChildren.begin(); it != maChildren.end(); ++it ) + (*it)->SetApplicationID(rWMClass); + } +} + +void X11SalFrame::updateWMClass() +{ + XClassHint* pClass = XAllocClassHint(); + rtl::OString aResName = X11SalData::getFrameResName( mnExtStyle ); + pClass->res_name = const_cast<char*>(aResName.getStr()); + + rtl::OString aResClass = rtl::OUStringToOString(m_sWMClass, RTL_TEXTENCODING_ASCII_US); + const char *pResClass = aResClass.getLength() ? aResClass.getStr() : X11SalData::getFrameClassName(); + + pClass->res_class = const_cast<char*>(pResClass); + XSetClassHint( GetXDisplay(), GetShellWindow(), pClass ); + XFree( pClass ); +} + + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void X11SalFrame::ShowFullScreen( sal_Bool bFullScreen, sal_Int32 nScreen ) +{ + if( GetDisplay()->IsXinerama() && GetDisplay()->GetXineramaScreens().size() > 1 ) + { + if( mbFullScreen == (bool)bFullScreen ) + return; + if( bFullScreen ) + { + maRestorePosSize = Rectangle( Point( maGeometry.nX, maGeometry.nY ), + Size( maGeometry.nWidth, maGeometry.nHeight ) ); + Rectangle aRect; + if( nScreen < 0 || nScreen >= static_cast<int>(GetDisplay()->GetXineramaScreens().size()) ) + aRect = Rectangle( Point(0,0), GetDisplay()->GetScreenSize( m_nScreen ) ); + else + aRect = GetDisplay()->GetXineramaScreens()[nScreen]; + nStyle_ |= SAL_FRAME_STYLE_PARTIAL_FULLSCREEN; + bool bVisible = bMapped_; + if( bVisible ) + Show( sal_False ); + maGeometry.nX = aRect.Left(); + maGeometry.nY = aRect.Top(); + maGeometry.nWidth = aRect.GetWidth(); + maGeometry.nHeight = aRect.GetHeight(); + mbMaximizedHorz = mbMaximizedVert = false; + mbFullScreen = true; + createNewWindow( None, m_nScreen ); + if( GetDisplay()->getWMAdaptor()->isLegacyPartialFullscreen() ) + GetDisplay()->getWMAdaptor()->enableAlwaysOnTop( this, true ); + else + GetDisplay()->getWMAdaptor()->showFullScreen( this, true ); + if( bVisible ) + Show(sal_True); + + } + else + { + mbFullScreen = false; + nStyle_ &= ~SAL_FRAME_STYLE_PARTIAL_FULLSCREEN; + bool bVisible = bMapped_; + Rectangle aRect = maRestorePosSize; + maRestorePosSize = Rectangle(); + if( bVisible ) + Show( sal_False ); + createNewWindow( None, m_nScreen ); + if( !aRect.IsEmpty() ) + SetPosSize( aRect.Left(), aRect.Top(), aRect.GetWidth(), aRect.GetHeight(), + SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y | + SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT ); + if( bVisible ) + Show( sal_True ); + } + } + else + { + if( nScreen < 0 || nScreen >= GetDisplay()->GetScreenCount() ) + nScreen = m_nScreen; + if( nScreen != m_nScreen ) + { + bool bVisible = bMapped_; + if( mbFullScreen ) + pDisplay_->getWMAdaptor()->showFullScreen( this, false ); + if( bVisible ) + Show( sal_False ); + createNewWindow( None, nScreen ); + if( mbFullScreen ) + pDisplay_->getWMAdaptor()->showFullScreen( this, true ); + if( bVisible ) + Show( sal_True ); + } + if( mbFullScreen == (bool)bFullScreen ) + return; + + pDisplay_->getWMAdaptor()->showFullScreen( this, bFullScreen ); + if( IsOverrideRedirect() + && WMSupportsFWS( GetXDisplay(), GetDisplay()->GetRootWindow( m_nScreen ) ) ) + { + AddFwsProtocols( GetXDisplay(), GetShellWindow() ); + RegisterFwsWindow( GetXDisplay(), GetShellWindow() ); + } + } +} + +/* --------------------------------------------------------------------- + the xautolock pseudo screen saver needs special treatment since it + doesn't cooperate with XxxxScreenSaver settings + ------------------------------------------------------------------- */ + +static Bool +IsRunningXAutoLock( Display *p_display, XLIB_Window a_window ) +{ + const char *p_atomname = "XAUTOLOCK_SEMAPHORE_PID"; + Atom a_pidatom; + + // xautolock interns this atom + a_pidatom = XInternAtom( p_display, p_atomname, True ); + if ( a_pidatom == None ) + return False; + + Atom a_type; + int n_format; + unsigned long n_items; + unsigned long n_bytes_after; + pid_t *p_pid; + pid_t n_pid; + // get pid of running xautolock + XGetWindowProperty (p_display, a_window, a_pidatom, 0L, 2L, False, + AnyPropertyType, &a_type, &n_format, &n_items, &n_bytes_after, + (unsigned char**) &p_pid ); + n_pid = *p_pid; + XFree( p_pid ); + + if ( a_type == XA_INTEGER ) + { + // check if xautolock pid points to a running process + if ( kill(n_pid, 0) == -1 ) + return False; + else + return True; + } + + return False; +} + +/* definitions from xautolock.c (pl15) */ +#define XAUTOLOCK_DISABLE 1 +#define XAUTOLOCK_ENABLE 2 + +static Bool +MessageToXAutoLock( Display *p_display, int n_message ) +{ + const char *p_atomname = "XAUTOLOCK_MESSAGE" ; + Atom a_messageatom; + XLIB_Window a_rootwindow; + + a_rootwindow = RootWindowOfScreen( ScreenOfDisplay(p_display, 0) ); + if ( ! IsRunningXAutoLock(p_display, a_rootwindow) ) + { + // remove any pending messages + a_messageatom = XInternAtom( p_display, p_atomname, True ); + if ( a_messageatom != None ) + XDeleteProperty( p_display, a_rootwindow, a_messageatom ); + return False; + } + + a_messageatom = XInternAtom( p_display, p_atomname, False ); + XChangeProperty (p_display, a_rootwindow, a_messageatom, XA_INTEGER, + 8, PropModeReplace, (unsigned char*)&n_message, sizeof(n_message) ); + + return True; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalFrame::StartPresentation( sal_Bool bStart ) +{ + I18NStatus::get().show( !bStart, I18NStatus::presentation ); + if ( bStart ) + MessageToXAutoLock( GetXDisplay(), XAUTOLOCK_DISABLE ); + else + MessageToXAutoLock( GetXDisplay(), XAUTOLOCK_ENABLE ); + + if( ! bStart && hPresentationWindow != None ) + doReparentPresentationDialogues( GetDisplay() ); + hPresentationWindow = (bStart && IsOverrideRedirect() ) ? GetWindow() : None; + + + // needs static here to save DPMS settings + int dummy; + static bool DPMSExtensionAvailable = +#if !defined(SOLARIS) && !defined(AIX) + (DPMSQueryExtension(GetXDisplay(), &dummy, &dummy) != 0); + static sal_Bool DPMSEnabled = false; +#else + false; + bool DPMSEnabled = false; + (void)dummy; +#define CARD16 unsigned short +#endif + static CARD16 dpms_standby_timeout=0; + static CARD16 dpms_suspend_timeout=0; + static CARD16 dpms_off_timeout=0; + + + if( bStart || nScreenSaversTimeout_ || DPMSEnabled) + { + if( hPresentationWindow ) + { + /* #i10559# workaround for WindowMaker: try to restore + * current focus after presentation window is gone + */ + int revert_to = 0; + XGetInputFocus( GetXDisplay(), &hPresFocusWindow, &revert_to ); + } + int timeout, interval, prefer_blanking, allow_exposures; + XGetScreenSaver( GetXDisplay(), + &timeout, + &interval, + &prefer_blanking, + &allow_exposures ); + + + // get the DPMS state right before the start + if (DPMSExtensionAvailable) + { +#if !defined(SOLARIS) && !defined(AIX) + CARD16 state; // card16 is defined in Xdm.h + DPMSInfo( GetXDisplay(), + &state, + &DPMSEnabled); +#endif + } + if( bStart ) // start show + { + if ( timeout ) + { + nScreenSaversTimeout_ = timeout; + XResetScreenSaver( GetXDisplay() ); + XSetScreenSaver( GetXDisplay(), + 0, + interval, + prefer_blanking, + allow_exposures ); + } +#if !defined(SOLARIS) && !defined(AIX) + if( DPMSEnabled ) + { + if ( DPMSExtensionAvailable ) + { + DPMSGetTimeouts( GetXDisplay(), + &dpms_standby_timeout, + &dpms_suspend_timeout, + &dpms_off_timeout); + DPMSSetTimeouts(GetXDisplay(), 0,0,0); + } + } +#endif + } + else + { + if( nScreenSaversTimeout_ ) + { + XSetScreenSaver( GetXDisplay(), + nScreenSaversTimeout_, + interval, + prefer_blanking, + allow_exposures ); + nScreenSaversTimeout_ = 0; + } +#if !defined(SOLARIS) && !defined(AIX) + if ( DPMSEnabled ) + { + if ( DPMSExtensionAvailable ) + { + // restore timeouts + DPMSSetTimeouts(GetXDisplay(), dpms_standby_timeout, + dpms_suspend_timeout, dpms_off_timeout); + } + } +#endif + } + } +} + +// Pointer +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalFrame::SetPointer( PointerStyle ePointerStyle ) +{ + hCursor_ = pDisplay_->GetPointer( ePointerStyle ); + XDefineCursor( GetXDisplay(), GetWindow(), hCursor_ ); + + if( IsCaptured() || nVisibleFloats > 0 ) + XChangeActivePointerGrab( GetXDisplay(), + PointerMotionMask|ButtonPressMask|ButtonReleaseMask, + hCursor_, + CurrentTime ); +} + +void X11SalFrame::SetPointerPos(long nX, long nY) +{ + /* when the application tries to center the mouse in the dialog the + * window isn't mapped already. So use coordinates relative to the root window. + */ + unsigned int nWindowLeft = maGeometry.nX + nX; + unsigned int nWindowTop = maGeometry.nY + nY; + + XWarpPointer( GetXDisplay(), None, pDisplay_->GetRootWindow( pDisplay_->GetDefaultScreenNumber() ), + 0, 0, 0, 0, nWindowLeft, nWindowTop); +} + +// delay handling of extended text input +#if !defined(__synchronous_extinput__) +void +X11SalFrame::PostExtTextEvent (sal_uInt16 nExtTextEventType, void *pExtTextEvent) +{ + XLIB_Window nFocusWindow = GetWindow(); + Atom nEventAtom = GetDisplay()->getWMAdaptor()->getAtom( WMAdaptor::SAL_EXTTEXTEVENT ); + + XEvent aEvent; + aEvent.xclient.type = ClientMessage; + aEvent.xclient.serial = 0; + aEvent.xclient.send_event = True; + aEvent.xclient.display = GetXDisplay(); + aEvent.xclient.window = nFocusWindow; + aEvent.xclient.message_type = nEventAtom; + aEvent.xclient.format = 32; + +#if SAL_TYPES_SIZEOFLONG > 4 + aEvent.xclient.data.l[0] = (sal_uInt32)((long)pExtTextEvent & 0xffffffff); + aEvent.xclient.data.l[1] = (sal_uInt32)((long)pExtTextEvent >> 32); +#else + aEvent.xclient.data.l[0] = (sal_uInt32)((long)pExtTextEvent); + aEvent.xclient.data.l[1] = 0; +#endif + aEvent.xclient.data.l[2] = (sal_uInt32)nExtTextEventType; + aEvent.xclient.data.l[3] = 0; + aEvent.xclient.data.l[4] = 0; + + XPutBackEvent( GetXDisplay(), &aEvent ); +} + +void +X11SalFrame::HandleExtTextEvent (XClientMessageEvent *pEvent) +{ + #if SAL_TYPES_SIZEOFLONG > 4 + void* pExtTextEvent = (void*)( (pEvent->data.l[0] & 0xffffffff) + | (pEvent->data.l[1] << 32) ); + #else + void* pExtTextEvent = (void*)(pEvent->data.l[0]); + #endif + sal_uInt16 nExtTextEventType = sal_uInt16(pEvent->data.l[2]); + + CallCallback(nExtTextEventType, pExtTextEvent); + + switch (nExtTextEventType) + { + case SALEVENT_ENDEXTTEXTINPUT: + break; + + case SALEVENT_EXTTEXTINPUT: + break; + + default: + + fprintf(stderr, "X11SalFrame::HandleExtTextEvent: invalid extended input\n"); + } +} +#endif /* defined(__synchronous_extinput__) */ + +// PostEvent +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +sal_Bool X11SalFrame::PostEvent( void *pData ) +{ + GetDisplay()->SendInternalEvent( this, pData ); + return sal_True; +} + +// Title +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalFrame::SetTitle( const XubString& rTitle ) +{ + if( ! ( IsChildWindow() || (nStyle_ & SAL_FRAME_STYLE_FLOAT ) ) ) + { + m_aTitle = rTitle; + GetDisplay()->getWMAdaptor()->setWMName( this, rTitle ); + } +} + +// ----------------------------------------------------------------------- + +void X11SalFrame::Flush() +{ + XFlush( GetDisplay()->GetDisplay() ); +} + +// ----------------------------------------------------------------------- + +void X11SalFrame::Sync() +{ + XSync( GetDisplay()->GetDisplay(), False ); +} + +// Keyboard +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +// ----------------------------------------------------------------------- + +void X11SalFrame::SetInputContext( SalInputContext* pContext ) +{ + if (pContext == NULL) + return; + + // 1. We should create an input context for this frame + // only when SAL_INPUTCONTEXT_TEXT is set. + + if (!(pContext->mnOptions & SAL_INPUTCONTEXT_TEXT)) + { + if( mpInputContext ) + mpInputContext->Unmap( this ); + return; + } + + // 2. We should use on-the-spot inputstyle + // only when SAL_INPUTCONTEXT_EXTTEXTINPUT is set. + + if (mpInputContext == NULL) + { + I18NStatus& rStatus( I18NStatus::get() ); + rStatus.setParent( this ); + mpInputContext = new SalI18N_InputContext( this ); + if (mpInputContext->UseContext()) + { + mpInputContext->ExtendEventMask( GetShellWindow() ); + if (pContext->mnOptions & SAL_INPUTCONTEXT_CHANGELANGUAGE) + mpInputContext->SetLanguage(pContext->meLanguage); + if (mbInputFocus) + mpInputContext->SetICFocus( this ); + } + } + else + mpInputContext->Map( this ); + return; +} + +// ----------------------------------------------------------------------- + +void X11SalFrame::EndExtTextInput( sal_uInt16 nFlags ) +{ + if (mpInputContext != NULL) + mpInputContext->EndExtTextInput( nFlags ); +} + +// ----------------------------------------------------------------------- + +XubString X11SalFrame::GetKeyName( sal_uInt16 nKeyCode ) +{ + return GetDisplay()->GetKeyName( nKeyCode ); +} + +XubString X11SalFrame::GetSymbolKeyName( const XubString&, sal_uInt16 nKeyCode ) +{ + return GetKeyName( nKeyCode ); +} + +sal_Bool X11SalFrame::MapUnicodeToKeyCode( sal_Unicode , LanguageType , KeyCode& ) +{ + // not supported yet + return sal_False; +} + +LanguageType X11SalFrame::GetInputLanguage() +{ + // could be improved by checking unicode ranges of the last input + return LANGUAGE_DONTKNOW; +} + +// Settings +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +inline Color getColorFromLong( long nColor ) +{ + return Color( (nColor & 0xff), (nColor & 0xff00)>>8, (nColor & 0xff0000)>>16); +} + +void X11SalFrame::UpdateSettings( AllSettings& rSettings ) +{ + + DtIntegrator* pIntegrator = GetDisplay()->getDtIntegrator(); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "DtIntegrator: %d\n", pIntegrator ? pIntegrator->GetDtType() : -1 ); +#endif + if( pIntegrator ) + pIntegrator->GetSystemLook( rSettings ); +} + +void X11SalFrame::CaptureMouse( sal_Bool bCapture ) +{ + nCaptured_ = pDisplay_->CaptureMouse( bCapture ? this : NULL ); +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +void X11SalFrame::SetParent( SalFrame* pNewParent ) +{ + if( mpParent != pNewParent ) + { + if( mpParent ) + mpParent->maChildren.remove( this ); + + mpParent = static_cast<X11SalFrame*>(pNewParent); + mpParent->maChildren.push_back( this ); + if( mpParent->m_nScreen != m_nScreen ) + createNewWindow( None, mpParent->m_nScreen ); + GetDisplay()->getWMAdaptor()->changeReferenceFrame( this, mpParent ); + } +} + +SalFrame* X11SalFrame::GetParent() const +{ + return mpParent; +} + +void X11SalFrame::createNewWindow( XLIB_Window aNewParent, int nScreen ) +{ + bool bWasVisible = bMapped_; + if( bWasVisible ) + Show( sal_False ); + + if( nScreen < 0 || nScreen >= GetDisplay()->GetScreenCount() ) + nScreen = m_nScreen; + + SystemParentData aParentData; + aParentData.aWindow = aNewParent; + aParentData.bXEmbedSupport = (aNewParent != None && m_bXEmbed); // caution: this is guesswork + if( aNewParent == None ) + { + aNewParent = GetDisplay()->GetRootWindow(nScreen); + aParentData.aWindow = None; + m_bXEmbed = false; + } + else + { + // is new parent a root window ? + Display* pDisp = GetDisplay()->GetDisplay(); + int nScreens = GetDisplay()->GetScreenCount(); + for( int i = 0; i < nScreens; i++ ) + { + if( aNewParent == RootWindow( pDisp, i ) ) + { + nScreen = i; + aParentData.aWindow = None; + m_bXEmbed = false; + break; + } + } + } + + // first deinit frame + updateGraphics(true); + if( mpInputContext ) + { + mpInputContext->UnsetICFocus( this ); + mpInputContext->Unmap( this ); + } + if( GetWindow() == hPresentationWindow ) + { + hPresentationWindow = None; + doReparentPresentationDialogues( GetDisplay() ); + } + XDestroyWindow( GetXDisplay(), mhWindow ); + mhWindow = None; + + passOnSaveYourSelf(); + + // now init with new parent again + if ( aParentData.aWindow != None ) + Init( nStyle_ | SAL_FRAME_STYLE_PLUG, nScreen, &aParentData ); + else + Init( nStyle_ & ~SAL_FRAME_STYLE_PLUG, nScreen, NULL, true ); + + // update graphics if necessary + updateGraphics(false); + + if( m_aTitle.Len() ) + SetTitle( m_aTitle ); + + if( mpParent ) + { + if( mpParent->m_nScreen != m_nScreen ) + SetParent( NULL ); + else + pDisplay_->getWMAdaptor()->changeReferenceFrame( this, mpParent ); + } + + if( bWasVisible ) + Show( sal_True ); + + std::list< X11SalFrame* > aChildren = maChildren; + for( std::list< X11SalFrame* >::iterator it = aChildren.begin(); it != aChildren.end(); ++it ) + (*it)->createNewWindow( None, m_nScreen ); + + // FIXME: SalObjects +} + +bool X11SalFrame::SetPluginParent( SystemParentData* pNewParent ) +{ + if( pNewParent->nSize >= sizeof(SystemParentData) ) + m_bXEmbed = pNewParent->aWindow != None && pNewParent->bXEmbedSupport; + createNewWindow( pNewParent ? pNewParent->aWindow : None ); + + return true; +} + +// Sound +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +void X11SalFrame::Beep( SoundType eSoundType ) // not fully suported +{ + switch( eSoundType ) + { + case SOUND_DEFAULT: + case SOUND_ERROR: + GetDisplay()->Beep(); + break; + default: + // Excessive beeping averted + break; + } +} + +// Event Handling +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +static sal_uInt16 sal_GetCode( int state ) +{ + sal_uInt16 nCode = 0; + + if( state & Button1Mask ) + nCode |= MOUSE_LEFT; + if( state & Button2Mask ) + nCode |= MOUSE_MIDDLE; + if( state & Button3Mask ) + nCode |= MOUSE_RIGHT; + + if( state & ShiftMask ) + nCode |= KEY_SHIFT; + if( state & ControlMask ) + nCode |= KEY_MOD1; + if( state & Mod1Mask ) + nCode |= KEY_MOD2; + + // Map Meta/Super modifier to MOD3 on all Unix systems + // except Mac OS X + if( (state & Mod3Mask) ) + nCode |= KEY_MOD3; + + return nCode; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +SalFrame::SalPointerState X11SalFrame::GetPointerState() +{ + SalPointerState aState; + XLIB_Window aRoot, aChild; + int rx, ry, wx, wy; + unsigned int nMask = 0; + XQueryPointer( GetXDisplay(), + GetShellWindow(), + &aRoot, + &aChild, + &rx, &ry, + &wx, &wy, + &nMask + ); + + aState.maPos = Point(wx, wy); + aState.mnState = sal_GetCode( nMask ); + return aState; +} + +SalFrame::SalIndicatorState X11SalFrame::GetIndicatorState() +{ + SalIndicatorState aState; + aState.mnState = GetX11SalData()->GetDisplay()->GetIndicatorState(); + return aState; +} + +void X11SalFrame::SimulateKeyPress( sal_uInt16 nKeyCode ) +{ + GetX11SalData()->GetDisplay()->SimulateKeyPress(nKeyCode); +} + +long X11SalFrame::HandleMouseEvent( XEvent *pEvent ) +{ + SalMouseEvent aMouseEvt; + sal_uInt16 nEvent = 0; + bool bClosePopups = false; + + if( nVisibleFloats && pEvent->type == EnterNotify ) + return 0; + + // Solaris X86: clicking the right button on a two-button mouse + // generates a button2 event not a button3 event + if (pDisplay_->GetProperties() & PROPERTY_SUPPORT_3ButtonMouse ) + { + switch (pEvent->type) + { + case EnterNotify: + case LeaveNotify: + if ( pEvent->xcrossing.state & Button2Mask ) + { + pEvent->xcrossing.state &= ~Button2Mask; + pEvent->xcrossing.state |= Button3Mask; + } + break; + + case MotionNotify: + if ( pEvent->xmotion.state & Button2Mask ) + { + pEvent->xmotion.state &= ~Button2Mask; + pEvent->xmotion.state |= Button3Mask; + } + break; + + default: + if ( Button2 == pEvent->xbutton.button ) + { + pEvent->xbutton.state &= ~Button2Mask; + pEvent->xbutton.state |= Button3Mask; + pEvent->xbutton.button = Button3; + } + break; + } + } + + + if( LeaveNotify == pEvent->type || EnterNotify == pEvent->type ) + { + /* + * some WMs (and/or) applications have a passive grab on + * mouse buttons (XGrabButton). This leads to enter/leave notifies + * with mouse buttons pressed in the state mask before the actual + * ButtonPress event gets dispatched. But EnterNotify + * is reported in vcl as MouseMove event. Some office code + * decides that a pressed button in a MouseMove belongs to + * a drag operation which leads to doing things differently. + * + * ignore Enter/LeaveNotify resulting from grabs so that + * help windows do not disappear just after appearing + * + * hopefully this workaround will not break anything. + */ + if( pEvent->xcrossing.mode == NotifyGrab || pEvent->xcrossing.mode == NotifyUngrab ) + return 0; + + aMouseEvt.mnX = pEvent->xcrossing.x; + aMouseEvt.mnY = pEvent->xcrossing.y; + aMouseEvt.mnTime = pEvent->xcrossing.time; + aMouseEvt.mnCode = sal_GetCode( pEvent->xcrossing.state ); + aMouseEvt.mnButton = 0; + + nEvent = LeaveNotify == pEvent->type + ? SALEVENT_MOUSELEAVE + : SALEVENT_MOUSEMOVE; + } + else if( pEvent->type == MotionNotify ) + { + aMouseEvt.mnX = pEvent->xmotion.x; + aMouseEvt.mnY = pEvent->xmotion.y; + aMouseEvt.mnTime = pEvent->xmotion.time; + aMouseEvt.mnCode = sal_GetCode( pEvent->xmotion.state ); + + aMouseEvt.mnButton = 0; + + nEvent = SALEVENT_MOUSEMOVE; + if( nVisibleFloats > 0 && mpParent ) + { + XLIB_Cursor aCursor = mpParent->GetCursor(); + if( pEvent->xmotion.x >= 0 && pEvent->xmotion.x < (int)maGeometry.nWidth && + pEvent->xmotion.y >= 0 && pEvent->xmotion.y < (int)maGeometry.nHeight ) + aCursor = None; + + XChangeActivePointerGrab( GetXDisplay(), + PointerMotionMask|ButtonPressMask|ButtonReleaseMask, + aCursor, + CurrentTime ); + } + } + else + { + // let mouse events reach the correct window + if( nVisibleFloats < 1 ) + { + if( ! (nStyle_ & SAL_FRAME_STYLE_OWNERDRAWDECORATION) ) + XUngrabPointer( GetXDisplay(), CurrentTime ); + } + else if( pEvent->type == ButtonPress ) + { + // see if the user clicks outside all of the floats + // if yes release the grab + bool bInside = false; + const std::list< SalFrame* >& rFrames = GetDisplay()->getFrames(); + for( std::list< SalFrame* >::const_iterator it = rFrames.begin(); it != rFrames.end(); ++it ) + { + const X11SalFrame* pFrame = static_cast< const X11SalFrame* >(*it); + if( pFrame->IsFloatGrabWindow() && + pFrame->bMapped_ && + pEvent->xbutton.x_root >= pFrame->maGeometry.nX && + pEvent->xbutton.x_root < pFrame->maGeometry.nX + (int)pFrame->maGeometry.nWidth && + pEvent->xbutton.y_root >= pFrame->maGeometry.nY && + pEvent->xbutton.y_root < pFrame->maGeometry.nY + (int)pFrame->maGeometry.nHeight ) + { + bInside = true; + break; + } + } + if( ! bInside ) + { + // need not take care of the XUngrabPointer in Show( sal_False ) + // because XUngrabPointer does not produce errors if pointer + // is not grabbed + XUngrabPointer( GetXDisplay(), CurrentTime ); + bClosePopups = true; + + /* #i15246# only close popups if pointer is outside all our frames + * cannot use our own geometry data here because stacking + * is unknown (the above case implicitly assumes + * that floats are on top which should be true) + */ + XLIB_Window aRoot, aChild; + int root_x, root_y, win_x, win_y; + unsigned int mask_return; + if( XQueryPointer( GetXDisplay(), + GetDisplay()->GetRootWindow( m_nScreen ), + &aRoot, &aChild, + &root_x, &root_y, + &win_x, &win_y, + &mask_return ) + && aChild // pointer may not be in any child + ) + { + for( std::list< SalFrame* >::const_iterator it = rFrames.begin(); it != rFrames.end(); ++it ) + { + const X11SalFrame* pFrame = static_cast< const X11SalFrame* >(*it); + if( ! pFrame->IsFloatGrabWindow() + && ( pFrame->GetWindow() == aChild || + pFrame->GetShellWindow() == aChild || + pFrame->GetStackingWindow() == aChild ) + ) + { + // #i63638# check that pointer is inside window, not + // only inside stacking window + if( root_x >= pFrame->maGeometry.nX && root_x < sal::static_int_cast< int >(pFrame->maGeometry.nX+pFrame->maGeometry.nWidth) && + root_y >= pFrame->maGeometry.nY && root_y < sal::static_int_cast< int >(pFrame->maGeometry.nX+pFrame->maGeometry.nHeight) ) + { + bClosePopups = false; + } + break; + } + } + } + } + } + + if( m_bXEmbed && pEvent->xbutton.button == Button1 ) + askForXEmbedFocus( pEvent->xbutton.time ); + + if( pEvent->xbutton.button == Button1 || + pEvent->xbutton.button == Button2 || + pEvent->xbutton.button == Button3 ) + { + aMouseEvt.mnX = pEvent->xbutton.x; + aMouseEvt.mnY = pEvent->xbutton.y; + aMouseEvt.mnTime = pEvent->xbutton.time; + aMouseEvt.mnCode = sal_GetCode( pEvent->xbutton.state ); + + if( Button1 == pEvent->xbutton.button ) + aMouseEvt.mnButton = MOUSE_LEFT; + else if( Button2 == pEvent->xbutton.button ) + aMouseEvt.mnButton = MOUSE_MIDDLE; + else if( Button3 == pEvent->xbutton.button ) + aMouseEvt.mnButton = MOUSE_RIGHT; + + nEvent = ButtonPress == pEvent->type + ? SALEVENT_MOUSEBUTTONDOWN + : SALEVENT_MOUSEBUTTONUP; + } + else if( pEvent->xbutton.button == Button4 || + pEvent->xbutton.button == Button5 || + pEvent->xbutton.button == Button6 || + pEvent->xbutton.button == Button7 ) + { + const bool bIncrement( + pEvent->xbutton.button == Button4 || + pEvent->xbutton.button == Button6 ); + const bool bHoriz( + pEvent->xbutton.button == Button6 || + pEvent->xbutton.button == Button7 ); + + if( pEvent->type == ButtonRelease ) + return 0; + + static sal_uLong nLines = 0; + if( ! nLines ) + { + char* pEnv = getenv( "SAL_WHEELLINES" ); + nLines = pEnv ? atoi( pEnv ) : 3; + if( nLines > 10 ) + nLines = SAL_WHEELMOUSE_EVENT_PAGESCROLL; + } + + SalWheelMouseEvent aWheelEvt; + aWheelEvt.mnTime = pEvent->xbutton.time; + aWheelEvt.mnX = pEvent->xbutton.x; + aWheelEvt.mnY = pEvent->xbutton.y; + aWheelEvt.mnDelta = bIncrement ? 120 : -120; + aWheelEvt.mnNotchDelta = bIncrement ? 1 : -1; + aWheelEvt.mnScrollLines = nLines; + aWheelEvt.mnCode = sal_GetCode( pEvent->xbutton.state ); + aWheelEvt.mbHorz = bHoriz; + + nEvent = SALEVENT_WHEELMOUSE; + + // --- RTL --- (mirror mouse pos) + if( Application::GetSettings().GetLayoutRTL() ) + aWheelEvt.mnX = nWidth_-1-aWheelEvt.mnX; + return CallCallback( nEvent, &aWheelEvt ); + } + } + + int nRet = 0; + if( nEvent == SALEVENT_MOUSELEAVE + || ( aMouseEvt.mnX < nWidth_ && aMouseEvt.mnX > -1 && + aMouseEvt.mnY < nHeight_ && aMouseEvt.mnY > -1 ) + || pDisplay_->MouseCaptured( this ) + ) + { + // --- RTL --- (mirror mouse pos) + if( Application::GetSettings().GetLayoutRTL() ) + aMouseEvt.mnX = nWidth_-1-aMouseEvt.mnX; + nRet = CallCallback( nEvent, &aMouseEvt ); + } + + if( bClosePopups ) + { + /* #108213# close popups after dispatching the event outside the popup; + * applications do weird things. + */ + ImplSVData* pSVData = ImplGetSVData(); + if ( pSVData->maWinData.mpFirstFloat ) + { + static const char* pEnv = getenv( "SAL_FLOATWIN_NOAPPFOCUSCLOSE" ); + if ( !(pSVData->maWinData.mpFirstFloat->GetPopupModeFlags() & FLOATWIN_POPUPMODE_NOAPPFOCUSCLOSE) && !(pEnv && *pEnv) ) + pSVData->maWinData.mpFirstFloat->EndPopupMode( FLOATWIN_POPUPMODEEND_CANCEL | FLOATWIN_POPUPMODEEND_CLOSEALL ); + } + } + + return nRet; +} + +// F10 means either KEY_F10 or KEY_MENU, which has to be decided +// in the independent part. +struct KeyAlternate +{ + sal_uInt16 nKeyCode; + sal_Unicode nCharCode; + KeyAlternate() : nKeyCode( 0 ), nCharCode( 0 ) {} + KeyAlternate( sal_uInt16 nKey, sal_Unicode nChar = 0 ) : nKeyCode( nKey ), nCharCode( nChar ) {} +}; + +inline KeyAlternate +GetAlternateKeyCode( const sal_uInt16 nKeyCode ) +{ + KeyAlternate aAlternate; + + switch( nKeyCode ) + { + case KEY_F10: aAlternate = KeyAlternate( KEY_MENU );break; + case KEY_F24: aAlternate = KeyAlternate( KEY_SUBTRACT, '-' );break; + } + + return aAlternate; +} + +void X11SalFrame::beginUnicodeSequence() +{ + rtl::OUString& rSeq( GetX11SalData()->GetUnicodeAccumulator() ); + DeletionListener aDeleteWatch( this ); + + if( rSeq.getLength() ) + endUnicodeSequence(); + + rSeq = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "u" ) ); + + if( ! aDeleteWatch.isDeleted() ) + { + sal_uInt16 nTextAttr = SAL_EXTTEXTINPUT_ATTR_UNDERLINE; + SalExtTextInputEvent aEv; + aEv.mnTime = 0; + aEv.maText = rSeq; + aEv.mpTextAttr = &nTextAttr; + aEv.mnCursorPos = 0; + aEv.mnDeltaStart = 0; + aEv.mnCursorFlags = 0; + aEv.mbOnlyCursor = sal_False; + + CallCallback(SALEVENT_EXTTEXTINPUT, (void*)&aEv); + } +} + +bool X11SalFrame::appendUnicodeSequence( sal_Unicode c ) +{ + bool bRet = false; + rtl::OUString& rSeq( GetX11SalData()->GetUnicodeAccumulator() ); + if( rSeq.getLength() > 0 ) + { + // range check + if( (c >= sal_Unicode('0') && c <= sal_Unicode('9')) || + (c >= sal_Unicode('a') && c <= sal_Unicode('f')) || + (c >= sal_Unicode('A') && c <= sal_Unicode('F')) ) + { + rtl::OUStringBuffer aBuf( rSeq.getLength() + 1 ); + aBuf.append( rSeq ); + aBuf.append( c ); + rSeq = aBuf.makeStringAndClear(); + std::vector<sal_uInt16> attribs( rSeq.getLength(), SAL_EXTTEXTINPUT_ATTR_UNDERLINE ); + + SalExtTextInputEvent aEv; + aEv.mnTime = 0; + aEv.maText = rSeq; + aEv.mpTextAttr = &attribs[0]; + aEv.mnCursorPos = 0; + aEv.mnDeltaStart = 0; + aEv.mnCursorFlags = 0; + aEv.mbOnlyCursor = sal_False; + + CallCallback(SALEVENT_EXTTEXTINPUT, (void*)&aEv); + bRet = true; + } + else + bRet = endUnicodeSequence(); + } + else + endUnicodeSequence(); + return bRet; +} + +bool X11SalFrame::endUnicodeSequence() +{ + rtl::OUString& rSeq( GetX11SalData()->GetUnicodeAccumulator() ); + + DeletionListener aDeleteWatch( this ); + if( rSeq.getLength() > 1 && rSeq.getLength() < 6 ) + { + // cut the "u" + rtl::OUString aNumbers( rSeq.copy( 1 ) ); + sal_Int32 nValue = aNumbers.toInt32( 16 ); + if( nValue >= 32 ) + { + sal_uInt16 nTextAttr = SAL_EXTTEXTINPUT_ATTR_UNDERLINE; + SalExtTextInputEvent aEv; + aEv.mnTime = 0; + aEv.maText = rtl::OUString( sal_Unicode(nValue) ); + aEv.mpTextAttr = &nTextAttr; + aEv.mnCursorPos = 0; + aEv.mnDeltaStart = 0; + aEv.mnCursorFlags = 0; + aEv.mbOnlyCursor = sal_False; + CallCallback(SALEVENT_EXTTEXTINPUT, (void*)&aEv); + } + } + bool bWasInput = rSeq.getLength() > 0; + rSeq = rtl::OUString(); + if( bWasInput && ! aDeleteWatch.isDeleted() ) + CallCallback(SALEVENT_ENDEXTTEXTINPUT, NULL); + return bWasInput; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +long X11SalFrame::HandleKeyEvent( XKeyEvent *pEvent ) +{ + KeySym nKeySym; + KeySym nUnmodifiedKeySym; + int nLen = 2048; + unsigned char *pPrintable = (unsigned char*)alloca( nLen ); + + // singlebyte code composed by input method, the new default + if (mpInputContext != NULL && mpInputContext->UseContext()) + { + // returns a keysym as well as the pPrintable (in system encoding) + // printable may be empty. + Status nStatus; + nKeySym = pDisplay_->GetKeySym( pEvent, pPrintable, &nLen, + &nUnmodifiedKeySym, + &nStatus, mpInputContext->GetContext() ); + if ( nStatus == XBufferOverflow ) + { + nLen *= 2; + pPrintable = (unsigned char*)alloca( nLen ); + nKeySym = pDisplay_->GetKeySym( pEvent, pPrintable, &nLen, + &nUnmodifiedKeySym, + &nStatus, mpInputContext->GetContext() ); + } + } + else + { + // fallback, this should never ever be called + Status nStatus = 0; + nKeySym = pDisplay_->GetKeySym( pEvent, pPrintable, &nLen, &nUnmodifiedKeySym, &nStatus ); + } + + SalKeyEvent aKeyEvt; + sal_uInt16 nKeyCode; + sal_uInt16 nModCode = 0; + char aDummy; + + if( pEvent->state & ShiftMask ) + nModCode |= KEY_SHIFT; + if( pEvent->state & ControlMask ) + nModCode |= KEY_MOD1; + if( pEvent->state & Mod1Mask ) + nModCode |= KEY_MOD2; + + if( nModCode != (KEY_SHIFT|KEY_MOD1) ) + endUnicodeSequence(); + + if( nKeySym == XK_Shift_L || nKeySym == XK_Shift_R + || nKeySym == XK_Control_L || nKeySym == XK_Control_R + || nKeySym == XK_Alt_L || nKeySym == XK_Alt_R + || nKeySym == XK_Meta_L || nKeySym == XK_Meta_R + || nKeySym == XK_Super_L || nKeySym == XK_Super_R ) + { + SalKeyModEvent aModEvt; + aModEvt.mnModKeyCode = 0; + if( pEvent->type == XLIB_KeyPress && mnExtKeyMod == 0 ) + mbSendExtKeyModChange = true; + else if( pEvent->type == KeyRelease && mbSendExtKeyModChange ) + { + aModEvt.mnModKeyCode = mnExtKeyMod; + mnExtKeyMod = 0; + } + + // pressing just the ctrl key leads to a keysym of XK_Control but + // the event state does not contain ControlMask. In the release + // event its the other way round: it does contain the Control mask. + // The modifier mode therefore has to be adapted manually. + sal_uInt16 nExtModMask = 0; + sal_uInt16 nModMask = 0; + switch( nKeySym ) + { + case XK_Control_L: + nExtModMask = MODKEY_LMOD1; + nModMask = KEY_MOD1; + break; + case XK_Control_R: + nExtModMask = MODKEY_RMOD1; + nModMask = KEY_MOD1; + break; + case XK_Alt_L: + nExtModMask = MODKEY_LMOD2; + nModMask = KEY_MOD2; + break; + case XK_Alt_R: + nExtModMask = MODKEY_RMOD2; + nModMask = KEY_MOD2; + break; + case XK_Shift_L: + nExtModMask = MODKEY_LSHIFT; + nModMask = KEY_SHIFT; + break; + case XK_Shift_R: + nExtModMask = MODKEY_RSHIFT; + nModMask = KEY_SHIFT; + break; + // Map Meta/Super keys to MOD3 modifier on all Unix systems + // except Mac OS X + case XK_Meta_L: + case XK_Super_L: + nExtModMask = MODKEY_LMOD3; + nModMask = KEY_MOD3; + break; + case XK_Meta_R: + case XK_Super_R: + nExtModMask = MODKEY_RMOD3; + nModMask = KEY_MOD3; + break; + } + if( pEvent->type == KeyRelease ) + { + nModCode &= ~nModMask; + mnExtKeyMod &= ~nExtModMask; + } + else + { + nModCode |= nModMask; + mnExtKeyMod |= nExtModMask; + } + + aModEvt.mnCode = nModCode; + aModEvt.mnTime = pEvent->time; + + int nRet = CallCallback( SALEVENT_KEYMODCHANGE, &aModEvt ); + + return nRet; + } + + mbSendExtKeyModChange = false; + + // try to figure out the vcl code for the keysym + // #i52338# use the unmodified KeySym if there is none for the real KeySym + // because the independent part has only keycodes for unshifted keys + nKeyCode = pDisplay_->GetKeyCode( nKeySym, &aDummy ); + if( nKeyCode == 0 ) + nKeyCode = pDisplay_->GetKeyCode( nUnmodifiedKeySym, &aDummy ); + + // try to figure out a printable if XmbLookupString returns only a keysym + // and NOT a printable. Do not store it in pPrintable[0] since it is expected to + // be in system encoding, not unicode. + // #i8988##, if KeySym and printable look equally promising then prefer KeySym + // the printable is bound to the encoding so the KeySym might contain more + // information (in et_EE locale: "Compose + Z + <" delivers "," in printable and + // (the desired) Zcaron in KeySym + sal_Unicode nKeyString = 0x0; + if ( (nLen == 0) + || ((nLen == 1) && (nKeySym > 0)) ) + nKeyString = KeysymToUnicode (nKeySym); + // if we have nothing we give up + if( !nKeyCode && !nLen && !nKeyString) + return 0; + + DeletionListener aDeleteWatch( this ); + + if( nModCode == (KEY_SHIFT | KEY_MOD1) && pEvent->type == XLIB_KeyPress ) + { + sal_uInt16 nSeqKeyCode = pDisplay_->GetKeyCode( nUnmodifiedKeySym, &aDummy ); + if( nSeqKeyCode == KEY_U ) + { + beginUnicodeSequence(); + return 1; + } + else if( nSeqKeyCode >= KEY_0 && nSeqKeyCode <= KEY_9 ) + { + if( appendUnicodeSequence( sal_Unicode( '0' ) + sal_Unicode(nSeqKeyCode - KEY_0) ) ) + return 1; + } + else if( nSeqKeyCode >= KEY_A && nSeqKeyCode <= KEY_F ) + { + if( appendUnicodeSequence( sal_Unicode( 'a' ) + sal_Unicode(nSeqKeyCode - KEY_A) ) ) + return 1; + } + else + endUnicodeSequence(); + } + + if( aDeleteWatch.isDeleted() ) + return 0; + + rtl_TextEncoding nEncoding; + + if (mpInputContext != NULL && mpInputContext->IsMultiLingual() ) + nEncoding = RTL_TEXTENCODING_UTF8; + else + nEncoding = osl_getThreadTextEncoding(); + + sal_Unicode *pBuffer; + sal_Unicode *pString; + sal_Size nBufferSize = nLen * 2; + sal_Size nSize; + pBuffer = (sal_Unicode*) malloc( nBufferSize + 2 ); + pBuffer[ 0 ] = 0; + + if (nKeyString != 0) + { + pString = &nKeyString; + nSize = 1; + } + else + if (nLen > 0 && nEncoding != RTL_TEXTENCODING_UNICODE) + { + // create text converter + rtl_TextToUnicodeConverter aConverter = + rtl_createTextToUnicodeConverter( nEncoding ); + rtl_TextToUnicodeContext aContext = + rtl_createTextToUnicodeContext( aConverter ); + + sal_uInt32 nConversionInfo; + sal_Size nConvertedChars; + + // convert to single byte text stream + nSize = rtl_convertTextToUnicode( + aConverter, aContext, + (char*)pPrintable, nLen, + pBuffer, nBufferSize, + RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_IGNORE | + RTL_TEXTTOUNICODE_FLAGS_INVALID_IGNORE, + &nConversionInfo, &nConvertedChars ); + + // destroy converter + rtl_destroyTextToUnicodeContext( aConverter, aContext ); + rtl_destroyTextToUnicodeConverter( aConverter ); + + pString = pBuffer; + } + else + if (nLen > 0 /* nEncoding == RTL_TEXTENCODING_UNICODE */) + { + pString = (sal_Unicode*)pPrintable; + nSize = nLen; + } + else + { + pString = pBuffer; + nSize = 0; + } + + if ( mpInputContext != NULL + && mpInputContext->UseContext() + && KeyRelease != pEvent->type + && ( (nSize > 1) + || (nSize > 0 && mpInputContext->IsPreeditMode())) ) + { + mpInputContext->CommitKeyEvent(pString, nSize); + } + else + // normal single character keyinput + { + aKeyEvt.mnCode = nKeyCode | nModCode; + aKeyEvt.mnRepeat = 0; + aKeyEvt.mnTime = pEvent->time; + aKeyEvt.mnCharCode = pString[ 0 ]; + + if( KeyRelease == pEvent->type ) + { + CallCallback( SALEVENT_KEYUP, &aKeyEvt ); + } + else + { + if ( ! CallCallback(SALEVENT_KEYINPUT, &aKeyEvt) ) + { + // independent layer doesnt want to handle key-event, so check + // whether the keycode may have an alternate meaning + KeyAlternate aAlternate = GetAlternateKeyCode( nKeyCode ); + if ( aAlternate.nKeyCode != 0 ) + { + aKeyEvt.mnCode = aAlternate.nKeyCode | nModCode; + if( aAlternate.nCharCode ) + aKeyEvt.mnCharCode = aAlternate.nCharCode; + CallCallback(SALEVENT_KEYINPUT, &aKeyEvt); + } + } + } + } + + // + // update the spot location for PreeditPosition IME style + // + if (! aDeleteWatch.isDeleted()) + { + if (mpInputContext != NULL && mpInputContext->UseContext()) + mpInputContext->UpdateSpotLocation(); + } + + free (pBuffer); + return True; +} + + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +long X11SalFrame::HandleFocusEvent( XFocusChangeEvent *pEvent ) +{ + // ReflectionX in Windows mode changes focus while mouse is grabbed + if( nVisibleFloats > 0 && GetDisplay()->getWMAdaptor()->getWindowManagerName().EqualsAscii( "ReflectionX Windows" ) ) + return 1; + + /* ignore focusout resulting from keyboard grabs + * we do not grab it and are not interested when + * someone else does CDE e.g. does a XGrabKey on arrow keys + * handle focus events with mode NotifyWhileGrabbed + * because with CDE alt-tab focus changing we do not get + * normal focus events + * cast focus event to the input context, otherwise the + * status window does not follow the application frame + */ + + if ( mpInputContext != NULL ) + { + if( FocusIn == pEvent->type ) + mpInputContext->SetICFocus( this ); + else + { + /* + * do not unset the IC focuse here because would kill + * a lookup choice windows that might have the focus now + * mpInputContext->UnsetICFocus( this ); + */ + I18NStatus::get().show( false, I18NStatus::focus ); + } + } + + + if ( pEvent->mode == NotifyNormal || pEvent->mode == NotifyWhileGrabbed || + ( ( nStyle_ & SAL_FRAME_STYLE_PLUG ) && pEvent->window == GetShellWindow() ) + ) + { + if( hPresentationWindow != None && hPresentationWindow != GetShellWindow() ) + return 0; + + if( FocusIn == pEvent->type ) + { + GetSalData()->m_pInstance->updatePrinterUpdate(); + mbInputFocus = True; + ImplSVData* pSVData = ImplGetSVData(); + + + + long nRet = CallCallback( SALEVENT_GETFOCUS, 0 ); + if ((mpParent != NULL && nStyle_ == 0) + && pSVData->maWinData.mpFirstFloat ) + { + sal_uLong nMode = pSVData->maWinData.mpFirstFloat->GetPopupModeFlags(); + pSVData->maWinData.mpFirstFloat->SetPopupModeFlags( + nMode & ~(FLOATWIN_POPUPMODE_NOAPPFOCUSCLOSE)); + } + return nRet; + } + else + { + mbInputFocus = False; + mbSendExtKeyModChange = false; + mnExtKeyMod = 0; + return CallCallback( SALEVENT_LOSEFOCUS, 0 ); + } + } + + return 0; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +long X11SalFrame::HandleExposeEvent( XEvent *pEvent ) +{ + XRectangle aRect = { 0, 0, 0, 0 }; + sal_uInt16 nCount = 0; + + if( pEvent->type == Expose ) + { + aRect.x = pEvent->xexpose.x; + aRect.y = pEvent->xexpose.y; + aRect.width = pEvent->xexpose.width; + aRect.height = pEvent->xexpose.height; + nCount = pEvent->xexpose.count; + } + else if( pEvent->type == GraphicsExpose ) + { + aRect.x = pEvent->xgraphicsexpose.x; + aRect.y = pEvent->xgraphicsexpose.y; + aRect.width = pEvent->xgraphicsexpose.width; + aRect.height = pEvent->xgraphicsexpose.height; + nCount = pEvent->xgraphicsexpose.count; + } + + if( IsOverrideRedirect() && mbFullScreen && + aPresentationReparentList.begin() == aPresentationReparentList.end() ) + // we are in fullscreen mode -> override redirect + // focus is possibly lost, so reget it + XSetInputFocus( GetXDisplay(), GetShellWindow(), RevertToNone, CurrentTime ); + + // width and height are extents, so they are of by one for rectangle + maPaintRegion.Union( Rectangle( Point(aRect.x, aRect.y), Size(aRect.width+1, aRect.height+1) ) ); + + if( nCount ) + // wait for last expose rectangle, do not wait for resize timer + // if a completed graphics expose sequence is available + return 1; + + SalPaintEvent aPEvt( maPaintRegion.Left(), maPaintRegion.Top(), maPaintRegion.GetWidth(), maPaintRegion.GetHeight() ); + + CallCallback( SALEVENT_PAINT, &aPEvt ); + maPaintRegion = Rectangle(); + + return 1; +} + +void X11SalFrame::RestackChildren( XLIB_Window* pTopLevelWindows, int nTopLevelWindows ) +{ + if( maChildren.begin() != maChildren.end() ) + { + int nWindow = nTopLevelWindows; + while( nWindow-- ) + if( pTopLevelWindows[nWindow] == GetStackingWindow() ) + break; + if( nWindow < 0 ) + return; + + std::list< X11SalFrame* >::const_iterator it; + for( it = maChildren.begin(); it != maChildren.end(); ++it ) + { + X11SalFrame* pData = *it; + if( pData->bMapped_ ) + { + int nChild = nWindow; + while( nChild-- ) + { + if( pTopLevelWindows[nChild] == pData->GetStackingWindow() ) + { + // if a child is behind its parent, place it above the + // parent (for insane WMs like Dtwm and olwm) + XWindowChanges aCfg; + aCfg.sibling = GetStackingWindow(); + aCfg.stack_mode = Above; + XConfigureWindow( GetXDisplay(), pData->GetStackingWindow(), CWSibling|CWStackMode, &aCfg ); + break; + } + } + } + } + for( it = maChildren.begin(); it != maChildren.end(); ++it ) + { + X11SalFrame* pData = *it; + pData->RestackChildren( pTopLevelWindows, nTopLevelWindows ); + } + } +} + +void X11SalFrame::RestackChildren() +{ + if( ! GetDisplay()->getWMAdaptor()->isTransientBehaviourAsExpected() + && maChildren.begin() != maChildren.end() ) + { + XLIB_Window aRoot, aParent, *pChildren = NULL; + unsigned int nChildren; + if( XQueryTree( GetXDisplay(), + GetDisplay()->GetRootWindow( m_nScreen ), + &aRoot, + &aParent, + &pChildren, + &nChildren ) ) + { + RestackChildren( pChildren, nChildren ); + XFree( pChildren ); + } + } +} + +static Bool size_event_predicate( Display*, XEvent* event, XPointer arg ) +{ + if( event->type != ConfigureNotify ) + return False; + X11SalFrame* frame = reinterpret_cast< X11SalFrame* >( arg ); + XConfigureEvent* pEvent = &event->xconfigure; + if( pEvent->window != frame->GetShellWindow() + && pEvent->window != frame->GetWindow() + && pEvent->window != frame->GetForeignParent() + && pEvent->window != frame->GetStackingWindow()) + { // ignored at top of HandleSizeEvent() + return False; + } + if( pEvent->window == frame->GetStackingWindow()) + return False; // filtered later in HandleSizeEvent() + // at this point we know that there is another similar event in the queue + frame->setPendingSizeEvent(); + return False; // but do not process the new event out of order +} + +void X11SalFrame::setPendingSizeEvent() +{ + mPendingSizeEvent = true; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +long X11SalFrame::HandleSizeEvent( XConfigureEvent *pEvent ) +{ + // NOTE: if you add more tests in this function, make sure to update size_event_predicate() + // so that it finds exactly the same events + + if ( pEvent->window != GetShellWindow() + && pEvent->window != GetWindow() + && pEvent->window != GetForeignParent() + && pEvent->window != GetStackingWindow() + ) + { + // could be as well a sys-child window (aka SalObject) + return 1; + } + + + if( ( nStyle_ & SAL_FRAME_STYLE_PLUG ) && pEvent->window == GetShellWindow() ) + { + // just update the children's positions + RestackChildren(); + return 1; + } + + if( pEvent->window == GetForeignParent() ) + XResizeWindow( GetXDisplay(), + GetWindow(), + pEvent->width, + pEvent->height ); + + XLIB_Window hDummy; + XTranslateCoordinates( GetXDisplay(), + GetWindow(), + pDisplay_->GetRootWindow( pDisplay_->GetDefaultScreenNumber() ), + 0, 0, + &pEvent->x, &pEvent->y, + &hDummy ); + + if( pEvent->window == GetStackingWindow() ) + { + if( maGeometry.nX != pEvent->x || maGeometry.nY != pEvent->y ) + { + maGeometry.nX = pEvent->x; + maGeometry.nY = pEvent->y; + CallCallback( SALEVENT_MOVE, NULL ); + } + return 1; + } + + // check size hints in first time SalFrame::Show + if( SHOWSTATE_UNKNOWN == nShowState_ && bMapped_ ) + nShowState_ = SHOWSTATE_NORMAL; + + // Avoid a race condition where resizing this window to one size and shortly after that + // to another size generates first size event with the old size and only after that + // with the new size, temporarily making us think the old size is valid (bnc#674806). + // So if there is another size event for this window pending, ignore this one. + mPendingSizeEvent = false; + XEvent dummy; + XCheckIfEvent( GetXDisplay(), &dummy, size_event_predicate, reinterpret_cast< XPointer >( this )); + if( mPendingSizeEvent ) + return 1; + + nWidth_ = pEvent->width; + nHeight_ = pEvent->height; + + bool bMoved = ( pEvent->x != maGeometry.nX || pEvent->y != maGeometry.nY ); + bool bSized = ( pEvent->width != (int)maGeometry.nWidth || pEvent->height != (int)maGeometry.nHeight ); + + maGeometry.nX = pEvent->x; + maGeometry.nY = pEvent->y; + maGeometry.nWidth = pEvent->width; + maGeometry.nHeight = pEvent->height; + updateScreenNumber(); + + // update children's position + RestackChildren(); + + if( bSized && ! bMoved ) + CallCallback( SALEVENT_RESIZE, NULL ); + else if( bMoved && ! bSized ) + CallCallback( SALEVENT_MOVE, NULL ); + else if( bMoved && bSized ) + CallCallback( SALEVENT_MOVERESIZE, NULL ); + + return 1; +} + +IMPL_LINK( X11SalFrame, HandleAlwaysOnTopRaise, void*, EMPTYARG ) +{ + if( bMapped_ ) + ToTop( 0 ); + return 0; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +long X11SalFrame::HandleReparentEvent( XReparentEvent *pEvent ) +{ + Display *pDisplay = pEvent->display; + XLIB_Window hWM_Parent; + XLIB_Window hRoot, *Children, hDummy; + unsigned int nChildren; + sal_Bool bNone = pDisplay_->GetProperties() + & PROPERTY_SUPPORT_WM_Parent_Pixmap_None; + sal_Bool bAccessParentWindow = ! (pDisplay_->GetProperties() + & PROPERTY_FEATURE_TrustedSolaris); + + static const char* pDisableStackingCheck = getenv( "SAL_DISABLE_STACKING_CHECK" ); + + GetDisplay()->GetXLib()->PushXErrorLevel( true ); + + /* + * don't rely on the new parent from the event. + * the event may be "out of date", that is the window manager + * window may not exist anymore. This can happen if someone + * shows a frame and hides it again quickly (not that that would + * be very sensible) + */ + hWM_Parent = GetShellWindow(); + do + { + Children = NULL; + XQueryTree( pDisplay, + hWM_Parent, + &hRoot, + &hDummy, + &Children, + &nChildren ); + if( GetDisplay()->GetXLib()->HasXErrorOccurred() ) + { + hWM_Parent = GetShellWindow(); + break; + } + /* this sometimes happens if a Show(sal_True) is + * immediately followed by Show(sal_False) (which is braindead anyway) + */ + if( hDummy == hWM_Parent ) + hDummy = hRoot; + if( hDummy != hRoot ) + { + hWM_Parent = hDummy; + if( bAccessParentWindow && bNone ) + XSetWindowBackgroundPixmap( pDisplay, hWM_Parent, None ); + } + if( Children ) + XFree( Children ); + } while( hDummy != hRoot ); + + if( GetStackingWindow() == None + && hWM_Parent != hPresentationWindow + && hWM_Parent != GetShellWindow() + && ( ! pDisableStackingCheck || ! *pDisableStackingCheck ) + ) + { + mhStackingWindow = hWM_Parent; + if (bAccessParentWindow) + XSelectInput( pDisplay, GetStackingWindow(), StructureNotifyMask ); + } + + if( hWM_Parent == pDisplay_->GetRootWindow( pDisplay_->GetDefaultScreenNumber() ) + || hWM_Parent == GetForeignParent() + || pEvent->parent == pDisplay_->GetRootWindow( pDisplay_->GetDefaultScreenNumber() ) + || ( nStyle_ & SAL_FRAME_STYLE_FLOAT ) ) + { + // Reparenting before Destroy + aPresentationReparentList.remove( GetStackingWindow() ); + mhStackingWindow = None; + GetDisplay()->GetXLib()->PopXErrorLevel(); + return 0; + } + + /* + * evil hack to show decorated windows on top + * of override redirect presentation windows: + * reparent the window manager window to the presentation window + * does not work with non-reparenting WMs + * in future this should not be necessary anymore with + * _NET_WM_STATE_FULLSCREEN available + */ + if( hPresentationWindow != None + && hPresentationWindow != GetWindow() + && GetStackingWindow() != None + && GetStackingWindow() != GetDisplay()->GetRootWindow( m_nScreen ) + ) + { + int x = 0, y = 0; + XLIB_Window aChild; + XTranslateCoordinates( GetXDisplay(), + GetStackingWindow(), + GetDisplay()->GetRootWindow( m_nScreen ), + 0, 0, + &x, &y, + &aChild + ); + XReparentWindow( GetXDisplay(), + GetStackingWindow(), + hPresentationWindow, + x, y + ); + aPresentationReparentList.push_back( GetStackingWindow() ); + } + + int nLeft = 0, nTop = 0; + XTranslateCoordinates( GetXDisplay(), + GetShellWindow(), + hWM_Parent, + 0, 0, + &nLeft, + &nTop, + &hDummy ); + maGeometry.nLeftDecoration = nLeft > 0 ? nLeft-1 : 0; + maGeometry.nTopDecoration = nTop > 0 ? nTop-1 : 0; + + /* + * decorations are not symmetric, + * so need real geometries here + * (this will fail with virtual roots ?) + */ + GetDisplay()->GetXLib()->ResetXErrorOccurred(); + int xp, yp, x, y; + unsigned int wp, w, hp, h, bw, d; + XGetGeometry( GetXDisplay(), + GetShellWindow(), + &hRoot, + &x, &y, &w, &h, &bw, &d ); + XGetGeometry( GetXDisplay(), + hWM_Parent, + &hRoot, + &xp, &yp, &wp, &hp, &bw, &d ); + bool bResized = false; + if( ! GetDisplay()->GetXLib()->HasXErrorOccurred() ) + { + maGeometry.nRightDecoration = wp - w - maGeometry.nLeftDecoration; + maGeometry.nBottomDecoration = hp - h - maGeometry.nTopDecoration; + /* + * note: this works because hWM_Parent is direct child of root, + * not necessarily parent of GetShellWindow() + */ + maGeometry.nX = xp + nLeft; + maGeometry.nY = yp + nTop; + bResized = w != maGeometry.nWidth || h != maGeometry.nHeight; + maGeometry.nWidth = w; + maGeometry.nHeight = h; + } + + + // limit width and height if we are too large: #47757 + // olwm and fvwm need this, it doesnt harm the rest + + // #i81311# do this only for sizable frames + if( (nStyle_ & SAL_FRAME_STYLE_SIZEABLE) != 0 ) + { + Size aScreenSize = GetDisplay()->GetScreenSize( m_nScreen ); + int nScreenWidth = aScreenSize.Width(); + int nScreenHeight = aScreenSize.Height(); + int nFrameWidth = maGeometry.nWidth + maGeometry.nLeftDecoration + maGeometry.nRightDecoration; + int nFrameHeight = maGeometry.nHeight + maGeometry.nTopDecoration + maGeometry.nBottomDecoration; + + if ((nFrameWidth > nScreenWidth) || (nFrameHeight > nScreenHeight)) + { + Size aSize(maGeometry.nWidth, maGeometry.nHeight); + + if (nFrameWidth > nScreenWidth) + aSize.Width() = nScreenWidth - maGeometry.nRightDecoration - maGeometry.nLeftDecoration; + if (nFrameHeight > nScreenHeight) + aSize.Height() = nScreenHeight - maGeometry.nBottomDecoration - maGeometry.nTopDecoration; + + SetSize( aSize ); + bResized = false; + } + } + if( bResized ) + CallCallback( SALEVENT_RESIZE, NULL ); + + GetDisplay()->GetXLib()->PopXErrorLevel(); + + return 1; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +long X11SalFrame::HandleColormapEvent( XColormapEvent* ) +{ + return 0; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +long X11SalFrame::HandleStateEvent( XPropertyEvent *pEvent ) +{ + Atom actual_type; + int actual_format; + unsigned long nitems, bytes_after; + unsigned char *prop = NULL; + + if( 0 != XGetWindowProperty( GetXDisplay(), + GetShellWindow(), + pEvent->atom, // property + 0, // long_offset (32bit) + 2, // long_length (32bit) + False, // delete + pEvent->atom, // req_type + &actual_type, + &actual_format, + &nitems, + &bytes_after, + &prop ) + || ! prop + ) + return 0; + + DBG_ASSERT( actual_type = pEvent->atom + && 32 == actual_format + && 2 == nitems + && 0 == bytes_after, "HandleStateEvent" ); + + if( *(unsigned long*)prop == NormalState ) + nShowState_ = SHOWSTATE_NORMAL; + else if( *(unsigned long*)prop == IconicState ) + nShowState_ = SHOWSTATE_MINIMIZED; + + XFree( prop ); + return 1; +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +long X11SalFrame::HandleClientMessage( XClientMessageEvent *pEvent ) +{ + const WMAdaptor& rWMAdaptor( *pDisplay_->getWMAdaptor() ); + +#if !defined(__synchronous_extinput__) + if( pEvent->message_type == rWMAdaptor.getAtom( WMAdaptor::SAL_EXTTEXTEVENT ) ) + { + HandleExtTextEvent (pEvent); + return 1; + } +#endif + else if( pEvent->message_type == rWMAdaptor.getAtom( WMAdaptor::SAL_QUITEVENT ) ) + { + stderr0( "X11SalFrame::Dispatch Quit\n" ); + Close(); // ??? + return 1; + } + else if( pEvent->message_type == rWMAdaptor.getAtom( WMAdaptor::WM_PROTOCOLS ) ) + { + if( (Atom)pEvent->data.l[0] == rWMAdaptor.getAtom( WMAdaptor::NET_WM_PING ) ) + rWMAdaptor.answerPing( this, pEvent ); + else if( ! ( nStyle_ & SAL_FRAME_STYLE_PLUG ) + && ! (( nStyle_ & SAL_FRAME_STYLE_FLOAT ) && (nStyle_ & SAL_FRAME_STYLE_OWNERDRAWDECORATION)) + ) + { + if( (Atom)pEvent->data.l[0] == rWMAdaptor.getAtom( WMAdaptor::WM_DELETE_WINDOW ) ) + { + Close(); + return 1; + } + else if( (Atom)pEvent->data.l[0] == rWMAdaptor.getAtom( WMAdaptor::WM_TAKE_FOCUS ) ) + { + // do nothing, we set the input focus in ToTop() if necessary + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "got WM_TAKE_FOCUS on %s window\n", + (nStyle_&SAL_FRAME_STYLE_OWNERDRAWDECORATION) ? + "ownerdraw" : "NON OWNERDRAW" ); + #endif + } + else if( (Atom)pEvent->data.l[0] == rWMAdaptor.getAtom( WMAdaptor::WM_SAVE_YOURSELF ) ) + { + bool bSession = rWMAdaptor.getWindowManagerName().EqualsAscii( "Dtwm" ); + + if( ! bSession ) + { + if( this == s_pSaveYourselfFrame ) + { + rtl::OString aExec(rtl::OUStringToOString(SessionManagerClient::getExecName(), osl_getThreadTextEncoding())); + const char* argv[2]; + argv[0] = "/bin/sh"; + argv[1] = const_cast<char*>(aExec.getStr()); + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "SaveYourself request, setting command: %s %s\n", argv[0], argv[1] ); + #endif + XSetCommand( GetXDisplay(), GetShellWindow(), (char**)argv, 2 ); + } + else + // can only happen in race between WM and window closing + XChangeProperty( GetXDisplay(), GetShellWindow(), rWMAdaptor.getAtom( WMAdaptor::WM_COMMAND ), XA_STRING, 8, PropModeReplace, (unsigned char*)"", 0 ); + } + else + { + // save open documents; would be good for non Dtwm, too, + // but there is no real Shutdown message in the ancient + // SM protocol; on Dtwm SaveYourself really means Shutdown, too. + IceSalSession::handleOldX11SaveYourself( this ); + } + } + } + } + else if( pEvent->message_type == rWMAdaptor.getAtom( WMAdaptor::XEMBED ) && + pEvent->window == GetWindow() ) + { + if( pEvent->data.l[1] == 1 || // XEMBED_WINDOW_ACTIVATE + pEvent->data.l[1] == 2 ) // XEMBED_WINDOW_DEACTIVATE + { + XFocusChangeEvent aEvent; + aEvent.type = (pEvent->data.l[1] == 1 ? FocusIn : FocusOut); + aEvent.serial = pEvent->serial; + aEvent.send_event = True; + aEvent.display = pEvent->display; + aEvent.window = pEvent->window; + aEvent.mode = NotifyNormal; + aEvent.detail = NotifyDetailNone; + HandleFocusEvent( &aEvent ); + } + } + return 0; +} + +void X11SalFrame::SaveYourselfDone( SalFrame* pSaveFrame ) +{ + // session save was done, inform dtwm + if( s_pSaveYourselfFrame && pSaveFrame ) + { + rtl::OString aExec(rtl::OUStringToOString(SessionManagerClient::getExecName(), osl_getThreadTextEncoding())); + const char* argv[2]; + argv[0] = "/bin/sh"; + argv[1] = const_cast<char*>(aExec.getStr()); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "SaveYourself request, setting command: %s %s\n", argv[0], argv[1] ); +#endif + XSetCommand( s_pSaveYourselfFrame->GetXDisplay(), + s_pSaveYourselfFrame->GetShellWindow(), + (char**)argv, 2 ); + if( pSaveFrame != s_pSaveYourselfFrame ) + { + // check if it still exists + const X11SalFrame* pFrame = NULL; + const std::list< SalFrame* >& rFrames = static_cast<X11SalFrame*>(pSaveFrame)->GetDisplay()->getFrames(); + std::list< SalFrame* >::const_iterator it = rFrames.begin(); + while( it != rFrames.end() ) + { + pFrame = static_cast< const X11SalFrame* >(*it); + if( pFrame == pSaveFrame ) + break; + ++it; + } + if( pFrame == pSaveFrame ) + { + const WMAdaptor& rWMAdaptor( *pFrame->pDisplay_->getWMAdaptor() ); + XChangeProperty( pFrame->GetXDisplay(), + pFrame->GetShellWindow(), + rWMAdaptor.getAtom( WMAdaptor::WM_COMMAND ), XA_STRING, 8, PropModeReplace, (unsigned char*)"", 0 ); + } + } + s_pSaveYourselfFrame->ShutDown(); + } +} + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +extern "C" +{ +Bool call_checkKeyReleaseForRepeat( Display* pDisplay, XEvent* pCheck, XPointer pX11SalFrame ) +{ + return X11SalFrame::checkKeyReleaseForRepeat( pDisplay, pCheck, pX11SalFrame ); +} +} + +Bool X11SalFrame::checkKeyReleaseForRepeat( Display*, XEvent* pCheck, XPointer pX11SalFrame ) +{ + X11SalFrame* pThis = (X11SalFrame*)pX11SalFrame; + return + pCheck->type == XLIB_KeyPress && + pCheck->xkey.state == pThis->nKeyState_ && + pCheck->xkey.keycode == pThis->nKeyCode_ && + pCheck->xkey.time == pThis->nReleaseTime_ ? True : False; +} + +long X11SalFrame::Dispatch( XEvent *pEvent ) +{ + long nRet = 0; + + if( -1 == nCaptured_ ) + { + CaptureMouse( sal_True ); +#ifdef DBG_UTIL + if( -1 != nCaptured_ ) + pDisplay_->PrintEvent( "Captured", pEvent ); +#endif + } + + if( pEvent->xany.window == GetShellWindow() || pEvent->xany.window == GetWindow() ) + { + switch( pEvent->type ) + { + case XLIB_KeyPress: + nKeyCode_ = pEvent->xkey.keycode; + nKeyState_ = pEvent->xkey.state; + nRet = HandleKeyEvent( &pEvent->xkey ); + break; + + case KeyRelease: + if( -1 == nCompose_ ) + { + nReleaseTime_ = pEvent->xkey.time; + XEvent aEvent; + if( XCheckIfEvent( pEvent->xkey.display, &aEvent, call_checkKeyReleaseForRepeat, (XPointer)this ) ) + XPutBackEvent( pEvent->xkey.display, &aEvent ); + else + nRet = HandleKeyEvent( &pEvent->xkey ); + } + break; + + case ButtonPress: + // if we loose the focus in presentation mode + // there are good chances that we never get it back + // since the WM ignores us + if( IsOverrideRedirect() ) + { + XSetInputFocus( GetXDisplay(), GetShellWindow(), + RevertToNone, CurrentTime ); + } + + case ButtonRelease: + case MotionNotify: + case EnterNotify: + case LeaveNotify: + nRet = HandleMouseEvent( pEvent ); + break; + + case FocusIn: + case FocusOut: + nRet = HandleFocusEvent( &pEvent->xfocus ); + break; + + case Expose: + case GraphicsExpose: + nRet = HandleExposeEvent( pEvent ); + break; + + case MapNotify: + if( pEvent->xmap.window == GetShellWindow() ) + { + if( nShowState_ == SHOWSTATE_HIDDEN ) + { + /* + * workaround for (at least) KWin 2.2.2 + * which will map windows that were once transient + * even if they are withdrawn when the respective + * document is mapped. + */ + if( ! (nStyle_ & SAL_FRAME_STYLE_PLUG) ) + XUnmapWindow( GetXDisplay(), GetShellWindow() ); + break; + } + bMapped_ = sal_True; + bViewable_ = sal_True; + nRet = sal_True; + if ( mpInputContext != NULL ) + mpInputContext->Map( this ); + CallCallback( SALEVENT_RESIZE, NULL ); + if( pDisplay_->GetServerVendor() == vendor_hummingbird ) + { + /* + * With Exceed sometimes there does not seem to be + * an Expose after the MapNotify. + * so start a delayed paint here + */ + maPaintRegion.Union( Rectangle( Point( 0, 0 ), Size( maGeometry.nWidth, maGeometry.nHeight ) ) ); + XEvent aEvent; + aEvent.xexpose.type = Expose; + aEvent.xexpose.display = pDisplay_->GetDisplay(); + aEvent.xexpose.x = 0; + aEvent.xexpose.y = 0; + aEvent.xexpose.width = maGeometry.nWidth; + aEvent.xexpose.height = maGeometry.nHeight; + aEvent.xexpose.count = 0; + XSendEvent( pDisplay_->GetDisplay(), + GetWindow(), + True, + ExposureMask, + &aEvent ); + } + + bool bSetFocus = m_bSetFocusOnMap; + /* another workaround for sawfish: if a transient window for the same parent is shown + * sawfish does not set the focus to it. Applies only for click to focus mode. + */ + if( ! (nStyle_ & SAL_FRAME_STYLE_FLOAT ) && mbInShow && GetDisplay()->getWMAdaptor()->getWindowManagerName().EqualsAscii( "Sawfish" ) ) + { + // don't set the focus into the IME status window + // since this will lead to a parent loose-focus, close status, + // reget focus, open status, .... flicker loop + if ( (I18NStatus::get().getStatusFrame() != this) ) + bSetFocus = true; + } + + /* + * sometimes a message box/dialogue is brought up when a frame is not mapped + * the corresponding TRANSIENT_FOR hint is then set to the root window + * so that the dialogue shows in all cases. Correct it here if the + * frame is shown afterwards. + */ + if( ! IsChildWindow() + && ! IsOverrideRedirect() + && ! IsFloatGrabWindow() + ) + { + for( std::list< X11SalFrame* >::const_iterator it = maChildren.begin(); + it != maChildren.end(); ++it ) + { + if( (*it)->mbTransientForRoot ) + pDisplay_->getWMAdaptor()->changeReferenceFrame( *it, this ); + } + } + + if( hPresentationWindow != None && GetShellWindow() == hPresentationWindow ) + XSetInputFocus( GetXDisplay(), GetShellWindow(), RevertToParent, CurrentTime ); + /* For unknown reasons Dtwm does respect the input_hint + * set to False, but not when mapping the window. So + * emulate the correct behaviour and set the focus back + * to where it most probably should have been. + */ + if( (nStyle_ & SAL_FRAME_STYLE_OWNERDRAWDECORATION) && + mpParent && + GetDisplay()->getWMAdaptor()->getWindowManagerName().EqualsAscii( "Dtwm" ) + ) + { + XSetInputFocus( GetXDisplay(), + mpParent->GetShellWindow(), + RevertToParent, + CurrentTime ); + bSetFocus = false; + } + + if( bSetFocus ) + { + XSetInputFocus( GetXDisplay(), + GetShellWindow(), + RevertToParent, + CurrentTime ); + } + + + RestackChildren(); + mbInShow = sal_False; + m_bSetFocusOnMap = false; + } + break; + + case UnmapNotify: + if( pEvent->xunmap.window == GetShellWindow() ) + { + bMapped_ = sal_False; + bViewable_ = sal_False; + nRet = sal_True; + if ( mpInputContext != NULL ) + mpInputContext->Unmap( this ); + CallCallback( SALEVENT_RESIZE, NULL ); + } + break; + + case ConfigureNotify: + if( pEvent->xconfigure.window == GetShellWindow() + || pEvent->xconfigure.window == GetWindow() ) + nRet = HandleSizeEvent( &pEvent->xconfigure ); + break; + + case VisibilityNotify: + nVisibility_ = pEvent->xvisibility.state; + nRet = sal_True; + if( bAlwaysOnTop_ + && bMapped_ + && ! GetDisplay()->getWMAdaptor()->isAlwaysOnTopOK() + && nVisibility_ != VisibilityUnobscured ) + maAlwaysOnTopRaiseTimer.Start(); + break; + + case ReparentNotify: + nRet = HandleReparentEvent( &pEvent->xreparent ); + break; + + case MappingNotify: + if( MappingPointer != pEvent->xmapping.request ) + nRet = CallCallback( SALEVENT_KEYBOARDCHANGED, 0 ); + break; + + case ColormapNotify: + nRet = HandleColormapEvent( &pEvent->xcolormap ); + break; + + case PropertyNotify: + { + if( pEvent->xproperty.atom == pDisplay_->getWMAdaptor()->getAtom( WMAdaptor::WM_STATE ) ) + nRet = HandleStateEvent( &pEvent->xproperty ); + else + nRet = pDisplay_->getWMAdaptor()->handlePropertyNotify( this, &pEvent->xproperty ); + break; + } + + case ClientMessage: + nRet = HandleClientMessage( &pEvent->xclient ); + break; + } + } + else + { + switch( pEvent->type ) + { + case FocusIn: + case FocusOut: + if( ( nStyle_ & SAL_FRAME_STYLE_PLUG ) + && ( pEvent->xfocus.window == GetShellWindow() + || pEvent->xfocus.window == GetForeignParent() ) + ) + { + nRet = HandleFocusEvent( &pEvent->xfocus ); + } + break; + + case ConfigureNotify: + if( pEvent->xconfigure.window == GetForeignParent() || + pEvent->xconfigure.window == GetShellWindow() ) + nRet = HandleSizeEvent( &pEvent->xconfigure ); + + if( pEvent->xconfigure.window == GetStackingWindow() ) + nRet = HandleSizeEvent( &pEvent->xconfigure ); + + RestackChildren(); + break; + } + } + + return nRet; +} + +void X11SalFrame::ResetClipRegion() +{ + delete [] m_pClipRectangles; + m_pClipRectangles = NULL; + m_nCurClipRect = m_nMaxClipRect = 0; + + const int dest_kind = ShapeBounding; + const int op = ShapeSet; + const int ordering = YSorted; + + XWindowAttributes win_attrib; + XRectangle win_size; + + XLIB_Window aShapeWindow = mhShellWindow; + + XGetWindowAttributes ( GetDisplay()->GetDisplay(), + aShapeWindow, + &win_attrib ); + + win_size.x = 0; + win_size.y = 0; + win_size.width = win_attrib.width; + win_size.height = win_attrib.height; + + XShapeCombineRectangles ( GetDisplay()->GetDisplay(), + aShapeWindow, + dest_kind, + 0, 0, // x_off, y_off + &win_size, // list of rectangles + 1, // number of rectangles + op, ordering ); +} + +void X11SalFrame::BeginSetClipRegion( sal_uLong nRects ) +{ + if( m_pClipRectangles ) + delete [] m_pClipRectangles; + if( nRects ) + m_pClipRectangles = new XRectangle[nRects]; + else + m_pClipRectangles = NULL; + m_nMaxClipRect = static_cast<int>(nRects); + m_nCurClipRect = 0; +} + +void X11SalFrame::UnionClipRegion( long nX, long nY, long nWidth, long nHeight ) +{ + if( m_pClipRectangles && m_nCurClipRect < m_nMaxClipRect ) + { + m_pClipRectangles[m_nCurClipRect].x = nX; + m_pClipRectangles[m_nCurClipRect].y = nY; + m_pClipRectangles[m_nCurClipRect].width = nWidth; + m_pClipRectangles[m_nCurClipRect].height = nHeight; + m_nCurClipRect++; + } +} + +void X11SalFrame::EndSetClipRegion() +{ + const int dest_kind = ShapeBounding; + const int ordering = YSorted; + const int op = ShapeSet; + + XLIB_Window aShapeWindow = mhShellWindow; + XShapeCombineRectangles ( GetDisplay()->GetDisplay(), + aShapeWindow, + dest_kind, + 0, 0, // x_off, y_off + m_pClipRectangles, + m_nCurClipRect, + op, ordering ); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/generic/window/salobj.cxx b/vcl/unx/generic/window/salobj.cxx new file mode 100644 index 000000000000..124c0f6cadbb --- /dev/null +++ b/vcl/unx/generic/window/salobj.cxx @@ -0,0 +1,569 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#if OSL_DEBUG_LEVEL > 1 +#include <stdio.h> +#endif +#include <tools/debug.hxx> + +#include <vcl/keycodes.hxx> + +#include <tools/prex.h> +#include <X11/Xlib.h> +#include <X11/extensions/shape.h> +#include <tools/postx.h> + +#include <unx/salunx.h> +#include <unx/salstd.hxx> +#include <unx/saldata.hxx> +#include <unx/salinst.h> +#include <unx/saldisp.hxx> +#include <unx/salframe.h> +#include <unx/salobj.h> + +#include <salwtype.hxx> + + +// ======================================================================= +// SalInstance member to create and destroy a SalObject + +SalObject* X11SalInstance::CreateObject( SalFrame* pParent, SystemWindowData* pWindowData, sal_Bool bShow ) +{ + return X11SalObject::CreateObject( pParent, pWindowData, bShow ); +} + +X11SalObject* X11SalObject::CreateObject( SalFrame* pParent, SystemWindowData* pWindowData, sal_Bool bShow ) +{ + int error_base, event_base; + X11SalObject* pObject = new X11SalObject(); + SystemChildData* pObjData = const_cast<SystemChildData*>(pObject->GetSystemData()); + + if ( ! XShapeQueryExtension( (Display*)pObjData->pDisplay, + &event_base, &error_base ) ) + { + delete pObject; + return NULL; + } + + pObject->mpParent = pParent; + + SalDisplay* pSalDisp = GetX11SalData()->GetDisplay(); + const SystemEnvData* pEnv = pParent->GetSystemData(); + Display* pDisp = pSalDisp->GetDisplay(); + XLIB_Window aObjectParent = (XLIB_Window)pEnv->aWindow; + + // find out on which screen that window is + XWindowAttributes aParentAttr; + XGetWindowAttributes( pDisp, aObjectParent, &aParentAttr ); + int nScreen = XScreenNumberOfScreen( aParentAttr.screen ); + Visual* pVisual = (pWindowData && pWindowData->pVisual) ? + (Visual*)pWindowData->pVisual : + pSalDisp->GetVisual( nScreen ).GetVisual(); + // get visual info + VisualID aVisID = XVisualIDFromVisual( pVisual ); + XVisualInfo aTemplate; + aTemplate.visualid = aVisID; + int nVisuals = 0; + XVisualInfo* pInfos = XGetVisualInfo( pDisp, VisualIDMask, &aTemplate, &nVisuals ); + // only one VisualInfo structure can match the visual id + DBG_ASSERT( nVisuals == 1, "match count for visual id is not 1" ); + unsigned int nDepth = pInfos->depth; + XFree( pInfos ); + XSetWindowAttributes aAttribs; + aAttribs.event_mask = StructureNotifyMask + | ButtonPressMask + | ButtonReleaseMask + | PointerMotionMask + | EnterWindowMask + | LeaveWindowMask + | FocusChangeMask + | ExposureMask + ; + + pObject->maPrimary = + XCreateSimpleWindow( pDisp, + aObjectParent, + 0, 0, + 1, 1, 0, + pSalDisp->GetColormap( nScreen ).GetBlackPixel(), + pSalDisp->GetColormap( nScreen ).GetWhitePixel() + ); + if( aVisID == pSalDisp->GetVisual( nScreen ).GetVisualId() ) + { + pObject->maSecondary = + XCreateSimpleWindow( pDisp, + pObject->maPrimary, + 0, 0, + 1, 1, 0, + pSalDisp->GetColormap( nScreen ).GetBlackPixel(), + pSalDisp->GetColormap( nScreen ).GetWhitePixel() + ); + } + else + { + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "visual id of vcl %x, of visual %x\n", + static_cast<unsigned int> (pSalDisp->GetVisual( nScreen ).GetVisualId()), + static_cast<unsigned int> (aVisID) ); + #endif + pSalDisp->GetXLib()->PushXErrorLevel( true ); + + // create colormap for visual - there might not be one + pObject->maColormap = aAttribs.colormap = XCreateColormap( + pDisp, + pSalDisp->GetRootWindow( nScreen ), + pVisual, + AllocNone ); + + pObject->maSecondary = + XCreateWindow( pDisp, + pSalDisp->GetRootWindow( nScreen ), + 0, 0, + 1, 1, 0, + nDepth, InputOutput, + pVisual, + CWEventMask|CWColormap, &aAttribs ); + XSync( pDisp, False ); + sal_Bool bWasXError = pSalDisp->GetXLib()->HasXErrorOccurred(); + pSalDisp->GetXLib()->PopXErrorLevel(); + if( bWasXError ) + { + pObject->maSecondary = None; + delete pObject; + return NULL; + } + XReparentWindow( pDisp, pObject->maSecondary, pObject->maPrimary, 0, 0 ); + } + + pSalDisp->GetXLib()->PushXErrorLevel( true ); + if( bShow ) { + XMapWindow( pDisp, pObject->maSecondary ); + XMapWindow( pDisp, pObject->maPrimary ); + } + + pObjData->pDisplay = pDisp; + pObjData->aWindow = pObject->maSecondary; + pObjData->pWidget = NULL; + pObjData->pVisual = pVisual; + pObjData->nDepth = nDepth; + pObjData->aColormap = aVisID == pSalDisp->GetVisual( nScreen ).GetVisualId() ? + pSalDisp->GetColormap( nScreen ).GetXColormap() : None; + pObjData->pAppContext = NULL; + + XSync(pDisp, False); + sal_Bool bWasXError = pSalDisp->GetXLib()->HasXErrorOccurred(); + pSalDisp->GetXLib()->PopXErrorLevel(); + if( bWasXError ) + { + delete pObject; + return NULL; + } + + return pObject; +} + + +void X11SalInstance::DestroyObject( SalObject* pObject ) +{ + delete pObject; +} + + +// ====================================================================== +// SalClipRegion is a member of SalObject +// definition of SalClipRegion my be found in unx/inc/salobj.h + + +SalClipRegion::SalClipRegion() +{ + ClipRectangleList = NULL; + numClipRectangles = 0; + maxClipRectangles = 0; + nClipRegionType = SAL_OBJECT_CLIP_INCLUDERECTS; +} + + +SalClipRegion::~SalClipRegion() +{ + if ( ClipRectangleList ) + delete [] ClipRectangleList; +} + + +void +SalClipRegion::BeginSetClipRegion( sal_uLong nRects ) +{ + if (ClipRectangleList) + delete [] ClipRectangleList; + + ClipRectangleList = new XRectangle[nRects]; + numClipRectangles = 0; + maxClipRectangles = nRects; +} + + +void +SalClipRegion::UnionClipRegion( long nX, long nY, long nWidth, long nHeight ) +{ + if ( nWidth && nHeight && (numClipRectangles < maxClipRectangles) ) + { + XRectangle *aRect = ClipRectangleList + numClipRectangles; + + aRect->x = (short) nX; + aRect->y = (short) nY; + aRect->width = (unsigned short) nWidth; + aRect->height= (unsigned short) nHeight; + + numClipRectangles++; + } +} + + +// ======================================================================= +// SalObject Implementation + + +X11SalObject::X11SalObject() +{ + maSystemChildData.nSize = sizeof( SystemChildData ); + maSystemChildData.pDisplay = GetX11SalData()->GetDisplay()->GetDisplay(); + maSystemChildData.aWindow = None; + maSystemChildData.pSalFrame = 0; + maSystemChildData.pWidget = 0; + maSystemChildData.pVisual = 0; + maSystemChildData.nDepth = 0; + maSystemChildData.aColormap = 0; + maSystemChildData.pAppContext = NULL; + maSystemChildData.aShellWindow = 0; + maSystemChildData.pShellWidget = NULL; + maPrimary = 0; + maSecondary = 0; + maColormap = 0; + + std::list< SalObject* >& rObjects = GetX11SalData()->GetDisplay()->getSalObjects(); + rObjects.push_back( this ); +} + + +X11SalObject::~X11SalObject() +{ + std::list< SalObject* >& rObjects = GetX11SalData()->GetDisplay()->getSalObjects(); + rObjects.remove( this ); + SalDisplay* pSalDisp = GetX11SalData()->GetDisplay(); + pSalDisp->GetXLib()->PushXErrorLevel( true ); + if ( maSecondary ) + XDestroyWindow( (Display*)maSystemChildData.pDisplay, maSecondary ); + if ( maPrimary ) + XDestroyWindow( (Display*)maSystemChildData.pDisplay, maPrimary ); + if ( maColormap ) + XFreeColormap((Display*)maSystemChildData.pDisplay, maColormap); + XSync( (Display*)maSystemChildData.pDisplay, False ); + pSalDisp->GetXLib()->PopXErrorLevel(); +} + + +void +X11SalObject::ResetClipRegion() +{ + maClipRegion.ResetClipRegion(); + + const int dest_kind = ShapeBounding; + const int op = ShapeSet; + const int ordering = YSorted; + + XWindowAttributes win_attrib; + XRectangle win_size; + + XLIB_Window aShapeWindow = maPrimary; + + XGetWindowAttributes ( (Display*)maSystemChildData.pDisplay, + aShapeWindow, + &win_attrib ); + + win_size.x = 0; + win_size.y = 0; + win_size.width = win_attrib.width; + win_size.height = win_attrib.height; + + XShapeCombineRectangles ( (Display*)maSystemChildData.pDisplay, + aShapeWindow, + dest_kind, + 0, 0, // x_off, y_off + &win_size, // list of rectangles + 1, // number of rectangles + op, ordering ); +} + + +void +X11SalObject::BeginSetClipRegion( sal_uLong nRectCount ) +{ + maClipRegion.BeginSetClipRegion ( nRectCount ); +} + + +void +X11SalObject::UnionClipRegion( long nX, long nY, long nWidth, long nHeight ) +{ + maClipRegion.UnionClipRegion ( nX, nY, nWidth, nHeight ); +} + + +void +X11SalObject::EndSetClipRegion() +{ + XRectangle *pRectangles = maClipRegion.EndSetClipRegion (); + const int nType = maClipRegion.GetClipRegionType(); + const int nRectangles = maClipRegion.GetRectangleCount(); + + const int dest_kind = ShapeBounding; + const int ordering = YSorted; + int op; + + switch ( nType ) + { + case SAL_OBJECT_CLIP_INCLUDERECTS : + op = ShapeSet; + break; + case SAL_OBJECT_CLIP_EXCLUDERECTS : + op = ShapeSubtract; + break; + case SAL_OBJECT_CLIP_ABSOLUTE : + op = ShapeSet; + break; + default : + op = ShapeUnion; + } + + XLIB_Window aShapeWindow = maPrimary; + + XShapeCombineRectangles ( (Display*)maSystemChildData.pDisplay, + aShapeWindow, + dest_kind, + 0, 0, // x_off, y_off + pRectangles, + nRectangles, + op, ordering ); +} + + +sal_uInt16 +X11SalObject::GetClipRegionType() +{ + return maClipRegion.GetClipRegionType(); +} + +// ----------------------------------------------------------------------- + +void +X11SalObject::SetPosSize( long nX, long nY, long nWidth, long nHeight ) +{ + if ( maPrimary && maSecondary && nWidth && nHeight ) + { + XMoveResizeWindow( (Display*)maSystemChildData.pDisplay, + maPrimary, + nX, nY, nWidth, nHeight ); + XMoveResizeWindow( (Display*)maSystemChildData.pDisplay, + maSecondary, + 0, 0, nWidth, nHeight ); + } +} + + +void +X11SalObject::Show( sal_Bool bVisible ) +{ + if ( ! maSystemChildData.aWindow ) + return; + + if ( bVisible ) { + XMapWindow( (Display*)maSystemChildData.pDisplay, + maSecondary ); + XMapWindow( (Display*)maSystemChildData.pDisplay, + maPrimary ); + } else { + XUnmapWindow( (Display*)maSystemChildData.pDisplay, + maPrimary ); + XUnmapWindow( (Display*)maSystemChildData.pDisplay, + maSecondary ); + } + mbVisible = bVisible; +} + +// ----------------------------------------------------------------------- + +void X11SalObject::Enable( sal_Bool ) +{ +} + +// ----------------------------------------------------------------------- + +void X11SalObject::GrabFocus() +{ + if( mbVisible ) + XSetInputFocus( (Display*)maSystemChildData.pDisplay, + maSystemChildData.aWindow, + RevertToNone, + CurrentTime ); +} + +// ----------------------------------------------------------------------- + +void X11SalObject::SetBackground() +{ +} + +// ----------------------------------------------------------------------- + +void X11SalObject::SetBackground( SalColor ) +{ +} + +// ----------------------------------------------------------------------- + +const SystemChildData* X11SalObject::GetSystemData() const +{ + return &maSystemChildData; +} + +static sal_uInt16 sal_GetCode( int state ) +{ + sal_uInt16 nCode = 0; + + if( state & Button1Mask ) + nCode |= MOUSE_LEFT; + if( state & Button2Mask ) + nCode |= MOUSE_MIDDLE; + if( state & Button3Mask ) + nCode |= MOUSE_RIGHT; + + if( state & ShiftMask ) + nCode |= KEY_SHIFT; + if( state & ControlMask ) + nCode |= KEY_MOD1; + if( state & Mod1Mask ) + nCode |= KEY_MOD2; + if( state & Mod3Mask ) + nCode |= KEY_MOD3; + + return nCode; +} + +long X11SalObject::Dispatch( XEvent* pEvent ) +{ + std::list< SalObject* >& rObjects = GetX11SalData()->GetDisplay()->getSalObjects(); + + for( std::list< SalObject* >::iterator it = rObjects.begin(); it != rObjects.end(); ++it ) + { + X11SalObject* pObject = static_cast<X11SalObject*>(*it); + if( pEvent->xany.window == pObject->maPrimary || + pEvent->xany.window == pObject->maSecondary ) + { + if( pObject->IsMouseTransparent() && ( + pEvent->type == ButtonPress || + pEvent->type == ButtonRelease || + pEvent->type == EnterNotify || + pEvent->type == LeaveNotify || + pEvent->type == MotionNotify + ) + ) + { + SalMouseEvent aEvt; + const SystemEnvData* pParentData = pObject->mpParent->GetSystemData(); + int dest_x, dest_y; + XLIB_Window aChild = None; + XTranslateCoordinates( pEvent->xbutton.display, + pEvent->xbutton.root, + pParentData->aWindow, + pEvent->xbutton.x_root, + pEvent->xbutton.y_root, + &dest_x, &dest_y, + &aChild ); + aEvt.mnX = dest_x; + aEvt.mnY = dest_y; + aEvt.mnTime = pEvent->xbutton.time; + aEvt.mnCode = sal_GetCode( pEvent->xbutton.state ); + aEvt.mnButton = 0; + sal_uInt16 nEvent = 0; + if( pEvent->type == ButtonPress || + pEvent->type == ButtonRelease ) + { + switch( pEvent->xbutton.button ) + { + case Button1: aEvt.mnButton = MOUSE_LEFT;break; + case Button2: aEvt.mnButton = MOUSE_MIDDLE;break; + case Button3: aEvt.mnButton = MOUSE_RIGHT;break; + } + nEvent = (pEvent->type == ButtonPress) ? + SALEVENT_MOUSEBUTTONDOWN : + SALEVENT_MOUSEBUTTONUP; + } + else if( pEvent->type == EnterNotify ) + nEvent = SALEVENT_MOUSELEAVE; + else + nEvent = SALEVENT_MOUSEMOVE; + pObject->mpParent->CallCallback( nEvent, &aEvt ); + } + else + { + switch( pEvent->type ) + { + case UnmapNotify: + pObject->mbVisible = sal_False; + return 1; + case MapNotify: + pObject->mbVisible = sal_True; + return 1; + case ButtonPress: + pObject->CallCallback( SALOBJ_EVENT_TOTOP, NULL ); + return 1; + case FocusIn: + pObject->CallCallback( SALOBJ_EVENT_GETFOCUS, NULL ); + return 1; + case FocusOut: + pObject->CallCallback( SALOBJ_EVENT_LOSEFOCUS, NULL ); + return 1; + default: break; + } + } + return 0; + } + } + return 0; +} + +// ----------------------------------------------------------------------- + +void X11SalObject::InterceptChildWindowKeyDown( sal_Bool /*bIntercept*/ ) +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
\ No newline at end of file |