e9e8edc018f8f4e7880affb2c6c10bc10eaa32a7
[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.JTree;
12 import javax.swing.SwingWorker;
13 import javax.swing.event.TreeSelectionEvent;
14 import javax.swing.event.TreeSelectionListener;
15 import javax.swing.plaf.basic.BasicTreeUI;
16 import javax.swing.tree.DefaultMutableTreeNode;
17 import javax.swing.tree.DefaultTreeCellRenderer;
18 import javax.swing.tree.DefaultTreeModel;
19 import javax.swing.tree.TreeCellRenderer;
20 import javax.swing.tree.TreePath;
21
22 import be.nikiroo.fanfix_swing.gui.SearchBar;
23 import be.nikiroo.fanfix_swing.gui.utils.ListenerPanel;
24 import be.nikiroo.fanfix_swing.gui.utils.TreeCellSpanner;
25 import be.nikiroo.fanfix_swing.gui.utils.TreeSnapshot;
26 import be.nikiroo.fanfix_swing.gui.utils.UiHelper;
27 import be.nikiroo.fanfix_swing.images.IconGenerator;
28 import be.nikiroo.fanfix_swing.images.IconGenerator.Icon;
29 import be.nikiroo.fanfix_swing.images.IconGenerator.Size;
30
31 public abstract class BasicTab<T> extends ListenerPanel {
32 private int totalCount = 0;
33 private List<String> selectedElements = new ArrayList<String>();
34 private T data;
35 private String baseTitle;
36 private String listenerCommand;
37 private int index;
38
39 private JTree tree;
40 private DefaultMutableTreeNode root;
41 private SearchBar searchBar;
42
43 public BasicTab(int index, String listenerCommand) {
44 setLayout(new BorderLayout());
45
46 this.index = index;
47 this.listenerCommand = listenerCommand;
48
49 data = createEmptyData();
50 totalCount = 0;
51
52 root = new DefaultMutableTreeNode();
53
54 tree = new JTree(root);
55 tree.setUI(new BasicTreeUI());
56 TreeCellSpanner spanner = new TreeCellSpanner(tree, generateCellRenderer());
57 tree.setCellRenderer(spanner);
58 tree.setRootVisible(false);
59 tree.setShowsRootHandles(false);
60
61 tree.addTreeSelectionListener(new TreeSelectionListener() {
62 @Override
63 public void valueChanged(TreeSelectionEvent e) {
64 List<String> elements = new ArrayList<String>();
65 TreePath[] paths = tree.getSelectionPaths();
66 if (paths != null) {
67 for (TreePath path : paths) {
68 String key = path.getLastPathComponent().toString();
69 elements.add(keyToElement(key));
70 }
71 }
72
73 List<String> selectedElements = new ArrayList<String>();
74 for (String element : elements) {
75 if (!selectedElements.contains(element)) {
76 selectedElements.add(element);
77 }
78 }
79
80 BasicTab.this.selectedElements = selectedElements;
81
82 fireActionPerformed(BasicTab.this.listenerCommand);
83 }
84 });
85
86 add(UiHelper.scroll(tree), BorderLayout.CENTER);
87
88 searchBar = new SearchBar();
89 add(searchBar, BorderLayout.NORTH);
90 searchBar.addActionListener(new ActionListener() {
91 @Override
92 public void actionPerformed(ActionEvent e) {
93 reloadData();
94 }
95 });
96
97 reloadData();
98 }
99
100 public void reloadData() {
101 final TreeSnapshot snapshot = new TreeSnapshot(tree);
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 fillData(data);
106 return null;
107 }
108
109 @Override
110 protected void done() {
111 try {
112 get();
113 } catch (Exception e) {
114 // TODO: error
115 }
116
117 // TODO: update is flickering...
118
119 root.removeAllChildren();
120 totalCount = loadData(root, data, searchBar.getText());
121 ((DefaultTreeModel) tree.getModel()).reload();
122
123 snapshot.apply();
124
125 fireActionPerformed(listenerCommand);
126 }
127 };
128 worker.execute();
129 }
130
131 /**
132 * The currently selected elements, or an empty list.
133 *
134 * @return the sources (cannot be NULL)
135 */
136 public List<String> getSelectedElements() {
137 return selectedElements;
138 }
139
140 public int getTotalCount() {
141 return totalCount;
142 }
143
144 public String getBaseTitle() {
145 return baseTitle;
146 }
147
148 public void setBaseTitle(String baseTitle) {
149 this.baseTitle = baseTitle;
150 }
151
152 public String getTitle() {
153 String title = getBaseTitle();
154 String count = "";
155 if (totalCount > 0) {
156 int selected = selectedElements.size();
157 count = " (" + (selected > 0 ? selected + "/" : "") + totalCount + ")";
158 }
159
160 return title + count;
161 }
162
163 public int getIndex() {
164 return index;
165 }
166
167 public void unselect() {
168 tree.clearSelection();
169 }
170
171 protected boolean checkFilter(String filter, String value) {
172 return (filter == null || filter.isEmpty() || value.toLowerCase().contains(filter.toLowerCase()));
173 }
174
175 protected boolean checkFilter(String filter, List<String> list) {
176 for (String value : list) {
177 if (checkFilter(filter, value))
178 return true;
179 }
180 return false;
181 }
182
183 protected abstract T createEmptyData();
184
185 protected abstract void fillData(T data);
186
187 protected abstract String keyToElement(String key);
188
189 protected abstract String keyToDisplay(String key);
190
191 protected abstract int loadData(DefaultMutableTreeNode root, T data, String filter);
192
193 private TreeCellRenderer generateCellRenderer() {
194 DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer() {
195 @Override
196 public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded,
197 boolean leaf, int row, boolean hasFocus) {
198 if (value instanceof DefaultMutableTreeNode) {
199 if (((DefaultMutableTreeNode) value).getLevel() > 1) {
200 setLeafIcon(null);
201 setLeafIcon(IconGenerator.get(Icon.empty, Size.x4));
202 } else {
203 setLeafIcon(IconGenerator.get(Icon.empty, Size.x16));
204 }
205 }
206
207 String display = value == null ? "" : value.toString();
208 if (!display.isEmpty())
209 display = keyToDisplay(display);
210
211 return super.getTreeCellRendererComponent(tree, display, selected, expanded, leaf, row, hasFocus);
212 }
213 };
214
215 renderer.setClosedIcon(IconGenerator.get(Icon.arrow_right, Size.x16));
216 renderer.setOpenIcon(IconGenerator.get(Icon.arrow_down, Size.x16));
217 renderer.setLeafIcon(IconGenerator.get(Icon.empty, Size.x16));
218
219 return renderer;
220 }
221 }