1 | /* **************************************************************************** |
2 | This file is part of Lokalize |
3 | |
4 | Copyright (C) 2007-2009 by Nick Shaforostoff <shafff@ukr.net> |
5 | |
6 | This program is free software; you can redistribute it and/or |
7 | modify it under the terms of the GNU General Public License as |
8 | published by the Free Software Foundation; either version 2 of |
9 | the License or (at your option) version 3 or any later version |
10 | accepted by the membership of KDE e.V. (or its successor approved |
11 | by the membership of KDE e.V.), which shall act as a proxy |
12 | defined in Section 14 of version 3 of the license. |
13 | |
14 | This program is distributed in the hope that it will be useful, |
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | GNU General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU General Public License |
20 | along with this program. If not, see <http://www.gnu.org/licenses/>. |
21 | |
22 | **************************************************************************** */ |
23 | |
24 | #include "editortab.h" |
25 | #include "editorview.h" |
26 | #include "catalog.h" |
27 | #include "pos.h" |
28 | #include "cmd.h" |
29 | #include "project.h" |
30 | #include "prefs_lokalize.h" |
31 | #include "ui_kaider_findextension.h" |
32 | #include "stemming.h" |
33 | |
34 | |
35 | #include <kglobal.h> |
36 | #include <kmessagebox.h> |
37 | #include <klocale.h> |
38 | #include <kdebug.h> |
39 | #include <kurl.h> |
40 | |
41 | #include <kprogressdialog.h> |
42 | |
43 | #include <kreplacedialog.h> |
44 | #include <kreplace.h> |
45 | |
46 | #include <sonnet/backgroundchecker.h> |
47 | #include <sonnet/dialog.h> |
48 | |
49 | #include <QTimer> |
50 | #include <QPointer> |
51 | |
52 | |
53 | #define IGNOREACCELS KFind::MinimumUserOption |
54 | #define INCLUDENOTES KFind::MinimumUserOption*2 |
55 | |
56 | static long makeOptions(long options, const Ui_findExtension* ui_findExtension) |
57 | { |
58 | return options |
59 | +IGNOREACCELS*ui_findExtension->m_ignoreAccelMarks->isChecked() |
60 | +INCLUDENOTES*ui_findExtension->m_notes->isChecked(); |
61 | //bool skipMarkup(){return ui_findExtension->m_skipTags->isChecked();} |
62 | } |
63 | |
64 | class EntryFindDialog: public KFindDialog |
65 | { |
66 | public: |
67 | EntryFindDialog(QWidget* parent); |
68 | ~EntryFindDialog(); |
69 | long options() const{return makeOptions(KFindDialog::options(),ui_findExtension);} |
70 | static EntryFindDialog* instance(QWidget* parent=0); |
71 | private: |
72 | static QPointer<EntryFindDialog> _instance; |
73 | static void cleanup(){delete EntryFindDialog::_instance;} |
74 | private: |
75 | Ui_findExtension* ui_findExtension; |
76 | }; |
77 | |
78 | QPointer<EntryFindDialog> EntryFindDialog::_instance=0; |
79 | EntryFindDialog* EntryFindDialog::instance(QWidget* parent) |
80 | { |
81 | if (_instance==0 ) |
82 | { |
83 | _instance=new EntryFindDialog(parent); |
84 | qAddPostRoutine(EntryFindDialog::cleanup); |
85 | } |
86 | return _instance; |
87 | } |
88 | |
89 | EntryFindDialog::EntryFindDialog(QWidget* parent) |
90 | : KFindDialog(parent) |
91 | , ui_findExtension(new Ui_findExtension) |
92 | { |
93 | ui_findExtension->setupUi(findExtension()); |
94 | setHasSelection(false); |
95 | |
96 | KConfig config; |
97 | KConfigGroup stateGroup(&config,"FindReplace" ); |
98 | setOptions(stateGroup.readEntry("FindOptions" ,(qlonglong)0)); |
99 | setFindHistory(stateGroup.readEntry("FindHistory" ,QStringList())); |
100 | } |
101 | |
102 | EntryFindDialog::~EntryFindDialog() |
103 | { |
104 | KConfig config; |
105 | KConfigGroup stateGroup(&config,"FindReplace" ); |
106 | stateGroup.writeEntry("FindOptions" ,(qlonglong)options()); |
107 | stateGroup.writeEntry("FindHistory" ,findHistory()); |
108 | |
109 | delete ui_findExtension; |
110 | } |
111 | |
112 | //BEGIN EntryReplaceDialog |
113 | class EntryReplaceDialog: public KReplaceDialog |
114 | { |
115 | public: |
116 | EntryReplaceDialog(QWidget* parent); |
117 | ~EntryReplaceDialog(); |
118 | long options() const{return makeOptions(KReplaceDialog::options(),ui_findExtension);} |
119 | static EntryReplaceDialog* instance(QWidget* parent=0); |
120 | private: |
121 | static QPointer<EntryReplaceDialog> _instance; |
122 | static void cleanup(){delete EntryReplaceDialog::_instance;} |
123 | private: |
124 | Ui_findExtension* ui_findExtension; |
125 | }; |
126 | |
127 | QPointer<EntryReplaceDialog> EntryReplaceDialog::_instance=0; |
128 | EntryReplaceDialog* EntryReplaceDialog::instance(QWidget* parent) |
129 | { |
130 | if (_instance==0 ) |
131 | { |
132 | _instance=new EntryReplaceDialog(parent); |
133 | qAddPostRoutine(EntryReplaceDialog::cleanup); |
134 | } |
135 | return _instance; |
136 | } |
137 | |
138 | EntryReplaceDialog::EntryReplaceDialog(QWidget* parent) |
139 | : KReplaceDialog(parent) |
140 | , ui_findExtension(new Ui_findExtension) |
141 | { |
142 | ui_findExtension->setupUi(findExtension()); |
143 | //ui_findExtension->m_notes->hide(); |
144 | setHasSelection(false); |
145 | |
146 | KConfig config; |
147 | KConfigGroup stateGroup(&config,"FindReplace" ); |
148 | setOptions(stateGroup.readEntry("ReplaceOptions" ,(qlonglong)0)); |
149 | setFindHistory(stateGroup.readEntry("ReplacePatternHistory" ,QStringList())); |
150 | setReplacementHistory(stateGroup.readEntry("ReplacementHistory" ,QStringList())); |
151 | } |
152 | |
153 | EntryReplaceDialog::~EntryReplaceDialog() |
154 | { |
155 | KConfig config; |
156 | KConfigGroup stateGroup(&config,"FindReplace" ); |
157 | stateGroup.writeEntry("ReplaceOptions" ,(qlonglong)options()); |
158 | stateGroup.writeEntry("ReplacePatternHistory" ,findHistory()); |
159 | stateGroup.writeEntry("ReplacementHistory" ,replacementHistory()); |
160 | |
161 | delete ui_findExtension; |
162 | } |
163 | //END EntryReplaceDialog |
164 | |
165 | //TODO &, |
166 | static void calcOffsetWithAccels(const QString& data, int& offset, int& length) |
167 | { |
168 | int i=0; |
169 | for (;i<offset;++i) |
170 | if (KDE_ISUNLIKELY( data.at(i)=='&' )) |
171 | ++offset; |
172 | |
173 | //if & is inside highlighted word |
174 | int limit=offset+length; |
175 | for (i=offset;i<limit;++i) |
176 | if (KDE_ISUNLIKELY( data.at(i)=='&' )) |
177 | { |
178 | ++length; |
179 | limit=qMin(data.size(),offset+length);//just safety |
180 | } |
181 | } |
182 | |
183 | void EditorTab::find() |
184 | { |
185 | //QWidget* p=0; QWidget* next=qobject_cast<QWidget*>(parent()); while(next) { p=next; next=qobject_cast<QWidget*>(next->parent()); } |
186 | EntryFindDialog::instance(nativeParentWidget()); |
187 | |
188 | QString sel=selectionInTarget(); |
189 | if (!(sel.isEmpty()&&selectionInSource().isEmpty())) |
190 | { |
191 | if (sel.isEmpty()) |
192 | sel=selectionInSource(); |
193 | if (_find&&_find->options()&IGNOREACCELS) |
194 | sel.remove('&'); |
195 | EntryFindDialog::instance()->setPattern(sel); |
196 | } |
197 | |
198 | if ( EntryFindDialog::instance()->exec() != QDialog::Accepted ) |
199 | return; |
200 | |
201 | if (_find) |
202 | { |
203 | _find->resetCounts(); |
204 | _find->setPattern(EntryFindDialog::instance()->pattern()); |
205 | _find->setOptions(EntryFindDialog::instance()->options()); |
206 | |
207 | } |
208 | else // This creates a find-next-prompt dialog if needed. |
209 | { |
210 | _find = new KFind(EntryFindDialog::instance()->pattern(),EntryFindDialog::instance()->options(),this,EntryFindDialog::instance()); |
211 | connect(_find,SIGNAL(highlight(QString,int,int)),this, SLOT(highlightFound(QString,int,int)) ); |
212 | connect(_find,SIGNAL(findNext()),this,SLOT(findNext())); |
213 | _find->closeFindNextDialog(); |
214 | } |
215 | |
216 | DocPosition pos; |
217 | if (_find->options() & KFind::FromCursor) |
218 | pos=m_currentPos; |
219 | else if (!determineStartingPos(_find,pos)) |
220 | return; |
221 | |
222 | |
223 | findNext(pos); |
224 | } |
225 | |
226 | bool EditorTab::determineStartingPos(KFind* find, |
227 | DocPosition& pos) |
228 | { |
229 | if (find->options() & KFind::FindBackwards) |
230 | { |
231 | pos.entry=m_catalog->numberOfEntries()-1; |
232 | pos.form=(m_catalog->isPlural(pos.entry))? |
233 | m_catalog->numberOfPluralForms()-1:0; |
234 | } |
235 | else |
236 | { |
237 | pos.entry=0; |
238 | pos.form=0; |
239 | } |
240 | return true; |
241 | } |
242 | |
243 | void EditorTab::findNext(const DocPosition& startingPos) |
244 | { |
245 | Catalog& catalog=*m_catalog; |
246 | KFind& find=*_find; |
247 | |
248 | if (KDE_ISUNLIKELY( catalog.numberOfEntries()<=startingPos.entry )) |
249 | return;//for the case when app wasn't able to process event before file close |
250 | |
251 | bool anotherEntry=_searchingPos.entry!=m_currentPos.entry; |
252 | _searchingPos=startingPos; |
253 | |
254 | if (anotherEntry) |
255 | _searchingPos.offset=0; |
256 | |
257 | |
258 | QRegExp rx("[^(\\\\n)>]\n" ); |
259 | QTime a;a.start(); |
260 | //_searchingPos.part=DocPosition::Source; |
261 | bool ignoreaccels=_find->options()&IGNOREACCELS; |
262 | bool includenotes=_find->options()&INCLUDENOTES; |
263 | int switchOptions=DocPosition::Source|DocPosition::Target|(includenotes*DocPosition::Comment); |
264 | int flag=1; |
265 | while (flag) |
266 | { |
267 | |
268 | flag=0; |
269 | KFind::Result res = KFind::NoMatch; |
270 | while (true) |
271 | { |
272 | if (find.needData()||anotherEntry||m_view->m_modifiedAfterFind) |
273 | { |
274 | anotherEntry=false; |
275 | m_view->m_modifiedAfterFind=false; |
276 | |
277 | QString data; |
278 | if (_searchingPos.part==DocPosition::Comment) |
279 | data=catalog.notes(_searchingPos).at(_searchingPos.form).content; |
280 | else |
281 | data=catalog.catalogString(_searchingPos).string; |
282 | |
283 | if (ignoreaccels) |
284 | data.remove('&'); |
285 | find.setData(data); |
286 | } |
287 | |
288 | res = find.find(); |
289 | //offset=-1; |
290 | if (res!=KFind::NoMatch) |
291 | break; |
292 | |
293 | if (!( |
294 | (find.options()&KFind::FindBackwards)? |
295 | switchPrev(m_catalog,_searchingPos,switchOptions): |
296 | switchNext(m_catalog,_searchingPos,switchOptions) |
297 | )) |
298 | break; |
299 | } |
300 | |
301 | if (res==KFind::NoMatch) |
302 | { |
303 | //file-wide search |
304 | if(find.shouldRestart(true,true)) |
305 | { |
306 | flag=1; |
307 | determineStartingPos(_find,_searchingPos); |
308 | } |
309 | find.resetCounts(); |
310 | } |
311 | } |
312 | } |
313 | |
314 | void EditorTab::findNext() |
315 | { |
316 | if (_find) |
317 | { |
318 | findNext((m_currentPos.entry==_searchingPos.entry&&_searchingPos.part==DocPosition::Comment)? |
319 | _searchingPos:m_currentPos); |
320 | } |
321 | else |
322 | find(); |
323 | |
324 | } |
325 | |
326 | void EditorTab::findPrev() |
327 | { |
328 | |
329 | if (_find) |
330 | { |
331 | _find->setOptions(_find->options() ^ KFind::FindBackwards); |
332 | findNext(m_currentPos); |
333 | } |
334 | else |
335 | { |
336 | find(); |
337 | } |
338 | |
339 | } |
340 | |
341 | void EditorTab::highlightFound(const QString &,int matchingIndex,int matchedLength) |
342 | { |
343 | if (_find->options()&IGNOREACCELS && _searchingPos.part!=DocPosition::Comment) |
344 | { |
345 | QString data=m_catalog->catalogString(_searchingPos).string; |
346 | calcOffsetWithAccels(data, matchingIndex, matchedLength); |
347 | } |
348 | |
349 | _searchingPos.offset=matchingIndex; |
350 | gotoEntry(_searchingPos,matchedLength); |
351 | } |
352 | |
353 | void EditorTab::replace() |
354 | { |
355 | EntryReplaceDialog::instance(nativeParentWidget()); |
356 | |
357 | if (!m_view->selectionInTarget().isEmpty()) |
358 | { |
359 | if (_replace&&_replace->options()&IGNOREACCELS) |
360 | { |
361 | QString tmp(m_view->selectionInTarget()); |
362 | tmp.remove('&'); |
363 | EntryReplaceDialog::instance()->setPattern(tmp); |
364 | } |
365 | else |
366 | EntryReplaceDialog::instance()->setPattern(m_view->selectionInTarget()); |
367 | } |
368 | |
369 | |
370 | if ( EntryReplaceDialog::instance()->exec() != QDialog::Accepted ) |
371 | return; |
372 | |
373 | |
374 | if (_replace) _replace->deleteLater();// _replace=0; |
375 | |
376 | // This creates a find-next-prompt dialog if needed. |
377 | { |
378 | _replace = new KReplace(EntryReplaceDialog::instance()->pattern(),EntryReplaceDialog::instance()->replacement(),EntryReplaceDialog::instance()->options(),this,EntryReplaceDialog::instance()); |
379 | connect(_replace,SIGNAL(highlight(QString,int,int)), this,SLOT(highlightFound_(QString,int,int))); |
380 | connect(_replace,SIGNAL(findNext()), this,SLOT(replaceNext())); |
381 | connect(_replace,SIGNAL(replace(QString,int,int,int)), this,SLOT(doReplace(QString,int,int,int))); |
382 | connect(_replace,SIGNAL(dialogClosed()), this,SLOT(cleanupReplace())); |
383 | // _replace->closeReplaceNextDialog(); |
384 | } |
385 | // else |
386 | // { |
387 | // _replace->resetCounts(); |
388 | // _replace->setPattern(EntryReplaceDialog::instance()->pattern()); |
389 | // _replace->setOptions(EntryReplaceDialog::instance()->options()); |
390 | // } |
391 | |
392 | //m_catalog->beginMacro(i18nc("@item Undo action item","Replace")); |
393 | m_doReplaceCalled=false; |
394 | |
395 | if (_replace->options() & KFind::FromCursor) |
396 | replaceNext(m_currentPos); |
397 | else |
398 | { |
399 | DocPosition pos; |
400 | if (!determineStartingPos(_replace,pos)) return; |
401 | replaceNext(pos); |
402 | } |
403 | |
404 | } |
405 | |
406 | |
407 | void EditorTab::replaceNext(const DocPosition& startingPos) |
408 | { |
409 | bool anotherEntry=_replacingPos.entry!=_replacingPos.entry; |
410 | _replacingPos=startingPos; |
411 | |
412 | if (anotherEntry) |
413 | _replacingPos.offset=0; |
414 | |
415 | |
416 | int flag=1; |
417 | bool ignoreaccels=_replace->options()&IGNOREACCELS; |
418 | bool includenotes=_replace->options()&INCLUDENOTES; |
419 | kWarning()<<"includenotes" <<includenotes; |
420 | int switchOptions=DocPosition::Target|(includenotes*DocPosition::Comment); |
421 | while (flag) |
422 | { |
423 | flag=0; |
424 | KFind::Result res=KFind::NoMatch; |
425 | while (1) |
426 | { |
427 | if (_replace->needData()||anotherEntry/*||m_view->m_modifiedAfterFind*/) |
428 | { |
429 | anotherEntry=false; |
430 | //m_view->m_modifiedAfterFind=false;//NOTE TEST THIS |
431 | |
432 | QString data; |
433 | if (_replacingPos.part==DocPosition::Comment) |
434 | data=m_catalog->notes(_replacingPos).at(_replacingPos.form).content; |
435 | else |
436 | { |
437 | data=m_catalog->targetWithTags(_replacingPos).string; |
438 | if (ignoreaccels) data.remove('&'); |
439 | } |
440 | _replace->setData(data); |
441 | } |
442 | res = _replace->replace(); |
443 | if (res!=KFind::NoMatch) |
444 | break; |
445 | |
446 | if (!( |
447 | (_replace->options()&KFind::FindBackwards)? |
448 | switchPrev(m_catalog,_replacingPos,switchOptions): |
449 | switchNext(m_catalog,_replacingPos,switchOptions) |
450 | )) |
451 | break; |
452 | } |
453 | |
454 | if (res==KFind::NoMatch) |
455 | { |
456 | if((_replace->options()&KFind::FromCursor) |
457 | &&_replace->shouldRestart(true)) |
458 | { |
459 | flag=1; |
460 | determineStartingPos(_replace,_replacingPos); |
461 | } |
462 | else |
463 | { |
464 | if(!(_replace->options() & KFind::FromCursor)) |
465 | _replace->displayFinalDialog(); |
466 | |
467 | _replace->closeReplaceNextDialog(); |
468 | cleanupReplace(); |
469 | } |
470 | _replace->resetCounts(); |
471 | } |
472 | } |
473 | } |
474 | |
475 | void EditorTab::cleanupReplace() |
476 | { |
477 | if(m_doReplaceCalled) |
478 | { |
479 | m_doReplaceCalled=false; |
480 | m_catalog->endMacro(); |
481 | } |
482 | } |
483 | |
484 | void EditorTab::replaceNext() |
485 | { |
486 | replaceNext(m_currentPos); |
487 | } |
488 | |
489 | void EditorTab::highlightFound_(const QString &,int matchingIndex,int matchedLength) |
490 | { |
491 | if (_replace->options()&IGNOREACCELS) |
492 | { |
493 | QString data=m_catalog->targetWithTags(_replacingPos).string; |
494 | calcOffsetWithAccels(data,matchingIndex,matchedLength); |
495 | } |
496 | |
497 | _replacingPos.offset=matchingIndex; |
498 | gotoEntry(_replacingPos,matchedLength); |
499 | } |
500 | |
501 | |
502 | void EditorTab::doReplace(const QString &newStr,int offset,int newLen,int remLen) |
503 | { |
504 | if(!m_doReplaceCalled) |
505 | { |
506 | m_doReplaceCalled=true; |
507 | m_catalog->beginMacro(i18nc("@item Undo action item" ,"Replace" )); |
508 | } |
509 | DocPosition pos=_replacingPos; |
510 | if (_replacingPos.part==DocPosition::Comment) |
511 | m_catalog->push(new SetNoteCmd(m_catalog,pos,newStr)); |
512 | else |
513 | { |
514 | QString oldStr=m_catalog->target(_replacingPos); |
515 | |
516 | if (_replace->options()&IGNOREACCELS) |
517 | calcOffsetWithAccels(oldStr,offset,remLen); |
518 | |
519 | pos.offset=offset; |
520 | m_catalog->push(new DelTextCmd(m_catalog,pos,oldStr.mid(offset,remLen))); |
521 | |
522 | if (newLen) |
523 | m_catalog->push(new InsTextCmd(m_catalog,pos,newStr.mid(offset,newLen))); |
524 | } |
525 | if (pos.entry==m_currentPos.entry) |
526 | { |
527 | pos.offset+=newLen; |
528 | m_view->gotoEntry(pos); |
529 | } |
530 | } |
531 | |
532 | |
533 | |
534 | |
535 | |
536 | |
537 | |
538 | |
539 | |
540 | |
541 | void EditorTab::spellcheck() |
542 | { |
543 | if (!m_sonnetDialog) |
544 | { |
545 | m_sonnetChecker=new Sonnet::BackgroundChecker(this); |
546 | m_sonnetChecker->changeLanguage(enhanceLangCode(Project::instance()->langCode())); |
547 | m_sonnetDialog=new Sonnet::Dialog(m_sonnetChecker,this); |
548 | connect(m_sonnetDialog,SIGNAL(done(QString)),this,SLOT(spellcheckNext())); |
549 | connect(m_sonnetDialog,SIGNAL(replace(QString,int,QString)), |
550 | this,SLOT(spellcheckReplace(QString,int,QString))); |
551 | connect(m_sonnetDialog,SIGNAL(stop()),this,SLOT(spellcheckStop())); |
552 | connect(m_sonnetDialog,SIGNAL(cancel()),this,SLOT(spellcheckCancel())); |
553 | |
554 | connect(m_sonnetDialog/*m_sonnetChecker*/,SIGNAL(misspelling(QString,int)), |
555 | this,SLOT(spellcheckShow(QString,int))); |
556 | // disconnect(/*m_sonnetDialog*/m_sonnetChecker,SIGNAL(misspelling(QString,int)), |
557 | // m_sonnetDialog,SLOT(slotMisspelling(QString,int))); |
558 | // |
559 | // connect( d->checker, SIGNAL(misspelling(const QString&, int)), |
560 | // SLOT(slotMisspelling(const QString&, int)) ); |
561 | } |
562 | |
563 | QString text=m_catalog->msgstr(m_currentPos); |
564 | if (!m_view->selectionInTarget().isEmpty()) |
565 | text=m_view->selectionInTarget(); |
566 | text.remove('&'); |
567 | m_sonnetDialog->setBuffer(text); |
568 | |
569 | _spellcheckPos=m_currentPos; |
570 | _spellcheckStartPos=m_currentPos; |
571 | _spellcheckStop=false; |
572 | //m_catalog->beginMacro(i18n("Spellcheck")); |
573 | _spellcheckStartUndoIndex=m_catalog->index(); |
574 | m_sonnetDialog->show(); |
575 | |
576 | } |
577 | |
578 | |
579 | void EditorTab::spellcheckNext() |
580 | { |
581 | if (_spellcheckStop) |
582 | return; |
583 | |
584 | do |
585 | { |
586 | if (!switchNext(m_catalog,_spellcheckPos)) |
587 | { |
588 | kWarning()<<_spellcheckStartPos.entry; |
589 | kWarning()<<_spellcheckStartPos.form; |
590 | bool continueFromStart= |
591 | !(_spellcheckStartPos.entry==0 && _spellcheckStartPos.form==0) |
592 | && KMessageBox::questionYesNo(this,i18n("Lokalize has reached end of document. Do you want to continue from start?" ), i18nc("@title" , "Spellcheck" ))==KMessageBox::Yes; |
593 | if (continueFromStart) |
594 | { |
595 | _spellcheckStartPos.entry=0; |
596 | _spellcheckStartPos.form=0; |
597 | _spellcheckPos=_spellcheckStartPos; |
598 | } |
599 | else |
600 | { |
601 | KMessageBox::information(this,i18n("Lokalize has finished spellchecking" ), i18nc("@title" , "Spellcheck" )); |
602 | return; |
603 | } |
604 | } |
605 | } |
606 | while (m_catalog->msgstr(_spellcheckPos).isEmpty() || !m_catalog->isApproved(_spellcheckPos.entry)); |
607 | |
608 | QString text=m_catalog->msgstr(_spellcheckPos); |
609 | text.remove('&'); |
610 | m_sonnetDialog->setBuffer(text); |
611 | } |
612 | |
613 | void EditorTab::spellcheckStop() |
614 | { |
615 | _spellcheckStop=true; |
616 | } |
617 | |
618 | void EditorTab::spellcheckCancel() |
619 | { |
620 | m_catalog->setIndex(_spellcheckStartUndoIndex); |
621 | gotoEntry(_spellcheckPos); |
622 | } |
623 | |
624 | void EditorTab::spellcheckShow(const QString &word, int offset) |
625 | { |
626 | QString source=m_catalog->source(_spellcheckPos); |
627 | source.remove('&'); |
628 | if (source.contains(word)) |
629 | { |
630 | m_sonnetDialog->setUpdatesEnabled(false); |
631 | m_sonnetChecker->continueChecking(); |
632 | return; |
633 | } |
634 | m_sonnetDialog->setUpdatesEnabled(true); |
635 | |
636 | show(); |
637 | |
638 | DocPosition pos=_spellcheckPos; |
639 | int length=word.length(); |
640 | calcOffsetWithAccels(m_catalog->target(pos),offset,length); |
641 | pos.offset=offset; |
642 | |
643 | gotoEntry(pos,length); |
644 | } |
645 | |
646 | void EditorTab::spellcheckReplace(QString oldWord, int offset, const QString &newWord) |
647 | { |
648 | DocPosition pos=_spellcheckPos; |
649 | int length=oldWord.length(); |
650 | calcOffsetWithAccels(m_catalog->target(pos),offset,length); |
651 | pos.offset=offset; |
652 | if (length>oldWord.length())//replaced word contains accel mark |
653 | oldWord=m_catalog->target(pos).mid(offset,length); |
654 | |
655 | m_catalog->push(new DelTextCmd(m_catalog,pos,oldWord)); |
656 | m_catalog->push(new InsTextCmd(m_catalog,pos,newWord)); |
657 | |
658 | |
659 | gotoEntry(pos,newWord.length()); |
660 | } |
661 | |
662 | |
663 | |
664 | |
665 | |
666 | |
667 | |
668 | |
669 | |
670 | |
671 | |
672 | bool EditorTab::findEntryBySourceContext(const QString& source, const QString& ctxt) |
673 | { |
674 | DocPosition pos(0); |
675 | do |
676 | { |
677 | if (m_catalog->source(pos)==source && m_catalog->context(pos.entry)==QStringList(ctxt)) |
678 | { |
679 | gotoEntry(pos); |
680 | return true; |
681 | } |
682 | } |
683 | while (switchNext(m_catalog,pos)); |
684 | return false; |
685 | } |
686 | |
687 | |
688 | void EditorTab::displayWordCount() |
689 | { |
690 | //TODO in trans and fuzzy separately |
691 | int sourceCount=0; |
692 | int targetCount=0; |
693 | QRegExp rxClean(Project::instance()->markup()+'|'+Project::instance()->accel());//cleaning regexp; NOTE isEmpty()? |
694 | QRegExp rxSplit("\\W|\\d" );//splitting regexp |
695 | DocPosition pos(0); |
696 | do |
697 | { |
698 | QString msg=m_catalog->source(pos); |
699 | msg.remove(rxClean); |
700 | QStringList words=msg.split(rxSplit,QString::SkipEmptyParts); |
701 | sourceCount+=words.size(); |
702 | |
703 | msg=m_catalog->target(pos); |
704 | msg.remove(rxClean); |
705 | words=msg.split(rxSplit,QString::SkipEmptyParts); |
706 | targetCount+=words.size(); |
707 | } |
708 | while (switchNext(m_catalog,pos)); |
709 | |
710 | KMessageBox::information(this, i18nc("@info words count" , |
711 | "Source text words: %1<br/>Target text words: %2" , |
712 | sourceCount,targetCount),i18nc("@title" ,"Word Count" )); |
713 | } |
714 | |
715 | |
716 | |