File: | home/maarten/src/libreoffice/core/include/rtl/ref.hxx |
Warning: | line 192, column 9 Use of memory after it is freed |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ | |||
2 | /* | |||
3 | * This file is part of the LibreOffice project. | |||
4 | * | |||
5 | * This Source Code Form is subject to the terms of the Mozilla Public | |||
6 | * License, v. 2.0. If a copy of the MPL was not distributed with this | |||
7 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. | |||
8 | * | |||
9 | * This file incorporates work covered by the following license notice: | |||
10 | * | |||
11 | * Licensed to the Apache Software Foundation (ASF) under one or more | |||
12 | * contributor license agreements. See the NOTICE file distributed | |||
13 | * with this work for additional information regarding copyright | |||
14 | * ownership. The ASF licenses this file to you under the Apache | |||
15 | * License, Version 2.0 (the "License"); you may not use this file | |||
16 | * except in compliance with the License. You may obtain a copy of | |||
17 | * the License at http://www.apache.org/licenses/LICENSE-2.0 . | |||
18 | */ | |||
19 | ||||
20 | #include <vcl/builder.hxx> | |||
21 | #include <vcl/event.hxx> | |||
22 | #include <vcl/cursor.hxx> | |||
23 | #include <vcl/menu.hxx> | |||
24 | #include <vcl/toolkit/edit.hxx> | |||
25 | #include <vcl/weld.hxx> | |||
26 | #include <vcl/specialchars.hxx> | |||
27 | #include <vcl/svapp.hxx> | |||
28 | #include <vcl/settings.hxx> | |||
29 | #include <vcl/uitest/uiobject.hxx> | |||
30 | #include <vcl/ptrstyle.hxx> | |||
31 | ||||
32 | #include <window.h> | |||
33 | #include <svdata.hxx> | |||
34 | #include <strings.hrc> | |||
35 | #include <controldata.hxx> | |||
36 | ||||
37 | #include <com/sun/star/i18n/BreakIterator.hpp> | |||
38 | #include <com/sun/star/i18n/CharacterIteratorMode.hpp> | |||
39 | #include <com/sun/star/i18n/WordType.hpp> | |||
40 | #include <com/sun/star/datatransfer/XTransferable.hpp> | |||
41 | #include <com/sun/star/datatransfer/clipboard/XClipboard.hpp> | |||
42 | ||||
43 | #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp> | |||
44 | #include <com/sun/star/datatransfer/dnd/XDragGestureRecognizer.hpp> | |||
45 | #include <com/sun/star/datatransfer/dnd/XDropTarget.hpp> | |||
46 | ||||
47 | #include <com/sun/star/i18n/InputSequenceChecker.hpp> | |||
48 | #include <com/sun/star/i18n/InputSequenceCheckMode.hpp> | |||
49 | #include <com/sun/star/i18n/ScriptType.hpp> | |||
50 | ||||
51 | #include <com/sun/star/uno/Any.hxx> | |||
52 | ||||
53 | #include <comphelper/processfactory.hxx> | |||
54 | #include <comphelper/string.hxx> | |||
55 | ||||
56 | #include <sot/exchange.hxx> | |||
57 | #include <sot/formats.hxx> | |||
58 | #include <sal/macros.h> | |||
59 | #include <sal/log.hxx> | |||
60 | ||||
61 | #include <i18nlangtag/languagetag.hxx> | |||
62 | #include <vcl/unohelp2.hxx> | |||
63 | #include <o3tl/safeint.hxx> | |||
64 | #include <officecfg/Office/Common.hxx> | |||
65 | ||||
66 | #include <algorithm> | |||
67 | #include <memory> | |||
68 | #include <string_view> | |||
69 | ||||
70 | using namespace ::com::sun::star; | |||
71 | using namespace ::com::sun::star::uno; | |||
72 | using namespace ::com::sun::star::lang; | |||
73 | ||||
74 | // - Redo | |||
75 | // - if Tracking-Cancel recreate DefaultSelection | |||
76 | ||||
77 | static FncGetSpecialChars pImplFncGetSpecialChars = nullptr; | |||
78 | ||||
79 | #define EDIT_ALIGN_LEFT1 1 | |||
80 | #define EDIT_ALIGN_CENTER2 2 | |||
81 | #define EDIT_ALIGN_RIGHT3 3 | |||
82 | ||||
83 | #define EDIT_DEL_LEFT1 1 | |||
84 | #define EDIT_DEL_RIGHT2 2 | |||
85 | ||||
86 | #define EDIT_DELMODE_SIMPLE11 11 | |||
87 | #define EDIT_DELMODE_RESTOFWORD12 12 | |||
88 | #define EDIT_DELMODE_RESTOFCONTENT13 13 | |||
89 | ||||
90 | struct DDInfo | |||
91 | { | |||
92 | vcl::Cursor aCursor; | |||
93 | Selection aDndStartSel; | |||
94 | sal_Int32 nDropPos; | |||
95 | bool bStarterOfDD; | |||
96 | bool bDroppedInMe; | |||
97 | bool bVisCursor; | |||
98 | bool bIsStringSupported; | |||
99 | ||||
100 | DDInfo() | |||
101 | { | |||
102 | aCursor.SetStyle( CURSOR_SHADOW(sal_uInt16(0x0001)) ); | |||
103 | nDropPos = 0; | |||
104 | bStarterOfDD = false; | |||
105 | bDroppedInMe = false; | |||
106 | bVisCursor = false; | |||
107 | bIsStringSupported = false; | |||
108 | } | |||
109 | }; | |||
110 | ||||
111 | struct Impl_IMEInfos | |||
112 | { | |||
113 | OUString aOldTextAfterStartPos; | |||
114 | std::unique_ptr<ExtTextInputAttr[]> | |||
115 | pAttribs; | |||
116 | sal_Int32 nPos; | |||
117 | sal_Int32 nLen; | |||
118 | bool bCursor; | |||
119 | bool bWasCursorOverwrite; | |||
120 | ||||
121 | Impl_IMEInfos(sal_Int32 nPos, const OUString& rOldTextAfterStartPos); | |||
122 | ||||
123 | void CopyAttribs(const ExtTextInputAttr* pA, sal_Int32 nL); | |||
124 | void DestroyAttribs(); | |||
125 | }; | |||
126 | ||||
127 | Impl_IMEInfos::Impl_IMEInfos(sal_Int32 nP, const OUString& rOldTextAfterStartPos) | |||
128 | : aOldTextAfterStartPos(rOldTextAfterStartPos), | |||
129 | nPos(nP), | |||
130 | nLen(0), | |||
131 | bCursor(true), | |||
132 | bWasCursorOverwrite(false) | |||
133 | { | |||
134 | } | |||
135 | ||||
136 | void Impl_IMEInfos::CopyAttribs(const ExtTextInputAttr* pA, sal_Int32 nL) | |||
137 | { | |||
138 | nLen = nL; | |||
139 | pAttribs.reset(new ExtTextInputAttr[ nL ]); | |||
140 | memcpy( pAttribs.get(), pA, nL*sizeof(ExtTextInputAttr) ); | |||
141 | } | |||
142 | ||||
143 | void Impl_IMEInfos::DestroyAttribs() | |||
144 | { | |||
145 | pAttribs.reset(); | |||
146 | nLen = 0; | |||
147 | } | |||
148 | ||||
149 | Edit::Edit( WindowType nType ) | |||
150 | : Control( nType ) | |||
151 | { | |||
152 | ImplInitEditData(); | |||
153 | } | |||
154 | ||||
155 | Edit::Edit( vcl::Window* pParent, WinBits nStyle ) | |||
156 | : Control( WindowType::EDIT ) | |||
157 | { | |||
158 | ImplInitEditData(); | |||
159 | ImplInit( pParent, nStyle ); | |||
160 | } | |||
161 | ||||
162 | void Edit::SetWidthInChars(sal_Int32 nWidthInChars) | |||
163 | { | |||
164 | if (mnWidthInChars != nWidthInChars) | |||
165 | { | |||
166 | mnWidthInChars = nWidthInChars; | |||
167 | queue_resize(); | |||
168 | } | |||
169 | } | |||
170 | ||||
171 | void Edit::setMaxWidthChars(sal_Int32 nWidth) | |||
172 | { | |||
173 | if (nWidth != mnMaxWidthChars) | |||
174 | { | |||
175 | mnMaxWidthChars = nWidth; | |||
176 | queue_resize(); | |||
177 | } | |||
178 | } | |||
179 | ||||
180 | bool Edit::set_property(const OString &rKey, const OUString &rValue) | |||
181 | { | |||
182 | if (rKey == "width-chars") | |||
183 | SetWidthInChars(rValue.toInt32()); | |||
184 | else if (rKey == "max-width-chars") | |||
185 | setMaxWidthChars(rValue.toInt32()); | |||
186 | else if (rKey == "max-length") | |||
187 | { | |||
188 | sal_Int32 nTextLen = rValue.toInt32(); | |||
189 | SetMaxTextLen(nTextLen == 0 ? EDIT_NOLIMIT((sal_Int32) 0x7FFFFFFF) : nTextLen); | |||
190 | } | |||
191 | else if (rKey == "editable") | |||
192 | { | |||
193 | SetReadOnly(!toBool(rValue)); | |||
194 | } | |||
195 | else if (rKey == "overwrite-mode") | |||
196 | { | |||
197 | SetInsertMode(!toBool(rValue)); | |||
198 | } | |||
199 | else if (rKey == "visibility") | |||
200 | { | |||
201 | mbPassword = false; | |||
202 | if (!toBool(rValue)) | |||
203 | mbPassword = true; | |||
204 | } | |||
205 | else if (rKey == "placeholder-text") | |||
206 | SetPlaceholderText(rValue); | |||
207 | else if (rKey == "shadow-type") | |||
208 | { | |||
209 | if (GetStyle() & WB_BORDER) | |||
210 | SetBorderStyle(rValue == "none" ? WindowBorderStyle::MONO : WindowBorderStyle::NORMAL); | |||
211 | } | |||
212 | else | |||
213 | return Control::set_property(rKey, rValue); | |||
214 | return true; | |||
215 | } | |||
216 | ||||
217 | Edit::~Edit() | |||
218 | { | |||
219 | disposeOnce(); | |||
220 | } | |||
221 | ||||
222 | void Edit::dispose() | |||
223 | { | |||
224 | mpUIBuilder.reset(); | |||
225 | mpDDInfo.reset(); | |||
226 | ||||
227 | vcl::Cursor* pCursor = GetCursor(); | |||
228 | if ( pCursor ) | |||
229 | { | |||
230 | SetCursor( nullptr ); | |||
231 | delete pCursor; | |||
232 | } | |||
233 | ||||
234 | mpIMEInfos.reset(); | |||
235 | ||||
236 | if ( mxDnDListener.is() ) | |||
237 | { | |||
238 | if ( GetDragGestureRecognizer().is() ) | |||
239 | { | |||
240 | uno::Reference< datatransfer::dnd::XDragGestureListener> xDGL( mxDnDListener, uno::UNO_QUERY ); | |||
241 | GetDragGestureRecognizer()->removeDragGestureListener( xDGL ); | |||
242 | } | |||
243 | if ( GetDropTarget().is() ) | |||
244 | { | |||
245 | uno::Reference< datatransfer::dnd::XDropTargetListener> xDTL( mxDnDListener, uno::UNO_QUERY ); | |||
246 | GetDropTarget()->removeDropTargetListener( xDTL ); | |||
247 | } | |||
248 | ||||
249 | mxDnDListener->disposing( lang::EventObject() ); // #95154# #96585# Empty Source means it's the Client | |||
250 | mxDnDListener.clear(); | |||
251 | } | |||
252 | ||||
253 | SetType(WindowType::WINDOW); | |||
254 | ||||
255 | mpSubEdit.disposeAndClear(); | |||
256 | Control::dispose(); | |||
257 | } | |||
258 | ||||
259 | void Edit::ImplInitEditData() | |||
260 | { | |||
261 | mpSubEdit = VclPtr<Edit>(); | |||
262 | mpFilterText = nullptr; | |||
263 | mnXOffset = 0; | |||
264 | mnAlign = EDIT_ALIGN_LEFT1; | |||
265 | mnMaxTextLen = EDIT_NOLIMIT((sal_Int32) 0x7FFFFFFF); | |||
266 | mnWidthInChars = -1; | |||
267 | mnMaxWidthChars = -1; | |||
268 | mbInternModified = false; | |||
269 | mbReadOnly = false; | |||
270 | mbInsertMode = true; | |||
271 | mbClickedInSelection = false; | |||
272 | mbActivePopup = false; | |||
273 | mbIsSubEdit = false; | |||
274 | mbForceControlBackground = false; | |||
275 | mbPassword = false; | |||
276 | mpDDInfo = nullptr; | |||
277 | mpIMEInfos = nullptr; | |||
278 | mcEchoChar = 0; | |||
279 | ||||
280 | // no default mirroring for Edit controls | |||
281 | // note: controls that use a subedit will revert this (SpinField, ComboBox) | |||
282 | EnableRTL( false ); | |||
283 | ||||
284 | vcl::unohelper::DragAndDropWrapper* pDnDWrapper = new vcl::unohelper::DragAndDropWrapper( this ); | |||
285 | mxDnDListener = pDnDWrapper; | |||
286 | } | |||
287 | ||||
288 | bool Edit::ImplUseNativeBorder(vcl::RenderContext const & rRenderContext, WinBits nStyle) | |||
289 | { | |||
290 | bool bRet = rRenderContext.IsNativeControlSupported(ImplGetNativeControlType(), | |||
291 | ControlPart::HasBackgroundTexture) | |||
292 | && ((nStyle & WB_BORDER) && !(nStyle & WB_NOBORDER)); | |||
293 | if (!bRet && mbIsSubEdit) | |||
294 | { | |||
295 | vcl::Window* pWindow = GetParent(); | |||
296 | nStyle = pWindow->GetStyle(); | |||
297 | bRet = pWindow->IsNativeControlSupported(ImplGetNativeControlType(), | |||
298 | ControlPart::HasBackgroundTexture) | |||
299 | && ((nStyle & WB_BORDER) && !(nStyle & WB_NOBORDER)); | |||
300 | } | |||
301 | return bRet; | |||
302 | } | |||
303 | ||||
304 | void Edit::ImplInit(vcl::Window* pParent, WinBits nStyle) | |||
305 | { | |||
306 | nStyle = ImplInitStyle(nStyle); | |||
307 | ||||
308 | if (!(nStyle & (WB_CENTER | WB_RIGHT))) | |||
309 | nStyle |= WB_LEFT; | |||
310 | ||||
311 | Control::ImplInit(pParent, nStyle, nullptr); | |||
312 | ||||
313 | mbReadOnly = (nStyle & WB_READONLY) != 0; | |||
314 | ||||
315 | mnAlign = EDIT_ALIGN_LEFT1; | |||
316 | ||||
317 | // hack: right align until keyinput and cursor travelling works | |||
318 | if( IsRTLEnabled() ) | |||
319 | mnAlign = EDIT_ALIGN_RIGHT3; | |||
320 | ||||
321 | if ( nStyle & WB_RIGHT ) | |||
322 | mnAlign = EDIT_ALIGN_RIGHT3; | |||
323 | else if ( nStyle & WB_CENTER ) | |||
324 | mnAlign = EDIT_ALIGN_CENTER2; | |||
325 | ||||
326 | SetCursor( new vcl::Cursor ); | |||
327 | ||||
328 | SetPointer( PointerStyle::Text ); | |||
329 | ApplySettings(*this); | |||
330 | ||||
331 | uno::Reference< datatransfer::dnd::XDragGestureListener> xDGL( mxDnDListener, uno::UNO_QUERY ); | |||
332 | uno::Reference< datatransfer::dnd::XDragGestureRecognizer > xDGR = GetDragGestureRecognizer(); | |||
333 | if ( xDGR.is() ) | |||
334 | { | |||
335 | xDGR->addDragGestureListener( xDGL ); | |||
336 | uno::Reference< datatransfer::dnd::XDropTargetListener> xDTL( mxDnDListener, uno::UNO_QUERY ); | |||
337 | GetDropTarget()->addDropTargetListener( xDTL ); | |||
338 | GetDropTarget()->setActive( true ); | |||
339 | GetDropTarget()->setDefaultActions( datatransfer::dnd::DNDConstants::ACTION_COPY_OR_MOVE ); | |||
340 | } | |||
341 | } | |||
342 | ||||
343 | WinBits Edit::ImplInitStyle( WinBits nStyle ) | |||
344 | { | |||
345 | if ( !(nStyle & WB_NOTABSTOP) ) | |||
346 | nStyle |= WB_TABSTOP; | |||
347 | if ( !(nStyle & WB_NOGROUP) ) | |||
348 | nStyle |= WB_GROUP; | |||
349 | ||||
350 | return nStyle; | |||
351 | } | |||
352 | ||||
353 | bool Edit::IsCharInput( const KeyEvent& rKeyEvent ) | |||
354 | { | |||
355 | // In the future we must use new Unicode functions for this | |||
356 | sal_Unicode cCharCode = rKeyEvent.GetCharCode(); | |||
357 | return ((cCharCode >= 32) && (cCharCode != 127) && | |||
358 | !rKeyEvent.GetKeyCode().IsMod3() && | |||
359 | !rKeyEvent.GetKeyCode().IsMod2() && | |||
360 | !rKeyEvent.GetKeyCode().IsMod1() ); | |||
361 | } | |||
362 | ||||
363 | void Edit::ApplySettings(vcl::RenderContext& rRenderContext) | |||
364 | { | |||
365 | Control::ApplySettings(rRenderContext); | |||
366 | ||||
367 | const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings(); | |||
368 | ||||
369 | const vcl::Font& aFont = rStyleSettings.GetFieldFont(); | |||
370 | ApplyControlFont(rRenderContext, aFont); | |||
371 | ||||
372 | ImplClearLayoutData(); | |||
373 | ||||
374 | Color aTextColor = rStyleSettings.GetFieldTextColor(); | |||
375 | ApplyControlForeground(rRenderContext, aTextColor); | |||
376 | ||||
377 | if (IsControlBackground()) | |||
378 | { | |||
379 | rRenderContext.SetBackground(GetControlBackground()); | |||
380 | rRenderContext.SetFillColor(GetControlBackground()); | |||
381 | ||||
382 | if (ImplUseNativeBorder(rRenderContext, GetStyle())) | |||
383 | { | |||
384 | // indicates that no non-native drawing of background should take place | |||
385 | mpWindowImpl->mnNativeBackground = ControlPart::Entire; | |||
386 | } | |||
387 | } | |||
388 | else if (ImplUseNativeBorder(rRenderContext, GetStyle())) | |||
389 | { | |||
390 | // Transparent background | |||
391 | rRenderContext.SetBackground(); | |||
392 | rRenderContext.SetFillColor(); | |||
393 | } | |||
394 | else | |||
395 | { | |||
396 | rRenderContext.SetBackground(rStyleSettings.GetFieldColor()); | |||
397 | rRenderContext.SetFillColor(rStyleSettings.GetFieldColor()); | |||
398 | } | |||
399 | } | |||
400 | ||||
401 | long Edit::ImplGetExtraXOffset() const | |||
402 | { | |||
403 | // MT 09/2002: nExtraOffsetX should become a member, instead of checking every time, | |||
404 | // but I need an incompatible update for this... | |||
405 | // #94095# Use extra offset only when edit has a border | |||
406 | long nExtraOffset = 0; | |||
407 | if( ( GetStyle() & WB_BORDER ) || ( mbIsSubEdit && ( GetParent()->GetStyle() & WB_BORDER ) ) ) | |||
408 | nExtraOffset = 2; | |||
409 | ||||
410 | return nExtraOffset; | |||
411 | } | |||
412 | ||||
413 | long Edit::ImplGetExtraYOffset() const | |||
414 | { | |||
415 | long nExtraOffset = 0; | |||
416 | ControlType eCtrlType = ImplGetNativeControlType(); | |||
417 | if (eCtrlType != ControlType::EditboxNoBorder) | |||
418 | { | |||
419 | // add some space between text entry and border | |||
420 | nExtraOffset = 2; | |||
421 | } | |||
422 | return nExtraOffset; | |||
423 | } | |||
424 | ||||
425 | OUString Edit::ImplGetText() const | |||
426 | { | |||
427 | if ( mcEchoChar || mbPassword ) | |||
428 | { | |||
429 | sal_Unicode cEchoChar; | |||
430 | if ( mcEchoChar ) | |||
431 | cEchoChar = mcEchoChar; | |||
432 | else | |||
433 | cEchoChar = u'\x2022'; | |||
434 | OUStringBuffer aText; | |||
435 | comphelper::string::padToLength(aText, maText.getLength(), cEchoChar); | |||
436 | return aText.makeStringAndClear(); | |||
437 | } | |||
438 | else | |||
439 | return maText.toString(); | |||
440 | } | |||
441 | ||||
442 | void Edit::ImplInvalidateOrRepaint() | |||
443 | { | |||
444 | if( IsPaintTransparent() ) | |||
445 | { | |||
446 | Invalidate(); | |||
447 | // FIXME: this is currently only on macOS | |||
448 | if( ImplGetSVData()->maNWFData.mbNoFocusRects ) | |||
449 | PaintImmediately(); | |||
450 | } | |||
451 | else | |||
452 | Invalidate(); | |||
453 | } | |||
454 | ||||
455 | long Edit::ImplGetTextYPosition() const | |||
456 | { | |||
457 | if ( GetStyle() & WB_TOP ) | |||
458 | return ImplGetExtraXOffset(); | |||
459 | else if ( GetStyle() & WB_BOTTOM ) | |||
460 | return GetOutputSizePixel().Height() - GetTextHeight() - ImplGetExtraXOffset(); | |||
461 | return ( GetOutputSizePixel().Height() - GetTextHeight() ) / 2; | |||
462 | } | |||
463 | ||||
464 | void Edit::ImplRepaint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRectangle) | |||
465 | { | |||
466 | if (!IsReallyVisible()) | |||
467 | return; | |||
468 | ||||
469 | ApplySettings(rRenderContext); | |||
470 | ||||
471 | const OUString aText = ImplGetText(); | |||
472 | const sal_Int32 nLen = aText.getLength(); | |||
473 | ||||
474 | long nDXBuffer[256]; | |||
475 | std::unique_ptr<long[]> pDXBuffer; | |||
476 | long* pDX = nDXBuffer; | |||
477 | ||||
478 | if (nLen) | |||
479 | { | |||
480 | if (o3tl::make_unsigned(2 * nLen) > SAL_N_ELEMENTS(nDXBuffer)(sizeof(sal_n_array_size(nDXBuffer)))) | |||
481 | { | |||
482 | pDXBuffer.reset(new long[2 * (nLen + 1)]); | |||
483 | pDX = pDXBuffer.get(); | |||
484 | } | |||
485 | ||||
486 | GetCaretPositions(aText, pDX, 0, nLen); | |||
487 | } | |||
488 | ||||
489 | long nTH = GetTextHeight(); | |||
490 | Point aPos(mnXOffset, ImplGetTextYPosition()); | |||
491 | ||||
492 | vcl::Cursor* pCursor = GetCursor(); | |||
493 | bool bVisCursor = pCursor && pCursor->IsVisible(); | |||
494 | if (pCursor) | |||
495 | pCursor->Hide(); | |||
496 | ||||
497 | ImplClearBackground(rRenderContext, rRectangle, 0, GetOutputSizePixel().Width()-1); | |||
498 | ||||
499 | bool bPaintPlaceholderText = aText.isEmpty() && !maPlaceholderText.isEmpty(); | |||
500 | ||||
501 | const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings(); | |||
502 | ||||
503 | if (!IsEnabled() || bPaintPlaceholderText) | |||
504 | rRenderContext.SetTextColor(rStyleSettings.GetDisableColor()); | |||
505 | ||||
506 | // Set background color of the normal text | |||
507 | if (mbForceControlBackground && IsControlBackground()) | |||
508 | { | |||
509 | // check if we need to set ControlBackground even in NWF case | |||
510 | rRenderContext.Push(PushFlags::FILLCOLOR | PushFlags::LINECOLOR); | |||
511 | rRenderContext.SetLineColor(); | |||
512 | rRenderContext.SetFillColor(GetControlBackground()); | |||
513 | rRenderContext.DrawRect(tools::Rectangle(aPos, Size(GetOutputSizePixel().Width() - 2 * mnXOffset, GetOutputSizePixel().Height()))); | |||
514 | rRenderContext.Pop(); | |||
515 | ||||
516 | rRenderContext.SetTextFillColor(GetControlBackground()); | |||
517 | } | |||
518 | else if (IsPaintTransparent() || ImplUseNativeBorder(rRenderContext, GetStyle())) | |||
519 | rRenderContext.SetTextFillColor(); | |||
520 | else | |||
521 | rRenderContext.SetTextFillColor(IsControlBackground() ? GetControlBackground() : rStyleSettings.GetFieldColor()); | |||
522 | ||||
523 | ImplPaintBorder(rRenderContext); | |||
524 | ||||
525 | bool bDrawSelection = maSelection.Len() && (HasFocus() || (GetStyle() & WB_NOHIDESELECTION) || mbActivePopup); | |||
526 | ||||
527 | aPos.setX( mnXOffset + ImplGetExtraXOffset() ); | |||
528 | if (bPaintPlaceholderText) | |||
529 | { | |||
530 | rRenderContext.DrawText(aPos, maPlaceholderText); | |||
531 | } | |||
532 | else if (!bDrawSelection && !mpIMEInfos) | |||
533 | { | |||
534 | rRenderContext.DrawText(aPos, aText, 0, nLen); | |||
535 | } | |||
536 | else | |||
537 | { | |||
538 | // save graphics state | |||
539 | rRenderContext.Push(); | |||
540 | // first calculate highlighted and non highlighted clip regions | |||
541 | vcl::Region aHighlightClipRegion; | |||
542 | vcl::Region aNormalClipRegion; | |||
543 | Selection aTmpSel(maSelection); | |||
544 | aTmpSel.Justify(); | |||
545 | // selection is highlighted | |||
546 | for(sal_Int32 i = 0; i < nLen; ++i) | |||
547 | { | |||
548 | tools::Rectangle aRect(aPos, Size(10, nTH)); | |||
549 | aRect.SetLeft( pDX[2 * i] + mnXOffset + ImplGetExtraXOffset() ); | |||
550 | aRect.SetRight( pDX[2 * i + 1] + mnXOffset + ImplGetExtraXOffset() ); | |||
551 | aRect.Justify(); | |||
552 | bool bHighlight = false; | |||
553 | if (i >= aTmpSel.Min() && i < aTmpSel.Max()) | |||
554 | bHighlight = true; | |||
555 | ||||
556 | if (mpIMEInfos && mpIMEInfos->pAttribs && | |||
557 | i >= mpIMEInfos->nPos && i < (mpIMEInfos->nPos+mpIMEInfos->nLen) && | |||
558 | (mpIMEInfos->pAttribs[i - mpIMEInfos->nPos] & ExtTextInputAttr::Highlight)) | |||
559 | { | |||
560 | bHighlight = true; | |||
561 | } | |||
562 | ||||
563 | if (bHighlight) | |||
564 | aHighlightClipRegion.Union(aRect); | |||
565 | else | |||
566 | aNormalClipRegion.Union(aRect); | |||
567 | } | |||
568 | // draw normal text | |||
569 | Color aNormalTextColor = rRenderContext.GetTextColor(); | |||
570 | rRenderContext.SetClipRegion(aNormalClipRegion); | |||
571 | ||||
572 | if (IsPaintTransparent()) | |||
573 | rRenderContext.SetTextFillColor(); | |||
574 | else | |||
575 | { | |||
576 | // Set background color when part of the text is selected | |||
577 | if (ImplUseNativeBorder(rRenderContext, GetStyle())) | |||
578 | { | |||
579 | if( mbForceControlBackground && IsControlBackground() ) | |||
580 | rRenderContext.SetTextFillColor(GetControlBackground()); | |||
581 | else | |||
582 | rRenderContext.SetTextFillColor(); | |||
583 | } | |||
584 | else | |||
585 | { | |||
586 | rRenderContext.SetTextFillColor(IsControlBackground() ? GetControlBackground() : rStyleSettings.GetFieldColor()); | |||
587 | } | |||
588 | } | |||
589 | rRenderContext.DrawText(aPos, aText, 0, nLen); | |||
590 | ||||
591 | // draw highlighted text | |||
592 | rRenderContext.SetClipRegion(aHighlightClipRegion); | |||
593 | rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor()); | |||
594 | rRenderContext.SetTextFillColor(rStyleSettings.GetHighlightColor()); | |||
595 | rRenderContext.DrawText(aPos, aText, 0, nLen); | |||
596 | ||||
597 | // if IME info exists loop over portions and output different font attributes | |||
598 | if (mpIMEInfos && mpIMEInfos->pAttribs) | |||
599 | { | |||
600 | for(int n = 0; n < 2; n++) | |||
601 | { | |||
602 | vcl::Region aRegion; | |||
603 | if (n == 0) | |||
604 | { | |||
605 | rRenderContext.SetTextColor(aNormalTextColor); | |||
606 | if (IsPaintTransparent()) | |||
607 | rRenderContext.SetTextFillColor(); | |||
608 | else | |||
609 | rRenderContext.SetTextFillColor(IsControlBackground() ? GetControlBackground() : rStyleSettings.GetFieldColor()); | |||
610 | aRegion = aNormalClipRegion; | |||
611 | } | |||
612 | else | |||
613 | { | |||
614 | rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor()); | |||
615 | rRenderContext.SetTextFillColor(rStyleSettings.GetHighlightColor()); | |||
616 | aRegion = aHighlightClipRegion; | |||
617 | } | |||
618 | ||||
619 | for(int i = 0; i < mpIMEInfos->nLen; ) | |||
620 | { | |||
621 | ExtTextInputAttr nAttr = mpIMEInfos->pAttribs[i]; | |||
622 | vcl::Region aClip; | |||
623 | int nIndex = i; | |||
624 | while (nIndex < mpIMEInfos->nLen && mpIMEInfos->pAttribs[nIndex] == nAttr) // #112631# check nIndex before using it | |||
625 | { | |||
626 | tools::Rectangle aRect( aPos, Size( 10, nTH ) ); | |||
627 | aRect.SetLeft( pDX[2 * (nIndex + mpIMEInfos->nPos)] + mnXOffset + ImplGetExtraXOffset() ); | |||
628 | aRect.SetRight( pDX[2 * (nIndex + mpIMEInfos->nPos) + 1] + mnXOffset + ImplGetExtraXOffset() ); | |||
629 | aRect.Justify(); | |||
630 | aClip.Union(aRect); | |||
631 | nIndex++; | |||
632 | } | |||
633 | i = nIndex; | |||
634 | aClip.Intersect(aRegion); | |||
635 | if (!aClip.IsEmpty() && nAttr != ExtTextInputAttr::NONE) | |||
636 | { | |||
637 | vcl::Font aFont = rRenderContext.GetFont(); | |||
638 | if (nAttr & ExtTextInputAttr::Underline) | |||
639 | aFont.SetUnderline(LINESTYLE_SINGLE); | |||
640 | else if (nAttr & ExtTextInputAttr::BoldUnderline) | |||
641 | aFont.SetUnderline( LINESTYLE_BOLD); | |||
642 | else if (nAttr & ExtTextInputAttr::DottedUnderline) | |||
643 | aFont.SetUnderline( LINESTYLE_DOTTED); | |||
644 | else if (nAttr & ExtTextInputAttr::DashDotUnderline) | |||
645 | aFont.SetUnderline( LINESTYLE_DASHDOT); | |||
646 | else if (nAttr & ExtTextInputAttr::GrayWaveline) | |||
647 | { | |||
648 | aFont.SetUnderline(LINESTYLE_WAVE); | |||
649 | rRenderContext.SetTextLineColor(COL_LIGHTGRAY); | |||
650 | } | |||
651 | rRenderContext.SetFont(aFont); | |||
652 | ||||
653 | if (nAttr & ExtTextInputAttr::RedText) | |||
654 | rRenderContext.SetTextColor(COL_RED); | |||
655 | else if (nAttr & ExtTextInputAttr::HalfToneText) | |||
656 | rRenderContext.SetTextColor(COL_LIGHTGRAY); | |||
657 | ||||
658 | rRenderContext.SetClipRegion(aClip); | |||
659 | rRenderContext.DrawText(aPos, aText, 0, nLen); | |||
660 | } | |||
661 | } | |||
662 | } | |||
663 | } | |||
664 | ||||
665 | // restore graphics state | |||
666 | rRenderContext.Pop(); | |||
667 | } | |||
668 | ||||
669 | if (bVisCursor && (!mpIMEInfos || mpIMEInfos->bCursor)) | |||
670 | pCursor->Show(); | |||
671 | } | |||
672 | ||||
673 | void Edit::ImplDelete( const Selection& rSelection, sal_uInt8 nDirection, sal_uInt8 nMode ) | |||
674 | { | |||
675 | const sal_Int32 nTextLen = ImplGetText().getLength(); | |||
676 | ||||
677 | // deleting possible? | |||
678 | if ( !rSelection.Len() && | |||
679 | (((rSelection.Min() == 0) && (nDirection == EDIT_DEL_LEFT1)) || | |||
680 | ((rSelection.Max() == nTextLen) && (nDirection == EDIT_DEL_RIGHT2))) ) | |||
681 | return; | |||
682 | ||||
683 | ImplClearLayoutData(); | |||
684 | ||||
685 | Selection aSelection( rSelection ); | |||
686 | aSelection.Justify(); | |||
687 | ||||
688 | if ( !aSelection.Len() ) | |||
689 | { | |||
690 | uno::Reference < i18n::XBreakIterator > xBI = ImplGetBreakIterator(); | |||
691 | if ( nDirection == EDIT_DEL_LEFT1 ) | |||
692 | { | |||
693 | if ( nMode == EDIT_DELMODE_RESTOFWORD12 ) | |||
694 | { | |||
695 | i18n::Boundary aBoundary = xBI->getWordBoundary( maText.toString(), aSelection.Min(), | |||
696 | GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, true ); | |||
697 | auto startPos = aBoundary.startPos; | |||
698 | if ( startPos == aSelection.Min() ) | |||
699 | { | |||
700 | aBoundary = xBI->previousWord( maText.toString(), aSelection.Min(), | |||
701 | GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES ); | |||
702 | startPos = std::max(aBoundary.startPos, sal_Int32(0)); | |||
703 | } | |||
704 | aSelection.Min() = startPos; | |||
705 | } | |||
706 | else if ( nMode == EDIT_DELMODE_RESTOFCONTENT13 ) | |||
707 | { | |||
708 | aSelection.Min() = 0; | |||
709 | } | |||
710 | else | |||
711 | { | |||
712 | sal_Int32 nCount = 1; | |||
713 | aSelection.Min() = xBI->previousCharacters( maText.toString(), aSelection.Min(), | |||
714 | GetSettings().GetLanguageTag().getLocale(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount ); | |||
715 | } | |||
716 | } | |||
717 | else | |||
718 | { | |||
719 | if ( nMode == EDIT_DELMODE_RESTOFWORD12 ) | |||
720 | { | |||
721 | i18n::Boundary aBoundary = xBI->nextWord( maText.toString(), aSelection.Max(), | |||
722 | GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES ); | |||
723 | aSelection.Max() = aBoundary.startPos; | |||
724 | } | |||
725 | else if ( nMode == EDIT_DELMODE_RESTOFCONTENT13 ) | |||
726 | { | |||
727 | aSelection.Max() = nTextLen; | |||
728 | } | |||
729 | else | |||
730 | { | |||
731 | sal_Int32 nCount = 1; | |||
732 | aSelection.Max() = xBI->nextCharacters( maText.toString(), aSelection.Max(), | |||
733 | GetSettings().GetLanguageTag().getLocale(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount ); | |||
734 | } | |||
735 | } | |||
736 | } | |||
737 | ||||
738 | const auto nSelectionMin = aSelection.Min(); | |||
739 | maText.remove( nSelectionMin, aSelection.Len() ); | |||
740 | maSelection.Min() = nSelectionMin; | |||
741 | maSelection.Max() = nSelectionMin; | |||
742 | ImplAlignAndPaint(); | |||
743 | mbInternModified = true; | |||
744 | } | |||
745 | ||||
746 | OUString Edit::ImplGetValidString( const OUString& rString ) | |||
747 | { | |||
748 | OUString aValidString = rString.replaceAll("\n", "").replaceAll("\r", ""); | |||
749 | aValidString = aValidString.replace('\t', ' '); | |||
750 | return aValidString; | |||
751 | } | |||
752 | ||||
753 | uno::Reference <i18n::XBreakIterator> const& Edit::ImplGetBreakIterator() | |||
754 | { | |||
755 | if (!mxBreakIterator) | |||
756 | mxBreakIterator = i18n::BreakIterator::create(::comphelper::getProcessComponentContext()); | |||
757 | return mxBreakIterator; | |||
758 | } | |||
759 | ||||
760 | uno::Reference <i18n::XExtendedInputSequenceChecker> const& Edit::ImplGetInputSequenceChecker() | |||
761 | { | |||
762 | if (!mxISC.is()) | |||
763 | mxISC = i18n::InputSequenceChecker::create(::comphelper::getProcessComponentContext()); | |||
764 | return mxISC; | |||
765 | } | |||
766 | ||||
767 | void Edit::ShowTruncationWarning(weld::Widget* pParent) | |||
768 | { | |||
769 | std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent, VclMessageType::Warning, | |||
770 | VclButtonsType::Ok, VclResId(SV_EDIT_WARNING_STRreinterpret_cast<char const *>("SV_EDIT_WARNING_STR" "\004" u8"The inserted text exceeded the maximum length of this text field. The text was truncated." )))); | |||
771 | xBox->run(); | |||
772 | } | |||
773 | ||||
774 | bool Edit::ImplTruncateToMaxLen( OUString& rStr, sal_Int32 nSelectionLen ) const | |||
775 | { | |||
776 | bool bWasTruncated = false; | |||
777 | if (maText.getLength() - nSelectionLen > mnMaxTextLen - rStr.getLength()) | |||
778 | { | |||
779 | sal_Int32 nErasePos = mnMaxTextLen - maText.getLength() + nSelectionLen; | |||
780 | rStr = rStr.copy( 0, nErasePos ); | |||
781 | bWasTruncated = true; | |||
782 | } | |||
783 | return bWasTruncated; | |||
784 | } | |||
785 | ||||
786 | void Edit::ImplInsertText( const OUString& rStr, const Selection* pNewSel, bool bIsUserInput ) | |||
787 | { | |||
788 | Selection aSelection( maSelection ); | |||
789 | aSelection.Justify(); | |||
790 | ||||
791 | OUString aNewText( ImplGetValidString( rStr ) ); | |||
792 | ||||
793 | // as below, if there's no selection, but we're in overwrite mode and not beyond | |||
794 | // the end of the existing text then that's like a selection of 1 | |||
795 | auto nSelectionLen = aSelection.Len(); | |||
796 | if (!nSelectionLen && !mbInsertMode && aSelection.Max() < maText.getLength()) | |||
797 | nSelectionLen = 1; | |||
798 | ImplTruncateToMaxLen( aNewText, nSelectionLen ); | |||
799 | ||||
800 | ImplClearLayoutData(); | |||
801 | ||||
802 | if ( aSelection.Len() ) | |||
803 | maText.remove( aSelection.Min(), aSelection.Len() ); | |||
804 | else if (!mbInsertMode && aSelection.Max() < maText.getLength()) | |||
805 | maText.remove( aSelection.Max(), 1 ); | |||
806 | ||||
807 | // take care of input-sequence-checking now | |||
808 | if (bIsUserInput && !rStr.isEmpty()) | |||
809 | { | |||
810 | SAL_WARN_IF( rStr.getLength() != 1, "vcl", "unexpected string length. User input is expected to provide 1 char only!" )do { if (true && (rStr.getLength() != 1)) { switch (sal_detail_log_report (::SAL_DETAIL_LOG_LEVEL_WARN, "vcl")) { case SAL_DETAIL_LOG_ACTION_IGNORE : break; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail ::getResult( ::sal::detail::StreamStart() << "unexpected string length. User input is expected to provide 1 char only!" ) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl" ), ("/home/maarten/src/libreoffice/core/vcl/source/control/edit.cxx" ":" "810" ": "), ::sal::detail::unwrapStream( ::sal::detail:: StreamStart() << "unexpected string length. User input is expected to provide 1 char only!" ), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream << "unexpected string length. User input is expected to provide 1 char only!" ; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl"), ("/home/maarten/src/libreoffice/core/vcl/source/control/edit.cxx" ":" "810" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL : if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart () << "unexpected string length. User input is expected to provide 1 char only!" ) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl" ), ("/home/maarten/src/libreoffice/core/vcl/source/control/edit.cxx" ":" "810" ": "), ::sal::detail::unwrapStream( ::sal::detail:: StreamStart() << "unexpected string length. User input is expected to provide 1 char only!" ), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream << "unexpected string length. User input is expected to provide 1 char only!" ; ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl"), ("/home/maarten/src/libreoffice/core/vcl/source/control/edit.cxx" ":" "810" ": "), sal_detail_stream, 0); }; std::abort(); break ; } } } while (false); | |||
811 | ||||
812 | // determine if input-sequence-checking should be applied or not | |||
813 | ||||
814 | uno::Reference < i18n::XBreakIterator > xBI = ImplGetBreakIterator(); | |||
815 | bool bIsInputSequenceChecking = rStr.getLength() == 1 && | |||
816 | officecfg::Office::Common::I18N::CTL::CTLFont::get() && | |||
817 | officecfg::Office::Common::I18N::CTL::CTLSequenceChecking::get() && | |||
818 | aSelection.Min() > 0 && /* first char needs not to be checked */ | |||
819 | xBI.is() && i18n::ScriptType::COMPLEX == xBI->getScriptType( rStr, 0 ); | |||
820 | ||||
821 | if (bIsInputSequenceChecking) | |||
822 | { | |||
823 | uno::Reference < i18n::XExtendedInputSequenceChecker > xISC = ImplGetInputSequenceChecker(); | |||
824 | if (xISC.is()) | |||
825 | { | |||
826 | sal_Unicode cChar = rStr[0]; | |||
827 | sal_Int32 nTmpPos = aSelection.Min(); | |||
828 | sal_Int16 nCheckMode = officecfg::Office::Common::I18N::CTL::CTLSequenceCheckingRestricted::get()? | |||
829 | i18n::InputSequenceCheckMode::STRICT : i18n::InputSequenceCheckMode::BASIC; | |||
830 | ||||
831 | // the text that needs to be checked is only the one | |||
832 | // before the current cursor position | |||
833 | const OUString aOldText( maText.getStr(), nTmpPos); | |||
834 | OUString aTmpText( aOldText ); | |||
835 | if (officecfg::Office::Common::I18N::CTL::CTLSequenceCheckingTypeAndReplace::get()) | |||
836 | { | |||
837 | xISC->correctInputSequence( aTmpText, nTmpPos - 1, cChar, nCheckMode ); | |||
838 | ||||
839 | // find position of first character that has changed | |||
840 | sal_Int32 nOldLen = aOldText.getLength(); | |||
841 | sal_Int32 nTmpLen = aTmpText.getLength(); | |||
842 | const sal_Unicode *pOldTxt = aOldText.getStr(); | |||
843 | const sal_Unicode *pTmpTxt = aTmpText.getStr(); | |||
844 | sal_Int32 nChgPos = 0; | |||
845 | while ( nChgPos < nOldLen && nChgPos < nTmpLen && | |||
846 | pOldTxt[nChgPos] == pTmpTxt[nChgPos] ) | |||
847 | ++nChgPos; | |||
848 | ||||
849 | const OUString aChgText( aTmpText.copy( nChgPos ) ); | |||
850 | ||||
851 | // remove text from first pos to be changed to current pos | |||
852 | maText.remove( nChgPos, nTmpPos - nChgPos ); | |||
853 | ||||
854 | if (!aChgText.isEmpty()) | |||
855 | { | |||
856 | aNewText = aChgText; | |||
857 | aSelection.Min() = nChgPos; // position for new text to be inserted | |||
858 | } | |||
859 | else | |||
860 | aNewText.clear(); | |||
861 | } | |||
862 | else | |||
863 | { | |||
864 | // should the character be ignored (i.e. not get inserted) ? | |||
865 | if (!xISC->checkInputSequence( aOldText, nTmpPos - 1, cChar, nCheckMode )) | |||
866 | aNewText.clear(); | |||
867 | } | |||
868 | } | |||
869 | } | |||
870 | ||||
871 | // at this point now we will insert the non-empty text 'normally' some lines below... | |||
872 | } | |||
873 | ||||
874 | if ( !aNewText.isEmpty() ) | |||
875 | maText.insert( aSelection.Min(), aNewText ); | |||
876 | ||||
877 | if ( !pNewSel ) | |||
878 | { | |||
879 | maSelection.Min() = aSelection.Min() + aNewText.getLength(); | |||
880 | maSelection.Max() = maSelection.Min(); | |||
881 | } | |||
882 | else | |||
883 | { | |||
884 | maSelection = *pNewSel; | |||
885 | if ( maSelection.Min() > maText.getLength() ) | |||
886 | maSelection.Min() = maText.getLength(); | |||
887 | if ( maSelection.Max() > maText.getLength() ) | |||
888 | maSelection.Max() = maText.getLength(); | |||
889 | } | |||
890 | ||||
891 | ImplAlignAndPaint(); | |||
892 | mbInternModified = true; | |||
893 | } | |||
894 | ||||
895 | void Edit::ImplSetText( const OUString& rText, const Selection* pNewSelection ) | |||
896 | { | |||
897 | // we delete text by "selecting" the old text completely then calling InsertText; this is flicker free | |||
898 | if ( ( rText.getLength() > mnMaxTextLen ) || | |||
899 | ( std::u16string_view(rText) == std::u16string_view(maText.getStr(), maText.getLength()) | |||
900 | && (!pNewSelection || (*pNewSelection == maSelection)) ) ) | |||
901 | return; | |||
902 | ||||
903 | ImplClearLayoutData(); | |||
904 | maSelection.Min() = 0; | |||
905 | maSelection.Max() = maText.getLength(); | |||
906 | if ( mnXOffset || HasPaintEvent() ) | |||
907 | { | |||
908 | mnXOffset = 0; | |||
909 | maText = ImplGetValidString( rText ); | |||
910 | ||||
911 | // #i54929# recalculate mnXOffset before ImplSetSelection, | |||
912 | // else cursor ends up in wrong position | |||
913 | ImplAlign(); | |||
914 | ||||
915 | if ( pNewSelection ) | |||
916 | ImplSetSelection( *pNewSelection, false ); | |||
917 | ||||
918 | if ( mnXOffset && !pNewSelection ) | |||
919 | maSelection.Max() = 0; | |||
920 | ||||
921 | Invalidate(); | |||
922 | } | |||
923 | else | |||
924 | ImplInsertText( rText, pNewSelection ); | |||
925 | ||||
926 | CallEventListeners( VclEventId::EditModify ); | |||
927 | } | |||
928 | ||||
929 | ControlType Edit::ImplGetNativeControlType() const | |||
930 | { | |||
931 | ControlType nCtrl = ControlType::Generic; | |||
932 | const vcl::Window* pControl = mbIsSubEdit ? GetParent() : this; | |||
933 | ||||
934 | switch (pControl->GetType()) | |||
935 | { | |||
936 | case WindowType::COMBOBOX: | |||
937 | case WindowType::PATTERNBOX: | |||
938 | case WindowType::NUMERICBOX: | |||
939 | case WindowType::METRICBOX: | |||
940 | case WindowType::CURRENCYBOX: | |||
941 | case WindowType::DATEBOX: | |||
942 | case WindowType::TIMEBOX: | |||
943 | case WindowType::LONGCURRENCYBOX: | |||
944 | nCtrl = ControlType::Combobox; | |||
945 | break; | |||
946 | ||||
947 | case WindowType::MULTILINEEDIT: | |||
948 | if ( GetWindow( GetWindowType::Border ) != this ) | |||
949 | nCtrl = ControlType::MultilineEditbox; | |||
950 | else | |||
951 | nCtrl = ControlType::EditboxNoBorder; | |||
952 | break; | |||
953 | ||||
954 | case WindowType::EDIT: | |||
955 | case WindowType::PATTERNFIELD: | |||
956 | case WindowType::METRICFIELD: | |||
957 | case WindowType::CURRENCYFIELD: | |||
958 | case WindowType::DATEFIELD: | |||
959 | case WindowType::TIMEFIELD: | |||
960 | case WindowType::SPINFIELD: | |||
961 | case WindowType::FORMATTEDFIELD: | |||
962 | if (pControl->GetStyle() & WB_SPIN) | |||
963 | nCtrl = ControlType::Spinbox; | |||
964 | else | |||
965 | { | |||
966 | if (GetWindow(GetWindowType::Border) != this) | |||
967 | nCtrl = ControlType::Editbox; | |||
968 | else | |||
969 | nCtrl = ControlType::EditboxNoBorder; | |||
970 | } | |||
971 | break; | |||
972 | ||||
973 | default: | |||
974 | nCtrl = ControlType::Editbox; | |||
975 | } | |||
976 | return nCtrl; | |||
977 | } | |||
978 | ||||
979 | void Edit::ImplClearBackground(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRectangle, long nXStart, long nXEnd ) | |||
980 | { | |||
981 | /* | |||
982 | * note: at this point the cursor must be switched off already | |||
983 | */ | |||
984 | tools::Rectangle aRect(Point(), GetOutputSizePixel()); | |||
985 | aRect.SetLeft( nXStart ); | |||
986 | aRect.SetRight( nXEnd ); | |||
987 | ||||
988 | if( !(ImplUseNativeBorder(rRenderContext, GetStyle()) || IsPaintTransparent())) | |||
989 | rRenderContext.Erase(aRect); | |||
990 | else if (SupportsDoubleBuffering() && mbIsSubEdit) | |||
991 | { | |||
992 | // ImplPaintBorder() is a NOP, we have a native border, and this is a sub-edit of a control. | |||
993 | // That means we have to draw the parent native widget to paint the edit area to clear our background. | |||
994 | vcl::PaintBufferGuard g(ImplGetWindowImpl()->mpFrameData, GetParent()); | |||
995 | GetParent()->Paint(rRenderContext, rRectangle); | |||
996 | } | |||
997 | } | |||
998 | ||||
999 | void Edit::ImplPaintBorder(vcl::RenderContext const & rRenderContext) | |||
1000 | { | |||
1001 | // this is not needed when double-buffering | |||
1002 | if (SupportsDoubleBuffering()) | |||
1003 | return; | |||
1004 | ||||
1005 | if (!(ImplUseNativeBorder(rRenderContext, GetStyle()) || IsPaintTransparent())) | |||
1006 | return; | |||
1007 | ||||
1008 | // draw the inner part by painting the whole control using its border window | |||
1009 | vcl::Window* pBorder = GetWindow(GetWindowType::Border); | |||
1010 | if (pBorder == this) | |||
1011 | { | |||
1012 | // we have no border, use parent | |||
1013 | vcl::Window* pControl = mbIsSubEdit ? GetParent() : this; | |||
1014 | pBorder = pControl->GetWindow(GetWindowType::Border); | |||
1015 | if (pBorder == this) | |||
1016 | pBorder = GetParent(); | |||
1017 | } | |||
1018 | ||||
1019 | if (!pBorder) | |||
1020 | return; | |||
1021 | ||||
1022 | // set proper clipping region to not overdraw the whole control | |||
1023 | vcl::Region aClipRgn = GetPaintRegion(); | |||
1024 | if (!aClipRgn.IsNull()) | |||
1025 | { | |||
1026 | // transform clipping region to border window's coordinate system | |||
1027 | if (IsRTLEnabled() != pBorder->IsRTLEnabled() && AllSettings::GetLayoutRTL()) | |||
1028 | { | |||
1029 | // need to mirror in case border is not RTL but edit is (or vice versa) | |||
1030 | ||||
1031 | // mirror | |||
1032 | tools::Rectangle aBounds(aClipRgn.GetBoundRect()); | |||
1033 | int xNew = GetOutputSizePixel().Width() - aBounds.GetWidth() - aBounds.Left(); | |||
1034 | aClipRgn.Move(xNew - aBounds.Left(), 0); | |||
1035 | ||||
1036 | // move offset of border window | |||
1037 | Point aBorderOffs = pBorder->ScreenToOutputPixel(OutputToScreenPixel(Point())); | |||
1038 | aClipRgn.Move(aBorderOffs.X(), aBorderOffs.Y()); | |||
1039 | } | |||
1040 | else | |||
1041 | { | |||
1042 | // normal case | |||
1043 | Point aBorderOffs = pBorder->ScreenToOutputPixel(OutputToScreenPixel(Point())); | |||
1044 | aClipRgn.Move(aBorderOffs.X(), aBorderOffs.Y()); | |||
1045 | } | |||
1046 | ||||
1047 | vcl::Region oldRgn(pBorder->GetClipRegion()); | |||
1048 | pBorder->SetClipRegion(aClipRgn); | |||
1049 | ||||
1050 | pBorder->Paint(*pBorder, tools::Rectangle()); | |||
1051 | ||||
1052 | pBorder->SetClipRegion(oldRgn); | |||
1053 | } | |||
1054 | else | |||
1055 | { | |||
1056 | pBorder->Paint(*pBorder, tools::Rectangle()); | |||
1057 | } | |||
1058 | } | |||
1059 | ||||
1060 | void Edit::ImplShowCursor( bool bOnlyIfVisible ) | |||
1061 | { | |||
1062 | if ( !IsUpdateMode() || ( bOnlyIfVisible && !IsReallyVisible() ) ) | |||
1063 | return; | |||
1064 | ||||
1065 | vcl::Cursor* pCursor = GetCursor(); | |||
1066 | OUString aText = ImplGetText(); | |||
1067 | ||||
1068 | long nTextPos = 0; | |||
1069 | ||||
1070 | long nDXBuffer[256]; | |||
1071 | std::unique_ptr<long[]> pDXBuffer; | |||
1072 | long* pDX = nDXBuffer; | |||
1073 | ||||
1074 | if( !aText.isEmpty() ) | |||
1075 | { | |||
1076 | if( o3tl::make_unsigned(2*aText.getLength()) > SAL_N_ELEMENTS(nDXBuffer)(sizeof(sal_n_array_size(nDXBuffer))) ) | |||
1077 | { | |||
1078 | pDXBuffer.reset(new long[2*(aText.getLength()+1)]); | |||
1079 | pDX = pDXBuffer.get(); | |||
1080 | } | |||
1081 | ||||
1082 | GetCaretPositions( aText, pDX, 0, aText.getLength() ); | |||
1083 | ||||
1084 | if( maSelection.Max() < aText.getLength() ) | |||
1085 | nTextPos = pDX[ 2*maSelection.Max() ]; | |||
1086 | else | |||
1087 | nTextPos = pDX[ 2*aText.getLength()-1 ]; | |||
1088 | } | |||
1089 | ||||
1090 | long nCursorWidth = 0; | |||
1091 | if ( !mbInsertMode && !maSelection.Len() && (maSelection.Max() < aText.getLength()) ) | |||
1092 | nCursorWidth = GetTextWidth(aText, maSelection.Max(), 1); | |||
1093 | long nCursorPosX = nTextPos + mnXOffset + ImplGetExtraXOffset(); | |||
1094 | ||||
1095 | // cursor should land in visible area | |||
1096 | const Size aOutSize = GetOutputSizePixel(); | |||
1097 | if ( (nCursorPosX < 0) || (nCursorPosX >= aOutSize.Width()) ) | |||
1098 | { | |||
1099 | long nOldXOffset = mnXOffset; | |||
1100 | ||||
1101 | if ( nCursorPosX < 0 ) | |||
1102 | { | |||
1103 | mnXOffset = - nTextPos; | |||
1104 | long nMaxX = 0; | |||
1105 | mnXOffset += aOutSize.Width() / 5; | |||
1106 | if ( mnXOffset > nMaxX ) | |||
1107 | mnXOffset = nMaxX; | |||
1108 | } | |||
1109 | else | |||
1110 | { | |||
1111 | mnXOffset = (aOutSize.Width()-ImplGetExtraXOffset()) - nTextPos; | |||
1112 | // Something more? | |||
1113 | if ( (aOutSize.Width()-ImplGetExtraXOffset()) < nTextPos ) | |||
1114 | { | |||
1115 | long nMaxNegX = (aOutSize.Width()-ImplGetExtraXOffset()) - GetTextWidth( aText ); | |||
1116 | mnXOffset -= aOutSize.Width() / 5; | |||
1117 | if ( mnXOffset < nMaxNegX ) // both negative... | |||
1118 | mnXOffset = nMaxNegX; | |||
1119 | } | |||
1120 | } | |||
1121 | ||||
1122 | nCursorPosX = nTextPos + mnXOffset + ImplGetExtraXOffset(); | |||
1123 | if ( nCursorPosX == aOutSize.Width() ) // then invisible... | |||
1124 | nCursorPosX--; | |||
1125 | ||||
1126 | if ( mnXOffset != nOldXOffset ) | |||
1127 | ImplInvalidateOrRepaint(); | |||
1128 | } | |||
1129 | ||||
1130 | const long nTextHeight = GetTextHeight(); | |||
1131 | const long nCursorPosY = ImplGetTextYPosition(); | |||
1132 | if (pCursor) | |||
1133 | { | |||
1134 | pCursor->SetPos( Point( nCursorPosX, nCursorPosY ) ); | |||
1135 | pCursor->SetSize( Size( nCursorWidth, nTextHeight ) ); | |||
1136 | pCursor->Show(); | |||
1137 | } | |||
1138 | } | |||
1139 | ||||
1140 | void Edit::ImplAlign() | |||
1141 | { | |||
1142 | if (mnAlign == EDIT_ALIGN_LEFT1 && !mnXOffset) | |||
1143 | { | |||
1144 | // short circuit common case and avoid slow GetTextWidth() calc | |||
1145 | return; | |||
1146 | } | |||
1147 | ||||
1148 | long nTextWidth = GetTextWidth( ImplGetText() ); | |||
1149 | long nOutWidth = GetOutputSizePixel().Width(); | |||
1150 | ||||
1151 | if ( mnAlign == EDIT_ALIGN_LEFT1 ) | |||
1152 | { | |||
1153 | if (nTextWidth < nOutWidth) | |||
1154 | mnXOffset = 0; | |||
1155 | } | |||
1156 | else if ( mnAlign == EDIT_ALIGN_RIGHT3 ) | |||
1157 | { | |||
1158 | long nMinXOffset = nOutWidth - nTextWidth - 1 - ImplGetExtraXOffset(); | |||
1159 | bool bRTL = IsRTLEnabled(); | |||
1160 | if( mbIsSubEdit && GetParent() ) | |||
1161 | bRTL = GetParent()->IsRTLEnabled(); | |||
1162 | if( bRTL ) | |||
1163 | { | |||
1164 | if( nTextWidth < nOutWidth ) | |||
1165 | mnXOffset = nMinXOffset; | |||
1166 | } | |||
1167 | else | |||
1168 | { | |||
1169 | if( nTextWidth < nOutWidth ) | |||
1170 | mnXOffset = nMinXOffset; | |||
1171 | else if ( mnXOffset < nMinXOffset ) | |||
1172 | mnXOffset = nMinXOffset; | |||
1173 | } | |||
1174 | } | |||
1175 | else if( mnAlign == EDIT_ALIGN_CENTER2 ) | |||
1176 | { | |||
1177 | // would be nicer with check while scrolling but then it's not centred in scrolled state | |||
1178 | mnXOffset = (nOutWidth - nTextWidth) / 2; | |||
1179 | } | |||
1180 | } | |||
1181 | ||||
1182 | void Edit::ImplAlignAndPaint() | |||
1183 | { | |||
1184 | ImplAlign(); | |||
1185 | ImplInvalidateOrRepaint(); | |||
1186 | ImplShowCursor(); | |||
1187 | } | |||
1188 | ||||
1189 | sal_Int32 Edit::ImplGetCharPos( const Point& rWindowPos ) const | |||
1190 | { | |||
1191 | sal_Int32 nIndex = EDIT_NOLIMIT((sal_Int32) 0x7FFFFFFF); | |||
1192 | OUString aText = ImplGetText(); | |||
1193 | ||||
1194 | long nDXBuffer[256]; | |||
1195 | std::unique_ptr<long[]> pDXBuffer; | |||
1196 | long* pDX = nDXBuffer; | |||
1197 | if( o3tl::make_unsigned(2*aText.getLength()) > SAL_N_ELEMENTS(nDXBuffer)(sizeof(sal_n_array_size(nDXBuffer))) ) | |||
1198 | { | |||
1199 | pDXBuffer.reset(new long[2*(aText.getLength()+1)]); | |||
1200 | pDX = pDXBuffer.get(); | |||
1201 | } | |||
1202 | ||||
1203 | GetCaretPositions( aText, pDX, 0, aText.getLength() ); | |||
1204 | long nX = rWindowPos.X() - mnXOffset - ImplGetExtraXOffset(); | |||
1205 | for (sal_Int32 i = 0; i < aText.getLength(); aText.iterateCodePoints(&i)) | |||
1206 | { | |||
1207 | if( (pDX[2*i] >= nX && pDX[2*i+1] <= nX) || | |||
1208 | (pDX[2*i+1] >= nX && pDX[2*i] <= nX)) | |||
1209 | { | |||
1210 | nIndex = i; | |||
1211 | if( pDX[2*i] < pDX[2*i+1] ) | |||
1212 | { | |||
1213 | if( nX > (pDX[2*i]+pDX[2*i+1])/2 ) | |||
1214 | aText.iterateCodePoints(&nIndex); | |||
1215 | } | |||
1216 | else | |||
1217 | { | |||
1218 | if( nX < (pDX[2*i]+pDX[2*i+1])/2 ) | |||
1219 | aText.iterateCodePoints(&nIndex); | |||
1220 | } | |||
1221 | break; | |||
1222 | } | |||
1223 | } | |||
1224 | if( nIndex == EDIT_NOLIMIT((sal_Int32) 0x7FFFFFFF) ) | |||
1225 | { | |||
1226 | nIndex = 0; | |||
1227 | sal_Int32 nFinalIndex = 0; | |||
1228 | long nDiff = std::abs( pDX[0]-nX ); | |||
1229 | sal_Int32 i = 0; | |||
1230 | if (!aText.isEmpty()) | |||
1231 | { | |||
1232 | aText.iterateCodePoints(&i); //skip the first character | |||
1233 | } | |||
1234 | while (i < aText.getLength()) | |||
1235 | { | |||
1236 | long nNewDiff = std::abs( pDX[2*i]-nX ); | |||
1237 | ||||
1238 | if( nNewDiff < nDiff ) | |||
1239 | { | |||
1240 | nIndex = i; | |||
1241 | nDiff = nNewDiff; | |||
1242 | } | |||
1243 | ||||
1244 | nFinalIndex = i; | |||
1245 | ||||
1246 | aText.iterateCodePoints(&i); | |||
1247 | } | |||
1248 | if (nIndex == nFinalIndex && std::abs( pDX[2*nIndex+1] - nX ) < nDiff) | |||
1249 | nIndex = EDIT_NOLIMIT((sal_Int32) 0x7FFFFFFF); | |||
1250 | } | |||
1251 | ||||
1252 | return nIndex; | |||
1253 | } | |||
1254 | ||||
1255 | void Edit::ImplSetCursorPos( sal_Int32 nChar, bool bSelect ) | |||
1256 | { | |||
1257 | Selection aSelection( maSelection ); | |||
1258 | aSelection.Max() = nChar; | |||
1259 | if ( !bSelect ) | |||
1260 | aSelection.Min() = aSelection.Max(); | |||
1261 | ImplSetSelection( aSelection ); | |||
1262 | } | |||
1263 | ||||
1264 | void Edit::ImplCopyToSelectionClipboard() | |||
1265 | { | |||
1266 | if ( GetSelection().Len() ) | |||
1267 | { | |||
1268 | css::uno::Reference<css::datatransfer::clipboard::XClipboard> aSelection(GetPrimarySelection()); | |||
1269 | ImplCopy( aSelection ); | |||
1270 | } | |||
1271 | } | |||
1272 | ||||
1273 | void Edit::ImplCopy( uno::Reference< datatransfer::clipboard::XClipboard > const & rxClipboard ) | |||
1274 | { | |||
1275 | vcl::unohelper::TextDataObject::CopyStringTo( GetSelected(), rxClipboard ); | |||
1276 | } | |||
1277 | ||||
1278 | void Edit::ImplPaste( uno::Reference< datatransfer::clipboard::XClipboard > const & rxClipboard ) | |||
1279 | { | |||
1280 | if ( !rxClipboard.is() ) | |||
1281 | return; | |||
1282 | ||||
1283 | uno::Reference< datatransfer::XTransferable > xDataObj; | |||
1284 | ||||
1285 | try | |||
1286 | { | |||
1287 | SolarMutexReleaser aReleaser; | |||
1288 | xDataObj = rxClipboard->getContents(); | |||
1289 | } | |||
1290 | catch( const css::uno::Exception& ) | |||
1291 | { | |||
1292 | } | |||
1293 | ||||
1294 | if ( !xDataObj.is() ) | |||
1295 | return; | |||
1296 | ||||
1297 | datatransfer::DataFlavor aFlavor; | |||
1298 | SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor ); | |||
1299 | try | |||
1300 | { | |||
1301 | uno::Any aData = xDataObj->getTransferData( aFlavor ); | |||
1302 | OUString aText; | |||
1303 | aData >>= aText; | |||
1304 | ||||
1305 | Selection aSelection(maSelection); | |||
1306 | aSelection.Justify(); | |||
1307 | if (ImplTruncateToMaxLen(aText, aSelection.Len())) | |||
1308 | ShowTruncationWarning(GetFrameWeld()); | |||
1309 | ||||
1310 | ReplaceSelected( aText ); | |||
1311 | } | |||
1312 | catch( const css::uno::Exception& ) | |||
1313 | { | |||
1314 | } | |||
1315 | } | |||
1316 | ||||
1317 | void Edit::MouseButtonDown( const MouseEvent& rMEvt ) | |||
1318 | { | |||
1319 | if ( mpSubEdit ) | |||
1320 | { | |||
1321 | Control::MouseButtonDown( rMEvt ); | |||
1322 | return; | |||
1323 | } | |||
1324 | ||||
1325 | sal_Int32 nCharPos = ImplGetCharPos( rMEvt.GetPosPixel() ); | |||
1326 | Selection aSelection( maSelection ); | |||
1327 | aSelection.Justify(); | |||
1328 | ||||
1329 | if ( rMEvt.GetClicks() < 4 ) | |||
1330 | { | |||
1331 | mbClickedInSelection = false; | |||
1332 | if ( rMEvt.GetClicks() == 3 ) | |||
1333 | { | |||
1334 | ImplSetSelection( Selection( 0, EDIT_NOLIMIT((sal_Int32) 0x7FFFFFFF)) ); | |||
1335 | ImplCopyToSelectionClipboard(); | |||
1336 | ||||
1337 | } | |||
1338 | else if ( rMEvt.GetClicks() == 2 ) | |||
1339 | { | |||
1340 | uno::Reference < i18n::XBreakIterator > xBI = ImplGetBreakIterator(); | |||
1341 | i18n::Boundary aBoundary = xBI->getWordBoundary( maText.toString(), aSelection.Max(), | |||
1342 | GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, true ); | |||
1343 | ImplSetSelection( Selection( aBoundary.startPos, aBoundary.endPos ) ); | |||
1344 | ImplCopyToSelectionClipboard(); | |||
1345 | } | |||
1346 | else if ( !rMEvt.IsShift() && HasFocus() && aSelection.IsInside( nCharPos ) ) | |||
1347 | mbClickedInSelection = true; | |||
1348 | else if ( rMEvt.IsLeft() ) | |||
1349 | ImplSetCursorPos( nCharPos, rMEvt.IsShift() ); | |||
1350 | ||||
1351 | if ( !mbClickedInSelection && rMEvt.IsLeft() && ( rMEvt.GetClicks() == 1 ) ) | |||
1352 | StartTracking( StartTrackingFlags::ScrollRepeat ); | |||
1353 | } | |||
1354 | ||||
1355 | GrabFocus(); | |||
1356 | } | |||
1357 | ||||
1358 | void Edit::MouseButtonUp( const MouseEvent& rMEvt ) | |||
1359 | { | |||
1360 | if ( mbClickedInSelection && rMEvt.IsLeft() ) | |||
1361 | { | |||
1362 | sal_Int32 nCharPos = ImplGetCharPos( rMEvt.GetPosPixel() ); | |||
1363 | ImplSetCursorPos( nCharPos, false ); | |||
1364 | mbClickedInSelection = false; | |||
1365 | } | |||
1366 | else if ( rMEvt.IsMiddle() && !mbReadOnly && | |||
1367 | ( GetSettings().GetMouseSettings().GetMiddleButtonAction() == MouseMiddleButtonAction::PasteSelection ) ) | |||
1368 | { | |||
1369 | css::uno::Reference<css::datatransfer::clipboard::XClipboard> aSelection(Window::GetPrimarySelection()); | |||
1370 | ImplPaste( aSelection ); | |||
1371 | Modify(); | |||
1372 | } | |||
1373 | } | |||
1374 | ||||
1375 | void Edit::Tracking( const TrackingEvent& rTEvt ) | |||
1376 | { | |||
1377 | if ( rTEvt.IsTrackingEnded() ) | |||
1378 | { | |||
1379 | if ( mbClickedInSelection ) | |||
1380 | { | |||
1381 | sal_Int32 nCharPos = ImplGetCharPos( rTEvt.GetMouseEvent().GetPosPixel() ); | |||
1382 | ImplSetCursorPos( nCharPos, false ); | |||
1383 | mbClickedInSelection = false; | |||
1384 | } | |||
1385 | else if ( rTEvt.GetMouseEvent().IsLeft() ) | |||
1386 | { | |||
1387 | ImplCopyToSelectionClipboard(); | |||
1388 | } | |||
1389 | } | |||
1390 | else | |||
1391 | { | |||
1392 | if( !mbClickedInSelection ) | |||
1393 | { | |||
1394 | sal_Int32 nCharPos = ImplGetCharPos( rTEvt.GetMouseEvent().GetPosPixel() ); | |||
1395 | ImplSetCursorPos( nCharPos, true ); | |||
1396 | } | |||
1397 | } | |||
1398 | } | |||
1399 | ||||
1400 | bool Edit::ImplHandleKeyEvent( const KeyEvent& rKEvt ) | |||
1401 | { | |||
1402 | bool bDone = false; | |||
1403 | sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode(); | |||
1404 | KeyFuncType eFunc = rKEvt.GetKeyCode().GetFunction(); | |||
1405 | ||||
1406 | mbInternModified = false; | |||
1407 | ||||
1408 | if ( eFunc != KeyFuncType::DONTKNOW ) | |||
1409 | { | |||
1410 | switch ( eFunc ) | |||
1411 | { | |||
1412 | case KeyFuncType::CUT: | |||
1413 | { | |||
1414 | if ( !mbReadOnly && maSelection.Len() && !mbPassword ) | |||
1415 | { | |||
1416 | Cut(); | |||
1417 | Modify(); | |||
1418 | bDone = true; | |||
1419 | } | |||
1420 | } | |||
1421 | break; | |||
1422 | ||||
1423 | case KeyFuncType::COPY: | |||
1424 | { | |||
1425 | if ( !mbPassword ) | |||
1426 | { | |||
1427 | Copy(); | |||
1428 | bDone = true; | |||
1429 | } | |||
1430 | } | |||
1431 | break; | |||
1432 | ||||
1433 | case KeyFuncType::PASTE: | |||
1434 | { | |||
1435 | if ( !mbReadOnly ) | |||
1436 | { | |||
1437 | Paste(); | |||
1438 | bDone = true; | |||
1439 | } | |||
1440 | } | |||
1441 | break; | |||
1442 | ||||
1443 | case KeyFuncType::UNDO: | |||
1444 | { | |||
1445 | if ( !mbReadOnly ) | |||
1446 | { | |||
1447 | Undo(); | |||
1448 | bDone = true; | |||
1449 | } | |||
1450 | } | |||
1451 | break; | |||
1452 | ||||
1453 | default: | |||
1454 | eFunc = KeyFuncType::DONTKNOW; | |||
1455 | } | |||
1456 | } | |||
1457 | ||||
1458 | if ( !bDone && rKEvt.GetKeyCode().IsMod1() && !rKEvt.GetKeyCode().IsMod2() ) | |||
1459 | { | |||
1460 | if ( nCode == KEY_A ) | |||
1461 | { | |||
1462 | ImplSetSelection( Selection( 0, maText.getLength() ) ); | |||
1463 | bDone = true; | |||
1464 | } | |||
1465 | else if ( rKEvt.GetKeyCode().IsShift() && (nCode == KEY_S) ) | |||
1466 | { | |||
1467 | if ( pImplFncGetSpecialChars ) | |||
1468 | { | |||
1469 | Selection aSaveSel = GetSelection(); // if someone changes the selection in Get/LoseFocus, e.g. URL bar | |||
1470 | OUString aChars = pImplFncGetSpecialChars( GetFrameWeld(), GetFont() ); | |||
1471 | SetSelection( aSaveSel ); | |||
1472 | if ( !aChars.isEmpty() ) | |||
1473 | { | |||
1474 | ImplInsertText( aChars ); | |||
1475 | Modify(); | |||
1476 | } | |||
1477 | bDone = true; | |||
1478 | } | |||
1479 | } | |||
1480 | } | |||
1481 | ||||
1482 | if ( eFunc == KeyFuncType::DONTKNOW && ! bDone ) | |||
1483 | { | |||
1484 | switch ( nCode ) | |||
1485 | { | |||
1486 | case css::awt::Key::SELECT_ALL: | |||
1487 | { | |||
1488 | ImplSetSelection( Selection( 0, maText.getLength() ) ); | |||
1489 | bDone = true; | |||
1490 | } | |||
1491 | break; | |||
1492 | ||||
1493 | case KEY_LEFT: | |||
1494 | case KEY_RIGHT: | |||
1495 | case KEY_HOME: | |||
1496 | case KEY_END: | |||
1497 | case css::awt::Key::MOVE_WORD_FORWARD: | |||
1498 | case css::awt::Key::SELECT_WORD_FORWARD: | |||
1499 | case css::awt::Key::MOVE_WORD_BACKWARD: | |||
1500 | case css::awt::Key::SELECT_WORD_BACKWARD: | |||
1501 | case css::awt::Key::MOVE_TO_BEGIN_OF_LINE: | |||
1502 | case css::awt::Key::MOVE_TO_END_OF_LINE: | |||
1503 | case css::awt::Key::SELECT_TO_BEGIN_OF_LINE: | |||
1504 | case css::awt::Key::SELECT_TO_END_OF_LINE: | |||
1505 | case css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH: | |||
1506 | case css::awt::Key::MOVE_TO_END_OF_PARAGRAPH: | |||
1507 | case css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH: | |||
1508 | case css::awt::Key::SELECT_TO_END_OF_PARAGRAPH: | |||
1509 | case css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT: | |||
1510 | case css::awt::Key::MOVE_TO_END_OF_DOCUMENT: | |||
1511 | case css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT: | |||
1512 | case css::awt::Key::SELECT_TO_END_OF_DOCUMENT: | |||
1513 | { | |||
1514 | if ( !rKEvt.GetKeyCode().IsMod2() ) | |||
1515 | { | |||
1516 | ImplClearLayoutData(); | |||
1517 | uno::Reference < i18n::XBreakIterator > xBI = ImplGetBreakIterator(); | |||
1518 | ||||
1519 | Selection aSel( maSelection ); | |||
1520 | bool bWord = rKEvt.GetKeyCode().IsMod1(); | |||
1521 | bool bSelect = rKEvt.GetKeyCode().IsShift(); | |||
1522 | bool bGoLeft = (nCode == KEY_LEFT); | |||
1523 | bool bGoRight = (nCode == KEY_RIGHT); | |||
1524 | bool bGoHome = (nCode == KEY_HOME); | |||
1525 | bool bGoEnd = (nCode == KEY_END); | |||
1526 | ||||
1527 | switch( nCode ) | |||
1528 | { | |||
1529 | case css::awt::Key::MOVE_WORD_FORWARD: | |||
1530 | bGoRight = bWord = true;break; | |||
1531 | case css::awt::Key::SELECT_WORD_FORWARD: | |||
1532 | bGoRight = bSelect = bWord = true;break; | |||
1533 | case css::awt::Key::MOVE_WORD_BACKWARD: | |||
1534 | bGoLeft = bWord = true;break; | |||
1535 | case css::awt::Key::SELECT_WORD_BACKWARD: | |||
1536 | bGoLeft = bSelect = bWord = true;break; | |||
1537 | case css::awt::Key::SELECT_TO_BEGIN_OF_LINE: | |||
1538 | case css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH: | |||
1539 | case css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT: | |||
1540 | bSelect = true; | |||
1541 | [[fallthrough]]; | |||
1542 | case css::awt::Key::MOVE_TO_BEGIN_OF_LINE: | |||
1543 | case css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH: | |||
1544 | case css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT: | |||
1545 | bGoHome = true;break; | |||
1546 | case css::awt::Key::SELECT_TO_END_OF_LINE: | |||
1547 | case css::awt::Key::SELECT_TO_END_OF_PARAGRAPH: | |||
1548 | case css::awt::Key::SELECT_TO_END_OF_DOCUMENT: | |||
1549 | bSelect = true; | |||
1550 | [[fallthrough]]; | |||
1551 | case css::awt::Key::MOVE_TO_END_OF_LINE: | |||
1552 | case css::awt::Key::MOVE_TO_END_OF_PARAGRAPH: | |||
1553 | case css::awt::Key::MOVE_TO_END_OF_DOCUMENT: | |||
1554 | bGoEnd = true;break; | |||
1555 | default: | |||
1556 | break; | |||
1557 | } | |||
1558 | ||||
1559 | // range is checked in ImplSetSelection ... | |||
1560 | if ( bGoLeft && aSel.Max() ) | |||
1561 | { | |||
1562 | if ( bWord ) | |||
1563 | { | |||
1564 | i18n::Boundary aBoundary = xBI->getWordBoundary( maText.toString(), aSel.Max(), | |||
1565 | GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, true ); | |||
1566 | if ( aBoundary.startPos == aSel.Max() ) | |||
1567 | aBoundary = xBI->previousWord( maText.toString(), aSel.Max(), | |||
1568 | GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES ); | |||
1569 | aSel.Max() = aBoundary.startPos; | |||
1570 | } | |||
1571 | else | |||
1572 | { | |||
1573 | sal_Int32 nCount = 1; | |||
1574 | aSel.Max() = xBI->previousCharacters( maText.toString(), aSel.Max(), | |||
1575 | GetSettings().GetLanguageTag().getLocale(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount ); | |||
1576 | } | |||
1577 | } | |||
1578 | else if ( bGoRight && ( aSel.Max() < maText.getLength() ) ) | |||
1579 | { | |||
1580 | if ( bWord ) | |||
1581 | { | |||
1582 | i18n::Boundary aBoundary = xBI->nextWord( maText.toString(), aSel.Max(), | |||
1583 | GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES ); | |||
1584 | aSel.Max() = aBoundary.startPos; | |||
1585 | } | |||
1586 | else | |||
1587 | { | |||
1588 | sal_Int32 nCount = 1; | |||
1589 | aSel.Max() = xBI->nextCharacters( maText.toString(), aSel.Max(), | |||
1590 | GetSettings().GetLanguageTag().getLocale(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount ); | |||
1591 | } | |||
1592 | } | |||
1593 | else if ( bGoHome ) | |||
1594 | { | |||
1595 | aSel.Max() = 0; | |||
1596 | } | |||
1597 | else if ( bGoEnd ) | |||
1598 | { | |||
1599 | aSel.Max() = EDIT_NOLIMIT((sal_Int32) 0x7FFFFFFF); | |||
1600 | } | |||
1601 | ||||
1602 | if ( !bSelect ) | |||
1603 | aSel.Min() = aSel.Max(); | |||
1604 | ||||
1605 | if ( aSel != GetSelection() ) | |||
1606 | { | |||
1607 | ImplSetSelection( aSel ); | |||
1608 | ImplCopyToSelectionClipboard(); | |||
1609 | } | |||
1610 | ||||
1611 | if (bGoEnd && maAutocompleteHdl.IsSet() && !rKEvt.GetKeyCode().GetModifier()) | |||
1612 | { | |||
1613 | if ( (maSelection.Min() == maSelection.Max()) && (maSelection.Min() == maText.getLength()) ) | |||
1614 | { | |||
1615 | maAutocompleteHdl.Call(*this); | |||
1616 | } | |||
1617 | } | |||
1618 | ||||
1619 | bDone = true; | |||
1620 | } | |||
1621 | } | |||
1622 | break; | |||
1623 | ||||
1624 | case css::awt::Key::DELETE_WORD_BACKWARD: | |||
1625 | case css::awt::Key::DELETE_WORD_FORWARD: | |||
1626 | case css::awt::Key::DELETE_TO_BEGIN_OF_LINE: | |||
1627 | case css::awt::Key::DELETE_TO_END_OF_LINE: | |||
1628 | case KEY_BACKSPACE: | |||
1629 | case KEY_DELETE: | |||
1630 | { | |||
1631 | if ( !mbReadOnly && !rKEvt.GetKeyCode().IsMod2() ) | |||
1632 | { | |||
1633 | sal_uInt8 nDel = (nCode == KEY_DELETE) ? EDIT_DEL_RIGHT2 : EDIT_DEL_LEFT1; | |||
1634 | sal_uInt8 nMode = rKEvt.GetKeyCode().IsMod1() ? EDIT_DELMODE_RESTOFWORD12 : EDIT_DELMODE_SIMPLE11; | |||
1635 | if ( (nMode == EDIT_DELMODE_RESTOFWORD12) && rKEvt.GetKeyCode().IsShift() ) | |||
1636 | nMode = EDIT_DELMODE_RESTOFCONTENT13; | |||
1637 | switch( nCode ) | |||
1638 | { | |||
1639 | case css::awt::Key::DELETE_WORD_BACKWARD: | |||
1640 | nDel = EDIT_DEL_LEFT1; | |||
1641 | nMode = EDIT_DELMODE_RESTOFWORD12; | |||
1642 | break; | |||
1643 | case css::awt::Key::DELETE_WORD_FORWARD: | |||
1644 | nDel = EDIT_DEL_RIGHT2; | |||
1645 | nMode = EDIT_DELMODE_RESTOFWORD12; | |||
1646 | break; | |||
1647 | case css::awt::Key::DELETE_TO_BEGIN_OF_LINE: | |||
1648 | nDel = EDIT_DEL_LEFT1; | |||
1649 | nMode = EDIT_DELMODE_RESTOFCONTENT13; | |||
1650 | break; | |||
1651 | case css::awt::Key::DELETE_TO_END_OF_LINE: | |||
1652 | nDel = EDIT_DEL_RIGHT2; | |||
1653 | nMode = EDIT_DELMODE_RESTOFCONTENT13; | |||
1654 | break; | |||
1655 | default: break; | |||
1656 | } | |||
1657 | sal_Int32 nOldLen = maText.getLength(); | |||
1658 | ImplDelete( maSelection, nDel, nMode ); | |||
1659 | if ( maText.getLength() != nOldLen ) | |||
1660 | Modify(); | |||
1661 | bDone = true; | |||
1662 | } | |||
1663 | } | |||
1664 | break; | |||
1665 | ||||
1666 | case KEY_INSERT: | |||
1667 | { | |||
1668 | if ( !mpIMEInfos && !mbReadOnly && !rKEvt.GetKeyCode().IsMod2() ) | |||
1669 | { | |||
1670 | SetInsertMode( !mbInsertMode ); | |||
1671 | bDone = true; | |||
1672 | } | |||
1673 | } | |||
1674 | break; | |||
1675 | ||||
1676 | case KEY_RETURN: | |||
1677 | if (maActivateHdl.IsSet()) | |||
1678 | { | |||
1679 | bDone = maActivateHdl.Call(*this); | |||
1680 | } | |||
1681 | break; | |||
1682 | ||||
1683 | default: | |||
1684 | { | |||
1685 | if ( IsCharInput( rKEvt ) ) | |||
1686 | { | |||
1687 | bDone = true; // read characters also when in ReadOnly | |||
1688 | if ( !mbReadOnly ) | |||
1689 | { | |||
1690 | ImplInsertText(OUString(rKEvt.GetCharCode()), nullptr, true); | |||
1691 | if (maAutocompleteHdl.IsSet()) | |||
1692 | { | |||
1693 | if ( (maSelection.Min() == maSelection.Max()) && (maSelection.Min() == maText.getLength()) ) | |||
1694 | { | |||
1695 | maAutocompleteHdl.Call(*this); | |||
1696 | } | |||
1697 | } | |||
1698 | } | |||
1699 | } | |||
1700 | } | |||
1701 | } | |||
1702 | } | |||
1703 | ||||
1704 | if ( mbInternModified ) | |||
1705 | Modify(); | |||
1706 | ||||
1707 | return bDone; | |||
1708 | } | |||
1709 | ||||
1710 | void Edit::KeyInput( const KeyEvent& rKEvt ) | |||
1711 | { | |||
1712 | if ( mpSubEdit || !ImplHandleKeyEvent( rKEvt ) ) | |||
1713 | Control::KeyInput( rKEvt ); | |||
1714 | } | |||
1715 | ||||
1716 | void Edit::FillLayoutData() const | |||
1717 | { | |||
1718 | mpControlData->mpLayoutData.reset( new vcl::ControlLayoutData ); | |||
1719 | const_cast<Edit*>(this)->Invalidate(); | |||
1720 | } | |||
1721 | ||||
1722 | void Edit::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRectangle) | |||
1723 | { | |||
1724 | if (!mpSubEdit) | |||
1725 | ImplRepaint(rRenderContext, rRectangle); | |||
1726 | } | |||
1727 | ||||
1728 | void Edit::Resize() | |||
1729 | { | |||
1730 | if ( !mpSubEdit && IsReallyVisible() ) | |||
1731 | { | |||
1732 | Control::Resize(); | |||
1733 | // because of vertical centering... | |||
1734 | mnXOffset = 0; | |||
1735 | ImplAlign(); | |||
1736 | Invalidate(); | |||
1737 | ImplShowCursor(); | |||
1738 | } | |||
1739 | } | |||
1740 | ||||
1741 | void Edit::Draw( OutputDevice* pDev, const Point& rPos, DrawFlags nFlags ) | |||
1742 | { | |||
1743 | ApplySettings(*pDev); | |||
1744 | ||||
1745 | Point aPos = pDev->LogicToPixel( rPos ); | |||
1746 | Size aSize = GetSizePixel(); | |||
1747 | vcl::Font aFont = GetDrawPixelFont( pDev ); | |||
1748 | ||||
1749 | pDev->Push(); | |||
1750 | pDev->SetMapMode(); | |||
1751 | pDev->SetFont( aFont ); | |||
1752 | pDev->SetTextFillColor(); | |||
1753 | ||||
1754 | // Border/Background | |||
1755 | pDev->SetLineColor(); | |||
1756 | pDev->SetFillColor(); | |||
1757 | bool bBorder = (GetStyle() & WB_BORDER); | |||
1758 | bool bBackground = IsControlBackground(); | |||
1759 | if ( bBorder || bBackground ) | |||
1760 | { | |||
1761 | tools::Rectangle aRect( aPos, aSize ); | |||
1762 | if ( bBorder ) | |||
1763 | { | |||
1764 | ImplDrawFrame( pDev, aRect ); | |||
1765 | } | |||
1766 | if ( bBackground ) | |||
1767 | { | |||
1768 | pDev->SetFillColor( GetControlBackground() ); | |||
1769 | pDev->DrawRect( aRect ); | |||
1770 | } | |||
1771 | } | |||
1772 | ||||
1773 | // Content | |||
1774 | if ( nFlags & DrawFlags::Mono ) | |||
1775 | pDev->SetTextColor( COL_BLACK ); | |||
1776 | else | |||
1777 | { | |||
1778 | if ( !IsEnabled() ) | |||
1779 | { | |||
1780 | const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); | |||
1781 | pDev->SetTextColor( rStyleSettings.GetDisableColor() ); | |||
1782 | } | |||
1783 | else | |||
1784 | { | |||
1785 | pDev->SetTextColor( GetTextColor() ); | |||
1786 | } | |||
1787 | } | |||
1788 | ||||
1789 | const long nOnePixel = GetDrawPixel( pDev, 1 ); | |||
1790 | const long nOffX = 3*nOnePixel; | |||
1791 | DrawTextFlags nTextStyle = DrawTextFlags::VCenter; | |||
1792 | tools::Rectangle aTextRect( aPos, aSize ); | |||
1793 | ||||
1794 | if ( GetStyle() & WB_CENTER ) | |||
1795 | nTextStyle |= DrawTextFlags::Center; | |||
1796 | else if ( GetStyle() & WB_RIGHT ) | |||
1797 | nTextStyle |= DrawTextFlags::Right; | |||
1798 | else | |||
1799 | nTextStyle |= DrawTextFlags::Left; | |||
1800 | ||||
1801 | aTextRect.AdjustLeft(nOffX ); | |||
1802 | aTextRect.AdjustRight( -nOffX ); | |||
1803 | ||||
1804 | OUString aText = ImplGetText(); | |||
1805 | long nTextHeight = pDev->GetTextHeight(); | |||
1806 | long nTextWidth = pDev->GetTextWidth( aText ); | |||
1807 | long nOffY = (aSize.Height() - nTextHeight) / 2; | |||
1808 | ||||
1809 | // Clipping? | |||
1810 | if ( (nOffY < 0) || | |||
1811 | ((nOffY+nTextHeight) > aSize.Height()) || | |||
1812 | ((nOffX+nTextWidth) > aSize.Width()) ) | |||
1813 | { | |||
1814 | tools::Rectangle aClip( aPos, aSize ); | |||
1815 | if ( nTextHeight > aSize.Height() ) | |||
1816 | aClip.AdjustBottom(nTextHeight-aSize.Height()+1 ); // prevent HP printers from 'optimizing' | |||
1817 | pDev->IntersectClipRegion( aClip ); | |||
1818 | } | |||
1819 | ||||
1820 | pDev->DrawText( aTextRect, aText, nTextStyle ); | |||
1821 | pDev->Pop(); | |||
1822 | ||||
1823 | if ( GetSubEdit() ) | |||
1824 | { | |||
1825 | Size aOrigSize(GetSubEdit()->GetSizePixel()); | |||
1826 | GetSubEdit()->SetSizePixel(GetSizePixel()); | |||
1827 | GetSubEdit()->Draw(pDev, rPos, nFlags); | |||
1828 | GetSubEdit()->SetSizePixel(aOrigSize); | |||
1829 | } | |||
1830 | } | |||
1831 | ||||
1832 | void Edit::ImplInvalidateOutermostBorder( vcl::Window* pWin ) | |||
1833 | { | |||
1834 | // allow control to show focused state | |||
1835 | vcl::Window *pInvalWin = pWin; | |||
1836 | for (;;) | |||
1837 | { | |||
1838 | vcl::Window* pBorder = pInvalWin->GetWindow( GetWindowType::Border ); | |||
1839 | if (pBorder == pInvalWin || !pBorder || | |||
1840 | pInvalWin->ImplGetFrame() != pBorder->ImplGetFrame() ) | |||
1841 | break; | |||
1842 | pInvalWin = pBorder; | |||
1843 | } | |||
1844 | ||||
1845 | pInvalWin->Invalidate( InvalidateFlags::Children | InvalidateFlags::Update ); | |||
1846 | } | |||
1847 | ||||
1848 | void Edit::GetFocus() | |||
1849 | { | |||
1850 | if ( mpSubEdit ) | |||
1851 | mpSubEdit->ImplGrabFocus( GetGetFocusFlags() ); | |||
1852 | else if ( !mbActivePopup ) | |||
1853 | { | |||
1854 | maUndoText = maText.toString(); | |||
1855 | SelectionOptions nSelOptions = GetSettings().GetStyleSettings().GetSelectionOptions(); | |||
1856 | if ( !( GetStyle() & (WB_NOHIDESELECTION|WB_READONLY) ) | |||
1857 | && ( GetGetFocusFlags() & (GetFocusFlags::Init|GetFocusFlags::Tab|GetFocusFlags::CURSOR|GetFocusFlags::Mnemonic) ) ) | |||
1858 | { | |||
1859 | if ( nSelOptions & SelectionOptions::ShowFirst ) | |||
1860 | { | |||
1861 | maSelection.Min() = maText.getLength(); | |||
1862 | maSelection.Max() = 0; | |||
1863 | } | |||
1864 | else | |||
1865 | { | |||
1866 | maSelection.Min() = 0; | |||
1867 | maSelection.Max() = maText.getLength(); | |||
1868 | } | |||
1869 | if ( mbIsSubEdit ) | |||
1870 | static_cast<Edit*>(GetParent())->CallEventListeners( VclEventId::EditSelectionChanged ); | |||
1871 | else | |||
1872 | CallEventListeners( VclEventId::EditSelectionChanged ); | |||
1873 | } | |||
1874 | ||||
1875 | ImplShowCursor(); | |||
1876 | ||||
1877 | // FIXME: this is currently only on macOS | |||
1878 | // check for other platforms that need similar handling | |||
1879 | if( ImplGetSVData()->maNWFData.mbNoFocusRects && | |||
1880 | IsNativeWidgetEnabled() && | |||
1881 | IsNativeControlSupported( ControlType::Editbox, ControlPart::Entire ) ) | |||
1882 | { | |||
1883 | ImplInvalidateOutermostBorder( mbIsSubEdit ? GetParent() : this ); | |||
1884 | } | |||
1885 | else if ( maSelection.Len() ) | |||
1886 | { | |||
1887 | // paint the selection | |||
1888 | if ( !HasPaintEvent() ) | |||
1889 | ImplInvalidateOrRepaint(); | |||
1890 | else | |||
1891 | Invalidate(); | |||
1892 | } | |||
1893 | ||||
1894 | SetInputContext( InputContext( GetFont(), !IsReadOnly() ? InputContextFlags::Text|InputContextFlags::ExtText : InputContextFlags::NONE ) ); | |||
1895 | } | |||
1896 | ||||
1897 | Control::GetFocus(); | |||
1898 | } | |||
1899 | ||||
1900 | void Edit::LoseFocus() | |||
1901 | { | |||
1902 | if ( !mpSubEdit ) | |||
1903 | { | |||
1904 | // FIXME: this is currently only on macOS | |||
1905 | // check for other platforms that need similar handling | |||
1906 | if( ImplGetSVData()->maNWFData.mbNoFocusRects && | |||
1907 | IsNativeWidgetEnabled() && | |||
1908 | IsNativeControlSupported( ControlType::Editbox, ControlPart::Entire ) ) | |||
1909 | { | |||
1910 | ImplInvalidateOutermostBorder( mbIsSubEdit ? GetParent() : this ); | |||
1911 | } | |||
1912 | ||||
1913 | if ( !mbActivePopup && !( GetStyle() & WB_NOHIDESELECTION ) && maSelection.Len() ) | |||
1914 | ImplInvalidateOrRepaint(); // paint the selection | |||
1915 | } | |||
1916 | ||||
1917 | Control::LoseFocus(); | |||
1918 | } | |||
1919 | ||||
1920 | void Edit::Command( const CommandEvent& rCEvt ) | |||
1921 | { | |||
1922 | if ( rCEvt.GetCommand() == CommandEventId::ContextMenu ) | |||
1923 | { | |||
1924 | VclPtr<PopupMenu> pPopup = Edit::CreatePopupMenu(); | |||
1925 | ||||
1926 | bool bEnableCut = true; | |||
1927 | bool bEnableCopy = true; | |||
1928 | bool bEnableDelete = true; | |||
1929 | bool bEnablePaste = true; | |||
1930 | bool bEnableSpecialChar = true; | |||
1931 | ||||
1932 | if ( !maSelection.Len() ) | |||
1933 | { | |||
1934 | bEnableCut = false; | |||
1935 | bEnableCopy = false; | |||
1936 | bEnableDelete = false; | |||
1937 | } | |||
1938 | ||||
1939 | if ( IsReadOnly() ) | |||
1940 | { | |||
1941 | bEnableCut = false; | |||
1942 | bEnablePaste = false; | |||
1943 | bEnableDelete = false; | |||
1944 | bEnableSpecialChar = false; | |||
1945 | } | |||
1946 | else | |||
1947 | { | |||
1948 | // only paste if text available in clipboard | |||
1949 | bool bData = false; | |||
1950 | uno::Reference< datatransfer::clipboard::XClipboard > xClipboard = GetClipboard(); | |||
1951 | ||||
1952 | if ( xClipboard.is() ) | |||
1953 | { | |||
1954 | uno::Reference< datatransfer::XTransferable > xDataObj; | |||
1955 | { | |||
1956 | SolarMutexReleaser aReleaser; | |||
1957 | xDataObj = xClipboard->getContents(); | |||
1958 | } | |||
1959 | if ( xDataObj.is() ) | |||
1960 | { | |||
1961 | datatransfer::DataFlavor aFlavor; | |||
1962 | SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor ); | |||
1963 | bData = xDataObj->isDataFlavorSupported( aFlavor ); | |||
1964 | } | |||
1965 | } | |||
1966 | bEnablePaste = bData; | |||
1967 | } | |||
1968 | ||||
1969 | pPopup->EnableItem(pPopup->GetItemId("cut"), bEnableCut); | |||
1970 | pPopup->EnableItem(pPopup->GetItemId("copy"), bEnableCopy); | |||
1971 | pPopup->EnableItem(pPopup->GetItemId("delete"), bEnableDelete); | |||
1972 | pPopup->EnableItem(pPopup->GetItemId("paste"), bEnablePaste); | |||
1973 | pPopup->EnableItem(pPopup->GetItemId("specialchar"), bEnableSpecialChar); | |||
1974 | pPopup->EnableItem( | |||
1975 | pPopup->GetItemId("undo"), | |||
1976 | std::u16string_view(maUndoText) | |||
1977 | != std::u16string_view(maText.getStr(), maText.getLength())); | |||
1978 | bool bAllSelected = maSelection.Min() == 0 && maSelection.Max() == maText.getLength(); | |||
1979 | pPopup->EnableItem(pPopup->GetItemId("selectall"), !bAllSelected); | |||
1980 | pPopup->ShowItem(pPopup->GetItemId("specialchar"), pImplFncGetSpecialChars != nullptr); | |||
1981 | ||||
1982 | mbActivePopup = true; | |||
1983 | Selection aSaveSel = GetSelection(); // if someone changes selection in Get/LoseFocus, e.g. URL bar | |||
1984 | Point aPos = rCEvt.GetMousePosPixel(); | |||
1985 | if ( !rCEvt.IsMouseEvent() ) | |||
1986 | { | |||
1987 | // Show menu eventually centered in selection | |||
1988 | Size aSize = GetOutputSizePixel(); | |||
1989 | aPos = Point( aSize.Width()/2, aSize.Height()/2 ); | |||
1990 | } | |||
1991 | sal_uInt16 n = pPopup->Execute( this, aPos ); | |||
1992 | SetSelection( aSaveSel ); | |||
1993 | OString sCommand = pPopup->GetItemIdent(n); | |||
1994 | if (sCommand == "undo") | |||
1995 | { | |||
1996 | Undo(); | |||
1997 | Modify(); | |||
1998 | } | |||
1999 | else if (sCommand == "cut") | |||
2000 | { | |||
2001 | Cut(); | |||
2002 | Modify(); | |||
2003 | } | |||
2004 | else if (sCommand == "copy") | |||
2005 | { | |||
2006 | Copy(); | |||
2007 | } | |||
2008 | else if (sCommand == "paste") | |||
2009 | { | |||
2010 | Paste(); | |||
2011 | Modify(); | |||
2012 | } | |||
2013 | else if (sCommand == "delete") | |||
2014 | { | |||
2015 | DeleteSelected(); | |||
2016 | Modify(); | |||
2017 | } | |||
2018 | else if (sCommand == "selectall") | |||
2019 | { | |||
2020 | ImplSetSelection( Selection( 0, maText.getLength() ) ); | |||
2021 | } | |||
2022 | else if (sCommand == "specialchar" && pImplFncGetSpecialChars) | |||
2023 | { | |||
2024 | OUString aChars = pImplFncGetSpecialChars(GetFrameWeld(), GetFont()); | |||
2025 | if (!IsDisposed()) // destroyed while the insert special character dialog was still open | |||
2026 | { | |||
2027 | SetSelection( aSaveSel ); | |||
2028 | if (!aChars.isEmpty()) | |||
2029 | { | |||
2030 | ImplInsertText( aChars ); | |||
2031 | Modify(); | |||
2032 | } | |||
2033 | } | |||
2034 | } | |||
2035 | pPopup.clear(); | |||
2036 | mbActivePopup = false; | |||
2037 | } | |||
2038 | else if ( rCEvt.GetCommand() == CommandEventId::StartExtTextInput ) | |||
2039 | { | |||
2040 | DeleteSelected(); | |||
2041 | sal_Int32 nPos = maSelection.Max(); | |||
2042 | mpIMEInfos.reset(new Impl_IMEInfos( nPos, maText.copy(nPos).makeStringAndClear() )); | |||
2043 | mpIMEInfos->bWasCursorOverwrite = !IsInsertMode(); | |||
2044 | } | |||
2045 | else if ( rCEvt.GetCommand() == CommandEventId::EndExtTextInput ) | |||
2046 | { | |||
2047 | bool bInsertMode = !mpIMEInfos->bWasCursorOverwrite; | |||
2048 | mpIMEInfos.reset(); | |||
2049 | ||||
2050 | SetInsertMode(bInsertMode); | |||
2051 | Modify(); | |||
2052 | ||||
2053 | Invalidate(); | |||
2054 | ||||
2055 | // #i25161# call auto complete handler for ext text commit also | |||
2056 | if (maAutocompleteHdl.IsSet()) | |||
2057 | { | |||
2058 | if ( (maSelection.Min() == maSelection.Max()) && (maSelection.Min() == maText.getLength()) ) | |||
2059 | { | |||
2060 | maAutocompleteHdl.Call(*this); | |||
2061 | } | |||
2062 | } | |||
2063 | } | |||
2064 | else if ( rCEvt.GetCommand() == CommandEventId::ExtTextInput ) | |||
2065 | { | |||
2066 | const CommandExtTextInputData* pData = rCEvt.GetExtTextInputData(); | |||
2067 | ||||
2068 | maText.remove( mpIMEInfos->nPos, mpIMEInfos->nLen ); | |||
2069 | maText.insert( mpIMEInfos->nPos, pData->GetText() ); | |||
2070 | if ( mpIMEInfos->bWasCursorOverwrite ) | |||
2071 | { | |||
2072 | const sal_Int32 nOldIMETextLen = mpIMEInfos->nLen; | |||
2073 | const sal_Int32 nNewIMETextLen = pData->GetText().getLength(); | |||
2074 | if ( ( nOldIMETextLen > nNewIMETextLen ) && | |||
2075 | ( nNewIMETextLen < mpIMEInfos->aOldTextAfterStartPos.getLength() ) ) | |||
2076 | { | |||
2077 | // restore old characters | |||
2078 | const sal_Int32 nRestore = nOldIMETextLen - nNewIMETextLen; | |||
2079 | maText.insert( mpIMEInfos->nPos + nNewIMETextLen, mpIMEInfos->aOldTextAfterStartPos.copy( nNewIMETextLen, nRestore ) ); | |||
2080 | } | |||
2081 | else if ( ( nOldIMETextLen < nNewIMETextLen ) && | |||
2082 | ( nOldIMETextLen < mpIMEInfos->aOldTextAfterStartPos.getLength() ) ) | |||
2083 | { | |||
2084 | const sal_Int32 nOverwrite = ( nNewIMETextLen > mpIMEInfos->aOldTextAfterStartPos.getLength() | |||
2085 | ? mpIMEInfos->aOldTextAfterStartPos.getLength() : nNewIMETextLen ) - nOldIMETextLen; | |||
2086 | maText.remove( mpIMEInfos->nPos + nNewIMETextLen, nOverwrite ); | |||
2087 | } | |||
2088 | } | |||
2089 | ||||
2090 | if ( pData->GetTextAttr() ) | |||
2091 | { | |||
2092 | mpIMEInfos->CopyAttribs( pData->GetTextAttr(), pData->GetText().getLength() ); | |||
2093 | mpIMEInfos->bCursor = pData->IsCursorVisible(); | |||
2094 | } | |||
2095 | else | |||
2096 | { | |||
2097 | mpIMEInfos->DestroyAttribs(); | |||
2098 | } | |||
2099 | ||||
2100 | ImplAlignAndPaint(); | |||
2101 | sal_Int32 nCursorPos = mpIMEInfos->nPos + pData->GetCursorPos(); | |||
2102 | SetSelection( Selection( nCursorPos, nCursorPos ) ); | |||
2103 | SetInsertMode( !pData->IsCursorOverwrite() ); | |||
2104 | ||||
2105 | if ( pData->IsCursorVisible() ) | |||
2106 | GetCursor()->Show(); | |||
2107 | else | |||
2108 | GetCursor()->Hide(); | |||
2109 | } | |||
2110 | else if ( rCEvt.GetCommand() == CommandEventId::CursorPos ) | |||
2111 | { | |||
2112 | if ( mpIMEInfos ) | |||
2113 | { | |||
2114 | sal_Int32 nCursorPos = GetSelection().Max(); | |||
2115 | SetCursorRect( nullptr, GetTextWidth( maText.toString(), nCursorPos, mpIMEInfos->nPos+mpIMEInfos->nLen-nCursorPos ) ); | |||
2116 | } | |||
2117 | else | |||
2118 | { | |||
2119 | SetCursorRect(); | |||
2120 | } | |||
2121 | } | |||
2122 | else if ( rCEvt.GetCommand() == CommandEventId::SelectionChange ) | |||
2123 | { | |||
2124 | const CommandSelectionChangeData *pData = rCEvt.GetSelectionChangeData(); | |||
2125 | Selection aSelection( pData->GetStart(), pData->GetEnd() ); | |||
2126 | SetSelection(aSelection); | |||
2127 | } | |||
2128 | else if ( rCEvt.GetCommand() == CommandEventId::QueryCharPosition ) | |||
2129 | { | |||
2130 | if (mpIMEInfos && mpIMEInfos->nLen > 0) | |||
2131 | { | |||
2132 | OUString aText = ImplGetText(); | |||
2133 | long nDXBuffer[256]; | |||
2134 | std::unique_ptr<long[]> pDXBuffer; | |||
2135 | long* pDX = nDXBuffer; | |||
2136 | ||||
2137 | if( !aText.isEmpty() ) | |||
2138 | { | |||
2139 | if( o3tl::make_unsigned(2*aText.getLength()) > SAL_N_ELEMENTS(nDXBuffer)(sizeof(sal_n_array_size(nDXBuffer))) ) | |||
2140 | { | |||
2141 | pDXBuffer.reset(new long[2*(aText.getLength()+1)]); | |||
2142 | pDX = pDXBuffer.get(); | |||
2143 | } | |||
2144 | ||||
2145 | GetCaretPositions( aText, pDX, 0, aText.getLength() ); | |||
2146 | } | |||
2147 | long nTH = GetTextHeight(); | |||
2148 | Point aPos( mnXOffset, ImplGetTextYPosition() ); | |||
2149 | ||||
2150 | std::unique_ptr<tools::Rectangle[]> aRects(new tools::Rectangle[ mpIMEInfos->nLen ]); | |||
2151 | for ( int nIndex = 0; nIndex < mpIMEInfos->nLen; ++nIndex ) | |||
2152 | { | |||
2153 | tools::Rectangle aRect( aPos, Size( 10, nTH ) ); | |||
2154 | aRect.SetLeft( pDX[2*(nIndex+mpIMEInfos->nPos)] + mnXOffset + ImplGetExtraXOffset() ); | |||
2155 | aRects[ nIndex ] = aRect; | |||
2156 | } | |||
2157 | SetCompositionCharRect( aRects.get(), mpIMEInfos->nLen ); | |||
2158 | } | |||
2159 | } | |||
2160 | else | |||
2161 | Control::Command( rCEvt ); | |||
2162 | } | |||
2163 | ||||
2164 | void Edit::StateChanged( StateChangedType nType ) | |||
2165 | { | |||
2166 | if (nType == StateChangedType::InitShow) | |||
2167 | { | |||
2168 | if (!mpSubEdit) | |||
2169 | { | |||
2170 | mnXOffset = 0; // if GrabFocus before while size was still wrong | |||
2171 | ImplAlign(); | |||
2172 | if (!mpSubEdit) | |||
2173 | ImplShowCursor(false); | |||
2174 | Invalidate(); | |||
2175 | } | |||
2176 | } | |||
2177 | else if (nType == StateChangedType::Enable) | |||
2178 | { | |||
2179 | if (!mpSubEdit) | |||
2180 | { | |||
2181 | // change text color only | |||
2182 | ImplInvalidateOrRepaint(); | |||
2183 | } | |||
2184 | } | |||
2185 | else if (nType == StateChangedType::Style || nType == StateChangedType::Mirroring) | |||
2186 | { | |||
2187 | WinBits nStyle = GetStyle(); | |||
2188 | if (nType == StateChangedType::Style) | |||
2189 | { | |||
2190 | nStyle = ImplInitStyle(GetStyle()); | |||
2191 | SetStyle(nStyle); | |||
2192 | } | |||
2193 | ||||
2194 | sal_uInt16 nOldAlign = mnAlign; | |||
2195 | mnAlign = EDIT_ALIGN_LEFT1; | |||
2196 | ||||
2197 | // hack: right align until keyinput and cursor travelling works | |||
2198 | // edits are always RTL disabled | |||
2199 | // however the parent edits contain the correct setting | |||
2200 | if (mbIsSubEdit && GetParent()->IsRTLEnabled()) | |||
2201 | { | |||
2202 | if (GetParent()->GetStyle() & WB_LEFT) | |||
2203 | mnAlign = EDIT_ALIGN_RIGHT3; | |||
2204 | if (nType == StateChangedType::Mirroring) | |||
2205 | SetLayoutMode(ComplexTextLayoutFlags::BiDiRtl | ComplexTextLayoutFlags::TextOriginLeft); | |||
2206 | } | |||
2207 | else if (mbIsSubEdit && !GetParent()->IsRTLEnabled()) | |||
2208 | { | |||
2209 | if (nType == StateChangedType::Mirroring) | |||
2210 | SetLayoutMode(ComplexTextLayoutFlags::TextOriginLeft); | |||
2211 | } | |||
2212 | ||||
2213 | if (nStyle & WB_RIGHT) | |||
2214 | mnAlign = EDIT_ALIGN_RIGHT3; | |||
2215 | else if (nStyle & WB_CENTER) | |||
2216 | mnAlign = EDIT_ALIGN_CENTER2; | |||
2217 | if (!maText.isEmpty() && (mnAlign != nOldAlign)) | |||
2218 | { | |||
2219 | ImplAlign(); | |||
2220 | Invalidate(); | |||
2221 | } | |||
2222 | ||||
2223 | } | |||
2224 | else if ((nType == StateChangedType::Zoom) || (nType == StateChangedType::ControlFont)) | |||
2225 | { | |||
2226 | if (!mpSubEdit) | |||
2227 | { | |||
2228 | ApplySettings(*this); | |||
2229 | ImplShowCursor(); | |||
2230 | Invalidate(); | |||
2231 | } | |||
2232 | } | |||
2233 | else if ((nType == StateChangedType::ControlForeground) || (nType == StateChangedType::ControlBackground)) | |||
2234 | { | |||
2235 | if (!mpSubEdit) | |||
2236 | { | |||
2237 | ApplySettings(*this); | |||
2238 | Invalidate(); | |||
2239 | } | |||
2240 | } | |||
2241 | ||||
2242 | Control::StateChanged(nType); | |||
2243 | } | |||
2244 | ||||
2245 | void Edit::DataChanged( const DataChangedEvent& rDCEvt ) | |||
2246 | { | |||
2247 | if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) || | |||
2248 | (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) || | |||
2249 | ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) && | |||
2250 | (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) ) | |||
2251 | { | |||
2252 | if ( !mpSubEdit ) | |||
2253 | { | |||
2254 | ApplySettings(*this); | |||
2255 | ImplShowCursor(); | |||
2256 | Invalidate(); | |||
2257 | } | |||
2258 | } | |||
2259 | ||||
2260 | Control::DataChanged( rDCEvt ); | |||
2261 | } | |||
2262 | ||||
2263 | void Edit::ImplShowDDCursor() | |||
2264 | { | |||
2265 | if (!mpDDInfo->bVisCursor) | |||
2266 | { | |||
2267 | long nTextWidth = GetTextWidth( maText.toString(), 0, mpDDInfo->nDropPos ); | |||
2268 | long nTextHeight = GetTextHeight(); | |||
2269 | tools::Rectangle aCursorRect( Point( nTextWidth + mnXOffset, (GetOutputSize().Height()-nTextHeight)/2 ), Size( 2, nTextHeight ) ); | |||
2270 | mpDDInfo->aCursor.SetWindow( this ); | |||
2271 | mpDDInfo->aCursor.SetPos( aCursorRect.TopLeft() ); | |||
2272 | mpDDInfo->aCursor.SetSize( aCursorRect.GetSize() ); | |||
2273 | mpDDInfo->aCursor.Show(); | |||
2274 | mpDDInfo->bVisCursor = true; | |||
2275 | } | |||
2276 | } | |||
2277 | ||||
2278 | void Edit::ImplHideDDCursor() | |||
2279 | { | |||
2280 | if ( mpDDInfo && mpDDInfo->bVisCursor ) | |||
2281 | { | |||
2282 | mpDDInfo->aCursor.Hide(); | |||
2283 | mpDDInfo->bVisCursor = false; | |||
2284 | } | |||
2285 | } | |||
2286 | ||||
2287 | TextFilter::TextFilter(const OUString &rForbiddenChars) | |||
2288 | : sForbiddenChars(rForbiddenChars) | |||
2289 | { | |||
2290 | } | |||
2291 | ||||
2292 | TextFilter::~TextFilter() | |||
2293 | { | |||
2294 | } | |||
2295 | ||||
2296 | OUString TextFilter::filter(const OUString &rText) | |||
2297 | { | |||
2298 | OUString sTemp(rText); | |||
2299 | for (sal_Int32 i = 0; i < sForbiddenChars.getLength(); ++i) | |||
2300 | { | |||
2301 | sTemp = sTemp.replaceAll(OUStringChar(sForbiddenChars[i]), ""); | |||
2302 | } | |||
2303 | return sTemp; | |||
2304 | } | |||
2305 | ||||
2306 | void Edit::filterText() | |||
2307 | { | |||
2308 | Selection aSel = GetSelection(); | |||
2309 | const OUString sOrig = GetText(); | |||
2310 | const OUString sNew = mpFilterText->filter(GetText()); | |||
2311 | if (sOrig != sNew) | |||
2312 | { | |||
2313 | sal_Int32 nDiff = sOrig.getLength() - sNew.getLength(); | |||
2314 | if (nDiff) | |||
2315 | { | |||
2316 | aSel.setMin(aSel.getMin() - nDiff); | |||
2317 | aSel.setMax(aSel.getMin()); | |||
2318 | } | |||
2319 | SetText(sNew); | |||
2320 | SetSelection(aSel); | |||
2321 | } | |||
2322 | } | |||
2323 | ||||
2324 | void Edit::Modify() | |||
2325 | { | |||
2326 | if (mpFilterText) | |||
2327 | filterText(); | |||
2328 | ||||
2329 | if ( mbIsSubEdit ) | |||
2330 | { | |||
2331 | static_cast<Edit*>(GetParent())->Modify(); | |||
2332 | } | |||
2333 | else | |||
2334 | { | |||
2335 | if ( ImplCallEventListenersAndHandler( VclEventId::EditModify, [this] () { maModifyHdl.Call(*this); } ) ) | |||
2336 | // have been destroyed while calling into the handlers | |||
2337 | return; | |||
2338 | ||||
2339 | // #i13677# notify edit listeners about caret position change | |||
2340 | CallEventListeners( VclEventId::EditCaretChanged ); | |||
2341 | // FIXME: this is currently only on macOS | |||
2342 | // check for other platforms that need similar handling | |||
2343 | if( ImplGetSVData()->maNWFData.mbNoFocusRects && | |||
2344 | IsNativeWidgetEnabled() && | |||
2345 | IsNativeControlSupported( ControlType::Editbox, ControlPart::Entire ) ) | |||
2346 | { | |||
2347 | ImplInvalidateOutermostBorder( this ); | |||
2348 | } | |||
2349 | } | |||
2350 | } | |||
2351 | ||||
2352 | void Edit::SetEchoChar( sal_Unicode c ) | |||
2353 | { | |||
2354 | mcEchoChar = c; | |||
2355 | if ( mpSubEdit ) | |||
2356 | mpSubEdit->SetEchoChar( c ); | |||
2357 | } | |||
2358 | ||||
2359 | void Edit::SetReadOnly( bool bReadOnly ) | |||
2360 | { | |||
2361 | if ( mbReadOnly != bReadOnly ) | |||
2362 | { | |||
2363 | mbReadOnly = bReadOnly; | |||
2364 | if ( mpSubEdit ) | |||
2365 | mpSubEdit->SetReadOnly( bReadOnly ); | |||
2366 | ||||
2367 | CompatStateChanged( StateChangedType::ReadOnly ); | |||
2368 | } | |||
2369 | } | |||
2370 | ||||
2371 | void Edit::SetInsertMode( bool bInsert ) | |||
2372 | { | |||
2373 | if ( bInsert != mbInsertMode ) | |||
2374 | { | |||
2375 | mbInsertMode = bInsert; | |||
2376 | if ( mpSubEdit ) | |||
2377 | mpSubEdit->SetInsertMode( bInsert ); | |||
2378 | else | |||
2379 | ImplShowCursor(); | |||
2380 | } | |||
2381 | } | |||
2382 | ||||
2383 | bool Edit::IsInsertMode() const | |||
2384 | { | |||
2385 | if ( mpSubEdit ) | |||
2386 | return mpSubEdit->IsInsertMode(); | |||
2387 | else | |||
2388 | return mbInsertMode; | |||
2389 | } | |||
2390 | ||||
2391 | void Edit::SetMaxTextLen(sal_Int32 nMaxLen) | |||
2392 | { | |||
2393 | mnMaxTextLen = nMaxLen > 0 ? nMaxLen : EDIT_NOLIMIT((sal_Int32) 0x7FFFFFFF); | |||
2394 | ||||
2395 | if ( mpSubEdit ) | |||
2396 | mpSubEdit->SetMaxTextLen( mnMaxTextLen ); | |||
2397 | else | |||
2398 | { | |||
2399 | if ( maText.getLength() > mnMaxTextLen ) | |||
2400 | ImplDelete( Selection( mnMaxTextLen, maText.getLength() ), EDIT_DEL_RIGHT2, EDIT_DELMODE_SIMPLE11 ); | |||
2401 | } | |||
2402 | } | |||
2403 | ||||
2404 | void Edit::SetSelection( const Selection& rSelection ) | |||
2405 | { | |||
2406 | // If the selection was changed from outside, e.g. by MouseButtonDown, don't call Tracking() | |||
2407 | // directly afterwards which would change the selection again | |||
2408 | if ( IsTracking() ) | |||
2409 | EndTracking(); | |||
2410 | else if ( mpSubEdit && mpSubEdit->IsTracking() ) | |||
2411 | mpSubEdit->EndTracking(); | |||
2412 | ||||
2413 | ImplSetSelection( rSelection ); | |||
2414 | } | |||
2415 | ||||
2416 | void Edit::ImplSetSelection( const Selection& rSelection, bool bPaint ) | |||
2417 | { | |||
2418 | if ( mpSubEdit ) | |||
2419 | mpSubEdit->ImplSetSelection( rSelection ); | |||
2420 | else | |||
2421 | { | |||
2422 | if ( rSelection != maSelection ) | |||
2423 | { | |||
2424 | Selection aOld( maSelection ); | |||
2425 | Selection aNew( rSelection ); | |||
2426 | ||||
2427 | if ( aNew.Min() > maText.getLength() ) | |||
2428 | aNew.Min() = maText.getLength(); | |||
2429 | if ( aNew.Max() > maText.getLength() ) | |||
2430 | aNew.Max() = maText.getLength(); | |||
2431 | if ( aNew.Min() < 0 ) | |||
2432 | aNew.Min() = 0; | |||
2433 | if ( aNew.Max() < 0 ) | |||
2434 | aNew.Max() = 0; | |||
2435 | ||||
2436 | if ( aNew != maSelection ) | |||
2437 | { | |||
2438 | ImplClearLayoutData(); | |||
2439 | Selection aTemp = maSelection; | |||
2440 | maSelection = aNew; | |||
2441 | ||||
2442 | if ( bPaint && ( aOld.Len() || aNew.Len() || IsPaintTransparent() ) ) | |||
2443 | ImplInvalidateOrRepaint(); | |||
2444 | ImplShowCursor(); | |||
2445 | ||||
2446 | bool bCaret = false, bSelection = false; | |||
2447 | long nB=aNew.Max(), nA=aNew.Min(),oB=aTemp.Max(), oA=aTemp.Min(); | |||
2448 | long nGap = nB-nA, oGap = oB-oA; | |||
2449 | if (nB != oB) | |||
2450 | bCaret = true; | |||
2451 | if (nGap != 0 || oGap != 0) | |||
2452 | bSelection = true; | |||
2453 | ||||
2454 | if (bSelection) | |||
2455 | { | |||
2456 | if ( mbIsSubEdit ) | |||
2457 | static_cast<Edit*>(GetParent())->CallEventListeners( VclEventId::EditSelectionChanged ); | |||
2458 | else | |||
2459 | CallEventListeners( VclEventId::EditSelectionChanged ); | |||
2460 | } | |||
2461 | ||||
2462 | if (bCaret) | |||
2463 | { | |||
2464 | if ( mbIsSubEdit ) | |||
2465 | static_cast<Edit*>(GetParent())->CallEventListeners( VclEventId::EditCaretChanged ); | |||
2466 | else | |||
2467 | CallEventListeners( VclEventId::EditCaretChanged ); | |||
2468 | } | |||
2469 | ||||
2470 | // #103511# notify combobox listeners of deselection | |||
2471 | if( !maSelection && GetParent() && GetParent()->GetType() == WindowType::COMBOBOX ) | |||
2472 | static_cast<Edit*>(GetParent())->CallEventListeners( VclEventId::ComboboxDeselect ); | |||
2473 | } | |||
2474 | } | |||
2475 | } | |||
2476 | } | |||
2477 | ||||
2478 | const Selection& Edit::GetSelection() const | |||
2479 | { | |||
2480 | if ( mpSubEdit ) | |||
2481 | return mpSubEdit->GetSelection(); | |||
2482 | else | |||
2483 | return maSelection; | |||
2484 | } | |||
2485 | ||||
2486 | void Edit::ReplaceSelected( const OUString& rStr ) | |||
2487 | { | |||
2488 | if ( mpSubEdit ) | |||
2489 | mpSubEdit->ReplaceSelected( rStr ); | |||
2490 | else | |||
2491 | ImplInsertText( rStr ); | |||
2492 | } | |||
2493 | ||||
2494 | void Edit::DeleteSelected() | |||
2495 | { | |||
2496 | if ( mpSubEdit ) | |||
2497 | mpSubEdit->DeleteSelected(); | |||
2498 | else | |||
2499 | { | |||
2500 | if ( maSelection.Len() ) | |||
2501 | ImplDelete( maSelection, EDIT_DEL_RIGHT2, EDIT_DELMODE_SIMPLE11 ); | |||
2502 | } | |||
2503 | } | |||
2504 | ||||
2505 | OUString Edit::GetSelected() const | |||
2506 | { | |||
2507 | if ( mpSubEdit ) | |||
2508 | return mpSubEdit->GetSelected(); | |||
2509 | else | |||
2510 | { | |||
2511 | Selection aSelection( maSelection ); | |||
2512 | aSelection.Justify(); | |||
2513 | return OUString( maText.getStr() + aSelection.Min(), aSelection.Len() ); | |||
2514 | } | |||
2515 | } | |||
2516 | ||||
2517 | void Edit::Cut() | |||
2518 | { | |||
2519 | if ( !mbPassword ) | |||
2520 | { | |||
2521 | Copy(); | |||
2522 | ReplaceSelected( OUString() ); | |||
2523 | } | |||
2524 | } | |||
2525 | ||||
2526 | void Edit::Copy() | |||
2527 | { | |||
2528 | if ( !mbPassword ) | |||
2529 | { | |||
2530 | css::uno::Reference<css::datatransfer::clipboard::XClipboard> aClipboard(GetClipboard()); | |||
2531 | ImplCopy( aClipboard ); | |||
2532 | } | |||
2533 | } | |||
2534 | ||||
2535 | void Edit::Paste() | |||
2536 | { | |||
2537 | css::uno::Reference<css::datatransfer::clipboard::XClipboard> aClipboard(GetClipboard()); | |||
2538 | ImplPaste( aClipboard ); | |||
2539 | } | |||
2540 | ||||
2541 | void Edit::Undo() | |||
2542 | { | |||
2543 | if ( mpSubEdit ) | |||
2544 | mpSubEdit->Undo(); | |||
2545 | else | |||
2546 | { | |||
2547 | const OUString aText( maText.toString() ); | |||
2548 | ImplDelete( Selection( 0, aText.getLength() ), EDIT_DEL_RIGHT2, EDIT_DELMODE_SIMPLE11 ); | |||
2549 | ImplInsertText( maUndoText ); | |||
2550 | ImplSetSelection( Selection( 0, maUndoText.getLength() ) ); | |||
2551 | maUndoText = aText; | |||
2552 | } | |||
2553 | } | |||
2554 | ||||
2555 | void Edit::SetText( const OUString& rStr ) | |||
2556 | { | |||
2557 | if ( mpSubEdit ) | |||
2558 | mpSubEdit->SetText( rStr ); // not directly ImplSetText if SetText overridden | |||
2559 | else | |||
2560 | { | |||
2561 | Selection aNewSel( 0, 0 ); // prevent scrolling | |||
2562 | ImplSetText( rStr, &aNewSel ); | |||
2563 | } | |||
2564 | } | |||
2565 | ||||
2566 | void Edit::SetText( const OUString& rStr, const Selection& rSelection ) | |||
2567 | { | |||
2568 | if ( mpSubEdit ) | |||
2569 | mpSubEdit->SetText( rStr, rSelection ); | |||
2570 | else | |||
2571 | ImplSetText( rStr, &rSelection ); | |||
2572 | } | |||
2573 | ||||
2574 | OUString Edit::GetText() const | |||
2575 | { | |||
2576 | if ( mpSubEdit ) | |||
2577 | return mpSubEdit->GetText(); | |||
2578 | else | |||
2579 | return maText.toString(); | |||
2580 | } | |||
2581 | ||||
2582 | void Edit::SetCursorAtLast(){ | |||
2583 | ImplSetCursorPos( GetText().getLength(), false ); | |||
2584 | } | |||
2585 | ||||
2586 | void Edit::SetPlaceholderText( const OUString& rStr ) | |||
2587 | { | |||
2588 | if ( mpSubEdit ) | |||
2589 | mpSubEdit->SetPlaceholderText( rStr ); | |||
2590 | else if ( maPlaceholderText != rStr ) | |||
2591 | { | |||
2592 | maPlaceholderText = rStr; | |||
2593 | if ( GetText().isEmpty() ) | |||
2594 | Invalidate(); | |||
2595 | } | |||
2596 | } | |||
2597 | ||||
2598 | void Edit::SetModifyFlag() | |||
2599 | { | |||
2600 | } | |||
2601 | ||||
2602 | void Edit::SetSubEdit(Edit* pEdit) | |||
2603 | { | |||
2604 | mpSubEdit.disposeAndClear(); | |||
| ||||
2605 | mpSubEdit.set(pEdit); | |||
2606 | ||||
2607 | if (mpSubEdit) | |||
2608 | { | |||
2609 | SetPointer(PointerStyle::Arrow); // Only SubEdit has the BEAM... | |||
2610 | mpSubEdit->mbIsSubEdit = true; | |||
2611 | ||||
2612 | mpSubEdit->SetReadOnly(mbReadOnly); | |||
2613 | mpSubEdit->maAutocompleteHdl = maAutocompleteHdl; | |||
2614 | } | |||
2615 | } | |||
2616 | ||||
2617 | Size Edit::CalcMinimumSizeForText(const OUString &rString) const | |||
2618 | { | |||
2619 | ControlType eCtrlType = ImplGetNativeControlType(); | |||
2620 | ||||
2621 | Size aSize; | |||
2622 | if (mnWidthInChars != -1) | |||
2623 | { | |||
2624 | //CalcSize calls CalcWindowSize, but we will call that also in this | |||
2625 | //function, so undo the first one with CalcOutputSize | |||
2626 | aSize = CalcOutputSize(CalcSize(mnWidthInChars)); | |||
2627 | } | |||
2628 | else | |||
2629 | { | |||
2630 | OUString aString; | |||
2631 | if (mnMaxWidthChars != -1 && mnMaxWidthChars < rString.getLength()) | |||
2632 | aString = rString.copy(0, mnMaxWidthChars); | |||
2633 | else | |||
2634 | aString = rString; | |||
2635 | ||||
2636 | aSize.setHeight( GetTextHeight() ); | |||
2637 | aSize.setWidth( GetTextWidth(aString) ); | |||
2638 | aSize.AdjustWidth(ImplGetExtraXOffset() * 2 ); | |||
2639 | ||||
2640 | // do not create edit fields in which one cannot enter anything | |||
2641 | // a default minimum width should exist for at least 3 characters | |||
2642 | ||||
2643 | //CalcSize calls CalcWindowSize, but we will call that also in this | |||
2644 | //function, so undo the first one with CalcOutputSize | |||
2645 | Size aMinSize(CalcOutputSize(CalcSize(3))); | |||
2646 | if (aSize.Width() < aMinSize.Width()) | |||
2647 | aSize.setWidth( aMinSize.Width() ); | |||
2648 | } | |||
2649 | ||||
2650 | aSize.AdjustHeight(ImplGetExtraYOffset() * 2 ); | |||
2651 | ||||
2652 | aSize = CalcWindowSize( aSize ); | |||
2653 | ||||
2654 | // ask NWF what if it has an opinion, too | |||
2655 | ImplControlValue aControlValue; | |||
2656 | tools::Rectangle aRect( Point( 0, 0 ), aSize ); | |||
2657 | tools::Rectangle aContent, aBound; | |||
2658 | if (GetNativeControlRegion(eCtrlType, ControlPart::Entire, aRect, ControlState::NONE, | |||
2659 | aControlValue, aBound, aContent)) | |||
2660 | { | |||
2661 | if (aBound.GetHeight() > aSize.Height()) | |||
2662 | aSize.setHeight( aBound.GetHeight() ); | |||
2663 | } | |||
2664 | return aSize; | |||
2665 | } | |||
2666 | ||||
2667 | Size Edit::CalcMinimumSize() const | |||
2668 | { | |||
2669 | return CalcMinimumSizeForText(GetText()); | |||
2670 | } | |||
2671 | ||||
2672 | Size Edit::GetOptimalSize() const | |||
2673 | { | |||
2674 | return CalcMinimumSize(); | |||
2675 | } | |||
2676 | ||||
2677 | Size Edit::CalcSize(sal_Int32 nChars) const | |||
2678 | { | |||
2679 | // width for N characters, independent from content. | |||
2680 | // works only correct for fixed fonts, average otherwise | |||
2681 | Size aSz( GetTextWidth( "x" ), GetTextHeight() ); | |||
2682 | aSz.setWidth( aSz.Width() * nChars ); | |||
2683 | aSz.AdjustWidth(ImplGetExtraXOffset() * 2 ); | |||
2684 | aSz = CalcWindowSize( aSz ); | |||
2685 | return aSz; | |||
2686 | } | |||
2687 | ||||
2688 | sal_Int32 Edit::GetMaxVisChars() const | |||
2689 | { | |||
2690 | const vcl::Window* pW = mpSubEdit ? mpSubEdit : this; | |||
2691 | sal_Int32 nOutWidth = pW->GetOutputSizePixel().Width(); | |||
2692 | sal_Int32 nCharWidth = GetTextWidth( "x" ); | |||
2693 | return nCharWidth ? nOutWidth/nCharWidth : 0; | |||
2694 | } | |||
2695 | ||||
2696 | namespace vcl | |||
2697 | { | |||
2698 | void SetGetSpecialCharsFunction( FncGetSpecialChars fn ) | |||
2699 | { | |||
2700 | pImplFncGetSpecialChars = fn; | |||
2701 | } | |||
2702 | ||||
2703 | FncGetSpecialChars GetGetSpecialCharsFunction() | |||
2704 | { | |||
2705 | return pImplFncGetSpecialChars; | |||
2706 | } | |||
2707 | } | |||
2708 | ||||
2709 | VclPtr<PopupMenu> Edit::CreatePopupMenu() | |||
2710 | { | |||
2711 | if (!mpUIBuilder) | |||
2712 | mpUIBuilder.reset(new VclBuilder(nullptr, AllSettings::GetUIRootDir(), "vcl/ui/editmenu.ui", "")); | |||
2713 | VclPtr<PopupMenu> pPopup = mpUIBuilder->get_menu("menu"); | |||
2714 | const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); | |||
2715 | if (rStyleSettings.GetHideDisabledMenuItems()) | |||
2716 | pPopup->SetMenuFlags( MenuFlags::HideDisabledEntries ); | |||
2717 | else | |||
2718 | pPopup->SetMenuFlags ( MenuFlags::AlwaysShowDisabledEntries ); | |||
2719 | if (rStyleSettings.GetContextMenuShortcuts()) | |||
2720 | { | |||
2721 | pPopup->SetAccelKey(pPopup->GetItemId("undo"), vcl::KeyCode( KeyFuncType::UNDO)); | |||
2722 | pPopup->SetAccelKey(pPopup->GetItemId("cut"), vcl::KeyCode( KeyFuncType::CUT)); | |||
2723 | pPopup->SetAccelKey(pPopup->GetItemId("copy"), vcl::KeyCode( KeyFuncType::COPY)); | |||
2724 | pPopup->SetAccelKey(pPopup->GetItemId("paste"), vcl::KeyCode( KeyFuncType::PASTE)); | |||
2725 | pPopup->SetAccelKey(pPopup->GetItemId("delete"), vcl::KeyCode( KeyFuncType::DELETE)); | |||
2726 | pPopup->SetAccelKey(pPopup->GetItemId("selectall"), vcl::KeyCode( KEY_A, false, true, false, false)); | |||
2727 | pPopup->SetAccelKey(pPopup->GetItemId("specialchar"), vcl::KeyCode( KEY_S, true, true, false, false)); | |||
2728 | } | |||
2729 | return pPopup; | |||
2730 | } | |||
2731 | ||||
2732 | // css::datatransfer::dnd::XDragGestureListener | |||
2733 | void Edit::dragGestureRecognized( const css::datatransfer::dnd::DragGestureEvent& rDGE ) | |||
2734 | { | |||
2735 | SolarMutexGuard aVclGuard; | |||
2736 | ||||
2737 | if ( !(!IsTracking() && maSelection.Len() && | |||
2738 | !mbPassword && (!mpDDInfo || !mpDDInfo->bStarterOfDD)) ) // no repeated D&D | |||
2739 | return; | |||
2740 | ||||
2741 | Selection aSel( maSelection ); | |||
2742 | aSel.Justify(); | |||
2743 | ||||
2744 | // only if mouse in the selection... | |||
2745 | Point aMousePos( rDGE.DragOriginX, rDGE.DragOriginY ); | |||
2746 | sal_Int32 nCharPos = ImplGetCharPos( aMousePos ); | |||
2747 | if ( (nCharPos < aSel.Min()) || (nCharPos >= aSel.Max()) ) | |||
2748 | return; | |||
2749 | ||||
2750 | if ( !mpDDInfo ) | |||
2751 | mpDDInfo.reset(new DDInfo); | |||
2752 | ||||
2753 | mpDDInfo->bStarterOfDD = true; | |||
2754 | mpDDInfo->aDndStartSel = aSel; | |||
2755 | ||||
2756 | if ( IsTracking() ) | |||
2757 | EndTracking(); // before D&D disable tracking | |||
2758 | ||||
2759 | vcl::unohelper::TextDataObject* pDataObj = new vcl::unohelper::TextDataObject( GetSelected() ); | |||
2760 | sal_Int8 nActions = datatransfer::dnd::DNDConstants::ACTION_COPY; | |||
2761 | if ( !IsReadOnly() ) | |||
2762 | nActions |= datatransfer::dnd::DNDConstants::ACTION_MOVE; | |||
2763 | rDGE.DragSource->startDrag( rDGE, nActions, 0 /*cursor*/, 0 /*image*/, pDataObj, mxDnDListener ); | |||
2764 | if ( GetCursor() ) | |||
2765 | GetCursor()->Hide(); | |||
2766 | } | |||
2767 | ||||
2768 | // css::datatransfer::dnd::XDragSourceListener | |||
2769 | void Edit::dragDropEnd( const css::datatransfer::dnd::DragSourceDropEvent& rDSDE ) | |||
2770 | { | |||
2771 | SolarMutexGuard aVclGuard; | |||
2772 | ||||
2773 | if (rDSDE.DropSuccess && (rDSDE.DropAction & datatransfer::dnd::DNDConstants::ACTION_MOVE) && mpDDInfo) | |||
2774 | { | |||
2775 | Selection aSel( mpDDInfo->aDndStartSel ); | |||
2776 | if ( mpDDInfo->bDroppedInMe ) | |||
2777 | { | |||
2778 | if ( aSel.Max() > mpDDInfo->nDropPos ) | |||
2779 | { | |||
2780 | long nLen = aSel.Len(); | |||
2781 | aSel.Min() += nLen; | |||
2782 | aSel.Max() += nLen; | |||
2783 | } | |||
2784 | } | |||
2785 | ImplDelete( aSel, EDIT_DEL_RIGHT2, EDIT_DELMODE_SIMPLE11 ); | |||
2786 | Modify(); | |||
2787 | } | |||
2788 | ||||
2789 | ImplHideDDCursor(); | |||
2790 | mpDDInfo.reset(); | |||
2791 | } | |||
2792 | ||||
2793 | // css::datatransfer::dnd::XDropTargetListener | |||
2794 | void Edit::drop( const css::datatransfer::dnd::DropTargetDropEvent& rDTDE ) | |||
2795 | { | |||
2796 | SolarMutexGuard aVclGuard; | |||
2797 | ||||
2798 | bool bChanges = false; | |||
2799 | if ( !mbReadOnly && mpDDInfo ) | |||
2800 | { | |||
2801 | ImplHideDDCursor(); | |||
2802 | ||||
2803 | Selection aSel( maSelection ); | |||
2804 | aSel.Justify(); | |||
2805 | ||||
2806 | if ( aSel.Len() && !mpDDInfo->bStarterOfDD ) | |||
2807 | ImplDelete( aSel, EDIT_DEL_RIGHT2, EDIT_DELMODE_SIMPLE11 ); | |||
2808 | ||||
2809 | mpDDInfo->bDroppedInMe = true; | |||
2810 | ||||
2811 | aSel.Min() = mpDDInfo->nDropPos; | |||
2812 | aSel.Max() = mpDDInfo->nDropPos; | |||
2813 | ImplSetSelection( aSel ); | |||
2814 | ||||
2815 | uno::Reference< datatransfer::XTransferable > xDataObj = rDTDE.Transferable; | |||
2816 | if ( xDataObj.is() ) | |||
2817 | { | |||
2818 | datatransfer::DataFlavor aFlavor; | |||
2819 | SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor ); | |||
2820 | if ( xDataObj->isDataFlavorSupported( aFlavor ) ) | |||
2821 | { | |||
2822 | uno::Any aData = xDataObj->getTransferData( aFlavor ); | |||
2823 | OUString aText; | |||
2824 | aData >>= aText; | |||
2825 | ImplInsertText( aText ); | |||
2826 | bChanges = true; | |||
2827 | Modify(); | |||
2828 | } | |||
2829 | } | |||
2830 | ||||
2831 | if ( !mpDDInfo->bStarterOfDD ) | |||
2832 | { | |||
2833 | mpDDInfo.reset(); | |||
2834 | } | |||
2835 | } | |||
2836 | ||||
2837 | rDTDE.Context->dropComplete( bChanges ); | |||
2838 | } | |||
2839 | ||||
2840 | void Edit::dragEnter( const css::datatransfer::dnd::DropTargetDragEnterEvent& rDTDE ) | |||
2841 | { | |||
2842 | if ( !mpDDInfo ) | |||
2843 | { | |||
2844 | mpDDInfo.reset(new DDInfo); | |||
2845 | } | |||
2846 | // search for string data type | |||
2847 | const Sequence< css::datatransfer::DataFlavor >& rFlavors( rDTDE.SupportedDataFlavors ); | |||
2848 | mpDDInfo->bIsStringSupported = std::any_of(rFlavors.begin(), rFlavors.end(), | |||
2849 | [](const css::datatransfer::DataFlavor& rFlavor) { | |||
2850 | sal_Int32 nIndex = 0; | |||
2851 | const OUString aMimetype = rFlavor.MimeType.getToken( 0, ';', nIndex ); | |||
2852 | return aMimetype == "text/plain"; | |||
2853 | }); | |||
2854 | } | |||
2855 | ||||
2856 | void Edit::dragExit( const css::datatransfer::dnd::DropTargetEvent& ) | |||
2857 | { | |||
2858 | SolarMutexGuard aVclGuard; | |||
2859 | ||||
2860 | ImplHideDDCursor(); | |||
2861 | } | |||
2862 | ||||
2863 | void Edit::dragOver( const css::datatransfer::dnd::DropTargetDragEvent& rDTDE ) | |||
2864 | { | |||
2865 | SolarMutexGuard aVclGuard; | |||
2866 | ||||
2867 | Point aMousePos( rDTDE.LocationX, rDTDE.LocationY ); | |||
2868 | ||||
2869 | sal_Int32 nPrevDropPos = mpDDInfo->nDropPos; | |||
2870 | mpDDInfo->nDropPos = ImplGetCharPos( aMousePos ); | |||
2871 | ||||
2872 | /* | |||
2873 | Size aOutSize = GetOutputSizePixel(); | |||
2874 | if ( ( aMousePos.X() < 0 ) || ( aMousePos.X() > aOutSize.Width() ) ) | |||
2875 | { | |||
2876 | // Scroll? | |||
2877 | // No, I will not receive events in this case... | |||
2878 | } | |||
2879 | */ | |||
2880 | ||||
2881 | Selection aSel( maSelection ); | |||
2882 | aSel.Justify(); | |||
2883 | ||||
2884 | // Don't accept drop in selection or read-only field... | |||
2885 | if ( IsReadOnly() || aSel.IsInside( mpDDInfo->nDropPos ) || ! mpDDInfo->bIsStringSupported ) | |||
2886 | { | |||
2887 | ImplHideDDCursor(); | |||
2888 | rDTDE.Context->rejectDrag(); | |||
2889 | } | |||
2890 | else | |||
2891 | { | |||
2892 | // draw the old cursor away... | |||
2893 | if ( !mpDDInfo->bVisCursor || ( nPrevDropPos != mpDDInfo->nDropPos ) ) | |||
2894 | { | |||
2895 | ImplHideDDCursor(); | |||
2896 | ImplShowDDCursor(); | |||
2897 | } | |||
2898 | rDTDE.Context->acceptDrag( rDTDE.DropAction ); | |||
2899 | } | |||
2900 | } | |||
2901 | ||||
2902 | OUString Edit::GetSurroundingText() const | |||
2903 | { | |||
2904 | if (mpSubEdit) | |||
2905 | return mpSubEdit->GetSurroundingText(); | |||
2906 | return maText.toString(); | |||
2907 | } | |||
2908 | ||||
2909 | Selection Edit::GetSurroundingTextSelection() const | |||
2910 | { | |||
2911 | return GetSelection(); | |||
2912 | } | |||
2913 | ||||
2914 | FactoryFunction Edit::GetUITestFactory() const | |||
2915 | { | |||
2916 | return EditUIObject::create; | |||
2917 | } | |||
2918 | ||||
2919 | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |
1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | /* |
3 | * This file is part of the LibreOffice project. |
4 | * |
5 | * This Source Code Form is subject to the terms of the Mozilla Public |
6 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
7 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
8 | * |
9 | * This file incorporates work covered by the following license notice: |
10 | * |
11 | * Licensed to the Apache Software Foundation (ASF) under one or more |
12 | * contributor license agreements. See the NOTICE file distributed |
13 | * with this work for additional information regarding copyright |
14 | * ownership. The ASF licenses this file to you under the Apache |
15 | * License, Version 2.0 (the "License"); you may not use this file |
16 | * except in compliance with the License. You may obtain a copy of |
17 | * the License at http://www.apache.org/licenses/LICENSE-2.0 . |
18 | */ |
19 | |
20 | #ifndef INCLUDED_VCL_PTR_HXX |
21 | #define INCLUDED_VCL_PTR_HXX |
22 | |
23 | #include <sal/config.h> |
24 | |
25 | #include <rtl/ref.hxx> |
26 | |
27 | #include <utility> |
28 | #include <type_traits> |
29 | |
30 | #ifdef DBG_UTIL |
31 | #ifndef _WIN32 |
32 | #include <vcl/vclmain.hxx> |
33 | #endif |
34 | #endif |
35 | |
36 | class VclReferenceBase; |
37 | |
38 | namespace vcl::detail { |
39 | |
40 | template<typename> |
41 | constexpr bool isIncompleteOrDerivedFromVclReferenceBase(...) { return true; } |
42 | |
43 | template<typename T> constexpr bool isIncompleteOrDerivedFromVclReferenceBase( |
44 | int (*)[sizeof(T)]) |
45 | { return std::is_base_of<VclReferenceBase, T>::value; } |
46 | |
47 | } // namespace vcl::detail |
48 | |
49 | /** |
50 | * A thin wrapper around rtl::Reference to implement the acquire and dispose semantics we want for references to vcl::Window subclasses. |
51 | * |
52 | * For more details on the design please see vcl/README.lifecycle |
53 | * |
54 | * @param reference_type must be a subclass of vcl::Window |
55 | */ |
56 | template <class reference_type> |
57 | class VclPtr |
58 | { |
59 | static_assert( |
60 | vcl::detail::isIncompleteOrDerivedFromVclReferenceBase<reference_type>( |
61 | nullptr), |
62 | "template argument type must be derived from VclReferenceBase"); |
63 | |
64 | ::rtl::Reference<reference_type> m_rInnerRef; |
65 | |
66 | public: |
67 | /** Constructor... |
68 | */ |
69 | VclPtr() |
70 | : m_rInnerRef() |
71 | {} |
72 | |
73 | /** Constructor... |
74 | */ |
75 | VclPtr (reference_type * pBody) |
76 | : m_rInnerRef(pBody) |
77 | {} |
78 | |
79 | /** Constructor... that doesn't take a ref. |
80 | */ |
81 | VclPtr (reference_type * pBody, __sal_NoAcquire) |
82 | : m_rInnerRef(pBody, SAL_NO_ACQUIRE) |
83 | {} |
84 | |
85 | /** Up-casting conversion constructor: Copies interface reference. |
86 | |
87 | Does not work for up-casts to ambiguous bases. For the special case of |
88 | up-casting to Reference< XInterface >, see the corresponding conversion |
89 | operator. |
90 | |
91 | @param rRef another reference |
92 | */ |
93 | template< class derived_type > |
94 | VclPtr( |
95 | const VclPtr< derived_type > & rRef, |
96 | typename std::enable_if< |
97 | std::is_base_of<reference_type, derived_type>::value, int>::type |
98 | = 0 ) |
99 | : m_rInnerRef( static_cast<reference_type*>(rRef) ) |
100 | { |
101 | } |
102 | |
103 | #if defined(DBG_UTIL) && !defined(_WIN32) |
104 | virtual ~VclPtr() |
105 | { |
106 | assert(m_rInnerRef.get() == nullptr || vclmain::isAlive())(static_cast <bool> (m_rInnerRef.get() == nullptr || vclmain ::isAlive()) ? void (0) : __assert_fail ("m_rInnerRef.get() == nullptr || vclmain::isAlive()" , "/home/maarten/src/libreoffice/core/include/vcl/vclptr.hxx" , 106, __extension__ __PRETTY_FUNCTION__)); |
107 | // We can be one of the intermediate counts, but if we are the last |
108 | // VclPtr keeping this object alive, then something forgot to call dispose(). |
109 | assert((!m_rInnerRef.get() || m_rInnerRef->isDisposed() || m_rInnerRef->getRefCount() > 1)(static_cast <bool> ((!m_rInnerRef.get() || m_rInnerRef ->isDisposed() || m_rInnerRef->getRefCount() > 1) && "someone forgot to call dispose()") ? void (0) : __assert_fail ("(!m_rInnerRef.get() || m_rInnerRef->isDisposed() || m_rInnerRef->getRefCount() > 1) && \"someone forgot to call dispose()\"" , "/home/maarten/src/libreoffice/core/include/vcl/vclptr.hxx" , 110, __extension__ __PRETTY_FUNCTION__)) |
110 | && "someone forgot to call dispose()")(static_cast <bool> ((!m_rInnerRef.get() || m_rInnerRef ->isDisposed() || m_rInnerRef->getRefCount() > 1) && "someone forgot to call dispose()") ? void (0) : __assert_fail ("(!m_rInnerRef.get() || m_rInnerRef->isDisposed() || m_rInnerRef->getRefCount() > 1) && \"someone forgot to call dispose()\"" , "/home/maarten/src/libreoffice/core/include/vcl/vclptr.hxx" , 110, __extension__ __PRETTY_FUNCTION__)); |
111 | } |
112 | VclPtr(VclPtr const &) = default; |
113 | VclPtr(VclPtr &&) = default; |
114 | VclPtr & operator =(VclPtr const &) = default; |
115 | VclPtr & operator =(VclPtr &&) = default; |
116 | #endif |
117 | |
118 | /** |
119 | * A construction helper for VclPtr. Since VclPtr types are created |
120 | * with a reference-count of one - to help fit into the existing |
121 | * code-flow; this helps us to construct them easily. |
122 | * |
123 | * For more details on the design please see vcl/README.lifecycle |
124 | * |
125 | * @tparam reference_type must be a subclass of vcl::Window |
126 | */ |
127 | template<typename... Arg> [[nodiscard]] static VclPtr< reference_type > Create(Arg &&... arg) |
128 | { |
129 | return VclPtr< reference_type >( new reference_type(std::forward<Arg>(arg)...), SAL_NO_ACQUIRE ); |
130 | } |
131 | |
132 | /** Probably most common used: handle->someBodyOp(). |
133 | */ |
134 | reference_type * operator->() const |
135 | { |
136 | return m_rInnerRef.get(); |
137 | } |
138 | |
139 | /** Get the body. Can be used instead of operator->(). |
140 | I.e. handle->someBodyOp() and handle.get()->someBodyOp() |
141 | are the same. |
142 | */ |
143 | reference_type * get() const |
144 | { |
145 | return m_rInnerRef.get(); |
146 | } |
147 | |
148 | void set(reference_type *pBody) |
149 | { |
150 | m_rInnerRef.set(pBody); |
151 | } |
152 | |
153 | void reset(reference_type *pBody) |
154 | { |
155 | m_rInnerRef.set(pBody); |
156 | } |
157 | |
158 | /** Up-casting copy assignment operator. |
159 | |
160 | Does not work for up-casts to ambiguous bases. |
161 | |
162 | @param rRef another reference |
163 | */ |
164 | template<typename derived_type> |
165 | typename std::enable_if< |
166 | std::is_base_of<reference_type, derived_type>::value, |
167 | VclPtr &>::type |
168 | operator =(VclPtr<derived_type> const & rRef) |
169 | { |
170 | m_rInnerRef.set(rRef.get()); |
171 | return *this; |
172 | } |
173 | |
174 | VclPtr & operator =(reference_type * pBody) |
175 | { |
176 | m_rInnerRef.set(pBody); |
177 | return *this; |
178 | } |
179 | |
180 | operator reference_type * () const |
181 | { |
182 | return m_rInnerRef.get(); |
183 | } |
184 | |
185 | explicit operator bool () const |
186 | { |
187 | return m_rInnerRef.get() != nullptr; |
188 | } |
189 | |
190 | void clear() |
191 | { |
192 | m_rInnerRef.clear(); |
193 | } |
194 | |
195 | void reset() |
196 | { |
197 | m_rInnerRef.clear(); |
198 | } |
199 | |
200 | void disposeAndClear() |
201 | { |
202 | // hold it alive for the lifetime of this method |
203 | ::rtl::Reference<reference_type> aTmp(m_rInnerRef); |
204 | m_rInnerRef.clear(); // we should use some 'swap' method ideally ;-) |
205 | if (aTmp.get()) { |
206 | aTmp->disposeOnce(); |
207 | } |
208 | } |
209 | |
210 | /** Needed to place VclPtr's into STL collection. |
211 | */ |
212 | bool operator< (const VclPtr<reference_type> & handle) const |
213 | { |
214 | return (m_rInnerRef < handle.m_rInnerRef); |
215 | } |
216 | }; // class VclPtr |
217 | |
218 | template<typename T1, typename T2> |
219 | inline bool operator ==(VclPtr<T1> const & p1, VclPtr<T2> const & p2) { |
220 | return p1.get() == p2.get(); |
221 | } |
222 | |
223 | template<typename T> inline bool operator ==(VclPtr<T> const & p1, T const * p2) |
224 | { |
225 | return p1.get() == p2; |
226 | } |
227 | |
228 | template<typename T> inline bool operator ==(VclPtr<T> const & p1, T * p2) { |
229 | return p1.get() == p2; |
230 | } |
231 | |
232 | template<typename T> inline bool operator ==(T const * p1, VclPtr<T> const & p2) |
233 | { |
234 | return p1 == p2.get(); |
235 | } |
236 | |
237 | template<typename T> inline bool operator ==(T * p1, VclPtr<T> const & p2) { |
238 | return p1 == p2.get(); |
239 | } |
240 | |
241 | template<typename T1, typename T2> |
242 | inline bool operator !=(VclPtr<T1> const & p1, VclPtr<T2> const & p2) { |
243 | return !(p1 == p2); |
244 | } |
245 | |
246 | template<typename T> inline bool operator !=(VclPtr<T> const & p1, T const * p2) |
247 | { |
248 | return !(p1 == p2); |
249 | } |
250 | |
251 | template<typename T> inline bool operator !=(VclPtr<T> const & p1, T * p2) { |
252 | return !(p1 == p2); |
253 | } |
254 | |
255 | template<typename T> inline bool operator !=(T const * p1, VclPtr<T> const & p2) |
256 | { |
257 | return !(p1 == p2); |
258 | } |
259 | |
260 | template<typename T> inline bool operator !=(T * p1, VclPtr<T> const & p2) { |
261 | return !(p1 == p2); |
262 | } |
263 | |
264 | /** |
265 | * A construction helper for a temporary VclPtr. Since VclPtr types |
266 | * are created with a reference-count of one - to help fit into |
267 | * the existing code-flow; this helps us to construct them easily. |
268 | * see also VclPtr::Create and ScopedVclPtr |
269 | * |
270 | * For more details on the design please see vcl/README.lifecycle |
271 | * |
272 | * @param reference_type must be a subclass of vcl::Window |
273 | */ |
274 | template <class reference_type> |
275 | class SAL_WARN_UNUSED__attribute__((warn_unused)) VclPtrInstance final : public VclPtr<reference_type> |
276 | { |
277 | public: |
278 | template<typename... Arg> VclPtrInstance(Arg &&... arg) |
279 | : VclPtr<reference_type>( new reference_type(std::forward<Arg>(arg)...), SAL_NO_ACQUIRE ) |
280 | { |
281 | } |
282 | |
283 | /** |
284 | * Override and disallow this, to prevent people accidentally calling it and actually |
285 | * getting VclPtr::Create and getting a naked VclPtr<> instance |
286 | */ |
287 | template<typename... Arg> static VclPtrInstance< reference_type > Create(Arg &&... ) = delete; |
288 | }; |
289 | |
290 | template <class reference_type> |
291 | class ScopedVclPtr : public VclPtr<reference_type> |
292 | { |
293 | public: |
294 | /** Constructor... |
295 | */ |
296 | ScopedVclPtr() |
297 | : VclPtr<reference_type>() |
298 | {} |
299 | |
300 | /** Constructor |
301 | */ |
302 | ScopedVclPtr (reference_type * pBody) |
303 | : VclPtr<reference_type>(pBody) |
304 | {} |
305 | |
306 | /** Copy constructor... |
307 | */ |
308 | ScopedVclPtr (const VclPtr<reference_type> & handle) |
309 | : VclPtr<reference_type>(handle) |
310 | {} |
311 | |
312 | /** |
313 | Assignment that releases the last reference. |
314 | */ |
315 | void disposeAndReset(reference_type *pBody) |
316 | { |
317 | if (pBody != this->get()) { |
318 | VclPtr<reference_type>::disposeAndClear(); |
319 | VclPtr<reference_type>::set(pBody); |
320 | } |
321 | } |
322 | |
323 | /** |
324 | Assignment that releases the last reference. |
325 | */ |
326 | ScopedVclPtr<reference_type>& operator = (reference_type * pBody) |
327 | { |
328 | disposeAndReset(pBody); |
329 | return *this; |
330 | } |
331 | |
332 | /** Up-casting conversion constructor: Copies interface reference. |
333 | |
334 | Does not work for up-casts to ambiguous bases. For the special case of |
335 | up-casting to Reference< XInterface >, see the corresponding conversion |
336 | operator. |
337 | |
338 | @param rRef another reference |
339 | */ |
340 | template< class derived_type > |
341 | ScopedVclPtr( |
342 | const VclPtr< derived_type > & rRef, |
343 | typename std::enable_if< |
344 | std::is_base_of<reference_type, derived_type>::value, int>::type |
345 | = 0 ) |
346 | : VclPtr<reference_type>( rRef ) |
347 | { |
348 | } |
349 | |
350 | /** Up-casting assignment operator. |
351 | |
352 | Does not work for up-casts to ambiguous bases. |
353 | |
354 | @param rRef another VclPtr |
355 | */ |
356 | template<typename derived_type> |
357 | typename std::enable_if< |
358 | std::is_base_of<reference_type, derived_type>::value, |
359 | ScopedVclPtr &>::type |
360 | operator =(VclPtr<derived_type> const & rRef) |
361 | { |
362 | disposeAndReset(rRef.get()); |
363 | return *this; |
364 | } |
365 | |
366 | /** |
367 | * Override and disallow this, to prevent people accidentally calling it and actually |
368 | * getting VclPtr::Create and getting a naked VclPtr<> instance |
369 | */ |
370 | template<typename... Arg> static ScopedVclPtr< reference_type > Create(Arg &&... ) = delete; |
371 | |
372 | ~ScopedVclPtr() |
373 | { |
374 | VclPtr<reference_type>::disposeAndClear(); |
375 | assert(VclPtr<reference_type>::get() == nullptr)(static_cast <bool> (VclPtr<reference_type>::get( ) == nullptr) ? void (0) : __assert_fail ("VclPtr<reference_type>::get() == nullptr" , "/home/maarten/src/libreoffice/core/include/vcl/vclptr.hxx" , 375, __extension__ __PRETTY_FUNCTION__)); // make sure there are no lingering references |
376 | } |
377 | |
378 | private: |
379 | // Most likely we don't want this default copy-constructor. |
380 | ScopedVclPtr (const ScopedVclPtr<reference_type> &) = delete; |
381 | // And certainly we don't want a default assignment operator. |
382 | ScopedVclPtr<reference_type>& operator = (const ScopedVclPtr<reference_type> &) = delete; |
383 | // And disallow reset as that doesn't call disposeAndClear on the original reference |
384 | void reset() = delete; |
385 | void reset(reference_type *pBody) = delete; |
386 | |
387 | protected: |
388 | ScopedVclPtr (reference_type * pBody, __sal_NoAcquire) |
389 | : VclPtr<reference_type>(pBody, SAL_NO_ACQUIRE) |
390 | {} |
391 | }; |
392 | |
393 | /** |
394 | * A construction helper for ScopedVclPtr. Since VclPtr types are created |
395 | * with a reference-count of one - to help fit into the existing |
396 | * code-flow; this helps us to construct them easily. |
397 | * |
398 | * For more details on the design please see vcl/README.lifecycle |
399 | * |
400 | * @param reference_type must be a subclass of vcl::Window |
401 | */ |
402 | #if defined _MSC_VER |
403 | #pragma warning(push) |
404 | #pragma warning(disable: 4521) // " multiple copy constructors specified" |
405 | #endif |
406 | template <class reference_type> |
407 | class SAL_WARN_UNUSED__attribute__((warn_unused)) ScopedVclPtrInstance final : public ScopedVclPtr<reference_type> |
408 | { |
409 | public: |
410 | template<typename... Arg> ScopedVclPtrInstance(Arg &&... arg) |
411 | : ScopedVclPtr<reference_type>( new reference_type(std::forward<Arg>(arg)...), SAL_NO_ACQUIRE ) |
412 | { |
413 | } |
414 | |
415 | /** |
416 | * Override and disallow this, to prevent people accidentally calling it and actually |
417 | * getting VclPtr::Create and getting a naked VclPtr<> instance |
418 | */ |
419 | template<typename... Arg> static ScopedVclPtrInstance< reference_type > Create(Arg &&...) = delete; |
420 | |
421 | private: |
422 | // Prevent the above perfect forwarding ctor from hijacking (accidental) |
423 | // attempts at ScopedVclPtrInstance copy construction (where the hijacking |
424 | // would typically lead to somewhat obscure error messages); both non-const |
425 | // and const variants are needed here, as the ScopedVclPtr base class has a |
426 | // const--variant copy ctor, so the implicitly declared copy ctor for |
427 | // ScopedVclPtrInstance would also be the const variant, so non-const copy |
428 | // construction attempts would be hijacked by the perfect forwarding ctor; |
429 | // but if we only declared a non-const variant here, the const variant would |
430 | // no longer be implicitly declared (as there would already be an explicitly |
431 | // declared copy ctor), so const copy construction attempts would then be |
432 | // hijacked by the perfect forwarding ctor: |
433 | ScopedVclPtrInstance(ScopedVclPtrInstance &) = delete; |
434 | ScopedVclPtrInstance(ScopedVclPtrInstance const &) = delete; |
435 | }; |
436 | #if defined _MSC_VER |
437 | #pragma warning(pop) |
438 | #endif |
439 | |
440 | #endif // INCLUDED_VCL_PTR_HXX |
441 | |
442 | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |
1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ | ||||||||
2 | /* | ||||||||
3 | * This file is part of the LibreOffice project. | ||||||||
4 | * | ||||||||
5 | * This Source Code Form is subject to the terms of the Mozilla Public | ||||||||
6 | * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||||||
7 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||||
8 | * | ||||||||
9 | * This file incorporates work covered by the following license notice: | ||||||||
10 | * | ||||||||
11 | * Licensed to the Apache Software Foundation (ASF) under one or more | ||||||||
12 | * contributor license agreements. See the NOTICE file distributed | ||||||||
13 | * with this work for additional information regarding copyright | ||||||||
14 | * ownership. The ASF licenses this file to you under the Apache | ||||||||
15 | * License, Version 2.0 (the "License"); you may not use this file | ||||||||
16 | * except in compliance with the License. You may obtain a copy of | ||||||||
17 | * the License at http://www.apache.org/licenses/LICENSE-2.0 . | ||||||||
18 | */ | ||||||||
19 | |||||||||
20 | #ifndef INCLUDED_RTL_REF_HXX | ||||||||
21 | #define INCLUDED_RTL_REF_HXX | ||||||||
22 | |||||||||
23 | #include "sal/config.h" | ||||||||
24 | |||||||||
25 | #include <cassert> | ||||||||
26 | #include <cstddef> | ||||||||
27 | #include <functional> | ||||||||
28 | #ifdef LIBO_INTERNAL_ONLY1 | ||||||||
29 | #include <type_traits> | ||||||||
30 | #endif | ||||||||
31 | |||||||||
32 | #include "sal/types.h" | ||||||||
33 | |||||||||
34 | namespace rtl | ||||||||
35 | { | ||||||||
36 | |||||||||
37 | /** Template reference class for reference type. | ||||||||
38 | */ | ||||||||
39 | template <class reference_type> | ||||||||
40 | class Reference | ||||||||
41 | { | ||||||||
42 | /** The <b>reference_type</b> body pointer. | ||||||||
43 | */ | ||||||||
44 | reference_type * m_pBody; | ||||||||
45 | |||||||||
46 | |||||||||
47 | public: | ||||||||
48 | /** Constructor... | ||||||||
49 | */ | ||||||||
50 | Reference() | ||||||||
51 | : m_pBody (NULL__null) | ||||||||
52 | {} | ||||||||
53 | |||||||||
54 | |||||||||
55 | /** Constructor... | ||||||||
56 | */ | ||||||||
57 | Reference (reference_type * pBody, __sal_NoAcquire) | ||||||||
58 | : m_pBody (pBody) | ||||||||
59 | { | ||||||||
60 | } | ||||||||
61 | |||||||||
62 | /** Constructor... | ||||||||
63 | */ | ||||||||
64 | Reference (reference_type * pBody) | ||||||||
65 | : m_pBody (pBody) | ||||||||
66 | { | ||||||||
67 | if (m_pBody) | ||||||||
68 | m_pBody->acquire(); | ||||||||
69 | } | ||||||||
70 | |||||||||
71 | /** Copy constructor... | ||||||||
72 | */ | ||||||||
73 | Reference (const Reference<reference_type> & handle) | ||||||||
74 | : m_pBody (handle.m_pBody) | ||||||||
75 | { | ||||||||
76 | if (m_pBody) | ||||||||
77 | m_pBody->acquire(); | ||||||||
78 | } | ||||||||
79 | |||||||||
80 | #ifdef LIBO_INTERNAL_ONLY1 | ||||||||
81 | /** Move constructor... | ||||||||
82 | */ | ||||||||
83 | Reference (Reference<reference_type> && handle) noexcept | ||||||||
84 | : m_pBody (handle.m_pBody) | ||||||||
85 | { | ||||||||
86 | handle.m_pBody = nullptr; | ||||||||
87 | } | ||||||||
88 | #endif | ||||||||
89 | |||||||||
90 | #if defined LIBO_INTERNAL_ONLY1 | ||||||||
91 | /** Up-casting conversion constructor: Copies interface reference. | ||||||||
92 | |||||||||
93 | Does not work for up-casts to ambiguous bases. | ||||||||
94 | |||||||||
95 | @param rRef another reference | ||||||||
96 | */ | ||||||||
97 | template< class derived_type > | ||||||||
98 | inline Reference( | ||||||||
99 | const Reference< derived_type > & rRef, | ||||||||
100 | std::enable_if_t<std::is_base_of_v<reference_type, derived_type>, int> = 0 ) | ||||||||
101 | : m_pBody (rRef.get()) | ||||||||
102 | { | ||||||||
103 | if (m_pBody) | ||||||||
104 | m_pBody->acquire(); | ||||||||
105 | } | ||||||||
106 | #endif | ||||||||
107 | |||||||||
108 | /** Destructor... | ||||||||
109 | */ | ||||||||
110 | ~Reference() COVERITY_NOEXCEPT_FALSE | ||||||||
111 | { | ||||||||
112 | if (m_pBody) | ||||||||
113 | m_pBody->release(); | ||||||||
114 | } | ||||||||
115 | |||||||||
116 | /** Set... | ||||||||
117 | Similar to assignment. | ||||||||
118 | */ | ||||||||
119 | Reference<reference_type> & | ||||||||
120 | SAL_CALL set (reference_type * pBody) | ||||||||
121 | { | ||||||||
122 | if (pBody) | ||||||||
123 | pBody->acquire(); | ||||||||
124 | reference_type * const pOld = m_pBody; | ||||||||
125 | m_pBody = pBody; | ||||||||
126 | if (pOld) | ||||||||
127 | pOld->release(); | ||||||||
128 | return *this; | ||||||||
129 | } | ||||||||
130 | |||||||||
131 | /** Assignment. | ||||||||
132 | Unbinds this instance from its body (if bound) and | ||||||||
133 | bind it to the body represented by the handle. | ||||||||
134 | */ | ||||||||
135 | Reference<reference_type> & | ||||||||
136 | SAL_CALL operator= (const Reference<reference_type> & handle) | ||||||||
137 | { | ||||||||
138 | return set( handle.m_pBody ); | ||||||||
139 | } | ||||||||
140 | |||||||||
141 | #ifdef LIBO_INTERNAL_ONLY1 | ||||||||
142 | /** Assignment. | ||||||||
143 | * Unbinds this instance from its body (if bound), | ||||||||
144 | * bind it to the body represented by the handle, and | ||||||||
145 | * set the body represented by the handle to nullptr. | ||||||||
146 | */ | ||||||||
147 | Reference<reference_type> & | ||||||||
148 | operator= (Reference<reference_type> && handle) | ||||||||
149 | { | ||||||||
150 | // self-movement guts ourself | ||||||||
151 | if (m_pBody) | ||||||||
152 | m_pBody->release(); | ||||||||
153 | m_pBody = handle.m_pBody; | ||||||||
154 | handle.m_pBody = nullptr; | ||||||||
155 | return *this; | ||||||||
156 | } | ||||||||
157 | #endif | ||||||||
158 | |||||||||
159 | /** Assignment... | ||||||||
160 | */ | ||||||||
161 | Reference<reference_type> & | ||||||||
162 | SAL_CALL operator= (reference_type * pBody) | ||||||||
163 | { | ||||||||
164 | return set( pBody ); | ||||||||
165 | } | ||||||||
166 | |||||||||
167 | /** Unbind the body from this handle. | ||||||||
168 | Note that for a handle representing a large body, | ||||||||
169 | "handle.clear().set(new body());" _might_ | ||||||||
170 | perform a little bit better than "handle.set(new body());", | ||||||||
171 | since in the second case two large objects exist in memory | ||||||||
172 | (the old body and the new body). | ||||||||
173 | */ | ||||||||
174 | Reference<reference_type> & SAL_CALL clear() | ||||||||
175 | { | ||||||||
176 | if (m_pBody
| ||||||||
177 | { | ||||||||
178 | reference_type * const pOld = m_pBody; | ||||||||
179 | m_pBody = NULL__null; | ||||||||
180 | pOld->release(); | ||||||||
181 | } | ||||||||
182 | return *this; | ||||||||
183 | } | ||||||||
184 | |||||||||
185 | |||||||||
186 | /** Get the body. Can be used instead of operator->(). | ||||||||
187 | I.e. handle->someBodyOp() and handle.get()->someBodyOp() | ||||||||
188 | are the same. | ||||||||
189 | */ | ||||||||
190 | reference_type * SAL_CALL get() const | ||||||||
191 | { | ||||||||
192 | return m_pBody; | ||||||||
| |||||||||
193 | } | ||||||||
194 | |||||||||
195 | |||||||||
196 | /** Probably most common used: handle->someBodyOp(). | ||||||||
197 | */ | ||||||||
198 | reference_type * SAL_CALL operator->() const | ||||||||
199 | { | ||||||||
200 | assert(m_pBody != NULL)(static_cast <bool> (m_pBody != __null) ? void (0) : __assert_fail ("m_pBody != NULL", "/home/maarten/src/libreoffice/core/include/rtl/ref.hxx" , 200, __extension__ __PRETTY_FUNCTION__)); | ||||||||
201 | return m_pBody; | ||||||||
202 | } | ||||||||
203 | |||||||||
204 | |||||||||
205 | /** Allows (*handle).someBodyOp(). | ||||||||
206 | */ | ||||||||
207 | reference_type & SAL_CALL operator*() const | ||||||||
208 | { | ||||||||
209 | assert(m_pBody != NULL)(static_cast <bool> (m_pBody != __null) ? void (0) : __assert_fail ("m_pBody != NULL", "/home/maarten/src/libreoffice/core/include/rtl/ref.hxx" , 209, __extension__ __PRETTY_FUNCTION__)); | ||||||||
210 | return *m_pBody; | ||||||||
211 | } | ||||||||
212 | |||||||||
213 | |||||||||
214 | /** Returns True if the handle does point to a valid body. | ||||||||
215 | */ | ||||||||
216 | bool SAL_CALL is() const | ||||||||
217 | { | ||||||||
218 | return (m_pBody != NULL__null); | ||||||||
219 | } | ||||||||
220 | |||||||||
221 | #if defined LIBO_INTERNAL_ONLY1 | ||||||||
222 | /** Returns True if the handle does point to a valid body. | ||||||||
223 | */ | ||||||||
224 | explicit operator bool() const | ||||||||
225 | { | ||||||||
226 | return is(); | ||||||||
227 | } | ||||||||
228 | #endif | ||||||||
229 | |||||||||
230 | /** Returns True if this points to pBody. | ||||||||
231 | */ | ||||||||
232 | bool SAL_CALL operator== (const reference_type * pBody) const | ||||||||
233 | { | ||||||||
234 | return (m_pBody == pBody); | ||||||||
235 | } | ||||||||
236 | |||||||||
237 | |||||||||
238 | /** Returns True if handle points to the same body. | ||||||||
239 | */ | ||||||||
240 | bool | ||||||||
241 | SAL_CALL operator== (const Reference<reference_type> & handle) const | ||||||||
242 | { | ||||||||
243 | return (m_pBody == handle.m_pBody); | ||||||||
244 | } | ||||||||
245 | |||||||||
246 | |||||||||
247 | /** Needed to place References into STL collection. | ||||||||
248 | */ | ||||||||
249 | bool | ||||||||
250 | SAL_CALL operator!= (const Reference<reference_type> & handle) const | ||||||||
251 | { | ||||||||
252 | return (m_pBody != handle.m_pBody); | ||||||||
253 | } | ||||||||
254 | |||||||||
255 | |||||||||
256 | /** Needed to place References into STL collection. | ||||||||
257 | */ | ||||||||
258 | bool | ||||||||
259 | SAL_CALL operator< (const Reference<reference_type> & handle) const | ||||||||
260 | { | ||||||||
261 | return (m_pBody < handle.m_pBody); | ||||||||
262 | } | ||||||||
263 | |||||||||
264 | |||||||||
265 | /** Needed to place References into STL collection. | ||||||||
266 | */ | ||||||||
267 | bool | ||||||||
268 | SAL_CALL operator> (const Reference<reference_type> & handle) const | ||||||||
269 | { | ||||||||
270 | return (m_pBody > handle.m_pBody); | ||||||||
271 | } | ||||||||
272 | }; | ||||||||
273 | |||||||||
274 | } // namespace rtl | ||||||||
275 | |||||||||
276 | #if defined LIBO_INTERNAL_ONLY1 | ||||||||
277 | namespace std | ||||||||
278 | { | ||||||||
279 | |||||||||
280 | /// @cond INTERNAL | ||||||||
281 | /** | ||||||||
282 | Make rtl::Reference hashable by default for use in STL containers. | ||||||||
283 | |||||||||
284 | @since LibreOffice 6.3 | ||||||||
285 | */ | ||||||||
286 | template<typename T> | ||||||||
287 | struct hash<::rtl::Reference<T>> | ||||||||
288 | { | ||||||||
289 | std::size_t operator()(::rtl::Reference<T> const & s) const | ||||||||
290 | { return std::size_t(s.get()); } | ||||||||
291 | }; | ||||||||
292 | /// @endcond | ||||||||
293 | |||||||||
294 | } | ||||||||
295 | |||||||||
296 | #endif | ||||||||
297 | |||||||||
298 | #endif /* ! INCLUDED_RTL_REF_HXX */ | ||||||||
299 | |||||||||
300 | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |
1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | /* |
3 | * This file is part of the LibreOffice project. |
4 | * |
5 | * This Source Code Form is subject to the terms of the Mozilla Public |
6 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
7 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
8 | * |
9 | * This file incorporates work covered by the following license notice: |
10 | * |
11 | * Licensed to the Apache Software Foundation (ASF) under one or more |
12 | * contributor license agreements. See the NOTICE file distributed |
13 | * with this work for additional information regarding copyright |
14 | * ownership. The ASF licenses this file to you under the Apache |
15 | * License, Version 2.0 (the "License"); you may not use this file |
16 | * except in compliance with the License. You may obtain a copy of |
17 | * the License at http://www.apache.org/licenses/LICENSE-2.0 . |
18 | */ |
19 | #ifndef INCLUDED_VCL_Reference_HXX |
20 | #define INCLUDED_VCL_Reference_HXX |
21 | |
22 | #include <vcl/dllapi.h> |
23 | #include <osl/interlck.h> |
24 | |
25 | class VCL_DLLPUBLIC__attribute__ ((visibility("default"))) VclReferenceBase |
26 | { |
27 | mutable oslInterlockedCount mnRefCnt; |
28 | |
29 | template<typename T> friend class VclPtr; |
30 | |
31 | public: |
32 | void acquire() const |
33 | { |
34 | osl_atomic_increment(&mnRefCnt)__sync_add_and_fetch((&mnRefCnt), 1); |
35 | } |
36 | |
37 | void release() const |
38 | { |
39 | if (osl_atomic_decrement(&mnRefCnt)__sync_sub_and_fetch((&mnRefCnt), 1) == 0) |
40 | delete this; |
41 | } |
42 | #ifdef DBG_UTIL |
43 | #ifndef _WIN32 |
44 | sal_Int32 getRefCount() const { return mnRefCnt; } |
45 | #endif |
46 | #endif |
47 | |
48 | |
49 | private: |
50 | VclReferenceBase(const VclReferenceBase&) = delete; |
51 | VclReferenceBase& operator=(const VclReferenceBase&) = delete; |
52 | |
53 | bool mbDisposed : 1; |
54 | |
55 | protected: |
56 | VclReferenceBase(); |
57 | protected: |
58 | virtual ~VclReferenceBase(); |
59 | |
60 | protected: |
61 | virtual void dispose(); |
62 | |
63 | public: |
64 | void disposeOnce(); |
65 | bool isDisposed() const { return mbDisposed; } |
66 | |
67 | }; |
68 | #endif |