/src/libreoffice/sw/source/uibase/uiview/viewling.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 <hintids.hxx> |
21 | | |
22 | | #include <com/sun/star/lang/Locale.hpp> |
23 | | #include <com/sun/star/linguistic2/XThesaurus.hpp> |
24 | | #include <com/sun/star/linguistic2/ProofreadingResult.hpp> |
25 | | #include <com/sun/star/linguistic2/XLinguProperties.hpp> |
26 | | #include <com/sun/star/i18n/TextConversionOption.hpp> |
27 | | #include <comphelper/lok.hxx> |
28 | | #include <comphelper/processfactory.hxx> |
29 | | #include <comphelper/propertyvalue.hxx> |
30 | | #include <comphelper/propertysequence.hxx> |
31 | | #include <comphelper/scopeguard.hxx> |
32 | | #include <toolkit/helper/vclunohelper.hxx> |
33 | | #include <unotools/fontdefs.hxx> |
34 | | #include <vcl/rendercontext/GetDefaultFontFlags.hxx> |
35 | | #include <vcl/weld/MessageDialog.hxx> |
36 | | #include <vcl/weld/weld.hxx> |
37 | | #include <sfx2/dispatch.hxx> |
38 | | #include <sfx2/viewfrm.hxx> |
39 | | #include <sfx2/request.hxx> |
40 | | #include <svx/dialmgr.hxx> |
41 | | #include <svx/ehdl.hxx> |
42 | | #include <svx/svxerr.hxx> |
43 | | #include <svx/svxdlg.hxx> |
44 | | #include <svx/chinese_translation_unodialog.hxx> |
45 | | #include <osl/diagnose.h> |
46 | | #include <swwait.hxx> |
47 | | #include <uitool.hxx> |
48 | | #include <view.hxx> |
49 | | #include <wrtsh.hxx> |
50 | | #include <viewopt.hxx> |
51 | | #include <swundo.hxx> |
52 | | #include <hyp.hxx> |
53 | | #include <olmenu.hxx> |
54 | | #include <pam.hxx> |
55 | | #include <edtwin.hxx> |
56 | | #include <ndtxt.hxx> |
57 | | #include <txtfrm.hxx> |
58 | | #include <cmdid.h> |
59 | | #include <strings.hrc> |
60 | | #include <hhcwrp.hxx> |
61 | | |
62 | | #include <boost/property_tree/json_parser.hpp> |
63 | | |
64 | | #include <com/sun/star/ui/dialogs/XExecutableDialog.hpp> |
65 | | #include <com/sun/star/ui/ContextMenuExecuteEvent.hpp> |
66 | | #include <com/sun/star/lang/XInitialization.hpp> |
67 | | #include <com/sun/star/frame/XDispatch.hpp> |
68 | | #include <com/sun/star/frame/XDispatchProvider.hpp> |
69 | | #include <com/sun/star/frame/XFrame.hpp> |
70 | | #include <com/sun/star/frame/XPopupMenuController.hpp> |
71 | | #include <com/sun/star/awt/PopupMenuDirection.hpp> |
72 | | #include <com/sun/star/awt/XVclWindowPeer.hpp> |
73 | | #include <com/sun/star/util/URL.hpp> |
74 | | #include <com/sun/star/beans/PropertyValue.hpp> |
75 | | #include <com/sun/star/beans/XPropertySet.hpp> |
76 | | #include <com/sun/star/util/URLTransformer.hpp> |
77 | | #include <com/sun/star/util/XURLTransformer.hpp> |
78 | | |
79 | | #include <vcl/svapp.hxx> |
80 | | #include <vcl/unohelp.hxx> |
81 | | #include <rtl/ustring.hxx> |
82 | | |
83 | | #include <svtools/langtab.hxx> |
84 | | |
85 | | #include <editeng/editerr.hxx> |
86 | | #include <LibreOfficeKit/LibreOfficeKitEnums.h> |
87 | | |
88 | | #include <memory> |
89 | | |
90 | | using namespace sw::mark; |
91 | | using namespace ::com::sun::star; |
92 | | using namespace ::com::sun::star::beans; |
93 | | using namespace ::com::sun::star::uno; |
94 | | using namespace ::com::sun::star::linguistic2; |
95 | | |
96 | | // Lingu-Dispatcher |
97 | | |
98 | | void SwView::ExecLingu(SfxRequest &rReq) |
99 | 0 | { |
100 | 0 | switch(rReq.GetSlot()) |
101 | 0 | { |
102 | 0 | case SID_THESAURUS: |
103 | 0 | StartThesaurus(); |
104 | 0 | rReq.Ignore(); |
105 | 0 | break; |
106 | 0 | case SID_HANGUL_HANJA_CONVERSION: |
107 | 0 | StartTextConversion( LANGUAGE_KOREAN, LANGUAGE_KOREAN, nullptr, |
108 | 0 | i18n::TextConversionOption::CHARACTER_BY_CHARACTER, true ); |
109 | 0 | break; |
110 | 0 | case SID_CHINESE_CONVERSION: |
111 | 0 | { |
112 | | //open ChineseTranslationDialog |
113 | 0 | Reference<awt::XWindow> xParentWindow; |
114 | 0 | if (weld::Window* pParentWindow = rReq.GetFrameWeld()) |
115 | 0 | xParentWindow = pParentWindow->GetXWindow(); |
116 | 0 | rtl::Reference< textconversiondlgs::ChineseTranslation_UnoDialog > xDialog(new textconversiondlgs::ChineseTranslation_UnoDialog(xParentWindow)); |
117 | | |
118 | | //execute dialog |
119 | 0 | sal_Int16 nDialogRet = xDialog->execute(); |
120 | 0 | if( RET_OK == nDialogRet ) |
121 | 0 | { |
122 | | //get some parameters from the dialog |
123 | 0 | bool bToSimplified = xDialog->getIsDirectionToSimplified(); |
124 | 0 | bool bCommonTerms = xDialog->getIsTranslateCommonTerms(); |
125 | | |
126 | | //execute translation |
127 | 0 | LanguageType nSourceLang = bToSimplified ? LANGUAGE_CHINESE_TRADITIONAL : LANGUAGE_CHINESE_SIMPLIFIED; |
128 | 0 | LanguageType nTargetLang = bToSimplified ? LANGUAGE_CHINESE_SIMPLIFIED : LANGUAGE_CHINESE_TRADITIONAL; |
129 | 0 | sal_Int32 nOptions = !bCommonTerms ? i18n::TextConversionOption::CHARACTER_BY_CHARACTER : 0; |
130 | |
|
131 | 0 | vcl::Font aTargetFont = OutputDevice::GetDefaultFont( DefaultFontType::CJK_TEXT, |
132 | 0 | nTargetLang, GetDefaultFontFlags::OnlyOne ); |
133 | | |
134 | | // disallow formatting, updating the view, ... while |
135 | | // converting the document. (saves time) |
136 | | // Also remember the current view and cursor position for later |
137 | 0 | m_pWrtShell->StartAction(); |
138 | | |
139 | | // remember cursor position data for later restoration of the cursor |
140 | 0 | const SwPosition *pPoint = m_pWrtShell->GetCursor()->GetPoint(); |
141 | 0 | bool bRestoreCursor = pPoint->GetNode().IsTextNode(); |
142 | 0 | const SwNodeIndex aPointNodeIndex( pPoint->GetNode() ); |
143 | 0 | sal_Int32 nPointIndex = pPoint->GetContentIndex(); |
144 | | |
145 | | // since this conversion is not interactive the whole converted |
146 | | // document should be undone in a single undo step. |
147 | 0 | m_pWrtShell->StartUndo( SwUndoId::OVERWRITE ); |
148 | |
|
149 | 0 | StartTextConversion( nSourceLang, nTargetLang, &aTargetFont, nOptions, false ); |
150 | |
|
151 | 0 | m_pWrtShell->EndUndo( SwUndoId::OVERWRITE ); |
152 | |
|
153 | 0 | if (bRestoreCursor) |
154 | 0 | { |
155 | 0 | SwTextNode *pTextNode = aPointNodeIndex.GetNode().GetTextNode(); |
156 | | // check for unexpected error case |
157 | 0 | OSL_ENSURE(pTextNode && pTextNode->GetText().getLength() >= nPointIndex, |
158 | 0 | "text missing: corrupted node?" ); |
159 | | // restore cursor to its original position |
160 | 0 | if (!pTextNode || pTextNode->GetText().getLength() < nPointIndex) |
161 | 0 | m_pWrtShell->GetCursor()->GetPoint()->Assign( aPointNodeIndex ); |
162 | 0 | else |
163 | 0 | m_pWrtShell->GetCursor()->GetPoint()->Assign( *pTextNode, nPointIndex ); |
164 | 0 | } |
165 | | |
166 | | // enable all, restore view and cursor position |
167 | 0 | m_pWrtShell->EndAction(); |
168 | 0 | } |
169 | 0 | break; |
170 | 0 | } |
171 | 0 | case FN_HYPHENATE_OPT_DLG: |
172 | 0 | HyphenateDocument(); |
173 | 0 | break; |
174 | 0 | default: |
175 | 0 | OSL_ENSURE(false, "wrong Dispatcher"); |
176 | 0 | return; |
177 | 0 | } |
178 | 0 | } |
179 | | |
180 | | // start language specific text conversion |
181 | | |
182 | | void SwView::StartTextConversion( |
183 | | LanguageType nSourceLang, |
184 | | LanguageType nTargetLang, |
185 | | const vcl::Font *pTargetFont, |
186 | | sal_Int32 nOptions, |
187 | | bool bIsInteractive ) |
188 | 0 | { |
189 | | // do not do text conversion if it is active elsewhere |
190 | 0 | if (SwEditShell::HasConvIter()) |
191 | 0 | { |
192 | 0 | return; |
193 | 0 | } |
194 | | |
195 | 0 | SpellContext(); |
196 | |
|
197 | 0 | const SwViewOption* pVOpt = m_pWrtShell->GetViewOptions(); |
198 | 0 | const bool bOldIdle = pVOpt->IsIdle(); |
199 | 0 | pVOpt->SetIdle( false ); |
200 | |
|
201 | 0 | bool bOldIns = m_pWrtShell->IsInsMode(); |
202 | 0 | m_pWrtShell->SetInsMode(); |
203 | |
|
204 | 0 | const bool bSelection = static_cast<SwCursorShell*>(m_pWrtShell.get())->HasSelection() || |
205 | 0 | m_pWrtShell->GetCursor() != m_pWrtShell->GetCursor()->GetNext(); |
206 | |
|
207 | 0 | const bool bStart = bSelection || m_pWrtShell->IsStartOfDoc(); |
208 | 0 | const bool bOther = !bSelection && !(m_pWrtShell->GetFrameType(nullptr,true) & FrameTypeFlags::BODY); |
209 | |
|
210 | 0 | { |
211 | 0 | const uno::Reference< uno::XComponentContext >& xContext( |
212 | 0 | comphelper::getProcessComponentContext() ); |
213 | 0 | SwHHCWrapper aWrap( *this, xContext, nSourceLang, nTargetLang, pTargetFont, |
214 | 0 | nOptions, bIsInteractive, |
215 | 0 | bStart, bOther, bSelection ); |
216 | 0 | aWrap.Convert(); |
217 | 0 | } |
218 | |
|
219 | 0 | m_pWrtShell->SetInsMode( bOldIns ); |
220 | 0 | pVOpt->SetIdle( bOldIdle ); |
221 | 0 | SpellContext(false); |
222 | 0 | } |
223 | | |
224 | | // spellcheck and text conversion related stuff |
225 | | |
226 | | void SwView::SpellStart( SvxSpellArea eWhich, |
227 | | bool bStartDone, bool bEndDone, |
228 | | SwConversionArgs *pConvArgs ) |
229 | 0 | { |
230 | 0 | Reference< XLinguProperties > xProp = ::GetLinguPropertySet(); |
231 | 0 | bool bIsWrapReverse = !pConvArgs && xProp.is() && xProp->getIsWrapReverse(); |
232 | |
|
233 | 0 | SwDocPositions eStart = SwDocPositions::Start; |
234 | 0 | SwDocPositions eEnd = SwDocPositions::End; |
235 | 0 | SwDocPositions eCurr = SwDocPositions::Curr; |
236 | 0 | switch ( eWhich ) |
237 | 0 | { |
238 | 0 | case SvxSpellArea::Body: |
239 | 0 | if( bIsWrapReverse ) |
240 | 0 | eCurr = SwDocPositions::End; |
241 | 0 | else |
242 | 0 | eCurr = SwDocPositions::Start; |
243 | 0 | break; |
244 | 0 | case SvxSpellArea::BodyEnd: |
245 | 0 | if( bIsWrapReverse ) |
246 | 0 | { |
247 | 0 | if( bStartDone ) |
248 | 0 | eStart = SwDocPositions::Curr; |
249 | 0 | eCurr = SwDocPositions::End; |
250 | 0 | } |
251 | 0 | else if( bStartDone ) |
252 | 0 | eCurr = SwDocPositions::Start; |
253 | 0 | break; |
254 | 0 | case SvxSpellArea::BodyStart: |
255 | 0 | if( !bIsWrapReverse ) |
256 | 0 | { |
257 | 0 | if( bEndDone ) |
258 | 0 | eEnd = SwDocPositions::Curr; |
259 | 0 | eCurr = SwDocPositions::Start; |
260 | 0 | } |
261 | 0 | else if( bEndDone ) |
262 | 0 | eCurr = SwDocPositions::End; |
263 | 0 | break; |
264 | 0 | case SvxSpellArea::Other: |
265 | 0 | if( bIsWrapReverse ) |
266 | 0 | { |
267 | 0 | eStart = SwDocPositions::OtherStart; |
268 | 0 | eEnd = SwDocPositions::OtherEnd; |
269 | 0 | eCurr = SwDocPositions::OtherEnd; |
270 | 0 | } |
271 | 0 | else |
272 | 0 | { |
273 | 0 | eStart = SwDocPositions::OtherStart; |
274 | 0 | eEnd = SwDocPositions::OtherEnd; |
275 | 0 | eCurr = SwDocPositions::OtherStart; |
276 | 0 | } |
277 | 0 | break; |
278 | 0 | default: |
279 | 0 | OSL_ENSURE( false, "SpellStart with unknown Area" ); |
280 | 0 | } |
281 | 0 | m_pWrtShell->SpellStart( eStart, eEnd, eCurr, pConvArgs ); |
282 | 0 | } |
283 | | |
284 | | // Error message while Spelling |
285 | | |
286 | | // The passed pointer nlang is itself the value |
287 | | void SwView::SpellError(LanguageType eLang) |
288 | 0 | { |
289 | 0 | int nPend = 0; |
290 | |
|
291 | 0 | if ( m_pWrtShell->ActionPend() ) |
292 | 0 | { |
293 | 0 | m_pWrtShell->Push(); |
294 | 0 | m_pWrtShell->ClearMark(); |
295 | 0 | do |
296 | 0 | { |
297 | 0 | m_pWrtShell->EndAction(); |
298 | 0 | ++nPend; |
299 | 0 | } |
300 | 0 | while( m_pWrtShell->ActionPend() ); |
301 | 0 | } |
302 | 0 | OUString aErr(SvtLanguageTable::GetLanguageString( eLang ) ); |
303 | |
|
304 | 0 | SwEditWin &rEditWin = GetEditWin(); |
305 | 0 | int nWaitCnt = 0; |
306 | 0 | while( rEditWin.IsWait() ) |
307 | 0 | { |
308 | 0 | rEditWin.LeaveWait(); |
309 | 0 | ++nWaitCnt; |
310 | 0 | } |
311 | 0 | if ( LANGUAGE_NONE == eLang ) |
312 | 0 | ErrorHandler::HandleError( ERRCODE_SVX_LINGU_NOLANGUAGE ); |
313 | 0 | else |
314 | 0 | ErrorHandler::HandleError( ErrCodeMsg( ERRCODE_SVX_LINGU_LANGUAGENOTEXISTS, aErr ) ); |
315 | |
|
316 | 0 | while( nWaitCnt ) |
317 | 0 | { |
318 | 0 | rEditWin.EnterWait(); |
319 | 0 | --nWaitCnt; |
320 | 0 | } |
321 | |
|
322 | 0 | if ( nPend ) |
323 | 0 | { |
324 | 0 | while( nPend-- ) |
325 | 0 | m_pWrtShell->StartAction(); |
326 | 0 | m_pWrtShell->Combine(); |
327 | 0 | } |
328 | 0 | } |
329 | | |
330 | | // Finish spelling and restore cursor |
331 | | |
332 | | void SwView::SpellEnd( SwConversionArgs const *pConvArgs ) |
333 | 0 | { |
334 | 0 | m_pWrtShell->SpellEnd( pConvArgs ); |
335 | 0 | if( m_pWrtShell->IsExtMode() ) |
336 | 0 | m_pWrtShell->SetMark(); |
337 | 0 | } |
338 | | |
339 | | void SwView::HyphStart( SvxSpellArea eWhich ) |
340 | 0 | { |
341 | 0 | switch ( eWhich ) |
342 | 0 | { |
343 | 0 | case SvxSpellArea::Body: |
344 | 0 | m_pWrtShell->HyphStart( SwDocPositions::Start, SwDocPositions::End ); |
345 | 0 | break; |
346 | 0 | case SvxSpellArea::BodyEnd: |
347 | 0 | m_pWrtShell->HyphStart( SwDocPositions::Curr, SwDocPositions::End ); |
348 | 0 | break; |
349 | 0 | case SvxSpellArea::BodyStart: |
350 | 0 | m_pWrtShell->HyphStart( SwDocPositions::Start, SwDocPositions::Curr ); |
351 | 0 | break; |
352 | 0 | case SvxSpellArea::Other: |
353 | 0 | m_pWrtShell->HyphStart( SwDocPositions::OtherStart, SwDocPositions::OtherEnd ); |
354 | 0 | break; |
355 | 0 | default: |
356 | 0 | OSL_ENSURE( false, "HyphStart with unknown Area" ); |
357 | 0 | } |
358 | 0 | } |
359 | | |
360 | | // Interactive separation |
361 | | |
362 | | void SwView::HyphenateDocument() |
363 | 0 | { |
364 | | // do not hyphenate if interactive hyphenation is active elsewhere |
365 | 0 | if (SwEditShell::HasHyphIter()) |
366 | 0 | { |
367 | 0 | std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetEditWin().GetFrameWeld(), |
368 | 0 | VclMessageType::Warning, VclButtonsType::Ok, SwResId(STR_MULT_INTERACT_HYPH_WARN))); |
369 | 0 | xBox->set_title(SwResId(STR_HYPH_TITLE)); |
370 | 0 | xBox->run(); |
371 | 0 | return; |
372 | 0 | } |
373 | | |
374 | 0 | SvxErrorContext aContext(ERRCTX_SVX_LINGU_HYPHENATION, OUString(), m_pEditWin->GetFrameWeld()); |
375 | |
|
376 | 0 | Reference< XHyphenator > xHyph( ::GetHyphenator() ); |
377 | 0 | if (!xHyph.is()) |
378 | 0 | { |
379 | 0 | ErrorHandler::HandleError( ERRCODE_SVX_LINGU_LINGUNOTEXISTS ); |
380 | 0 | return; |
381 | 0 | } |
382 | | |
383 | 0 | if (m_pWrtShell->GetSelectionType() & (SelectionType::DrawObjectEditMode|SelectionType::DrawObject)) |
384 | 0 | { |
385 | | // Hyphenation in a Draw object |
386 | 0 | HyphenateDrawText(); |
387 | 0 | } |
388 | 0 | else |
389 | 0 | { |
390 | 0 | SwViewOption* pVOpt = const_cast<SwViewOption*>(m_pWrtShell->GetViewOptions()); |
391 | 0 | bool bOldIdle = pVOpt->IsIdle(); |
392 | 0 | pVOpt->SetIdle( false ); |
393 | |
|
394 | 0 | Reference< XLinguProperties > xProp( ::GetLinguPropertySet() ); |
395 | |
|
396 | 0 | m_pWrtShell->StartUndo(SwUndoId::INSATTR); // valid later |
397 | |
|
398 | 0 | bool bHyphSpecial = xProp.is() && xProp->getIsHyphSpecial(); |
399 | 0 | bool bSelection = static_cast<SwCursorShell*>(m_pWrtShell.get())->HasSelection() || |
400 | 0 | m_pWrtShell->GetCursor() != m_pWrtShell->GetCursor()->GetNext(); |
401 | 0 | bool bOther = m_pWrtShell->HasOtherCnt() && bHyphSpecial && !bSelection; |
402 | 0 | bool bStart = bSelection || ( !bOther && m_pWrtShell->IsStartOfDoc() ); |
403 | 0 | bool bStop = false; |
404 | 0 | if( !bOther && !(m_pWrtShell->GetFrameType(nullptr,true) & FrameTypeFlags::BODY) && !bSelection ) |
405 | | // turned on no special area |
406 | 0 | { |
407 | | // I want also in special areas hyphenation |
408 | 0 | std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetEditWin().GetFrameWeld(), |
409 | 0 | VclMessageType::Question, VclButtonsType::YesNo, |
410 | 0 | SwResId(STR_QUERY_SPECIAL_FORCED))); |
411 | 0 | if (xBox->run() == RET_YES) |
412 | 0 | { |
413 | 0 | bOther = true; |
414 | 0 | if (xProp.is()) |
415 | 0 | { |
416 | 0 | xProp->setIsHyphSpecial( true ); |
417 | 0 | } |
418 | 0 | } |
419 | 0 | else |
420 | 0 | bStop = true; // No hyphenation |
421 | 0 | } |
422 | |
|
423 | 0 | if( !bStop ) |
424 | 0 | { |
425 | 0 | SwHyphWrapper aWrap( *this, xHyph, bStart, bOther, bSelection ); |
426 | 0 | aWrap.SpellDocument(); |
427 | 0 | m_pWrtShell->EndUndo(SwUndoId::INSATTR); |
428 | 0 | } |
429 | 0 | pVOpt->SetIdle( bOldIdle ); |
430 | 0 | } |
431 | 0 | } |
432 | | |
433 | | bool SwView::IsValidSelectionForThesaurus() const |
434 | 0 | { |
435 | | // must not be a multi-selection, and if it is a selection it needs |
436 | | // to be within a single paragraph |
437 | |
|
438 | 0 | const bool bMultiSel = m_pWrtShell->GetCursor()->IsMultiSelection(); |
439 | 0 | const bool bSelection = static_cast<SwCursorShell*>(m_pWrtShell.get())->HasSelection(); |
440 | 0 | return !bMultiSel && (!bSelection || m_pWrtShell->IsSelOnePara() ); |
441 | 0 | } |
442 | | |
443 | | OUString SwView::GetThesaurusLookUpText( bool bSelection ) const |
444 | 0 | { |
445 | 0 | return bSelection ? m_pWrtShell->GetSelText() : m_pWrtShell->GetCurWord(); |
446 | 0 | } |
447 | | |
448 | | void SwView::InsertThesaurusSynonym( const OUString &rSynonmText, const OUString &rLookUpText, bool bSelection ) |
449 | 0 | { |
450 | 0 | bool bOldIns = m_pWrtShell->IsInsMode(); |
451 | 0 | m_pWrtShell->SetInsMode(); |
452 | |
|
453 | 0 | m_pWrtShell->StartAllAction(); |
454 | 0 | m_pWrtShell->StartUndo(SwUndoId::DELETE); |
455 | |
|
456 | 0 | if( !bSelection ) |
457 | 0 | { |
458 | 0 | if(m_pWrtShell->IsEndWrd()) |
459 | 0 | m_pWrtShell->Left(SwCursorSkipMode::Cells, false, 1, false ); |
460 | |
|
461 | 0 | m_pWrtShell->SelWrd(); |
462 | | |
463 | | // make sure the selection build later from the data below does not |
464 | | // include "in word" character to the left and right in order to |
465 | | // preserve those. Therefore count those "in words" in order to modify |
466 | | // the selection accordingly. |
467 | 0 | const sal_Unicode* pChar = rLookUpText.getStr(); |
468 | 0 | sal_Int32 nLeft = 0; |
469 | 0 | while (*pChar++ == CH_TXTATR_INWORD) |
470 | 0 | ++nLeft; |
471 | 0 | pChar = rLookUpText.getLength() ? rLookUpText.getStr() + rLookUpText.getLength() - 1 : nullptr; |
472 | 0 | sal_Int32 nRight = 0; |
473 | 0 | while (pChar && *pChar-- == CH_TXTATR_INWORD) |
474 | 0 | ++nRight; |
475 | | |
476 | | // adjust existing selection |
477 | 0 | SwPaM *pCursor = m_pWrtShell->GetCursor(); |
478 | 0 | pCursor->GetPoint()->AdjustContent(-nRight); |
479 | 0 | pCursor->GetMark()->AdjustContent(nLeft); |
480 | 0 | } |
481 | |
|
482 | 0 | m_pWrtShell->Insert( rSynonmText ); |
483 | |
|
484 | 0 | m_pWrtShell->EndUndo(SwUndoId::DELETE); |
485 | 0 | m_pWrtShell->EndAllAction(); |
486 | |
|
487 | 0 | m_pWrtShell->SetInsMode( bOldIns ); |
488 | 0 | } |
489 | | |
490 | | // Start thesaurus |
491 | | |
492 | | void SwView::StartThesaurus() |
493 | 0 | { |
494 | 0 | if (!IsValidSelectionForThesaurus()) |
495 | 0 | return; |
496 | | |
497 | 0 | SvxErrorContext aContext(ERRCTX_SVX_LINGU_THESAURUS, OUString(), m_pEditWin->GetFrameWeld()); |
498 | | |
499 | | // Determine language |
500 | 0 | LanguageType eLang = m_pWrtShell->GetCurLang(); |
501 | 0 | if( LANGUAGE_SYSTEM == eLang ) |
502 | 0 | eLang = GetAppLanguage(); |
503 | |
|
504 | 0 | if( eLang == LANGUAGE_DONTKNOW || eLang == LANGUAGE_NONE ) |
505 | 0 | { |
506 | 0 | SpellError( LANGUAGE_NONE ); |
507 | 0 | return; |
508 | 0 | } |
509 | | |
510 | 0 | SwViewOption* pVOpt = const_cast<SwViewOption*>(m_pWrtShell->GetViewOptions()); |
511 | 0 | const bool bOldIdle = pVOpt->IsIdle(); |
512 | 0 | pVOpt->SetIdle( false ); |
513 | 0 | comphelper::ScopeGuard guard([&]() { pVOpt->SetIdle(bOldIdle); }); // restore when leaving scope |
514 | | |
515 | | // get initial LookUp text |
516 | 0 | const bool bSelection = static_cast<SwCursorShell*>(m_pWrtShell.get())->HasSelection(); |
517 | 0 | OUString aTmp = GetThesaurusLookUpText( bSelection ); |
518 | |
|
519 | 0 | Reference< XThesaurus > xThes( ::GetThesaurus() ); |
520 | |
|
521 | 0 | if ( !xThes.is() || !xThes->hasLocale( LanguageTag::convertToLocale( eLang ) ) ) |
522 | 0 | SpellError( eLang ); |
523 | 0 | else |
524 | 0 | { |
525 | 0 | VclPtr<AbstractThesaurusDialog> pDlg; |
526 | | // create dialog |
527 | 0 | { //Scope for SwWait-Object |
528 | 0 | SwWait aWait( *GetDocShell(), true ); |
529 | | // load library with dialog only on demand ... |
530 | 0 | SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); |
531 | 0 | pDlg.reset(pFact->CreateThesaurusDialog(GetEditWin().GetFrameWeld(), xThes, aTmp, eLang)); |
532 | 0 | } |
533 | |
|
534 | 0 | if (pDlg) |
535 | 0 | { |
536 | 0 | guard.dismiss(); // ignore, we'll call SetIdle() explicitly after the dialog ends |
537 | |
|
538 | 0 | pDlg->StartExecuteAsync([aTmp, bSelection, bOldIdle, pDlg, pVOpt, this](sal_Int32 nResult){ |
539 | 0 | if (nResult == RET_OK ) |
540 | 0 | InsertThesaurusSynonym(pDlg->GetWord(), aTmp, bSelection); |
541 | |
|
542 | 0 | pVOpt->SetIdle(bOldIdle); |
543 | 0 | pDlg->disposeOnce(); |
544 | 0 | }); |
545 | 0 | } |
546 | 0 | } |
547 | 0 | } |
548 | | |
549 | | // Offer online suggestions |
550 | | |
551 | | namespace { |
552 | | |
553 | | //!! Start of extra code for context menu modifying extensions |
554 | | struct ExecuteInfo |
555 | | { |
556 | | uno::Reference< frame::XDispatch > xDispatch; |
557 | | util::URL aTargetURL; |
558 | | }; |
559 | | |
560 | | class AsyncExecute |
561 | | { |
562 | | public: |
563 | | DECL_STATIC_LINK( AsyncExecute, ExecuteHdl_Impl, void*, void ); |
564 | | }; |
565 | | |
566 | | } |
567 | | |
568 | | IMPL_STATIC_LINK( AsyncExecute, ExecuteHdl_Impl, void*, p, void ) |
569 | 0 | { |
570 | 0 | ExecuteInfo* pExecuteInfo = static_cast<ExecuteInfo*>(p); |
571 | 0 | SolarMutexReleaser aReleaser; |
572 | 0 | try |
573 | 0 | { |
574 | | // Asynchronous execution as this can lead to our own destruction! |
575 | | // Framework can recycle our current frame and the layout manager disposes all user interface |
576 | | // elements if a component gets detached from its frame! |
577 | 0 | pExecuteInfo->xDispatch->dispatch(pExecuteInfo->aTargetURL, uno::Sequence<beans::PropertyValue>()); |
578 | 0 | } |
579 | 0 | catch (const Exception&) |
580 | 0 | { |
581 | 0 | } |
582 | |
|
583 | 0 | delete pExecuteInfo; |
584 | 0 | } |
585 | | //!! End of extra code for context menu modifying extensions |
586 | | |
587 | | bool SwView::ExecSpellPopup(const Point& rPt, bool bIsMouseEvent) |
588 | 0 | { |
589 | 0 | bool bRet = false; |
590 | 0 | const SwViewOption* pVOpt = m_pWrtShell->GetViewOptions(); |
591 | 0 | if( pVOpt->IsOnlineSpell() && |
592 | 0 | !m_pWrtShell->IsSelection()) |
593 | 0 | { |
594 | 0 | if (m_pWrtShell->GetSelectionType() & SelectionType::DrawObjectEditMode) |
595 | 0 | bRet = ExecDrwTextSpellPopup(rPt); |
596 | 0 | else if (!m_pWrtShell->IsSelFrameMode()) |
597 | 0 | { |
598 | 0 | const bool bOldViewLock = m_pWrtShell->IsViewLocked(); |
599 | 0 | m_pWrtShell->LockView( true ); |
600 | 0 | if (!comphelper::LibreOfficeKit::isActive()) |
601 | 0 | m_pWrtShell->Push(); |
602 | 0 | SwRect aToFill; |
603 | |
|
604 | 0 | SwCursorShell *pCursorShell = m_pWrtShell.get(); |
605 | 0 | SwPaM *pCursor = pCursorShell->GetCursor(); |
606 | 0 | SwPosition aPoint(*pCursor->GetPoint()); |
607 | 0 | const SwTextNode *pNode = aPoint.GetNode().GetTextNode(); |
608 | | |
609 | | // Spell-check in case the idle jobs haven't had a chance to kick in. |
610 | | // This makes it possible to suggest spelling corrections for |
611 | | // wrong words independent of the spell-checking idle job. |
612 | 0 | if (pNode && pNode->IsWrongDirty() && |
613 | 0 | !pCursorShell->IsTableMode() && |
614 | 0 | !pCursor->HasMark() && !pCursor->IsMultiSelection()) |
615 | 0 | { |
616 | 0 | std::pair<Point, bool> const tmp(rPt, false); |
617 | 0 | SwContentFrame *const pContentFrame = pCursor->GetPointContentNode()->getLayoutFrame( |
618 | 0 | pCursorShell->GetLayout(), |
619 | 0 | &aPoint, &tmp); |
620 | 0 | if (pContentFrame) |
621 | 0 | { |
622 | 0 | SwRect aRepaint(static_cast<SwTextFrame*>(pContentFrame)->AutoSpell_( |
623 | 0 | *pCursor->GetPointContentNode()->GetTextNode(), 0)); |
624 | 0 | if (aRepaint.HasArea()) |
625 | 0 | m_pWrtShell->InvalidateWindows(aRepaint); |
626 | 0 | } |
627 | 0 | } |
628 | | |
629 | | // decide which variant of the context menu to use... |
630 | | // if neither spell checking nor grammar checking provides suggestions use the |
631 | | // default context menu. |
632 | 0 | bool bUseGrammarContext = false; |
633 | 0 | Reference<XSpellAlternatives> xAlt( |
634 | 0 | m_pWrtShell->GetCorrection(bIsMouseEvent ? &rPt : nullptr, aToFill)); |
635 | 0 | ProofreadingResult aGrammarCheckRes; |
636 | 0 | sal_Int32 nErrorInResult = -1; |
637 | 0 | uno::Sequence< OUString > aSuggestions; |
638 | 0 | bool bCorrectionRes = false; |
639 | 0 | if (!xAlt.is() || !xAlt->getAlternatives().hasElements()) |
640 | 0 | { |
641 | 0 | sal_Int32 nErrorPosInText = -1; |
642 | 0 | bCorrectionRes = m_pWrtShell->GetGrammarCorrection( |
643 | 0 | aGrammarCheckRes, nErrorPosInText, nErrorInResult, aSuggestions, |
644 | 0 | bIsMouseEvent ? &rPt : nullptr, aToFill); |
645 | 0 | OUString aMessageText; |
646 | 0 | if (nErrorInResult >= 0) |
647 | 0 | aMessageText = aGrammarCheckRes.aErrors[ nErrorInResult ].aShortComment; |
648 | | // we like to use the grammar checking context menu if we either get |
649 | | // some suggestions or at least a comment about the error found... |
650 | 0 | bUseGrammarContext = bCorrectionRes && |
651 | 0 | (aSuggestions.hasElements() || !aMessageText.isEmpty()); |
652 | 0 | } |
653 | | |
654 | | // open respective context menu for spell check or grammar errors with correction suggestions... |
655 | 0 | if ((!bUseGrammarContext && xAlt.is()) || |
656 | 0 | (bUseGrammarContext && bCorrectionRes && aGrammarCheckRes.aErrors.hasElements())) |
657 | 0 | { |
658 | | // get paragraph text |
659 | 0 | OUString aParaText; |
660 | 0 | if (pNode) |
661 | 0 | { |
662 | 0 | pCursorShell->Push(); |
663 | 0 | if (!pCursorShell->IsSttPara()) |
664 | 0 | { |
665 | 0 | pCursorShell->MovePara(GoCurrPara, fnParaStart); |
666 | 0 | } |
667 | 0 | pCursorShell->SetMark(); |
668 | 0 | if (!pCursorShell->IsEndPara()) |
669 | 0 | { |
670 | 0 | pCursorShell->MovePara(GoCurrPara, fnParaEnd); |
671 | 0 | } |
672 | 0 | aParaText = pCursorShell->GetSelText(); |
673 | 0 | pCursorShell->Pop(SwCursorShell::PopMode::DeleteCurrent); |
674 | 0 | } |
675 | 0 | else |
676 | 0 | { |
677 | 0 | OSL_FAIL("text node expected but not found" ); |
678 | 0 | } |
679 | |
|
680 | 0 | bRet = true; |
681 | 0 | m_pWrtShell->SttSelect(); |
682 | 0 | std::unique_ptr<SwSpellPopup> xPopup(bUseGrammarContext ? |
683 | 0 | new SwSpellPopup(m_pWrtShell.get(), aGrammarCheckRes, nErrorInResult, aSuggestions, aParaText) : |
684 | 0 | new SwSpellPopup(m_pWrtShell.get(), xAlt, aParaText)); |
685 | 0 | ui::ContextMenuExecuteEvent aEvent; |
686 | 0 | const Point aPixPos = GetEditWin().LogicToPixel( rPt ); |
687 | |
|
688 | 0 | aEvent.SourceWindow = VCLUnoHelper::GetInterface( m_pEditWin ); |
689 | 0 | aEvent.ExecutePosition.X = aPixPos.X(); |
690 | 0 | aEvent.ExecutePosition.Y = aPixPos.Y(); |
691 | 0 | rtl::Reference<VCLXPopupMenu> xMenu; |
692 | |
|
693 | 0 | OUString sMenuName = bUseGrammarContext ? |
694 | 0 | u"private:resource/GrammarContextMenu"_ustr : u"private:resource/SpellContextMenu"_ustr; |
695 | 0 | rtl::Reference<VCLXPopupMenu> xMenuInterface = xPopup->CreateMenuInterface(); |
696 | 0 | if (TryContextMenuInterception(xMenuInterface, sMenuName, xMenu, aEvent)) |
697 | 0 | { |
698 | | //! happy hacking for context menu modifying extensions of this |
699 | | //! 'custom made' menu... *sigh* (code copied from sfx2 and framework) |
700 | 0 | if (xMenu.is()) |
701 | 0 | { |
702 | 0 | css::uno::Reference<css::awt::XWindowPeer> xParent(aEvent.SourceWindow, css::uno::UNO_QUERY); |
703 | 0 | const sal_uInt16 nId = xMenu->execute(xParent, css::awt::Rectangle(aPixPos.X(), aPixPos.Y(), 1, 1), |
704 | 0 | css::awt::PopupMenuDirection::EXECUTE_DOWN); |
705 | 0 | OUString aCommand = xMenu->getCommand(nId); |
706 | 0 | if (aCommand.isEmpty() ) |
707 | 0 | { |
708 | 0 | if (!ExecuteMenuCommand(xMenu, GetViewFrame(), nId)) |
709 | 0 | xPopup->Execute(nId); |
710 | 0 | } |
711 | 0 | else |
712 | 0 | { |
713 | 0 | SfxViewFrame& rSfxViewFrame = GetViewFrame(); |
714 | 0 | uno::Reference<frame::XFrame> xFrame = rSfxViewFrame.GetFrame().GetFrameInterface(); |
715 | 0 | css::util::URL aURL; |
716 | 0 | uno::Reference< frame::XDispatchProvider > xDispatchProvider( xFrame, UNO_QUERY ); |
717 | |
|
718 | 0 | try |
719 | 0 | { |
720 | 0 | uno::Reference< frame::XDispatch > xDispatch; |
721 | 0 | uno::Reference< util::XURLTransformer > xURLTransformer = util::URLTransformer::create(comphelper::getProcessComponentContext()); |
722 | |
|
723 | 0 | aURL.Complete = aCommand; |
724 | 0 | xURLTransformer->parseStrict(aURL); |
725 | 0 | xDispatch = xDispatchProvider->queryDispatch( aURL, OUString(), 0 ); |
726 | |
|
727 | 0 | if (xDispatch.is()) |
728 | 0 | { |
729 | | // Execute dispatch asynchronously |
730 | 0 | ExecuteInfo* pExecuteInfo = new ExecuteInfo; |
731 | 0 | pExecuteInfo->xDispatch = std::move(xDispatch); |
732 | 0 | pExecuteInfo->aTargetURL = std::move(aURL); |
733 | 0 | Application::PostUserEvent( LINK(nullptr, AsyncExecute , ExecuteHdl_Impl), pExecuteInfo ); |
734 | 0 | } |
735 | 0 | } |
736 | 0 | catch (const Exception&) |
737 | 0 | { |
738 | 0 | } |
739 | 0 | } |
740 | 0 | } |
741 | 0 | else |
742 | 0 | { |
743 | 0 | if (comphelper::LibreOfficeKit::isActive()) |
744 | 0 | { |
745 | 0 | if (SfxViewShell* pViewShell = SfxViewShell::Current()) |
746 | 0 | { |
747 | 0 | boost::property_tree::ptree aMenu = SfxDispatcher::fillPopupMenu(xMenuInterface); |
748 | 0 | boost::property_tree::ptree aRoot; |
749 | 0 | aRoot.add_child("menu", aMenu); |
750 | |
|
751 | 0 | std::stringstream aStream; |
752 | 0 | boost::property_tree::write_json(aStream, aRoot, true); |
753 | 0 | pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CONTEXT_MENU, OString(aStream.str())); |
754 | 0 | } |
755 | 0 | } |
756 | 0 | else |
757 | 0 | { |
758 | 0 | xPopup->Execute(aToFill.SVRect(), m_pEditWin); |
759 | 0 | } |
760 | 0 | } |
761 | 0 | } |
762 | 0 | } |
763 | |
|
764 | 0 | if (!comphelper::LibreOfficeKit::isActive()) |
765 | 0 | m_pWrtShell->Pop(SwCursorShell::PopMode::DeleteCurrent); |
766 | 0 | m_pWrtShell->LockView( bOldViewLock ); |
767 | 0 | } |
768 | 0 | } |
769 | 0 | return bRet; |
770 | 0 | } |
771 | | |
772 | | /** Function: ExecSmartTagPopup |
773 | | |
774 | | This function shows the popup menu for smarttag |
775 | | actions. |
776 | | */ |
777 | | void SwView::ExecSmartTagPopup( const Point& rPt ) |
778 | 0 | { |
779 | 0 | const bool bOldViewLock = m_pWrtShell->IsViewLocked(); |
780 | 0 | m_pWrtShell->LockView( true ); |
781 | 0 | m_pWrtShell->Push(); |
782 | |
|
783 | 0 | css::uno::Sequence< css::uno::Any > aArgs{ |
784 | 0 | css::uno::Any(comphelper::makePropertyValue( u"Frame"_ustr, GetDispatcher().GetFrame()->GetFrame().GetFrameInterface() )), |
785 | 0 | css::uno::Any(comphelper::makePropertyValue( u"CommandURL"_ustr, u".uno:OpenSmartTagMenuOnCursor"_ustr )) |
786 | 0 | }; |
787 | |
|
788 | 0 | const css::uno::Reference< css::uno::XComponentContext >& xContext = comphelper::getProcessComponentContext(); |
789 | 0 | css::uno::Reference< css::frame::XPopupMenuController > xPopupController( |
790 | 0 | xContext->getServiceManager()->createInstanceWithArgumentsAndContext( |
791 | 0 | u"com.sun.star.comp.svx.SmartTagMenuController"_ustr, aArgs, xContext ), css::uno::UNO_QUERY ); |
792 | |
|
793 | 0 | rtl::Reference< VCLXPopupMenu > xPopupMenu( new VCLXPopupMenu() ); |
794 | |
|
795 | 0 | if ( xPopupController.is() ) |
796 | 0 | { |
797 | 0 | xPopupController->setPopupMenu( xPopupMenu ); |
798 | |
|
799 | 0 | SwRect aToFill; |
800 | 0 | m_pWrtShell->GetSmartTagRect( rPt, aToFill ); |
801 | 0 | m_pWrtShell->SttSelect(); |
802 | |
|
803 | 0 | if ( aToFill.HasArea() ) |
804 | 0 | xPopupMenu->execute( m_pEditWin->GetComponentInterface(), |
805 | 0 | vcl::unohelper::ConvertToAWTRect( m_pEditWin->LogicToPixel( aToFill.SVRect() ) ), css::awt::PopupMenuDirection::EXECUTE_DOWN ); |
806 | |
|
807 | 0 | css::uno::Reference< css::lang::XComponent > xComponent( xPopupController, css::uno::UNO_QUERY ); |
808 | 0 | if ( xComponent.is() ) |
809 | 0 | xComponent->dispose(); |
810 | 0 | } |
811 | |
|
812 | 0 | m_pWrtShell->Pop(SwCursorShell::PopMode::DeleteCurrent); |
813 | 0 | m_pWrtShell->LockView( bOldViewLock ); |
814 | 0 | } |
815 | | |
816 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |