/* * Copyright 2011 Luke Usherwood. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.bettyluke.tracinstant.ui; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Font; import java.awt.Graphics; import java.util.TreeMap; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.DefaultListCellRenderer; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.plaf.basic.BasicComboBoxEditor; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import net.bettyluke.tracinstant.data.SavedSearch; import net.bettyluke.tracinstant.prefs.TracInstantProperties; /** * NB: Most of the customisation is in SearchComboEditor. This class mainly ties a few parts * together. */ public class SearchCombo extends JComboBox<SavedSearch> { private final class DropDownRenderer extends DefaultListCellRenderer { JPanel panel = new JPanel(new BorderLayout()); Box box = Box.createHorizontalBox(); JLabel desc = new JLabel(" "); JLabel alias = new JLabel(" "); JLabel searchText = new JLabel(" "); Font monoFont = new Font(Font.MONOSPACED, Font.BOLD, 12); Font descFont = desc.getFont().deriveFont(desc.getFont().getSize() + 2f); public DropDownRenderer() { desc.setBackground(TRANSPARENT); desc.setFont(descFont); desc.setBorder(BorderFactory.createEmptyBorder(2, 6, 2, 6)); alias.setBackground(TRANSPARENT); alias.setFont(monoFont); searchText.setBackground(TRANSPARENT); searchText.setFont(monoFont); box.add(alias); box.add(searchText); box.add(Box.createGlue()); box.setBorder(BorderFactory.createEmptyBorder(0, 6, 2, 6)); panel.add(desc, BorderLayout.NORTH); panel.add(box, BorderLayout.SOUTH); panel.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.LIGHT_GRAY)); } @Override public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) { SavedSearch ss = (SavedSearch) value; Color fg = isSelected ? list.getSelectionForeground() : list.getForeground(); desc.setText(ss.name.isEmpty() ? "" : ss.name); desc.setForeground(fg); alias.setText(ss.alias); alias.setForeground(isSelected ? fg : Color.BLUE); String searchBase = ss.alias.isEmpty() ? "" : " : "; searchText.setText(searchBase + ss.searchText); searchText.setForeground(isSelected ? fg : Color.GRAY); Color bg = isSelected ? list.getSelectionBackground() : TRANSPARENT; panel.setBackground(bg); panel.setOpaque(isSelected); return panel; } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); } } private static final Color TRANSPARENT = new Color(0, 0, 0, 0); public SearchCombo() { super(new SearchComboBoxModel()); setEditable(true); setEditor(new BasicComboBoxEditor() { @Override protected JTextField createEditorComponent() { return new SearchComboEditor(getModel(), "", 9); } @Override public Component getEditorComponent() { Component editorComponent = super.getEditorComponent(); return editorComponent; } }); SearchComboEditor ed = getEditorComponent(); ed.setFont(new Font(Font.MONOSPACED, Font.BOLD, 14)); setPreferredSize(getPreferredSize()); changeListRenderer(); loadLastSearch(); } protected final String getText() { try { Document doc = getEditorComponent().getDocument(); return doc.getText(0, doc.getLength()); } catch (BadLocationException e) { throw new RuntimeException(e); } } private void changeListRenderer() { setRenderer(new DropDownRenderer()); } @Override public SearchComboBoxModel getModel() { return (SearchComboBoxModel) super.getModel(); } @Override public SavedSearch getItemAt(int index) { return getModel().getElementAt(index); } public String getEditorText() { return getEditorComponent().getText(); } public void setEditorText(String text) { getEditorComponent().setText(text); } public SearchComboEditor getEditorComponent() { return (SearchComboEditor) getEditor().getEditorComponent(); } /** Get the text, with all saved shorthands expanded. */ public String getExpandedText() { TreeMap<String, SavedSearch> map = new TreeMap<>( String.CASE_INSENSITIVE_ORDER); int num = getModel().getSize(); for (int i = 0; i < num; i++) { SavedSearch ss = getModel().getElementAt(i); if (!ss.alias.isEmpty()) { map.put(ss.alias, ss); } } StringBuilder result = new StringBuilder(); String space = ""; // Perhaps this class should just be returning data, rather than processing it, // but it seems a little easier to do this here. final String[] words = getEditorText().split("\\s"); for (String word : words) { result.append(space); space = " "; SavedSearch ss = map.get(word); if (ss == null) { result.append(word); } else { result.append(ss.searchText); } } return result.toString(); } private void loadLastSearch() { setEditorText(TracInstantProperties.get().getValue("CurrentFilter")); } public void saveSearches() { TracInstantProperties.get().putString("CurrentFilter", getEditorText()); getModel().saveSavedSearches(); } }