d467a91792ea78d83107dcd1952785e9fe73b9b9
[fanfix.git] / src / be / nikiroo / fanfix_swing / gui / browser / BasicTab.java
1 package be.nikiroo.fanfix_swing.gui.browser;
2
3 import java.awt.BorderLayout;
4 import java.awt.Component;
5 import java.awt.event.ActionEvent;
6 import java.awt.event.ActionListener;
7 import java.util.ArrayList;
8 import java.util.List;
9 import java.util.Map;
10
11 import javax.swing.JPanel;
12 import javax.swing.JScrollPane;
13 import javax.swing.JTree;
14 import javax.swing.SwingWorker;
15 import javax.swing.UIDefaults;
16 import javax.swing.event.TreeSelectionEvent;
17 import javax.swing.event.TreeSelectionListener;
18 import javax.swing.plaf.TreeUI;
19 import javax.swing.plaf.basic.BasicTreeUI;
20 import javax.swing.tree.DefaultMutableTreeNode;
21 import javax.swing.tree.DefaultTreeCellRenderer;
22 import javax.swing.tree.DefaultTreeModel;
23 import javax.swing.tree.TreeCellRenderer;
24 import javax.swing.tree.TreePath;
25
26 import be.nikiroo.fanfix.Instance;
27 import be.nikiroo.fanfix_swing.gui.SearchBar;
28 import be.nikiroo.fanfix_swing.gui.utils.TreeCellSpanner;
29 import be.nikiroo.fanfix_swing.gui.utils.UiHelper;
30 import be.nikiroo.fanfix_swing.images.IconGenerator;
31 import be.nikiroo.fanfix_swing.images.IconGenerator.Icon;
32 import be.nikiroo.fanfix_swing.images.IconGenerator.Size;
33
34 public abstract class BasicTab<T> extends JPanel {
35 private int totalCount = 0;
36 private List<String> selectedElements = new ArrayList<String>();
37 private T data;
38 private String baseTitle;
39 private String listenerCommand;
40 private int index;
41
42 private JTree tree;
43 private SearchBar searchBar;
44
45 public BasicTab(int index, String listenerCommand) {
46 setLayout(new BorderLayout());
47
48 this.index = index;
49 this.listenerCommand = listenerCommand;
50
51 data = createEmptyData();
52 totalCount = 0;
53
54 final DefaultMutableTreeNode root = new DefaultMutableTreeNode();
55
56 tree = new JTree(root);
57 tree.setUI(new BasicTreeUI());
58 TreeCellSpanner spanner = new TreeCellSpanner(tree, generateCellRenderer());
59 tree.setCellRenderer(spanner);
60 tree.setRootVisible(false);
61 tree.setShowsRootHandles(false);
62
63 tree.addTreeSelectionListener(new TreeSelectionListener() {
64 @Override
65 public void valueChanged(TreeSelectionEvent e) {
66 List<String> elements = new ArrayList<String>();
67 TreePath[] paths = tree.getSelectionPaths();
68 if (paths != null) {
69 for (TreePath path : paths) {
70 String key = path.getLastPathComponent().toString();
71 elements.add(keyToElement(key));
72 }
73 }
74
75 List<String> selectedElements = new ArrayList<String>();
76 for (String element : elements) {
77 if (!selectedElements.contains(element)) {
78 selectedElements.add(element);
79 }
80 }
81
82 BasicTab.this.selectedElements = selectedElements;
83
84 fireActionPerformed();
85 }
86 });
87
88 add(UiHelper.scroll(tree), BorderLayout.CENTER);
89
90 searchBar = new SearchBar();
91 add(searchBar, BorderLayout.NORTH);
92 searchBar.addActionListener(new ActionListener() {
93 @Override
94 public void actionPerformed(ActionEvent e) {
95 root.removeAllChildren();
96 loadData(root, data, searchBar.getText());
97 ((DefaultTreeModel) tree.getModel()).reload();
98 fireActionPerformed();
99 }
100 });
101
102 SwingWorker<Map<String, List<String>>, Integer> worker = new SwingWorker<Map<String, List<String>>, Integer>() {
103 @Override
104 protected Map<String, List<String>> doInBackground() throws Exception {
105 return Instance.getInstance().getLibrary().getSourcesGrouped();
106 }
107
108 @Override
109 protected void done() {
110 fillData(data);
111 root.removeAllChildren();
112 totalCount = loadData(root, data, searchBar.getText());
113 ((DefaultTreeModel) tree.getModel()).reload();
114
115 fireActionPerformed();
116 }
117 };
118 worker.execute();
119 }
120
121 /**
122 * The currently selected elements, or an empty list.
123 *
124 * @return the sources (cannot be NULL)
125 */
126 public List<String> getSelectedElements() {
127 return selectedElements;
128 }
129
130 public int getTotalCount() {
131 return totalCount;
132 }
133
134 public String getBaseTitle() {
135 return baseTitle;
136 }
137
138 public void setBaseTitle(String baseTitle) {
139 this.baseTitle = baseTitle;
140 }
141
142 public String getTitle() {
143 String title = getBaseTitle();
144 String count = "";
145 if (totalCount > 0) {
146 int selected = selectedElements.size();
147 count = " (" + (selected > 0 ? selected + "/" : "") + totalCount + ")";
148 }
149
150 return title + count;
151 }
152
153 public int getIndex() {
154 return index;
155 }
156
157 public void unselect() {
158 tree.clearSelection();
159 }
160
161 /**
162 * Adds the specified action listener to receive action events from this
163 * {@link SearchBar}.
164 *
165 * @param listener the action listener to be added
166 */
167 public synchronized void addActionListener(ActionListener listener) {
168 listenerList.add(ActionListener.class, listener);
169 }
170
171 /**
172 * Removes the specified action listener so that it no longer receives action
173 * events from this {@link SearchBar}.
174 *
175 * @param listener the action listener to be removed
176 */
177 public synchronized void removeActionListener(ActionListener listener) {
178 listenerList.remove(ActionListener.class, listener);
179 }
180
181 /**
182 * Notify the listeners of an action.
183 */
184 protected void fireActionPerformed() {
185 ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, listenerCommand);
186 Object[] listeners = listenerList.getListenerList();
187 for (int i = listeners.length - 2; i >= 0; i -= 2) {
188 if (listeners[i] == ActionListener.class) {
189 ((ActionListener) listeners[i + 1]).actionPerformed(e);
190 }
191 }
192 }
193
194 protected boolean checkFilter(String filter, String value) {
195 return (filter == null || filter.isEmpty() || value.toLowerCase().contains(filter.toLowerCase()));
196 }
197
198 protected boolean checkFilter(String filter, List<String> list) {
199 for (String value : list) {
200 if (checkFilter(filter, value))
201 return true;
202 }
203 return false;
204 }
205
206 protected abstract T createEmptyData();
207
208 protected abstract void fillData(T data);
209
210 protected abstract String keyToElement(String key);
211
212 protected abstract String keyToDisplay(String key);
213
214 protected abstract int loadData(DefaultMutableTreeNode root, T data, String filter);
215
216 private TreeCellRenderer generateCellRenderer() {
217 DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer() {
218 @Override
219 public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded,
220 boolean leaf, int row, boolean hasFocus) {
221 if (value instanceof DefaultMutableTreeNode) {
222 if (((DefaultMutableTreeNode) value).getLevel() > 1) {
223 setLeafIcon(null);
224 setLeafIcon(IconGenerator.get(Icon.empty, Size.x4));
225 } else {
226 setLeafIcon(IconGenerator.get(Icon.empty, Size.x16));
227 }
228 }
229
230 String display = value == null ? "" : value.toString();
231 if (!display.isEmpty())
232 display = keyToDisplay(display);
233
234 return super.getTreeCellRendererComponent(tree, display, selected, expanded, leaf, row, hasFocus);
235 }
236 };
237
238 renderer.setClosedIcon(IconGenerator.get(Icon.arrow_right, Size.x16));
239 renderer.setOpenIcon(IconGenerator.get(Icon.arrow_down, Size.x16));
240 renderer.setLeafIcon(IconGenerator.get(Icon.empty, Size.x16));
241
242 return renderer;
243 }
244 }