TDE core libraries
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1025 lines
31KB

  1. /* This file is part of the KDE libraries
  2. Copyright (C) 2004-2005 Anders Lund <anders@alweb.dk>
  3. Copyright (C) 2003 Clarence Dang <dang@kde.org>
  4. Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org>
  5. Copyright (C) 2001-2004 Christoph Cullmann <cullmann@kde.org>
  6. Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
  7. Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
  8. This library is free software; you can redistribute it and/or
  9. modify it under the terms of the GNU Library General Public
  10. License version 2 as published by the Free Software Foundation.
  11. This library is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. Library General Public License for more details.
  15. You should have received a copy of the GNU Library General Public License
  16. along with this library; see the file COPYING.LIB. If not, write to
  17. the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  18. Boston, MA 02110-1301, USA.
  19. */
  20. #include "katesearch.h"
  21. #include "katesearch.moc"
  22. #include "kateview.h"
  23. #include "katedocument.h"
  24. #include "katesupercursor.h"
  25. #include "katearbitraryhighlight.h"
  26. #include "kateconfig.h"
  27. #include "katehighlight.h"
  28. #include <tdelocale.h>
  29. #include <kstdaction.h>
  30. #include <tdemessagebox.h>
  31. #include <kstringhandler.h>
  32. #include <kdebug.h>
  33. #include <kfinddialog.h>
  34. #include <kreplacedialog.h>
  35. #include <kpushbutton.h>
  36. #include <tqlayout.h>
  37. #include <tqlabel.h>
  38. //BEGIN KateSearch
  39. TQStringList KateSearch::s_searchList = TQStringList();
  40. TQStringList KateSearch::s_replaceList = TQStringList();
  41. TQString KateSearch::s_pattern = TQString();
  42. static const bool arbitraryHLExample = false;
  43. KateSearch::KateSearch( KateView* view )
  44. : TQObject( view, "kate search" )
  45. , m_view( view )
  46. , m_doc( view->doc() )
  47. , replacePrompt( new KateReplacePrompt( view ) )
  48. {
  49. m_arbitraryHLList = new KateSuperRangeList();
  50. if (arbitraryHLExample) m_doc->arbitraryHL()->addHighlightToView(m_arbitraryHLList, m_view);
  51. connect(replacePrompt,TQT_SIGNAL(clicked()),this,TQT_SLOT(replaceSlot()));
  52. }
  53. KateSearch::~KateSearch()
  54. {
  55. delete m_arbitraryHLList;
  56. }
  57. void KateSearch::createActions( TDEActionCollection* ac )
  58. {
  59. KStdAction::find( this, TQT_SLOT(find()), ac )->setWhatsThis(
  60. i18n("Look up the first occurrence of a piece of text or regular expression."));
  61. KStdAction::findNext( this, TQT_SLOT(slotFindNext()), ac )->setWhatsThis(
  62. i18n("Look up the next occurrence of the search phrase."));
  63. KStdAction::findPrev( this, TQT_SLOT(slotFindPrev()), ac, "edit_find_prev" )->setWhatsThis(
  64. i18n("Look up the previous occurrence of the search phrase."));
  65. KStdAction::replace( this, TQT_SLOT(replace()), ac )->setWhatsThis(
  66. i18n("Look up a piece of text or regular expression and replace the result with some given text."));
  67. }
  68. void KateSearch::addToList( TQStringList& list, const TQString& s )
  69. {
  70. if( list.count() > 0 ) {
  71. TQStringList::Iterator it = list.find( s );
  72. if( *it != 0L )
  73. list.remove( it );
  74. if( list.count() >= 16 )
  75. list.remove( list.fromLast() );
  76. }
  77. list.prepend( s );
  78. }
  79. void KateSearch::find()
  80. {
  81. // if multiline selection around, search in it
  82. long searchf = KateViewConfig::global()->searchFlags();
  83. if (m_view->hasSelection() && m_view->selStartLine() != m_view->selEndLine())
  84. searchf |= KFindDialog::SelectedText;
  85. KFindDialog *findDialog = new KFindDialog ( m_view, "", searchf,
  86. s_searchList, m_view->hasSelection() );
  87. findDialog->setPattern (getSearchText());
  88. if( findDialog->exec() == TQDialog::Accepted ) {
  89. s_searchList = findDialog->findHistory () ;
  90. // Do *not* remove the TQString() wrapping, it fixes a nasty crash
  91. find( TQString(s_searchList.first()), findDialog->options(), true, true );
  92. }
  93. delete findDialog;
  94. m_view->repaintText ();
  95. }
  96. void KateSearch::find( const TQString &pattern, long flags, bool add, bool shownotfound )
  97. {
  98. KateViewConfig::global()->setSearchFlags( flags );
  99. if( add )
  100. addToList( s_searchList, pattern );
  101. s_pattern = pattern;
  102. SearchFlags searchFlags;
  103. searchFlags.caseSensitive = KateViewConfig::global()->searchFlags() & KFindDialog::CaseSensitive;
  104. searchFlags.wholeWords = KateViewConfig::global()->searchFlags() & KFindDialog::WholeWordsOnly;
  105. searchFlags.fromBeginning = !(KateViewConfig::global()->searchFlags() & KFindDialog::FromCursor)
  106. && !(KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText);
  107. searchFlags.backward = KateViewConfig::global()->searchFlags() & KFindDialog::FindBackwards;
  108. searchFlags.selected = KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText;
  109. searchFlags.prompt = false;
  110. searchFlags.replace = false;
  111. searchFlags.finished = false;
  112. searchFlags.regExp = KateViewConfig::global()->searchFlags() & KFindDialog::RegularExpression;
  113. searchFlags.useBackRefs = KateViewConfig::global()->searchFlags() & KReplaceDialog::BackReference;
  114. if ( searchFlags.selected )
  115. {
  116. s.selBegin = KateTextCursor( m_view->selStartLine(), m_view->selStartCol() );
  117. s.selEnd = KateTextCursor( m_view->selEndLine(), m_view->selEndCol() );
  118. s.cursor = s.flags.backward ? s.selEnd : s.selBegin;
  119. } else {
  120. s.cursor = getCursor( searchFlags );
  121. }
  122. s.wrappedEnd = s.cursor;
  123. s.wrapped = false;
  124. s.showNotFound = shownotfound;
  125. search( searchFlags );
  126. }
  127. void KateSearch::replace()
  128. {
  129. if (!doc()->isReadWrite()) return;
  130. // if multiline selection around, search in it
  131. long searchf = KateViewConfig::global()->searchFlags();
  132. if (m_view->hasSelection() && m_view->selStartLine() != m_view->selEndLine())
  133. searchf |= KFindDialog::SelectedText;
  134. KReplaceDialog *replaceDialog = new KReplaceDialog ( m_view, "", searchf,
  135. s_searchList, s_replaceList, m_view->hasSelection() );
  136. replaceDialog->setPattern (getSearchText());
  137. if( replaceDialog->exec() == TQDialog::Accepted ) {
  138. long opts = replaceDialog->options();
  139. m_replacement = replaceDialog->replacement();
  140. s_searchList = replaceDialog->findHistory () ;
  141. s_replaceList = replaceDialog->replacementHistory () ;
  142. // Do *not* remove the TQString() wrapping, it fixes a nasty crash
  143. replace( TQString(s_searchList.first()), m_replacement, opts );
  144. }
  145. delete replaceDialog;
  146. m_view->update ();
  147. }
  148. void KateSearch::replace( const TQString& pattern, const TQString &replacement, long flags )
  149. {
  150. if (!doc()->isReadWrite()) return;
  151. addToList( s_searchList, pattern );
  152. s_pattern = pattern;
  153. addToList( s_replaceList, replacement );
  154. m_replacement = replacement;
  155. KateViewConfig::global()->setSearchFlags( flags );
  156. SearchFlags searchFlags;
  157. searchFlags.caseSensitive = KateViewConfig::global()->searchFlags() & KFindDialog::CaseSensitive;
  158. searchFlags.wholeWords = KateViewConfig::global()->searchFlags() & KFindDialog::WholeWordsOnly;
  159. searchFlags.fromBeginning = !(KateViewConfig::global()->searchFlags() & KFindDialog::FromCursor)
  160. && !(KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText);
  161. searchFlags.backward = KateViewConfig::global()->searchFlags() & KFindDialog::FindBackwards;
  162. searchFlags.selected = KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText;
  163. searchFlags.prompt = KateViewConfig::global()->searchFlags() & KReplaceDialog::PromptOnReplace;
  164. searchFlags.replace = true;
  165. searchFlags.finished = false;
  166. searchFlags.regExp = KateViewConfig::global()->searchFlags() & KFindDialog::RegularExpression;
  167. searchFlags.useBackRefs = KateViewConfig::global()->searchFlags() & KReplaceDialog::BackReference;
  168. if ( searchFlags.selected )
  169. {
  170. s.selBegin = KateTextCursor( m_view->selStartLine(), m_view->selStartCol() );
  171. s.selEnd = KateTextCursor( m_view->selEndLine(), m_view->selEndCol() );
  172. s.cursor = s.flags.backward ? s.selEnd : s.selBegin;
  173. } else {
  174. s.cursor = getCursor( searchFlags );
  175. }
  176. s.wrappedEnd = s.cursor;
  177. s.wrapped = false;
  178. search( searchFlags );
  179. }
  180. void KateSearch::findAgain( bool reverseDirection )
  181. {
  182. SearchFlags searchFlags;
  183. searchFlags.caseSensitive = KateViewConfig::global()->searchFlags() & KFindDialog::CaseSensitive;
  184. searchFlags.wholeWords = KateViewConfig::global()->searchFlags() & KFindDialog::WholeWordsOnly;
  185. searchFlags.fromBeginning = !(KateViewConfig::global()->searchFlags() & KFindDialog::FromCursor)
  186. && !(KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText);
  187. searchFlags.backward = KateViewConfig::global()->searchFlags() & KFindDialog::FindBackwards;
  188. searchFlags.selected = KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText;
  189. searchFlags.prompt = KateViewConfig::global()->searchFlags() & KReplaceDialog::PromptOnReplace;
  190. searchFlags.replace = false;
  191. searchFlags.finished = false;
  192. searchFlags.regExp = KateViewConfig::global()->searchFlags() & KFindDialog::RegularExpression;
  193. searchFlags.useBackRefs = KateViewConfig::global()->searchFlags() & KReplaceDialog::BackReference;
  194. if (reverseDirection)
  195. searchFlags.backward = !searchFlags.backward;
  196. searchFlags.fromBeginning = false;
  197. searchFlags.prompt = true; // ### why is the above assignment there?
  198. s.cursor = getCursor( searchFlags );
  199. search( searchFlags );
  200. }
  201. void KateSearch::search( SearchFlags flags )
  202. {
  203. s.flags = flags;
  204. if( s.flags.fromBeginning ) {
  205. if( !s.flags.backward ) {
  206. s.cursor.setPos(0, 0);
  207. } else {
  208. s.cursor.setLine(doc()->numLines() - 1);
  209. s.cursor.setCol(doc()->lineLength( s.cursor.line() ));
  210. }
  211. }
  212. if((!s.flags.backward &&
  213. s.cursor.col() == 0 &&
  214. s.cursor.line() == 0 ) ||
  215. ( s.flags.backward &&
  216. s.cursor.col() == doc()->lineLength( s.cursor.line() ) &&
  217. s.cursor.line() == (((int)doc()->numLines()) - 1) ) ) {
  218. s.flags.finished = true;
  219. }
  220. if( s.flags.replace ) {
  221. replaces = 0;
  222. if( s.flags.prompt )
  223. promptReplace();
  224. else
  225. replaceAll();
  226. } else {
  227. findAgain();
  228. }
  229. }
  230. void KateSearch::wrapSearch()
  231. {
  232. if( s.flags.selected )
  233. {
  234. KateTextCursor start (s.selBegin);
  235. KateTextCursor end (s.selEnd);
  236. // recalc for block sel, to have start with lowest col, end with highest
  237. if (m_view->blockSelectionMode())
  238. {
  239. start.setCol (kMin(s.selBegin.col(), s.selEnd.col()));
  240. end.setCol (kMax(s.selBegin.col(), s.selEnd.col()));
  241. }
  242. s.cursor = s.flags.backward ? end : start;
  243. }
  244. else
  245. {
  246. if( !s.flags.backward ) {
  247. s.cursor.setPos(0, 0);
  248. } else {
  249. s.cursor.setLine(doc()->numLines() - 1);
  250. s.cursor.setCol(doc()->lineLength( s.cursor.line() ) );
  251. }
  252. }
  253. // oh, we wrapped around one time allready now !
  254. // only check that on replace
  255. s.wrapped = s.flags.replace;
  256. replaces = 0;
  257. s.flags.finished = true;
  258. }
  259. void KateSearch::findAgain()
  260. {
  261. if( s_pattern.isEmpty() ) {
  262. find();
  263. return;
  264. }
  265. if ( doSearch( s_pattern ) ) {
  266. exposeFound( s.cursor, s.matchedLength );
  267. } else if( !s.flags.finished ) {
  268. if( askContinue() ) {
  269. wrapSearch();
  270. findAgain();
  271. } else {
  272. if (arbitraryHLExample) m_arbitraryHLList->clear();
  273. }
  274. } else {
  275. if (arbitraryHLExample) m_arbitraryHLList->clear();
  276. if ( s.showNotFound )
  277. KMessageBox::sorry( view(),
  278. i18n("Search string '%1' not found!")
  279. .arg( KStringHandler::csqueeze( s_pattern ) ),
  280. i18n("Find"));
  281. }
  282. }
  283. void KateSearch::replaceAll()
  284. {
  285. doc()->editStart ();
  286. while( doSearch( s_pattern ) )
  287. replaceOne();
  288. doc()->editEnd ();
  289. if( !s.flags.finished ) {
  290. if( askContinue() ) {
  291. wrapSearch();
  292. replaceAll();
  293. }
  294. } else {
  295. KMessageBox::information( view(),
  296. i18n("%n replacement made.","%n replacements made.",replaces),
  297. i18n("Replace") );
  298. }
  299. }
  300. void KateSearch::promptReplace()
  301. {
  302. if ( doSearch( s_pattern ) ) {
  303. exposeFound( s.cursor, s.matchedLength );
  304. replacePrompt->show();
  305. replacePrompt->setFocus ();
  306. } else if( !s.flags.finished && askContinue() ) {
  307. wrapSearch();
  308. promptReplace();
  309. } else {
  310. if (arbitraryHLExample) m_arbitraryHLList->clear();
  311. replacePrompt->hide();
  312. KMessageBox::information( view(),
  313. i18n("%n replacement made.","%n replacements made.",replaces),
  314. i18n("Replace") );
  315. }
  316. }
  317. void KateSearch::replaceOne()
  318. {
  319. TQString replaceWith = m_replacement;
  320. if ( s.flags.regExp && s.flags.useBackRefs ) {
  321. // Replace each "\0"..."\9" with the corresponding capture,
  322. // "\n" and "\t" with newline and tab,
  323. // "\\" with "\",
  324. // and remove the "\" for any other sequence.
  325. TQRegExp br("\\\\(.)");
  326. int pos = br.search( replaceWith );
  327. int ncaps = m_re.numCaptures();
  328. while ( pos >= 0 ) {
  329. TQString substitute;
  330. TQChar argument = TQString(br.cap(1)).at(0);
  331. if ( argument.isDigit() ) {
  332. // the second character is a digit, this is a backreference
  333. int ccap = argument.digitValue();
  334. if (ccap <= ncaps ) {
  335. substitute = m_re.cap( ccap );
  336. } else {
  337. kdDebug()<<"KateSearch::replaceOne(): you don't have "<<ccap<<" backreferences in regexp '"<<TQString(m_re.pattern())<<"'"<<endl;
  338. break;
  339. }
  340. } else if ( argument == 'n' ) {
  341. substitute = '\n';
  342. } else if ( argument == 't' ) {
  343. substitute = '\t';
  344. } else {
  345. // handle a validly escaped backslash, or an invalid escape.
  346. substitute = argument;
  347. }
  348. replaceWith.replace( pos, br.matchedLength(), substitute );
  349. pos = br.search( replaceWith, pos + substitute.length() );
  350. }
  351. }
  352. doc()->editStart();
  353. doc()->removeText( s.cursor.line(), s.cursor.col(),
  354. s.cursor.line(), s.cursor.col() + s.matchedLength );
  355. doc()->insertText( s.cursor.line(), s.cursor.col(), replaceWith );
  356. doc()->editEnd(),
  357. replaces++;
  358. // if we inserted newlines, we better adjust.
  359. uint newlines = replaceWith.contains('\n');
  360. if ( newlines )
  361. {
  362. if ( ! s.flags.backward )
  363. {
  364. s.cursor.setLine( s.cursor.line() + newlines );
  365. s.cursor.setCol( replaceWith.length() - replaceWith.findRev('\n') );
  366. }
  367. // selection?
  368. if ( s.flags.selected )
  369. s.selEnd.setLine( s.selEnd.line() + newlines );
  370. }
  371. // adjust selection endcursor if needed
  372. if( s.flags.selected && s.cursor.line() == s.selEnd.line() )
  373. {
  374. s.selEnd.setCol(s.selEnd.col() + replaceWith.length() - s.matchedLength );
  375. }
  376. // adjust wrap cursor if needed
  377. if( s.cursor.line() == s.wrappedEnd.line() && s.cursor.col() <= s.wrappedEnd.col())
  378. {
  379. s.wrappedEnd.setCol(s.wrappedEnd.col() + replaceWith.length() - s.matchedLength );
  380. }
  381. if( !s.flags.backward ) {
  382. s.cursor.setCol(s.cursor.col() + replaceWith.length());
  383. } else if( s.cursor.col() > 0 ) {
  384. s.cursor.setCol(s.cursor.col() - 1);
  385. } else {
  386. s.cursor.setLine(s.cursor.line() - 1);
  387. if( s.cursor.line() >= 0 ) {
  388. s.cursor.setCol(doc()->lineLength( s.cursor.line() ));
  389. }
  390. }
  391. }
  392. void KateSearch::skipOne()
  393. {
  394. if( !s.flags.backward ) {
  395. s.cursor.setCol(s.cursor.col() + s.matchedLength);
  396. } else if( s.cursor.col() > 0 ) {
  397. s.cursor.setCol(s.cursor.col() - 1);
  398. } else {
  399. s.cursor.setLine(s.cursor.line() - 1);
  400. if( s.cursor.line() >= 0 ) {
  401. s.cursor.setCol(doc()->lineLength(s.cursor.line()));
  402. }
  403. }
  404. }
  405. void KateSearch::replaceSlot() {
  406. switch( (Dialog_results)replacePrompt->result() ) {
  407. case srCancel: replacePrompt->hide(); break;
  408. case srAll: replacePrompt->hide(); replaceAll(); break;
  409. case srYes: replaceOne(); promptReplace(); break;
  410. case srLast: replacePrompt->hide(), replaceOne(); break;
  411. case srNo: skipOne(); promptReplace(); break;
  412. }
  413. }
  414. bool KateSearch::askContinue()
  415. {
  416. TQString made =
  417. i18n( "%n replacement made.",
  418. "%n replacements made.",
  419. replaces );
  420. TQString reached = !s.flags.backward ?
  421. i18n( "End of document reached." ) :
  422. i18n( "Beginning of document reached." );
  423. if (KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText)
  424. {
  425. reached = !s.flags.backward ?
  426. i18n( "End of selection reached." ) :
  427. i18n( "Beginning of selection reached." );
  428. }
  429. TQString question = !s.flags.backward ?
  430. i18n( "Continue from the beginning?" ) :
  431. i18n( "Continue from the end?" );
  432. TQString text = s.flags.replace ?
  433. made + "\n" + reached + "\n" + question :
  434. reached + "\n" + question;
  435. return KMessageBox::Yes == KMessageBox::questionYesNo(
  436. view(), text, s.flags.replace ? i18n("Replace") : i18n("Find"),
  437. KStdGuiItem::cont(), i18n("&Stop") );
  438. }
  439. TQString KateSearch::getSearchText()
  440. {
  441. // SelectionOnly: use selection
  442. // WordOnly: use word under cursor
  443. // SelectionWord: use selection if available, else use word under cursor
  444. // WordSelection: use word if available, else use selection
  445. TQString str;
  446. int getFrom = view()->config()->textToSearchMode();
  447. switch (getFrom)
  448. {
  449. case KateViewConfig::SelectionOnly: // (Windows)
  450. //kdDebug() << "getSearchText(): SelectionOnly" << endl;
  451. if( m_view->hasSelection() )
  452. str = m_view->selection();
  453. break;
  454. case KateViewConfig::SelectionWord: // (classic Kate behavior)
  455. //kdDebug() << "getSearchText(): SelectionWord" << endl;
  456. if( m_view->hasSelection() )
  457. str = m_view->selection();
  458. else
  459. str = view()->currentWord();
  460. break;
  461. case KateViewConfig::WordOnly: // (weird?)
  462. //kdDebug() << "getSearchText(): WordOnly" << endl;
  463. str = view()->currentWord();
  464. break;
  465. case KateViewConfig::WordSelection: // (persistent selection lover)
  466. //kdDebug() << "getSearchText(): WordSelection" << endl;
  467. str = view()->currentWord();
  468. if (str.isEmpty() && m_view->hasSelection() )
  469. str = m_view->selection();
  470. break;
  471. default: // (nowhere)
  472. //kdDebug() << "getSearchText(): Nowhere" << endl;
  473. break;
  474. }
  475. str.replace( TQRegExp("^\\n"), "" );
  476. str.replace( TQRegExp("\\n.*"), "" );
  477. return str;
  478. }
  479. KateTextCursor KateSearch::getCursor( SearchFlags flags )
  480. {
  481. if (flags.backward && !flags.selected && view()->hasSelection())
  482. {
  483. // We're heading backwards (and not within a selection),
  484. // the selection might start before the cursor.
  485. return kMin( KateTextCursor(view()->selStartLine(), view()->selStartCol()),
  486. KateTextCursor(view()->cursorLine(), view()->cursorColumnReal()));
  487. }
  488. return KateTextCursor(view()->cursorLine(), view()->cursorColumnReal());
  489. }
  490. bool KateSearch::doSearch( const TQString& text )
  491. {
  492. /*
  493. rodda: Still Working on this... :)
  494. bool result = false;
  495. if (m_searchResults.count()) {
  496. m_resultIndex++;
  497. if (m_resultIndex < (int)m_searchResults.count()) {
  498. s = m_searchResults[m_resultIndex];
  499. result = true;
  500. }
  501. } else {
  502. int temp = 0;
  503. do {*/
  504. #if 0
  505. static int oldLine = -1;
  506. static int oldCol = -1;
  507. #endif
  508. uint line = s.cursor.line();
  509. uint col = s.cursor.col();// + (result ? s.matchedLength : 0);
  510. bool backward = s.flags.backward;
  511. bool caseSensitive = s.flags.caseSensitive;
  512. bool regExp = s.flags.regExp;
  513. bool wholeWords = s.flags.wholeWords;
  514. uint foundLine, foundCol, matchLen;
  515. bool found = false;
  516. //kdDebug() << "Searching at " << line << ", " << col << endl;
  517. // kdDebug()<<"KateSearch::doSearch: "<<line<<", "<<col<<", "<<backward<<endl;
  518. if (backward)
  519. {
  520. KateDocCursor docCursor(line, col, doc());
  521. // If we're at the top of the document, we're not gonna find anything, so bail.
  522. if (docCursor.line() == 0 && docCursor.col() == 0)
  523. return false;
  524. // Move one step backward before searching, if this is a "find again", we don't
  525. // want to find the same match.
  526. docCursor.moveBackward(1);
  527. line = docCursor.line();
  528. col = docCursor.col();
  529. }
  530. do {
  531. if( regExp ) {
  532. m_re = TQRegExp( text, caseSensitive );
  533. found = doc()->searchText( line, col, m_re,
  534. &foundLine, &foundCol,
  535. &matchLen, backward );
  536. }
  537. else if ( wholeWords )
  538. {
  539. bool maybefound = false;
  540. do
  541. {
  542. maybefound = doc()->searchText( line, col, text,
  543. &foundLine, &foundCol,
  544. &matchLen, caseSensitive, backward );
  545. if ( maybefound )
  546. {
  547. found = (
  548. ( foundCol == 0 ||
  549. ! doc()->highlight()->isInWord( doc()->textLine( foundLine ).at( foundCol - 1 ) ) ) &&
  550. ( foundCol + matchLen == doc()->lineLength( foundLine ) ||
  551. ! doc()->highlight()->isInWord( doc()->textLine( foundLine ).at( foundCol + matchLen ) ) )
  552. );
  553. if ( found )
  554. {
  555. break;
  556. }
  557. else if ( backward && foundCol == 0 ) // we are done on this line and want to avoid endless loops like in #137312
  558. {
  559. if ( line == 0 ) // we are completely done...
  560. break;
  561. else
  562. line--;
  563. }
  564. else
  565. {
  566. line = foundLine;
  567. col = foundCol + 1;
  568. }
  569. }
  570. } while ( maybefound );
  571. }
  572. else {
  573. found = doc()->searchText( line, col, text,
  574. &foundLine, &foundCol,
  575. &matchLen, caseSensitive, backward );
  576. }
  577. if ( found && s.flags.selected )
  578. {
  579. KateTextCursor start (s.selBegin);
  580. KateTextCursor end (s.selEnd);
  581. // recalc for block sel, to have start with lowest col, end with highest
  582. if (m_view->blockSelectionMode())
  583. {
  584. start.setCol (kMin(s.selBegin.col(), s.selEnd.col()));
  585. end.setCol (kMax(s.selBegin.col(), s.selEnd.col()));
  586. }
  587. if ( !s.flags.backward && KateTextCursor( foundLine, foundCol ) >= end
  588. || s.flags.backward && KateTextCursor( foundLine, foundCol ) < start )
  589. {
  590. found = false;
  591. }
  592. else if (m_view->blockSelectionMode())
  593. {
  594. if ((int)foundCol >= start.col() && (int)foundCol < end.col())
  595. break;
  596. }
  597. }
  598. line = foundLine;
  599. col = foundCol+1;
  600. }
  601. while (s.flags.selected && m_view->blockSelectionMode() && found);
  602. // in the case we want to search in selection + blockselection we need to loop
  603. if( !found ) return false;
  604. // save the search result
  605. s.cursor.setPos(foundLine, foundCol);
  606. s.matchedLength = matchLen;
  607. // we allready wrapped around one time
  608. if (s.wrapped)
  609. {
  610. if (s.flags.backward)
  611. {
  612. if ( (s.cursor.line() < s.wrappedEnd.line())
  613. || ( (s.cursor.line() == s.wrappedEnd.line()) && ((s.cursor.col()+matchLen) <= uint(s.wrappedEnd.col())) ) )
  614. return false;
  615. }
  616. else
  617. {
  618. if ( (s.cursor.line() > s.wrappedEnd.line())
  619. || ( (s.cursor.line() == s.wrappedEnd.line()) && (s.cursor.col() > s.wrappedEnd.col()) ) )
  620. return false;
  621. }
  622. }
  623. // kdDebug() << "Found at " << s.cursor.line() << ", " << s.cursor.col() << endl;
  624. //m_searchResults.append(s);
  625. if (arbitraryHLExample) {
  626. KateArbitraryHighlightRange* hl = new KateArbitraryHighlightRange(new KateSuperCursor(m_doc, true, s.cursor), new KateSuperCursor(m_doc, true, s.cursor.line(), s.cursor.col() + s.matchedLength), this);
  627. hl->setBold();
  628. hl->setTextColor(Qt::white);
  629. hl->setBGColor(Qt::black);
  630. // destroy the highlight upon change
  631. connect(hl, TQT_SIGNAL(contentsChanged()), hl, TQT_SIGNAL(eliminated()));
  632. m_arbitraryHLList->append(hl);
  633. }
  634. return true;
  635. /* rodda: more of my search highlighting work
  636. } while (++temp < 100);
  637. if (result) {
  638. s = m_searchResults.first();
  639. m_resultIndex = 0;
  640. }
  641. }
  642. return result;*/
  643. }
  644. void KateSearch::exposeFound( KateTextCursor &cursor, int slen )
  645. {
  646. view()->setCursorPositionInternal ( cursor.line(), cursor.col() + slen, 1 );
  647. view()->setSelection( cursor.line(), cursor.col(), cursor.line(), cursor.col() + slen );
  648. view()->syncSelectionCache();
  649. }
  650. //END KateSearch
  651. //BEGIN KateReplacePrompt
  652. // this dialog is not modal
  653. KateReplacePrompt::KateReplacePrompt ( TQWidget *parent )
  654. : KDialogBase ( parent, 0L, false, i18n( "Replace Confirmation" ),
  655. User3 | User2 | User1 | Close | Ok , Ok, true,
  656. i18n("Replace &All"), i18n("Re&place && Close"), i18n("&Replace") )
  657. {
  658. setButtonOK( i18n("&Find Next") );
  659. TQWidget *page = new TQWidget(this);
  660. setMainWidget(page);
  661. TQBoxLayout *topLayout = new TQVBoxLayout( page, 0, spacingHint() );
  662. TQLabel *label = new TQLabel(i18n("Found an occurrence of your search term. What do you want to do?"),page);
  663. topLayout->addWidget(label );
  664. }
  665. void KateReplacePrompt::slotOk ()
  666. { // Search Next
  667. done(KateSearch::srNo);
  668. actionButton(Ok)->setFocus();
  669. }
  670. void KateReplacePrompt::slotClose ()
  671. { // Close
  672. done(KateSearch::srCancel);
  673. actionButton(Close)->setFocus();
  674. }
  675. void KateReplacePrompt::slotUser1 ()
  676. { // Replace All
  677. done(KateSearch::srAll);
  678. actionButton(User1)->setFocus();
  679. }
  680. void KateReplacePrompt::slotUser2 ()
  681. { // Replace & Close
  682. done(KateSearch::srLast);
  683. actionButton(User2)->setFocus();
  684. }
  685. void KateReplacePrompt::slotUser3 ()
  686. { // Replace
  687. done(KateSearch::srYes);
  688. actionButton(User3)->setFocus();
  689. }
  690. void KateReplacePrompt::done (int result)
  691. {
  692. setResult(result);
  693. emit clicked();
  694. }
  695. //END KateReplacePrompt
  696. //BEGIN SearchCommand
  697. bool SearchCommand::exec(class Kate::View *view, const TQString &cmd, TQString &msg)
  698. {
  699. TQString flags, pattern, replacement;
  700. if ( cmd.startsWith( "find" ) )
  701. {
  702. static TQRegExp re_find("find(?::([bcersw]*))?\\s+(.+)");
  703. if ( re_find.search( cmd ) < 0 )
  704. {
  705. msg = i18n("Usage: find[:[bcersw]] PATTERN");
  706. return false;
  707. }
  708. flags = re_find.cap( 1 );
  709. pattern = re_find.cap( 2 );
  710. }
  711. else if ( cmd.startsWith( "ifind" ) )
  712. {
  713. static TQRegExp re_ifind("ifind(?::([bcrs]*))?\\s+(.*)");
  714. if ( re_ifind.search( cmd ) < 0 )
  715. {
  716. msg = i18n("Usage: ifind[:[bcrs]] PATTERN");
  717. return false;
  718. }
  719. ifindClear();
  720. return true;
  721. }
  722. else if ( cmd.startsWith( "replace" ) )
  723. {
  724. // Try if the pattern and replacement is quoted, using a quote character ["']
  725. static TQRegExp re_rep("replace(?::([bceprsw]*))?\\s+([\"'])((?:[^\\\\\\\\2]|\\\\.)*)\\2\\s+\\2((?:[^\\\\\\\\2]|\\\\.)*)\\2\\s*$");
  726. // Or one quoted argument
  727. TQRegExp re_rep1("replace(?::([bceprsw]*))?\\s+([\"'])((?:[^\\\\\\\\2]|\\\\.)*)\\2\\s*$");
  728. // Else, it's just one or two (space separated) words
  729. TQRegExp re_rep2("replace(?::([bceprsw]*))?\\s+(\\S+)(.*)");
  730. #define unbackslash(s) p=0;\
  731. while ( (p = pattern.find( '\\' + delim, p )) > -1 )\
  732. {\
  733. if ( !p || pattern[p-1] != '\\' )\
  734. pattern.remove( p, 1 );\
  735. p++;\
  736. }
  737. if ( re_rep.search( cmd ) >= 0 )
  738. {
  739. flags = re_rep.cap(1);
  740. pattern = re_rep.cap( 3 );
  741. replacement = re_rep.cap( 4 );
  742. int p(0);
  743. // unbackslash backslashed delimiter strings
  744. // in pattern ..
  745. TQString delim = re_rep.cap( 2 );
  746. unbackslash(pattern);
  747. // .. and in replacement
  748. unbackslash(replacement);
  749. }
  750. else if ( re_rep1.search( cmd ) >= 0 )
  751. {
  752. flags = re_rep1.cap(1);
  753. pattern = re_rep1.cap( 3 );
  754. int p(0);
  755. TQString delim = re_rep1.cap( 2 );
  756. unbackslash(pattern);
  757. }
  758. else if ( re_rep2.search( cmd ) >= 0 )
  759. {
  760. flags = re_rep2.cap( 1 );
  761. pattern = re_rep2.cap( 2 );
  762. replacement = TQString(re_rep2.cap( 3 )).stripWhiteSpace();
  763. }
  764. else
  765. {
  766. msg = i18n("Usage: replace[:[bceprsw]] PATTERN [REPLACEMENT]");
  767. return false;
  768. }
  769. kdDebug()<<"replace '"<<pattern<<"' with '"<<replacement<<"'"<<endl;
  770. #undef unbackslash
  771. }
  772. long f = 0;
  773. if ( flags.contains( 'b' ) ) f |= KFindDialog::FindBackwards;
  774. if ( flags.contains( 'c' ) ) f |= KFindDialog::FromCursor;
  775. if ( flags.contains( 'e' ) ) f |= KFindDialog::SelectedText;
  776. if ( flags.contains( 'r' ) ) f |= KFindDialog::RegularExpression;
  777. if ( flags.contains( 'p' ) ) f |= KReplaceDialog::PromptOnReplace;
  778. if ( flags.contains( 's' ) ) f |= KFindDialog::CaseSensitive;
  779. if ( flags.contains( 'w' ) ) f |= KFindDialog::WholeWordsOnly;
  780. if ( cmd.startsWith( "find" ) )
  781. {
  782. ((KateView*)view)->find( pattern, f );
  783. return true;
  784. }
  785. else if ( cmd.startsWith( "replace" ) )
  786. {
  787. f |= KReplaceDialog::BackReference; // mandatory here?
  788. ((KateView*)view)->replace( pattern, replacement, f );
  789. return true;
  790. }
  791. return false;
  792. }
  793. bool SearchCommand::help(class Kate::View *, const TQString &cmd, TQString &msg)
  794. {
  795. if ( cmd == "find" )
  796. msg = i18n("<p>Usage: <code>find[:bcersw] PATTERN</code></p>");
  797. else if ( cmd == "ifind" )
  798. msg = i18n("<p>Usage: <code>ifind:[:bcrs] PATTERN</code>"
  799. "<br>ifind does incremental or 'as-you-type' search</p>");
  800. else
  801. msg = i18n("<p>Usage: <code>replace[:bceprsw] PATTERN REPLACEMENT</code></p>");
  802. msg += i18n(
  803. "<h4><caption>Options</h4><p>"
  804. "<b>b</b> - Search backward"
  805. "<br><b>c</b> - Search from cursor"
  806. "<br><b>r</b> - Pattern is a regular expression"
  807. "<br><b>s</b> - Case sensitive search"
  808. );
  809. if ( cmd == "find" )
  810. msg += i18n(
  811. "<br><b>e</b> - Search in selected text only"
  812. "<br><b>w</b> - Search whole words only"
  813. );
  814. if ( cmd == "replace" )
  815. msg += i18n(
  816. "<br><b>p</b> - Prompt for replace</p>"
  817. "<p>If REPLACEMENT is not present, an empty string is used.</p>"
  818. "<p>If you want to have whitespace in your PATTERN, you need to "
  819. "quote both PATTERN and REPLACEMENT with either single or double "
  820. "quotes. To have the quote characters in the strings, prepend them "
  821. "with a backslash.");
  822. msg += "</p>";
  823. return true;
  824. }
  825. TQStringList SearchCommand::cmds()
  826. {
  827. TQStringList l;
  828. l << "find" << "replace" << "ifind";
  829. return l;
  830. }
  831. bool SearchCommand::wantsToProcessText( const TQString &cmdname )
  832. {
  833. return cmdname == "ifind";
  834. }
  835. void SearchCommand::processText( Kate::View *view, const TQString &cmd )
  836. {
  837. static TQRegExp re_ifind("ifind(?::([bcrs]*))?\\s(.*)");
  838. if ( re_ifind.search( cmd ) > -1 )
  839. {
  840. TQString flags = re_ifind.cap( 1 );
  841. TQString pattern = re_ifind.cap( 2 );
  842. // if there is no setup, or the text length is 0, set up the properties
  843. if ( ! m_ifindFlags || pattern.isEmpty() )
  844. ifindInit( flags );
  845. // if there is no fromCursor, add it if this is not the first character
  846. else if ( ! ( m_ifindFlags & KFindDialog::FromCursor ) && ! pattern.isEmpty() )
  847. m_ifindFlags |= KFindDialog::FromCursor;
  848. // search..
  849. if ( ! pattern.isEmpty() )
  850. {
  851. KateView *v = (KateView*)view;
  852. // If it *looks like* we are continuing, place the cursor
  853. // at the beginning of the selection, so that the search continues.
  854. // ### check more carefully, like is the cursor currently at the end
  855. // of the selection.
  856. if ( pattern.startsWith( v->selection() ) &&
  857. v->selection().length() + 1 == pattern.length() )
  858. v->setCursorPositionInternal( v->selStartLine(), v->selStartCol() );
  859. v->find( pattern, m_ifindFlags, false );
  860. }
  861. }
  862. }
  863. void SearchCommand::ifindInit( const TQString &flags )
  864. {
  865. long f = 0;
  866. if ( flags.contains( 'b' ) ) f |= KFindDialog::FindBackwards;
  867. if ( flags.contains( 'c' ) ) f |= KFindDialog::FromCursor;
  868. if ( flags.contains( 'r' ) ) f |= KFindDialog::RegularExpression;
  869. if ( flags.contains( 's' ) ) f |= KFindDialog::CaseSensitive;
  870. m_ifindFlags = f;
  871. }
  872. void SearchCommand::ifindClear()
  873. {
  874. m_ifindFlags = 0;
  875. }
  876. //END SearchCommand
  877. // kate: space-indent on; indent-width 2; replace-tabs on;