001: package org.columba.core.gui.search;
002:
003: import java.awt.AlphaComposite;
004: import java.awt.BorderLayout;
005: import java.awt.Component;
006: import java.awt.Container;
007: import java.awt.Graphics;
008: import java.awt.Graphics2D;
009: import java.awt.Insets;
010: import java.awt.event.ActionEvent;
011: import java.awt.event.ActionListener;
012: import java.awt.event.KeyAdapter;
013: import java.awt.event.KeyEvent;
014: import java.awt.geom.RoundRectangle2D;
015: import java.util.Iterator;
016: import java.util.List;
017: import java.util.Vector;
018:
019: import javax.swing.BorderFactory;
020: import javax.swing.Box;
021: import javax.swing.DefaultListCellRenderer;
022: import javax.swing.Icon;
023: import javax.swing.ImageIcon;
024: import javax.swing.JButton;
025: import javax.swing.JCheckBox;
026: import javax.swing.JComboBox;
027: import javax.swing.JLabel;
028: import javax.swing.JList;
029: import javax.swing.JMenuBar;
030: import javax.swing.JMenuItem;
031: import javax.swing.JPanel;
032: import javax.swing.JPopupMenu;
033: import javax.swing.JToolBar;
034: import javax.swing.UIManager;
035: import javax.swing.event.DocumentEvent;
036: import javax.swing.event.DocumentListener;
037: import javax.swing.event.PopupMenuEvent;
038: import javax.swing.event.PopupMenuListener;
039:
040: import org.columba.api.gui.frame.IFrameMediator;
041: import org.columba.core.context.ContextResultListenerAdapter;
042: import org.columba.core.context.api.IContextResultEvent;
043: import org.columba.core.gui.action.AbstractColumbaAction;
044: import org.columba.core.gui.base.AscendingIcon;
045: import org.columba.core.gui.search.api.ICriteriaRenderer;
046: import org.columba.core.gui.search.api.ISearchPanel;
047: import org.columba.core.gui.toolbar.ToolBarButtonFactory;
048: import org.columba.core.resourceloader.IconKeys;
049: import org.columba.core.resourceloader.ImageLoader;
050: import org.columba.core.search.ResultListenerAdapter;
051: import org.columba.core.search.SearchRequest;
052: import org.columba.core.search.api.IResultEvent;
053: import org.columba.core.search.api.ISearchCriteria;
054: import org.columba.core.search.api.ISearchManager;
055: import org.columba.core.search.api.ISearchProvider;
056: import org.columba.core.search.api.ISearchRequest;
057:
058: import com.jgoodies.forms.builder.DefaultFormBuilder;
059: import com.jgoodies.forms.layout.FormLayout;
060:
061: public class SearchBar extends JPanel implements PopupMenuListener {
062:
063: private IconTextField textField;
064:
065: private ImageIcon icon = ImageLoader
066: .getSmallIcon(IconKeys.EDIT_FIND);
067:
068: private JButton searchButton;
069:
070: private JCheckBox searchInsideCheckBox;
071:
072: private ISearchPanel searchPanel;
073:
074: private JComboBox providerComboBox;
075:
076: private JButton contextButton;
077:
078: private ContextSearchAction action;
079:
080: private SearchDialog searchDialog;
081:
082: private JButton moreButton;
083:
084: private IFrameMediator frameMediator;
085:
086: public SearchBar(final ISearchPanel searchPanel,
087: final IFrameMediator frameMediator) {
088: super ();
089:
090: this .searchPanel = searchPanel;
091: this .frameMediator = frameMediator;
092:
093: initComponents();
094:
095: layoutComponents(false);
096:
097: initListeners();
098: }
099:
100: public void search(Iterator<ICriteriaRenderer> it, boolean matchAll) {
101:
102: // create list of search requests
103: List<ISearchRequest> list = new Vector<ISearchRequest>();
104: while (it.hasNext()) {
105: ICriteriaRenderer r = it.next();
106: ISearchCriteria c = r.getCriteria();
107: String providerName = r.getProvider().getTechnicalName();
108: Object value = r.getValue();
109: // skip if user didn't specify search term for this criteria
110: if (value == null)
111: continue;
112:
113: String searchTerm = value.toString();
114:
115: list.add(new SearchRequest(c.getTechnicalName(),
116: providerName, searchTerm));
117: }
118:
119: // execute search
120: searchPanel.searchComplex(list, matchAll, searchInsideCheckBox
121: .isSelected());
122: }
123:
124: public boolean isSelectedSearchInside() {
125: return searchInsideCheckBox.isSelected();
126: }
127:
128: private void toggleButtonStates(boolean enabled) {
129: action.setEnabled(enabled);
130: contextButton.setEnabled(enabled);
131: searchButton.setEnabled(enabled);
132: textField.setEnabled(enabled);
133: searchInsideCheckBox.setEnabled(enabled);
134: try {
135: moreButton.setEnabled(enabled);
136: } catch (RuntimeException e) {
137: e.printStackTrace();
138: }
139: }
140:
141: private void toggleSearchDialogVisibility() {
142: if (searchDialog == null)
143: searchDialog = new SearchDialog(frameMediator
144: .getContainer().getFrame(), searchPanel
145: .getSearchManager().getAllProviders());
146:
147: if (!searchDialog.isVisible()) {
148: int x = getLocationOnScreen().x;
149: // toolbar container
150: Container c2 = getParent().getParent();
151: int y = c2.getLocationOnScreen().y + c2.getHeight();
152: int maxX = x + getWidth();
153:
154: int xx = maxX - searchDialog.getWidth();
155: searchDialog.setLocation(xx, y);
156: searchDialog.setVisible(true);
157:
158: if (searchDialog.isSuccess()) {
159: // user pressed "Search" button
160: search(searchDialog.getAllCriteriaRenderer(),
161: searchDialog.isMatchAll());
162: }
163: } else {
164: searchDialog.setVisible(false);
165: }
166: }
167:
168: private void initListeners() {
169: // show search dialog
170: moreButton.addActionListener(new ActionListener() {
171: public void actionPerformed(ActionEvent e) {
172: toggleSearchDialogVisibility();
173: }
174: });
175:
176: // on "RETURN" start seach
177: textField.addKeyListener(new MyKeyListener());
178:
179: textField.getDocument().addDocumentListener(
180: new MyDocumentListener());
181:
182: // update popup menu with search term before its made visible
183: // textField.addPopupMenuListener(this);
184:
185: // enable/disable search bar during search
186: searchPanel.getSearchManager().addResultListener(
187: new MyResultListener());
188:
189: // start search
190: searchButton.addActionListener(new ActionListener() {
191: public void actionPerformed(ActionEvent e) {
192: toggleButtonStates(false);
193: SearchBar.this .searchPanel.searchAll(textField
194: .getText(), searchInsideCheckBox.isSelected());
195: }
196: });
197:
198: // to enable button again after search is finished
199: searchPanel.getContextSearchManager().addResultListener(
200: new MyContextResultListener());
201: }
202:
203: class MyDocumentListener implements DocumentListener {
204:
205: public void changedUpdate(DocumentEvent e) {
206: handleEvent(e);
207: }
208:
209: public void insertUpdate(DocumentEvent e) {
210: handleEvent(e);
211: }
212:
213: public void removeUpdate(DocumentEvent e) {
214: handleEvent(e);
215: }
216:
217: private void handleEvent(DocumentEvent e) {
218: updatePopupMenu(textField.getPopupMenu(), textField
219: .getText());
220: //textField.showPopup();
221: //textField.requestFocus();
222: }
223:
224: }
225:
226: class MyComboBoxRenderer extends DefaultListCellRenderer {
227:
228: @Override
229: public Component getListCellRendererComponent(JList list,
230: Object value, int index, boolean isSelected,
231: boolean cellHasFocus) {
232: super .getListCellRendererComponent(list, value, index,
233: isSelected, cellHasFocus);
234:
235: // skip "Search All"
236: if (value instanceof ISearchProvider) {
237: ISearchProvider p = (ISearchProvider) value;
238: setText(p.getName());
239: setIcon(p.getIcon());
240: }
241:
242: if (getIcon() == null) {
243: setBorder(BorderFactory.createEmptyBorder(0,
244: 16 + getIconTextGap(), 0, 0));
245: } else {
246: setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
247: }
248:
249: return this ;
250: }
251:
252: }
253:
254: class CoolButton extends JButton {
255:
256: public CoolButton(String text, Icon icon) {
257: super (text, icon);
258: }
259:
260: public void paintComponent(Graphics g) {
261: Graphics2D g2d = (Graphics2D) g;
262:
263: int w = getWidth();
264: int h = getHeight();
265:
266: g2d.setBackground(UIManager.getColor("control.background"));
267: g2d.setColor(UIManager.getColor("control.text"));
268:
269: // create a rounded clip LARGER than the comp
270: RoundRectangle2D.Float r2d = new RoundRectangle2D.Float(0,
271: 0, w + 30, h - 1, 20, 20);
272:
273: // intersect this with the existing clip
274: g2d.clip(r2d);
275:
276: // fill the clipped area
277: // g2d.setPaint(LIGHT_GRADIENT);
278: g2d.fillRect(0, 0, w, h);
279: // restore original clip
280:
281: // paint outer border
282: // g2d.setPaint(OUTER);
283: g2d.drawRoundRect(0, 0, w + 30, h - 1, 20, 20);
284:
285: // paint inner border
286: // g2d.setPaint(INNER);
287: g2d.drawRoundRect(1, 1, w + 30, h - 3, 18, 18);
288:
289: // paint right outside border
290: // g2d.setPaint(p1);
291: g2d.drawLine(w - 1, 1, w - 1, h);
292:
293: // paint right inside border
294: // g2d.setPaint(p2);
295: g2d.drawLine(w - 2, 2, w - 2, h - 1);
296: // make it translucent
297: g2d.setComposite(AlphaComposite.getInstance(
298: AlphaComposite.SRC_OVER, 0.1f));
299:
300: // paint the text and icon
301: super .paintComponent(g);
302:
303: }
304: }
305:
306: private void layoutComponents(boolean hasResults) {
307:
308: this .removeAll();
309:
310: FormLayout layout = new FormLayout(
311: "fill:default:grow, 2dlu, pref,1dlu, pref, 2dlu",
312: // 2 columns
313: "fill:default:grow");
314:
315: // create a form builder
316: DefaultFormBuilder builder = new DefaultFormBuilder(layout,
317: this );
318:
319: // builder.append(contextButton);
320: // builder.append(providerComboBox);
321:
322: // builder.append(moreButton);
323: JLabel label = new JLabel("Search:");
324: label.setLabelFor(textField);
325: label.setDisplayedMnemonic('s');
326: builder.append(label);
327:
328: builder.append(textField);
329:
330: builder.append(moreButton);
331:
332: // if ( hasResults)
333: // builder.append(searchInsideCheckBox);
334:
335: // builder.append(moreButton);
336:
337: // builder.append(searchButton);
338:
339: }
340:
341: private void initComponents() {
342: textField = new IconTextField(icon, 20);
343:
344: searchButton = new JButton("Search");
345: searchButton.setMnemonic('s');
346:
347: searchInsideCheckBox = new JCheckBox("Search Inside");
348: searchInsideCheckBox
349: .setToolTipText("Search Inside Previous Search Results");
350: searchInsideCheckBox.setMnemonic('i');
351: searchInsideCheckBox.setSelected(false);
352: // searchInsideCheckBox.setEnabled(false);
353:
354: action = new ContextSearchAction(frameMediator);
355:
356: contextButton = ToolBarButtonFactory.createButton(action);
357: contextButton.setEnabled(true);
358:
359: moreButton = ToolBarButtonFactory.createButton("",
360: new AscendingIcon(), false);
361: moreButton.setIcon(new AscendingIcon());
362: moreButton.setDisabledIcon(new AscendingIcon());
363: moreButton.setMargin(new Insets(1, 1, 1, 1));
364: moreButton.setToolTipText("Advanced Search Options...");
365:
366: providerComboBox = new JComboBox();
367: providerComboBox.setRenderer(new MyComboBoxRenderer());
368:
369: providerComboBox.addItem("Search All");
370: ISearchManager manager = searchPanel.getSearchManager();
371: Iterator<ISearchProvider> it = manager.getAllProviders();
372: while (it.hasNext()) {
373: final ISearchProvider p = it.next();
374: providerComboBox.addItem(p);
375: }
376: }
377:
378: class MyContextResultListener extends ContextResultListenerAdapter {
379:
380: @Override
381: public void finished(IContextResultEvent event) {
382: toggleButtonStates(true);
383: }
384:
385: }
386:
387: class ContextSearchAction extends AbstractColumbaAction {
388: ContextSearchAction(IFrameMediator mediator) {
389: super (mediator, "What's related");
390: putValue(SMALL_ICON, ImageLoader
391: .getSmallIcon("system-search.png"));
392:
393: // large icon for toolbar
394: putValue(LARGE_ICON, ImageLoader
395: .getIcon("system-search.png"));
396:
397: putValue(AbstractColumbaAction.SHORT_DESCRIPTION,
398: "What's related");
399: putValue(AbstractColumbaAction.LONG_DESCRIPTION,
400: "What's related");
401:
402: putValue(TOOLBAR_NAME, "What's related");
403:
404: setShowToolBarText(true);
405:
406: }
407:
408: public void actionPerformed(ActionEvent e) {
409: toggleButtonStates(false);
410: searchPanel.searchInContext();
411: }
412:
413: }
414:
415: class MyKeyListener extends KeyAdapter {
416:
417: @Override
418: public void keyReleased(KeyEvent e) {
419: char ch = e.getKeyChar();
420:
421: if (ch == KeyEvent.VK_ENTER) {
422: if (textField.getText().length() == 0)
423: return;
424:
425: toggleButtonStates(false);
426:
427: // get first provider
428: // ISearchProvider provider = searchPanel.getSearchManager()
429: // .getAllProviders().next();
430: // ISearchCriteria defaultCriteria = null;
431: //
432: // SearchBar.this.searchPanel.searchInCriteria(
433: // textField.getText(), provider.getTechnicalName(),
434: // provider.getDefaultCriteria(textField.getText())
435: // .getTechnicalName(), searchInsideCheckBox
436: // .isSelected());
437:
438: searchPanel.searchAll(textField.getText(),
439: searchInsideCheckBox.isSelected());
440: } else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
441: if (textField.getText().length() == 0)
442: return;
443: textField.showPopup();
444: textField.getPopupMenu().requestFocus();
445: textField.getPopupMenu().getSelectionModel()
446: .setSelectedIndex(0);
447: }
448:
449: }
450:
451: }
452:
453: public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
454: System.out.println("menu will become visible");
455:
456: // update popup menu based on searchterm
457: updatePopupMenu(textField.getPopupMenu(), textField.getText());
458: }
459:
460: public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
461: }
462:
463: public void popupMenuCanceled(PopupMenuEvent e) {
464: }
465:
466: private void updatePopupMenu(JPopupMenu menu, String searchTerm) {
467: System.out.println("update menu=" + searchTerm);
468:
469: menu.removeAll();
470:
471: // add menuitem to search across all components
472: // JMenuItem m2 = new JMenuItem("Search All");
473: // m2.setToolTipText("Search across all components");
474: // m2.addActionListener(new ActionListener() {
475: // public void actionPerformed(ActionEvent e) {
476: // searchPanel.searchAll(textField.getText(), searchInsideCheckBox
477: // .isSelected());
478: // }
479: // });
480: // menu.add(m2);
481: // menu.addSeparator();
482:
483: ISearchManager manager = searchPanel.getSearchManager();
484: Iterator<ISearchProvider> it = manager.getAllProviders();
485: while (it.hasNext()) {
486: final ISearchProvider p = it.next();
487:
488: // create a single menu item for all the search criteria
489: // of this provider
490: // JMenuItem m = new JMenuItem(p.getName());
491: // m.setToolTipText(p.getDescription());
492: // m.setIcon(p.getIcon());
493: // m.addActionListener(new ActionListener() {
494: // public void actionPerformed(ActionEvent e) {
495: // searchPanel.searchInProvider(textField.getText(), p
496: // .getTechnicalName(), searchInsideCheckBox.isSelected());
497: // }
498: // });
499: // menu.add(m);
500:
501: JMenuItem m;
502: // create all individual search criteria for this provider
503: List<ISearchCriteria> v = p.getAllCriteria(searchTerm);
504: Iterator<ISearchCriteria> it2 = v.iterator();
505: while (it2.hasNext()) {
506: final ISearchCriteria c = it2.next();
507: if (c == null)
508: continue;
509:
510: m = new JMenuItem(c.getTitle());
511: m.setToolTipText(c.getDescription());
512: m.setIcon(p.getIcon());
513: m.addActionListener(new ActionListener() {
514: public void actionPerformed(ActionEvent e) {
515: searchPanel.searchInCriteria(textField
516: .getText(), p.getTechnicalName(), c
517: .getTechnicalName(),
518: searchInsideCheckBox.isSelected());
519: }
520: });
521: menu.add(m);
522: }
523:
524: if (it.hasNext())
525: menu.addSeparator();
526: }
527:
528: menu.addSeparator();
529:
530: JMenuItem m = new JMenuItem("Advanced Search...");
531: m.setToolTipText("Advanced Search...");
532: m.addActionListener(new ActionListener() {
533: public void actionPerformed(ActionEvent e) {
534: toggleSearchDialogVisibility();
535: }
536: });
537: menu.add(m);
538:
539: // create search history
540:
541: // Map<String, ISearchProvider> historyMap = SearchHistoryList
542: // .getInstance().getHistoryMap();
543:
544: // if (historyMap.size() > 0) {
545: // Iterator<String> it3 = historyMap.keySet().iterator();
546: // while (it3.hasNext()) {
547: // String term = it3.next();
548: // ISearchProvider p = historyMap.get(term);
549: // ISearchCriteria c = p.getAllCriteria(term);
550: // if (c == null)
551: // continue;
552: //
553: // JMenuItem m = new JMenuItem(c.getTitle());
554: // m.setToolTipText(c.getDescription());
555: // m.setIcon(c.getIcon());
556: // m.setActionCommand(p.getName());
557: // m.addActionListener(listener);
558: // menu.add(m);
559: // }
560: //
561: // menu.insert(new JSeparator(), menu.getComponentCount() -
562: // historyMap.size());
563: // }
564:
565: menu.revalidate();
566: menu.pack();
567: }
568:
569: public void install(JMenuBar menubar) {
570: if (menubar == null)
571: throw new IllegalArgumentException("menubar == null");
572:
573: Component box = Box.createHorizontalGlue();
574: menubar.add(box);
575:
576: menubar.add(this );
577: }
578:
579: public void install(JToolBar toolbar) {
580: if (toolbar == null)
581: throw new IllegalArgumentException("toolbar");
582:
583: toolbar.add(contextButton);
584:
585: JPanel p = new JPanel();
586: p.setBorder(BorderFactory.createEmptyBorder(3, 0, 2, 0));
587: p.setLayout(new BorderLayout());
588: p.add(this , BorderLayout.EAST);
589:
590: toolbar.add(p);
591: }
592:
593: class MyResultListener extends ResultListenerAdapter {
594:
595: MyResultListener() {
596: }
597:
598: @Override
599: public void finished(IResultEvent event) {
600: // search is finished
601: // -> enable search button again
602: toggleButtonStates(true);
603:
604: // do layout again
605: layoutComponents(true);
606: }
607:
608: }
609:
610: }
|