LibreOffice Module sw (master) 1
SwGrammarContact.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <vcl/timer.hxx>
21#include <IGrammarContact.hxx>
22#include <pam.hxx>
23#include <ndtxt.hxx>
24#include <SwGrammarMarkUp.hxx>
25#include <txtfrm.hxx>
26#include <svl/listener.hxx>
27
28namespace {
29
30/*
31 * This class is responsible for the delayed display of grammar checks when a paragraph is edited
32 * It's a client of the paragraph the cursor points to.
33 * If the cursor position changes, updateCursorPosition has to be called
34 * If the grammar checker wants to set a grammar marker at a paragraph, he has to request
35 * the grammar list from this class. If the requested paragraph is not edited, it returns
36 * the normal grammar list. But if the paragraph is the active one, a proxy list will be returned and
37 * all changes are set in this proxy list. If the cursor leaves the paragraph the proxy list
38 * will replace the old list. If the grammar checker has completed the paragraph ('setChecked')
39 * then a timer is setup which replaces the old list as well.
40 */
41class SwGrammarContact final : public IGrammarContact, public SvtListener
42{
43 Timer m_aTimer;
44 std::unique_ptr<SwGrammarMarkUp> m_pProxyList;
45 bool m_isFinished;
46 SwTextNode* m_pTextNode;
47 DECL_LINK( TimerRepaint, Timer *, void );
48
49public:
50 SwGrammarContact();
51 virtual ~SwGrammarContact() override { m_aTimer.Stop(); }
52
53 // (pure) virtual functions of IGrammarContact
54 virtual void updateCursorPosition( const SwPosition& rNewPos ) override;
55 virtual SwGrammarMarkUp* getGrammarCheck( SwTextNode& rTextNode, bool bCreate ) override;
56 virtual void finishGrammarCheck( SwTextNode& rTextNode ) override;
57 void CheckBroadcaster()
58 {
59 if(HasBroadcaster())
60 return;
61 m_pTextNode = nullptr;
62 m_pProxyList.reset();
63 }
64};
65
66}
67
68SwGrammarContact::SwGrammarContact()
69 : m_aTimer( "sw::SwGrammarContact TimerRepaint" ),
70 m_isFinished( false ),
71 m_pTextNode(nullptr)
72{
73 m_aTimer.SetTimeout( 2000 ); // Repaint of grammar check after 'setChecked'
74 m_aTimer.SetInvokeHandler( LINK(this, SwGrammarContact, TimerRepaint) );
75}
76
77IMPL_LINK( SwGrammarContact, TimerRepaint, Timer *, pTimer, void )
78{
79 CheckBroadcaster();
80 if( pTimer )
81 {
82 pTimer->Stop();
83 if( m_pTextNode )
84 { //Replace the old wrong list by the proxy list and repaint all frames
85 m_pTextNode->SetGrammarCheck( std::move(m_pProxyList) );
86 SwTextFrame::repaintTextFrames( *m_pTextNode );
87 }
88 }
89}
90
91/* I'm always a client of the current paragraph */
92void SwGrammarContact::updateCursorPosition( const SwPosition& rNewPos )
93{
94 CheckBroadcaster();
95 SwTextNode* pTextNode = rNewPos.GetNode().GetTextNode();
96 if( pTextNode == m_pTextNode ) // paragraph has been changed
97 return;
98
99 m_aTimer.Stop();
100 if( m_pTextNode ) // My last paragraph has been left
101 {
102 if( m_pProxyList )
103 { // replace old list by the proxy list and repaint
104 m_pTextNode->SetGrammarCheck( std::move(m_pProxyList) );
105 SwTextFrame::repaintTextFrames( *m_pTextNode );
106 }
107 EndListeningAll();
108 }
109 if( pTextNode )
110 {
111 m_pTextNode = pTextNode;
112 EndListeningAll();
113 StartListening(pTextNode->GetNotifier()); // welcome new paragraph
114 }
115}
116
117/* deliver a grammar check list for the given text node */
118SwGrammarMarkUp* SwGrammarContact::getGrammarCheck( SwTextNode& rTextNode, bool bCreate )
119{
120 SwGrammarMarkUp *pRet = nullptr;
121 CheckBroadcaster();
122 if( m_pTextNode == &rTextNode ) // hey, that's my current paragraph!
123 { // so you will get a proxy list...
124 if( bCreate )
125 {
126 if( m_isFinished )
127 {
128 m_pProxyList.reset();
129 }
130 if( !m_pProxyList )
131 {
132 if( rTextNode.GetGrammarCheck() )
133 m_pProxyList.reset( static_cast<SwGrammarMarkUp*>(rTextNode.GetGrammarCheck()->Clone()) );
134 else
135 {
136 m_pProxyList.reset( new SwGrammarMarkUp() );
137 m_pProxyList->SetInvalid( 0, COMPLETE_STRING );
138 }
139 }
140 m_isFinished = false;
141 }
142 pRet = m_pProxyList.get();
143 }
144 else
145 {
146 pRet = rTextNode.GetGrammarCheck(); // do you have already a list?
147 if( bCreate && !pRet ) // do you want to create a list?
148 {
149 pRet = new SwGrammarMarkUp();
150 pRet->SetInvalid( 0, COMPLETE_STRING );
151 rTextNode.SetGrammarCheck( std::unique_ptr<SwGrammarMarkUp>(pRet) );
152 rTextNode.SetGrammarCheckDirty( true );
153 }
154 }
155 return pRet;
156}
157
159{
160 CheckBroadcaster();
161 if( &rTextNode != m_pTextNode ) // not my paragraph
162 SwTextFrame::repaintTextFrames( rTextNode ); // can be repainted directly
163 else
164 {
165 if( m_pProxyList )
166 {
167 m_isFinished = true;
168 m_aTimer.Start(); // will replace old list and repaint with delay
169 }
170 else if( m_pTextNode->GetGrammarCheck() )
171 { // all grammar problems seems to be gone, no delay needed
172 m_pTextNode->ClearGrammarCheck();
173 SwTextFrame::repaintTextFrames( *m_pTextNode );
174 }
175 }
176}
177
179{
180 return new SwGrammarContact();
181}
182
184{
185 IGrammarContact* pGrammarContact = getGrammarContact( rTextNode );
186 if( pGrammarContact )
187 pGrammarContact->finishGrammarCheck( rTextNode );
188}
189
190/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
IGrammarContact * createGrammarContact()
Factory for a grammar contact.
void finishGrammarCheck(SwTextNode &rTextNode)
finishGrammarCheck() calls the same function of the grammar contact of the document (for a given text...
IMPL_LINK(SwGrammarContact, TimerRepaint, Timer *, pTimer, void)
Organizer of the contact between SwTextNodes and grammar checker.
virtual SwGrammarMarkUp * getGrammarCheck(SwTextNode &rTextNode, bool bCreate)=0
getGrammarCheck checks if the given text node is blocked by the current cursor if not,...
virtual void finishGrammarCheck(SwTextNode &rTextNode)=0
finishGrammarCheck() has to be called if a grammar checking has been completed for a text node.
virtual void updateCursorPosition(const SwPosition &rNewPos)=0
Update cursor position reacts to a change of the current input cursor As long as the cursor in inside...
bool HasBroadcaster() const
virtual SwWrongList * Clone() override
SwTextNode * GetTextNode()
Inline methods from Node.hxx.
Definition: ndtxt.hxx:877
static void repaintTextFrames(const SwTextNode &rNode)
Repaint all text frames of the given text node.
Definition: txtfrm.cxx:4055
SwTextNode is a paragraph in the document model.
Definition: ndtxt.hxx:89
void SetGrammarCheckDirty(bool bNew) const
Definition: txtedt.cxx:2325
void ClearGrammarCheck()
Definition: txtedt.cxx:2243
SwGrammarMarkUp * GetGrammarCheck()
Definition: txtedt.cxx:2254
void SetGrammarCheck(std::unique_ptr< SwGrammarMarkUp > pNew)
Definition: txtedt.cxx:2237
void SetInvalid(sal_Int32 nBegin, sal_Int32 nEnd)
Definition: wrong.cxx:254
SvtBroadcaster & GetNotifier()
Definition: calbck.hxx:101
DECL_LINK(CheckNameHdl, SvxNameDialog &, bool)
IGrammarContact * getGrammarContact(const SwTextNode &rTextNode)
getGrammarContact() delivers the grammar contact of the document (for a given textnode)
Definition: docnew.cxx:811
Marks a position in the document model.
Definition: pam.hxx:37
SwNode & GetNode() const
Definition: pam.hxx:80
constexpr sal_Int32 COMPLETE_STRING
Definition: swtypes.hxx:57