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.

496 lines
14KB

  1. /* This file is part of the KDE libraries
  2. Copyright (C) 2002 by John Firebaugh <jfirebaugh@kde.org>
  3. This library is free software; you can redistribute it and/or
  4. modify it under the terms of the GNU Library General Public
  5. License version 2 as published by the Free Software Foundation.
  6. This library is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  9. Library General Public License for more details.
  10. You should have received a copy of the GNU Library General Public License
  11. along with this library; see the file COPYING.LIB. If not, write to
  12. the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  13. Boston, MA 02110-1301, USA.
  14. */
  15. #include <tqlabel.h>
  16. #include <tqregexp.h>
  17. #include <tqstyle.h>
  18. #include <tqpopupmenu.h>
  19. #include <kgenericfactory.h>
  20. #include <tdelocale.h>
  21. #include <tdeaction.h>
  22. #include <kcombobox.h>
  23. #include <tdeconfig.h>
  24. #include <kdebug.h>
  25. #include "ISearchPlugin.h"
  26. #include "ISearchPlugin.moc"
  27. K_EXPORT_COMPONENT_FACTORY( tdetexteditor_isearch, KGenericFactory<ISearchPlugin>( "tdetexteditor_isearch" ) )
  28. ISearchPluginView::ISearchPluginView( KTextEditor::View *view )
  29. : TQObject ( view ), KXMLGUIClient (view)
  30. , m_view( 0L )
  31. , m_doc( 0L )
  32. , m_searchIF( 0L )
  33. , m_cursorIF( 0L )
  34. , m_selectIF( 0L )
  35. // , m_toolBarAction( 0L )
  36. , m_searchForwardAction( 0L )
  37. , m_searchBackwardAction( 0L )
  38. , m_label( 0L )
  39. , m_combo( 0L )
  40. , m_lastString( "" )
  41. , m_searchBackward( false )
  42. , m_caseSensitive( false )
  43. , m_fromBeginning( false )
  44. , m_regExp( false )
  45. , m_autoWrap( false )
  46. , m_wrapped( false )
  47. , m_startLine( 0 )
  48. , m_startCol( 0 )
  49. , m_searchLine( 0 )
  50. , m_searchCol( 0 )
  51. , m_foundLine( 0 )
  52. , m_foundCol( 0 )
  53. , m_matchLen( 0 )
  54. , m_toolBarWasHidden( false )
  55. {
  56. view->insertChildClient (this);
  57. setInstance( KGenericFactory<ISearchPlugin>::instance() );
  58. m_searchForwardAction = new TDEAction(
  59. i18n("Search Incrementally"), CTRL+ALT+Key_F,
  60. this, TQT_SLOT(slotSearchForwardAction()),
  61. actionCollection(), "edit_isearch" );
  62. m_searchBackwardAction = new TDEAction(
  63. i18n("Search Incrementally Backwards"), CTRL+ALT+SHIFT+Key_F,
  64. this, TQT_SLOT(slotSearchBackwardAction()),
  65. actionCollection(), "edit_isearch_reverse" );
  66. m_label = new TQLabel( i18n("I-Search:"), 0L, "kde toolbar widget" );
  67. KWidgetAction* labelAction = new KWidgetAction(
  68. m_label,
  69. i18n("I-Search:"), 0, 0, 0,
  70. actionCollection(), "isearch_label" );
  71. labelAction->setShortcutConfigurable( false );
  72. m_combo = new KHistoryCombo();
  73. m_combo->setDuplicatesEnabled( false );
  74. m_combo->setMaximumWidth( 300 );
  75. m_combo->lineEdit()->installEventFilter( this );
  76. connect( m_combo, TQT_SIGNAL(textChanged(const TQString&)),
  77. this, TQT_SLOT(slotTextChanged(const TQString&)) );
  78. connect( m_combo, TQT_SIGNAL(returnPressed(const TQString&)),
  79. this, TQT_SLOT(slotReturnPressed(const TQString&)) );
  80. connect( m_combo, TQT_SIGNAL(aboutToShowContextMenu(TQPopupMenu*)),
  81. this, TQT_SLOT(slotAddContextMenuItems(TQPopupMenu*)) );
  82. m_comboAction = new KWidgetAction(
  83. m_combo,
  84. i18n("Search"), 0, 0, 0,
  85. actionCollection(), "isearch_combo" );
  86. m_comboAction->setAutoSized( true );
  87. m_comboAction->setShortcutConfigurable( false );
  88. TDEActionMenu* optionMenu = new TDEActionMenu(
  89. i18n("Search Options"), "configure",
  90. actionCollection(), "isearch_options" );
  91. optionMenu->setDelayed( false );
  92. TDEToggleAction* action = new TDEToggleAction(
  93. i18n("Case Sensitive"), TDEShortcut(),
  94. actionCollection(), "isearch_case_sensitive" );
  95. action->setShortcutConfigurable( false );
  96. connect( action, TQT_SIGNAL(toggled(bool)),
  97. this, TQT_SLOT(setCaseSensitive(bool)) );
  98. action->setChecked( m_caseSensitive );
  99. optionMenu->insert( action );
  100. action = new TDEToggleAction(
  101. i18n("From Beginning"), TDEShortcut(),
  102. actionCollection(), "isearch_from_beginning" );
  103. action->setShortcutConfigurable( false );
  104. connect( action, TQT_SIGNAL(toggled(bool)),
  105. this, TQT_SLOT(setFromBeginning(bool)) );
  106. action->setChecked( m_fromBeginning );
  107. optionMenu->insert( action );
  108. action = new TDEToggleAction(
  109. i18n("Regular Expression"), TDEShortcut(),
  110. actionCollection(), "isearch_reg_exp" );
  111. action->setShortcutConfigurable( false );
  112. connect( action, TQT_SIGNAL(toggled(bool)),
  113. this, TQT_SLOT(setRegExp(bool)) );
  114. action->setChecked( m_regExp );
  115. optionMenu->insert( action );
  116. // optionMenu->insert( new TDEActionSeparator() );
  117. //
  118. // action = new TDEToggleAction(
  119. // i18n("Auto-Wrap Search"), TDEShortcut(),
  120. // actionCollection(), "isearch_auto_wrap" );
  121. // connect( action, TQT_SIGNAL(toggled(bool)),
  122. // this, TQT_SLOT(setAutoWrap(bool)) );
  123. // action->setChecked( m_autoWrap );
  124. // optionMenu->insert( action );
  125. setXMLFile( "tdetexteditor_isearchui.rc" );
  126. }
  127. ISearchPluginView::~ISearchPluginView()
  128. {
  129. writeConfig();
  130. m_combo->lineEdit()->removeEventFilter( this );
  131. delete m_combo;
  132. delete m_label;
  133. }
  134. void ISearchPluginView::setView( KTextEditor::View* view )
  135. {
  136. m_view = view;
  137. m_doc = m_view->document();
  138. m_searchIF = KTextEditor::searchInterface ( m_doc );
  139. m_cursorIF = KTextEditor::viewCursorInterface ( m_view );
  140. m_selectIF = KTextEditor::selectionInterface ( m_doc );
  141. if( !m_doc || !m_cursorIF || !m_selectIF ) {
  142. m_view = 0L;
  143. m_doc = 0L;
  144. m_searchIF = 0L;
  145. m_cursorIF = 0L;
  146. m_selectIF = 0L;
  147. }
  148. readConfig();
  149. }
  150. void ISearchPluginView::readConfig()
  151. {
  152. // TDEConfig* config = instance()->config();
  153. }
  154. void ISearchPluginView::writeConfig()
  155. {
  156. // TDEConfig* config = instance()->config();
  157. }
  158. void ISearchPluginView::setCaseSensitive( bool caseSensitive )
  159. {
  160. m_caseSensitive = caseSensitive;
  161. }
  162. void ISearchPluginView::setFromBeginning( bool fromBeginning )
  163. {
  164. m_fromBeginning = fromBeginning;
  165. if( m_fromBeginning ) {
  166. m_searchLine = m_searchCol = 0;
  167. }
  168. }
  169. void ISearchPluginView::setRegExp( bool regExp )
  170. {
  171. m_regExp = regExp;
  172. }
  173. void ISearchPluginView::setAutoWrap( bool autoWrap )
  174. {
  175. m_autoWrap = autoWrap;
  176. }
  177. bool ISearchPluginView::eventFilter( TQObject* o, TQEvent* e )
  178. {
  179. if( TQT_BASE_OBJECT(o) != TQT_BASE_OBJECT(m_combo->lineEdit()) )
  180. return false;
  181. if( e->type() == TQEvent::FocusIn ) {
  182. TQFocusEvent* focusEvent = (TQFocusEvent*)e;
  183. if( focusEvent->reason() == TQFocusEvent::ActiveWindow ||
  184. focusEvent->reason() == TQFocusEvent::Popup )
  185. return false;
  186. startSearch();
  187. }
  188. if( e->type() == TQEvent::FocusOut ) {
  189. TQFocusEvent* focusEvent = (TQFocusEvent*)e;
  190. if( focusEvent->reason() == TQFocusEvent::ActiveWindow ||
  191. focusEvent->reason() == TQFocusEvent::Popup )
  192. return false;
  193. endSearch();
  194. }
  195. if( e->type() == TQEvent::KeyPress ) {
  196. TQKeyEvent *keyEvent = (TQKeyEvent*)e;
  197. if( keyEvent->key() == Qt::Key_Escape )
  198. quitToView( TQString::null );
  199. }
  200. return false;
  201. }
  202. // Sigh... i18n hell.
  203. void ISearchPluginView::updateLabelText(
  204. bool failing /* = false */, bool reverse /* = false */,
  205. bool wrapped /* = false */, bool overwrapped /* = false */ )
  206. {
  207. TQString text;
  208. // Reverse binary:
  209. // 0000
  210. if( !failing && !reverse && !wrapped && !overwrapped ) {
  211. text = i18n("Incremental Search", "I-Search:");
  212. // 1000
  213. } else if ( failing && !reverse && !wrapped && !overwrapped ) {
  214. text = i18n("Incremental Search found no match", "Failing I-Search:");
  215. // 0100
  216. } else if ( !failing && reverse && !wrapped && !overwrapped ) {
  217. text = i18n("Incremental Search in the reverse direction", "I-Search Backward:");
  218. // 1100
  219. } else if ( failing && reverse && !wrapped && !overwrapped ) {
  220. text = i18n("Failing I-Search Backward:");
  221. // 0010
  222. } else if ( !failing && !reverse && wrapped && !overwrapped ) {
  223. text = i18n("Incremental Search has passed the end of the document", "Wrapped I-Search:");
  224. // 1010
  225. } else if ( failing && !reverse && wrapped && !overwrapped ) {
  226. text = i18n("Failing Wrapped I-Search:");
  227. // 0110
  228. } else if ( !failing && reverse && wrapped && !overwrapped ) {
  229. text = i18n("Wrapped I-Search Backward:");
  230. // 1110
  231. } else if ( failing && reverse && wrapped && !overwrapped ) {
  232. text = i18n("Failing Wrapped I-Search Backward:");
  233. // 0011
  234. } else if ( !failing && !reverse && overwrapped ) {
  235. text = i18n("Incremental Search has passed both the end of the document "
  236. "and the original starting position", "Overwrapped I-Search:");
  237. // 1011
  238. } else if ( failing && !reverse && overwrapped ) {
  239. text = i18n("Failing Overwrapped I-Search:");
  240. // 0111
  241. } else if ( !failing && reverse && overwrapped ) {
  242. text = i18n("Overwrapped I-Search Backwards:");
  243. // 1111
  244. } else if ( failing && reverse && overwrapped ) {
  245. text = i18n("Failing Overwrapped I-Search Backward:");
  246. } else {
  247. text = i18n("Error: unknown i-search state!");
  248. }
  249. m_label->setText( text );
  250. }
  251. void ISearchPluginView::slotSearchForwardAction()
  252. {
  253. slotSearchAction( false );
  254. }
  255. void ISearchPluginView::slotSearchBackwardAction()
  256. {
  257. slotSearchAction( true );
  258. }
  259. void ISearchPluginView::slotSearchAction( bool reverse )
  260. {
  261. if( !m_combo->hasFocus() ) {
  262. if( m_comboAction->container(0) && m_comboAction->container(0)->isHidden() ) {
  263. m_toolBarWasHidden = true;
  264. m_comboAction->container(0)->setHidden( false );
  265. } else {
  266. m_toolBarWasHidden = false;
  267. }
  268. m_combo->setFocus(); // Will call startSearch()
  269. } else {
  270. nextMatch( reverse );
  271. }
  272. }
  273. void ISearchPluginView::nextMatch( bool reverse )
  274. {
  275. TQString text = m_combo->currentText();
  276. if( text.isEmpty() )
  277. return;
  278. if( state != MatchSearch ) {
  279. // Last search was performed by typing, start from that match.
  280. if( !reverse ) {
  281. m_searchLine = m_foundLine;
  282. m_searchCol = m_foundCol + m_matchLen;
  283. } else {
  284. m_searchLine = m_foundLine;
  285. m_searchCol = m_foundCol;
  286. }
  287. state = MatchSearch;
  288. }
  289. bool found = iSearch( m_searchLine, m_searchCol, text, reverse, m_autoWrap );
  290. if( found ) {
  291. m_searchLine = m_foundLine;
  292. m_searchCol = m_foundCol + m_matchLen;
  293. } else {
  294. m_wrapped = true;
  295. m_searchLine = m_searchCol = 0;
  296. }
  297. }
  298. void ISearchPluginView::startSearch()
  299. {
  300. if( !m_view ) return;
  301. m_searchForwardAction->setText( i18n("Next Incremental Search Match") );
  302. m_searchBackwardAction->setText( i18n("Previous Incremental Search Match") );
  303. m_wrapped = false;
  304. if( m_fromBeginning ) {
  305. m_startLine = m_startCol = 0;
  306. } else {
  307. m_cursorIF->cursorPositionReal( &m_startLine, &m_startCol );
  308. }
  309. m_searchLine = m_startLine;
  310. m_searchCol = m_startCol;
  311. updateLabelText( false, m_searchBackward );
  312. m_combo->blockSignals( true );
  313. TQString text = m_selectIF->selection();
  314. if( text.isEmpty() )
  315. text = m_lastString;
  316. m_combo->setCurrentText( text );
  317. m_combo->blockSignals( false );
  318. m_combo->lineEdit()->selectAll();
  319. // kdDebug() << "Starting search at " << m_startLine << ", " << m_startCol << endl;
  320. }
  321. void ISearchPluginView::endSearch()
  322. {
  323. m_searchForwardAction->setText( i18n("Search Incrementally") );
  324. m_searchBackwardAction->setText( i18n("Search Incrementally Backwards") );
  325. updateLabelText();
  326. if( m_toolBarWasHidden && m_comboAction->containerCount() > 0 ) {
  327. m_comboAction->container(0)->setHidden( true );
  328. }
  329. }
  330. void ISearchPluginView::quitToView( const TQString &text )
  331. {
  332. if( !text.isNull() && !text.isEmpty() ) {
  333. m_combo->addToHistory( text );
  334. m_lastString = text;
  335. }
  336. if( m_view ) {
  337. m_view->setFocus(); // Will call endSearch()
  338. }
  339. }
  340. void ISearchPluginView::slotTextChanged( const TQString& text )
  341. {
  342. state = TextSearch;
  343. if( text.isEmpty() )
  344. return;
  345. iSearch( m_searchLine, m_searchCol, text, m_searchBackward, m_autoWrap );
  346. }
  347. void ISearchPluginView::slotReturnPressed( const TQString& text )
  348. {
  349. quitToView( text );
  350. }
  351. void ISearchPluginView::slotAddContextMenuItems( TQPopupMenu *menu )
  352. {
  353. if( menu ) {
  354. menu->insertSeparator();
  355. menu->insertItem( i18n("Case Sensitive"), this,
  356. TQT_SLOT(setCaseSensitive(bool)));
  357. menu->insertItem( i18n("From Beginning"), this,
  358. TQT_SLOT(setFromBeginning(bool)));
  359. menu->insertItem( i18n("Regular Expression"), this,
  360. TQT_SLOT(setRegExp(bool)));
  361. //menu->insertItem( i18n("Auto-Wrap Search"), this,
  362. // TQT_SLOT(setAutoWrap(bool)));
  363. }
  364. }
  365. bool ISearchPluginView::iSearch(
  366. uint startLine, uint startCol,
  367. const TQString& text, bool reverse,
  368. bool autoWrap )
  369. {
  370. if( !m_view ) return false;
  371. // kdDebug() << "Searching for " << text << " at " << startLine << ", " << startCol << endl;
  372. bool found = false;
  373. if( !m_regExp ) {
  374. found = m_searchIF->searchText( startLine,
  375. startCol,
  376. text,
  377. &m_foundLine,
  378. &m_foundCol,
  379. &m_matchLen,
  380. m_caseSensitive,
  381. reverse );
  382. } else {
  383. found = m_searchIF->searchText( startLine,
  384. startCol,
  385. TQRegExp( text ),
  386. &m_foundLine,
  387. &m_foundCol,
  388. &m_matchLen,
  389. reverse );
  390. }
  391. if( found ) {
  392. // kdDebug() << "Found '" << text << "' at " << m_foundLine << ", " << m_foundCol << endl;
  393. // v->gotoLineNumber( m_foundLine );
  394. m_cursorIF->setCursorPositionReal( m_foundLine, m_foundCol + m_matchLen );
  395. m_selectIF->setSelection( m_foundLine, m_foundCol, m_foundLine, m_foundCol + m_matchLen );
  396. } else if ( autoWrap ) {
  397. m_wrapped = true;
  398. found = iSearch( 0, 0, text, reverse, false );
  399. }
  400. // FIXME
  401. bool overwrapped = ( m_wrapped &&
  402. ((m_foundLine > m_startLine ) ||
  403. (m_foundLine == m_startLine && m_foundCol >= m_startCol)) );
  404. // kdDebug() << "Overwrap = " << overwrapped << ". Start was " << m_startLine << ", " << m_startCol << endl;
  405. updateLabelText( !found, reverse, m_wrapped, overwrapped );
  406. return found;
  407. }
  408. ISearchPlugin::ISearchPlugin( TQObject *parent, const char* name, const TQStringList& )
  409. : KTextEditor::Plugin ( (KTextEditor::Document*) parent, name )
  410. {
  411. }
  412. ISearchPlugin::~ISearchPlugin()
  413. {
  414. }
  415. void ISearchPlugin::addView(KTextEditor::View *view)
  416. {
  417. ISearchPluginView *nview = new ISearchPluginView (view);
  418. nview->setView (view);
  419. m_views.append (nview);
  420. }
  421. void ISearchPlugin::removeView(KTextEditor::View *view)
  422. {
  423. for (uint z=0; z < m_views.count(); z++)
  424. {
  425. if (m_views.at(z)->parentClient() == view)
  426. {
  427. ISearchPluginView *nview = m_views.at(z);
  428. m_views.remove (nview);
  429. delete nview;
  430. }
  431. }
  432. }