There are 2 bugs: - In Window::GetDrawPixelFont we asked for the font to the current window, which didn't work correctly when printing. The device itself should provide the font and not the window. - In paint method the font wasn't setup correctly which should happen before painting by calling ApplySettings. The effect of this is that print preview didn't show the correct font for the edit control and similar when you copy + paste the edit control. The work around for this is to call ApplySettings again. Change-Id: I74960355823c71c1d5a18a82bbc86561a3c4b760
3079 lines
98 KiB
C++
3079 lines
98 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#include <tools/rc.h>
|
|
#include <vcl/decoview.hxx>
|
|
#include <vcl/event.hxx>
|
|
#include <vcl/cursor.hxx>
|
|
#include <vcl/virdev.hxx>
|
|
#include <vcl/menu.hxx>
|
|
#include <vcl/edit.hxx>
|
|
#include <vcl/layout.hxx>
|
|
#include <vcl/svapp.hxx>
|
|
#include <vcl/settings.hxx>
|
|
|
|
#include <window.h>
|
|
#include <svdata.hxx>
|
|
#include <svids.hrc>
|
|
#include <controldata.hxx>
|
|
|
|
#include <osl/mutex.hxx>
|
|
|
|
#include <com/sun/star/i18n/BreakIterator.hpp>
|
|
#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
|
|
#include <com/sun/star/i18n/WordType.hpp>
|
|
#include <cppuhelper/weak.hxx>
|
|
#include <com/sun/star/datatransfer/XTransferable.hpp>
|
|
#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
|
|
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
|
|
|
|
#include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
|
|
#include <com/sun/star/datatransfer/dnd/XDragGestureRecognizer.hpp>
|
|
#include <com/sun/star/datatransfer/dnd/XDropTarget.hpp>
|
|
|
|
#include <com/sun/star/i18n/InputSequenceChecker.hpp>
|
|
#include <com/sun/star/i18n/InputSequenceCheckMode.hpp>
|
|
#include <com/sun/star/i18n/ScriptType.hpp>
|
|
#include <com/sun/star/container/XNameAccess.hpp>
|
|
|
|
#include <com/sun/star/uno/Any.hxx>
|
|
|
|
#include <comphelper/processfactory.hxx>
|
|
#include <comphelper/string.hxx>
|
|
|
|
#include <sot/exchange.hxx>
|
|
#include <sot/formats.hxx>
|
|
#include <sal/macros.h>
|
|
|
|
#include <vcl/unohelp.hxx>
|
|
#include <vcl/unohelp2.hxx>
|
|
|
|
#include <officecfg/Office/Common.hxx>
|
|
|
|
#include <boost/signals2/signal.hpp>
|
|
|
|
#include <memory>
|
|
|
|
using namespace ::com::sun::star;
|
|
using namespace ::com::sun::star::uno;
|
|
using namespace ::com::sun::star::lang;
|
|
|
|
// - Redo
|
|
// - Bei Tracking-Cancel DefaultSelection wieder herstellen
|
|
|
|
static FncGetSpecialChars pImplFncGetSpecialChars = NULL;
|
|
|
|
#define EDIT_ALIGN_LEFT 1
|
|
#define EDIT_ALIGN_CENTER 2
|
|
#define EDIT_ALIGN_RIGHT 3
|
|
|
|
#define EDIT_DEL_LEFT 1
|
|
#define EDIT_DEL_RIGHT 2
|
|
|
|
#define EDIT_DELMODE_SIMPLE 11
|
|
#define EDIT_DELMODE_RESTOFWORD 12
|
|
#define EDIT_DELMODE_RESTOFCONTENT 13
|
|
|
|
struct DDInfo
|
|
{
|
|
vcl::Cursor aCursor;
|
|
Selection aDndStartSel;
|
|
sal_Int32 nDropPos;
|
|
bool bStarterOfDD;
|
|
bool bDroppedInMe;
|
|
bool bVisCursor;
|
|
bool bIsStringSupported;
|
|
|
|
DDInfo()
|
|
{
|
|
aCursor.SetStyle( CURSOR_SHADOW );
|
|
nDropPos = 0;
|
|
bStarterOfDD = false;
|
|
bDroppedInMe = false;
|
|
bVisCursor = false;
|
|
bIsStringSupported = false;
|
|
}
|
|
};
|
|
|
|
struct Impl_IMEInfos
|
|
{
|
|
OUString aOldTextAfterStartPos;
|
|
sal_uInt16* pAttribs;
|
|
sal_Int32 nPos;
|
|
sal_Int32 nLen;
|
|
bool bCursor;
|
|
bool bWasCursorOverwrite;
|
|
|
|
Impl_IMEInfos(sal_Int32 nPos, const OUString& rOldTextAfterStartPos);
|
|
~Impl_IMEInfos();
|
|
|
|
void CopyAttribs(const sal_uInt16* pA, sal_Int32 nL);
|
|
void DestroyAttribs();
|
|
};
|
|
|
|
Impl_IMEInfos::Impl_IMEInfos(sal_Int32 nP, const OUString& rOldTextAfterStartPos)
|
|
: aOldTextAfterStartPos(rOldTextAfterStartPos)
|
|
{
|
|
nPos = nP;
|
|
nLen = 0;
|
|
bCursor = true;
|
|
pAttribs = NULL;
|
|
bWasCursorOverwrite = false;
|
|
}
|
|
|
|
Impl_IMEInfos::~Impl_IMEInfos()
|
|
{
|
|
delete[] pAttribs;
|
|
}
|
|
|
|
void Impl_IMEInfos::CopyAttribs(const sal_uInt16* pA, sal_Int32 nL)
|
|
{
|
|
nLen = nL;
|
|
delete[] pAttribs;
|
|
pAttribs = new sal_uInt16[ nL ];
|
|
memcpy( pAttribs, pA, nL*sizeof(sal_uInt16) );
|
|
}
|
|
|
|
void Impl_IMEInfos::DestroyAttribs()
|
|
{
|
|
delete[] pAttribs;
|
|
pAttribs = NULL;
|
|
nLen = 0;
|
|
}
|
|
|
|
struct Edit::Impl
|
|
{
|
|
boost::signals2::signal< void (Edit *) > m_AutocompleteSignal;
|
|
};
|
|
|
|
Edit::Edit( WindowType nType )
|
|
: Control( nType )
|
|
, m_pImpl(new Impl)
|
|
{
|
|
ImplInitEditData();
|
|
}
|
|
|
|
Edit::Edit( vcl::Window* pParent, WinBits nStyle )
|
|
: Control( WINDOW_EDIT )
|
|
, m_pImpl(new Impl)
|
|
{
|
|
ImplInitEditData();
|
|
ImplInit( pParent, nStyle );
|
|
}
|
|
|
|
Edit::Edit( vcl::Window* pParent, const ResId& rResId )
|
|
: Control( WINDOW_EDIT )
|
|
, m_pImpl(new Impl)
|
|
{
|
|
rResId.SetRT( RSC_EDIT );
|
|
WinBits nStyle = ImplInitRes( rResId );
|
|
ImplInitEditData();
|
|
ImplInit( pParent, nStyle );
|
|
ImplLoadRes( rResId );
|
|
|
|
if ( !(nStyle & WB_HIDE) )
|
|
Show();
|
|
}
|
|
|
|
void Edit::SetWidthInChars(sal_Int32 nWidthInChars)
|
|
{
|
|
if (mnWidthInChars != nWidthInChars)
|
|
{
|
|
mnWidthInChars = nWidthInChars;
|
|
queue_resize();
|
|
}
|
|
}
|
|
|
|
void Edit::setMaxWidthChars(sal_Int32 nWidth)
|
|
{
|
|
if (nWidth != mnMaxWidthChars)
|
|
{
|
|
mnMaxWidthChars = nWidth;
|
|
queue_resize();
|
|
}
|
|
}
|
|
|
|
bool Edit::set_property(const OString &rKey, const OString &rValue)
|
|
{
|
|
if (rKey == "width-chars")
|
|
SetWidthInChars(rValue.toInt32());
|
|
else if (rKey == "max-width-chars")
|
|
setMaxWidthChars(rValue.toInt32());
|
|
else if (rKey == "max-length")
|
|
{
|
|
sal_Int32 nTextLen = rValue.toInt32();
|
|
SetMaxTextLen(nTextLen == 0 ? EDIT_NOLIMIT : nTextLen);
|
|
}
|
|
else if (rKey == "editable")
|
|
{
|
|
bool bReadOnly = !toBool(rValue);
|
|
SetReadOnly(bReadOnly);
|
|
//disable tab to traverse into readonly editables
|
|
WinBits nBits = GetStyle();
|
|
nBits &= ~(WB_TABSTOP|WB_NOTABSTOP);
|
|
if (!bReadOnly)
|
|
nBits |= WB_TABSTOP;
|
|
else
|
|
nBits |= WB_NOTABSTOP;
|
|
SetStyle(nBits);
|
|
}
|
|
else if (rKey == "visibility")
|
|
{
|
|
WinBits nBits = GetStyle();
|
|
nBits &= ~(WB_PASSWORD);
|
|
if (!toBool(rValue))
|
|
nBits |= WB_PASSWORD;
|
|
SetStyle(nBits);
|
|
}
|
|
else if (rKey == "placeholder-text")
|
|
SetPlaceholderText(OStringToOUString(rValue, RTL_TEXTENCODING_UTF8));
|
|
else
|
|
return Control::set_property(rKey, rValue);
|
|
return true;
|
|
}
|
|
|
|
Edit::~Edit()
|
|
{
|
|
disposeOnce();
|
|
}
|
|
|
|
void Edit::dispose()
|
|
{
|
|
delete mpDDInfo;
|
|
mpDDInfo = NULL;
|
|
|
|
vcl::Cursor* pCursor = GetCursor();
|
|
if ( pCursor )
|
|
{
|
|
SetCursor( NULL );
|
|
delete pCursor;
|
|
}
|
|
|
|
delete mpIMEInfos;
|
|
mpIMEInfos = NULL;
|
|
|
|
delete mpUpdateDataTimer;
|
|
mpUpdateDataTimer = NULL;
|
|
|
|
if ( mxDnDListener.is() )
|
|
{
|
|
if ( GetDragGestureRecognizer().is() )
|
|
{
|
|
uno::Reference< datatransfer::dnd::XDragGestureListener> xDGL( mxDnDListener, uno::UNO_QUERY );
|
|
GetDragGestureRecognizer()->removeDragGestureListener( xDGL );
|
|
}
|
|
if ( GetDropTarget().is() )
|
|
{
|
|
uno::Reference< datatransfer::dnd::XDropTargetListener> xDTL( mxDnDListener, uno::UNO_QUERY );
|
|
GetDropTarget()->removeDropTargetListener( xDTL );
|
|
}
|
|
|
|
uno::Reference< lang::XEventListener> xEL( mxDnDListener, uno::UNO_QUERY );
|
|
xEL->disposing( lang::EventObject() ); // #95154# #96585# Empty Source means it's the Client
|
|
mxDnDListener.clear();
|
|
}
|
|
|
|
SetType(WINDOW_WINDOW);
|
|
|
|
mpSubEdit.disposeAndClear();
|
|
Control::dispose();
|
|
}
|
|
|
|
void Edit::ImplInitEditData()
|
|
{
|
|
mpSubEdit = VclPtr<Edit>();
|
|
mpUpdateDataTimer = NULL;
|
|
mpFilterText = NULL;
|
|
mnXOffset = 0;
|
|
mnAlign = EDIT_ALIGN_LEFT;
|
|
mnMaxTextLen = EDIT_NOLIMIT;
|
|
mnWidthInChars = -1;
|
|
mnMaxWidthChars = -1;
|
|
meAutocompleteAction = AUTOCOMPLETE_KEYINPUT;
|
|
mbModified = false;
|
|
mbInternModified = false;
|
|
mbReadOnly = false;
|
|
mbInsertMode = true;
|
|
mbClickedInSelection = false;
|
|
mbActivePopup = false;
|
|
mbIsSubEdit = false;
|
|
mbInMBDown = false;
|
|
mpDDInfo = NULL;
|
|
mpIMEInfos = NULL;
|
|
mcEchoChar = 0;
|
|
|
|
// --- RTL --- no default mirroring for Edit controls
|
|
// note: controls that use a subedit will revert this (SpinField, ComboBox)
|
|
EnableRTL( false );
|
|
|
|
vcl::unohelper::DragAndDropWrapper* pDnDWrapper = new vcl::unohelper::DragAndDropWrapper( this );
|
|
mxDnDListener = pDnDWrapper;
|
|
}
|
|
|
|
bool Edit::ImplUseNativeBorder(vcl::RenderContext& rRenderContext, WinBits nStyle)
|
|
{
|
|
bool bRet = rRenderContext.IsNativeControlSupported(ImplGetNativeControlType(),
|
|
HAS_BACKGROUND_TEXTURE)
|
|
&& ((nStyle & WB_BORDER) && !(nStyle & WB_NOBORDER));
|
|
if (!bRet && mbIsSubEdit)
|
|
{
|
|
vcl::Window* pWindow = GetParent();
|
|
nStyle = pWindow->GetStyle();
|
|
bRet = pWindow->IsNativeControlSupported(ImplGetNativeControlType(),
|
|
HAS_BACKGROUND_TEXTURE)
|
|
&& ((nStyle & WB_BORDER) && !(nStyle & WB_NOBORDER));
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
void Edit::ImplInit(vcl::Window* pParent, WinBits nStyle)
|
|
{
|
|
nStyle = ImplInitStyle(nStyle);
|
|
|
|
if (!(nStyle & (WB_CENTER | WB_RIGHT)))
|
|
nStyle |= WB_LEFT;
|
|
|
|
Control::ImplInit(pParent, nStyle, NULL);
|
|
|
|
mbReadOnly = (nStyle & WB_READONLY) != 0;
|
|
|
|
mnAlign = EDIT_ALIGN_LEFT;
|
|
|
|
// --- RTL --- hack: right align until keyinput and cursor travelling works
|
|
if( IsRTLEnabled() )
|
|
mnAlign = EDIT_ALIGN_RIGHT;
|
|
|
|
if ( nStyle & WB_RIGHT )
|
|
mnAlign = EDIT_ALIGN_RIGHT;
|
|
else if ( nStyle & WB_CENTER )
|
|
mnAlign = EDIT_ALIGN_CENTER;
|
|
|
|
SetCursor( new vcl::Cursor );
|
|
|
|
SetPointer( Pointer( PointerStyle::Text ) );
|
|
|
|
uno::Reference< datatransfer::dnd::XDragGestureListener> xDGL( mxDnDListener, uno::UNO_QUERY );
|
|
uno::Reference< datatransfer::dnd::XDragGestureRecognizer > xDGR = GetDragGestureRecognizer();
|
|
if ( xDGR.is() )
|
|
{
|
|
xDGR->addDragGestureListener( xDGL );
|
|
uno::Reference< datatransfer::dnd::XDropTargetListener> xDTL( mxDnDListener, uno::UNO_QUERY );
|
|
GetDropTarget()->addDropTargetListener( xDTL );
|
|
GetDropTarget()->setActive( true );
|
|
GetDropTarget()->setDefaultActions( datatransfer::dnd::DNDConstants::ACTION_COPY_OR_MOVE );
|
|
}
|
|
}
|
|
|
|
WinBits Edit::ImplInitStyle( WinBits nStyle )
|
|
{
|
|
if ( !(nStyle & WB_NOTABSTOP) )
|
|
nStyle |= WB_TABSTOP;
|
|
if ( !(nStyle & WB_NOGROUP) )
|
|
nStyle |= WB_GROUP;
|
|
|
|
return nStyle;
|
|
}
|
|
|
|
bool Edit::IsCharInput( const KeyEvent& rKeyEvent )
|
|
{
|
|
// In the future we must use new Unicode functions for this
|
|
sal_Unicode cCharCode = rKeyEvent.GetCharCode();
|
|
return ((cCharCode >= 32) && (cCharCode != 127) &&
|
|
!rKeyEvent.GetKeyCode().IsMod3() &&
|
|
!rKeyEvent.GetKeyCode().IsMod2() &&
|
|
!rKeyEvent.GetKeyCode().IsMod1() );
|
|
}
|
|
|
|
void Edit::ImplModified()
|
|
{
|
|
mbModified = true;
|
|
Modify();
|
|
}
|
|
|
|
void Edit::ApplySettings(vcl::RenderContext& rRenderContext)
|
|
{
|
|
Control::ApplySettings(rRenderContext);
|
|
|
|
const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
|
|
|
|
vcl::Font aFont = rStyleSettings.GetFieldFont();
|
|
ApplyControlFont(rRenderContext, aFont);
|
|
|
|
ImplClearLayoutData();
|
|
|
|
Color aTextColor = rStyleSettings.GetFieldTextColor();
|
|
ApplyControlForeground(rRenderContext, aTextColor);
|
|
|
|
if (ImplUseNativeBorder(rRenderContext, GetStyle()) || IsPaintTransparent())
|
|
{
|
|
// Transparent background
|
|
rRenderContext.SetBackground();
|
|
rRenderContext.SetFillColor();
|
|
}
|
|
else if (IsControlBackground())
|
|
{
|
|
rRenderContext.SetBackground(GetControlBackground());
|
|
rRenderContext.SetFillColor(GetControlBackground());
|
|
}
|
|
else
|
|
{
|
|
rRenderContext.SetBackground(rStyleSettings.GetFieldColor());
|
|
rRenderContext.SetFillColor(rStyleSettings.GetFieldColor());
|
|
}
|
|
}
|
|
|
|
long Edit::ImplGetExtraXOffset() const
|
|
{
|
|
// MT 09/2002: nExtraOffsetX should become a member, instead of checking every time,
|
|
// but I need an incompatible update for this...
|
|
// #94095# Use extra offset only when edit has a border
|
|
long nExtraOffset = 0;
|
|
if( ( GetStyle() & WB_BORDER ) || ( mbIsSubEdit && ( GetParent()->GetStyle() & WB_BORDER ) ) )
|
|
nExtraOffset = 2;
|
|
|
|
return nExtraOffset;
|
|
}
|
|
|
|
long Edit::ImplGetExtraYOffset() const
|
|
{
|
|
long nExtraOffset = 0;
|
|
int eCtrlType = ImplGetNativeControlType();
|
|
if (eCtrlType != CTRL_EDITBOX_NOBORDER)
|
|
{
|
|
// add some space between text entry and border
|
|
nExtraOffset = 2;
|
|
}
|
|
return nExtraOffset;
|
|
}
|
|
|
|
OUString Edit::ImplGetText() const
|
|
{
|
|
if ( mcEchoChar || (GetStyle() & WB_PASSWORD) )
|
|
{
|
|
sal_Unicode cEchoChar;
|
|
if ( mcEchoChar )
|
|
cEchoChar = mcEchoChar;
|
|
else
|
|
cEchoChar = '*';
|
|
OUStringBuffer aText;
|
|
comphelper::string::padToLength(aText, maText.getLength(), cEchoChar);
|
|
return aText.makeStringAndClear();
|
|
}
|
|
else
|
|
return maText.toString();
|
|
}
|
|
|
|
void Edit::ImplInvalidateOrRepaint()
|
|
{
|
|
if( IsPaintTransparent() )
|
|
{
|
|
Invalidate();
|
|
// FIXME: this is currently only on OS X
|
|
if( ImplGetSVData()->maNWFData.mbNoFocusRects )
|
|
Update();
|
|
}
|
|
else
|
|
Invalidate();
|
|
}
|
|
|
|
long Edit::ImplGetTextYPosition() const
|
|
{
|
|
if ( GetStyle() & WB_TOP )
|
|
return ImplGetExtraXOffset();
|
|
else if ( GetStyle() & WB_BOTTOM )
|
|
return GetOutputSizePixel().Height() - GetTextHeight() - ImplGetExtraXOffset();
|
|
return ( GetOutputSizePixel().Height() - GetTextHeight() ) / 2;
|
|
}
|
|
|
|
void Edit::ImplRepaint(vcl::RenderContext& rRenderContext, const Rectangle& rRectangle, bool bLayout)
|
|
{
|
|
if (!IsReallyVisible())
|
|
return;
|
|
|
|
ApplySettings(rRenderContext);
|
|
|
|
OUString aText = ImplGetText();
|
|
sal_Int32 nLen = aText.getLength();
|
|
|
|
long nDXBuffer[256];
|
|
std::unique_ptr<long[]> pDXBuffer;
|
|
long* pDX = nDXBuffer;
|
|
|
|
if (!aText.isEmpty())
|
|
{
|
|
if ((size_t) (2 * aText.getLength()) > SAL_N_ELEMENTS(nDXBuffer))
|
|
{
|
|
pDXBuffer.reset(new long[2 * (aText.getLength() + 1)]);
|
|
pDX = pDXBuffer.get();
|
|
}
|
|
|
|
GetCaretPositions(aText, pDX, 0, nLen);
|
|
}
|
|
|
|
long nTH = GetTextHeight();
|
|
Point aPos(mnXOffset, ImplGetTextYPosition());
|
|
|
|
if (bLayout)
|
|
{
|
|
aPos.X() = mnXOffset + ImplGetExtraXOffset();
|
|
|
|
MetricVector* pVector = &mpControlData->mpLayoutData->m_aUnicodeBoundRects;
|
|
OUString* pDisplayText = &mpControlData->mpLayoutData->m_aDisplayText;
|
|
|
|
rRenderContext.DrawText(aPos, aText, 0, nLen, pVector, pDisplayText);
|
|
return;
|
|
}
|
|
|
|
vcl::Cursor* pCursor = GetCursor();
|
|
bool bVisCursor = pCursor && pCursor->IsVisible();
|
|
if (pCursor)
|
|
pCursor->Hide();
|
|
|
|
ImplClearBackground(rRenderContext, rRectangle, 0, GetOutputSizePixel().Width());
|
|
|
|
bool bPaintPlaceholderText = aText.isEmpty() && !maPlaceholderText.isEmpty();
|
|
|
|
const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
|
|
|
|
if (!IsEnabled() || bPaintPlaceholderText)
|
|
rRenderContext.SetTextColor(rStyleSettings.GetDisableColor());
|
|
|
|
// Set background color of the normal text
|
|
if ((GetStyle() & WB_FORCECTRLBACKGROUND) != 0 && IsControlBackground())
|
|
{
|
|
// check if we need to set ControlBackground even in NWF case
|
|
rRenderContext.Push(PushFlags::FILLCOLOR | PushFlags::LINECOLOR);
|
|
rRenderContext.SetLineColor();
|
|
rRenderContext.SetFillColor(GetControlBackground());
|
|
rRenderContext.DrawRect(Rectangle(aPos, Size(GetOutputSizePixel().Width() - 2 * mnXOffset, GetOutputSizePixel().Height())));
|
|
rRenderContext.Pop();
|
|
|
|
rRenderContext.SetTextFillColor(GetControlBackground());
|
|
}
|
|
else if (IsPaintTransparent() || ImplUseNativeBorder(rRenderContext, GetStyle()))
|
|
rRenderContext.SetTextFillColor();
|
|
else
|
|
rRenderContext.SetTextFillColor(IsControlBackground() ? GetControlBackground() : rStyleSettings.GetFieldColor());
|
|
|
|
ImplPaintBorder(rRenderContext, 0, GetOutputSizePixel().Width());
|
|
|
|
bool bDrawSelection = maSelection.Len() && (HasFocus() || (GetStyle() & WB_NOHIDESELECTION) || mbActivePopup);
|
|
|
|
aPos.X() = mnXOffset + ImplGetExtraXOffset();
|
|
if (bPaintPlaceholderText)
|
|
{
|
|
rRenderContext.DrawText(aPos, maPlaceholderText);
|
|
}
|
|
else if (!bDrawSelection && !mpIMEInfos)
|
|
{
|
|
rRenderContext.DrawText(aPos, aText, 0, nLen);
|
|
}
|
|
else
|
|
{
|
|
// save graphics state
|
|
rRenderContext.Push();
|
|
// first calculate higlighted and non highlighted clip regions
|
|
vcl::Region aHiglightClipRegion;
|
|
vcl::Region aNormalClipRegion;
|
|
Selection aTmpSel(maSelection);
|
|
aTmpSel.Justify();
|
|
// selection is highlighted
|
|
int i;
|
|
for(i = 0; i < aText.getLength(); i++)
|
|
{
|
|
Rectangle aRect(aPos, Size(10, nTH));
|
|
aRect.Left() = pDX[2 * i] + mnXOffset + ImplGetExtraXOffset();
|
|
aRect.Right() = pDX[2 * i + 1] + mnXOffset + ImplGetExtraXOffset();
|
|
aRect.Justify();
|
|
bool bHighlight = false;
|
|
if (i >= aTmpSel.Min() && i < aTmpSel.Max())
|
|
bHighlight = true;
|
|
|
|
if (mpIMEInfos && mpIMEInfos->pAttribs &&
|
|
i >= mpIMEInfos->nPos && i < (mpIMEInfos->nPos+mpIMEInfos->nLen) &&
|
|
(mpIMEInfos->pAttribs[i - mpIMEInfos->nPos] & EXTTEXTINPUT_ATTR_HIGHLIGHT))
|
|
{
|
|
bHighlight = true;
|
|
}
|
|
|
|
if (bHighlight)
|
|
aHiglightClipRegion.Union(aRect);
|
|
else
|
|
aNormalClipRegion.Union(aRect);
|
|
}
|
|
// draw normal text
|
|
Color aNormalTextColor = rRenderContext.GetTextColor();
|
|
rRenderContext.SetClipRegion(aNormalClipRegion);
|
|
|
|
if (IsPaintTransparent())
|
|
rRenderContext.SetTextFillColor();
|
|
else
|
|
{
|
|
// Set background color when part of the text is selected
|
|
if (ImplUseNativeBorder(rRenderContext, GetStyle()))
|
|
{
|
|
if( (GetStyle() & WB_FORCECTRLBACKGROUND) != 0 && IsControlBackground() )
|
|
rRenderContext.SetTextFillColor(GetControlBackground());
|
|
else
|
|
rRenderContext.SetTextFillColor();
|
|
}
|
|
else
|
|
{
|
|
rRenderContext.SetTextFillColor(IsControlBackground() ? GetControlBackground() : rStyleSettings.GetFieldColor());
|
|
}
|
|
}
|
|
rRenderContext.DrawText(aPos, aText, 0, nLen);
|
|
|
|
// draw highlighted text
|
|
rRenderContext.SetClipRegion(aHiglightClipRegion);
|
|
rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor());
|
|
rRenderContext.SetTextFillColor(rStyleSettings.GetHighlightColor());
|
|
rRenderContext.DrawText(aPos, aText, 0, nLen);
|
|
|
|
// if IME info exists loop over portions and output different font attributes
|
|
if (mpIMEInfos && mpIMEInfos->pAttribs)
|
|
{
|
|
for(int n = 0; n < 2; n++)
|
|
{
|
|
vcl::Region aRegion;
|
|
if (n == 0)
|
|
{
|
|
rRenderContext.SetTextColor(aNormalTextColor);
|
|
if (IsPaintTransparent())
|
|
rRenderContext.SetTextFillColor();
|
|
else
|
|
rRenderContext.SetTextFillColor(IsControlBackground() ? GetControlBackground() : rStyleSettings.GetFieldColor());
|
|
aRegion = aNormalClipRegion;
|
|
}
|
|
else
|
|
{
|
|
rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor());
|
|
rRenderContext.SetTextFillColor(rStyleSettings.GetHighlightColor());
|
|
aRegion = aHiglightClipRegion;
|
|
}
|
|
|
|
for(i = 0; i < mpIMEInfos->nLen; )
|
|
{
|
|
sal_uInt16 nAttr = mpIMEInfos->pAttribs[i];
|
|
vcl::Region aClip;
|
|
int nIndex = i;
|
|
while (nIndex < mpIMEInfos->nLen && mpIMEInfos->pAttribs[nIndex] == nAttr) // #112631# check nIndex before using it
|
|
{
|
|
Rectangle aRect( aPos, Size( 10, nTH ) );
|
|
aRect.Left() = pDX[2 * (nIndex + mpIMEInfos->nPos)] + mnXOffset + ImplGetExtraXOffset();
|
|
aRect.Right() = pDX[2 * (nIndex + mpIMEInfos->nPos) + 1] + mnXOffset + ImplGetExtraXOffset();
|
|
aRect.Justify();
|
|
aClip.Union(aRect);
|
|
nIndex++;
|
|
}
|
|
i = nIndex;
|
|
aClip.Intersect(aRegion);
|
|
if (!aClip.IsEmpty() && nAttr)
|
|
{
|
|
vcl::Font aFont = rRenderContext.GetFont();
|
|
if (nAttr & EXTTEXTINPUT_ATTR_UNDERLINE)
|
|
aFont.SetUnderline(UNDERLINE_SINGLE);
|
|
else if (nAttr & EXTTEXTINPUT_ATTR_BOLDUNDERLINE)
|
|
aFont.SetUnderline( UNDERLINE_BOLD);
|
|
else if (nAttr & EXTTEXTINPUT_ATTR_DOTTEDUNDERLINE)
|
|
aFont.SetUnderline( UNDERLINE_DOTTED);
|
|
else if (nAttr & EXTTEXTINPUT_ATTR_DASHDOTUNDERLINE)
|
|
aFont.SetUnderline( UNDERLINE_DASHDOT);
|
|
else if (nAttr & EXTTEXTINPUT_ATTR_GRAYWAVELINE)
|
|
{
|
|
aFont.SetUnderline(UNDERLINE_WAVE);
|
|
rRenderContext.SetTextLineColor(Color(COL_LIGHTGRAY));
|
|
}
|
|
rRenderContext.SetFont(aFont);
|
|
|
|
if (nAttr & EXTTEXTINPUT_ATTR_REDTEXT)
|
|
rRenderContext.SetTextColor(Color(COL_RED));
|
|
else if (nAttr & EXTTEXTINPUT_ATTR_HALFTONETEXT)
|
|
rRenderContext.SetTextColor(Color(COL_LIGHTGRAY));
|
|
|
|
rRenderContext.SetClipRegion(aClip);
|
|
rRenderContext.DrawText(aPos, aText, 0, nLen);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// restore graphics state
|
|
rRenderContext.Pop();
|
|
}
|
|
|
|
if (bVisCursor && (!mpIMEInfos || mpIMEInfos->bCursor))
|
|
pCursor->Show();
|
|
}
|
|
|
|
void Edit::ImplDelete( const Selection& rSelection, sal_uInt8 nDirection, sal_uInt8 nMode )
|
|
{
|
|
OUString aText = ImplGetText();
|
|
|
|
// loeschen moeglich?
|
|
if ( !rSelection.Len() &&
|
|
(((rSelection.Min() == 0) && (nDirection == EDIT_DEL_LEFT)) ||
|
|
((rSelection.Max() == aText.getLength()) && (nDirection == EDIT_DEL_RIGHT))) )
|
|
return;
|
|
|
|
ImplClearLayoutData();
|
|
|
|
Selection aSelection( rSelection );
|
|
aSelection.Justify();
|
|
|
|
if ( !aSelection.Len() )
|
|
{
|
|
uno::Reference < i18n::XBreakIterator > xBI = ImplGetBreakIterator();
|
|
if ( nDirection == EDIT_DEL_LEFT )
|
|
{
|
|
if ( nMode == EDIT_DELMODE_RESTOFWORD )
|
|
{
|
|
i18n::Boundary aBoundary = xBI->getWordBoundary( maText.toString(), aSelection.Min(),
|
|
GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, true );
|
|
if ( aBoundary.startPos == aSelection.Min() )
|
|
aBoundary = xBI->previousWord( maText.toString(), aSelection.Min(),
|
|
GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
|
|
aSelection.Min() = aBoundary.startPos;
|
|
}
|
|
else if ( nMode == EDIT_DELMODE_RESTOFCONTENT )
|
|
{
|
|
aSelection.Min() = 0;
|
|
}
|
|
else
|
|
{
|
|
sal_Int32 nCount = 1;
|
|
aSelection.Min() = xBI->previousCharacters( maText.toString(), aSelection.Min(),
|
|
GetSettings().GetLanguageTag().getLocale(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( nMode == EDIT_DELMODE_RESTOFWORD )
|
|
{
|
|
i18n::Boundary aBoundary = xBI->nextWord( maText.toString(), aSelection.Max(),
|
|
GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
|
|
aSelection.Max() = aBoundary.startPos;
|
|
}
|
|
else if ( nMode == EDIT_DELMODE_RESTOFCONTENT )
|
|
{
|
|
aSelection.Max() = aText.getLength();
|
|
}
|
|
else
|
|
{
|
|
sal_Int32 nCount = 1;
|
|
aSelection.Max() = xBI->nextCharacters( maText.toString(), aSelection.Max(),
|
|
GetSettings().GetLanguageTag().getLocale(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount );
|
|
}
|
|
}
|
|
}
|
|
|
|
maText.remove( static_cast<sal_Int32>(aSelection.Min()), static_cast<sal_Int32>(aSelection.Len()) );
|
|
maSelection.Min() = aSelection.Min();
|
|
maSelection.Max() = aSelection.Min();
|
|
ImplAlignAndPaint();
|
|
mbInternModified = true;
|
|
}
|
|
|
|
OUString Edit::ImplGetValidString( const OUString& rString ) const
|
|
{
|
|
OUString aValidString( rString );
|
|
aValidString = comphelper::string::remove(aValidString, '\n');
|
|
aValidString = comphelper::string::remove(aValidString, '\r');
|
|
aValidString = aValidString.replace('\t', ' ');
|
|
return aValidString;
|
|
}
|
|
|
|
uno::Reference < i18n::XBreakIterator > Edit::ImplGetBreakIterator() const
|
|
{
|
|
//!! since we don't want to become incompatible in the next minor update
|
|
//!! where this code will get integrated into, xISC will be a local
|
|
//!! variable instead of a class member!
|
|
uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
|
|
return i18n::BreakIterator::create(xContext);
|
|
}
|
|
|
|
uno::Reference < i18n::XExtendedInputSequenceChecker > Edit::ImplGetInputSequenceChecker()
|
|
{
|
|
if ( !mxISC.is() )
|
|
{
|
|
mxISC = i18n::InputSequenceChecker::create(
|
|
::comphelper::getProcessComponentContext() );
|
|
}
|
|
return mxISC;
|
|
}
|
|
|
|
void Edit::ShowTruncationWarning( vcl::Window* pParent )
|
|
{
|
|
ResMgr* pResMgr = ImplGetResMgr();
|
|
if( pResMgr )
|
|
{
|
|
ScopedVclPtrInstance< MessageDialog > aBox( pParent, ResId(SV_EDIT_WARNING_STR, *pResMgr), VCL_MESSAGE_WARNING );
|
|
aBox->Execute();
|
|
}
|
|
}
|
|
|
|
bool Edit::ImplTruncateToMaxLen( OUString& rStr, sal_Int32 nSelectionLen ) const
|
|
{
|
|
bool bWasTruncated = false;
|
|
if (maText.getLength() - nSelectionLen > mnMaxTextLen - rStr.getLength())
|
|
{
|
|
sal_Int32 nErasePos = mnMaxTextLen - maText.getLength() + nSelectionLen;
|
|
rStr = rStr.copy( 0, nErasePos );
|
|
bWasTruncated = true;
|
|
}
|
|
return bWasTruncated;
|
|
}
|
|
|
|
void Edit::ImplInsertText( const OUString& rStr, const Selection* pNewSel, bool bIsUserInput )
|
|
{
|
|
Selection aSelection( maSelection );
|
|
aSelection.Justify();
|
|
|
|
OUString aNewText( ImplGetValidString( rStr ) );
|
|
ImplTruncateToMaxLen( aNewText, aSelection.Len() );
|
|
|
|
ImplClearLayoutData();
|
|
|
|
if ( aSelection.Len() )
|
|
maText.remove( static_cast<sal_Int32>(aSelection.Min()), static_cast<sal_Int32>(aSelection.Len()) );
|
|
else if ( !mbInsertMode && (aSelection.Max() < maText.getLength()) )
|
|
maText.remove( static_cast<sal_Int32>(aSelection.Max()), 1 );
|
|
|
|
// take care of input-sequence-checking now
|
|
if (bIsUserInput && !rStr.isEmpty())
|
|
{
|
|
DBG_ASSERT( rStr.getLength() == 1, "unexpected string length. User input is expected to providse 1 char only!" );
|
|
|
|
// determine if input-sequence-checking should be applied or not
|
|
|
|
uno::Reference < i18n::XBreakIterator > xBI( ImplGetBreakIterator(), UNO_QUERY );
|
|
bool bIsInputSequenceChecking = rStr.getLength() == 1 &&
|
|
officecfg::Office::Common::I18N::CTL::CTLFont::get() &&
|
|
officecfg::Office::Common::I18N::CTL::CTLSequenceChecking::get() &&
|
|
aSelection.Min() > 0 && /* first char needs not to be checked */
|
|
xBI.is() && i18n::ScriptType::COMPLEX == xBI->getScriptType( rStr, 0 );
|
|
|
|
uno::Reference < i18n::XExtendedInputSequenceChecker > xISC;
|
|
if (bIsInputSequenceChecking && (xISC = ImplGetInputSequenceChecker()).is())
|
|
{
|
|
sal_Unicode cChar = rStr[0];
|
|
sal_Int32 nTmpPos = static_cast< sal_Int32 >( aSelection.Min() );
|
|
sal_Int16 nCheckMode = officecfg::Office::Common::I18N::CTL::CTLSequenceCheckingRestricted::get()?
|
|
i18n::InputSequenceCheckMode::STRICT : i18n::InputSequenceCheckMode::BASIC;
|
|
|
|
// the text that needs to be checked is only the one
|
|
// before the current cursor position
|
|
OUString aOldText( maText.getStr(), nTmpPos);
|
|
OUString aTmpText( aOldText );
|
|
if (officecfg::Office::Common::I18N::CTL::CTLSequenceCheckingTypeAndReplace::get())
|
|
{
|
|
xISC->correctInputSequence( aTmpText, nTmpPos - 1, cChar, nCheckMode );
|
|
|
|
// find position of first character that has changed
|
|
sal_Int32 nOldLen = aOldText.getLength();
|
|
sal_Int32 nTmpLen = aTmpText.getLength();
|
|
const sal_Unicode *pOldTxt = aOldText.getStr();
|
|
const sal_Unicode *pTmpTxt = aTmpText.getStr();
|
|
sal_Int32 nChgPos = 0;
|
|
while ( nChgPos < nOldLen && nChgPos < nTmpLen &&
|
|
pOldTxt[nChgPos] == pTmpTxt[nChgPos] )
|
|
++nChgPos;
|
|
|
|
OUString aChgText( aTmpText.copy( nChgPos ) );
|
|
|
|
// remove text from first pos to be changed to current pos
|
|
maText.remove( nChgPos, nTmpPos - nChgPos );
|
|
|
|
if (!aChgText.isEmpty())
|
|
{
|
|
aNewText = aChgText;
|
|
aSelection.Min() = nChgPos; // position for new text to be inserted
|
|
}
|
|
else
|
|
aNewText.clear();
|
|
}
|
|
else
|
|
{
|
|
// should the character be ignored (i.e. not get inserted) ?
|
|
if (!xISC->checkInputSequence( aOldText, nTmpPos - 1, cChar, nCheckMode ))
|
|
aNewText.clear();
|
|
}
|
|
}
|
|
|
|
// at this point now we will insert the non-empty text 'normally' some lines below...
|
|
}
|
|
|
|
if ( !aNewText.isEmpty() )
|
|
maText.insert( static_cast<sal_Int32>(aSelection.Min()), aNewText );
|
|
|
|
if ( !pNewSel )
|
|
{
|
|
maSelection.Min() = aSelection.Min() + aNewText.getLength();
|
|
maSelection.Max() = maSelection.Min();
|
|
}
|
|
else
|
|
{
|
|
maSelection = *pNewSel;
|
|
if ( maSelection.Min() > maText.getLength() )
|
|
maSelection.Min() = maText.getLength();
|
|
if ( maSelection.Max() > maText.getLength() )
|
|
maSelection.Max() = maText.getLength();
|
|
}
|
|
|
|
ImplAlignAndPaint();
|
|
mbInternModified = true;
|
|
}
|
|
|
|
void Edit::ImplSetText( const OUString& rText, const Selection* pNewSelection )
|
|
{
|
|
// we delete text by "selecting" the old text completely then calling InsertText; this is flicker free
|
|
if ( ( rText.getLength() <= mnMaxTextLen ) &&
|
|
( (rText != maText.getStr()) || (pNewSelection && (*pNewSelection != maSelection)) ) )
|
|
{
|
|
ImplClearLayoutData();
|
|
maSelection.Min() = 0;
|
|
maSelection.Max() = maText.getLength();
|
|
if ( mnXOffset || HasPaintEvent() )
|
|
{
|
|
mnXOffset = 0;
|
|
maText = ImplGetValidString( rText );
|
|
|
|
// #i54929# recalculate mnXOffset before ImplSetSelection,
|
|
// else cursor ends up in wrong position
|
|
ImplAlign();
|
|
|
|
if ( pNewSelection )
|
|
ImplSetSelection( *pNewSelection, false );
|
|
|
|
if ( mnXOffset && !pNewSelection )
|
|
maSelection.Max() = 0;
|
|
|
|
Invalidate();
|
|
}
|
|
else
|
|
ImplInsertText( rText, pNewSelection );
|
|
|
|
CallEventListeners( VCLEVENT_EDIT_MODIFY );
|
|
}
|
|
}
|
|
|
|
int Edit::ImplGetNativeControlType() const
|
|
{
|
|
int nCtrl = 0;
|
|
const vcl::Window* pControl = mbIsSubEdit ? GetParent() : this;
|
|
|
|
switch (pControl->GetType())
|
|
{
|
|
case WINDOW_COMBOBOX:
|
|
case WINDOW_PATTERNBOX:
|
|
case WINDOW_NUMERICBOX:
|
|
case WINDOW_METRICBOX:
|
|
case WINDOW_CURRENCYBOX:
|
|
case WINDOW_DATEBOX:
|
|
case WINDOW_TIMEBOX:
|
|
case WINDOW_LONGCURRENCYBOX:
|
|
nCtrl = CTRL_COMBOBOX;
|
|
break;
|
|
|
|
case WINDOW_MULTILINEEDIT:
|
|
if ( GetWindow( GetWindowType::Border ) != this )
|
|
nCtrl = CTRL_MULTILINE_EDITBOX;
|
|
else
|
|
nCtrl = CTRL_EDITBOX_NOBORDER;
|
|
break;
|
|
|
|
case WINDOW_EDIT:
|
|
case WINDOW_PATTERNFIELD:
|
|
case WINDOW_METRICFIELD:
|
|
case WINDOW_CURRENCYFIELD:
|
|
case WINDOW_DATEFIELD:
|
|
case WINDOW_TIMEFIELD:
|
|
case WINDOW_LONGCURRENCYFIELD:
|
|
case WINDOW_NUMERICFIELD:
|
|
case WINDOW_SPINFIELD:
|
|
if (pControl->GetStyle() & WB_SPIN)
|
|
nCtrl = CTRL_SPINBOX;
|
|
else
|
|
{
|
|
if (GetWindow(GetWindowType::Border) != this)
|
|
nCtrl = CTRL_EDITBOX;
|
|
else
|
|
nCtrl = CTRL_EDITBOX_NOBORDER;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
nCtrl = CTRL_EDITBOX;
|
|
}
|
|
return nCtrl;
|
|
}
|
|
|
|
void Edit::ImplClearBackground(vcl::RenderContext& rRenderContext, const Rectangle& rRectangle, long nXStart, long nXEnd )
|
|
{
|
|
/*
|
|
* note: at this point the cursor must be switched off already
|
|
*/
|
|
Point aTmpPoint;
|
|
Rectangle aRect(aTmpPoint, GetOutputSizePixel());
|
|
aRect.Left() = nXStart;
|
|
aRect.Right() = nXEnd;
|
|
|
|
if( !(ImplUseNativeBorder(rRenderContext, GetStyle()) || IsPaintTransparent()))
|
|
rRenderContext.Erase(aRect);
|
|
else if (SupportsDoubleBuffering() && mbIsSubEdit)
|
|
{
|
|
// ImplPaintBorder() is a NOP, we have a native border, and this is a sub-edit of a control.
|
|
// That means we have to draw the parent native widget to paint the edit area to clear our background.
|
|
PaintBufferGuard g(ImplGetWindowImpl()->mpFrameData, GetParent());
|
|
GetParent()->Paint(rRenderContext, rRectangle);
|
|
}
|
|
}
|
|
|
|
void Edit::ImplPaintBorder(vcl::RenderContext& rRenderContext, long nXStart, long nXEnd)
|
|
{
|
|
// this is not needed when double-buffering
|
|
if (SupportsDoubleBuffering())
|
|
return;
|
|
|
|
Point aTmpPoint;
|
|
Rectangle aRect(aTmpPoint, GetOutputSizePixel());
|
|
aRect.Left() = nXStart;
|
|
aRect.Right() = nXEnd;
|
|
|
|
if (ImplUseNativeBorder(rRenderContext, GetStyle()) || IsPaintTransparent())
|
|
{
|
|
// draw the inner part by painting the whole control using its border window
|
|
vcl::Window* pBorder = GetWindow(GetWindowType::Border);
|
|
if (pBorder == this)
|
|
{
|
|
// we have no border, use parent
|
|
vcl::Window* pControl = mbIsSubEdit ? GetParent() : this;
|
|
pBorder = pControl->GetWindow(GetWindowType::Border);
|
|
if (pBorder == this)
|
|
pBorder = GetParent();
|
|
}
|
|
|
|
if (pBorder)
|
|
{
|
|
// set proper clipping region to not overdraw the whole control
|
|
vcl::Region aClipRgn = GetPaintRegion();
|
|
if (!aClipRgn.IsNull())
|
|
{
|
|
// transform clipping region to border window's coordinate system
|
|
if (IsRTLEnabled() != pBorder->IsRTLEnabled() && AllSettings::GetLayoutRTL())
|
|
{
|
|
// need to mirror in case border is not RTL but edit is (or vice versa)
|
|
|
|
// mirror
|
|
Rectangle aBounds(aClipRgn.GetBoundRect());
|
|
int xNew = GetOutputSizePixel().Width() - aBounds.GetWidth() - aBounds.Left();
|
|
aClipRgn.Move(xNew - aBounds.Left(), 0);
|
|
|
|
// move offset of border window
|
|
Point aBorderOffs;
|
|
aBorderOffs = pBorder->ScreenToOutputPixel(OutputToScreenPixel(aBorderOffs));
|
|
aClipRgn.Move(aBorderOffs.X(), aBorderOffs.Y());
|
|
}
|
|
else
|
|
{
|
|
// normal case
|
|
Point aBorderOffs;
|
|
aBorderOffs = pBorder->ScreenToOutputPixel(OutputToScreenPixel(aBorderOffs));
|
|
aClipRgn.Move(aBorderOffs.X(), aBorderOffs.Y());
|
|
}
|
|
|
|
vcl::Region oldRgn(pBorder->GetClipRegion());
|
|
pBorder->SetClipRegion(aClipRgn);
|
|
|
|
pBorder->Paint(*pBorder, Rectangle());
|
|
|
|
pBorder->SetClipRegion(oldRgn);
|
|
}
|
|
else
|
|
{
|
|
pBorder->Paint(*pBorder, Rectangle());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Edit::ImplShowCursor( bool bOnlyIfVisible )
|
|
{
|
|
if ( !IsUpdateMode() || ( bOnlyIfVisible && !IsReallyVisible() ) )
|
|
return;
|
|
|
|
vcl::Cursor* pCursor = GetCursor();
|
|
OUString aText = ImplGetText();
|
|
|
|
long nTextPos = 0;
|
|
|
|
long nDXBuffer[256];
|
|
std::unique_ptr<long[]> pDXBuffer;
|
|
long* pDX = nDXBuffer;
|
|
|
|
if( !aText.isEmpty() )
|
|
{
|
|
if( (size_t) (2*aText.getLength()) > SAL_N_ELEMENTS(nDXBuffer) )
|
|
{
|
|
pDXBuffer.reset(new long[2*(aText.getLength()+1)]);
|
|
pDX = pDXBuffer.get();
|
|
}
|
|
|
|
GetCaretPositions( aText, pDX, 0, aText.getLength() );
|
|
|
|
if( maSelection.Max() < aText.getLength() )
|
|
nTextPos = pDX[ 2*maSelection.Max() ];
|
|
else
|
|
nTextPos = pDX[ 2*aText.getLength()-1 ];
|
|
}
|
|
|
|
long nCursorWidth = 0;
|
|
if ( !mbInsertMode && !maSelection.Len() && (maSelection.Max() < aText.getLength()) )
|
|
nCursorWidth = GetTextWidth(aText, maSelection.Max(), 1);
|
|
long nCursorPosX = nTextPos + mnXOffset + ImplGetExtraXOffset();
|
|
|
|
// cursor should land in visible area
|
|
const Size aOutSize = GetOutputSizePixel();
|
|
if ( (nCursorPosX < 0) || (nCursorPosX >= aOutSize.Width()) )
|
|
{
|
|
long nOldXOffset = mnXOffset;
|
|
|
|
if ( nCursorPosX < 0 )
|
|
{
|
|
mnXOffset = - nTextPos;
|
|
long nMaxX = 0;
|
|
mnXOffset += aOutSize.Width() / 5;
|
|
if ( mnXOffset > nMaxX )
|
|
mnXOffset = nMaxX;
|
|
}
|
|
else
|
|
{
|
|
mnXOffset = (aOutSize.Width()-ImplGetExtraXOffset()) - nTextPos;
|
|
// Etwas mehr?
|
|
if ( (aOutSize.Width()-ImplGetExtraXOffset()) < nTextPos )
|
|
{
|
|
long nMaxNegX = (aOutSize.Width()-ImplGetExtraXOffset()) - GetTextWidth( aText );
|
|
mnXOffset -= aOutSize.Width() / 5;
|
|
if ( mnXOffset < nMaxNegX ) // beides negativ...
|
|
mnXOffset = nMaxNegX;
|
|
}
|
|
}
|
|
|
|
nCursorPosX = nTextPos + mnXOffset + ImplGetExtraXOffset();
|
|
if ( nCursorPosX == aOutSize.Width() ) // dann nicht sichtbar...
|
|
nCursorPosX--;
|
|
|
|
if ( mnXOffset != nOldXOffset )
|
|
ImplInvalidateOrRepaint();
|
|
}
|
|
|
|
const long nTextHeight = GetTextHeight();
|
|
const long nCursorPosY = ImplGetTextYPosition();
|
|
if (pCursor)
|
|
{
|
|
pCursor->SetPos( Point( nCursorPosX, nCursorPosY ) );
|
|
pCursor->SetSize( Size( nCursorWidth, nTextHeight ) );
|
|
pCursor->Show();
|
|
}
|
|
}
|
|
|
|
void Edit::ImplAlign()
|
|
{
|
|
long nTextWidth = GetTextWidth( ImplGetText() );
|
|
long nOutWidth = GetOutputSizePixel().Width();
|
|
|
|
if ( mnAlign == EDIT_ALIGN_LEFT )
|
|
{
|
|
if( mnXOffset && ( nTextWidth < nOutWidth ) )
|
|
mnXOffset = 0;
|
|
|
|
}
|
|
else if ( mnAlign == EDIT_ALIGN_RIGHT )
|
|
{
|
|
long nMinXOffset = nOutWidth - nTextWidth - 1 - ImplGetExtraXOffset();
|
|
bool bRTL = IsRTLEnabled();
|
|
if( mbIsSubEdit && GetParent() )
|
|
bRTL = GetParent()->IsRTLEnabled();
|
|
if( bRTL )
|
|
{
|
|
if( nTextWidth < nOutWidth )
|
|
mnXOffset = nMinXOffset;
|
|
}
|
|
else
|
|
{
|
|
if( nTextWidth < nOutWidth )
|
|
mnXOffset = nMinXOffset;
|
|
else if ( mnXOffset < nMinXOffset )
|
|
mnXOffset = nMinXOffset;
|
|
}
|
|
}
|
|
else if( mnAlign == EDIT_ALIGN_CENTER )
|
|
{
|
|
// would be nicer with check while scrolling but then it's not centred in scrolled state
|
|
mnXOffset = (nOutWidth - nTextWidth) / 2;
|
|
}
|
|
}
|
|
|
|
void Edit::ImplAlignAndPaint()
|
|
{
|
|
ImplAlign();
|
|
ImplInvalidateOrRepaint();
|
|
ImplShowCursor();
|
|
}
|
|
|
|
sal_Int32 Edit::ImplGetCharPos( const Point& rWindowPos ) const
|
|
{
|
|
sal_Int32 nIndex = EDIT_NOLIMIT;
|
|
OUString aText = ImplGetText();
|
|
|
|
long nDXBuffer[256];
|
|
std::unique_ptr<long[]> pDXBuffer;
|
|
long* pDX = nDXBuffer;
|
|
if( (size_t) (2*aText.getLength()) > SAL_N_ELEMENTS(nDXBuffer) )
|
|
{
|
|
pDXBuffer.reset(new long[2*(aText.getLength()+1)]);
|
|
pDX = pDXBuffer.get();
|
|
}
|
|
|
|
GetCaretPositions( aText, pDX, 0, aText.getLength() );
|
|
long nX = rWindowPos.X() - mnXOffset - ImplGetExtraXOffset();
|
|
for( sal_Int32 i = 0; i < aText.getLength(); i++ )
|
|
{
|
|
if( (pDX[2*i] >= nX && pDX[2*i+1] <= nX) ||
|
|
(pDX[2*i+1] >= nX && pDX[2*i] <= nX))
|
|
{
|
|
nIndex = i;
|
|
if( pDX[2*i] < pDX[2*i+1] )
|
|
{
|
|
if( nX > (pDX[2*i]+pDX[2*i+1])/2 )
|
|
nIndex++;
|
|
}
|
|
else
|
|
{
|
|
if( nX < (pDX[2*i]+pDX[2*i+1])/2 )
|
|
nIndex++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if( nIndex == EDIT_NOLIMIT )
|
|
{
|
|
nIndex = 0;
|
|
long nDiff = std::abs( pDX[0]-nX );
|
|
for( sal_Int32 i = 1; i < aText.getLength(); i++ )
|
|
{
|
|
long nNewDiff = std::abs( pDX[2*i]-nX );
|
|
|
|
if( nNewDiff < nDiff )
|
|
{
|
|
nIndex = i;
|
|
nDiff = nNewDiff;
|
|
}
|
|
}
|
|
if( nIndex == aText.getLength()-1 && std::abs( pDX[2*nIndex+1] - nX ) < nDiff )
|
|
nIndex = EDIT_NOLIMIT;
|
|
}
|
|
|
|
return nIndex;
|
|
}
|
|
|
|
void Edit::ImplSetCursorPos( sal_Int32 nChar, bool bSelect )
|
|
{
|
|
Selection aSelection( maSelection );
|
|
aSelection.Max() = nChar;
|
|
if ( !bSelect )
|
|
aSelection.Min() = aSelection.Max();
|
|
ImplSetSelection( aSelection );
|
|
}
|
|
|
|
void Edit::ImplLoadRes( const ResId& rResId )
|
|
{
|
|
Control::ImplLoadRes( rResId );
|
|
|
|
sal_uInt16 nTextLength = ReadShortRes();
|
|
if ( nTextLength )
|
|
SetMaxTextLen( nTextLength );
|
|
}
|
|
|
|
void Edit::ImplCopyToSelectionClipboard()
|
|
{
|
|
if ( GetSelection().Len() )
|
|
{
|
|
::com::sun::star::uno::Reference<com::sun::star::datatransfer::clipboard::XClipboard> aSelection(GetPrimarySelection());
|
|
ImplCopy( aSelection );
|
|
}
|
|
}
|
|
|
|
void Edit::ImplCopy( uno::Reference< datatransfer::clipboard::XClipboard >& rxClipboard )
|
|
{
|
|
vcl::unohelper::TextDataObject::CopyStringTo( GetSelected(), rxClipboard );
|
|
}
|
|
|
|
void Edit::ImplPaste( uno::Reference< datatransfer::clipboard::XClipboard >& rxClipboard )
|
|
{
|
|
if ( rxClipboard.is() )
|
|
{
|
|
uno::Reference< datatransfer::XTransferable > xDataObj;
|
|
|
|
try
|
|
{
|
|
SolarMutexReleaser aReleaser;
|
|
xDataObj = rxClipboard->getContents();
|
|
}
|
|
catch( const ::com::sun::star::uno::Exception& )
|
|
{
|
|
}
|
|
|
|
if ( xDataObj.is() )
|
|
{
|
|
datatransfer::DataFlavor aFlavor;
|
|
SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor );
|
|
try
|
|
{
|
|
uno::Any aData = xDataObj->getTransferData( aFlavor );
|
|
OUString aText;
|
|
aData >>= aText;
|
|
if( ImplTruncateToMaxLen( aText, maSelection.Len() ) )
|
|
ShowTruncationWarning( this );
|
|
ReplaceSelected( aText );
|
|
}
|
|
catch( const ::com::sun::star::uno::Exception& )
|
|
{
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Edit::MouseButtonDown( const MouseEvent& rMEvt )
|
|
{
|
|
if ( mpSubEdit )
|
|
{
|
|
Control::MouseButtonDown( rMEvt );
|
|
return;
|
|
}
|
|
|
|
sal_Int32 nCharPos = ImplGetCharPos( rMEvt.GetPosPixel() );
|
|
Selection aSelection( maSelection );
|
|
aSelection.Justify();
|
|
|
|
if ( rMEvt.GetClicks() < 4 )
|
|
{
|
|
mbClickedInSelection = false;
|
|
if ( rMEvt.GetClicks() == 3 )
|
|
{
|
|
ImplSetSelection( Selection( 0, EDIT_NOLIMIT) );
|
|
ImplCopyToSelectionClipboard();
|
|
|
|
}
|
|
else if ( rMEvt.GetClicks() == 2 )
|
|
{
|
|
uno::Reference < i18n::XBreakIterator > xBI = ImplGetBreakIterator();
|
|
i18n::Boundary aBoundary = xBI->getWordBoundary( maText.toString(), aSelection.Max(),
|
|
GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, true );
|
|
ImplSetSelection( Selection( aBoundary.startPos, aBoundary.endPos ) );
|
|
ImplCopyToSelectionClipboard();
|
|
}
|
|
else if ( !rMEvt.IsShift() && HasFocus() && aSelection.IsInside( nCharPos ) )
|
|
mbClickedInSelection = true;
|
|
else if ( rMEvt.IsLeft() )
|
|
ImplSetCursorPos( nCharPos, rMEvt.IsShift() );
|
|
|
|
if ( !mbClickedInSelection && rMEvt.IsLeft() && ( rMEvt.GetClicks() == 1 ) )
|
|
StartTracking( StartTrackingFlags::ScrollRepeat );
|
|
}
|
|
|
|
mbInMBDown = true; // then do not select all in GetFocus
|
|
GrabFocus();
|
|
mbInMBDown = false;
|
|
}
|
|
|
|
void Edit::MouseButtonUp( const MouseEvent& rMEvt )
|
|
{
|
|
if ( mbClickedInSelection && rMEvt.IsLeft() )
|
|
{
|
|
sal_Int32 nCharPos = ImplGetCharPos( rMEvt.GetPosPixel() );
|
|
ImplSetCursorPos( nCharPos, false );
|
|
mbClickedInSelection = false;
|
|
}
|
|
else if ( rMEvt.IsMiddle() && !mbReadOnly &&
|
|
( GetSettings().GetMouseSettings().GetMiddleButtonAction() == MouseMiddleButtonAction::PasteSelection ) )
|
|
{
|
|
::com::sun::star::uno::Reference<com::sun::star::datatransfer::clipboard::XClipboard> aSelection(Window::GetPrimarySelection());
|
|
ImplPaste( aSelection );
|
|
ImplModified();
|
|
}
|
|
}
|
|
|
|
void Edit::Tracking( const TrackingEvent& rTEvt )
|
|
{
|
|
if ( rTEvt.IsTrackingEnded() )
|
|
{
|
|
if ( mbClickedInSelection )
|
|
{
|
|
sal_Int32 nCharPos = ImplGetCharPos( rTEvt.GetMouseEvent().GetPosPixel() );
|
|
ImplSetCursorPos( nCharPos, false );
|
|
mbClickedInSelection = false;
|
|
}
|
|
else if ( rTEvt.GetMouseEvent().IsLeft() )
|
|
{
|
|
ImplCopyToSelectionClipboard();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( !mbClickedInSelection )
|
|
{
|
|
sal_Int32 nCharPos = ImplGetCharPos( rTEvt.GetMouseEvent().GetPosPixel() );
|
|
ImplSetCursorPos( nCharPos, true );
|
|
}
|
|
}
|
|
|
|
if ( mpUpdateDataTimer && !mbIsSubEdit && mpUpdateDataTimer->IsActive() )
|
|
mpUpdateDataTimer->Start();//do not update while the user is still travelling in the control
|
|
}
|
|
|
|
bool Edit::ImplHandleKeyEvent( const KeyEvent& rKEvt )
|
|
{
|
|
bool bDone = false;
|
|
sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
|
|
KeyFuncType eFunc = rKEvt.GetKeyCode().GetFunction();
|
|
|
|
mbInternModified = false;
|
|
|
|
if ( eFunc != KeyFuncType::DONTKNOW )
|
|
{
|
|
switch ( eFunc )
|
|
{
|
|
case KeyFuncType::CUT:
|
|
{
|
|
if ( !mbReadOnly && maSelection.Len() && !(GetStyle() & WB_PASSWORD) )
|
|
{
|
|
Cut();
|
|
ImplModified();
|
|
bDone = true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case KeyFuncType::COPY:
|
|
{
|
|
if ( !(GetStyle() & WB_PASSWORD) )
|
|
{
|
|
Copy();
|
|
bDone = true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case KeyFuncType::PASTE:
|
|
{
|
|
if ( !mbReadOnly )
|
|
{
|
|
Paste();
|
|
bDone = true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case KeyFuncType::UNDO:
|
|
{
|
|
if ( !mbReadOnly )
|
|
{
|
|
Undo();
|
|
bDone = true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
eFunc = KeyFuncType::DONTKNOW;
|
|
}
|
|
}
|
|
|
|
if ( !bDone && rKEvt.GetKeyCode().IsMod1() && !rKEvt.GetKeyCode().IsMod2() )
|
|
{
|
|
if ( nCode == KEY_A )
|
|
{
|
|
ImplSetSelection( Selection( 0, maText.getLength() ) );
|
|
bDone = true;
|
|
}
|
|
else if ( rKEvt.GetKeyCode().IsShift() && (nCode == KEY_S) )
|
|
{
|
|
if ( pImplFncGetSpecialChars )
|
|
{
|
|
Selection aSaveSel = GetSelection(); // if someone changes the selection in Get/LoseFocus, e.g. URL bar
|
|
OUString aChars = pImplFncGetSpecialChars( this, GetFont() );
|
|
SetSelection( aSaveSel );
|
|
if ( !aChars.isEmpty() )
|
|
{
|
|
ImplInsertText( aChars );
|
|
ImplModified();
|
|
}
|
|
bDone = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( eFunc == KeyFuncType::DONTKNOW && ! bDone )
|
|
{
|
|
switch ( nCode )
|
|
{
|
|
case com::sun::star::awt::Key::SELECT_ALL:
|
|
{
|
|
ImplSetSelection( Selection( 0, maText.getLength() ) );
|
|
bDone = true;
|
|
}
|
|
break;
|
|
|
|
case KEY_LEFT:
|
|
case KEY_RIGHT:
|
|
case KEY_HOME:
|
|
case KEY_END:
|
|
case com::sun::star::awt::Key::MOVE_WORD_FORWARD:
|
|
case com::sun::star::awt::Key::SELECT_WORD_FORWARD:
|
|
case com::sun::star::awt::Key::MOVE_WORD_BACKWARD:
|
|
case com::sun::star::awt::Key::SELECT_WORD_BACKWARD:
|
|
case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_LINE:
|
|
case com::sun::star::awt::Key::MOVE_TO_END_OF_LINE:
|
|
case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_LINE:
|
|
case com::sun::star::awt::Key::SELECT_TO_END_OF_LINE:
|
|
case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH:
|
|
case com::sun::star::awt::Key::MOVE_TO_END_OF_PARAGRAPH:
|
|
case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH:
|
|
case com::sun::star::awt::Key::SELECT_TO_END_OF_PARAGRAPH:
|
|
case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT:
|
|
case com::sun::star::awt::Key::MOVE_TO_END_OF_DOCUMENT:
|
|
case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT:
|
|
case com::sun::star::awt::Key::SELECT_TO_END_OF_DOCUMENT:
|
|
{
|
|
if ( !rKEvt.GetKeyCode().IsMod2() )
|
|
{
|
|
ImplClearLayoutData();
|
|
uno::Reference < i18n::XBreakIterator > xBI = ImplGetBreakIterator();
|
|
|
|
Selection aSel( maSelection );
|
|
bool bWord = rKEvt.GetKeyCode().IsMod1();
|
|
bool bSelect = rKEvt.GetKeyCode().IsShift();
|
|
bool bGoLeft = (nCode == KEY_LEFT);
|
|
bool bGoRight = (nCode == KEY_RIGHT);
|
|
bool bGoHome = (nCode == KEY_HOME);
|
|
bool bGoEnd = (nCode == KEY_END);
|
|
|
|
switch( nCode )
|
|
{
|
|
case com::sun::star::awt::Key::MOVE_WORD_FORWARD:
|
|
bGoRight = bWord = true;break;
|
|
case com::sun::star::awt::Key::SELECT_WORD_FORWARD:
|
|
bGoRight = bSelect = bWord = true;break;
|
|
case com::sun::star::awt::Key::MOVE_WORD_BACKWARD:
|
|
bGoLeft = bWord = true;break;
|
|
case com::sun::star::awt::Key::SELECT_WORD_BACKWARD:
|
|
bGoLeft = bSelect = bWord = true;break;
|
|
case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_LINE:
|
|
case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH:
|
|
case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT:
|
|
bSelect = true;
|
|
// fallthrough intended
|
|
case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_LINE:
|
|
case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH:
|
|
case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT:
|
|
bGoHome = true;break;
|
|
case com::sun::star::awt::Key::SELECT_TO_END_OF_LINE:
|
|
case com::sun::star::awt::Key::SELECT_TO_END_OF_PARAGRAPH:
|
|
case com::sun::star::awt::Key::SELECT_TO_END_OF_DOCUMENT:
|
|
bSelect = true;
|
|
// fallthrough intended
|
|
case com::sun::star::awt::Key::MOVE_TO_END_OF_LINE:
|
|
case com::sun::star::awt::Key::MOVE_TO_END_OF_PARAGRAPH:
|
|
case com::sun::star::awt::Key::MOVE_TO_END_OF_DOCUMENT:
|
|
bGoEnd = true;break;
|
|
default:
|
|
break;
|
|
};
|
|
|
|
// Range wird in ImplSetSelection geprueft...
|
|
if ( bGoLeft && aSel.Max() )
|
|
{
|
|
if ( bWord )
|
|
{
|
|
i18n::Boundary aBoundary = xBI->getWordBoundary( maText.toString(), aSel.Max(),
|
|
GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, true );
|
|
if ( aBoundary.startPos == aSel.Max() )
|
|
aBoundary = xBI->previousWord( maText.toString(), aSel.Max(),
|
|
GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
|
|
aSel.Max() = aBoundary.startPos;
|
|
}
|
|
else
|
|
{
|
|
sal_Int32 nCount = 1;
|
|
aSel.Max() = xBI->previousCharacters( maText.toString(), aSel.Max(),
|
|
GetSettings().GetLanguageTag().getLocale(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount );
|
|
}
|
|
}
|
|
else if ( bGoRight && ( aSel.Max() < maText.getLength() ) )
|
|
{
|
|
if ( bWord )
|
|
{
|
|
i18n::Boundary aBoundary = xBI->nextWord( maText.toString(), aSel.Max(),
|
|
GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
|
|
aSel.Max() = aBoundary.startPos;
|
|
}
|
|
else
|
|
{
|
|
sal_Int32 nCount = 1;
|
|
aSel.Max() = xBI->nextCharacters( maText.toString(), aSel.Max(),
|
|
GetSettings().GetLanguageTag().getLocale(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount );
|
|
}
|
|
}
|
|
else if ( bGoHome )
|
|
{
|
|
aSel.Max() = 0;
|
|
}
|
|
else if ( bGoEnd )
|
|
{
|
|
aSel.Max() = EDIT_NOLIMIT;
|
|
}
|
|
|
|
if ( !bSelect )
|
|
aSel.Min() = aSel.Max();
|
|
|
|
if ( aSel != GetSelection() )
|
|
{
|
|
ImplSetSelection( aSel );
|
|
ImplCopyToSelectionClipboard();
|
|
}
|
|
|
|
if (bGoEnd && !m_pImpl->m_AutocompleteSignal.empty() && !rKEvt.GetKeyCode().GetModifier())
|
|
{
|
|
if ( (maSelection.Min() == maSelection.Max()) && (maSelection.Min() == maText.getLength()) )
|
|
{
|
|
meAutocompleteAction = AUTOCOMPLETE_KEYINPUT;
|
|
m_pImpl->m_AutocompleteSignal( this );
|
|
}
|
|
}
|
|
|
|
bDone = true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case com::sun::star::awt::Key::DELETE_WORD_BACKWARD:
|
|
case com::sun::star::awt::Key::DELETE_WORD_FORWARD:
|
|
case com::sun::star::awt::Key::DELETE_TO_BEGIN_OF_LINE:
|
|
case com::sun::star::awt::Key::DELETE_TO_END_OF_LINE:
|
|
case KEY_BACKSPACE:
|
|
case KEY_DELETE:
|
|
{
|
|
if ( !mbReadOnly && !rKEvt.GetKeyCode().IsMod2() )
|
|
{
|
|
sal_uInt8 nDel = (nCode == KEY_DELETE) ? EDIT_DEL_RIGHT : EDIT_DEL_LEFT;
|
|
sal_uInt8 nMode = rKEvt.GetKeyCode().IsMod1() ? EDIT_DELMODE_RESTOFWORD : EDIT_DELMODE_SIMPLE;
|
|
if ( (nMode == EDIT_DELMODE_RESTOFWORD) && rKEvt.GetKeyCode().IsShift() )
|
|
nMode = EDIT_DELMODE_RESTOFCONTENT;
|
|
switch( nCode )
|
|
{
|
|
case com::sun::star::awt::Key::DELETE_WORD_BACKWARD:
|
|
nDel = EDIT_DEL_LEFT;
|
|
nMode = EDIT_DELMODE_RESTOFWORD;
|
|
break;
|
|
case com::sun::star::awt::Key::DELETE_WORD_FORWARD:
|
|
nDel = EDIT_DEL_RIGHT;
|
|
nMode = EDIT_DELMODE_RESTOFWORD;
|
|
break;
|
|
case com::sun::star::awt::Key::DELETE_TO_BEGIN_OF_LINE:
|
|
nDel = EDIT_DEL_LEFT;
|
|
nMode = EDIT_DELMODE_RESTOFCONTENT;
|
|
break;
|
|
case com::sun::star::awt::Key::DELETE_TO_END_OF_LINE:
|
|
nDel = EDIT_DEL_RIGHT;
|
|
nMode = EDIT_DELMODE_RESTOFCONTENT;
|
|
break;
|
|
default: break;
|
|
}
|
|
sal_Int32 nOldLen = maText.getLength();
|
|
ImplDelete( maSelection, nDel, nMode );
|
|
if ( maText.getLength() != nOldLen )
|
|
ImplModified();
|
|
bDone = true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case KEY_INSERT:
|
|
{
|
|
if ( !mpIMEInfos && !mbReadOnly && !rKEvt.GetKeyCode().IsMod2() )
|
|
{
|
|
SetInsertMode( !mbInsertMode );
|
|
bDone = true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* #i101255# disable autocomplete tab forward/backward
|
|
users expect tab/shift-tab to move the focus to other controls
|
|
not suddenly to cycle the autocompletion
|
|
case KEY_TAB:
|
|
{
|
|
if ( !mbReadOnly && !autocompleteSignal.empty() &&
|
|
maSelection.Min() && (maSelection.Min() == maText.Len()) &&
|
|
!rKEvt.GetKeyCode().IsMod1() && !rKEvt.GetKeyCode().IsMod2() )
|
|
{
|
|
// Kein Autocomplete wenn alles Selektiert oder Edit leer, weil dann
|
|
// keine vernuenftige Tab-Steuerung!
|
|
if ( rKEvt.GetKeyCode().IsShift() )
|
|
meAutocompleteAction = AUTOCOMPLETE_TABBACKWARD;
|
|
else
|
|
meAutocompleteAction = AUTOCOMPLETE_TABFORWARD;
|
|
|
|
autocompleteSignal( this );
|
|
|
|
// Wurde nichts veraendert, dann TAB fuer DialogControl
|
|
if ( GetSelection().Len() )
|
|
bDone = true;
|
|
}
|
|
}
|
|
break;
|
|
*/
|
|
|
|
default:
|
|
{
|
|
if ( IsCharInput( rKEvt ) )
|
|
{
|
|
bDone = true; // read characters also when in ReadOnly
|
|
if ( !mbReadOnly )
|
|
{
|
|
ImplInsertText(OUString(rKEvt.GetCharCode()), 0, true);
|
|
if (!m_pImpl->m_AutocompleteSignal.empty())
|
|
{
|
|
if ( (maSelection.Min() == maSelection.Max()) && (maSelection.Min() == maText.getLength()) )
|
|
{
|
|
meAutocompleteAction = AUTOCOMPLETE_KEYINPUT;
|
|
m_pImpl->m_AutocompleteSignal( this );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( mbInternModified )
|
|
ImplModified();
|
|
|
|
return bDone;
|
|
}
|
|
|
|
void Edit::KeyInput( const KeyEvent& rKEvt )
|
|
{
|
|
if ( mpUpdateDataTimer && !mbIsSubEdit && mpUpdateDataTimer->IsActive() )
|
|
mpUpdateDataTimer->Start();//do not update while the user is still travelling in the control
|
|
|
|
if ( mpSubEdit || !ImplHandleKeyEvent( rKEvt ) )
|
|
Control::KeyInput( rKEvt );
|
|
}
|
|
|
|
void Edit::FillLayoutData() const
|
|
{
|
|
mpControlData->mpLayoutData = new vcl::ControlLayoutData();
|
|
const_cast<Edit*>(this)->Invalidate();
|
|
}
|
|
|
|
void Edit::Paint(vcl::RenderContext& rRenderContext, const Rectangle& rRectangle)
|
|
{
|
|
if (!mpSubEdit)
|
|
ImplRepaint(rRenderContext, rRectangle);
|
|
}
|
|
|
|
void Edit::Resize()
|
|
{
|
|
if ( !mpSubEdit && IsReallyVisible() )
|
|
{
|
|
Control::Resize();
|
|
// Wegen vertikaler Zentrierung...
|
|
mnXOffset = 0;
|
|
ImplAlign();
|
|
Invalidate();
|
|
ImplShowCursor();
|
|
}
|
|
}
|
|
|
|
void Edit::Draw( OutputDevice* pDev, const Point& rPos, const Size& rSize, DrawFlags nFlags )
|
|
{
|
|
Point aPos = pDev->LogicToPixel( rPos );
|
|
Size aSize = pDev->LogicToPixel( rSize );
|
|
vcl::Font aFont = GetDrawPixelFont( pDev );
|
|
OutDevType eOutDevType = pDev->GetOutDevType();
|
|
|
|
pDev->Push();
|
|
pDev->SetMapMode();
|
|
pDev->SetFont( aFont );
|
|
pDev->SetTextFillColor();
|
|
|
|
// Border/Background
|
|
pDev->SetLineColor();
|
|
pDev->SetFillColor();
|
|
bool bBorder = !(nFlags & DrawFlags::NoBorder ) && (GetStyle() & WB_BORDER);
|
|
bool bBackground = !(nFlags & DrawFlags::NoBackground) && IsControlBackground();
|
|
if ( bBorder || bBackground )
|
|
{
|
|
Rectangle aRect( aPos, aSize );
|
|
if ( bBorder )
|
|
{
|
|
ImplDrawFrame( pDev, aRect );
|
|
}
|
|
if ( bBackground )
|
|
{
|
|
pDev->SetFillColor( GetControlBackground() );
|
|
pDev->DrawRect( aRect );
|
|
}
|
|
}
|
|
|
|
// Inhalt
|
|
if ( ( nFlags & DrawFlags::Mono ) || ( eOutDevType == OUTDEV_PRINTER ) )
|
|
pDev->SetTextColor( Color( COL_BLACK ) );
|
|
else
|
|
{
|
|
if ( !(nFlags & DrawFlags::NoDisable ) && !IsEnabled() )
|
|
{
|
|
const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
|
|
pDev->SetTextColor( rStyleSettings.GetDisableColor() );
|
|
}
|
|
else
|
|
{
|
|
pDev->SetTextColor( GetTextColor() );
|
|
}
|
|
}
|
|
|
|
OUString aText = ImplGetText();
|
|
long nTextHeight = pDev->GetTextHeight();
|
|
long nTextWidth = pDev->GetTextWidth( aText );
|
|
long nOnePixel = GetDrawPixel( pDev, 1 );
|
|
long nOffX = 3*nOnePixel;
|
|
long nOffY = (aSize.Height() - nTextHeight) / 2;
|
|
|
|
// Clipping?
|
|
if ( (nOffY < 0) ||
|
|
((nOffY+nTextHeight) > aSize.Height()) ||
|
|
((nOffX+nTextWidth) > aSize.Width()) )
|
|
{
|
|
Rectangle aClip( aPos, aSize );
|
|
if ( nTextHeight > aSize.Height() )
|
|
aClip.Bottom() += nTextHeight-aSize.Height()+1; // prevent HP printers from 'optimizing'
|
|
pDev->IntersectClipRegion( aClip );
|
|
}
|
|
|
|
if ( GetStyle() & WB_CENTER )
|
|
{
|
|
aPos.X() += (aSize.Width() - nTextWidth) / 2;
|
|
nOffX = 0;
|
|
}
|
|
else if ( GetStyle() & WB_RIGHT )
|
|
{
|
|
aPos.X() += aSize.Width() - nTextWidth;
|
|
nOffX = -nOffX;
|
|
}
|
|
|
|
pDev->DrawText( Point( aPos.X() + nOffX, aPos.Y() + nOffY ), aText );
|
|
pDev->Pop();
|
|
|
|
if ( GetSubEdit() )
|
|
{
|
|
GetSubEdit()->Draw( pDev, rPos, rSize, nFlags );
|
|
}
|
|
}
|
|
|
|
void Edit::ImplInvalidateOutermostBorder( vcl::Window* pWin )
|
|
{
|
|
// allow control to show focused state
|
|
vcl::Window *pInvalWin = pWin, *pBorder = pWin;
|
|
while( ( pBorder = pInvalWin->GetWindow( GetWindowType::Border ) ) != pInvalWin && pBorder &&
|
|
pInvalWin->ImplGetFrame() == pBorder->ImplGetFrame() )
|
|
{
|
|
pInvalWin = pBorder;
|
|
}
|
|
|
|
pInvalWin->Invalidate( InvalidateFlags::Children | InvalidateFlags::Update );
|
|
}
|
|
|
|
void Edit::GetFocus()
|
|
{
|
|
if ( mpSubEdit )
|
|
mpSubEdit->ImplGrabFocus( GetGetFocusFlags() );
|
|
else if ( !mbActivePopup )
|
|
{
|
|
maUndoText = maText.toString();
|
|
|
|
SelectionOptions nSelOptions = GetSettings().GetStyleSettings().GetSelectionOptions();
|
|
if ( !( GetStyle() & (WB_NOHIDESELECTION|WB_READONLY) )
|
|
&& ( GetGetFocusFlags() & (GetFocusFlags::Init|GetFocusFlags::Tab|GetFocusFlags::CURSOR|GetFocusFlags::Mnemonic) ) )
|
|
{
|
|
if ( nSelOptions & SelectionOptions::ShowFirst )
|
|
{
|
|
maSelection.Min() = maText.getLength();
|
|
maSelection.Max() = 0;
|
|
}
|
|
else
|
|
{
|
|
maSelection.Min() = 0;
|
|
maSelection.Max() = maText.getLength();
|
|
}
|
|
if ( mbIsSubEdit )
|
|
static_cast<Edit*>(GetParent())->CallEventListeners( VCLEVENT_EDIT_SELECTIONCHANGED );
|
|
else
|
|
CallEventListeners( VCLEVENT_EDIT_SELECTIONCHANGED );
|
|
}
|
|
|
|
ImplShowCursor();
|
|
|
|
// FIXME: this is currently only on OS X
|
|
// check for other platforms that need similar handling
|
|
if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
|
|
IsNativeWidgetEnabled() &&
|
|
IsNativeControlSupported( CTRL_EDITBOX, PART_ENTIRE_CONTROL ) )
|
|
{
|
|
ImplInvalidateOutermostBorder( mbIsSubEdit ? GetParent() : this );
|
|
}
|
|
else if ( maSelection.Len() )
|
|
{
|
|
// Selektion malen
|
|
if ( !HasPaintEvent() )
|
|
ImplInvalidateOrRepaint();
|
|
else
|
|
Invalidate();
|
|
}
|
|
|
|
SetInputContext( InputContext( GetFont(), !IsReadOnly() ? InputContextFlags::Text|InputContextFlags::ExtText : InputContextFlags::NONE ) );
|
|
}
|
|
|
|
Control::GetFocus();
|
|
}
|
|
|
|
vcl::Window* Edit::GetPreferredKeyInputWindow()
|
|
{
|
|
if ( mpSubEdit )
|
|
return mpSubEdit->GetPreferredKeyInputWindow();
|
|
else
|
|
return this;
|
|
}
|
|
|
|
void Edit::LoseFocus()
|
|
{
|
|
if ( mpUpdateDataTimer && !mbIsSubEdit && mpUpdateDataTimer->IsActive() )
|
|
{
|
|
//notify an update latest when the focus is lost
|
|
mpUpdateDataTimer->Stop();
|
|
mpUpdateDataTimer->Timeout();
|
|
}
|
|
|
|
if ( !mpSubEdit )
|
|
{
|
|
// FIXME: this is currently only on OS X
|
|
// check for other platforms that need similar handling
|
|
if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
|
|
IsNativeWidgetEnabled() &&
|
|
IsNativeControlSupported( CTRL_EDITBOX, PART_ENTIRE_CONTROL ) )
|
|
{
|
|
ImplInvalidateOutermostBorder( mbIsSubEdit ? GetParent() : this );
|
|
}
|
|
|
|
if ( !mbActivePopup && !( GetStyle() & WB_NOHIDESELECTION ) && maSelection.Len() )
|
|
ImplInvalidateOrRepaint(); // Selektion malen
|
|
}
|
|
|
|
Control::LoseFocus();
|
|
}
|
|
|
|
void Edit::Command( const CommandEvent& rCEvt )
|
|
{
|
|
if ( rCEvt.GetCommand() == CommandEventId::ContextMenu )
|
|
{
|
|
PopupMenu* pPopup = Edit::CreatePopupMenu();
|
|
|
|
if ( !maSelection.Len() )
|
|
{
|
|
pPopup->EnableItem( SV_MENU_EDIT_CUT, false );
|
|
pPopup->EnableItem( SV_MENU_EDIT_COPY, false );
|
|
pPopup->EnableItem( SV_MENU_EDIT_DELETE, false );
|
|
}
|
|
|
|
if ( IsReadOnly() )
|
|
{
|
|
pPopup->EnableItem( SV_MENU_EDIT_CUT, false );
|
|
pPopup->EnableItem( SV_MENU_EDIT_PASTE, false );
|
|
pPopup->EnableItem( SV_MENU_EDIT_DELETE, false );
|
|
pPopup->EnableItem( SV_MENU_EDIT_INSERTSYMBOL, false );
|
|
}
|
|
else
|
|
{
|
|
// only paste if text available in clipboard
|
|
bool bData = false;
|
|
uno::Reference< datatransfer::clipboard::XClipboard > xClipboard = GetClipboard();
|
|
|
|
if ( xClipboard.is() )
|
|
{
|
|
uno::Reference< datatransfer::XTransferable > xDataObj;
|
|
{
|
|
SolarMutexReleaser aReleaser;
|
|
xDataObj = xClipboard->getContents();
|
|
}
|
|
if ( xDataObj.is() )
|
|
{
|
|
datatransfer::DataFlavor aFlavor;
|
|
SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor );
|
|
bData = xDataObj->isDataFlavorSupported( aFlavor );
|
|
}
|
|
}
|
|
pPopup->EnableItem( SV_MENU_EDIT_PASTE, bData );
|
|
}
|
|
|
|
if ( maUndoText == maText.getStr() )
|
|
pPopup->EnableItem( SV_MENU_EDIT_UNDO, false );
|
|
if ( ( maSelection.Min() == 0 ) && ( maSelection.Max() == maText.getLength() ) )
|
|
pPopup->EnableItem( SV_MENU_EDIT_SELECTALL, false );
|
|
if ( !pImplFncGetSpecialChars )
|
|
{
|
|
sal_uInt16 nPos = pPopup->GetItemPos( SV_MENU_EDIT_INSERTSYMBOL );
|
|
pPopup->RemoveItem( nPos );
|
|
pPopup->RemoveItem( nPos-1 );
|
|
}
|
|
|
|
mbActivePopup = true;
|
|
Selection aSaveSel = GetSelection(); // if someone changes selection in Get/LoseFocus, e.g. URL bar
|
|
Point aPos = rCEvt.GetMousePosPixel();
|
|
if ( !rCEvt.IsMouseEvent() )
|
|
{
|
|
// Show menu eventually centered in selection
|
|
Size aSize = GetOutputSizePixel();
|
|
aPos = Point( aSize.Width()/2, aSize.Height()/2 );
|
|
}
|
|
sal_uInt16 n = pPopup->Execute( this, aPos );
|
|
Edit::DeletePopupMenu( pPopup );
|
|
SetSelection( aSaveSel );
|
|
switch ( n )
|
|
{
|
|
case SV_MENU_EDIT_UNDO:
|
|
Undo();
|
|
ImplModified();
|
|
break;
|
|
case SV_MENU_EDIT_CUT:
|
|
Cut();
|
|
ImplModified();
|
|
break;
|
|
case SV_MENU_EDIT_COPY:
|
|
Copy();
|
|
break;
|
|
case SV_MENU_EDIT_PASTE:
|
|
Paste();
|
|
ImplModified();
|
|
break;
|
|
case SV_MENU_EDIT_DELETE:
|
|
DeleteSelected();
|
|
ImplModified();
|
|
break;
|
|
case SV_MENU_EDIT_SELECTALL:
|
|
ImplSetSelection( Selection( 0, maText.getLength() ) );
|
|
break;
|
|
case SV_MENU_EDIT_INSERTSYMBOL:
|
|
{
|
|
OUString aChars = pImplFncGetSpecialChars( this, GetFont() );
|
|
SetSelection( aSaveSel );
|
|
if ( !aChars.isEmpty() )
|
|
{
|
|
ImplInsertText( aChars );
|
|
ImplModified();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
mbActivePopup = false;
|
|
}
|
|
else if ( rCEvt.GetCommand() == CommandEventId::StartExtTextInput )
|
|
{
|
|
DeleteSelected();
|
|
delete mpIMEInfos;
|
|
sal_Int32 nPos = static_cast<sal_Int32>(maSelection.Max());
|
|
mpIMEInfos = new Impl_IMEInfos( nPos, OUString(maText.getStr() + nPos ) );
|
|
mpIMEInfos->bWasCursorOverwrite = !IsInsertMode();
|
|
}
|
|
else if ( rCEvt.GetCommand() == CommandEventId::EndExtTextInput )
|
|
{
|
|
bool bInsertMode = !mpIMEInfos->bWasCursorOverwrite;
|
|
delete mpIMEInfos;
|
|
mpIMEInfos = NULL;
|
|
|
|
SetInsertMode(bInsertMode);
|
|
ImplModified();
|
|
|
|
Invalidate();
|
|
|
|
// #i25161# call auto complete handler for ext text commit also
|
|
if (m_pImpl->m_AutocompleteSignal.empty())
|
|
{
|
|
if ( (maSelection.Min() == maSelection.Max()) && (maSelection.Min() == maText.getLength()) )
|
|
{
|
|
meAutocompleteAction = AUTOCOMPLETE_KEYINPUT;
|
|
m_pImpl->m_AutocompleteSignal( this );
|
|
}
|
|
}
|
|
}
|
|
else if ( rCEvt.GetCommand() == CommandEventId::ExtTextInput )
|
|
{
|
|
const CommandExtTextInputData* pData = rCEvt.GetExtTextInputData();
|
|
|
|
maText.remove( mpIMEInfos->nPos, mpIMEInfos->nLen );
|
|
maText.insert( mpIMEInfos->nPos, pData->GetText() );
|
|
if ( mpIMEInfos->bWasCursorOverwrite )
|
|
{
|
|
const sal_Int32 nOldIMETextLen = mpIMEInfos->nLen;
|
|
const sal_Int32 nNewIMETextLen = pData->GetText().getLength();
|
|
if ( ( nOldIMETextLen > nNewIMETextLen ) &&
|
|
( nNewIMETextLen < mpIMEInfos->aOldTextAfterStartPos.getLength() ) )
|
|
{
|
|
// restore old characters
|
|
const sal_Int32 nRestore = nOldIMETextLen - nNewIMETextLen;
|
|
maText.insert( mpIMEInfos->nPos + nNewIMETextLen, mpIMEInfos->aOldTextAfterStartPos.copy( nNewIMETextLen, nRestore ) );
|
|
}
|
|
else if ( ( nOldIMETextLen < nNewIMETextLen ) &&
|
|
( nOldIMETextLen < mpIMEInfos->aOldTextAfterStartPos.getLength() ) )
|
|
{
|
|
const sal_Int32 nOverwrite = ( nNewIMETextLen > mpIMEInfos->aOldTextAfterStartPos.getLength()
|
|
? mpIMEInfos->aOldTextAfterStartPos.getLength() : nNewIMETextLen ) - nOldIMETextLen;
|
|
maText.remove( mpIMEInfos->nPos + nNewIMETextLen, nOverwrite );
|
|
}
|
|
}
|
|
|
|
if ( pData->GetTextAttr() )
|
|
{
|
|
mpIMEInfos->CopyAttribs( pData->GetTextAttr(), pData->GetText().getLength() );
|
|
mpIMEInfos->bCursor = pData->IsCursorVisible();
|
|
}
|
|
else
|
|
{
|
|
mpIMEInfos->DestroyAttribs();
|
|
}
|
|
|
|
ImplAlignAndPaint();
|
|
sal_Int32 nCursorPos = mpIMEInfos->nPos + pData->GetCursorPos();
|
|
SetSelection( Selection( nCursorPos, nCursorPos ) );
|
|
SetInsertMode( !pData->IsCursorOverwrite() );
|
|
|
|
if ( pData->IsCursorVisible() )
|
|
GetCursor()->Show();
|
|
else
|
|
GetCursor()->Hide();
|
|
}
|
|
else if ( rCEvt.GetCommand() == CommandEventId::CursorPos )
|
|
{
|
|
if ( mpIMEInfos )
|
|
{
|
|
sal_Int32 nCursorPos = GetSelection().Max();
|
|
SetCursorRect( NULL, GetTextWidth( maText.toString(), nCursorPos, mpIMEInfos->nPos+mpIMEInfos->nLen-nCursorPos ) );
|
|
}
|
|
else
|
|
{
|
|
SetCursorRect();
|
|
}
|
|
}
|
|
else if ( rCEvt.GetCommand() == CommandEventId::SelectionChange )
|
|
{
|
|
const CommandSelectionChangeData *pData = rCEvt.GetSelectionChangeData();
|
|
Selection aSelection( pData->GetStart(), pData->GetEnd() );
|
|
SetSelection(aSelection);
|
|
}
|
|
else if ( rCEvt.GetCommand() == CommandEventId::QueryCharPosition )
|
|
{
|
|
if (mpIMEInfos && mpIMEInfos->nLen > 0)
|
|
{
|
|
OUString aText = ImplGetText();
|
|
long nDXBuffer[256];
|
|
std::unique_ptr<long[]> pDXBuffer;
|
|
long* pDX = nDXBuffer;
|
|
|
|
if( !aText.isEmpty() )
|
|
{
|
|
if( (size_t) (2*aText.getLength()) > SAL_N_ELEMENTS(nDXBuffer) )
|
|
{
|
|
pDXBuffer.reset(new long[2*(aText.getLength()+1)]);
|
|
pDX = pDXBuffer.get();
|
|
}
|
|
|
|
GetCaretPositions( aText, pDX, 0, aText.getLength() );
|
|
}
|
|
long nTH = GetTextHeight();
|
|
Point aPos( mnXOffset, ImplGetTextYPosition() );
|
|
|
|
std::unique_ptr<Rectangle[]> aRects(new Rectangle[ mpIMEInfos->nLen ]);
|
|
for ( int nIndex = 0; nIndex < mpIMEInfos->nLen; ++nIndex )
|
|
{
|
|
Rectangle aRect( aPos, Size( 10, nTH ) );
|
|
aRect.Left() = pDX[2*(nIndex+mpIMEInfos->nPos)] + mnXOffset + ImplGetExtraXOffset();
|
|
aRects[ nIndex ] = aRect;
|
|
}
|
|
SetCompositionCharRect( aRects.get(), mpIMEInfos->nLen );
|
|
}
|
|
}
|
|
else
|
|
Control::Command( rCEvt );
|
|
}
|
|
|
|
void Edit::StateChanged( StateChangedType nType )
|
|
{
|
|
if (nType == StateChangedType::InitShow)
|
|
{
|
|
if (!mpSubEdit)
|
|
{
|
|
mnXOffset = 0; // if GrabFocus before while size was still wrong
|
|
ImplAlign();
|
|
if (!mpSubEdit)
|
|
ImplShowCursor(false);
|
|
Invalidate();
|
|
}
|
|
}
|
|
else if (nType == StateChangedType::Enable)
|
|
{
|
|
if (!mpSubEdit)
|
|
{
|
|
// change text color only
|
|
ImplInvalidateOrRepaint();
|
|
}
|
|
}
|
|
else if (nType == StateChangedType::Style || nType == StateChangedType::Mirroring)
|
|
{
|
|
WinBits nStyle = GetStyle();
|
|
if (nType == StateChangedType::Style)
|
|
{
|
|
nStyle = ImplInitStyle(GetStyle());
|
|
SetStyle(nStyle);
|
|
}
|
|
|
|
sal_uInt16 nOldAlign = mnAlign;
|
|
mnAlign = EDIT_ALIGN_LEFT;
|
|
|
|
// --- RTL --- hack: right align until keyinput and cursor travelling works
|
|
// edits are always RTL disabled
|
|
// however the parent edits contain the correct setting
|
|
if (mbIsSubEdit && GetParent()->IsRTLEnabled())
|
|
{
|
|
if (GetParent()->GetStyle() & WB_LEFT)
|
|
mnAlign = EDIT_ALIGN_RIGHT;
|
|
if (nType == StateChangedType::Mirroring)
|
|
SetLayoutMode(TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_TEXTORIGIN_LEFT);
|
|
}
|
|
else if (mbIsSubEdit && !GetParent()->IsRTLEnabled())
|
|
{
|
|
if (nType == StateChangedType::Mirroring)
|
|
SetLayoutMode(TEXT_LAYOUT_TEXTORIGIN_LEFT);
|
|
}
|
|
|
|
if (nStyle & WB_RIGHT)
|
|
mnAlign = EDIT_ALIGN_RIGHT;
|
|
else if (nStyle & WB_CENTER)
|
|
mnAlign = EDIT_ALIGN_CENTER;
|
|
if (!maText.isEmpty() && (mnAlign != nOldAlign))
|
|
{
|
|
ImplAlign();
|
|
Invalidate();
|
|
}
|
|
|
|
}
|
|
else if (nType == StateChangedType::Zoom)
|
|
{
|
|
if (!mpSubEdit)
|
|
{
|
|
ImplShowCursor();
|
|
Invalidate();
|
|
}
|
|
}
|
|
else if (nType == StateChangedType::ControlFont)
|
|
{
|
|
if (!mpSubEdit)
|
|
{
|
|
ImplShowCursor();
|
|
Invalidate();
|
|
}
|
|
}
|
|
else if (nType == StateChangedType::ControlForeground)
|
|
{
|
|
if (!mpSubEdit)
|
|
{
|
|
Invalidate();
|
|
}
|
|
}
|
|
else if (nType == StateChangedType::ControlBackground)
|
|
{
|
|
if (!mpSubEdit)
|
|
{
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
Control::StateChanged(nType);
|
|
}
|
|
|
|
void Edit::DataChanged( const DataChangedEvent& rDCEvt )
|
|
{
|
|
if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
|
|
(rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
|
|
((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
|
|
(rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
|
|
{
|
|
if ( !mpSubEdit )
|
|
{
|
|
ImplShowCursor();
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
Control::DataChanged( rDCEvt );
|
|
}
|
|
|
|
void Edit::ImplShowDDCursor()
|
|
{
|
|
if (!mpDDInfo->bVisCursor)
|
|
{
|
|
long nTextWidth = GetTextWidth( maText.toString(), 0, mpDDInfo->nDropPos );
|
|
long nTextHeight = GetTextHeight();
|
|
Rectangle aCursorRect( Point( nTextWidth + mnXOffset, (GetOutputSize().Height()-nTextHeight)/2 ), Size( 2, nTextHeight ) );
|
|
mpDDInfo->aCursor.SetWindow( this );
|
|
mpDDInfo->aCursor.SetPos( aCursorRect.TopLeft() );
|
|
mpDDInfo->aCursor.SetSize( aCursorRect.GetSize() );
|
|
mpDDInfo->aCursor.Show();
|
|
mpDDInfo->bVisCursor = true;
|
|
}
|
|
}
|
|
|
|
void Edit::ImplHideDDCursor()
|
|
{
|
|
if ( mpDDInfo && mpDDInfo->bVisCursor )
|
|
{
|
|
mpDDInfo->aCursor.Hide();
|
|
mpDDInfo->bVisCursor = false;
|
|
}
|
|
}
|
|
|
|
TextFilter::TextFilter(const OUString &rForbiddenChars)
|
|
: sForbiddenChars(rForbiddenChars)
|
|
{
|
|
}
|
|
|
|
TextFilter::~TextFilter()
|
|
{
|
|
}
|
|
|
|
OUString TextFilter::filter(const OUString &rText)
|
|
{
|
|
OUString sTemp(rText);
|
|
for (sal_Int32 i = 0; i < sForbiddenChars.getLength(); ++i)
|
|
{
|
|
sTemp = comphelper::string::remove(sTemp, sForbiddenChars[i]);
|
|
}
|
|
return sTemp;
|
|
}
|
|
|
|
void Edit::filterText()
|
|
{
|
|
Selection aSel = GetSelection();
|
|
OUString sOrig = GetText();
|
|
OUString sNew = mpFilterText->filter(GetText());
|
|
if (sOrig != sNew)
|
|
{
|
|
sal_Int32 nDiff = sOrig.getLength() - sNew.getLength();
|
|
if (nDiff)
|
|
{
|
|
aSel.setMin(aSel.getMin() - nDiff);
|
|
aSel.setMax(aSel.getMin());
|
|
}
|
|
SetText(sNew);
|
|
SetSelection(aSel);
|
|
}
|
|
}
|
|
|
|
void Edit::Modify()
|
|
{
|
|
if (mpFilterText)
|
|
filterText();
|
|
|
|
if ( mbIsSubEdit )
|
|
{
|
|
static_cast<Edit*>(GetParent())->Modify();
|
|
}
|
|
else
|
|
{
|
|
if ( mpUpdateDataTimer )
|
|
mpUpdateDataTimer->Start();
|
|
|
|
if ( ImplCallEventListenersAndHandler( VCLEVENT_EDIT_MODIFY, [this] () { maModifyHdl.Call(*this); } ) )
|
|
// have been destroyed while calling into the handlers
|
|
return;
|
|
|
|
// #i13677# notify edit listeners about caret position change
|
|
CallEventListeners( VCLEVENT_EDIT_CARETCHANGED );
|
|
// FIXME: this is currently only on OS X
|
|
// check for other platforms that need similar handling
|
|
if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
|
|
IsNativeWidgetEnabled() &&
|
|
IsNativeControlSupported( CTRL_EDITBOX, PART_ENTIRE_CONTROL ) )
|
|
{
|
|
ImplInvalidateOutermostBorder( this );
|
|
}
|
|
}
|
|
}
|
|
|
|
void Edit::UpdateData()
|
|
{
|
|
maUpdateDataHdl.Call( *this );
|
|
}
|
|
|
|
IMPL_LINK_NOARG_TYPED(Edit, ImplUpdateDataHdl, Timer *, void)
|
|
{
|
|
UpdateData();
|
|
}
|
|
|
|
void Edit::EnableUpdateData( sal_uLong nTimeout )
|
|
{
|
|
if ( !nTimeout )
|
|
DisableUpdateData();
|
|
else
|
|
{
|
|
if ( !mpUpdateDataTimer )
|
|
{
|
|
mpUpdateDataTimer = new Timer;
|
|
mpUpdateDataTimer->SetTimeoutHdl( LINK( this, Edit, ImplUpdateDataHdl ) );
|
|
}
|
|
|
|
mpUpdateDataTimer->SetTimeout( nTimeout );
|
|
}
|
|
}
|
|
|
|
void Edit::SetEchoChar( sal_Unicode c )
|
|
{
|
|
mcEchoChar = c;
|
|
if ( mpSubEdit )
|
|
mpSubEdit->SetEchoChar( c );
|
|
}
|
|
|
|
void Edit::SetReadOnly( bool bReadOnly )
|
|
{
|
|
if ( mbReadOnly != bool(bReadOnly) )
|
|
{
|
|
mbReadOnly = bReadOnly;
|
|
if ( mpSubEdit )
|
|
mpSubEdit->SetReadOnly( bReadOnly );
|
|
|
|
CompatStateChanged( StateChangedType::ReadOnly );
|
|
}
|
|
}
|
|
|
|
void Edit::SetInsertMode( bool bInsert )
|
|
{
|
|
if ( bInsert != mbInsertMode )
|
|
{
|
|
mbInsertMode = bInsert;
|
|
if ( mpSubEdit )
|
|
mpSubEdit->SetInsertMode( bInsert );
|
|
else
|
|
ImplShowCursor();
|
|
}
|
|
}
|
|
|
|
bool Edit::IsInsertMode() const
|
|
{
|
|
if ( mpSubEdit )
|
|
return mpSubEdit->IsInsertMode();
|
|
else
|
|
return mbInsertMode;
|
|
}
|
|
|
|
void Edit::SetMaxTextLen(sal_Int32 nMaxLen)
|
|
{
|
|
mnMaxTextLen = nMaxLen > 0 ? nMaxLen : EDIT_NOLIMIT;
|
|
|
|
if ( mpSubEdit )
|
|
mpSubEdit->SetMaxTextLen( mnMaxTextLen );
|
|
else
|
|
{
|
|
if ( maText.getLength() > mnMaxTextLen )
|
|
ImplDelete( Selection( mnMaxTextLen, maText.getLength() ), EDIT_DEL_RIGHT, EDIT_DELMODE_SIMPLE );
|
|
}
|
|
}
|
|
|
|
void Edit::SetSelection( const Selection& rSelection )
|
|
{
|
|
// If the selection was changed from outside, e.g. by MouseButtonDown, don't call Tracking()
|
|
// directly afterwards which would change the selection again
|
|
if ( IsTracking() )
|
|
EndTracking();
|
|
else if ( mpSubEdit && mpSubEdit->IsTracking() )
|
|
mpSubEdit->EndTracking();
|
|
|
|
ImplSetSelection( rSelection );
|
|
}
|
|
|
|
void Edit::ImplSetSelection( const Selection& rSelection, bool bPaint )
|
|
{
|
|
if ( mpSubEdit )
|
|
mpSubEdit->ImplSetSelection( rSelection );
|
|
else
|
|
{
|
|
if ( rSelection != maSelection )
|
|
{
|
|
Selection aOld( maSelection );
|
|
Selection aNew( rSelection );
|
|
|
|
if ( aNew.Min() > maText.getLength() )
|
|
aNew.Min() = maText.getLength();
|
|
if ( aNew.Max() > maText.getLength() )
|
|
aNew.Max() = maText.getLength();
|
|
if ( aNew.Min() < 0 )
|
|
aNew.Min() = 0;
|
|
if ( aNew.Max() < 0 )
|
|
aNew.Max() = 0;
|
|
|
|
if ( aNew != maSelection )
|
|
{
|
|
ImplClearLayoutData();
|
|
Selection aTemp = maSelection;
|
|
maSelection = aNew;
|
|
|
|
if ( bPaint && ( aOld.Len() || aNew.Len() || IsPaintTransparent() ) )
|
|
ImplInvalidateOrRepaint();
|
|
ImplShowCursor();
|
|
|
|
bool bCaret = false, bSelection = false;
|
|
long nB=aNew.Max(), nA=aNew.Min(),oB=aTemp.Max(), oA=aTemp.Min();
|
|
long nGap = nB-nA, oGap = oB-oA;
|
|
if (nB != oB)
|
|
bCaret = true;
|
|
if (nGap != 0 || oGap != 0)
|
|
bSelection = true;
|
|
|
|
if (bSelection)
|
|
{
|
|
if ( mbIsSubEdit )
|
|
static_cast<Edit*>(GetParent())->CallEventListeners( VCLEVENT_EDIT_SELECTIONCHANGED );
|
|
else
|
|
CallEventListeners( VCLEVENT_EDIT_SELECTIONCHANGED );
|
|
}
|
|
|
|
if (bCaret)
|
|
{
|
|
if ( mbIsSubEdit )
|
|
static_cast<Edit*>(GetParent())->CallEventListeners( VCLEVENT_EDIT_CARETCHANGED );
|
|
else
|
|
CallEventListeners( VCLEVENT_EDIT_CARETCHANGED );
|
|
}
|
|
|
|
// #103511# notify combobox listeners of deselection
|
|
if( !maSelection && GetParent() && GetParent()->GetType() == WINDOW_COMBOBOX )
|
|
static_cast<Edit*>(GetParent())->CallEventListeners( VCLEVENT_COMBOBOX_DESELECT );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const Selection& Edit::GetSelection() const
|
|
{
|
|
if ( mpSubEdit )
|
|
return mpSubEdit->GetSelection();
|
|
else
|
|
return maSelection;
|
|
}
|
|
|
|
void Edit::ReplaceSelected( const OUString& rStr )
|
|
{
|
|
if ( mpSubEdit )
|
|
mpSubEdit->ReplaceSelected( rStr );
|
|
else
|
|
ImplInsertText( rStr );
|
|
}
|
|
|
|
void Edit::DeleteSelected()
|
|
{
|
|
if ( mpSubEdit )
|
|
mpSubEdit->DeleteSelected();
|
|
else
|
|
{
|
|
if ( maSelection.Len() )
|
|
ImplDelete( maSelection, EDIT_DEL_RIGHT, EDIT_DELMODE_SIMPLE );
|
|
}
|
|
}
|
|
|
|
OUString Edit::GetSelected() const
|
|
{
|
|
if ( mpSubEdit )
|
|
return mpSubEdit->GetSelected();
|
|
else
|
|
{
|
|
Selection aSelection( maSelection );
|
|
aSelection.Justify();
|
|
return OUString( maText.getStr() + aSelection.Min(), aSelection.Len() );
|
|
}
|
|
}
|
|
|
|
void Edit::Cut()
|
|
{
|
|
if ( !(GetStyle() & WB_PASSWORD ) )
|
|
{
|
|
Copy();
|
|
ReplaceSelected( OUString() );
|
|
}
|
|
}
|
|
|
|
void Edit::Copy()
|
|
{
|
|
if ( !(GetStyle() & WB_PASSWORD ) )
|
|
{
|
|
::com::sun::star::uno::Reference<com::sun::star::datatransfer::clipboard::XClipboard> aClipboard(GetClipboard());
|
|
ImplCopy( aClipboard );
|
|
}
|
|
}
|
|
|
|
void Edit::Paste()
|
|
{
|
|
::com::sun::star::uno::Reference<com::sun::star::datatransfer::clipboard::XClipboard> aClipboard(GetClipboard());
|
|
ImplPaste( aClipboard );
|
|
}
|
|
|
|
void Edit::Undo()
|
|
{
|
|
if ( mpSubEdit )
|
|
mpSubEdit->Undo();
|
|
else
|
|
{
|
|
OUString aText( maText.toString() );
|
|
ImplDelete( Selection( 0, aText.getLength() ), EDIT_DEL_RIGHT, EDIT_DELMODE_SIMPLE );
|
|
ImplInsertText( maUndoText );
|
|
ImplSetSelection( Selection( 0, maUndoText.getLength() ) );
|
|
maUndoText = aText;
|
|
}
|
|
}
|
|
|
|
void Edit::SetText( const OUString& rStr )
|
|
{
|
|
if ( mpSubEdit )
|
|
mpSubEdit->SetText( rStr ); // not directly ImplSetText if SetText overridden
|
|
else
|
|
{
|
|
Selection aNewSel( 0, 0 ); // prevent scrolling
|
|
ImplSetText( rStr, &aNewSel );
|
|
}
|
|
}
|
|
|
|
void Edit::SetText( const OUString& rStr, const Selection& rSelection )
|
|
{
|
|
if ( mpSubEdit )
|
|
mpSubEdit->SetText( rStr, rSelection );
|
|
else
|
|
ImplSetText( rStr, &rSelection );
|
|
}
|
|
|
|
OUString Edit::GetText() const
|
|
{
|
|
if ( mpSubEdit )
|
|
return mpSubEdit->GetText();
|
|
else
|
|
return maText.toString();
|
|
}
|
|
|
|
void Edit::SetPlaceholderText( const OUString& rStr )
|
|
{
|
|
if ( mpSubEdit )
|
|
mpSubEdit->SetPlaceholderText( rStr );
|
|
else if ( maPlaceholderText != rStr )
|
|
{
|
|
maPlaceholderText = rStr;
|
|
if ( GetText().isEmpty() )
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
OUString Edit::GetPlaceholderText() const
|
|
{
|
|
if ( mpSubEdit )
|
|
return mpSubEdit->GetPlaceholderText();
|
|
|
|
return maPlaceholderText;
|
|
}
|
|
|
|
void Edit::SetModifyFlag()
|
|
{
|
|
if ( mpSubEdit )
|
|
mpSubEdit->mbModified = true;
|
|
else
|
|
mbModified = true;
|
|
}
|
|
|
|
void Edit::ClearModifyFlag()
|
|
{
|
|
if ( mpSubEdit )
|
|
mpSubEdit->mbModified = false;
|
|
else
|
|
mbModified = false;
|
|
}
|
|
|
|
void Edit::SetSubEdit(Edit* pEdit)
|
|
{
|
|
mpSubEdit.disposeAndClear();
|
|
mpSubEdit.set(pEdit);
|
|
|
|
if (mpSubEdit)
|
|
{
|
|
SetPointer(PointerStyle::Arrow); // Nur das SubEdit hat den BEAM...
|
|
mpSubEdit->mbIsSubEdit = true;
|
|
|
|
mpSubEdit->SetReadOnly(mbReadOnly);
|
|
mpSubEdit->m_pImpl->m_AutocompleteSignal.connect(m_pImpl->m_AutocompleteSignal);
|
|
}
|
|
}
|
|
|
|
Size Edit::CalcMinimumSizeForText(const OUString &rString) const
|
|
{
|
|
int eCtrlType = ImplGetNativeControlType();
|
|
|
|
Size aSize;
|
|
if (mnWidthInChars != -1)
|
|
{
|
|
//CalcSize calls CalcWindowSize, but we will call that also in this
|
|
//function, so undo the first one with CalcOutputSize
|
|
aSize = CalcOutputSize(CalcSize(mnWidthInChars));
|
|
}
|
|
else
|
|
{
|
|
OUString aString;
|
|
if (mnMaxWidthChars != -1 && mnMaxWidthChars < rString.getLength())
|
|
aString = rString.copy(0, mnMaxWidthChars);
|
|
else
|
|
aString = rString;
|
|
|
|
aSize.Height() = GetTextHeight();
|
|
aSize.Width() = GetTextWidth(aString);
|
|
aSize.Width() += ImplGetExtraXOffset() * 2;
|
|
|
|
// do not create edit fields in which one cannot enter anything
|
|
// a default minimum width should exist for at least 3 characters
|
|
|
|
//CalcSize calls CalcWindowSize, but we will call that also in this
|
|
//function, so undo the first one with CalcOutputSize
|
|
Size aMinSize(CalcOutputSize(CalcSize(3)));
|
|
if (aSize.Width() < aMinSize.Width())
|
|
aSize.Width() = aMinSize.Width();
|
|
}
|
|
|
|
aSize.Height() += ImplGetExtraYOffset() * 2;
|
|
|
|
aSize = CalcWindowSize( aSize );
|
|
|
|
// ask NWF what if it has an opinion, too
|
|
ImplControlValue aControlValue;
|
|
Rectangle aRect( Point( 0, 0 ), aSize );
|
|
Rectangle aContent, aBound;
|
|
if (GetNativeControlRegion(eCtrlType, PART_ENTIRE_CONTROL, aRect, ControlState::NONE,
|
|
aControlValue, OUString(), aBound, aContent))
|
|
{
|
|
if (aBound.GetHeight() > aSize.Height())
|
|
aSize.Height() = aBound.GetHeight();
|
|
}
|
|
return aSize;
|
|
}
|
|
|
|
Size Edit::CalcMinimumSize() const
|
|
{
|
|
return CalcMinimumSizeForText(GetText());
|
|
}
|
|
|
|
Size Edit::GetMinimumEditSize()
|
|
{
|
|
vcl::Window* pDefWin = ImplGetDefaultWindow();
|
|
ScopedVclPtrInstance< Edit > aEdit( pDefWin, WB_BORDER );
|
|
Size aSize( aEdit->CalcMinimumSize() );
|
|
return aSize;
|
|
}
|
|
|
|
Size Edit::GetOptimalSize() const
|
|
{
|
|
return CalcMinimumSize();
|
|
}
|
|
|
|
Size Edit::CalcSize(sal_Int32 nChars) const
|
|
{
|
|
// width for N characters, independent from content.
|
|
// works only correct for fixed fonts, average otherwise
|
|
Size aSz( GetTextWidth( OUString('x') ), GetTextHeight() );
|
|
aSz.Width() *= nChars;
|
|
aSz.Width() += ImplGetExtraXOffset() * 2;
|
|
aSz = CalcWindowSize( aSz );
|
|
return aSz;
|
|
}
|
|
|
|
sal_Int32 Edit::GetMaxVisChars() const
|
|
{
|
|
const vcl::Window* pW = mpSubEdit ? mpSubEdit : this;
|
|
sal_Int32 nOutWidth = pW->GetOutputSizePixel().Width();
|
|
sal_Int32 nCharWidth = GetTextWidth( OUString('x') );
|
|
return nCharWidth ? nOutWidth/nCharWidth : 0;
|
|
}
|
|
|
|
sal_Int32 Edit::GetCharPos( const Point& rWindowPos ) const
|
|
{
|
|
return ImplGetCharPos( rWindowPos );
|
|
}
|
|
|
|
void Edit::SetGetSpecialCharsFunction( FncGetSpecialChars fn )
|
|
{
|
|
pImplFncGetSpecialChars = fn;
|
|
}
|
|
|
|
FncGetSpecialChars Edit::GetGetSpecialCharsFunction()
|
|
{
|
|
return pImplFncGetSpecialChars;
|
|
}
|
|
|
|
PopupMenu* Edit::CreatePopupMenu()
|
|
{
|
|
ResMgr* pResMgr = ImplGetResMgr();
|
|
if( ! pResMgr )
|
|
return new PopupMenu();
|
|
|
|
PopupMenu* pPopup = new PopupMenu( ResId( SV_RESID_MENU_EDIT, *pResMgr ) );
|
|
const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
|
|
if ( rStyleSettings.GetHideDisabledMenuItems() )
|
|
pPopup->SetMenuFlags( MenuFlags::HideDisabledEntries );
|
|
else
|
|
pPopup->SetMenuFlags ( MenuFlags::AlwaysShowDisabledEntries );
|
|
if ( rStyleSettings.GetAcceleratorsInContextMenus() )
|
|
{
|
|
pPopup->SetAccelKey( SV_MENU_EDIT_UNDO, vcl::KeyCode( KeyFuncType::UNDO ) );
|
|
pPopup->SetAccelKey( SV_MENU_EDIT_CUT, vcl::KeyCode( KeyFuncType::CUT ) );
|
|
pPopup->SetAccelKey( SV_MENU_EDIT_COPY, vcl::KeyCode( KeyFuncType::COPY ) );
|
|
pPopup->SetAccelKey( SV_MENU_EDIT_PASTE, vcl::KeyCode( KeyFuncType::PASTE ) );
|
|
pPopup->SetAccelKey( SV_MENU_EDIT_DELETE, vcl::KeyCode( KeyFuncType::DELETE ) );
|
|
pPopup->SetAccelKey( SV_MENU_EDIT_SELECTALL, vcl::KeyCode( KEY_A, false, true, false, false ) );
|
|
pPopup->SetAccelKey( SV_MENU_EDIT_INSERTSYMBOL, vcl::KeyCode( KEY_S, true, true, false, false ) );
|
|
}
|
|
return pPopup;
|
|
}
|
|
|
|
void Edit::DeletePopupMenu( PopupMenu* pMenu )
|
|
{
|
|
delete pMenu;
|
|
}
|
|
|
|
// ::com::sun::star::datatransfer::dnd::XDragGestureListener
|
|
void Edit::dragGestureRecognized( const ::com::sun::star::datatransfer::dnd::DragGestureEvent& rDGE ) throw (::com::sun::star::uno::RuntimeException, std::exception)
|
|
{
|
|
SolarMutexGuard aVclGuard;
|
|
|
|
if ( !IsTracking() && maSelection.Len() &&
|
|
!(GetStyle() & WB_PASSWORD) && (!mpDDInfo || !mpDDInfo->bStarterOfDD) ) // Kein Mehrfach D&D
|
|
{
|
|
Selection aSel( maSelection );
|
|
aSel.Justify();
|
|
|
|
// Nur wenn Maus in der Selektion...
|
|
Point aMousePos( rDGE.DragOriginX, rDGE.DragOriginY );
|
|
sal_Int32 nCharPos = ImplGetCharPos( aMousePos );
|
|
if ( (nCharPos >= aSel.Min()) && (nCharPos < aSel.Max()) )
|
|
{
|
|
if ( !mpDDInfo )
|
|
mpDDInfo = new DDInfo;
|
|
|
|
mpDDInfo->bStarterOfDD = true;
|
|
mpDDInfo->aDndStartSel = aSel;
|
|
|
|
if ( IsTracking() )
|
|
EndTracking(); // Vor D&D Tracking ausschalten
|
|
|
|
vcl::unohelper::TextDataObject* pDataObj = new vcl::unohelper::TextDataObject( GetSelected() );
|
|
sal_Int8 nActions = datatransfer::dnd::DNDConstants::ACTION_COPY;
|
|
if ( !IsReadOnly() )
|
|
nActions |= datatransfer::dnd::DNDConstants::ACTION_MOVE;
|
|
rDGE.DragSource->startDrag( rDGE, nActions, 0 /*cursor*/, 0 /*image*/, pDataObj, mxDnDListener );
|
|
if ( GetCursor() )
|
|
GetCursor()->Hide();
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
// ::com::sun::star::datatransfer::dnd::XDragSourceListener
|
|
void Edit::dragDropEnd( const ::com::sun::star::datatransfer::dnd::DragSourceDropEvent& rDSDE ) throw (::com::sun::star::uno::RuntimeException, std::exception)
|
|
{
|
|
SolarMutexGuard aVclGuard;
|
|
|
|
if ( rDSDE.DropSuccess && ( rDSDE.DropAction & datatransfer::dnd::DNDConstants::ACTION_MOVE ) )
|
|
{
|
|
Selection aSel( mpDDInfo->aDndStartSel );
|
|
if ( mpDDInfo->bDroppedInMe )
|
|
{
|
|
if ( aSel.Max() > mpDDInfo->nDropPos )
|
|
{
|
|
long nLen = aSel.Len();
|
|
aSel.Min() += nLen;
|
|
aSel.Max() += nLen;
|
|
}
|
|
}
|
|
ImplDelete( aSel, EDIT_DEL_RIGHT, EDIT_DELMODE_SIMPLE );
|
|
ImplModified();
|
|
}
|
|
|
|
ImplHideDDCursor();
|
|
delete mpDDInfo;
|
|
mpDDInfo = NULL;
|
|
}
|
|
|
|
// ::com::sun::star::datatransfer::dnd::XDropTargetListener
|
|
void Edit::drop( const ::com::sun::star::datatransfer::dnd::DropTargetDropEvent& rDTDE ) throw (::com::sun::star::uno::RuntimeException, std::exception)
|
|
{
|
|
SolarMutexGuard aVclGuard;
|
|
|
|
bool bChanges = false;
|
|
if ( !mbReadOnly && mpDDInfo )
|
|
{
|
|
ImplHideDDCursor();
|
|
|
|
Selection aSel( maSelection );
|
|
aSel.Justify();
|
|
|
|
if ( aSel.Len() && !mpDDInfo->bStarterOfDD )
|
|
ImplDelete( aSel, EDIT_DEL_RIGHT, EDIT_DELMODE_SIMPLE );
|
|
|
|
mpDDInfo->bDroppedInMe = true;
|
|
|
|
aSel.Min() = mpDDInfo->nDropPos;
|
|
aSel.Max() = mpDDInfo->nDropPos;
|
|
ImplSetSelection( aSel );
|
|
|
|
uno::Reference< datatransfer::XTransferable > xDataObj = rDTDE.Transferable;
|
|
if ( xDataObj.is() )
|
|
{
|
|
datatransfer::DataFlavor aFlavor;
|
|
SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor );
|
|
if ( xDataObj->isDataFlavorSupported( aFlavor ) )
|
|
{
|
|
uno::Any aData = xDataObj->getTransferData( aFlavor );
|
|
OUString aText;
|
|
aData >>= aText;
|
|
ImplInsertText( aText );
|
|
bChanges = true;
|
|
ImplModified();
|
|
}
|
|
}
|
|
|
|
if ( !mpDDInfo->bStarterOfDD )
|
|
{
|
|
delete mpDDInfo;
|
|
mpDDInfo = NULL;
|
|
}
|
|
}
|
|
|
|
rDTDE.Context->dropComplete( bChanges );
|
|
}
|
|
|
|
void Edit::dragEnter( const ::com::sun::star::datatransfer::dnd::DropTargetDragEnterEvent& rDTDE ) throw (::com::sun::star::uno::RuntimeException, std::exception)
|
|
{
|
|
if ( !mpDDInfo )
|
|
{
|
|
mpDDInfo = new DDInfo;
|
|
}
|
|
// search for string data type
|
|
const Sequence< com::sun::star::datatransfer::DataFlavor >& rFlavors( rDTDE.SupportedDataFlavors );
|
|
sal_Int32 nEle = rFlavors.getLength();
|
|
mpDDInfo->bIsStringSupported = false;
|
|
for( sal_Int32 i = 0; i < nEle; i++ )
|
|
{
|
|
sal_Int32 nIndex = 0;
|
|
OUString aMimetype = rFlavors[i].MimeType.getToken( 0, ';', nIndex );
|
|
if ( aMimetype == "text/plain" )
|
|
{
|
|
mpDDInfo->bIsStringSupported = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Edit::dragExit( const ::com::sun::star::datatransfer::dnd::DropTargetEvent& ) throw (::com::sun::star::uno::RuntimeException, std::exception)
|
|
{
|
|
SolarMutexGuard aVclGuard;
|
|
|
|
ImplHideDDCursor();
|
|
}
|
|
|
|
void Edit::dragOver( const ::com::sun::star::datatransfer::dnd::DropTargetDragEvent& rDTDE ) throw (::com::sun::star::uno::RuntimeException, std::exception)
|
|
{
|
|
SolarMutexGuard aVclGuard;
|
|
|
|
Point aMousePos( rDTDE.LocationX, rDTDE.LocationY );
|
|
|
|
sal_Int32 nPrevDropPos = mpDDInfo->nDropPos;
|
|
mpDDInfo->nDropPos = ImplGetCharPos( aMousePos );
|
|
|
|
/*
|
|
Size aOutSize = GetOutputSizePixel();
|
|
if ( ( aMousePos.X() < 0 ) || ( aMousePos.X() > aOutSize.Width() ) )
|
|
{
|
|
// Scroll?
|
|
// No, I will not receive events in this case....
|
|
}
|
|
*/
|
|
|
|
Selection aSel( maSelection );
|
|
aSel.Justify();
|
|
|
|
// Don't accept drop in selection or read-only field...
|
|
if ( IsReadOnly() || aSel.IsInside( mpDDInfo->nDropPos ) || ! mpDDInfo->bIsStringSupported )
|
|
{
|
|
ImplHideDDCursor();
|
|
rDTDE.Context->rejectDrag();
|
|
}
|
|
else
|
|
{
|
|
// Alten Cursor wegzeichnen...
|
|
if ( !mpDDInfo->bVisCursor || ( nPrevDropPos != mpDDInfo->nDropPos ) )
|
|
{
|
|
ImplHideDDCursor();
|
|
ImplShowDDCursor();
|
|
}
|
|
rDTDE.Context->acceptDrag( rDTDE.DropAction );
|
|
}
|
|
}
|
|
|
|
OUString Edit::GetSurroundingText() const
|
|
{
|
|
if (mpSubEdit)
|
|
return mpSubEdit->GetSurroundingText();
|
|
return maText.toString();
|
|
}
|
|
|
|
Selection Edit::GetSurroundingTextSelection() const
|
|
{
|
|
return GetSelection();
|
|
}
|
|
|
|
void Edit::SignalConnectAutocomplete(
|
|
boost::signals2::connection *const pConnection,
|
|
std::function<void (Edit *)> slot)
|
|
{
|
|
boost::signals2::connection const& rConnection(
|
|
m_pImpl->m_AutocompleteSignal.connect(slot));
|
|
if (pConnection)
|
|
*pConnection = rConnection;
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|