/src/libreoffice/svx/source/svdraw/svdedxv.cxx
Line | Count | Source |
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 <com/sun/star/i18n/WordType.hpp> |
21 | | #include <editeng/editdata.hxx> |
22 | | #include <editeng/editeng.hxx> |
23 | | #include <editeng/editobj.hxx> |
24 | | #include <editeng/editstat.hxx> |
25 | | #include <editeng/outlobj.hxx> |
26 | | #include <editeng/unotext.hxx> |
27 | | #include <o3tl/deleter.hxx> |
28 | | #include <officecfg/Office/Common.hxx> |
29 | | #include <svl/itemiter.hxx> |
30 | | #include <svl/style.hxx> |
31 | | #include <svl/whiter.hxx> |
32 | | #include <svx/sdtfchim.hxx> |
33 | | #include <svx/selectioncontroller.hxx> |
34 | | #include <svx/svdedxv.hxx> |
35 | | #include <svx/svdetc.hxx> |
36 | | #include <svx/svdotable.hxx> |
37 | | #include <svx/svdotext.hxx> |
38 | | #include <svx/svdoutl.hxx> |
39 | | #include <svx/svdpage.hxx> |
40 | | #include <svx/svdpagv.hxx> |
41 | | #include <svx/svdundo.hxx> |
42 | | #include <vcl/canvastools.hxx> |
43 | | #include <vcl/commandevent.hxx> |
44 | | #include <vcl/cursor.hxx> |
45 | | #include <vcl/dndlistenercontainer.hxx> |
46 | | #include <vcl/weld.hxx> |
47 | | #include <vcl/window.hxx> |
48 | | #include <comphelper/lok.hxx> |
49 | | #include <basegfx/polygon/b2dpolygontools.hxx> |
50 | | #include <drawinglayer/processor2d/baseprocessor2d.hxx> |
51 | | #include <drawinglayer/primitive2d/maskprimitive2d.hxx> |
52 | | #include <drawinglayer/processor2d/processor2dtools.hxx> |
53 | | #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> |
54 | | #include <editeng/outliner.hxx> |
55 | | #include <sal/log.hxx> |
56 | | #include <sdr/overlay/overlaytools.hxx> |
57 | | #include <sfx2/viewsh.hxx> |
58 | | #include <svx/dialmgr.hxx> |
59 | | #include <svx/sdr/overlay/overlaymanager.hxx> |
60 | | #include <svx/sdr/overlay/overlayselection.hxx> |
61 | | #include <svx/sdr/table/tablecontroller.hxx> |
62 | | #include <svx/sdrpagewindow.hxx> |
63 | | #include <svx/sdrpaintwindow.hxx> |
64 | | #include <svx/sdrundomanager.hxx> |
65 | | #include <svx/strings.hrc> |
66 | | #include <svx/svdviter.hxx> |
67 | | #include <svtools/optionsdrawinglayer.hxx> |
68 | | #include <textchain.hxx> |
69 | | #include <textchaincursor.hxx> |
70 | | #include <tools/debug.hxx> |
71 | | #include <vcl/svapp.hxx> |
72 | | #include <svx/sdr/contact/viewcontact.hxx> |
73 | | |
74 | | #include <memory> |
75 | | |
76 | | SdrObjEditView::SdrObjEditView(SdrModel& rSdrModel, OutputDevice* pOut) |
77 | 345k | : SdrGlueEditView(rSdrModel, pOut) |
78 | 345k | , maTEOverlayGroup() |
79 | 345k | , maTextEditUpdateTimer("TextEditUpdateTimer") |
80 | 345k | , mxWeakTextEditObj() |
81 | 345k | , mpTextEditPV(nullptr) |
82 | 345k | , mpTextEditOutlinerView(nullptr) |
83 | 345k | , mpTextEditWin(nullptr) |
84 | 345k | , m_pTextEditCursorBuffer(nullptr) |
85 | 345k | , m_pMacroObj(nullptr) |
86 | 345k | , m_pMacroPV(nullptr) |
87 | 345k | , m_pMacroWin(nullptr) |
88 | 345k | , m_aTextEditArea() |
89 | 345k | , m_aMinTextEditArea() |
90 | 345k | , m_aOldCalcFieldValueLink() |
91 | 345k | , m_aMacroDownPos() |
92 | 345k | , m_nMacroTol(0) |
93 | 345k | , mbTextEditDontDelete(false) |
94 | 345k | , mbTextEditOnlyOneView(false) |
95 | 345k | , mbTextEditNewObj(false) |
96 | 345k | , mbQuickTextEditMode(true) |
97 | 345k | , mbMacroDown(false) |
98 | 345k | , mbInteractiveSlideShow(false) |
99 | 345k | , mxSelectionController() |
100 | 345k | , mxLastSelectionController() |
101 | 345k | , mpOldTextEditUndoManager(nullptr) |
102 | 345k | , mpLocalTextEditUndoManager() |
103 | 345k | { |
104 | | // init some timer settings (not starting it of course) |
105 | 345k | maTextEditUpdateTimer.SetTimeout(EDIT_UPDATEDATA_TIMEOUT); |
106 | 345k | maTextEditUpdateTimer.SetInvokeHandler(LINK(this, SdrObjEditView, TextEditUpdate)); |
107 | 345k | } |
108 | | |
109 | | IMPL_LINK_NOARG(SdrObjEditView, ImpModifyHdl, LinkParamNone*, void) |
110 | 0 | { |
111 | | // IASS: active TextEdit had a model change. Check and react. |
112 | 0 | if (nullptr == mpTextEditOutliner) |
113 | | // no Outliner, no TextEdit |
114 | 0 | return; |
115 | | |
116 | 0 | if (!mxWeakTextEditObj.get().is()) |
117 | | // no TextObject, no TextEdit |
118 | 0 | return; |
119 | | |
120 | | // reset & restart the timer |
121 | 0 | maTextEditUpdateTimer.SetTimeout(EDIT_UPDATEDATA_TIMEOUT); |
122 | 0 | maTextEditUpdateTimer.Start(); |
123 | 0 | } |
124 | | |
125 | | IMPL_LINK_NOARG(SdrObjEditView, TextEditUpdate, Timer*, void) |
126 | 0 | { |
127 | | // IASS: text was changed and EDIT_UPDATEDATA_TIMEOUT has passed |
128 | | // since last user input |
129 | 0 | maTextEditUpdateTimer.Stop(); |
130 | | |
131 | | // be safe: still in TextEdit? |
132 | 0 | if (nullptr == mpTextEditOutliner) |
133 | | // no Outliner, no TextEdit |
134 | 0 | return; |
135 | | |
136 | 0 | if (!mxWeakTextEditObj.get().is()) |
137 | | // no TextObject, no TextEdit |
138 | 0 | return; |
139 | | |
140 | | // launch an ObjectChange: This is the straightforward method |
141 | | // to get this broadcasted. We do not risk to set the model |
142 | | // unwantedly to changed, we had a text edit going on already. |
143 | | // This is needed for SlideShow since it is not (yet) using the |
144 | | // standard schema with VC/VOC/OC |
145 | 0 | if (isInteractiveSlideShow()) |
146 | 0 | mxWeakTextEditObj.get()->BroadcastObjectChange(); |
147 | | |
148 | | // force repaint for objects with changed text in all views |
149 | | // that are VC/VOC/OC based (SlideShow is not yet) |
150 | 0 | sdr::contact::ViewContact& rVC(mxWeakTextEditObj.get()->GetViewContact()); |
151 | |
|
152 | 0 | if (!rVC.hasMultipleViewObjectContacts()) |
153 | | // only one VOC -> this is us |
154 | 0 | return; |
155 | | |
156 | 0 | if (nullptr == mpTextEditPV) |
157 | | // should not happen, just invalidate all visualizations |
158 | 0 | rVC.ActionChanged(); |
159 | 0 | else |
160 | | // invalidate only visualizations in different views: |
161 | | // this is important to not cause evtl. high repaint costs |
162 | | // in the EditView -> we avoid this by running the TextEdit |
163 | | // on the overlay. NOTE: This is only for better performance, |
164 | | // any repaint will just work fine and do the right thing |
165 | 0 | rVC.ActionChangedIfDifferentPageView(*mpTextEditPV); |
166 | 0 | } |
167 | | |
168 | | SdrObjEditView::~SdrObjEditView() |
169 | 345k | { |
170 | 345k | maTextEditUpdateTimer.Stop(); |
171 | 345k | mpTextEditWin = nullptr; // so there's no ShowCursor in SdrEndTextEdit |
172 | 345k | assert(!IsTextEdit()); |
173 | 345k | if (IsTextEdit()) |
174 | 0 | suppress_fun_call_w_exception(SdrEndTextEdit()); |
175 | 345k | mpTextEditOutliner.reset(); |
176 | 345k | assert(nullptr == mpOldTextEditUndoManager); // should have been reset |
177 | 345k | } |
178 | | |
179 | 0 | bool SdrObjEditView::IsAction() const { return IsMacroObj() || SdrGlueEditView::IsAction(); } |
180 | | |
181 | | void SdrObjEditView::MovAction(const Point& rPnt) |
182 | 0 | { |
183 | 0 | if (IsMacroObj()) |
184 | 0 | MovMacroObj(rPnt); |
185 | 0 | SdrGlueEditView::MovAction(rPnt); |
186 | 0 | } |
187 | | |
188 | | void SdrObjEditView::EndAction() |
189 | 0 | { |
190 | 0 | if (IsMacroObj()) |
191 | 0 | EndMacroObj(); |
192 | 0 | SdrGlueEditView::EndAction(); |
193 | 0 | } |
194 | | |
195 | | void SdrObjEditView::BckAction() |
196 | 0 | { |
197 | 0 | BrkMacroObj(); |
198 | 0 | SdrGlueEditView::BckAction(); |
199 | 0 | } |
200 | | |
201 | | void SdrObjEditView::BrkAction() |
202 | 67.3k | { |
203 | 67.3k | BrkMacroObj(); |
204 | 67.3k | SdrGlueEditView::BrkAction(); |
205 | 67.3k | } |
206 | | |
207 | | SdrPageView* SdrObjEditView::ShowSdrPage(SdrPage* pPage) |
208 | 67.3k | { |
209 | 67.3k | SdrPageView* pPageView = SdrGlueEditView::ShowSdrPage(pPage); |
210 | | |
211 | 67.3k | if (comphelper::LibreOfficeKit::isActive() && pPageView) |
212 | 0 | { |
213 | | // Check if other views have an active text edit on the same page as |
214 | | // this one. |
215 | 0 | SdrViewIter::ForAllViews(pPageView->GetPage(), [this](SdrView* pView) { |
216 | 0 | if (pView == this || !pView->IsTextEdit()) |
217 | 0 | return; |
218 | | |
219 | 0 | OutputDevice* pOutDev = GetFirstOutputDevice(); |
220 | 0 | if (!pOutDev || pOutDev->GetOutDevType() != OUTDEV_WINDOW) |
221 | 0 | return; |
222 | | |
223 | | // Found one, so create an outliner view, to get invalidations when |
224 | | // the text edit changes. |
225 | | // Call GetSfxViewShell() to make sure ImpMakeOutlinerView() |
226 | | // registers the view shell of this draw view, and not the view |
227 | | // shell of pView. |
228 | 0 | OutlinerView* pOutlinerView |
229 | 0 | = pView->ImpMakeOutlinerView(pOutDev->GetOwnerWindow(), nullptr, GetSfxViewShell()); |
230 | 0 | pOutlinerView->HideCursor(); |
231 | 0 | pView->GetTextEditOutliner()->InsertView(pOutlinerView); |
232 | 0 | }); |
233 | 0 | } |
234 | | |
235 | 67.3k | return pPageView; |
236 | 67.3k | } |
237 | | |
238 | | namespace |
239 | | { |
240 | | /// Removes outliner views registered in other draw views that use pOutputDevice. |
241 | | void lcl_RemoveTextEditOutlinerViews(SdrObjEditView const* pThis, SdrPageView const* pPageView, |
242 | | OutputDevice const* pOutputDevice) |
243 | 67.3k | { |
244 | 67.3k | if (!comphelper::LibreOfficeKit::isActive()) |
245 | 67.3k | return; |
246 | | |
247 | 0 | if (!pPageView) |
248 | 0 | return; |
249 | | |
250 | 0 | if (!pOutputDevice || pOutputDevice->GetOutDevType() != OUTDEV_WINDOW) |
251 | 0 | return; |
252 | | |
253 | 0 | SdrViewIter::ForAllViews(pPageView->GetPage(), [&pThis, &pOutputDevice](SdrView* pView) { |
254 | 0 | if (pView == pThis || !pView->IsTextEdit()) |
255 | 0 | return; |
256 | | |
257 | 0 | SdrOutliner* pOutliner = pView->GetTextEditOutliner(); |
258 | 0 | for (size_t nView = 0; nView < pOutliner->GetViewCount(); ++nView) |
259 | 0 | { |
260 | 0 | OutlinerView* pOutlinerView = pOutliner->GetView(nView); |
261 | 0 | if (pOutlinerView->GetWindow()->GetOutDev() != pOutputDevice) |
262 | 0 | continue; |
263 | | |
264 | 0 | pOutliner->RemoveView(pOutlinerView); |
265 | 0 | delete pOutlinerView; |
266 | 0 | } |
267 | 0 | }); |
268 | 0 | } |
269 | | } |
270 | | |
271 | | void SdrObjEditView::HideSdrPage() |
272 | 67.3k | { |
273 | 67.3k | lcl_RemoveTextEditOutlinerViews(this, GetSdrPageView(), GetFirstOutputDevice()); |
274 | | |
275 | 67.3k | if (mpTextEditPV == GetSdrPageView()) |
276 | 0 | { |
277 | | // HideSdrPage() will clear mpPageView, avoid a dangling pointer. |
278 | 0 | mpTextEditPV = nullptr; |
279 | 0 | } |
280 | | |
281 | 67.3k | SdrGlueEditView::HideSdrPage(); |
282 | 67.3k | } |
283 | | |
284 | | void SdrObjEditView::TakeActionRect(tools::Rectangle& rRect) const |
285 | 0 | { |
286 | 0 | if (IsMacroObj()) |
287 | 0 | { |
288 | 0 | rRect = m_pMacroObj->GetCurrentBoundRect(); |
289 | 0 | } |
290 | 0 | else |
291 | 0 | { |
292 | 0 | SdrGlueEditView::TakeActionRect(rRect); |
293 | 0 | } |
294 | 0 | } |
295 | | |
296 | | void SdrObjEditView::Notify(SfxBroadcaster& rBC, const SfxHint& rHint) |
297 | 544M | { |
298 | 544M | SdrGlueEditView::Notify(rBC, rHint); |
299 | 544M | if (mpTextEditOutliner == nullptr) |
300 | 544M | return; |
301 | | |
302 | | // change of printer while editing |
303 | 0 | if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint) |
304 | 0 | return; |
305 | | |
306 | 0 | const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint); |
307 | 0 | SdrHintKind eKind = pSdrHint->GetKind(); |
308 | 0 | if (eKind == SdrHintKind::RefDeviceChange) |
309 | 0 | { |
310 | 0 | mpTextEditOutliner->SetRefDevice(GetModel().GetRefDevice()); |
311 | 0 | } |
312 | 0 | if (eKind == SdrHintKind::DefaultTabChange) |
313 | 0 | { |
314 | 0 | mpTextEditOutliner->SetDefTab(GetModel().GetDefaultTabulator()); |
315 | 0 | } |
316 | 0 | } |
317 | | |
318 | | void SdrObjEditView::ModelHasChanged() |
319 | 0 | { |
320 | 0 | SdrGlueEditView::ModelHasChanged(); |
321 | 0 | rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get(); |
322 | 0 | if (pTextObj && !pTextObj->IsInserted()) |
323 | 0 | SdrEndTextEdit(); // object deleted |
324 | | // TextEditObj changed? |
325 | 0 | if (!IsTextEdit()) |
326 | 0 | return; |
327 | | |
328 | 0 | if (pTextObj != nullptr) |
329 | 0 | { |
330 | 0 | size_t nOutlViewCnt = mpTextEditOutliner->GetViewCount(); |
331 | 0 | bool bAreaChg = false; |
332 | 0 | bool bAnchorChg = false; |
333 | 0 | bool bColorChg = false; |
334 | 0 | bool bContourFrame = pTextObj->IsContourTextFrame(); |
335 | 0 | EEAnchorMode eNewAnchor(EEAnchorMode::VCenterHCenter); |
336 | 0 | tools::Rectangle aOldArea(m_aMinTextEditArea); |
337 | 0 | aOldArea.Union(m_aTextEditArea); |
338 | 0 | Color aNewColor; |
339 | 0 | { // check area |
340 | 0 | Size aPaperMin1; |
341 | 0 | Size aPaperMax1; |
342 | 0 | tools::Rectangle aEditArea1; |
343 | 0 | tools::Rectangle aMinArea1; |
344 | 0 | pTextObj->TakeTextEditArea(&aPaperMin1, &aPaperMax1, &aEditArea1, &aMinArea1); |
345 | 0 | Point aPvOfs(pTextObj->GetTextEditOffset()); |
346 | | |
347 | | // add possible GridOffset to up-to-now view-independent EditAreas |
348 | 0 | basegfx::B2DVector aGridOffset(0.0, 0.0); |
349 | 0 | if (getPossibleGridOffsetForSdrObject(aGridOffset, pTextObj.get(), GetSdrPageView())) |
350 | 0 | { |
351 | 0 | const Point aOffset(basegfx::fround<tools::Long>(aGridOffset.getX()), |
352 | 0 | basegfx::fround<tools::Long>(aGridOffset.getY())); |
353 | |
|
354 | 0 | aEditArea1 += aOffset; |
355 | 0 | aMinArea1 += aOffset; |
356 | 0 | } |
357 | |
|
358 | 0 | aEditArea1.Move(aPvOfs.X(), aPvOfs.Y()); |
359 | 0 | aMinArea1.Move(aPvOfs.X(), aPvOfs.Y()); |
360 | 0 | tools::Rectangle aNewArea(aMinArea1); |
361 | 0 | aNewArea.Union(aEditArea1); |
362 | |
|
363 | 0 | if (aNewArea != aOldArea || aEditArea1 != m_aTextEditArea |
364 | 0 | || aMinArea1 != m_aMinTextEditArea |
365 | 0 | || mpTextEditOutliner->GetMinAutoPaperSize() != aPaperMin1 |
366 | 0 | || mpTextEditOutliner->GetMaxAutoPaperSize() != aPaperMax1) |
367 | 0 | { |
368 | 0 | m_aTextEditArea = aEditArea1; |
369 | 0 | m_aMinTextEditArea = aMinArea1; |
370 | |
|
371 | 0 | const bool bPrevUpdateLayout = mpTextEditOutliner->SetUpdateLayout(false); |
372 | 0 | mpTextEditOutliner->SetMinAutoPaperSize(aPaperMin1); |
373 | 0 | mpTextEditOutliner->SetMaxAutoPaperSize(aPaperMax1); |
374 | 0 | mpTextEditOutliner->SetPaperSize(Size(0, 0)); // re-format Outliner |
375 | |
|
376 | 0 | if (!bContourFrame) |
377 | 0 | { |
378 | 0 | mpTextEditOutliner->ClearPolygon(); |
379 | 0 | EEControlBits nStat = mpTextEditOutliner->GetControlWord(); |
380 | 0 | nStat |= EEControlBits::AUTOPAGESIZE; |
381 | 0 | mpTextEditOutliner->SetControlWord(nStat); |
382 | 0 | } |
383 | 0 | else |
384 | 0 | { |
385 | 0 | EEControlBits nStat = mpTextEditOutliner->GetControlWord(); |
386 | 0 | nStat &= ~EEControlBits::AUTOPAGESIZE; |
387 | 0 | mpTextEditOutliner->SetControlWord(nStat); |
388 | 0 | tools::Rectangle aAnchorRect; |
389 | 0 | pTextObj->TakeTextAnchorRect(aAnchorRect); |
390 | 0 | pTextObj->ImpSetContourPolygon(*mpTextEditOutliner, aAnchorRect, true); |
391 | 0 | } |
392 | 0 | for (size_t nOV = 0; nOV < nOutlViewCnt; nOV++) |
393 | 0 | { |
394 | 0 | OutlinerView* pOLV = mpTextEditOutliner->GetView(nOV); |
395 | 0 | EVControlBits nStat0 = pOLV->GetControlWord(); |
396 | 0 | EVControlBits nStat = nStat0; |
397 | | // AutoViewSize only if not ContourFrame. |
398 | 0 | if (!bContourFrame) |
399 | 0 | nStat |= EVControlBits::AUTOSIZE; |
400 | 0 | else |
401 | 0 | nStat &= ~EVControlBits::AUTOSIZE; |
402 | 0 | if (nStat != nStat0) |
403 | 0 | pOLV->SetControlWord(nStat); |
404 | 0 | } |
405 | |
|
406 | 0 | mpTextEditOutliner->SetUpdateLayout(bPrevUpdateLayout); |
407 | 0 | bAreaChg = true; |
408 | 0 | } |
409 | 0 | } |
410 | 0 | if (mpTextEditOutlinerView != nullptr) |
411 | 0 | { // check fill and anchor |
412 | 0 | EEAnchorMode eOldAnchor = mpTextEditOutlinerView->GetAnchorMode(); |
413 | 0 | eNewAnchor = pTextObj->GetOutlinerViewAnchorMode(); |
414 | 0 | bAnchorChg = eOldAnchor != eNewAnchor; |
415 | 0 | Color aOldColor(mpTextEditOutlinerView->GetBackgroundColor()); |
416 | 0 | aNewColor = GetTextEditBackgroundColor(*this); |
417 | 0 | bColorChg = aOldColor != aNewColor; |
418 | 0 | } |
419 | | // refresh always when it's a contour frame. That |
420 | | // refresh is necessary since it triggers the repaint |
421 | | // which makes the Handles visible. Changes at TakeTextRect() |
422 | | // seem to have resulted in a case where no refresh is executed. |
423 | | // Before that, a refresh must have been always executed |
424 | | // (else this error would have happened earlier), thus I |
425 | | // even think here a refresh should be done always. |
426 | | // Since follow-up problems cannot even be guessed I only |
427 | | // add this one more case to the if below. |
428 | | // BTW: It's VERY bad style that here, inside ModelHasChanged() |
429 | | // the outliner is again massively changed for the text object |
430 | | // in text edit mode. Normally, all necessary data should be |
431 | | // set at SdrBeginTextEdit(). Some changes and value assigns in |
432 | | // SdrBeginTextEdit() are completely useless since they are set here |
433 | | // again on ModelHasChanged(). |
434 | 0 | if (bContourFrame || bAreaChg || bAnchorChg || bColorChg) |
435 | 0 | { |
436 | 0 | for (size_t nOV = 0; nOV < nOutlViewCnt; nOV++) |
437 | 0 | { |
438 | 0 | OutlinerView* pOLV = mpTextEditOutliner->GetView(nOV); |
439 | 0 | vcl::Window* pWin = pOLV->GetWindow(); |
440 | 0 | { // invalidate old OutlinerView area |
441 | 0 | tools::Rectangle aTmpRect(aOldArea); |
442 | 0 | sal_uInt16 nPixSiz = pOLV->GetInvalidateMore() + 1; |
443 | 0 | Size aMore(pWin->PixelToLogic(Size(nPixSiz, nPixSiz))); |
444 | 0 | aTmpRect.AdjustLeft(-(aMore.Width())); |
445 | 0 | aTmpRect.AdjustRight(aMore.Width()); |
446 | 0 | aTmpRect.AdjustTop(-(aMore.Height())); |
447 | 0 | aTmpRect.AdjustBottom(aMore.Height()); |
448 | 0 | InvalidateOneWin(*pWin->GetOutDev(), aTmpRect); |
449 | 0 | } |
450 | 0 | if (bAnchorChg) |
451 | 0 | pOLV->SetAnchorMode(eNewAnchor); |
452 | 0 | if (bColorChg) |
453 | 0 | pOLV->SetBackgroundColor(aNewColor); |
454 | |
|
455 | 0 | bool bWasCoursorVisible = pOLV->IsCursorVisible(); |
456 | 0 | vcl::Cursor* pOldCursor = pWin->GetCursor(); |
457 | 0 | pOLV->SetOutputArea( |
458 | 0 | m_aTextEditArea); // because otherwise, we're not re-anchoring correctly |
459 | 0 | ImpInvalidateOutlinerView(*pOLV); |
460 | | // Undo SetOutputArea setting and showing the cursor |
461 | 0 | if (!bWasCoursorVisible) |
462 | 0 | pOLV->HideCursor(); |
463 | 0 | pWin->SetCursor(pOldCursor); |
464 | 0 | } |
465 | 0 | mpTextEditOutlinerView->ShowCursor(); |
466 | 0 | } |
467 | 0 | } |
468 | 0 | ImpMakeTextCursorAreaVisible(); |
469 | 0 | } |
470 | | |
471 | | namespace |
472 | | { |
473 | | class TextEditFrameOverlayObject; |
474 | | class TextEditHighContrastOverlaySelection; |
475 | | |
476 | | /** |
477 | | Helper class to visualize the content of an active EditView as an |
478 | | OverlayObject. These objects work with Primitives and are handled |
479 | | from the OverlayManager(s) in place as needed. |
480 | | |
481 | | It allows complete visualization of the content of the active |
482 | | EditView without the need of Invalidates triggered by the EditView |
483 | | and thus avoiding potentially expensive repaints by using the |
484 | | automatically buffered Overlay mechanism. |
485 | | |
486 | | It buffers as much as possible locally and *only* triggers a real |
487 | | change (see call to objectChange()) when really needed. |
488 | | */ |
489 | | class TextEditOverlayObject : public sdr::overlay::OverlayObject |
490 | | { |
491 | | protected: |
492 | | /// local access to associated sdr::overlay::OverlaySelection |
493 | | std::unique_ptr<sdr::overlay::OverlaySelection> mxOverlayTransparentSelection; |
494 | | std::unique_ptr<TextEditHighContrastOverlaySelection> mxOverlayHighContrastSelection; |
495 | | std::unique_ptr<TextEditFrameOverlayObject> mxOverlayFrame; |
496 | | |
497 | | /// local definition depends on active OutlinerView |
498 | | OutlinerView& mrOutlinerView; |
499 | | |
500 | | /// geometry definitions with buffering |
501 | | basegfx::B2DRange maLastRange; |
502 | | basegfx::B2DRange maRange; |
503 | | |
504 | | /// text content definitions with buffering |
505 | | drawinglayer::primitive2d::Primitive2DContainer maTextPrimitives; |
506 | | drawinglayer::primitive2d::Primitive2DContainer maLastTextPrimitives; |
507 | | |
508 | | // geometry creation for OverlayObject, can use local *Last* values |
509 | | virtual drawinglayer::primitive2d::Primitive2DContainer |
510 | | createOverlayObjectPrimitive2DSequence() override; |
511 | | |
512 | | public: |
513 | | TextEditOverlayObject(const Color& rColor, OutlinerView& rOutlinerView); |
514 | | virtual ~TextEditOverlayObject() override; |
515 | | |
516 | | sdr::overlay::OverlayObject* getOverlaySelection(); |
517 | | sdr::overlay::OverlayObject* getOverlayFrame(); |
518 | | |
519 | 0 | const OutlinerView& getOutlinerView() const { return mrOutlinerView; } |
520 | | |
521 | | /// override to check conditions for last createOverlayObjectPrimitive2DSequence |
522 | | virtual drawinglayer::primitive2d::Primitive2DContainer |
523 | | getOverlayObjectPrimitive2DSequence() const override; |
524 | | |
525 | | // data write access. In this OverlayObject we only have the |
526 | | // callback that triggers detecting if something *has* changed |
527 | | void checkDataChange(const basegfx::B2DRange& rMinTextEditArea); |
528 | | void checkSelectionChange(); |
529 | | |
530 | 0 | const basegfx::B2DRange& getRange() const { return maRange; } |
531 | | const drawinglayer::primitive2d::Primitive2DContainer& getTextPrimitives() const |
532 | 0 | { |
533 | 0 | return maTextPrimitives; |
534 | 0 | } |
535 | | }; |
536 | | |
537 | | class TextEditFrameOverlayObject : public sdr::overlay::OverlayObject |
538 | | { |
539 | | private: |
540 | | const TextEditOverlayObject& mrTextEditOverlayObject; |
541 | | |
542 | | // geometry creation for OverlayObject, can use local *Last* values |
543 | | virtual drawinglayer::primitive2d::Primitive2DContainer |
544 | | createOverlayObjectPrimitive2DSequence() override; |
545 | | |
546 | | public: |
547 | | TextEditFrameOverlayObject(const TextEditOverlayObject& rTextEditOverlayObject); |
548 | | using sdr::overlay::OverlayObject::objectChange; |
549 | | virtual ~TextEditFrameOverlayObject() override; |
550 | | }; |
551 | | |
552 | | class TextEditHighContrastOverlaySelection : public sdr::overlay::OverlayObject |
553 | | { |
554 | | private: |
555 | | const TextEditOverlayObject& mrTextEditOverlayObject; |
556 | | std::vector<basegfx::B2DRange> maRanges; |
557 | | |
558 | | // geometry creation for OverlayObject, can use local *Last* values |
559 | | virtual drawinglayer::primitive2d::Primitive2DContainer |
560 | | createOverlayObjectPrimitive2DSequence() override; |
561 | | |
562 | | public: |
563 | | TextEditHighContrastOverlaySelection(const TextEditOverlayObject& rTextEditOverlayObject); |
564 | | void setRanges(std::vector<basegfx::B2DRange>&& rNew); |
565 | | virtual ~TextEditHighContrastOverlaySelection() override; |
566 | | }; |
567 | | |
568 | | TextEditHighContrastOverlaySelection::TextEditHighContrastOverlaySelection( |
569 | | const TextEditOverlayObject& rTextEditOverlayObject) |
570 | 0 | : OverlayObject(rTextEditOverlayObject.getBaseColor()) |
571 | 0 | , mrTextEditOverlayObject(rTextEditOverlayObject) |
572 | 0 | { |
573 | 0 | allowAntiAliase(rTextEditOverlayObject.allowsAntiAliase()); |
574 | | // use selection colors in HighContrast mode |
575 | 0 | mbHighContrastSelection = true; |
576 | 0 | } |
577 | | |
578 | | void TextEditHighContrastOverlaySelection::setRanges(std::vector<basegfx::B2DRange>&& rNew) |
579 | 0 | { |
580 | 0 | if (rNew != maRanges) |
581 | 0 | { |
582 | 0 | maRanges = std::move(rNew); |
583 | 0 | objectChange(); |
584 | 0 | } |
585 | 0 | } |
586 | | |
587 | | drawinglayer::primitive2d::Primitive2DContainer |
588 | | TextEditHighContrastOverlaySelection::createOverlayObjectPrimitive2DSequence() |
589 | 0 | { |
590 | 0 | drawinglayer::primitive2d::Primitive2DContainer aRetval; |
591 | |
|
592 | 0 | size_t nCount = maRanges.size(); |
593 | |
|
594 | 0 | if (nCount) |
595 | 0 | { |
596 | 0 | basegfx::B2DPolyPolygon aClipPolyPolygon; |
597 | |
|
598 | 0 | basegfx::BColor aRGBColor(getBaseColor().getBColor()); |
599 | |
|
600 | 0 | for (size_t a = 0; a < nCount; ++a) |
601 | 0 | aClipPolyPolygon.append(basegfx::utils::createPolygonFromRect(maRanges[a])); |
602 | | |
603 | | // This is used in high contrast mode, we will render the selection |
604 | | // with the bg forced to the selection Highlight color and the fg color |
605 | | // forced to the HighlightText color |
606 | 0 | aRetval.append(new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( |
607 | 0 | basegfx::B2DPolyPolygon( |
608 | 0 | basegfx::utils::createPolygonFromRect(aClipPolyPolygon.getB2DRange())), |
609 | 0 | aRGBColor)); |
610 | 0 | aRetval.append(mrTextEditOverlayObject.getTextPrimitives()); |
611 | 0 | aRetval.append(new drawinglayer::primitive2d::MaskPrimitive2D(std::move(aClipPolyPolygon), |
612 | 0 | std::move(aRetval))); |
613 | 0 | } |
614 | |
|
615 | 0 | return aRetval; |
616 | 0 | } |
617 | | |
618 | | TextEditHighContrastOverlaySelection::~TextEditHighContrastOverlaySelection() |
619 | 0 | { |
620 | 0 | if (getOverlayManager()) |
621 | 0 | { |
622 | 0 | getOverlayManager()->remove(*this); |
623 | 0 | } |
624 | 0 | } |
625 | | |
626 | | sdr::overlay::OverlayObject* TextEditOverlayObject::getOverlaySelection() |
627 | 0 | { |
628 | 0 | if (mxOverlayTransparentSelection) |
629 | 0 | return mxOverlayTransparentSelection.get(); |
630 | 0 | return mxOverlayHighContrastSelection.get(); |
631 | 0 | } |
632 | | |
633 | | sdr::overlay::OverlayObject* TextEditOverlayObject::getOverlayFrame() |
634 | 0 | { |
635 | 0 | if (!mxOverlayFrame) |
636 | 0 | mxOverlayFrame.reset(new TextEditFrameOverlayObject(*this)); |
637 | 0 | return mxOverlayFrame.get(); |
638 | 0 | } |
639 | | |
640 | | drawinglayer::primitive2d::Primitive2DContainer |
641 | | TextEditOverlayObject::createOverlayObjectPrimitive2DSequence() |
642 | 0 | { |
643 | 0 | drawinglayer::primitive2d::Primitive2DContainer aRetval; |
644 | | |
645 | | // add buffered TextPrimitives |
646 | 0 | aRetval.append(maTextPrimitives); |
647 | |
|
648 | 0 | return aRetval; |
649 | 0 | } |
650 | | |
651 | | drawinglayer::primitive2d::Primitive2DContainer |
652 | | TextEditFrameOverlayObject::createOverlayObjectPrimitive2DSequence() |
653 | 0 | { |
654 | 0 | drawinglayer::primitive2d::Primitive2DContainer aRetval; |
655 | | |
656 | | /// outer frame visualization |
657 | 0 | const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01); |
658 | 0 | const sal_uInt16 nPixSiz(mrTextEditOverlayObject.getOutlinerView().GetInvalidateMore() - 1); |
659 | |
|
660 | 0 | aRetval.push_back(new drawinglayer::primitive2d::OverlayRectanglePrimitive( |
661 | 0 | mrTextEditOverlayObject.getRange(), getBaseColor().getBColor(), fTransparence, |
662 | 0 | std::max(6, nPixSiz - 2), // grow |
663 | 0 | 0.0, // shrink |
664 | 0 | 0.0)); |
665 | |
|
666 | 0 | return aRetval; |
667 | 0 | } |
668 | | |
669 | | TextEditOverlayObject::TextEditOverlayObject(const Color& rColor, OutlinerView& rOutlinerView) |
670 | 0 | : OverlayObject(rColor) |
671 | 0 | , mrOutlinerView(rOutlinerView) |
672 | 0 | { |
673 | | // no AA for TextEdit overlay |
674 | 0 | allowAntiAliase(false); |
675 | | |
676 | | // create local OverlaySelection - this is an integral part of EditText |
677 | | // visualization |
678 | 0 | if (Application::GetSettings().GetStyleSettings().GetHighContrastMode()) |
679 | 0 | { |
680 | 0 | mxOverlayHighContrastSelection.reset(new TextEditHighContrastOverlaySelection(*this)); |
681 | 0 | } |
682 | 0 | else |
683 | 0 | { |
684 | 0 | std::vector<basegfx::B2DRange> aEmptySelection{}; |
685 | 0 | mxOverlayTransparentSelection.reset(new sdr::overlay::OverlaySelection( |
686 | 0 | sdr::overlay::OverlayType::Transparent, rColor, std::move(aEmptySelection), true)); |
687 | 0 | } |
688 | 0 | } |
689 | | |
690 | | TextEditOverlayObject::~TextEditOverlayObject() |
691 | 0 | { |
692 | 0 | mxOverlayTransparentSelection.reset(); |
693 | 0 | mxOverlayHighContrastSelection.reset(); |
694 | |
|
695 | 0 | if (getOverlayManager()) |
696 | 0 | { |
697 | 0 | getOverlayManager()->remove(*this); |
698 | 0 | } |
699 | 0 | } |
700 | | |
701 | | TextEditFrameOverlayObject::TextEditFrameOverlayObject( |
702 | | const TextEditOverlayObject& rTextEditOverlayObject) |
703 | 0 | : OverlayObject(rTextEditOverlayObject.getBaseColor()) |
704 | 0 | , mrTextEditOverlayObject(rTextEditOverlayObject) |
705 | 0 | { |
706 | 0 | allowAntiAliase(rTextEditOverlayObject.allowsAntiAliase()); |
707 | | // use selection colors in HighContrast mode |
708 | 0 | mbHighContrastSelection = true; |
709 | 0 | } |
710 | | |
711 | | TextEditFrameOverlayObject::~TextEditFrameOverlayObject() |
712 | 0 | { |
713 | 0 | if (getOverlayManager()) |
714 | 0 | { |
715 | 0 | getOverlayManager()->remove(*this); |
716 | 0 | } |
717 | 0 | } |
718 | | |
719 | | drawinglayer::primitive2d::Primitive2DContainer |
720 | | TextEditOverlayObject::getOverlayObjectPrimitive2DSequence() const |
721 | 0 | { |
722 | 0 | if (!getPrimitive2DSequence().empty()) |
723 | 0 | { |
724 | 0 | if (!maRange.equal(maLastRange) || maLastTextPrimitives != maTextPrimitives) |
725 | 0 | { |
726 | | // conditions of last local decomposition have changed, delete to force new evaluation |
727 | 0 | const_cast<TextEditOverlayObject*>(this)->resetPrimitive2DSequence(); |
728 | 0 | } |
729 | 0 | } |
730 | |
|
731 | 0 | if (getPrimitive2DSequence().empty()) |
732 | 0 | { |
733 | | // remember new buffered values |
734 | 0 | const_cast<TextEditOverlayObject*>(this)->maLastRange = maRange; |
735 | 0 | const_cast<TextEditOverlayObject*>(this)->maLastTextPrimitives = maTextPrimitives; |
736 | 0 | } |
737 | | |
738 | | // call base implementation |
739 | 0 | return OverlayObject::getOverlayObjectPrimitive2DSequence(); |
740 | 0 | } |
741 | | |
742 | | void TextEditOverlayObject::checkDataChange(const basegfx::B2DRange& rMinTextEditArea) |
743 | 0 | { |
744 | 0 | bool bObjectChange(false); |
745 | | |
746 | | // check current range |
747 | 0 | const tools::Rectangle aOutArea(mrOutlinerView.GetOutputArea()); |
748 | 0 | basegfx::B2DRange aNewRange = vcl::unotools::b2DRectangleFromRectangle(aOutArea); |
749 | 0 | aNewRange.expand(rMinTextEditArea); |
750 | |
|
751 | 0 | if (aNewRange != maRange) |
752 | 0 | { |
753 | 0 | maRange = aNewRange; |
754 | 0 | bObjectChange = true; |
755 | 0 | } |
756 | | |
757 | | // check if text primitives did change |
758 | 0 | SdrOutliner* pSdrOutliner = dynamic_cast<SdrOutliner*>(&getOutlinerView().GetOutliner()); |
759 | |
|
760 | 0 | if (pSdrOutliner) |
761 | 0 | { |
762 | | // get TextPrimitives directly from active Outliner |
763 | 0 | basegfx::B2DHomMatrix aNewTransformA; |
764 | 0 | basegfx::B2DHomMatrix aNewTransformB; |
765 | 0 | basegfx::B2DRange aClipRange; |
766 | 0 | drawinglayer::primitive2d::Primitive2DContainer aNewTextPrimitives; |
767 | | |
768 | | // active Outliner is always in unified oriented coordinate system (currently) |
769 | | // so just translate to TopLeft of visible Range. Keep in mind that top-left |
770 | | // depends on vertical text and top-to-bottom text attributes |
771 | 0 | const tools::Rectangle aVisArea(mrOutlinerView.GetVisArea()); |
772 | 0 | const bool bVerticalWriting(pSdrOutliner->IsVertical()); |
773 | 0 | const bool bTopToBottom(pSdrOutliner->IsTopToBottom()); |
774 | 0 | const double fStartInX(bVerticalWriting && bTopToBottom |
775 | 0 | ? aOutArea.Right() - aVisArea.Left() |
776 | 0 | : aOutArea.Left() - aVisArea.Left()); |
777 | 0 | const double fStartInY(bVerticalWriting && !bTopToBottom |
778 | 0 | ? aOutArea.Bottom() - aVisArea.Top() |
779 | 0 | : aOutArea.Top() - aVisArea.Top()); |
780 | |
|
781 | 0 | aNewTransformB.translate(fStartInX, fStartInY); |
782 | | |
783 | | // get the current TextPrimitives. This is the most expensive part |
784 | | // of this mechanism, it *may* be possible to buffer layouted |
785 | | // primitives per ParaPortion with/in/dependent on the EditEngine |
786 | | // content if needed. For now, get and compare |
787 | 0 | TextHierarchyBreakupBlockText aBreakup(*pSdrOutliner, aNewTransformA, aNewTransformB, |
788 | 0 | aClipRange); |
789 | 0 | pSdrOutliner->StripPortions(aBreakup); |
790 | 0 | aNewTextPrimitives.append(aBreakup.getTextPortionPrimitives()); |
791 | |
|
792 | 0 | if (aNewTextPrimitives != maTextPrimitives) |
793 | 0 | { |
794 | 0 | maTextPrimitives = std::move(aNewTextPrimitives); |
795 | 0 | bObjectChange = true; |
796 | 0 | } |
797 | 0 | } |
798 | |
|
799 | 0 | if (bObjectChange) |
800 | 0 | { |
801 | | // if there really *was* a change signal the OverlayManager to |
802 | | // refresh this object's visualization |
803 | 0 | objectChange(); |
804 | |
|
805 | 0 | if (mxOverlayFrame) |
806 | 0 | mxOverlayFrame->objectChange(); |
807 | | |
808 | | // on data change, always do a SelectionChange, too |
809 | | // since the selection is an integral part of text visualization |
810 | 0 | checkSelectionChange(); |
811 | 0 | } |
812 | 0 | } |
813 | | |
814 | | void TextEditOverlayObject::checkSelectionChange() |
815 | 0 | { |
816 | 0 | if (!(getOverlaySelection() && getOverlayManager())) |
817 | 0 | return; |
818 | | |
819 | 0 | std::vector<tools::Rectangle> aLogicRects; |
820 | 0 | std::vector<basegfx::B2DRange> aLogicRanges; |
821 | 0 | const Size aLogicPixel(getOverlayManager()->getOutputDevice().PixelToLogic(Size(1, 1))); |
822 | | |
823 | | // get logic selection |
824 | 0 | getOutlinerView().GetSelectionRectangles(aLogicRects); |
825 | |
|
826 | 0 | aLogicRanges.reserve(aLogicRects.size()); |
827 | 0 | for (const auto& aRect : aLogicRects) |
828 | 0 | { |
829 | | // convert from logic Rectangles to logic Ranges, do not forget to add |
830 | | // one Unit (in this case logical units for one pixel, pre-calculated) |
831 | 0 | aLogicRanges.emplace_back( |
832 | 0 | aRect.Left() - aLogicPixel.Width(), aRect.Top() - aLogicPixel.Height(), |
833 | 0 | aRect.Right() + aLogicPixel.Width(), aRect.Bottom() + aLogicPixel.Height()); |
834 | 0 | } |
835 | |
|
836 | 0 | if (mxOverlayTransparentSelection) |
837 | 0 | mxOverlayTransparentSelection->setRanges(std::move(aLogicRanges)); |
838 | 0 | else |
839 | 0 | mxOverlayHighContrastSelection->setRanges(std::move(aLogicRanges)); |
840 | 0 | } |
841 | | } // end of anonymous namespace |
842 | | |
843 | | // TextEdit |
844 | | |
845 | | // callback from the active EditView, forward to evtl. existing instances of the |
846 | | // TextEditOverlayObject(s). This will additionally update the selection which |
847 | | // is an integral part of the text visualization |
848 | | void SdrObjEditView::EditViewInvalidate(const tools::Rectangle&) |
849 | 0 | { |
850 | 0 | if (!IsTextEdit()) |
851 | 0 | return; |
852 | | |
853 | | // MinTextRange may have changed. Forward it, too |
854 | 0 | const basegfx::B2DRange aMinTextRange |
855 | 0 | = vcl::unotools::b2DRectangleFromRectangle(m_aMinTextEditArea); |
856 | |
|
857 | 0 | for (sal_uInt32 a(0); a < maTEOverlayGroup.count(); a++) |
858 | 0 | { |
859 | 0 | TextEditOverlayObject* pCandidate |
860 | 0 | = dynamic_cast<TextEditOverlayObject*>(&maTEOverlayGroup.getOverlayObject(a)); |
861 | |
|
862 | 0 | if (pCandidate) |
863 | 0 | { |
864 | 0 | pCandidate->checkDataChange(aMinTextRange); |
865 | 0 | } |
866 | 0 | } |
867 | 0 | } |
868 | | |
869 | | // callback from the active EditView, forward to evtl. existing instances of the |
870 | | // TextEditOverlayObject(s). This cvall *only* updates the selection visualization |
871 | | // which is e.g. used when only the selection is changed, but not the text |
872 | | void SdrObjEditView::EditViewSelectionChange() |
873 | 0 | { |
874 | 0 | if (!IsTextEdit()) |
875 | 0 | return; |
876 | | |
877 | 0 | for (sal_uInt32 a(0); a < maTEOverlayGroup.count(); a++) |
878 | 0 | { |
879 | 0 | TextEditOverlayObject* pCandidate |
880 | 0 | = dynamic_cast<TextEditOverlayObject*>(&maTEOverlayGroup.getOverlayObject(a)); |
881 | |
|
882 | 0 | if (pCandidate) |
883 | 0 | { |
884 | 0 | pCandidate->checkSelectionChange(); |
885 | 0 | } |
886 | 0 | } |
887 | 0 | } |
888 | | |
889 | 0 | OutputDevice& SdrObjEditView::EditViewOutputDevice() const { return *mpTextEditWin->GetOutDev(); } |
890 | | |
891 | | Point SdrObjEditView::EditViewPointerPosPixel() const |
892 | 0 | { |
893 | 0 | return mpTextEditWin->GetPointerPosPixel(); |
894 | 0 | } |
895 | | |
896 | | css::uno::Reference<css::datatransfer::clipboard::XClipboard> SdrObjEditView::GetClipboard() const |
897 | 0 | { |
898 | 0 | if (!mpTextEditWin) |
899 | 0 | return nullptr; |
900 | 0 | return mpTextEditWin->GetClipboard(); |
901 | 0 | } |
902 | | |
903 | | css::uno::Reference<css::datatransfer::dnd::XDropTarget> SdrObjEditView::GetDropTarget() |
904 | 0 | { |
905 | 0 | if (!mpTextEditWin) |
906 | 0 | return nullptr; |
907 | 0 | return mpTextEditWin->GetDropTarget(); |
908 | 0 | } |
909 | | |
910 | | void SdrObjEditView::EditViewInputContext(const InputContext& rInputContext) |
911 | 0 | { |
912 | 0 | if (!mpTextEditWin) |
913 | 0 | return; |
914 | 0 | mpTextEditWin->SetInputContext(rInputContext); |
915 | 0 | } |
916 | | |
917 | | void SdrObjEditView::EditViewCursorRect(const tools::Rectangle& rRect, int nExtTextInputWidth) |
918 | 0 | { |
919 | 0 | if (!mpTextEditWin) |
920 | 0 | return; |
921 | 0 | mpTextEditWin->SetCursorRect(&rRect, nExtTextInputWidth); |
922 | 0 | } |
923 | | |
924 | | void SdrObjEditView::TextEditDrawing(SdrPaintWindow& rPaintWindow) |
925 | 0 | { |
926 | 0 | if (!comphelper::LibreOfficeKit::isActive()) |
927 | 0 | { |
928 | | // adapt all TextEditOverlayObject(s), so call EditViewInvalidate() |
929 | | // to update accordingly (will update selection, too). Suppress new |
930 | | // stuff when LibreOfficeKit is active |
931 | 0 | EditViewInvalidate(tools::Rectangle()); |
932 | 0 | } |
933 | 0 | else |
934 | 0 | { |
935 | | // draw old text edit stuff |
936 | 0 | if (IsTextEdit()) |
937 | 0 | { |
938 | 0 | const SdrOutliner* pActiveOutliner = GetTextEditOutliner(); |
939 | |
|
940 | 0 | if (pActiveOutliner) |
941 | 0 | { |
942 | 0 | const sal_uInt32 nViewCount(pActiveOutliner->GetViewCount()); |
943 | |
|
944 | 0 | if (nViewCount) |
945 | 0 | { |
946 | 0 | const vcl::Region& rRedrawRegion = rPaintWindow.GetRedrawRegion(); |
947 | 0 | const tools::Rectangle aCheckRect(rRedrawRegion.GetBoundRect()); |
948 | |
|
949 | 0 | for (sal_uInt32 i(0); i < nViewCount; i++) |
950 | 0 | { |
951 | 0 | OutlinerView* pOLV = pActiveOutliner->GetView(i); |
952 | | |
953 | | // If rPaintWindow knows that the output device is a render |
954 | | // context and is aware of the underlying vcl::Window, |
955 | | // compare against that; that's how double-buffering can |
956 | | // still find the matching OutlinerView. |
957 | 0 | OutputDevice* pOutputDevice = rPaintWindow.GetWindow() |
958 | 0 | ? rPaintWindow.GetWindow()->GetOutDev() |
959 | 0 | : &rPaintWindow.GetOutputDevice(); |
960 | 0 | if (pOLV->GetWindow()->GetOutDev() == pOutputDevice |
961 | 0 | || comphelper::LibreOfficeKit::isActive()) |
962 | 0 | { |
963 | 0 | ImpPaintOutlinerView(*pOLV, aCheckRect, |
964 | 0 | rPaintWindow.GetTargetOutputDevice()); |
965 | 0 | return; |
966 | 0 | } |
967 | 0 | } |
968 | 0 | } |
969 | 0 | } |
970 | 0 | } |
971 | 0 | } |
972 | 0 | } |
973 | | |
974 | | void SdrObjEditView::ImpPaintOutlinerView(OutlinerView& rOutlView, const tools::Rectangle& rRect, |
975 | | OutputDevice& rTargetDevice) const |
976 | 0 | { |
977 | 0 | const SdrTextObj* pText = GetTextEditObject(); |
978 | 0 | bool bTextFrame(pText && pText->IsTextFrame()); |
979 | 0 | bool bFitToSize(mpTextEditOutliner->GetControlWord() & EEControlBits::STRETCHING); |
980 | 0 | bool bModified(mpTextEditOutliner->IsModified()); |
981 | 0 | tools::Rectangle aBlankRect(rOutlView.GetOutputArea()); |
982 | 0 | aBlankRect.Union(m_aMinTextEditArea); |
983 | 0 | tools::Rectangle aPixRect(rTargetDevice.LogicToPixel(aBlankRect)); |
984 | | |
985 | | // in the tiled rendering case, the setup is incomplete, and we very |
986 | | // easily get an empty rRect on input - that will cause that everything is |
987 | | // clipped; happens in case of editing text inside a shape in Calc. |
988 | | // FIXME would be better to complete the setup so that we don't get an |
989 | | // empty rRect here |
990 | 0 | if (!comphelper::LibreOfficeKit::isActive() || !rRect.IsEmpty()) |
991 | 0 | aBlankRect.Intersection(rRect); |
992 | |
|
993 | 0 | rOutlView.GetOutliner().SetUpdateLayout(true); // Bugfix #22596# |
994 | 0 | rOutlView.DrawText_ToEditView(aBlankRect, &rTargetDevice); |
995 | |
|
996 | 0 | if (!bModified) |
997 | 0 | { |
998 | 0 | mpTextEditOutliner->ClearModifyFlag(); |
999 | 0 | } |
1000 | |
|
1001 | 0 | if (bTextFrame && !bFitToSize) |
1002 | 0 | { |
1003 | | // completely reworked to use primitives; this ensures same look and functionality |
1004 | 0 | const drawinglayer::geometry::ViewInformation2D aViewInformation2D; |
1005 | 0 | std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> xProcessor( |
1006 | 0 | drawinglayer::processor2d::createProcessor2DFromOutputDevice(rTargetDevice, |
1007 | 0 | aViewInformation2D)); |
1008 | |
|
1009 | 0 | const bool bMapModeEnabled(rTargetDevice.IsMapModeEnabled()); |
1010 | 0 | const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(aPixRect); |
1011 | 0 | const Color aHilightColor(SvtOptionsDrawinglayer::getHilightColor()); |
1012 | 0 | const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01); |
1013 | 0 | const sal_uInt16 nPixSiz(rOutlView.GetInvalidateMore() - 1); |
1014 | 0 | const drawinglayer::primitive2d::Primitive2DReference xReference( |
1015 | 0 | new drawinglayer::primitive2d::OverlayRectanglePrimitive( |
1016 | 0 | aRange, aHilightColor.getBColor(), fTransparence, std::max(6, nPixSiz - 2), // grow |
1017 | 0 | 0.0, // shrink |
1018 | 0 | 0.0)); |
1019 | 0 | const drawinglayer::primitive2d::Primitive2DContainer aSequence{ xReference }; |
1020 | |
|
1021 | 0 | rTargetDevice.EnableMapMode(false); |
1022 | 0 | xProcessor->process(aSequence); |
1023 | 0 | rTargetDevice.EnableMapMode(bMapModeEnabled); |
1024 | 0 | } |
1025 | |
|
1026 | 0 | rOutlView.ShowCursor(/*bGotoCursor=*/true, /*bActivate=*/true); |
1027 | 0 | } |
1028 | | |
1029 | | void SdrObjEditView::ImpInvalidateOutlinerView(OutlinerView const& rOutlView) const |
1030 | 0 | { |
1031 | 0 | vcl::Window* pWin = rOutlView.GetWindow(); |
1032 | |
|
1033 | 0 | if (!pWin) |
1034 | 0 | return; |
1035 | | |
1036 | 0 | const SdrTextObj* pText = GetTextEditObject(); |
1037 | 0 | bool bTextFrame(pText && pText->IsTextFrame()); |
1038 | 0 | bool bFitToSize(pText && pText->IsFitToSize()); |
1039 | |
|
1040 | 0 | if (!bTextFrame || bFitToSize) |
1041 | 0 | return; |
1042 | | |
1043 | 0 | tools::Rectangle aBlankRect(rOutlView.GetOutputArea()); |
1044 | 0 | aBlankRect.Union(m_aMinTextEditArea); |
1045 | 0 | tools::Rectangle aPixRect(pWin->LogicToPixel(aBlankRect)); |
1046 | 0 | sal_uInt16 nPixSiz(rOutlView.GetInvalidateMore() - 1); |
1047 | |
|
1048 | 0 | aPixRect.AdjustLeft(-1); |
1049 | 0 | aPixRect.AdjustTop(-1); |
1050 | 0 | aPixRect.AdjustRight(1); |
1051 | 0 | aPixRect.AdjustBottom(1); |
1052 | |
|
1053 | 0 | { |
1054 | | // limit xPixRect because of driver problems when pixel coordinates are too far out |
1055 | 0 | Size aMaxXY(pWin->GetOutputSizePixel()); |
1056 | 0 | tools::Long a(2 * nPixSiz); |
1057 | 0 | tools::Long nMaxX(aMaxXY.Width() + a); |
1058 | 0 | tools::Long nMaxY(aMaxXY.Height() + a); |
1059 | |
|
1060 | 0 | if (aPixRect.Left() < -a) |
1061 | 0 | aPixRect.SetLeft(-a); |
1062 | 0 | if (aPixRect.Top() < -a) |
1063 | 0 | aPixRect.SetTop(-a); |
1064 | 0 | if (aPixRect.Right() > nMaxX) |
1065 | 0 | aPixRect.SetRight(nMaxX); |
1066 | 0 | if (aPixRect.Bottom() > nMaxY) |
1067 | 0 | aPixRect.SetBottom(nMaxY); |
1068 | 0 | } |
1069 | |
|
1070 | 0 | tools::Rectangle aOuterPix(aPixRect); |
1071 | 0 | aOuterPix.AdjustLeft(-nPixSiz); |
1072 | 0 | aOuterPix.AdjustTop(-nPixSiz); |
1073 | 0 | aOuterPix.AdjustRight(nPixSiz); |
1074 | 0 | aOuterPix.AdjustBottom(nPixSiz); |
1075 | |
|
1076 | 0 | bool bMapModeEnabled(pWin->IsMapModeEnabled()); |
1077 | 0 | pWin->EnableMapMode(false); |
1078 | 0 | pWin->Invalidate(aOuterPix); |
1079 | 0 | pWin->EnableMapMode(bMapModeEnabled); |
1080 | 0 | } |
1081 | | |
1082 | | OutlinerView* SdrObjEditView::ImpMakeOutlinerView(vcl::Window* pWin, OutlinerView* pGivenView, |
1083 | | SfxViewShell* pViewShell) const |
1084 | 0 | { |
1085 | | // background |
1086 | 0 | Color aBackground(GetTextEditBackgroundColor(*this)); |
1087 | 0 | rtl::Reference<SdrTextObj> pText = mxWeakTextEditObj.get(); |
1088 | 0 | bool bTextFrame = pText != nullptr && pText->IsTextFrame(); |
1089 | 0 | bool bContourFrame = pText != nullptr && pText->IsContourTextFrame(); |
1090 | | // create OutlinerView |
1091 | 0 | OutlinerView* pOutlView = pGivenView; |
1092 | 0 | mpTextEditOutliner->SetUpdateLayout(false); |
1093 | |
|
1094 | 0 | if (pOutlView == nullptr) |
1095 | 0 | { |
1096 | 0 | pOutlView = new OutlinerView(*mpTextEditOutliner, pWin); |
1097 | 0 | } |
1098 | 0 | else |
1099 | 0 | { |
1100 | 0 | pOutlView->SetWindow(pWin); |
1101 | 0 | } |
1102 | |
|
1103 | 0 | if (mbNegativeX) |
1104 | 0 | pOutlView->GetEditView().SetNegativeX(mbNegativeX); |
1105 | | |
1106 | | // disallow scrolling |
1107 | 0 | EVControlBits nStat = pOutlView->GetControlWord(); |
1108 | 0 | nStat &= ~EVControlBits::AUTOSCROLL; |
1109 | | // AutoViewSize only if not ContourFrame. |
1110 | 0 | if (!bContourFrame) |
1111 | 0 | nStat |= EVControlBits::AUTOSIZE; |
1112 | 0 | if (bTextFrame) |
1113 | 0 | { |
1114 | 0 | sal_uInt16 nPixSiz = maHdlList.GetHdlSize() * 2 + 1; |
1115 | 0 | nStat |= EVControlBits::INVONEMORE; |
1116 | 0 | pOutlView->SetInvalidateMore(nPixSiz); |
1117 | 0 | } |
1118 | 0 | pOutlView->SetControlWord(nStat); |
1119 | 0 | pOutlView->SetBackgroundColor(aBackground); |
1120 | | |
1121 | | // In case we're in the process of constructing a new view shell, |
1122 | | // SfxViewShell::Current() may still point to the old one. So if possible, |
1123 | | // depend on the application owning this draw view to provide the view |
1124 | | // shell. |
1125 | 0 | SfxViewShell* pSfxViewShell = pViewShell ? pViewShell : GetSfxViewShell(); |
1126 | 0 | pOutlView->RegisterViewShell(pSfxViewShell ? pSfxViewShell : SfxViewShell::Current()); |
1127 | |
|
1128 | 0 | if (pText != nullptr) |
1129 | 0 | { |
1130 | 0 | pOutlView->SetAnchorMode(pText->GetOutlinerViewAnchorMode()); |
1131 | 0 | mpTextEditOutliner->SetFixedCellHeight( |
1132 | 0 | pText->GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue()); |
1133 | 0 | } |
1134 | | // do update before setting output area so that aTextEditArea can be recalculated |
1135 | 0 | mpTextEditOutliner->SetUpdateLayout(true); |
1136 | 0 | pOutlView->SetOutputArea(m_aTextEditArea); |
1137 | 0 | ImpInvalidateOutlinerView(*pOutlView); |
1138 | 0 | return pOutlView; |
1139 | 0 | } |
1140 | | |
1141 | | IMPL_LINK(SdrObjEditView, ImpOutlinerStatusEventHdl, EditStatus&, rEditStat, void) |
1142 | 0 | { |
1143 | 0 | if (mpTextEditOutliner) |
1144 | 0 | { |
1145 | 0 | rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get(); |
1146 | 0 | if (pTextObj) |
1147 | 0 | { |
1148 | 0 | pTextObj->onEditOutlinerStatusEvent(&rEditStat); |
1149 | 0 | } |
1150 | 0 | } |
1151 | 0 | } |
1152 | | |
1153 | | void SdrObjEditView::ImpChainingEventHdl() |
1154 | 0 | { |
1155 | 0 | if (!mpTextEditOutliner) |
1156 | 0 | return; |
1157 | | |
1158 | 0 | rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get(); |
1159 | 0 | OutlinerView* pOLV = GetTextEditOutlinerView(); |
1160 | 0 | if (pTextObj && pOLV) |
1161 | 0 | { |
1162 | 0 | TextChain* pTextChain = pTextObj->GetTextChain(); |
1163 | | |
1164 | | // XXX: IsChainable and GetNilChainingEvent are a bit mixed up atm |
1165 | 0 | if (!pTextObj->IsChainable()) |
1166 | 0 | { |
1167 | 0 | return; |
1168 | 0 | } |
1169 | | // This is true during an underflow-caused overflow (with pEdtOutl->SetText()) |
1170 | 0 | if (pTextChain->GetNilChainingEvent(pTextObj.get())) |
1171 | 0 | { |
1172 | 0 | return; |
1173 | 0 | } |
1174 | | |
1175 | | // We prevent to trigger further handling of overflow/underflow for pTextObj |
1176 | 0 | pTextChain->SetNilChainingEvent(pTextObj.get(), true); // XXX |
1177 | | |
1178 | | // Save previous selection pos // NOTE: It must be done to have the right CursorEvent in KeyInput |
1179 | 0 | pTextChain->SetPreChainingSel(pTextObj.get(), pOLV->GetSelection()); |
1180 | | //maPreChainingSel = new ESelection(pOLV->GetSelection()); |
1181 | | |
1182 | | // Handling Undo |
1183 | 0 | const int nText = 0; // XXX: hardcoded index (SdrTextObj::getText handles only 0) |
1184 | |
|
1185 | 0 | const bool bUndoEnabled = IsUndoEnabled(); |
1186 | 0 | std::unique_ptr<SdrUndoObjSetText> pTxtUndo; |
1187 | 0 | if (bUndoEnabled) |
1188 | 0 | pTxtUndo.reset( |
1189 | 0 | dynamic_cast<SdrUndoObjSetText*>(GetModel() |
1190 | 0 | .GetSdrUndoFactory() |
1191 | 0 | .CreateUndoObjectSetText(*pTextObj, nText) |
1192 | 0 | .release())); |
1193 | | |
1194 | | // trigger actual chaining |
1195 | 0 | pTextObj->onChainingEvent(); |
1196 | |
|
1197 | 0 | if (pTxtUndo) |
1198 | 0 | { |
1199 | 0 | pTxtUndo->AfterSetText(); |
1200 | 0 | if (!pTxtUndo->IsDifferent()) |
1201 | 0 | { |
1202 | 0 | pTxtUndo.reset(); |
1203 | 0 | } |
1204 | 0 | } |
1205 | |
|
1206 | 0 | if (pTxtUndo) |
1207 | 0 | AddUndo(std::move(pTxtUndo)); |
1208 | | |
1209 | | //maCursorEvent = new CursorChainingEvent(pTextChain->GetCursorEvent(pTextObj)); |
1210 | | //SdrTextObj *pNextLink = pTextObj->GetNextLinkInChain(); |
1211 | | |
1212 | | // NOTE: Must be called. Don't let the function return if you set it to true and not reset it |
1213 | 0 | pTextChain->SetNilChainingEvent(pTextObj.get(), false); |
1214 | 0 | } |
1215 | 0 | else |
1216 | 0 | { |
1217 | | // XXX |
1218 | 0 | SAL_INFO("svx.chaining", "[OnChaining] No Edit Outliner View"); |
1219 | 0 | } |
1220 | 0 | } |
1221 | | |
1222 | | IMPL_LINK_NOARG(SdrObjEditView, ImpAfterCutOrPasteChainingEventHdl, LinkParamNone*, void) |
1223 | 0 | { |
1224 | 0 | SdrTextObj* pTextObj = GetTextEditObject(); |
1225 | 0 | if (!pTextObj) |
1226 | 0 | return; |
1227 | 0 | ImpChainingEventHdl(); |
1228 | 0 | TextChainCursorManager aCursorManager(this, pTextObj); |
1229 | 0 | ImpMoveCursorAfterChainingEvent(&aCursorManager); |
1230 | 0 | } |
1231 | | |
1232 | | void SdrObjEditView::ImpMoveCursorAfterChainingEvent(TextChainCursorManager* pCursorManager) |
1233 | 0 | { |
1234 | 0 | rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get(); |
1235 | |
|
1236 | 0 | if (!pTextObj || !pCursorManager) |
1237 | 0 | return; |
1238 | | |
1239 | | // Check if it has links to move it to |
1240 | 0 | if (!pTextObj || !pTextObj->IsChainable()) |
1241 | 0 | return; |
1242 | | |
1243 | 0 | TextChain* pTextChain = pTextObj->GetTextChain(); |
1244 | 0 | ESelection aNewSel = pTextChain->GetPostChainingSel(pTextObj.get()); |
1245 | |
|
1246 | 0 | pCursorManager->HandleCursorEventAfterChaining(pTextChain->GetCursorEvent(pTextObj.get()), |
1247 | 0 | aNewSel); |
1248 | | |
1249 | | // Reset event |
1250 | 0 | pTextChain->SetCursorEvent(pTextObj.get(), CursorChainingEvent::NULL_EVENT); |
1251 | 0 | } |
1252 | | |
1253 | | IMPL_LINK(SdrObjEditView, ImpOutlinerCalcFieldValueHdl, EditFieldInfo*, pFI, void) |
1254 | 0 | { |
1255 | 0 | bool bOk = false; |
1256 | 0 | OUString& rStr = pFI->GetRepresentation(); |
1257 | 0 | rStr.clear(); |
1258 | 0 | rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get(); |
1259 | 0 | if (pTextObj != nullptr) |
1260 | 0 | { |
1261 | 0 | std::optional<Color> pTxtCol; |
1262 | 0 | std::optional<Color> pFldCol; |
1263 | 0 | std::optional<FontLineStyle> pFldLineStyle; |
1264 | 0 | bOk = pTextObj->CalcFieldValue(pFI->GetField(), pFI->GetPara(), pFI->GetPos(), true, |
1265 | 0 | pTxtCol, pFldCol, pFldLineStyle, rStr); |
1266 | 0 | if (bOk) |
1267 | 0 | { |
1268 | 0 | if (pTxtCol) |
1269 | 0 | { |
1270 | 0 | pFI->SetTextColor(*pTxtCol); |
1271 | 0 | } |
1272 | 0 | if (pFldLineStyle) |
1273 | 0 | { |
1274 | 0 | pFI->SetFontLineStyle(*pFldLineStyle); |
1275 | 0 | } |
1276 | 0 | if (pFldCol) |
1277 | 0 | { |
1278 | 0 | pFI->SetFieldColor(*pFldCol); |
1279 | 0 | } |
1280 | 0 | else |
1281 | 0 | { |
1282 | 0 | pFI->SetFieldColor(COL_LIGHTGRAY); // TODO: remove this later on (357) |
1283 | 0 | } |
1284 | 0 | } |
1285 | 0 | } |
1286 | 0 | Outliner& rDrawOutl = GetModel().GetDrawOutliner(pTextObj.get()); |
1287 | 0 | Link<EditFieldInfo*, void> aDrawOutlLink = rDrawOutl.GetCalcFieldValueHdl(); |
1288 | 0 | if (!bOk && aDrawOutlLink.IsSet()) |
1289 | 0 | { |
1290 | 0 | aDrawOutlLink.Call(pFI); |
1291 | 0 | bOk = !rStr.isEmpty(); |
1292 | 0 | } |
1293 | 0 | if (!bOk) |
1294 | 0 | { |
1295 | 0 | m_aOldCalcFieldValueLink.Call(pFI); |
1296 | 0 | } |
1297 | 0 | } |
1298 | | |
1299 | 0 | IMPL_LINK_NOARG(SdrObjEditView, EndTextEditHdl, SdrUndoManager*, void) { SdrEndTextEdit(); } |
1300 | | |
1301 | | // Default implementation - null UndoManager |
1302 | | std::unique_ptr<SdrUndoManager> SdrObjEditView::createLocalTextUndoManager() |
1303 | 0 | { |
1304 | 0 | SAL_WARN("svx", "SdrObjEditView::createLocalTextUndoManager needs to be overridden"); |
1305 | 0 | return std::unique_ptr<SdrUndoManager>(); |
1306 | 0 | } |
1307 | | |
1308 | | bool SdrObjEditView::SdrBeginTextEdit(SdrObject* pObj_, SdrPageView* pPV, vcl::Window* pWin, |
1309 | | bool bIsNewObj, SdrOutliner* pGivenOutliner, |
1310 | | OutlinerView* pGivenOutlinerView, bool bDontDeleteOutliner, |
1311 | | bool bOnlyOneView, bool bGrabFocus) |
1312 | 0 | { |
1313 | | // FIXME cannot be an assert() yet, the code is not ready for that; |
1314 | | // eg. press F7 in Impress when you are inside a text object with spelling |
1315 | | // mistakes => boom; and it is unclear how to avoid that |
1316 | 0 | SAL_WARN_IF(IsTextEdit(), "svx", "SdrBeginTextEdit called when IsTextEdit() is already true."); |
1317 | | // FIXME this encourages all sorts of bad habits and should be removed |
1318 | 0 | SdrEndTextEdit(); |
1319 | |
|
1320 | 0 | SdrTextObj* pObj = DynCastSdrTextObj(pObj_); |
1321 | 0 | if (!pObj) |
1322 | 0 | return false; // currently only possible with text objects |
1323 | | |
1324 | 0 | if (bGrabFocus && pWin) |
1325 | 0 | { |
1326 | | // attention, this call may cause an EndTextEdit() call to this view |
1327 | 0 | pWin->GrabFocus(); // to force the cursor into the edit view |
1328 | 0 | } |
1329 | |
|
1330 | 0 | mbTextEditDontDelete = bDontDeleteOutliner && pGivenOutliner != nullptr; |
1331 | 0 | mbTextEditOnlyOneView = bOnlyOneView; |
1332 | 0 | mbTextEditNewObj = bIsNewObj; |
1333 | 0 | const sal_uInt32 nWinCount(PaintWindowCount()); |
1334 | |
|
1335 | 0 | bool bBrk(false); |
1336 | |
|
1337 | 0 | if (!pWin) |
1338 | 0 | { |
1339 | 0 | for (sal_uInt32 i = 0; i < nWinCount && !pWin; i++) |
1340 | 0 | { |
1341 | 0 | SdrPaintWindow* pPaintWindow = GetPaintWindow(i); |
1342 | |
|
1343 | 0 | if (OUTDEV_WINDOW == pPaintWindow->GetOutputDevice().GetOutDevType()) |
1344 | 0 | { |
1345 | 0 | pWin = pPaintWindow->GetOutputDevice().GetOwnerWindow(); |
1346 | 0 | } |
1347 | 0 | } |
1348 | | |
1349 | | // break, when no window exists |
1350 | 0 | if (!pWin) |
1351 | 0 | { |
1352 | 0 | bBrk = true; |
1353 | 0 | } |
1354 | 0 | } |
1355 | |
|
1356 | 0 | if (!bBrk && !pPV) |
1357 | 0 | { |
1358 | 0 | pPV = GetSdrPageView(); |
1359 | | |
1360 | | // break, when no PageView for the object exists |
1361 | 0 | if (!pPV) |
1362 | 0 | { |
1363 | 0 | bBrk = true; |
1364 | 0 | } |
1365 | 0 | } |
1366 | | |
1367 | | // no TextEdit on objects in locked Layer |
1368 | 0 | if (pPV && pPV->GetLockedLayers().IsSet(pObj->GetLayer())) |
1369 | 0 | { |
1370 | 0 | bBrk = true; |
1371 | 0 | } |
1372 | |
|
1373 | 0 | if (mpTextEditOutliner) |
1374 | 0 | { |
1375 | 0 | OSL_FAIL("SdrObjEditView::SdrBeginTextEdit(): Old Outliner still exists."); |
1376 | 0 | mpTextEditOutliner.reset(); |
1377 | 0 | } |
1378 | |
|
1379 | 0 | if (!bBrk) |
1380 | 0 | { |
1381 | 0 | mpTextEditWin = pWin; |
1382 | 0 | mpTextEditPV = pPV; |
1383 | 0 | mxWeakTextEditObj = pObj; |
1384 | 0 | if (pGivenOutliner) |
1385 | 0 | { |
1386 | 0 | mpTextEditOutliner.reset(pGivenOutliner); |
1387 | 0 | pGivenOutliner = nullptr; // so we don't delete it on the error path |
1388 | 0 | } |
1389 | 0 | else |
1390 | 0 | mpTextEditOutliner |
1391 | 0 | = SdrMakeOutliner(OutlinerMode::TextObject, pObj->getSdrModelFromSdrObject()); |
1392 | |
|
1393 | 0 | { |
1394 | 0 | mpTextEditOutliner->ForceAutoColor( |
1395 | 0 | officecfg::Office::Common::Accessibility::IsAutomaticFontColor::get()); |
1396 | 0 | } |
1397 | |
|
1398 | 0 | m_aOldCalcFieldValueLink = mpTextEditOutliner->GetCalcFieldValueHdl(); |
1399 | | // FieldHdl has to be set by SdrBeginTextEdit, because this call an UpdateFields |
1400 | 0 | mpTextEditOutliner->SetCalcFieldValueHdl( |
1401 | 0 | LINK(this, SdrObjEditView, ImpOutlinerCalcFieldValueHdl)); |
1402 | 0 | mpTextEditOutliner->SetBeginPasteOrDropHdl(LINK(this, SdrObjEditView, BeginPasteOrDropHdl)); |
1403 | 0 | mpTextEditOutliner->SetEndPasteOrDropHdl(LINK(this, SdrObjEditView, EndPasteOrDropHdl)); |
1404 | | |
1405 | | // It is just necessary to make the visualized page known. Set it. |
1406 | 0 | mpTextEditOutliner->setVisualizedPage(pPV->GetPage()); |
1407 | |
|
1408 | 0 | rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get(); |
1409 | 0 | mpTextEditOutliner->SetTextObjNoInit(pTextObj.get()); |
1410 | |
|
1411 | 0 | if (pTextObj->BegTextEdit(*mpTextEditOutliner)) |
1412 | 0 | { |
1413 | | // switch off any running TextAnimations |
1414 | 0 | pTextObj->SetTextAnimationAllowed(false); |
1415 | | |
1416 | | // remember old cursor |
1417 | 0 | if (mpTextEditOutliner->GetViewCount() != 0) |
1418 | 0 | { |
1419 | 0 | mpTextEditOutliner->RemoveView(static_cast<size_t>(0)); |
1420 | 0 | } |
1421 | | |
1422 | | // Determine EditArea via TakeTextEditArea. |
1423 | | // TODO: This could theoretically be left out, because TakeTextRect() calculates the aTextEditArea, |
1424 | | // but aMinTextEditArea has to happen, too (therefore leaving this in right now) |
1425 | 0 | pTextObj->TakeTextEditArea(nullptr, nullptr, &m_aTextEditArea, &m_aMinTextEditArea); |
1426 | |
|
1427 | 0 | tools::Rectangle aTextRect; |
1428 | 0 | tools::Rectangle aAnchorRect; |
1429 | 0 | pTextObj->TakeTextRect(*mpTextEditOutliner, aTextRect, true, |
1430 | 0 | &aAnchorRect /* Give true here, not false */); |
1431 | |
|
1432 | 0 | if (!pTextObj->IsContourTextFrame()) |
1433 | 0 | { |
1434 | | // FitToSize not together with ContourFrame, for now |
1435 | 0 | if (pTextObj->IsFitToSize()) |
1436 | 0 | aTextRect = aAnchorRect; |
1437 | 0 | } |
1438 | |
|
1439 | 0 | m_aTextEditArea = aTextRect; |
1440 | | |
1441 | | // add possible GridOffset to up-to-now view-independent EditAreas |
1442 | 0 | basegfx::B2DVector aGridOffset(0.0, 0.0); |
1443 | 0 | if (getPossibleGridOffsetForSdrObject(aGridOffset, pTextObj.get(), pPV)) |
1444 | 0 | { |
1445 | 0 | const Point aOffset(basegfx::fround<tools::Long>(aGridOffset.getX()), |
1446 | 0 | basegfx::fround<tools::Long>(aGridOffset.getY())); |
1447 | |
|
1448 | 0 | m_aTextEditArea += aOffset; |
1449 | 0 | m_aMinTextEditArea += aOffset; |
1450 | 0 | } |
1451 | |
|
1452 | 0 | Point aPvOfs(pTextObj->GetTextEditOffset()); |
1453 | 0 | m_aTextEditArea.Move(aPvOfs.X(), aPvOfs.Y()); |
1454 | 0 | m_aMinTextEditArea.Move(aPvOfs.X(), aPvOfs.Y()); |
1455 | 0 | m_pTextEditCursorBuffer = pWin->GetCursor(); |
1456 | |
|
1457 | 0 | maHdlList.SetMoveOutside(true); |
1458 | | |
1459 | | // Since IsMarkHdlWhenTextEdit() is ignored, it is necessary |
1460 | | // to call AdjustMarkHdl() always. |
1461 | 0 | AdjustMarkHdl(); |
1462 | |
|
1463 | 0 | mpTextEditOutlinerView = ImpMakeOutlinerView(pWin, pGivenOutlinerView); |
1464 | |
|
1465 | 0 | if (!comphelper::LibreOfficeKit::isActive() && mpTextEditOutlinerView) |
1466 | 0 | { |
1467 | | // activate visualization of EditView on Overlay, suppress when |
1468 | | // LibreOfficeKit is active |
1469 | 0 | mpTextEditOutlinerView->GetEditView().setEditViewCallbacks(this); |
1470 | |
|
1471 | 0 | const Color aHilightColor(SvtOptionsDrawinglayer::getHilightColor()); |
1472 | 0 | const SdrTextObj* pText = GetTextEditObject(); |
1473 | | // show for cases like tdf#94223 but not for table cells like tdf#151311 |
1474 | 0 | const bool bVisualizeSurroundingFrame( |
1475 | 0 | pText && pText->GetObjIdentifier() != SdrObjKind::Table); |
1476 | 0 | SdrPageView* pPageView = GetSdrPageView(); |
1477 | |
|
1478 | 0 | if (pPageView) |
1479 | 0 | { |
1480 | 0 | for (sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++) |
1481 | 0 | { |
1482 | 0 | const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b); |
1483 | |
|
1484 | 0 | if (rPageWindow.GetPaintWindow().OutputToWindow()) |
1485 | 0 | { |
1486 | 0 | const rtl::Reference<sdr::overlay::OverlayManager>& xManager |
1487 | 0 | = rPageWindow.GetOverlayManager(); |
1488 | 0 | if (xManager.is()) |
1489 | 0 | { |
1490 | 0 | std::unique_ptr<TextEditOverlayObject> pNewTextEditOverlayObject( |
1491 | 0 | new TextEditOverlayObject(aHilightColor, |
1492 | 0 | *mpTextEditOutlinerView)); |
1493 | |
|
1494 | 0 | xManager->add(*pNewTextEditOverlayObject); |
1495 | 0 | if (bVisualizeSurroundingFrame) |
1496 | 0 | xManager->add(*pNewTextEditOverlayObject->getOverlayFrame()); |
1497 | 0 | xManager->add(*pNewTextEditOverlayObject->getOverlaySelection()); |
1498 | |
|
1499 | 0 | maTEOverlayGroup.append(std::move(pNewTextEditOverlayObject)); |
1500 | 0 | } |
1501 | 0 | } |
1502 | 0 | } |
1503 | 0 | } |
1504 | 0 | } |
1505 | | |
1506 | | // check if this view is already inserted |
1507 | 0 | size_t i2, nCount = mpTextEditOutliner->GetViewCount(); |
1508 | 0 | for (i2 = 0; i2 < nCount; i2++) |
1509 | 0 | { |
1510 | 0 | if (mpTextEditOutliner->GetView(i2) == mpTextEditOutlinerView) |
1511 | 0 | break; |
1512 | 0 | } |
1513 | |
|
1514 | 0 | if (i2 == nCount) |
1515 | 0 | mpTextEditOutliner->InsertView(mpTextEditOutlinerView, 0); |
1516 | |
|
1517 | 0 | maHdlList.SetMoveOutside(false); |
1518 | 0 | maHdlList.SetMoveOutside(true); |
1519 | | |
1520 | | // register all windows as OutlinerViews with the Outliner |
1521 | 0 | if (!bOnlyOneView) |
1522 | 0 | { |
1523 | 0 | for (sal_uInt32 i = 0; i < nWinCount; i++) |
1524 | 0 | { |
1525 | 0 | SdrPaintWindow* pPaintWindow = GetPaintWindow(i); |
1526 | 0 | OutputDevice& rOutDev = pPaintWindow->GetOutputDevice(); |
1527 | |
|
1528 | 0 | if (&rOutDev != pWin->GetOutDev() && OUTDEV_WINDOW == rOutDev.GetOutDevType()) |
1529 | 0 | { |
1530 | 0 | OutlinerView* pOutlView |
1531 | 0 | = ImpMakeOutlinerView(rOutDev.GetOwnerWindow(), nullptr); |
1532 | 0 | mpTextEditOutliner->InsertView(pOutlView, static_cast<sal_uInt16>(i)); |
1533 | 0 | } |
1534 | 0 | } |
1535 | |
|
1536 | 0 | if (comphelper::LibreOfficeKit::isActive()) |
1537 | 0 | { |
1538 | | // Register an outliner view for all other sdr views that |
1539 | | // show the same page, so that when the text edit changes, |
1540 | | // all interested windows get an invalidation. |
1541 | 0 | SdrViewIter::ForAllViews(pObj->getSdrPageFromSdrObject(), [this, &pWin]( |
1542 | 0 | SdrView* pView) { |
1543 | 0 | if (pView == this) |
1544 | 0 | return; |
1545 | | |
1546 | 0 | for (sal_uInt32 nViewPaintWindow = 0; |
1547 | 0 | nViewPaintWindow < pView->PaintWindowCount(); ++nViewPaintWindow) |
1548 | 0 | { |
1549 | 0 | SdrPaintWindow* pPaintWindow = pView->GetPaintWindow(nViewPaintWindow); |
1550 | 0 | OutputDevice& rOutDev = pPaintWindow->GetOutputDevice(); |
1551 | |
|
1552 | 0 | if (&rOutDev != pWin->GetOutDev() |
1553 | 0 | && OUTDEV_WINDOW == rOutDev.GetOutDevType()) |
1554 | 0 | { |
1555 | 0 | OutlinerView* pOutlView |
1556 | 0 | = ImpMakeOutlinerView(rOutDev.GetOwnerWindow(), nullptr); |
1557 | 0 | pOutlView->HideCursor(); |
1558 | 0 | rOutDev.GetOwnerWindow()->SetCursor(nullptr); |
1559 | 0 | mpTextEditOutliner->InsertView(pOutlView); |
1560 | 0 | } |
1561 | 0 | } |
1562 | 0 | }); |
1563 | 0 | } |
1564 | 0 | } |
1565 | |
|
1566 | 0 | mpTextEditOutlinerView->ShowCursor(); |
1567 | 0 | mpTextEditOutliner->SetStatusEventHdl( |
1568 | 0 | LINK(this, SdrObjEditView, ImpOutlinerStatusEventHdl)); |
1569 | | |
1570 | | // IASS: start listening to ModelChanges of TextEdit |
1571 | 0 | if (isInteractiveSlideShow() |
1572 | 0 | || pTextObj->GetViewContact().hasMultipleViewObjectContacts()) |
1573 | 0 | mpTextEditOutliner->SetModifyHdl(LINK(this, SdrObjEditView, ImpModifyHdl)); |
1574 | |
|
1575 | 0 | if (pTextObj->IsChainable()) |
1576 | 0 | { |
1577 | 0 | mpTextEditOutlinerView->SetEndCutPasteLinkHdl( |
1578 | 0 | LINK(this, SdrObjEditView, ImpAfterCutOrPasteChainingEventHdl)); |
1579 | 0 | } |
1580 | |
|
1581 | 0 | mpTextEditOutliner->ClearModifyFlag(); |
1582 | |
|
1583 | 0 | if (pTextObj->IsFitToSize()) |
1584 | 0 | { |
1585 | 0 | pWin->Invalidate(m_aTextEditArea); |
1586 | 0 | } |
1587 | |
|
1588 | 0 | SdrHint aHint(SdrHintKind::BeginEdit, *pTextObj); |
1589 | 0 | GetModel().Broadcast(aHint); |
1590 | 0 | if (auto pBroadcaster = pTextObj->GetBroadcaster()) |
1591 | 0 | pBroadcaster->Broadcast(aHint); |
1592 | |
|
1593 | 0 | mpTextEditOutliner->setVisualizedPage(nullptr); |
1594 | |
|
1595 | 0 | if (mxSelectionController.is()) |
1596 | 0 | mxSelectionController->onSelectionHasChanged(); |
1597 | |
|
1598 | 0 | if (IsUndoEnabled() && !GetModel().GetDisableTextEditUsesCommonUndoManager()) |
1599 | 0 | { |
1600 | 0 | SdrUndoManager* pSdrUndoManager = nullptr; |
1601 | 0 | mpLocalTextEditUndoManager = createLocalTextUndoManager(); |
1602 | |
|
1603 | 0 | if (mpLocalTextEditUndoManager) |
1604 | 0 | pSdrUndoManager = mpLocalTextEditUndoManager.get(); |
1605 | |
|
1606 | 0 | if (pSdrUndoManager) |
1607 | 0 | { |
1608 | | // we have an outliner, undo manager and it's an EditUndoManager, exchange |
1609 | | // the document undo manager and the default one from the outliner and tell |
1610 | | // it that text edit starts by setting a callback if it needs to end text edit mode. |
1611 | 0 | assert(nullptr == mpOldTextEditUndoManager); |
1612 | |
|
1613 | 0 | mpOldTextEditUndoManager = mpTextEditOutliner->SetUndoManager(pSdrUndoManager); |
1614 | 0 | pSdrUndoManager->SetEndTextEditHdl(LINK(this, SdrObjEditView, EndTextEditHdl)); |
1615 | 0 | } |
1616 | 0 | else |
1617 | 0 | { |
1618 | 0 | OSL_ENSURE(false, |
1619 | 0 | "The document undo manager is not derived from SdrUndoManager (!)"); |
1620 | 0 | } |
1621 | 0 | } |
1622 | |
|
1623 | 0 | return true; // ran fine, let TextEdit run now |
1624 | 0 | } |
1625 | 0 | else |
1626 | 0 | { |
1627 | 0 | mpTextEditOutliner->SetCalcFieldValueHdl(m_aOldCalcFieldValueLink); |
1628 | 0 | mpTextEditOutliner->SetBeginPasteOrDropHdl(Link<PasteOrDropInfos*, void>()); |
1629 | 0 | mpTextEditOutliner->SetEndPasteOrDropHdl(Link<PasteOrDropInfos*, void>()); |
1630 | 0 | } |
1631 | 0 | } |
1632 | 0 | if (mpTextEditOutliner != nullptr) |
1633 | 0 | { |
1634 | 0 | mpTextEditOutliner->setVisualizedPage(nullptr); |
1635 | 0 | } |
1636 | | |
1637 | | // something went wrong... |
1638 | 0 | if (!bDontDeleteOutliner) |
1639 | 0 | { |
1640 | 0 | delete pGivenOutliner; |
1641 | 0 | if (pGivenOutlinerView != nullptr) |
1642 | 0 | { |
1643 | 0 | delete pGivenOutlinerView; |
1644 | 0 | pGivenOutlinerView = nullptr; |
1645 | 0 | } |
1646 | 0 | } |
1647 | 0 | mpTextEditOutliner.reset(); |
1648 | |
|
1649 | 0 | mpTextEditOutlinerView = nullptr; |
1650 | 0 | mxWeakTextEditObj.clear(); |
1651 | 0 | mpTextEditPV = nullptr; |
1652 | 0 | mpTextEditWin = nullptr; |
1653 | 0 | maHdlList.SetMoveOutside(false); |
1654 | |
|
1655 | 0 | return false; |
1656 | 0 | } |
1657 | | |
1658 | | SdrEndTextEditKind SdrObjEditView::SdrEndTextEdit(bool bDontDeleteReally) |
1659 | 0 | { |
1660 | | // IASS: stop evtl. running timer immediately |
1661 | 0 | maTextEditUpdateTimer.Stop(); |
1662 | |
|
1663 | 0 | SdrEndTextEditKind eRet = SdrEndTextEditKind::Unchanged; |
1664 | 0 | rtl::Reference<SdrTextObj> pTEObj = mxWeakTextEditObj.get(); |
1665 | 0 | vcl::Window* pTEWin = mpTextEditWin; |
1666 | 0 | OutlinerView* pTEOutlinerView = mpTextEditOutlinerView; |
1667 | 0 | vcl::Cursor* pTECursorBuffer = m_pTextEditCursorBuffer; |
1668 | 0 | SdrUndoManager* pUndoEditUndoManager = nullptr; |
1669 | 0 | bool bNeedToUndoSavedRedoTextEdit(false); |
1670 | |
|
1671 | 0 | if (IsUndoEnabled() && pTEObj && mpTextEditOutliner |
1672 | 0 | && !GetModel().GetDisableTextEditUsesCommonUndoManager()) |
1673 | 0 | { |
1674 | | // change back the UndoManager to the remembered original one |
1675 | 0 | SfxUndoManager* pOriginal = mpTextEditOutliner->SetUndoManager(mpOldTextEditUndoManager); |
1676 | 0 | mpOldTextEditUndoManager = nullptr; |
1677 | |
|
1678 | 0 | if (pOriginal) |
1679 | 0 | { |
1680 | | // check if we got back our document undo manager |
1681 | 0 | SdrUndoManager* pSdrUndoManager = mpLocalTextEditUndoManager.get(); |
1682 | |
|
1683 | 0 | if (pSdrUndoManager && dynamic_cast<SdrUndoManager*>(pOriginal) == pSdrUndoManager) |
1684 | 0 | { |
1685 | 0 | if (pSdrUndoManager->isEndTextEditTriggeredFromUndo()) |
1686 | 0 | { |
1687 | | // remember the UndoManager where missing Undos have to be triggered after end |
1688 | | // text edit. When the undo had triggered the end text edit, the original action |
1689 | | // which had to be undone originally is not yet undone. |
1690 | 0 | pUndoEditUndoManager = pSdrUndoManager; |
1691 | | |
1692 | | // We are ending text edit; if text edit was triggered from undo, execute all redos |
1693 | | // to create a complete text change undo action for the redo buffer. Also mark this |
1694 | | // state when at least one redo was executed; the created extra TextChange needs to |
1695 | | // be undone in addition to the first real undo outside the text edit changes |
1696 | 0 | while (pSdrUndoManager->GetRedoActionCount() |
1697 | 0 | > pSdrUndoManager->GetRedoActionCountBeforeTextEdit()) |
1698 | 0 | { |
1699 | 0 | bNeedToUndoSavedRedoTextEdit = true; |
1700 | 0 | pSdrUndoManager->Redo(); |
1701 | 0 | } |
1702 | 0 | } |
1703 | | |
1704 | | // reset the callback link and let the undo manager cleanup all text edit |
1705 | | // undo actions to get the stack back to the form before the text edit |
1706 | 0 | pSdrUndoManager->SetEndTextEditHdl(Link<SdrUndoManager*, void>()); |
1707 | 0 | } |
1708 | 0 | else |
1709 | 0 | { |
1710 | 0 | OSL_ENSURE(false, "Got UndoManager back in SdrEndTextEdit which is NOT the " |
1711 | 0 | "expected document UndoManager (!)"); |
1712 | 0 | delete pOriginal; |
1713 | 0 | } |
1714 | | |
1715 | | // cid#1493241 - Wrapper object use after free |
1716 | 0 | if (pUndoEditUndoManager == mpLocalTextEditUndoManager.get()) |
1717 | 0 | pUndoEditUndoManager = nullptr; |
1718 | 0 | mpLocalTextEditUndoManager.reset(); |
1719 | 0 | } |
1720 | 0 | } |
1721 | 0 | else |
1722 | 0 | { |
1723 | 0 | assert(nullptr == mpOldTextEditUndoManager); // cannot be restored! |
1724 | 0 | } |
1725 | |
|
1726 | 0 | if (auto pTextEditObj = mxWeakTextEditObj.get()) |
1727 | 0 | { |
1728 | 0 | SdrHint aHint(SdrHintKind::EndEdit, *pTextEditObj); |
1729 | 0 | GetModel().Broadcast(aHint); |
1730 | 0 | if (auto pBroadcaster = pTextEditObj->GetBroadcaster()) |
1731 | 0 | pBroadcaster->Broadcast(aHint); |
1732 | 0 | } |
1733 | | |
1734 | | // if new mechanism was used, clean it up. At cleanup no need to check |
1735 | | // for LibreOfficeKit |
1736 | 0 | if (mpTextEditOutlinerView) |
1737 | 0 | { |
1738 | 0 | mpTextEditOutlinerView->GetEditView().setEditViewCallbacks(nullptr); |
1739 | 0 | maTEOverlayGroup.clear(); |
1740 | 0 | } |
1741 | |
|
1742 | 0 | mxWeakTextEditObj.clear(); |
1743 | 0 | mpTextEditPV = nullptr; |
1744 | 0 | mpTextEditWin = nullptr; |
1745 | 0 | mpTextEditOutlinerView = nullptr; |
1746 | 0 | m_pTextEditCursorBuffer = nullptr; |
1747 | 0 | m_aTextEditArea = tools::Rectangle(); |
1748 | |
|
1749 | 0 | if (SdrOutliner* pTEOutliner = mpTextEditOutliner.release()) |
1750 | 0 | { |
1751 | 0 | bool bModified = pTEOutliner->IsModified(); |
1752 | 0 | if (pTEOutlinerView != nullptr) |
1753 | 0 | { |
1754 | 0 | pTEOutlinerView->HideCursor(); |
1755 | 0 | } |
1756 | 0 | if (pTEObj != nullptr) |
1757 | 0 | { |
1758 | 0 | pTEOutliner->CompleteOnlineSpelling(); |
1759 | |
|
1760 | 0 | std::unique_ptr<SdrUndoObjSetText> pTxtUndo; |
1761 | |
|
1762 | 0 | if (bModified) |
1763 | 0 | { |
1764 | 0 | sal_Int32 nText; |
1765 | 0 | for (nText = 0; nText < pTEObj->getTextCount(); ++nText) |
1766 | 0 | if (pTEObj->getText(nText) == pTEObj->getActiveText()) |
1767 | 0 | break; |
1768 | |
|
1769 | 0 | pTxtUndo.reset( |
1770 | 0 | dynamic_cast<SdrUndoObjSetText*>(GetModel() |
1771 | 0 | .GetSdrUndoFactory() |
1772 | 0 | .CreateUndoObjectSetText(*pTEObj, nText) |
1773 | 0 | .release())); |
1774 | 0 | } |
1775 | 0 | DBG_ASSERT(!bModified || pTxtUndo, |
1776 | 0 | "svx::SdrObjEditView::EndTextEdit(), could not create undo action!"); |
1777 | | // Set old CalcFieldValue-Handler again, this |
1778 | | // has to happen before Obj::EndTextEdit(), as this does UpdateFields(). |
1779 | 0 | pTEOutliner->SetCalcFieldValueHdl(m_aOldCalcFieldValueLink); |
1780 | 0 | pTEOutliner->SetBeginPasteOrDropHdl(Link<PasteOrDropInfos*, void>()); |
1781 | 0 | pTEOutliner->SetEndPasteOrDropHdl(Link<PasteOrDropInfos*, void>()); |
1782 | | |
1783 | | // IASS: stop listening to ModelChanges of TextEdit |
1784 | 0 | pTEOutliner->SetModifyHdl(Link<LinkParamNone*, void>()); |
1785 | |
|
1786 | 0 | const bool bUndo = IsUndoEnabled(); |
1787 | 0 | if (bUndo) |
1788 | 0 | { |
1789 | 0 | OUString aObjName(pTEObj->TakeObjNameSingul()); |
1790 | 0 | BegUndo(SvxResId(STR_UndoObjSetText), aObjName); |
1791 | 0 | } |
1792 | |
|
1793 | 0 | pTEObj->EndTextEdit(*pTEOutliner); |
1794 | |
|
1795 | 0 | if ((pTEObj->GetRotateAngle() != 0_deg100) || (pTEObj && pTEObj->IsFontwork())) |
1796 | 0 | { |
1797 | 0 | pTEObj->ActionChanged(); |
1798 | 0 | } |
1799 | |
|
1800 | 0 | if (pTxtUndo != nullptr) |
1801 | 0 | { |
1802 | 0 | pTxtUndo->AfterSetText(); |
1803 | 0 | if (!pTxtUndo->IsDifferent()) |
1804 | 0 | { |
1805 | 0 | pTxtUndo.reset(); |
1806 | 0 | } |
1807 | 0 | } |
1808 | | // check deletion of entire TextObj |
1809 | 0 | std::unique_ptr<SdrUndoAction> pDelUndo; |
1810 | 0 | bool bDelObj = false; |
1811 | 0 | if (mbTextEditNewObj) |
1812 | 0 | { |
1813 | 0 | bDelObj = pTEObj->IsTextFrame() && !pTEObj->HasText() && !pTEObj->IsEmptyPresObj() |
1814 | 0 | && !pTEObj->HasFill() && !pTEObj->HasLine(); |
1815 | |
|
1816 | 0 | if (pTEObj->IsInserted() && bDelObj |
1817 | 0 | && pTEObj->GetObjInventor() == SdrInventor::Default && !bDontDeleteReally) |
1818 | 0 | { |
1819 | 0 | SdrObjKind eIdent = pTEObj->GetObjIdentifier(); |
1820 | 0 | if (eIdent == SdrObjKind::Text) |
1821 | 0 | { |
1822 | 0 | pDelUndo = GetModel().GetSdrUndoFactory().CreateUndoDeleteObject(*pTEObj); |
1823 | 0 | } |
1824 | 0 | } |
1825 | 0 | } |
1826 | 0 | if (pTxtUndo) |
1827 | 0 | { |
1828 | 0 | if (bUndo) |
1829 | 0 | AddUndo(std::move(pTxtUndo)); |
1830 | 0 | eRet = SdrEndTextEditKind::Changed; |
1831 | 0 | } |
1832 | 0 | if (pDelUndo != nullptr) |
1833 | 0 | { |
1834 | 0 | if (bUndo) |
1835 | 0 | { |
1836 | 0 | AddUndo(std::move(pDelUndo)); |
1837 | 0 | } |
1838 | 0 | eRet = SdrEndTextEditKind::Deleted; |
1839 | 0 | DBG_ASSERT(pTEObj->getParentSdrObjListFromSdrObject() != nullptr, |
1840 | 0 | "SdrObjEditView::SdrEndTextEdit(): Fatal: Object edited doesn't have an " |
1841 | 0 | "ObjList!"); |
1842 | 0 | if (pTEObj->getParentSdrObjListFromSdrObject() != nullptr) |
1843 | 0 | { |
1844 | 0 | pTEObj->getParentSdrObjListFromSdrObject()->RemoveObject(pTEObj->GetOrdNum()); |
1845 | 0 | CheckMarked(); // remove selection immediately... |
1846 | 0 | } |
1847 | 0 | } |
1848 | 0 | else if (bDelObj) |
1849 | 0 | { // for Writer: the app has to do the deletion itself. |
1850 | 0 | eRet = SdrEndTextEditKind::ShouldBeDeleted; |
1851 | 0 | } |
1852 | |
|
1853 | 0 | if (bUndo) |
1854 | 0 | EndUndo(); // EndUndo after Remove, in case UndoStack is deleted immediately |
1855 | | |
1856 | | // Switch on any TextAnimation again after TextEdit |
1857 | 0 | if (pTEObj) |
1858 | 0 | { |
1859 | 0 | pTEObj->SetTextAnimationAllowed(true); |
1860 | 0 | } |
1861 | | |
1862 | | // Since IsMarkHdlWhenTextEdit() is ignored, it is necessary |
1863 | | // to call AdjustMarkHdl() always. |
1864 | 0 | AdjustMarkHdl(); |
1865 | 0 | } |
1866 | 0 | if (pTEWin != nullptr) |
1867 | 0 | { |
1868 | 0 | pTEWin->SetCursor(pTECursorBuffer); |
1869 | 0 | } |
1870 | | // delete all OutlinerViews |
1871 | 0 | for (size_t i = pTEOutliner->GetViewCount(); i > 0;) |
1872 | 0 | { |
1873 | 0 | i--; |
1874 | 0 | OutlinerView* pOLV = pTEOutliner->GetView(i); |
1875 | 0 | sal_uInt16 nMorePix = pOLV->GetInvalidateMore() + 10; |
1876 | 0 | VclPtr<vcl::Window> pWin = pOLV->GetWindow(); |
1877 | 0 | tools::Rectangle aRect(pOLV->GetOutputArea()); |
1878 | 0 | pTEOutliner->RemoveView(i); |
1879 | 0 | if (!mbTextEditDontDelete || i != 0) |
1880 | 0 | { |
1881 | | // may not own the zeroth one |
1882 | 0 | delete pOLV; |
1883 | 0 | } |
1884 | 0 | aRect.Union(m_aTextEditArea); |
1885 | 0 | aRect.Union(m_aMinTextEditArea); |
1886 | 0 | aRect = pWin->LogicToPixel(aRect); |
1887 | 0 | aRect.AdjustLeft(-nMorePix); |
1888 | 0 | aRect.AdjustTop(-nMorePix); |
1889 | 0 | aRect.AdjustRight(nMorePix); |
1890 | 0 | aRect.AdjustBottom(nMorePix); |
1891 | 0 | aRect = pWin->PixelToLogic(aRect); |
1892 | 0 | InvalidateOneWin(*pWin->GetOutDev(), aRect); |
1893 | 0 | pWin->GetOutDev()->SetFillColor(); |
1894 | 0 | pWin->GetOutDev()->SetLineColor(COL_BLACK); |
1895 | 0 | } |
1896 | | // and now the Outliner itself |
1897 | 0 | if (!mbTextEditDontDelete) |
1898 | 0 | delete pTEOutliner; |
1899 | 0 | else |
1900 | 0 | pTEOutliner->Clear(); |
1901 | 0 | maHdlList.SetMoveOutside(false); |
1902 | 0 | if (eRet != SdrEndTextEditKind::Unchanged) |
1903 | 0 | { |
1904 | 0 | GetMarkedObjectListWriteAccess().SetNameDirty(); |
1905 | 0 | } |
1906 | | // coverity[leaked_storage] - if pTEOutliner wasn't deleted it didn't really belong to us |
1907 | 0 | } |
1908 | |
|
1909 | 0 | if (pTEObj && !pTEObj->getSdrModelFromSdrObject().isLocked() && pTEObj->GetBroadcaster()) |
1910 | 0 | { |
1911 | 0 | SdrHint aHint(SdrHintKind::EndEdit, *pTEObj); |
1912 | 0 | pTEObj->GetBroadcaster()->Broadcast(aHint); |
1913 | 0 | } |
1914 | |
|
1915 | 0 | if (pUndoEditUndoManager) |
1916 | 0 | { |
1917 | 0 | if (bNeedToUndoSavedRedoTextEdit) |
1918 | 0 | { |
1919 | | // undo the text edit action since it was created as part of an EndTextEdit |
1920 | | // callback from undo itself. This needs to be done after the call to |
1921 | | // FmFormView::SdrEndTextEdit since it gets created there |
1922 | 0 | pUndoEditUndoManager->Undo(); |
1923 | 0 | } |
1924 | | |
1925 | | // trigger the Undo which was not executed, but lead to this |
1926 | | // end text edit |
1927 | 0 | pUndoEditUndoManager->Undo(); |
1928 | 0 | } |
1929 | |
|
1930 | 0 | return eRet; |
1931 | 0 | } |
1932 | | |
1933 | | // info about TextEdit. Default is false. |
1934 | 592k | bool SdrObjEditView::IsTextEdit() const { return mxWeakTextEditObj.get().is(); } |
1935 | | |
1936 | | // info about TextEditPageView. Default is 0L. |
1937 | 0 | SdrPageView* SdrObjEditView::GetTextEditPageView() const { return mpTextEditPV; } |
1938 | | |
1939 | | OutlinerView* SdrObjEditView::ImpFindOutlinerView(vcl::Window const* pWin) const |
1940 | 0 | { |
1941 | 0 | if (pWin == nullptr) |
1942 | 0 | return nullptr; |
1943 | 0 | if (mpTextEditOutliner == nullptr) |
1944 | 0 | return nullptr; |
1945 | 0 | OutlinerView* pNewView = nullptr; |
1946 | 0 | size_t nWinCount = mpTextEditOutliner->GetViewCount(); |
1947 | 0 | for (size_t i = 0; i < nWinCount && pNewView == nullptr; i++) |
1948 | 0 | { |
1949 | 0 | OutlinerView* pView = mpTextEditOutliner->GetView(i); |
1950 | 0 | if (pView->GetWindow() == pWin) |
1951 | 0 | pNewView = pView; |
1952 | 0 | } |
1953 | 0 | return pNewView; |
1954 | 0 | } |
1955 | | |
1956 | | void SdrObjEditView::SetTextEditWin(vcl::Window* pWin) |
1957 | 0 | { |
1958 | 0 | if (!(mxWeakTextEditObj.get() && pWin != nullptr && pWin != mpTextEditWin)) |
1959 | 0 | return; |
1960 | | |
1961 | 0 | OutlinerView* pNewView = ImpFindOutlinerView(pWin); |
1962 | 0 | if (pNewView != nullptr && pNewView != mpTextEditOutlinerView) |
1963 | 0 | { |
1964 | 0 | if (mpTextEditOutlinerView != nullptr) |
1965 | 0 | { |
1966 | 0 | mpTextEditOutlinerView->HideCursor(); |
1967 | 0 | } |
1968 | 0 | mpTextEditOutlinerView = pNewView; |
1969 | 0 | mpTextEditWin = pWin; |
1970 | 0 | pWin->GrabFocus(); // Make the cursor blink here as well |
1971 | 0 | pNewView->ShowCursor(); |
1972 | 0 | ImpMakeTextCursorAreaVisible(); |
1973 | 0 | } |
1974 | 0 | } |
1975 | | |
1976 | | bool SdrObjEditView::IsTextEditHit(const Point& rHit) const |
1977 | 0 | { |
1978 | 0 | bool bOk = false; |
1979 | 0 | if (mxWeakTextEditObj.get()) |
1980 | 0 | { |
1981 | 0 | tools::Rectangle aEditArea; |
1982 | 0 | if (OutlinerView* pOLV = mpTextEditOutliner->GetView(0)) |
1983 | 0 | aEditArea.Union(pOLV->GetOutputArea()); |
1984 | |
|
1985 | 0 | if (aEditArea.Contains(rHit)) |
1986 | 0 | { // check if any characters were actually hit |
1987 | 0 | const Point aPnt(rHit - aEditArea.TopLeft()); |
1988 | 0 | tools::Long nHitTol = 2000; |
1989 | 0 | if (OutputDevice* pRef = mpTextEditOutliner->GetRefDevice()) |
1990 | 0 | nHitTol = OutputDevice::LogicToLogic(nHitTol, MapUnit::Map100thMM, |
1991 | 0 | pRef->GetMapMode().GetMapUnit()); |
1992 | |
|
1993 | 0 | bOk = mpTextEditOutliner->IsTextPos(aPnt, static_cast<sal_uInt16>(nHitTol)); |
1994 | 0 | } |
1995 | 0 | } |
1996 | 0 | return bOk; |
1997 | 0 | } |
1998 | | |
1999 | | bool SdrObjEditView::IsTextEditFrameHit(const Point& rHit) const |
2000 | 0 | { |
2001 | 0 | bool bOk = false; |
2002 | 0 | if (rtl::Reference<SdrTextObj> pText = mxWeakTextEditObj.get()) |
2003 | 0 | { |
2004 | 0 | OutlinerView* pOLV = mpTextEditOutliner->GetView(0); |
2005 | 0 | if (pOLV) |
2006 | 0 | { |
2007 | 0 | vcl::Window* pWin = pOLV->GetWindow(); |
2008 | 0 | if (pText != nullptr && pText->IsTextFrame() && pWin != nullptr) |
2009 | 0 | { |
2010 | 0 | sal_uInt16 nPixSiz = pOLV->GetInvalidateMore(); |
2011 | 0 | tools::Rectangle aEditArea(m_aMinTextEditArea); |
2012 | 0 | aEditArea.Union(pOLV->GetOutputArea()); |
2013 | 0 | if (!aEditArea.Contains(rHit)) |
2014 | 0 | { |
2015 | 0 | Size aSiz(pWin->PixelToLogic(Size(nPixSiz, nPixSiz))); |
2016 | 0 | aEditArea.AdjustLeft(-(aSiz.Width())); |
2017 | 0 | aEditArea.AdjustTop(-(aSiz.Height())); |
2018 | 0 | aEditArea.AdjustRight(aSiz.Width()); |
2019 | 0 | aEditArea.AdjustBottom(aSiz.Height()); |
2020 | 0 | bOk = aEditArea.Contains(rHit); |
2021 | 0 | } |
2022 | 0 | } |
2023 | 0 | } |
2024 | 0 | } |
2025 | 0 | return bOk; |
2026 | 0 | } |
2027 | | |
2028 | | std::unique_ptr<TextChainCursorManager> |
2029 | | SdrObjEditView::ImpHandleMotionThroughBoxesKeyInput(const KeyEvent& rKEvt, bool* bOutHandled) |
2030 | 0 | { |
2031 | 0 | *bOutHandled = false; |
2032 | |
|
2033 | 0 | rtl::Reference<SdrTextObj> pTextObj = mxWeakTextEditObj.get(); |
2034 | 0 | if (!pTextObj) |
2035 | 0 | return nullptr; |
2036 | | |
2037 | 0 | if (!pTextObj->GetNextLinkInChain() && !pTextObj->GetPrevLinkInChain()) |
2038 | 0 | return nullptr; |
2039 | | |
2040 | 0 | std::unique_ptr<TextChainCursorManager> pCursorManager( |
2041 | 0 | new TextChainCursorManager(this, pTextObj.get())); |
2042 | 0 | if (pCursorManager->HandleKeyEvent(rKEvt)) |
2043 | 0 | { |
2044 | | // Possibly do other stuff here if necessary... |
2045 | | // XXX: Careful with the checks below (in KeyInput) for pWin and co. You should do them here I guess. |
2046 | 0 | *bOutHandled = true; |
2047 | 0 | } |
2048 | |
|
2049 | 0 | return pCursorManager; |
2050 | 0 | } |
2051 | | |
2052 | | bool SdrObjEditView::KeyInput(const KeyEvent& rKEvt, vcl::Window* pWin) |
2053 | 0 | { |
2054 | 0 | if (mpTextEditOutlinerView) |
2055 | 0 | { |
2056 | | /* Start special handling of keys within a chain */ |
2057 | | // We possibly move to another box before any handling |
2058 | 0 | bool bHandled = false; |
2059 | 0 | std::unique_ptr<TextChainCursorManager> xCursorManager( |
2060 | 0 | ImpHandleMotionThroughBoxesKeyInput(rKEvt, &bHandled)); |
2061 | 0 | if (bHandled) |
2062 | 0 | return true; |
2063 | | /* End special handling of keys within a chain */ |
2064 | | |
2065 | 0 | if (mpTextEditOutlinerView->PostKeyEvent(rKEvt, pWin)) |
2066 | 0 | { |
2067 | 0 | if (mpTextEditOutliner && mpTextEditOutliner->IsModified()) |
2068 | 0 | { |
2069 | 0 | GetModel().SetChanged(); |
2070 | 0 | SetInnerTextAreaForLOKit(); |
2071 | 0 | } |
2072 | | |
2073 | | /* Start chaining processing */ |
2074 | 0 | ImpChainingEventHdl(); |
2075 | 0 | ImpMoveCursorAfterChainingEvent(xCursorManager.get()); |
2076 | | /* End chaining processing */ |
2077 | |
|
2078 | 0 | if (pWin != nullptr && pWin != mpTextEditWin) |
2079 | 0 | SetTextEditWin(pWin); |
2080 | 0 | ImpMakeTextCursorAreaVisible(); |
2081 | 0 | return true; |
2082 | 0 | } |
2083 | 0 | } |
2084 | 0 | return SdrGlueEditView::KeyInput(rKEvt, pWin); |
2085 | 0 | } |
2086 | | |
2087 | | bool SdrObjEditView::MouseButtonDown(const MouseEvent& rMEvt, OutputDevice* pWin) |
2088 | 0 | { |
2089 | 0 | if (mpTextEditOutlinerView != nullptr) |
2090 | 0 | { |
2091 | 0 | bool bPostIt = mpTextEditOutliner->IsInSelectionMode(); |
2092 | 0 | if (!bPostIt) |
2093 | 0 | { |
2094 | 0 | Point aPt(rMEvt.GetPosPixel()); |
2095 | 0 | if (pWin != nullptr) |
2096 | 0 | aPt = pWin->PixelToLogic(aPt); |
2097 | 0 | else if (mpTextEditWin != nullptr) |
2098 | 0 | aPt = mpTextEditWin->PixelToLogic(aPt); |
2099 | 0 | bPostIt = IsTextEditHit(aPt); |
2100 | 0 | } |
2101 | 0 | if (bPostIt) |
2102 | 0 | { |
2103 | 0 | Point aPixPos(rMEvt.GetPosPixel()); |
2104 | 0 | if (pWin) |
2105 | 0 | { |
2106 | 0 | tools::Rectangle aR(pWin->LogicToPixel(mpTextEditOutlinerView->GetOutputArea())); |
2107 | 0 | if (aPixPos.X() < aR.Left()) |
2108 | 0 | aPixPos.setX(aR.Left()); |
2109 | 0 | if (aPixPos.X() > aR.Right()) |
2110 | 0 | aPixPos.setX(aR.Right()); |
2111 | 0 | if (aPixPos.Y() < aR.Top()) |
2112 | 0 | aPixPos.setY(aR.Top()); |
2113 | 0 | if (aPixPos.Y() > aR.Bottom()) |
2114 | 0 | aPixPos.setY(aR.Bottom()); |
2115 | 0 | } |
2116 | 0 | MouseEvent aMEvt(aPixPos, rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(), |
2117 | 0 | rMEvt.GetModifier()); |
2118 | 0 | if (mpTextEditOutlinerView->MouseButtonDown(aMEvt)) |
2119 | 0 | { |
2120 | 0 | if (pWin != nullptr && pWin != mpTextEditWin->GetOutDev() |
2121 | 0 | && pWin->GetOutDevType() == OUTDEV_WINDOW) |
2122 | 0 | SetTextEditWin(pWin->GetOwnerWindow()); |
2123 | 0 | ImpMakeTextCursorAreaVisible(); |
2124 | 0 | return true; |
2125 | 0 | } |
2126 | 0 | } |
2127 | 0 | } |
2128 | 0 | return SdrGlueEditView::MouseButtonDown(rMEvt, pWin); |
2129 | 0 | } |
2130 | | |
2131 | | bool SdrObjEditView::MouseButtonUp(const MouseEvent& rMEvt, OutputDevice* pWin) |
2132 | 0 | { |
2133 | 0 | if (mpTextEditOutlinerView != nullptr) |
2134 | 0 | { |
2135 | 0 | bool bPostIt = mpTextEditOutliner->IsInSelectionMode(); |
2136 | 0 | if (!bPostIt) |
2137 | 0 | { |
2138 | 0 | Point aPt(rMEvt.GetPosPixel()); |
2139 | 0 | if (pWin != nullptr) |
2140 | 0 | aPt = pWin->PixelToLogic(aPt); |
2141 | 0 | else if (mpTextEditWin != nullptr) |
2142 | 0 | aPt = mpTextEditWin->PixelToLogic(aPt); |
2143 | 0 | bPostIt = IsTextEditHit(aPt); |
2144 | 0 | } |
2145 | 0 | if (bPostIt && pWin) |
2146 | 0 | { |
2147 | 0 | Point aPixPos(rMEvt.GetPosPixel()); |
2148 | 0 | tools::Rectangle aR(pWin->LogicToPixel(mpTextEditOutlinerView->GetOutputArea())); |
2149 | 0 | if (aPixPos.X() < aR.Left()) |
2150 | 0 | aPixPos.setX(aR.Left()); |
2151 | 0 | if (aPixPos.X() > aR.Right()) |
2152 | 0 | aPixPos.setX(aR.Right()); |
2153 | 0 | if (aPixPos.Y() < aR.Top()) |
2154 | 0 | aPixPos.setY(aR.Top()); |
2155 | 0 | if (aPixPos.Y() > aR.Bottom()) |
2156 | 0 | aPixPos.setY(aR.Bottom()); |
2157 | 0 | MouseEvent aMEvt(aPixPos, rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(), |
2158 | 0 | rMEvt.GetModifier()); |
2159 | 0 | if (mpTextEditOutlinerView->MouseButtonUp(aMEvt)) |
2160 | 0 | { |
2161 | 0 | ImpMakeTextCursorAreaVisible(); |
2162 | 0 | return true; |
2163 | 0 | } |
2164 | 0 | } |
2165 | 0 | } |
2166 | 0 | return SdrGlueEditView::MouseButtonUp(rMEvt, pWin); |
2167 | 0 | } |
2168 | | |
2169 | | bool SdrObjEditView::MouseMove(const MouseEvent& rMEvt, OutputDevice* pWin) |
2170 | 0 | { |
2171 | 0 | if (mpTextEditOutlinerView != nullptr) |
2172 | 0 | { |
2173 | 0 | bool bSelMode = mpTextEditOutliner->IsInSelectionMode(); |
2174 | 0 | bool bPostIt = bSelMode; |
2175 | 0 | if (!bPostIt) |
2176 | 0 | { |
2177 | 0 | Point aPt(rMEvt.GetPosPixel()); |
2178 | 0 | if (pWin) |
2179 | 0 | aPt = pWin->PixelToLogic(aPt); |
2180 | 0 | else if (mpTextEditWin) |
2181 | 0 | aPt = mpTextEditWin->PixelToLogic(aPt); |
2182 | 0 | bPostIt = IsTextEditHit(aPt); |
2183 | 0 | } |
2184 | 0 | if (bPostIt) |
2185 | 0 | { |
2186 | 0 | Point aPixPos(rMEvt.GetPosPixel()); |
2187 | 0 | tools::Rectangle aR(mpTextEditOutlinerView->GetOutputArea()); |
2188 | 0 | if (pWin) |
2189 | 0 | aR = pWin->LogicToPixel(aR); |
2190 | 0 | else if (mpTextEditWin) |
2191 | 0 | aR = mpTextEditWin->LogicToPixel(aR); |
2192 | 0 | if (aPixPos.X() < aR.Left()) |
2193 | 0 | aPixPos.setX(aR.Left()); |
2194 | 0 | if (aPixPos.X() > aR.Right()) |
2195 | 0 | aPixPos.setX(aR.Right()); |
2196 | 0 | if (aPixPos.Y() < aR.Top()) |
2197 | 0 | aPixPos.setY(aR.Top()); |
2198 | 0 | if (aPixPos.Y() > aR.Bottom()) |
2199 | 0 | aPixPos.setY(aR.Bottom()); |
2200 | 0 | MouseEvent aMEvt(aPixPos, rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(), |
2201 | 0 | rMEvt.GetModifier()); |
2202 | 0 | if (mpTextEditOutlinerView->MouseMove(aMEvt) && bSelMode) |
2203 | 0 | { |
2204 | 0 | ImpMakeTextCursorAreaVisible(); |
2205 | 0 | return true; |
2206 | 0 | } |
2207 | 0 | } |
2208 | 0 | } |
2209 | 0 | return SdrGlueEditView::MouseMove(rMEvt, pWin); |
2210 | 0 | } |
2211 | | |
2212 | | bool SdrObjEditView::Command(const CommandEvent& rCEvt, vcl::Window* pWin) |
2213 | 0 | { |
2214 | | // as long as OutlinerView returns a sal_Bool, it only gets CommandEventId::StartDrag |
2215 | 0 | if (mpTextEditOutlinerView != nullptr) |
2216 | 0 | { |
2217 | 0 | if (rCEvt.GetCommand() == CommandEventId::StartDrag) |
2218 | 0 | { |
2219 | 0 | bool bPostIt = mpTextEditOutliner->IsInSelectionMode() || !rCEvt.IsMouseEvent(); |
2220 | 0 | if (!bPostIt && rCEvt.IsMouseEvent()) |
2221 | 0 | { |
2222 | 0 | Point aPt(rCEvt.GetMousePosPixel()); |
2223 | 0 | if (pWin != nullptr) |
2224 | 0 | aPt = pWin->PixelToLogic(aPt); |
2225 | 0 | else if (mpTextEditWin != nullptr) |
2226 | 0 | aPt = mpTextEditWin->PixelToLogic(aPt); |
2227 | 0 | bPostIt = IsTextEditHit(aPt); |
2228 | 0 | } |
2229 | 0 | if (bPostIt) |
2230 | 0 | { |
2231 | 0 | Point aPixPos(rCEvt.GetMousePosPixel()); |
2232 | 0 | if (rCEvt.IsMouseEvent() && pWin) |
2233 | 0 | { |
2234 | 0 | tools::Rectangle aR( |
2235 | 0 | pWin->LogicToPixel(mpTextEditOutlinerView->GetOutputArea())); |
2236 | 0 | if (aPixPos.X() < aR.Left()) |
2237 | 0 | aPixPos.setX(aR.Left()); |
2238 | 0 | if (aPixPos.X() > aR.Right()) |
2239 | 0 | aPixPos.setX(aR.Right()); |
2240 | 0 | if (aPixPos.Y() < aR.Top()) |
2241 | 0 | aPixPos.setY(aR.Top()); |
2242 | 0 | if (aPixPos.Y() > aR.Bottom()) |
2243 | 0 | aPixPos.setY(aR.Bottom()); |
2244 | 0 | } |
2245 | 0 | CommandEvent aCEvt(aPixPos, rCEvt.GetCommand(), rCEvt.IsMouseEvent()); |
2246 | | // Command is void at the OutlinerView, sadly |
2247 | 0 | mpTextEditOutlinerView->Command(aCEvt); |
2248 | 0 | if (pWin != nullptr && pWin != mpTextEditWin) |
2249 | 0 | SetTextEditWin(pWin); |
2250 | 0 | ImpMakeTextCursorAreaVisible(); |
2251 | 0 | return true; |
2252 | 0 | } |
2253 | 0 | } |
2254 | 0 | else |
2255 | 0 | { |
2256 | 0 | mpTextEditOutlinerView->Command(rCEvt); |
2257 | 0 | if (comphelper::LibreOfficeKit::isActive()) |
2258 | 0 | { |
2259 | | // It could execute CommandEventId::ExtTextInput, while SdrObjEditView::KeyInput |
2260 | | // isn't called |
2261 | 0 | if (mpTextEditOutliner && mpTextEditOutliner->IsModified()) |
2262 | 0 | { |
2263 | 0 | GetModel().SetChanged(); |
2264 | 0 | SetInnerTextAreaForLOKit(); |
2265 | 0 | } |
2266 | 0 | } |
2267 | 0 | return true; |
2268 | 0 | } |
2269 | 0 | } |
2270 | 0 | return SdrGlueEditView::Command(rCEvt, pWin); |
2271 | 0 | } |
2272 | | |
2273 | | bool SdrObjEditView::ImpIsTextEditAllSelected() const |
2274 | 0 | { |
2275 | 0 | bool bRet = false; |
2276 | 0 | if (mpTextEditOutliner != nullptr && mpTextEditOutlinerView != nullptr) |
2277 | 0 | { |
2278 | 0 | if (SdrTextObj::HasTextImpl(mpTextEditOutliner.get())) |
2279 | 0 | { |
2280 | 0 | const sal_Int32 nParaCnt = mpTextEditOutliner->GetParagraphCount(); |
2281 | 0 | Paragraph* pLastPara |
2282 | 0 | = mpTextEditOutliner->GetParagraph(nParaCnt > 1 ? nParaCnt - 1 : 0); |
2283 | |
|
2284 | 0 | ESelection aESel(mpTextEditOutlinerView->GetSelection()); |
2285 | 0 | if (aESel.start.nPara == 0 && aESel.start.nIndex == 0 |
2286 | 0 | && aESel.end.nPara == (nParaCnt - 1)) |
2287 | 0 | { |
2288 | 0 | if (mpTextEditOutliner->GetText(pLastPara).getLength() == aESel.end.nIndex) |
2289 | 0 | bRet = true; |
2290 | 0 | } |
2291 | | // in case the selection was done backwards |
2292 | 0 | if (!bRet && aESel.end.nPara == 0 && aESel.end.nIndex == 0 |
2293 | 0 | && aESel.start.nPara == (nParaCnt - 1)) |
2294 | 0 | { |
2295 | 0 | if (mpTextEditOutliner->GetText(pLastPara).getLength() == aESel.start.nIndex) |
2296 | 0 | bRet = true; |
2297 | 0 | } |
2298 | 0 | } |
2299 | 0 | else |
2300 | 0 | { |
2301 | 0 | bRet = true; |
2302 | 0 | } |
2303 | 0 | } |
2304 | 0 | return bRet; |
2305 | 0 | } |
2306 | | |
2307 | | void SdrObjEditView::ImpMakeTextCursorAreaVisible() |
2308 | 0 | { |
2309 | 0 | if (mpTextEditOutlinerView != nullptr && mpTextEditWin != nullptr) |
2310 | 0 | { |
2311 | 0 | vcl::Cursor* pCsr = mpTextEditWin->GetCursor(); |
2312 | 0 | if (pCsr != nullptr) |
2313 | 0 | { |
2314 | 0 | Size aSiz(pCsr->GetSize()); |
2315 | 0 | if (!aSiz.IsEmpty()) |
2316 | 0 | { |
2317 | 0 | MakeVisible(tools::Rectangle(pCsr->GetPos(), aSiz), *mpTextEditWin); |
2318 | 0 | } |
2319 | 0 | } |
2320 | 0 | } |
2321 | 0 | } |
2322 | | |
2323 | | SvtScriptType SdrObjEditView::GetScriptType() const |
2324 | 0 | { |
2325 | 0 | SvtScriptType nScriptType = SvtScriptType::NONE; |
2326 | |
|
2327 | 0 | if (IsTextEdit()) |
2328 | 0 | { |
2329 | 0 | auto pText = mxWeakTextEditObj.get(); |
2330 | 0 | if (pText->GetOutlinerParaObject()) |
2331 | 0 | nScriptType = pText->GetOutlinerParaObject()->GetTextObject().GetScriptType(); |
2332 | |
|
2333 | 0 | if (mpTextEditOutlinerView) |
2334 | 0 | nScriptType = mpTextEditOutlinerView->GetSelectedScriptType(); |
2335 | 0 | } |
2336 | 0 | else |
2337 | 0 | { |
2338 | 0 | const SdrMarkList& rMarkList = GetMarkedObjectList(); |
2339 | 0 | const size_t nMarkCount(rMarkList.GetMarkCount()); |
2340 | |
|
2341 | 0 | for (size_t i = 0; i < nMarkCount; ++i) |
2342 | 0 | { |
2343 | 0 | OutlinerParaObject* pParaObj |
2344 | 0 | = rMarkList.GetMark(i)->GetMarkedSdrObj()->GetOutlinerParaObject(); |
2345 | |
|
2346 | 0 | if (pParaObj) |
2347 | 0 | { |
2348 | 0 | nScriptType |= pParaObj->GetTextObject().GetScriptType(); |
2349 | 0 | } |
2350 | 0 | } |
2351 | 0 | } |
2352 | |
|
2353 | 0 | if (nScriptType == SvtScriptType::NONE) |
2354 | 0 | nScriptType = SvtScriptType::LATIN; |
2355 | |
|
2356 | 0 | return nScriptType; |
2357 | 0 | } |
2358 | | |
2359 | | void SdrObjEditView::GetAttributes(SfxItemSet& rTargetSet, bool bOnlyHardAttr) const |
2360 | 0 | { |
2361 | 0 | if (mxSelectionController.is()) |
2362 | 0 | if (mxSelectionController->GetAttributes(rTargetSet, bOnlyHardAttr)) |
2363 | 0 | return; |
2364 | | |
2365 | 0 | if (IsTextEdit()) |
2366 | 0 | { |
2367 | 0 | DBG_ASSERT(mpTextEditOutlinerView != nullptr, |
2368 | 0 | "SdrObjEditView::GetAttributes(): mpTextEditOutlinerView=NULL"); |
2369 | 0 | DBG_ASSERT(mpTextEditOutliner != nullptr, |
2370 | 0 | "SdrObjEditView::GetAttributes(): mpTextEditOutliner=NULL"); |
2371 | |
|
2372 | 0 | auto pText = mxWeakTextEditObj.get(); |
2373 | | // take care of bOnlyHardAttr(!) |
2374 | 0 | if (!bOnlyHardAttr && pText->GetStyleSheet()) |
2375 | 0 | rTargetSet.Put(pText->GetStyleSheet()->GetItemSet()); |
2376 | | |
2377 | | // add object attributes |
2378 | 0 | rTargetSet.Put(pText->GetMergedItemSet()); |
2379 | |
|
2380 | 0 | if (mpTextEditOutlinerView) |
2381 | 0 | { |
2382 | | // FALSE= regard InvalidItems as "holes," not as Default |
2383 | 0 | rTargetSet.Put(mpTextEditOutlinerView->GetAttribs(), false); |
2384 | 0 | } |
2385 | |
|
2386 | 0 | const SdrMarkList& rMarkList = GetMarkedObjectList(); |
2387 | 0 | if (rMarkList.GetMarkCount() == 1 && rMarkList.GetMark(0)->GetMarkedSdrObj() == pText.get()) |
2388 | 0 | { |
2389 | 0 | MergeNotPersistAttrFromMarked(rTargetSet); |
2390 | 0 | } |
2391 | 0 | } |
2392 | 0 | else |
2393 | 0 | { |
2394 | 0 | SdrGlueEditView::GetAttributes(rTargetSet, bOnlyHardAttr); |
2395 | 0 | } |
2396 | 0 | } |
2397 | | |
2398 | | bool SdrObjEditView::SetAttributes(const SfxItemSet& rSet, bool bReplaceAll) |
2399 | 0 | { |
2400 | 0 | bool bRet = false; |
2401 | 0 | rtl::Reference<SdrTextObj> pTextEditObj = mxWeakTextEditObj.get(); |
2402 | 0 | bool bTextEdit = mpTextEditOutlinerView != nullptr && pTextEditObj != nullptr; |
2403 | 0 | bool bAllTextSelected = ImpIsTextEditAllSelected(); |
2404 | 0 | const SfxItemSet* pSet = &rSet; |
2405 | |
|
2406 | 0 | if (!bTextEdit) |
2407 | 0 | { |
2408 | | // no TextEdit active -> all Items to drawing object |
2409 | 0 | if (mxSelectionController.is()) |
2410 | 0 | bRet = mxSelectionController->SetAttributes(*pSet, bReplaceAll); |
2411 | |
|
2412 | 0 | if (!bRet) |
2413 | 0 | { |
2414 | 0 | SdrGlueEditView::SetAttributes(*pSet, bReplaceAll); |
2415 | 0 | bRet = true; |
2416 | 0 | } |
2417 | 0 | } |
2418 | 0 | else |
2419 | 0 | { |
2420 | | #ifdef DBG_UTIL |
2421 | | { |
2422 | | bool bHasEEFeatureItems = false; |
2423 | | SfxItemIter aIter(rSet); |
2424 | | for (const SfxPoolItem* pItem = aIter.GetCurItem(); !bHasEEFeatureItems && pItem; |
2425 | | pItem = aIter.NextItem()) |
2426 | | { |
2427 | | if (!IsInvalidItem(pItem)) |
2428 | | { |
2429 | | sal_uInt16 nW = pItem->Which(); |
2430 | | if (nW >= EE_FEATURE_START && nW <= EE_FEATURE_END) |
2431 | | bHasEEFeatureItems = true; |
2432 | | } |
2433 | | } |
2434 | | |
2435 | | if (bHasEEFeatureItems) |
2436 | | { |
2437 | | std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog( |
2438 | | nullptr, VclMessageType::Info, VclButtonsType::Ok, |
2439 | | u"SdrObjEditView::SetAttributes(): Setting EE_FEATURE items " |
2440 | | "at the SdrView does not make sense! It only leads to " |
2441 | | "overhead and unreadable documents."_ustr)); |
2442 | | xInfoBox->run(); |
2443 | | } |
2444 | | } |
2445 | | #endif |
2446 | |
|
2447 | 0 | bool bOnlyEEItems; |
2448 | 0 | bool bNoEEItems = !SearchOutlinerItems(*pSet, bReplaceAll, &bOnlyEEItems); |
2449 | | // everything selected? -> attributes to the border, too |
2450 | | // if no EEItems, attributes to the border only |
2451 | 0 | if (bAllTextSelected || bNoEEItems) |
2452 | 0 | { |
2453 | 0 | if (mxSelectionController.is()) |
2454 | 0 | bRet = mxSelectionController->SetAttributes(*pSet, bReplaceAll); |
2455 | |
|
2456 | 0 | if (!bRet) |
2457 | 0 | { |
2458 | 0 | const bool bUndo = IsUndoEnabled(); |
2459 | |
|
2460 | 0 | if (bUndo) |
2461 | 0 | { |
2462 | 0 | BegUndo(ImpGetDescriptionString(STR_EditSetAttributes)); |
2463 | 0 | AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pTextEditObj)); |
2464 | | |
2465 | | // If this is a text object also rescue the OutlinerParaObject since |
2466 | | // applying attributes to the object may change text layout when |
2467 | | // multiple portions exist with multiple formats. If an OutlinerParaObject |
2468 | | // really exists and needs to be rescued is evaluated in the undo |
2469 | | // implementation itself. |
2470 | 0 | bool bRescueText(pTextEditObj); |
2471 | |
|
2472 | 0 | AddUndo(GetModel().GetSdrUndoFactory().CreateUndoAttrObject( |
2473 | 0 | *pTextEditObj, false, !bNoEEItems || bRescueText)); |
2474 | 0 | EndUndo(); |
2475 | 0 | } |
2476 | |
|
2477 | 0 | pTextEditObj->SetMergedItemSetAndBroadcast(*pSet, bReplaceAll); |
2478 | |
|
2479 | 0 | FlushComeBackTimer(); // to set ModeHasChanged immediately |
2480 | 0 | } |
2481 | 0 | } |
2482 | 0 | else if (!bOnlyEEItems) |
2483 | 0 | { |
2484 | | // Otherwise split Set, if necessary. |
2485 | | // Now we build an ItemSet aSet that doesn't contain EE_Items from |
2486 | | // *pSet (otherwise it would be a copy). |
2487 | 0 | WhichRangesContainer pNewWhichTable |
2488 | 0 | = RemoveWhichRange(pSet->GetRanges(), EE_ITEMS_START, EE_ITEMS_END); |
2489 | 0 | SfxItemSet aSet(GetModel().GetItemPool(), std::move(pNewWhichTable)); |
2490 | 0 | SfxWhichIter aIter(aSet); |
2491 | 0 | sal_uInt16 nWhich = aIter.FirstWhich(); |
2492 | 0 | while (nWhich != 0) |
2493 | 0 | { |
2494 | 0 | const SfxPoolItem* pItem; |
2495 | 0 | SfxItemState eState = pSet->GetItemState(nWhich, false, &pItem); |
2496 | 0 | if (eState == SfxItemState::SET) |
2497 | 0 | aSet.Put(*pItem); |
2498 | 0 | nWhich = aIter.NextWhich(); |
2499 | 0 | } |
2500 | |
|
2501 | 0 | if (mxSelectionController.is()) |
2502 | 0 | bRet = mxSelectionController->SetAttributes(aSet, bReplaceAll); |
2503 | |
|
2504 | 0 | if (!bRet) |
2505 | 0 | { |
2506 | 0 | if (IsUndoEnabled()) |
2507 | 0 | { |
2508 | 0 | BegUndo(ImpGetDescriptionString(STR_EditSetAttributes)); |
2509 | 0 | AddUndo(GetModel().GetSdrUndoFactory().CreateUndoGeoObject(*pTextEditObj)); |
2510 | 0 | AddUndo(GetModel().GetSdrUndoFactory().CreateUndoAttrObject(*pTextEditObj)); |
2511 | 0 | EndUndo(); |
2512 | 0 | } |
2513 | |
|
2514 | 0 | pTextEditObj->SetMergedItemSetAndBroadcast(aSet, bReplaceAll); |
2515 | |
|
2516 | 0 | const SdrMarkList& rMarkList = GetMarkedObjectList(); |
2517 | 0 | if (rMarkList.GetMarkCount() == 1 |
2518 | 0 | && rMarkList.GetMark(0)->GetMarkedSdrObj() == pTextEditObj.get()) |
2519 | 0 | { |
2520 | 0 | SetNotPersistAttrToMarked(aSet); |
2521 | 0 | } |
2522 | 0 | } |
2523 | 0 | FlushComeBackTimer(); |
2524 | 0 | } |
2525 | 0 | if (!bNoEEItems) |
2526 | 0 | { |
2527 | | // and now the attributes to the EditEngine |
2528 | 0 | if (bReplaceAll) |
2529 | 0 | { |
2530 | 0 | mpTextEditOutlinerView->RemoveAttribs(true); |
2531 | 0 | } |
2532 | 0 | mpTextEditOutlinerView->SetAttribs(rSet); |
2533 | |
|
2534 | 0 | const Outliner& rTEOutliner = mpTextEditOutlinerView->GetOutliner(); |
2535 | 0 | if (rTEOutliner.IsModified()) |
2536 | 0 | { |
2537 | 0 | GetModel().SetChanged(); |
2538 | 0 | SetInnerTextAreaForLOKit(); |
2539 | 0 | } |
2540 | |
|
2541 | 0 | ImpMakeTextCursorAreaVisible(); |
2542 | 0 | } |
2543 | 0 | bRet = true; |
2544 | 0 | } |
2545 | 0 | return bRet; |
2546 | 0 | } |
2547 | | |
2548 | | SfxStyleSheet* SdrObjEditView::GetStyleSheet() const |
2549 | 0 | { |
2550 | 0 | SfxStyleSheet* pSheet = nullptr; |
2551 | |
|
2552 | 0 | if (mxSelectionController.is()) |
2553 | 0 | { |
2554 | 0 | if (mxSelectionController->GetStyleSheet(pSheet)) |
2555 | 0 | return pSheet; |
2556 | 0 | } |
2557 | | |
2558 | 0 | if (mpTextEditOutlinerView) |
2559 | 0 | { |
2560 | 0 | pSheet = mpTextEditOutlinerView->GetStyleSheet(); |
2561 | 0 | } |
2562 | 0 | else |
2563 | 0 | { |
2564 | 0 | pSheet = SdrGlueEditView::GetStyleSheet(); |
2565 | 0 | } |
2566 | 0 | return pSheet; |
2567 | 0 | } |
2568 | | |
2569 | | void SdrObjEditView::SetStyleSheet(SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr) |
2570 | 0 | { |
2571 | 0 | if (mxSelectionController.is()) |
2572 | 0 | { |
2573 | 0 | if (mxSelectionController->SetStyleSheet(pStyleSheet, bDontRemoveHardAttr)) |
2574 | 0 | return; |
2575 | 0 | } |
2576 | | |
2577 | | // if we are currently in edit mode we must also set the stylesheet |
2578 | | // on all paragraphs in the Outliner for the edit view |
2579 | 0 | if (nullptr != mpTextEditOutlinerView) |
2580 | 0 | { |
2581 | 0 | Outliner& rOutliner = mpTextEditOutlinerView->GetOutliner(); |
2582 | |
|
2583 | 0 | const sal_Int32 nParaCount = rOutliner.GetParagraphCount(); |
2584 | 0 | for (sal_Int32 nPara = 0; nPara < nParaCount; nPara++) |
2585 | 0 | rOutliner.SetStyleSheet(nPara, pStyleSheet); |
2586 | 0 | } |
2587 | |
|
2588 | 0 | SdrGlueEditView::SetStyleSheet(pStyleSheet, bDontRemoveHardAttr); |
2589 | 0 | } |
2590 | | |
2591 | | void SdrObjEditView::AddDeviceToPaintView(OutputDevice& rNewDev, vcl::Window* pWindow) |
2592 | 0 | { |
2593 | 0 | SdrGlueEditView::AddDeviceToPaintView(rNewDev, pWindow); |
2594 | |
|
2595 | 0 | if (mxWeakTextEditObj.get() && !mbTextEditOnlyOneView |
2596 | 0 | && rNewDev.GetOutDevType() == OUTDEV_WINDOW) |
2597 | 0 | { |
2598 | 0 | OutlinerView* pOutlView = ImpMakeOutlinerView(rNewDev.GetOwnerWindow(), nullptr); |
2599 | 0 | mpTextEditOutliner->InsertView(pOutlView); |
2600 | 0 | } |
2601 | 0 | } |
2602 | | |
2603 | | void SdrObjEditView::DeleteDeviceFromPaintView(OutputDevice& rOldDev) |
2604 | 0 | { |
2605 | 0 | SdrGlueEditView::DeleteDeviceFromPaintView(rOldDev); |
2606 | |
|
2607 | 0 | if (mxWeakTextEditObj.get() && !mbTextEditOnlyOneView |
2608 | 0 | && rOldDev.GetOutDevType() == OUTDEV_WINDOW) |
2609 | 0 | { |
2610 | 0 | for (size_t i = mpTextEditOutliner->GetViewCount(); i > 0;) |
2611 | 0 | { |
2612 | 0 | i--; |
2613 | 0 | OutlinerView* pOLV = mpTextEditOutliner->GetView(i); |
2614 | 0 | if (pOLV && pOLV->GetWindow() == rOldDev.GetOwnerWindow()) |
2615 | 0 | { |
2616 | 0 | mpTextEditOutliner->RemoveView(i); |
2617 | 0 | } |
2618 | 0 | } |
2619 | 0 | } |
2620 | |
|
2621 | 0 | lcl_RemoveTextEditOutlinerViews(this, GetSdrPageView(), &rOldDev); |
2622 | 0 | } |
2623 | | |
2624 | | bool SdrObjEditView::IsTextEditInSelectionMode() const |
2625 | 0 | { |
2626 | 0 | return mpTextEditOutliner != nullptr && mpTextEditOutliner->IsInSelectionMode(); |
2627 | 0 | } |
2628 | | |
2629 | | // MacroMode |
2630 | | |
2631 | | void SdrObjEditView::BegMacroObj(const Point& rPnt, short nTol, SdrObject* pObj, SdrPageView* pPV, |
2632 | | vcl::Window* pWin) |
2633 | 0 | { |
2634 | 0 | BrkMacroObj(); |
2635 | 0 | if (pObj != nullptr && pPV != nullptr && pWin != nullptr && pObj->HasMacro()) |
2636 | 0 | { |
2637 | 0 | nTol = ImpGetHitTolLogic(nTol, nullptr); |
2638 | 0 | m_pMacroObj = pObj; |
2639 | 0 | m_pMacroPV = pPV; |
2640 | 0 | m_pMacroWin = pWin; |
2641 | 0 | mbMacroDown = false; |
2642 | 0 | m_nMacroTol = sal_uInt16(nTol); |
2643 | 0 | m_aMacroDownPos = rPnt; |
2644 | 0 | MovMacroObj(rPnt); |
2645 | 0 | } |
2646 | 0 | } |
2647 | | |
2648 | | void SdrObjEditView::ImpMacroUp(const Point& rUpPos) |
2649 | 0 | { |
2650 | 0 | if (m_pMacroObj != nullptr && mbMacroDown) |
2651 | 0 | { |
2652 | 0 | SdrObjMacroHitRec aHitRec; |
2653 | 0 | aHitRec.aPos = rUpPos; |
2654 | 0 | aHitRec.nTol = m_nMacroTol; |
2655 | 0 | aHitRec.pVisiLayer = &m_pMacroPV->GetVisibleLayers(); |
2656 | 0 | aHitRec.pPageView = m_pMacroPV; |
2657 | 0 | m_pMacroObj->PaintMacro(*m_pMacroWin->GetOutDev(), tools::Rectangle(), aHitRec); |
2658 | 0 | mbMacroDown = false; |
2659 | 0 | } |
2660 | 0 | } |
2661 | | |
2662 | | void SdrObjEditView::ImpMacroDown(const Point& rDownPos) |
2663 | 0 | { |
2664 | 0 | if (m_pMacroObj != nullptr && !mbMacroDown) |
2665 | 0 | { |
2666 | 0 | SdrObjMacroHitRec aHitRec; |
2667 | 0 | aHitRec.aPos = rDownPos; |
2668 | 0 | aHitRec.nTol = m_nMacroTol; |
2669 | 0 | aHitRec.pVisiLayer = &m_pMacroPV->GetVisibleLayers(); |
2670 | 0 | aHitRec.pPageView = m_pMacroPV; |
2671 | 0 | m_pMacroObj->PaintMacro(*m_pMacroWin->GetOutDev(), tools::Rectangle(), aHitRec); |
2672 | 0 | mbMacroDown = true; |
2673 | 0 | } |
2674 | 0 | } |
2675 | | |
2676 | | void SdrObjEditView::MovMacroObj(const Point& rPnt) |
2677 | 0 | { |
2678 | 0 | if (m_pMacroObj == nullptr) |
2679 | 0 | return; |
2680 | | |
2681 | 0 | SdrObjMacroHitRec aHitRec; |
2682 | 0 | aHitRec.aPos = rPnt; |
2683 | 0 | aHitRec.nTol = m_nMacroTol; |
2684 | 0 | aHitRec.pVisiLayer = &m_pMacroPV->GetVisibleLayers(); |
2685 | 0 | aHitRec.pPageView = m_pMacroPV; |
2686 | 0 | bool bDown = m_pMacroObj->IsMacroHit(aHitRec); |
2687 | 0 | if (bDown) |
2688 | 0 | ImpMacroDown(rPnt); |
2689 | 0 | else |
2690 | 0 | ImpMacroUp(rPnt); |
2691 | 0 | } |
2692 | | |
2693 | | void SdrObjEditView::BrkMacroObj() |
2694 | 67.3k | { |
2695 | 67.3k | if (m_pMacroObj != nullptr) |
2696 | 0 | { |
2697 | 0 | ImpMacroUp(m_aMacroDownPos); |
2698 | 0 | m_pMacroObj = nullptr; |
2699 | 0 | m_pMacroPV = nullptr; |
2700 | 0 | m_pMacroWin = nullptr; |
2701 | 0 | } |
2702 | 67.3k | } |
2703 | | |
2704 | | bool SdrObjEditView::EndMacroObj() |
2705 | 0 | { |
2706 | 0 | if (m_pMacroObj != nullptr && mbMacroDown) |
2707 | 0 | { |
2708 | 0 | ImpMacroUp(m_aMacroDownPos); |
2709 | 0 | SdrObjMacroHitRec aHitRec; |
2710 | 0 | aHitRec.aPos = m_aMacroDownPos; |
2711 | 0 | aHitRec.nTol = m_nMacroTol; |
2712 | 0 | aHitRec.pVisiLayer = &m_pMacroPV->GetVisibleLayers(); |
2713 | 0 | aHitRec.pPageView = m_pMacroPV; |
2714 | 0 | bool bRet = m_pMacroObj->DoMacro(aHitRec); |
2715 | 0 | m_pMacroObj = nullptr; |
2716 | 0 | m_pMacroPV = nullptr; |
2717 | 0 | m_pMacroWin = nullptr; |
2718 | 0 | return bRet; |
2719 | 0 | } |
2720 | 0 | else |
2721 | 0 | { |
2722 | 0 | BrkMacroObj(); |
2723 | 0 | return false; |
2724 | 0 | } |
2725 | 0 | } |
2726 | | |
2727 | | /** fills the given any with a XTextCursor for the current text selection. |
2728 | | Leaves the any untouched if there currently is no text selected */ |
2729 | | void SdrObjEditView::getTextSelection(css::uno::Any& rSelection) |
2730 | 0 | { |
2731 | 0 | if (!IsTextEdit()) |
2732 | 0 | return; |
2733 | | |
2734 | 0 | OutlinerView* pOutlinerView = GetTextEditOutlinerView(); |
2735 | 0 | if (!(pOutlinerView && pOutlinerView->HasSelection())) |
2736 | 0 | return; |
2737 | | |
2738 | 0 | SdrObject* pObj = GetTextEditObject(); |
2739 | |
|
2740 | 0 | if (!pObj) |
2741 | 0 | return; |
2742 | | |
2743 | 0 | css::uno::Reference<css::text::XText> xText(pObj->getUnoShape(), css::uno::UNO_QUERY); |
2744 | 0 | if (xText.is()) |
2745 | 0 | { |
2746 | 0 | SvxUnoTextBase* pRange = comphelper::getFromUnoTunnel<SvxUnoTextBase>(xText); |
2747 | 0 | if (pRange) |
2748 | 0 | { |
2749 | 0 | rSelection <<= pRange->createTextCursorBySelection(pOutlinerView->GetSelection()); |
2750 | 0 | } |
2751 | 0 | } |
2752 | 0 | } |
2753 | | |
2754 | | /* check if we have a single selection and that single object likes |
2755 | | to handle the mouse and keyboard events itself |
2756 | | |
2757 | | TODO: the selection controller should be queried from the |
2758 | | object specific view contact. Currently this method only |
2759 | | works for tables. |
2760 | | */ |
2761 | | void SdrObjEditView::MarkListHasChanged() |
2762 | 72.9k | { |
2763 | 72.9k | SdrGlueEditView::MarkListHasChanged(); |
2764 | | |
2765 | 72.9k | if (mxSelectionController.is()) |
2766 | 0 | { |
2767 | 0 | mxLastSelectionController = mxSelectionController; |
2768 | 0 | mxSelectionController->onSelectionHasChanged(); |
2769 | 0 | } |
2770 | | |
2771 | 72.9k | mxSelectionController.clear(); |
2772 | | |
2773 | 72.9k | const SdrMarkList& rMarkList = GetMarkedObjectList(); |
2774 | 72.9k | if (rMarkList.GetMarkCount() != 1) |
2775 | 72.9k | return; |
2776 | | |
2777 | 0 | const SdrObject* pObj(rMarkList.GetMark(0)->GetMarkedSdrObj()); |
2778 | 0 | SdrView* pView(dynamic_cast<SdrView*>(this)); |
2779 | | |
2780 | | // check for table |
2781 | 0 | if (pObj && pView && (pObj->GetObjInventor() == SdrInventor::Default) |
2782 | 0 | && (pObj->GetObjIdentifier() == SdrObjKind::Table)) |
2783 | 0 | { |
2784 | 0 | mxSelectionController = sdr::table::CreateTableController( |
2785 | 0 | *pView, static_cast<const sdr::table::SdrTableObj&>(*pObj), mxLastSelectionController); |
2786 | |
|
2787 | 0 | if (mxSelectionController.is()) |
2788 | 0 | { |
2789 | 0 | mxLastSelectionController.clear(); |
2790 | 0 | mxSelectionController->onSelectionHasChanged(); |
2791 | 0 | } |
2792 | 0 | } |
2793 | 0 | } |
2794 | | |
2795 | | IMPL_LINK(SdrObjEditView, EndPasteOrDropHdl, PasteOrDropInfos*, pInfo, void) |
2796 | 0 | { |
2797 | 0 | OnEndPasteOrDrop(pInfo); |
2798 | 0 | } |
2799 | | |
2800 | | IMPL_LINK(SdrObjEditView, BeginPasteOrDropHdl, PasteOrDropInfos*, pInfo, void) |
2801 | 0 | { |
2802 | 0 | OnBeginPasteOrDrop(pInfo); |
2803 | 0 | } |
2804 | | |
2805 | | void SdrObjEditView::OnBeginPasteOrDrop(PasteOrDropInfos*) |
2806 | 0 | { |
2807 | | // applications can derive from these virtual methods to do something before a drop or paste operation |
2808 | 0 | } |
2809 | | |
2810 | | void SdrObjEditView::OnEndPasteOrDrop(PasteOrDropInfos*) |
2811 | 0 | { |
2812 | | // applications can derive from these virtual methods to do something before a drop or paste operation |
2813 | 0 | } |
2814 | | |
2815 | | sal_uInt16 SdrObjEditView::GetSelectionLevel() const |
2816 | 0 | { |
2817 | 0 | if (!IsTextEdit()) |
2818 | 0 | return 0xFFFF; |
2819 | 0 | DBG_ASSERT(mpTextEditOutlinerView != nullptr, |
2820 | 0 | "SdrObjEditView::GetAttributes(): mpTextEditOutlinerView=NULL"); |
2821 | 0 | DBG_ASSERT(mpTextEditOutliner != nullptr, |
2822 | 0 | "SdrObjEditView::GetAttributes(): mpTextEditOutliner=NULL"); |
2823 | 0 | if (!mpTextEditOutlinerView) |
2824 | 0 | return 0xFFFF; |
2825 | | //start and end position |
2826 | 0 | ESelection aSelect = mpTextEditOutlinerView->GetSelection(); |
2827 | 0 | sal_Int32 nStartPara = ::std::min(aSelect.start.nPara, aSelect.end.nPara); |
2828 | 0 | sal_Int32 nEndPara = ::std::max(aSelect.start.nPara, aSelect.end.nPara); |
2829 | | //get level from each paragraph |
2830 | 0 | sal_uInt16 nLevel = 0; |
2831 | 0 | for (sal_Int32 nPara = nStartPara; nPara <= nEndPara; nPara++) |
2832 | 0 | { |
2833 | 0 | sal_Int16 nDepth = mpTextEditOutliner->GetDepth(nPara); |
2834 | 0 | assert(nDepth <= 15); |
2835 | 0 | if (nDepth >= 0) |
2836 | 0 | { |
2837 | 0 | sal_uInt16 nParaDepth = 1 << static_cast<sal_uInt16>(nDepth); |
2838 | 0 | if (!(nLevel & nParaDepth)) |
2839 | 0 | nLevel += nParaDepth; |
2840 | 0 | } |
2841 | 0 | } |
2842 | | //reduce one level for Outliner Object |
2843 | | //if( nLevel > 0 && GetTextEditObject()->GetObjIdentifier() == OBJ_OUTLINETEXT ) |
2844 | | // nLevel = nLevel >> 1; |
2845 | | //no bullet paragraph selected |
2846 | 0 | if (nLevel == 0) |
2847 | 0 | nLevel = 0xFFFF; |
2848 | 0 | return nLevel; |
2849 | 0 | } |
2850 | | |
2851 | | bool SdrObjEditView::SupportsFormatPaintbrush(SdrInventor nObjectInventor, |
2852 | | SdrObjKind nObjectIdentifier) |
2853 | 0 | { |
2854 | 0 | if (nObjectInventor != SdrInventor::Default && nObjectInventor != SdrInventor::E3d) |
2855 | 0 | return false; |
2856 | 0 | switch (nObjectIdentifier) |
2857 | 0 | { |
2858 | 0 | case SdrObjKind::NONE: |
2859 | 0 | case SdrObjKind::Group: |
2860 | 0 | return false; |
2861 | 0 | case SdrObjKind::Line: |
2862 | 0 | case SdrObjKind::Rectangle: |
2863 | 0 | case SdrObjKind::CircleOrEllipse: |
2864 | 0 | case SdrObjKind::CircleSection: |
2865 | 0 | case SdrObjKind::CircleArc: |
2866 | 0 | case SdrObjKind::CircleCut: |
2867 | 0 | case SdrObjKind::Polygon: |
2868 | 0 | case SdrObjKind::PolyLine: |
2869 | 0 | case SdrObjKind::PathLine: |
2870 | 0 | case SdrObjKind::PathFill: |
2871 | 0 | case SdrObjKind::FreehandLine: |
2872 | 0 | case SdrObjKind::FreehandFill: |
2873 | 0 | case SdrObjKind::Text: |
2874 | 0 | case SdrObjKind::TitleText: |
2875 | 0 | case SdrObjKind::OutlineText: |
2876 | 0 | case SdrObjKind::Graphic: |
2877 | 0 | case SdrObjKind::OLE2: |
2878 | 0 | case SdrObjKind::Table: |
2879 | 0 | return true; |
2880 | 0 | case SdrObjKind::Caption: |
2881 | 0 | return false; |
2882 | 0 | case SdrObjKind::Edge: |
2883 | 0 | case SdrObjKind::PathPoly: |
2884 | 0 | case SdrObjKind::PathPolyLine: |
2885 | 0 | return true; |
2886 | 0 | case SdrObjKind::Page: |
2887 | 0 | case SdrObjKind::Measure: |
2888 | 0 | case SdrObjKind::OLEPluginFrame: |
2889 | 0 | case SdrObjKind::UNO: |
2890 | 0 | return false; |
2891 | 0 | case SdrObjKind::CustomShape: |
2892 | 0 | return true; |
2893 | 0 | default: |
2894 | 0 | return false; |
2895 | 0 | } |
2896 | 0 | } |
2897 | | |
2898 | | static const WhichRangesContainer& GetFormatRangeImpl(bool bTextOnly, bool withParagraphAttr = true) |
2899 | 0 | { |
2900 | 0 | static const WhichRangesContainer gFull( |
2901 | 0 | svl::Items<XATTR_LINE_FIRST, XATTR_LINE_LAST, XATTR_FILL_FIRST, XATTRSET_FILL, |
2902 | 0 | SDRATTR_SHADOW_FIRST, SDRATTR_SHADOW_LAST, SDRATTR_MISC_FIRST, |
2903 | 0 | SDRATTR_MISC_LAST, // table cell formats |
2904 | 0 | SDRATTR_GRAF_FIRST, SDRATTR_GRAF_LAST, SDRATTR_TABLE_FIRST, SDRATTR_TABLE_LAST, |
2905 | 0 | SDRATTR_GLOW_FIRST, SDRATTR_GLOW_LAST, SDRATTR_SOFTEDGE_FIRST, |
2906 | 0 | SDRATTR_SOFTEDGE_LAST, SDRATTR_GLOW_TEXT_FIRST, SDRATTR_GLOW_TEXT_LAST, |
2907 | 0 | EE_PARA_START, EE_PARA_END, EE_CHAR_START, EE_CHAR_END>); |
2908 | |
|
2909 | 0 | static const WhichRangesContainer gTextOnly( |
2910 | 0 | svl::Items<SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST, EE_CHAR_START, EE_CHAR_END>); |
2911 | |
|
2912 | 0 | static const WhichRangesContainer gParaTextOnly( |
2913 | 0 | svl::Items<SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST, EE_PARA_START, EE_PARA_END, EE_CHAR_START, |
2914 | 0 | EE_CHAR_END>); |
2915 | |
|
2916 | 0 | return bTextOnly ? withParagraphAttr ? gParaTextOnly : gTextOnly : gFull; |
2917 | 0 | } |
2918 | | |
2919 | | sal_Int32 SdrObjEditView::TakeFormatPaintBrush(std::shared_ptr<SfxItemSet>& rFormatSet) |
2920 | 0 | { |
2921 | 0 | sal_Int32 nDepth = -2; |
2922 | 0 | const SdrMarkList& rMarkList = GetMarkedObjectList(); |
2923 | 0 | if (rMarkList.GetMarkCount() <= 0) |
2924 | 0 | return nDepth; |
2925 | | |
2926 | 0 | OutlinerView* pOLV = GetTextEditOutlinerView(); |
2927 | 0 | bool isParaSelection |
2928 | 0 | = pOLV ? !pOLV->GetEditView().HasSelection() || pOLV->GetEditView().IsSelectionFullPara() |
2929 | 0 | : false; |
2930 | 0 | rFormatSet = std::make_shared<SfxItemSet>(GetModel().GetItemPool(), |
2931 | 0 | GetFormatRangeImpl(pOLV != nullptr, isParaSelection)); |
2932 | 0 | if (pOLV) |
2933 | 0 | { |
2934 | 0 | rFormatSet->Put(pOLV->GetAttribs()); |
2935 | 0 | if (isParaSelection) |
2936 | 0 | nDepth = pOLV->GetDepth(); |
2937 | 0 | } |
2938 | 0 | else |
2939 | 0 | { |
2940 | 0 | const bool bOnlyHardAttr = false; |
2941 | 0 | rFormatSet->Put(GetAttrFromMarked(bOnlyHardAttr)); |
2942 | 0 | } |
2943 | | |
2944 | | // check for cloning from table cell, in which case we need to copy cell-specific formatting attributes |
2945 | 0 | const SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); |
2946 | 0 | if (pObj && (pObj->GetObjInventor() == SdrInventor::Default) |
2947 | 0 | && (pObj->GetObjIdentifier() == SdrObjKind::Table)) |
2948 | 0 | { |
2949 | 0 | auto pTable = static_cast<const sdr::table::SdrTableObj*>(pObj); |
2950 | 0 | if (mxSelectionController.is() && pTable->getActiveCell().is()) |
2951 | 0 | { |
2952 | 0 | mxSelectionController->GetAttributes(*rFormatSet, false); |
2953 | 0 | } |
2954 | 0 | } |
2955 | 0 | return nDepth; |
2956 | 0 | } |
2957 | | |
2958 | | static SfxItemSet CreatePaintSet(const WhichRangesContainer& pRanges, SfxItemPool& rPool, |
2959 | | const SfxItemSet& rSourceSet, const SfxItemSet& rTargetSet, |
2960 | | bool bNoCharacterFormats, bool bNoParagraphFormats) |
2961 | 0 | { |
2962 | 0 | SfxItemSet aPaintSet(rPool, pRanges); |
2963 | |
|
2964 | 0 | for (const auto& pRange : pRanges) |
2965 | 0 | { |
2966 | 0 | sal_uInt16 nWhich = pRange.first; |
2967 | 0 | const sal_uInt16 nLastWhich = pRange.second; |
2968 | |
|
2969 | 0 | if (bNoCharacterFormats && (nWhich == EE_CHAR_START)) |
2970 | 0 | continue; |
2971 | | |
2972 | 0 | if (bNoParagraphFormats && (nWhich == EE_PARA_START)) |
2973 | 0 | continue; |
2974 | | |
2975 | 0 | for (; nWhich <= nLastWhich; nWhich++) |
2976 | 0 | { |
2977 | 0 | const SfxPoolItem* pSourceItem = rSourceSet.GetItem(nWhich); |
2978 | 0 | const SfxPoolItem* pTargetItem = rTargetSet.GetItem(nWhich); |
2979 | |
|
2980 | 0 | if ((pSourceItem && !pTargetItem) |
2981 | 0 | || (pSourceItem && pTargetItem && *pSourceItem != *pTargetItem)) |
2982 | 0 | { |
2983 | 0 | aPaintSet.Put(*pSourceItem); |
2984 | 0 | } |
2985 | 0 | } |
2986 | 0 | } |
2987 | 0 | return aPaintSet; |
2988 | 0 | } |
2989 | | |
2990 | | void SdrObjEditView::ApplyFormatPaintBrushToText(SfxItemSet const& rFormatSet, SdrTextObj& rTextObj, |
2991 | | SdrText* pText, sal_Int16 nDepth, |
2992 | | bool bNoCharacterFormats, bool bNoParagraphFormats) |
2993 | 0 | { |
2994 | 0 | OutlinerParaObject* pParaObj = pText ? pText->GetOutlinerParaObject() : nullptr; |
2995 | 0 | if (!pParaObj) |
2996 | 0 | return; |
2997 | | |
2998 | 0 | SdrOutliner& rOutliner = rTextObj.ImpGetDrawOutliner(); |
2999 | 0 | rOutliner.SetText(*pParaObj); |
3000 | |
|
3001 | 0 | sal_Int32 nParaCount(rOutliner.GetParagraphCount()); |
3002 | |
|
3003 | 0 | if (!nParaCount) |
3004 | 0 | return; |
3005 | | |
3006 | 0 | for (sal_Int32 nPara = 0; nPara < nParaCount; nPara++) |
3007 | 0 | { |
3008 | 0 | if (!bNoCharacterFormats) |
3009 | 0 | rOutliner.RemoveCharAttribs(nPara); |
3010 | |
|
3011 | 0 | SfxItemSet aSet(rOutliner.GetParaAttribs(nPara)); |
3012 | 0 | aSet.Put(CreatePaintSet(GetFormatRangeImpl(true), *aSet.GetPool(), rFormatSet, aSet, |
3013 | 0 | bNoCharacterFormats, bNoParagraphFormats)); |
3014 | 0 | rOutliner.SetParaAttribs(nPara, aSet); |
3015 | 0 | Paragraph* pParagraph = rOutliner.GetParagraph(nPara); |
3016 | 0 | if (nDepth > -2) |
3017 | 0 | rOutliner.SetDepth(pParagraph, nDepth); |
3018 | 0 | } |
3019 | |
|
3020 | 0 | std::optional<OutlinerParaObject> pTemp = rOutliner.CreateParaObject(0, nParaCount); |
3021 | 0 | rOutliner.Clear(); |
3022 | |
|
3023 | 0 | rTextObj.NbcSetOutlinerParaObjectForText(std::move(pTemp), pText); |
3024 | 0 | } |
3025 | | |
3026 | | void SdrObjEditView::DisposeUndoManager() |
3027 | 3.67k | { |
3028 | 3.67k | if (mpTextEditOutliner) |
3029 | 0 | { |
3030 | 0 | if (typeid(mpTextEditOutliner->GetUndoManager()) != typeid(EditUndoManager)) |
3031 | 0 | { |
3032 | | // Non-owning pointer, clear it. |
3033 | 0 | mpTextEditOutliner->SetUndoManager(nullptr); |
3034 | 0 | } |
3035 | 0 | } |
3036 | | |
3037 | 3.67k | mpOldTextEditUndoManager = nullptr; |
3038 | 3.67k | } |
3039 | | |
3040 | | void SdrObjEditView::ApplyFormatPaintBrush(SfxItemSet& rFormatSet, sal_Int16 nDepth, |
3041 | | bool bNoCharacterFormats, bool bNoParagraphFormats) |
3042 | 0 | { |
3043 | 0 | if (mxSelectionController.is() |
3044 | 0 | && mxSelectionController->ApplyFormatPaintBrush(rFormatSet, nDepth, bNoCharacterFormats, |
3045 | 0 | bNoParagraphFormats)) |
3046 | 0 | { |
3047 | 0 | return; |
3048 | 0 | } |
3049 | | |
3050 | 0 | OutlinerView* pOLV = GetTextEditOutlinerView(); |
3051 | 0 | const SdrMarkList& rMarkList = GetMarkedObjectList(); |
3052 | 0 | if (!pOLV) |
3053 | 0 | { |
3054 | 0 | SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); |
3055 | 0 | const SfxItemSet& rShapeSet = pObj->GetMergedItemSet(); |
3056 | | |
3057 | | // if not in text edit mode (aka the user selected text or clicked on a word) |
3058 | | // apply formatting attributes to selected shape |
3059 | | // All formatting items (see ranges above) that are unequal in selected shape and |
3060 | | // the format paintbrush are hard set on the selected shape. |
3061 | |
|
3062 | 0 | const WhichRangesContainer& pRanges = rFormatSet.GetRanges(); |
3063 | 0 | bool bTextOnly = true; |
3064 | |
|
3065 | 0 | for (const auto& pRange : pRanges) |
3066 | 0 | { |
3067 | 0 | if ((pRange.first != EE_PARA_START) && (pRange.first != EE_CHAR_START)) |
3068 | 0 | { |
3069 | 0 | bTextOnly = false; |
3070 | 0 | break; |
3071 | 0 | } |
3072 | 0 | } |
3073 | |
|
3074 | 0 | if (!bTextOnly) |
3075 | 0 | { |
3076 | 0 | SfxItemSet aPaintSet(CreatePaintSet(GetFormatRangeImpl(false), *rShapeSet.GetPool(), |
3077 | 0 | rFormatSet, rShapeSet, bNoCharacterFormats, |
3078 | 0 | bNoParagraphFormats)); |
3079 | 0 | SetAttrToMarked(aPaintSet, false /*bReplaceAll*/); |
3080 | 0 | } |
3081 | | |
3082 | | // now apply character and paragraph formatting to text, if the shape has any |
3083 | 0 | SdrTextObj* pTextObj = DynCastSdrTextObj(pObj); |
3084 | 0 | if (pTextObj) |
3085 | 0 | { |
3086 | 0 | sal_Int32 nText = pTextObj->getTextCount(); |
3087 | |
|
3088 | 0 | while (--nText >= 0) |
3089 | 0 | { |
3090 | 0 | SdrText* pText = pTextObj->getText(nText); |
3091 | 0 | ApplyFormatPaintBrushToText(rFormatSet, *pTextObj, pText, nDepth, |
3092 | 0 | bNoCharacterFormats, bNoParagraphFormats); |
3093 | 0 | } |
3094 | 0 | } |
3095 | 0 | } |
3096 | 0 | else |
3097 | 0 | { |
3098 | 0 | ::Outliner& rOutliner = pOLV->GetOutliner(); |
3099 | 0 | const EditEngine& rEditEngine = rOutliner.GetEditEngine(); |
3100 | |
|
3101 | 0 | ESelection aSel(pOLV->GetSelection()); |
3102 | 0 | bool fullParaSelection |
3103 | 0 | = aSel.end.nPara != aSel.start.nPara || pOLV->GetEditView().IsSelectionFullPara(); |
3104 | 0 | if (!aSel.HasRange()) |
3105 | 0 | pOLV->SetSelection(rEditEngine.GetWord(aSel, css::i18n::WordType::DICTIONARY_WORD)); |
3106 | 0 | const bool bRemoveParaAttribs = !bNoParagraphFormats && !fullParaSelection; |
3107 | 0 | pOLV->RemoveAttribsKeepLanguages(bRemoveParaAttribs); |
3108 | 0 | SfxItemSet aSet(pOLV->GetAttribs()); |
3109 | 0 | SfxItemSet aPaintSet(CreatePaintSet(GetFormatRangeImpl(true), *aSet.GetPool(), rFormatSet, |
3110 | 0 | aSet, bNoCharacterFormats, bNoParagraphFormats)); |
3111 | 0 | pOLV->SetAttribs(aPaintSet); |
3112 | 0 | if (!bNoParagraphFormats && nDepth > -2) |
3113 | 0 | { |
3114 | 0 | for (sal_Int32 nPara = aSel.start.nPara; nPara <= aSel.end.nPara; ++nPara) |
3115 | 0 | pOLV->SetDepth(nPara, nDepth); |
3116 | 0 | } |
3117 | 0 | } |
3118 | | |
3119 | | // check for cloning to table cell, in which case we need to copy cell-specific formatting attributes |
3120 | 0 | SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); |
3121 | 0 | if (pObj && (pObj->GetObjInventor() == SdrInventor::Default) |
3122 | 0 | && (pObj->GetObjIdentifier() == SdrObjKind::Table)) |
3123 | 0 | { |
3124 | 0 | auto pTable = static_cast<sdr::table::SdrTableObj*>(pObj); |
3125 | 0 | if (pTable->getActiveCell().is() && mxSelectionController.is()) |
3126 | 0 | { |
3127 | 0 | mxSelectionController->SetAttributes(rFormatSet, false); |
3128 | 0 | } |
3129 | 0 | } |
3130 | 0 | } |
3131 | | |
3132 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |