/plugins/XSearch/tags/release-1-0-7/XSearch/xsearch/XSearchHistoryTextField.java

# · Java · 568 lines · 362 code · 70 blank · 136 comment · 78 complexity · 64c636e2040f45064fe149fbf135f9cc MD5 · raw file

  1. /*
  2. * XSearchHistoryTextField.java - Text field with a history
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 2003 Rudolf Widmann
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21. */
  22. package xsearch;
  23. //{{{ Imports
  24. import javax.swing.*;
  25. import javax.swing.border.Border;
  26. import javax.swing.border.AbstractBorder;
  27. import javax.swing.border.CompoundBorder;
  28. import javax.swing.event.MouseInputAdapter;
  29. import java.awt.*;
  30. import java.awt.event.*;
  31. import org.gjt.sp.jedit.*;
  32. import org.gjt.sp.jedit.gui.*;
  33. //}}}
  34. /**
  35. * History Text field which memorizes which action occured
  36. * @author Rudolf Widmann
  37. * @version $Id: XSearchHistoryTextField.java,v 0.9 2003/03/21
  38. */
  39. public class XSearchHistoryTextField extends JTextField
  40. {
  41. public static final boolean RECEIVED_EVENT_ENTER = true;
  42. public static final boolean RECEIVED_EVENT_SELECT = false;
  43. //{{{ HistoryTextField constructor
  44. /**
  45. * Creates a new history text field.
  46. * @param name The history model name
  47. * @param instantPopups If true, selecting a value from the history
  48. * popup will immediately fire an ActionEvent. If false, the user
  49. * will have to press 'Enter' first
  50. * @param enterAddsToHistory If true, pressing the Enter key will
  51. * automatically add the currently entered text to the history.
  52. *
  53. * @since jEdit 2.6pre5
  54. */
  55. public XSearchHistoryTextField(String name, boolean instantPopups,
  56. boolean enterAddsToHistory)
  57. {
  58. setBorder(new CompoundBorder(getBorder(),new HistoryBorder()));
  59. if(name != null)
  60. historyModel = HistoryModel.getModel(name);
  61. MouseHandler mouseHandler = new MouseHandler();
  62. addMouseListener(mouseHandler);
  63. addMouseMotionListener(mouseHandler);
  64. this.instantPopups = instantPopups;
  65. this.enterAddsToHistory = enterAddsToHistory;
  66. index = -1;
  67. } //}}}
  68. //{{{ setInstantPopups() method
  69. /**
  70. * Sets if selecting a value from the popup should immediately fire
  71. * an ActionEvent.
  72. * @since jEdit 4.0pre3
  73. */
  74. public void setInstantPopups(boolean instantPopups)
  75. {
  76. this.instantPopups = instantPopups;
  77. } //}}}
  78. //{{{ getInstantPopups() method
  79. /**
  80. * Returns if selecting a value from the popup should immediately fire
  81. * an ActionEvent.
  82. * @since jEdit 4.0pre3
  83. */
  84. public boolean getInstantPopups()
  85. {
  86. return instantPopups;
  87. } //}}}
  88. //{{{ setEnterAddsToHistory() method
  89. /**
  90. * Sets if pressing Enter should automatically add the currently
  91. * entered text to the history.
  92. * @since jEdit 4.0pre3
  93. */
  94. public void setEnterAddsToHistory(boolean enterAddsToHistory)
  95. {
  96. this.enterAddsToHistory = enterAddsToHistory;
  97. } //}}}
  98. //{{{ getEnterAddsToHistory() method
  99. /**
  100. * Returns if pressing Enter should automatically add the currently
  101. * entered text to the history.
  102. * @since jEdit 4.0pre3
  103. */
  104. public boolean setEnterAddsToHistory()
  105. {
  106. return enterAddsToHistory;
  107. } //}}}
  108. //{{{ setSelectAllOnFocus() method
  109. /**
  110. * Sets if all text should be selected when the field gets focus.
  111. * @since jEdit 4.0pre3
  112. */
  113. public void setSelectAllOnFocus(boolean selectAllOnFocus)
  114. {
  115. this.selectAllOnFocus = selectAllOnFocus;
  116. } //}}}
  117. //{{{ getSelectAllOnFocus() method
  118. /**
  119. * Returns if all text should be selected when the field gets focus.
  120. * @since jEdit 4.0pre3
  121. */
  122. public boolean setSelectAllOnFocus()
  123. {
  124. return selectAllOnFocus;
  125. } //}}}
  126. //{{{ setModel() method
  127. /**
  128. * Sets the history list model.
  129. * @param name The model name
  130. * @since jEdit 2.3pre3
  131. */
  132. public void setModel(String name)
  133. {
  134. if(name == null)
  135. historyModel = null;
  136. else
  137. historyModel = HistoryModel.getModel(name);
  138. index = -1;
  139. repaint();
  140. } //}}}
  141. //{{{ addCurrentToHistory() method
  142. /**
  143. * Adds the currently entered item to the history.
  144. */
  145. public void addCurrentToHistory()
  146. {
  147. if(historyModel != null)
  148. historyModel.addItem(getText());
  149. index = 0;
  150. } //}}}
  151. //{{{ setText() method
  152. /**
  153. * Sets the displayed text.
  154. */
  155. public void setText(String text)
  156. {
  157. super.setText(text);
  158. index = -1;
  159. } //}}}
  160. //{{{ getModel() method
  161. /**
  162. * Returns the underlying history model.
  163. */
  164. public HistoryModel getModel()
  165. {
  166. return historyModel;
  167. } //}}}
  168. //{{{ fireActionPerformed() method
  169. /**
  170. * Fires an action event to all listeners. This is public so
  171. * that inner classes can access it.
  172. */
  173. public void fireActionPerformed(boolean receivedEvent)
  174. {
  175. this.receivedEvent = receivedEvent;
  176. super.fireActionPerformed();
  177. } //}}}
  178. //{{{ getReceivedEvent() method
  179. /**
  180. * Returns the event which caused actionPerformed
  181. * This makes it possible to distinguish <enter> and <select item>
  182. */
  183. public boolean getReceivedEvent()
  184. {
  185. return receivedEvent;
  186. } //}}}
  187. //{{{ Protected members
  188. //{{{ processKeyEvent() method
  189. protected void processKeyEvent(KeyEvent evt)
  190. {
  191. if(!isEnabled())
  192. return;
  193. /*evt = KeyEventWorkaround.processKeyEvent(evt);
  194. if(evt == null)
  195. return;*/
  196. if(evt.getID() == KeyEvent.KEY_PRESSED)
  197. {
  198. if(evt.getKeyCode() == KeyEvent.VK_ENTER)
  199. {
  200. if(enterAddsToHistory)
  201. addCurrentToHistory();
  202. if(evt.getModifiers() == 0)
  203. {
  204. fireActionPerformed(RECEIVED_EVENT_ENTER);
  205. evt.consume();
  206. }
  207. }
  208. else if(evt.getKeyCode() == KeyEvent.VK_UP)
  209. {
  210. if(evt.isShiftDown())
  211. doBackwardSearch();
  212. else
  213. historyPrevious();
  214. evt.consume();
  215. }
  216. else if(evt.getKeyCode() == KeyEvent.VK_DOWN)
  217. {
  218. if(evt.isShiftDown())
  219. doForwardSearch();
  220. else
  221. historyNext();
  222. evt.consume();
  223. }
  224. else if(evt.getKeyCode() == KeyEvent.VK_TAB
  225. && evt.isControlDown())
  226. {
  227. doBackwardSearch();
  228. evt.consume();
  229. }
  230. }
  231. if(!evt.isConsumed())
  232. super.processKeyEvent(evt);
  233. } //}}}
  234. //{{{ processMouseEvent() method
  235. protected void processMouseEvent(MouseEvent evt)
  236. {
  237. if(!isEnabled())
  238. return;
  239. switch(evt.getID())
  240. {
  241. case MouseEvent.MOUSE_PRESSED:
  242. Border border = getBorder();
  243. Insets insets = border.getBorderInsets(XSearchHistoryTextField.this);
  244. if(evt.getX() >= getWidth() - insets.right
  245. || GUIUtilities.isPopupTrigger(evt))
  246. {
  247. if(evt.isShiftDown())
  248. showPopupMenu(getText().substring(0,
  249. getSelectionStart()),0,getHeight());
  250. else
  251. showPopupMenu("",0,getHeight());
  252. }
  253. else
  254. super.processMouseEvent(evt);
  255. break;
  256. case MouseEvent.MOUSE_EXITED:
  257. setCursor(Cursor.getDefaultCursor());
  258. super.processMouseEvent(evt);
  259. break;
  260. default:
  261. super.processMouseEvent(evt);
  262. break;
  263. }
  264. } //}}}
  265. //}}}
  266. //{{{ Private members
  267. //{{{ Instance variables
  268. private HistoryModel historyModel;
  269. private JPopupMenu popup;
  270. private boolean instantPopups;
  271. private boolean enterAddsToHistory;
  272. private boolean selectAllOnFocus;
  273. private String current;
  274. private int index;
  275. private boolean receivedEvent;
  276. //}}}
  277. //{{{ doBackwardSearch() method
  278. private void doBackwardSearch()
  279. {
  280. if(historyModel == null)
  281. return;
  282. if(getSelectionEnd() != getDocument().getLength())
  283. {
  284. setCaretPosition(getDocument().getLength());
  285. }
  286. String text = getText().substring(0,getSelectionStart());
  287. if(text == null)
  288. {
  289. historyPrevious();
  290. return;
  291. }
  292. for(int i = index + 1; i < historyModel.getSize(); i++)
  293. {
  294. String item = historyModel.getItem(i);
  295. if(item.startsWith(text))
  296. {
  297. replaceSelection(item.substring(text.length()));
  298. select(text.length(),getDocument().getLength());
  299. index = i;
  300. return;
  301. }
  302. }
  303. getToolkit().beep();
  304. } //}}}
  305. //{{{ doForwardSearch() method
  306. private void doForwardSearch()
  307. {
  308. if(historyModel == null)
  309. return;
  310. if(getSelectionEnd() != getDocument().getLength())
  311. {
  312. setCaretPosition(getDocument().getLength());
  313. }
  314. String text = getText().substring(0,getSelectionStart());
  315. if(text == null)
  316. {
  317. historyNext();
  318. return;
  319. }
  320. for(int i = index - 1; i >= 0; i--)
  321. {
  322. String item = historyModel.getItem(i);
  323. if(item.startsWith(text))
  324. {
  325. replaceSelection(item.substring(text.length()));
  326. select(text.length(),getDocument().getLength());
  327. index = i;
  328. return;
  329. }
  330. }
  331. getToolkit().beep();
  332. } //}}}
  333. //{{{ historyPrevious() method
  334. private void historyPrevious()
  335. {
  336. if(historyModel == null)
  337. return;
  338. if(index == historyModel.getSize() - 1)
  339. getToolkit().beep();
  340. else if(index == -1)
  341. {
  342. current = getText();
  343. setText(historyModel.getItem(0));
  344. index = 0;
  345. }
  346. else
  347. {
  348. // have to do this because setText() sets index to -1
  349. int newIndex = index + 1;
  350. setText(historyModel.getItem(newIndex));
  351. index = newIndex;
  352. }
  353. } //}}}
  354. //{{{ historyNext() method
  355. private void historyNext()
  356. {
  357. if(historyModel == null)
  358. return;
  359. if(index == -1)
  360. getToolkit().beep();
  361. else if(index == 0)
  362. setText(current);
  363. else
  364. {
  365. // have to do this because setText() sets index to -1
  366. int newIndex = index - 1;
  367. setText(historyModel.getItem(newIndex));
  368. index = newIndex;
  369. }
  370. } //}}}
  371. //{{{ showPopupMenu() method
  372. private void showPopupMenu(String text, int x, int y)
  373. {
  374. if(historyModel == null)
  375. return;
  376. requestFocus();
  377. if(popup != null && popup.isVisible())
  378. {
  379. popup.setVisible(false);
  380. return;
  381. }
  382. ActionHandler actionListener = new ActionHandler();
  383. popup = new JPopupMenu();
  384. JMenuItem caption = new JMenuItem(jEdit.getProperty(
  385. "history.caption"));
  386. caption.getModel().setEnabled(false);
  387. popup.add(caption);
  388. popup.addSeparator();
  389. for(int i = 0; i < historyModel.getSize(); i++)
  390. {
  391. String item = historyModel.getItem(i);
  392. if(item.startsWith(text))
  393. {
  394. JMenuItem menuItem = new JMenuItem(item);
  395. menuItem.setActionCommand(String.valueOf(i));
  396. menuItem.addActionListener(actionListener);
  397. popup.add(menuItem);
  398. }
  399. }
  400. GUIUtilities.showPopupMenu(popup,this,x,y,false);
  401. } //}}}
  402. //}}}
  403. //{{{ Inner classes
  404. //{{{ ActionHandler class
  405. class ActionHandler implements ActionListener
  406. {
  407. public void actionPerformed(ActionEvent evt)
  408. {
  409. int ind = Integer.parseInt(evt.getActionCommand());
  410. if(ind == -1)
  411. {
  412. if(index != -1)
  413. setText(current);
  414. }
  415. else
  416. {
  417. setText(historyModel.getItem(ind));
  418. index = ind;
  419. }
  420. if(instantPopups)
  421. {
  422. addCurrentToHistory();
  423. fireActionPerformed(RECEIVED_EVENT_SELECT);
  424. }
  425. }
  426. } //}}}
  427. //{{{ MouseHandler class
  428. class MouseHandler extends MouseInputAdapter
  429. {
  430. boolean selectAll;
  431. //{{{ mousePressed() method
  432. public void mousePressed(MouseEvent evt)
  433. {
  434. selectAll = (!hasFocus() && selectAllOnFocus);
  435. } //}}}
  436. //{{{ mouseReleased() method
  437. public void mouseReleased(MouseEvent evt)
  438. {
  439. SwingUtilities.invokeLater(new Runnable()
  440. {
  441. public void run()
  442. {
  443. if(selectAll)
  444. selectAll();
  445. }
  446. });
  447. } //}}}
  448. //{{{ mouseMoved() method
  449. public void mouseMoved(MouseEvent evt)
  450. {
  451. Border border = getBorder();
  452. Insets insets = border.getBorderInsets(XSearchHistoryTextField.this);
  453. if(evt.getX() >= getWidth() - insets.right)
  454. setCursor(Cursor.getDefaultCursor());
  455. else
  456. setCursor(Cursor.getPredefinedCursor(
  457. Cursor.TEXT_CURSOR));
  458. } //}}}
  459. //{{{ mouseDragged() method
  460. public void mouseDragged(MouseEvent evt)
  461. {
  462. selectAll = false;
  463. } //}}}
  464. } //}}}
  465. //{{{ HistoryBorder class
  466. static class HistoryBorder extends AbstractBorder
  467. {
  468. static final int WIDTH = 16;
  469. public void paintBorder(Component c, Graphics g,
  470. int x, int y, int w, int h)
  471. {
  472. g.translate(x+w-WIDTH,y-1);
  473. //if(c.isEnabled())
  474. //{
  475. // // vertical separation line
  476. // g.setColor(UIManager.getColor("controlDkShadow"));
  477. // g.drawLine(0,0,0,h);
  478. //}
  479. // down arrow
  480. int w2 = WIDTH/2;
  481. int h2 = h/2;
  482. g.setColor(UIManager.getColor(c.isEnabled()
  483. && ((XSearchHistoryTextField)c).getModel() != null
  484. ? "TextField.foreground" : "TextField.disabledForeground"));
  485. g.drawLine(w2-5,h2-2,w2+4,h2-2);
  486. g.drawLine(w2-4,h2-1,w2+3,h2-1);
  487. g.drawLine(w2-3,h2 ,w2+2,h2 );
  488. g.drawLine(w2-2,h2+1,w2+1,h2+1);
  489. g.drawLine(w2-1,h2+2,w2 ,h2+2);
  490. g.translate(-(x+w-WIDTH),-(y-1));
  491. }
  492. public Insets getBorderInsets(Component c)
  493. {
  494. return new Insets(0,0,0,WIDTH);
  495. }
  496. } //}}}
  497. //}}}
  498. }