/src/libreoffice/sw/source/uibase/docvw/AnnotationWin.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 <config_wasm_strip.h> |
21 | | |
22 | | #include <AnnotationWin.hxx> |
23 | | |
24 | | #include <PostItMgr.hxx> |
25 | | |
26 | | #include <strings.hrc> |
27 | | |
28 | | #include <uiobject.hxx> |
29 | | |
30 | | #include <vcl/svapp.hxx> |
31 | | #include <vcl/uitest/logger.hxx> |
32 | | #include <vcl/uitest/eventdescription.hxx> |
33 | | #include <vcl/weld/Container.hxx> |
34 | | |
35 | | #include <svl/undo.hxx> |
36 | | #include <svtools/svparser.hxx> |
37 | | #include <unotools/localedatawrapper.hxx> |
38 | | #include <unotools/syslocale.hxx> |
39 | | #include <svl/languageoptions.hxx> |
40 | | #include <osl/diagnose.h> |
41 | | |
42 | | #include <editeng/eeitem.hxx> |
43 | | #include <editeng/postitem.hxx> |
44 | | #include <editeng/fhgtitem.hxx> |
45 | | #include <editeng/langitem.hxx> |
46 | | #include <editeng/editund2.hxx> |
47 | | |
48 | | #include <editeng/editview.hxx> |
49 | | #include <editeng/outliner.hxx> |
50 | | #include <editeng/editeng.hxx> |
51 | | #include <editeng/editobj.hxx> |
52 | | #include <editeng/outlobj.hxx> |
53 | | |
54 | | #include <comphelper/lok.hxx> |
55 | | #include <comphelper/random.hxx> |
56 | | #include <docufld.hxx> |
57 | | #include <txtfld.hxx> |
58 | | #include <ndtxt.hxx> |
59 | | #include <view.hxx> |
60 | | #include <viewopt.hxx> |
61 | | #include <wrtsh.hxx> |
62 | | #include <docsh.hxx> |
63 | | #include <doc.hxx> |
64 | | #include <IDocumentUndoRedo.hxx> |
65 | | #if ENABLE_YRS |
66 | | #include <IDocumentState.hxx> |
67 | | #endif |
68 | | #include <SwUndoField.hxx> |
69 | | #include <edtwin.hxx> |
70 | | #include "ShadowOverlayObject.hxx" |
71 | | #include "AnchorOverlayObject.hxx" |
72 | | #include "OverlayRanges.hxx" |
73 | | #include "SidebarTxtControl.hxx" |
74 | | #include "SidebarWinAcc.hxx" |
75 | | |
76 | | #include <memory> |
77 | | |
78 | | namespace{ |
79 | | |
80 | | void collectUIInformation( const OUString& aevent , const OUString& aID ) |
81 | 0 | { |
82 | 0 | EventDescription aDescription; |
83 | 0 | aDescription.aID = aID; |
84 | 0 | aDescription.aParameters = {{"" , ""}}; |
85 | 0 | aDescription.aAction = aevent; |
86 | 0 | aDescription.aParent = "MainWindow"; |
87 | 0 | aDescription.aKeyWord = "SwEditWinUIObject"; |
88 | 0 | UITestLogger::getInstance().logEvent(aDescription); |
89 | 0 | } |
90 | | |
91 | | } |
92 | | |
93 | | namespace SwPostItHelper { |
94 | | |
95 | | void ImportHTML(Outliner& rOutliner, const OUString& rHtml) |
96 | 0 | { |
97 | 0 | OString sHtmlContent(rHtml.toUtf8()); |
98 | 0 | SvMemoryStream aHTMLStream(const_cast<char*>(sHtmlContent.getStr()), |
99 | 0 | sHtmlContent.getLength(), StreamMode::READ); |
100 | 0 | SvKeyValueIteratorRef xValues(new SvKeyValueIterator); |
101 | | // Insert newlines for divs, not normally done, so to keep things simple |
102 | | // only enable that for this case. |
103 | 0 | xValues->Append(SvKeyValue("newline-on-div", "true")); |
104 | 0 | xValues->Append(SvKeyValue("content-type", "text/html;charset=utf-8")); |
105 | 0 | rOutliner.Read(aHTMLStream, "", EETextFormat::Html, xValues.get()); |
106 | 0 | } |
107 | | |
108 | | } |
109 | | |
110 | | namespace sw::annotation { |
111 | | |
112 | | // see AnnotationContents in sd for something similar |
113 | | SwAnnotationWin::SwAnnotationWin( SwEditWin& rEditWin, |
114 | | SwPostItMgr& aMgr, |
115 | | SwAnnotationItem& rSidebarItem, |
116 | | SwFormatField* aField ) |
117 | 0 | : InterimItemWindow(&rEditWin, u"modules/swriter/ui/annotation.ui"_ustr, u"Annotation"_ustr) |
118 | 0 | , mrMgr(aMgr) |
119 | 0 | , mrView(rEditWin.GetView()) |
120 | 0 | , mnDeleteEventId(nullptr) |
121 | 0 | , meSidebarPosition(sw::sidebarwindows::SidebarPosition::NONE) |
122 | 0 | , mPageBorder(0) |
123 | 0 | , mbAnchorRectChanged(false) |
124 | 0 | , mbResolvedStateUpdated(false) |
125 | 0 | , mbMouseOver(false) |
126 | 0 | , mLayoutStatus(SwPostItHelper::INVISIBLE) |
127 | 0 | , mbReadonly(false) |
128 | 0 | , mbIsFollow(false) |
129 | 0 | , mpSidebarItem(&rSidebarItem) |
130 | 0 | , mpAnchorFrame(rSidebarItem.maLayoutInfo.mpAnchorFrame) |
131 | 0 | , mpFormatField(aField) |
132 | 0 | , mpField( static_cast<SwPostItField*>(aField->GetField())) |
133 | 0 | { |
134 | 0 | set_id("Comment"+OUString::number(mpField->GetPostItId())); |
135 | |
|
136 | 0 | m_xContainer->connect_mouse_move(LINK(this, SwAnnotationWin, MouseMoveHdl)); |
137 | |
|
138 | 0 | mpShadow = sidebarwindows::ShadowOverlayObject::CreateShadowOverlayObject( mrView ); |
139 | 0 | if ( mpShadow ) |
140 | 0 | { |
141 | 0 | mpShadow->setVisible(false); |
142 | 0 | } |
143 | |
|
144 | 0 | #if !ENABLE_WASM_STRIP_ACCESSIBILITY |
145 | 0 | if (rSidebarItem.maLayoutInfo.mpAnchorFrame) |
146 | 0 | { |
147 | 0 | mrMgr.ConnectSidebarWinToFrame( *(rSidebarItem.maLayoutInfo.mpAnchorFrame), |
148 | 0 | mpSidebarItem->GetFormatField(), |
149 | 0 | *this ); |
150 | 0 | } |
151 | 0 | #endif |
152 | |
|
153 | 0 | if (SupportsDoubleBuffering()) |
154 | | // When double-buffering, allow parents to paint on our area. That's |
155 | | // necessary when parents paint the complete buffer. |
156 | 0 | SetParentClipMode(ParentClipMode::NoClip); |
157 | 0 | } Unexecuted instantiation: sw::annotation::SwAnnotationWin::SwAnnotationWin(SwEditWin&, SwPostItMgr&, SwAnnotationItem&, SwFormatField*) Unexecuted instantiation: sw::annotation::SwAnnotationWin::SwAnnotationWin(SwEditWin&, SwPostItMgr&, SwAnnotationItem&, SwFormatField*) |
158 | | |
159 | | SwAnnotationWin::~SwAnnotationWin() |
160 | 0 | { |
161 | 0 | disposeOnce(); |
162 | 0 | } |
163 | | |
164 | | void SwAnnotationWin::dispose() |
165 | 0 | { |
166 | 0 | #if !ENABLE_WASM_STRIP_ACCESSIBILITY |
167 | 0 | mrMgr.DisconnectSidebarWinFromFrame( *(mpSidebarItem->maLayoutInfo.mpAnchorFrame), |
168 | 0 | *this ); |
169 | 0 | #endif |
170 | 0 | Disable(); |
171 | |
|
172 | 0 | mxSidebarTextControlWin.reset(); |
173 | 0 | mxSidebarTextControl.reset(); |
174 | |
|
175 | 0 | mxMetadataAuthor.reset(); |
176 | 0 | mxMetadataResolved.reset(); |
177 | 0 | mxMetadataDate.reset(); |
178 | 0 | mxVScrollbar.reset(); |
179 | |
|
180 | 0 | mpAnchor.reset(); |
181 | 0 | mpShadow.reset(); |
182 | |
|
183 | 0 | mpTextRangeOverlay.reset(); |
184 | |
|
185 | 0 | mxMenuButton.reset(); |
186 | |
|
187 | 0 | if (mnDeleteEventId) |
188 | 0 | Application::RemoveUserEvent(mnDeleteEventId); |
189 | |
|
190 | 0 | mpOutliner.reset(); |
191 | 0 | mpOutlinerView.reset(); |
192 | |
|
193 | 0 | InterimItemWindow::dispose(); |
194 | 0 | } |
195 | | |
196 | | void SwAnnotationWin::SetPostItText() |
197 | 0 | { |
198 | | //If the cursor was visible, then make it visible again after |
199 | | //changing text, e.g. fdo#33599 |
200 | 0 | vcl::Cursor *pCursor = GetOutlinerView()->GetEditView().GetCursor(); |
201 | 0 | bool bCursorVisible = pCursor && pCursor->IsVisible(); |
202 | | |
203 | | //If the new text is the same as the old text, keep the same insertion |
204 | | //point .e.g. fdo#33599 |
205 | 0 | mpField = static_cast<SwPostItField*>(mpFormatField->GetField()); |
206 | 0 | OUString sNewText = mpField->GetPar2(); |
207 | 0 | bool bTextUnchanged = sNewText == mpOutliner->GetEditEngine().GetText(); |
208 | 0 | ESelection aOrigSelection(GetOutlinerView()->GetEditView().GetSelection()); |
209 | | |
210 | | // get text from SwPostItField and insert into our textview |
211 | 0 | mpOutliner->SetModifyHdl( Link<LinkParamNone*,void>() ); |
212 | 0 | mpOutliner->EnableUndo( false ); |
213 | | #if ENABLE_YRS |
214 | | auto const mode = mrView.GetDocShell()->GetDoc()->getIDocumentState().SetYrsMode(IYrsTransactionSupplier::Mode::Replay); |
215 | | #endif |
216 | 0 | if( mpField->GetTextObject() ) |
217 | 0 | mpOutliner->SetText( *mpField->GetTextObject() ); |
218 | 0 | else |
219 | 0 | { |
220 | 0 | mpOutliner->Clear(); |
221 | 0 | GetOutlinerView()->SetStyleSheet(SwResId(STR_POOLCOLL_COMMENT)); |
222 | 0 | GetOutlinerView()->InsertText(sNewText); |
223 | 0 | } |
224 | | #if ENABLE_YRS |
225 | | mrView.GetDocShell()->GetDoc()->getIDocumentState().SetYrsMode(mode); |
226 | | #endif |
227 | |
|
228 | 0 | mpOutliner->ClearModifyFlag(); |
229 | 0 | mpOutliner->GetUndoManager().Clear(); |
230 | 0 | mpOutliner->EnableUndo( true ); |
231 | 0 | mpOutliner->SetModifyHdl( LINK( this, SwAnnotationWin, ModifyHdl ) ); |
232 | 0 | if (bTextUnchanged) |
233 | 0 | GetOutlinerView()->GetEditView().SetSelection(aOrigSelection); |
234 | 0 | if (bCursorVisible) |
235 | 0 | GetOutlinerView()->ShowCursor(); |
236 | 0 | Invalidate(); |
237 | 0 | } |
238 | | |
239 | | void SwAnnotationWin::GeneratePostItName() |
240 | 0 | { |
241 | 0 | if (mpField && mpField->GetName().isEmpty()) |
242 | 0 | { |
243 | 0 | mpField->SetName(sw::mark::MarkBase::GenerateNewName(u"__Annotation__")); |
244 | 0 | } |
245 | 0 | } |
246 | | |
247 | | void SwAnnotationWin::SetResolved(bool resolved) |
248 | 0 | { |
249 | 0 | bool oldState = IsResolved(); |
250 | 0 | static_cast<SwPostItField*>(mpFormatField->GetField())->SetResolved(resolved); |
251 | 0 | if (SwWrtShell* pWrtShell = mrView.GetWrtShellPtr()) |
252 | 0 | { |
253 | 0 | const SwViewOption* pVOpt = pWrtShell->GetViewOptions(); |
254 | 0 | mpSidebarItem->mbShow = !IsResolved() || (pVOpt->IsResolvedPostIts()); |
255 | 0 | } |
256 | |
|
257 | 0 | mpTextRangeOverlay.reset(); |
258 | |
|
259 | 0 | if(IsResolved()) |
260 | 0 | mxMetadataResolved->show(); |
261 | 0 | else |
262 | 0 | mxMetadataResolved->hide(); |
263 | |
|
264 | 0 | if(IsResolved() != oldState) |
265 | 0 | { |
266 | 0 | mbResolvedStateUpdated = true; |
267 | | #if ENABLE_YRS |
268 | | // for undo, before UpdateData() |
269 | | mrView.GetDocShell()->GetDoc()->getIDocumentState().YrsNotifySetResolved( |
270 | | GetOutlinerView()->GetEditView().GetYrsCommentId(), |
271 | | *static_cast<SwPostItField const*>(mpFormatField->GetField())); |
272 | | #endif |
273 | 0 | } |
274 | 0 | UpdateData(); |
275 | 0 | Invalidate(); |
276 | 0 | collectUIInformation(u"SETRESOLVED"_ustr,get_id()); |
277 | 0 | } |
278 | | |
279 | | void SwAnnotationWin::ToggleResolved() |
280 | 0 | { |
281 | 0 | SetResolved(!IsResolved()); |
282 | 0 | } |
283 | | |
284 | | void SwAnnotationWin::ToggleResolvedForThread() |
285 | 0 | { |
286 | 0 | auto pTop = GetTopReplyNote(); |
287 | 0 | pTop->ToggleResolved(); |
288 | 0 | mrMgr.UpdateResolvedStatus(pTop); |
289 | 0 | mrMgr.LayoutPostIts(); |
290 | 0 | } |
291 | | |
292 | | sal_uInt32 SwAnnotationWin::GetParaId() |
293 | 0 | { |
294 | 0 | auto pField = static_cast<SwPostItField*>(mpFormatField->GetField()); |
295 | 0 | auto nParaId = pField->GetParaId(); |
296 | 0 | if (nParaId == 0) |
297 | 0 | { |
298 | | // The parent annotation does not have a paraId. This happens when the annotation was just |
299 | | // created, and not imported. paraIds are regenerated upon export, thus this new paraId |
300 | | // is only generated so that children annotations can refer to it |
301 | 0 | nParaId = CreateUniqueParaId(); |
302 | 0 | pField->SetParaId(nParaId); |
303 | 0 | } |
304 | 0 | return nParaId; |
305 | 0 | } |
306 | | |
307 | | sal_uInt32 SwAnnotationWin::CreateUniqueParaId() |
308 | 0 | { |
309 | 0 | return comphelper::rng::uniform_uint_distribution(0, std::numeric_limits<sal_uInt32>::max()); |
310 | 0 | } |
311 | | |
312 | | void SwAnnotationWin::DeleteThread() |
313 | 0 | { |
314 | | // Go to the top and delete each comment one by one |
315 | 0 | SwAnnotationWin* topNote = GetTopReplyNote(); |
316 | 0 | for (SwAnnotationWin* current = topNote;;) |
317 | 0 | { |
318 | 0 | SwAnnotationWin* next = mrMgr.GetNextPostIt(KEY_PAGEDOWN, current); |
319 | 0 | current->mnDeleteEventId = Application::PostUserEvent( LINK( current, SwAnnotationWin, DeleteHdl), nullptr, true ); |
320 | 0 | if (!next || next->GetTopReplyNote() != topNote) |
321 | 0 | return; |
322 | 0 | current = next; |
323 | 0 | } |
324 | 0 | } |
325 | | |
326 | | bool SwAnnotationWin::IsResolved() const |
327 | 0 | { |
328 | 0 | return static_cast<SwPostItField*>(mpFormatField->GetField())->GetResolved(); |
329 | 0 | } |
330 | | |
331 | | bool SwAnnotationWin::IsThreadResolved() |
332 | 0 | { |
333 | | /// First Get the top note |
334 | | // then iterate downwards checking resolved status |
335 | 0 | SwAnnotationWin* topNote = GetTopReplyNote(); |
336 | 0 | for (SwAnnotationWin* current = topNote;;) |
337 | 0 | { |
338 | 0 | if (!current->IsResolved()) |
339 | 0 | return false; |
340 | 0 | current = mrMgr.GetNextPostIt(KEY_PAGEDOWN, current); |
341 | 0 | if (!current || current->GetTopReplyNote() != topNote) |
342 | 0 | return true; |
343 | 0 | } |
344 | 0 | } |
345 | | |
346 | | bool SwAnnotationWin::IsRootNote() const |
347 | 0 | { |
348 | 0 | return static_cast<SwPostItField*>(mpFormatField->GetField())->GetParentPostItId() == 0; |
349 | 0 | } |
350 | | |
351 | | void SwAnnotationWin::SetAsRoot() |
352 | 0 | { |
353 | 0 | if (!IsRootNote()) |
354 | 0 | { |
355 | 0 | SwPostItField* pPostIt = static_cast<SwPostItField*>(mpFormatField->GetField()); |
356 | 0 | pPostIt->SetParentId(0); |
357 | 0 | pPostIt->SetParentPostItId(0); |
358 | 0 | pPostIt->SetParentName(SwMarkName()); |
359 | 0 | mrMgr.MoveSubthreadToRoot(this); |
360 | 0 | mpFormatField->Broadcast(SwFormatFieldHint(nullptr, SwFormatFieldHintWhich::CHANGED)); |
361 | 0 | } |
362 | 0 | } |
363 | | |
364 | | void SwAnnotationWin::UpdateData() |
365 | 0 | { |
366 | 0 | if ( mpOutliner->IsModified() || mbResolvedStateUpdated ) |
367 | 0 | { |
368 | 0 | IDocumentUndoRedo & rUndoRedo( |
369 | 0 | mrView.GetDocShell()->GetDoc()->GetIDocumentUndoRedo()); |
370 | 0 | std::unique_ptr<SwField> pOldField; |
371 | 0 | if (rUndoRedo.DoesUndo()) |
372 | 0 | { |
373 | 0 | pOldField = mpField->Copy(); |
374 | 0 | } |
375 | 0 | mpField->SetPar2(mpOutliner->GetEditEngine().GetText()); |
376 | 0 | mpField->SetTextObject(mpOutliner->CreateParaObject()); |
377 | 0 | if (rUndoRedo.DoesUndo()) |
378 | 0 | { |
379 | 0 | SwTextField *const pTextField = mpFormatField->GetTextField(); |
380 | 0 | SwPosition aPosition( pTextField->GetTextNode(), pTextField->GetStart() ); |
381 | 0 | rUndoRedo.AppendUndo( |
382 | 0 | std::make_unique<SwUndoFieldFromDoc>(aPosition, *pOldField, *mpField, true)); |
383 | | #if ENABLE_YRS |
384 | | mrView.GetDocShell()->GetDoc()->getIDocumentState().YrsEndUndo(); |
385 | | #endif |
386 | 0 | } |
387 | | // so we get a new layout of notes (anchor position is still the same and we would otherwise not get one) |
388 | 0 | mrMgr.SetLayout(); |
389 | | // #i98686# if we have several views, all notes should update their text |
390 | 0 | if(mbResolvedStateUpdated) |
391 | 0 | mpFormatField->Broadcast(SwFormatFieldHint( nullptr, SwFormatFieldHintWhich::RESOLVED)); |
392 | 0 | else |
393 | 0 | mpFormatField->Broadcast(SwFormatFieldHint( nullptr, SwFormatFieldHintWhich::CHANGED)); |
394 | 0 | mrView.GetDocShell()->SetModified(); |
395 | |
|
396 | 0 | UpdateMenu(); |
397 | 0 | } |
398 | 0 | mpOutliner->ClearModifyFlag(); |
399 | 0 | mpOutliner->GetUndoManager().Clear(); |
400 | 0 | mbResolvedStateUpdated = false; |
401 | 0 | } |
402 | | |
403 | | void SwAnnotationWin::Delete() |
404 | 0 | { |
405 | 0 | collectUIInformation(u"DELETE"_ustr,get_id()); |
406 | 0 | SwWrtShell* pWrtShell = mrView.GetWrtShellPtr(); |
407 | 0 | if (!(pWrtShell && pWrtShell->GotoField(*mpFormatField))) |
408 | 0 | return; |
409 | | |
410 | 0 | if ( mrMgr.GetActiveSidebarWin() == this) |
411 | 0 | { |
412 | 0 | mrMgr.SetActiveSidebarWin(nullptr); |
413 | | // if the note is empty, the previous line will send a delete event, but we are already there |
414 | 0 | if (mnDeleteEventId) |
415 | 0 | { |
416 | 0 | Application::RemoveUserEvent(mnDeleteEventId); |
417 | 0 | mnDeleteEventId = nullptr; |
418 | 0 | } |
419 | 0 | } |
420 | | // we delete the field directly, the Mgr cleans up the PostIt by listening |
421 | 0 | GrabFocusToDocument(); |
422 | 0 | pWrtShell->ClearMark(); |
423 | 0 | auto restoreGuard = mrMgr.ConfigureForCommentDelete(); |
424 | 0 | pWrtShell->DelRight(); |
425 | 0 | } |
426 | | |
427 | | void SwAnnotationWin::GotoPos() |
428 | 0 | { |
429 | 0 | mrView.GetDocShell()->GetWrtShell()->GotoField(*mpFormatField); |
430 | 0 | } |
431 | | |
432 | | sal_uInt32 SwAnnotationWin::MoveCaret() |
433 | 0 | { |
434 | | // if this is an answer, do not skip over all following ones, but insert directly behind the current one |
435 | | // but when just leaving a note, skip all following ones as well to continue typing |
436 | 0 | return mrMgr.IsAnswer() |
437 | 0 | ? 1 |
438 | 0 | : 1 + CountFollowing(); |
439 | 0 | } |
440 | | |
441 | | // counts how many SwPostItField we have right after the current one |
442 | | sal_uInt32 SwAnnotationWin::CountFollowing() |
443 | 0 | { |
444 | 0 | SwTextField* pTextField = mpFormatField->GetTextField(); |
445 | 0 | SwPosition aPosition( pTextField->GetTextNode(), pTextField->GetStart() ); |
446 | |
|
447 | 0 | for (sal_Int32 n = 1;; ++n) |
448 | 0 | { |
449 | 0 | SwTextAttr * pTextAttr = pTextField->GetTextNode().GetTextAttrForCharAt( |
450 | 0 | aPosition.GetContentIndex() + n, |
451 | 0 | RES_TXTATR_ANNOTATION ); |
452 | 0 | if (!pTextAttr) |
453 | 0 | return n - 1; |
454 | 0 | const SwField* pField = pTextAttr->GetFormatField().GetField(); |
455 | 0 | if (!pField || pField->Which() != SwFieldIds::Postit) |
456 | 0 | return n - 1; |
457 | 0 | } |
458 | 0 | } |
459 | | |
460 | | void SwAnnotationWin::InitAnswer(OutlinerParaObject const & rText) |
461 | 0 | { |
462 | | // If tiled annotations is off in lok case, skip adding additional reply text. |
463 | 0 | if (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations()) |
464 | 0 | return; |
465 | | |
466 | | //collect our old meta data |
467 | 0 | SwAnnotationWin* pWin = mrMgr.GetNextPostIt(KEY_PAGEUP, this); |
468 | 0 | if (!pWin) |
469 | 0 | return; |
470 | | |
471 | 0 | const SvtSysLocale aSysLocale; |
472 | 0 | const LocaleDataWrapper& rLocalData = aSysLocale.GetLocaleData(); |
473 | 0 | SwRewriter aRewriter; |
474 | 0 | aRewriter.AddRule(UndoArg1, pWin->GetAuthor()); |
475 | 0 | const OUString aText = aRewriter.Apply(SwResId(STR_REPLY)) |
476 | 0 | + " (" + rLocalData.getDate( pWin->GetDate()) |
477 | 0 | + ", " + rLocalData.getTime( pWin->GetTime(), false) |
478 | 0 | + "): \""; |
479 | 0 | GetOutlinerView()->InsertText(aText); |
480 | | |
481 | | // insert old, selected text or "..." |
482 | | // TODO: iterate over all paragraphs, not only first one to find out if it is empty |
483 | 0 | if (rText.GetTextObject().HasText(0)) |
484 | 0 | GetOutlinerView()->GetEditView().InsertText(rText.GetTextObject()); |
485 | 0 | else |
486 | 0 | GetOutlinerView()->InsertText(u"..."_ustr); |
487 | 0 | GetOutlinerView()->InsertText(u"\"\n"_ustr); |
488 | |
|
489 | 0 | GetOutlinerView()->SetSelection(ESelection::All()); |
490 | 0 | SfxItemSet aAnswerSet( mrView.GetDocShell()->GetPool() ); |
491 | 0 | aAnswerSet.Put(SvxFontHeightItem(200,80,EE_CHAR_FONTHEIGHT)); |
492 | 0 | aAnswerSet.Put(SvxPostureItem(ITALIC_NORMAL,EE_CHAR_ITALIC)); |
493 | 0 | GetOutlinerView()->SetAttribs(aAnswerSet); |
494 | 0 | GetOutlinerView()->SetSelection(ESelection::AtEnd()); |
495 | | |
496 | | //remove all attributes and reset our standard ones |
497 | 0 | GetOutlinerView()->GetEditView().RemoveAttribsKeepLanguages(true); |
498 | | // let's insert an undo step so the initial text can be easily deleted |
499 | | // but do not use UpdateData() directly, would set modified state again and reentrance into Mgr |
500 | 0 | mpOutliner->SetModifyHdl( Link<LinkParamNone*,void>() ); |
501 | 0 | IDocumentUndoRedo & rUndoRedo( |
502 | 0 | mrView.GetDocShell()->GetDoc()->GetIDocumentUndoRedo()); |
503 | 0 | std::unique_ptr<SwField> pOldField; |
504 | 0 | if (rUndoRedo.DoesUndo()) |
505 | 0 | { |
506 | 0 | pOldField = mpField->Copy(); |
507 | 0 | } |
508 | 0 | mpField->SetPar2(mpOutliner->GetEditEngine().GetText()); |
509 | 0 | mpField->SetTextObject(mpOutliner->CreateParaObject()); |
510 | 0 | if (rUndoRedo.DoesUndo()) |
511 | 0 | { |
512 | 0 | SwTextField *const pTextField = mpFormatField->GetTextField(); |
513 | 0 | SwPosition aPosition( pTextField->GetTextNode(), pTextField->GetStart() ); |
514 | 0 | rUndoRedo.AppendUndo( |
515 | 0 | std::make_unique<SwUndoFieldFromDoc>(aPosition, *pOldField, *mpField, true)); |
516 | | #if ENABLE_YRS |
517 | | // no! there is a StartUndo wrapping this mrView.GetDocShell()->GetDoc()->getIDocumentState().YrsEndUndo(); |
518 | | #endif |
519 | 0 | } |
520 | 0 | mpOutliner->SetModifyHdl( LINK( this, SwAnnotationWin, ModifyHdl ) ); |
521 | 0 | mpOutliner->ClearModifyFlag(); |
522 | 0 | mpOutliner->GetUndoManager().Clear(); |
523 | 0 | } |
524 | | |
525 | | void SwAnnotationWin::UpdateText(const OUString& aText) |
526 | 0 | { |
527 | 0 | mpOutliner->Clear(); |
528 | 0 | GetOutlinerView()->InsertText(aText); |
529 | 0 | UpdateData(); |
530 | 0 | } |
531 | | |
532 | | void SwAnnotationWin::UpdateHTML(const OUString& rHtml) |
533 | 0 | { |
534 | 0 | mpOutliner->Clear(); |
535 | 0 | SwPostItHelper::ImportHTML(*mpOutliner, rHtml); |
536 | 0 | UpdateData(); |
537 | 0 | } |
538 | | |
539 | | OString SwAnnotationWin::GetSimpleHtml() const |
540 | 0 | { |
541 | 0 | return GetOutlinerView()->GetEditView().GetSimpleHtml(); |
542 | 0 | } |
543 | | |
544 | | bool SwAnnotationWin::IsReadOnly() const |
545 | 0 | { |
546 | 0 | return mbReadonly; |
547 | 0 | } |
548 | | |
549 | | bool SwAnnotationWin::IsReadOnlyOrProtected() const |
550 | 0 | { |
551 | 0 | return IsReadOnly() || |
552 | 0 | GetLayoutStatus() == SwPostItHelper::DELETED || |
553 | 0 | ( mpFormatField && mpFormatField->IsProtect() ); |
554 | 0 | } |
555 | | |
556 | | OUString SwAnnotationWin::GetAuthor() const |
557 | 0 | { |
558 | 0 | return mpField->GetPar1(); |
559 | 0 | } |
560 | | |
561 | | Date SwAnnotationWin::GetDate() const |
562 | 0 | { |
563 | 0 | return mpField->GetDate(); |
564 | 0 | } |
565 | | |
566 | | tools::Time SwAnnotationWin::GetTime() const |
567 | 0 | { |
568 | 0 | return mpField->GetTime(); |
569 | 0 | } |
570 | | |
571 | | FactoryFunction SwAnnotationWin::GetUITestFactory() const |
572 | 0 | { |
573 | 0 | return CommentUIObject::create; |
574 | 0 | } |
575 | | |
576 | | } // end of namespace sw::annotation |
577 | | |
578 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |