86b4a02062ff4da9cf16a2bfa35893fbbedbc4ea
[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 @Override
103 protected boolean isSamePath(TreePath oldPath, TreePath newPath) {
104 String oldString = oldPath.toString();
105 if (oldString.endsWith("/]"))
106 oldString = oldString.substring(0, oldString.length() - 2) + "]";
107
108 String newString = newPath.toString();
109 if (newString.endsWith("/]"))
110 newString = newString.substring(0, newString.length() - 2) + "]";
111
112 return oldString.equals(newString);
113 }
114 };
115 SwingWorker<Map<String, List<String>>, Integer> worker = new SwingWorker<Map<String, List<String>>, Integer>() {
116 @Override
117 protected Map<String, List<String>> doInBackground() throws Exception {
118 fillData(data);
119 return null;
120 }
121
122 @Override
123 protected void done() {
124 try {
125 get();
126 } catch (Exception e) {
127 // TODO: error
128 }
129
130 root.removeAllChildren();
131 totalCount = loadData(root, data, searchBar.getText());
132 ((DefaultTreeModel) tree.getModel()).reload();
133
134 snapshot.apply();
135
136 fireActionPerformed(listenerCommand);
137 }
138 };
139 worker.execute();
140 }
141
142 /**
143 * The currently selected elements, or an empty list.
144 *
145 * @return the sources (cannot be NULL)
146 */
147 public List<String> getSelectedElements() {
148 return selectedElements;
149 }
150
151 public int getTotalCount() {
152 return totalCount;
153 }
154
155 public String getBaseTitle() {
156 return baseTitle;
157 }
158
159 public void setBaseTitle(String baseTitle) {
160 this.baseTitle = baseTitle;
161 }
162
163 public String getTitle() {
164 String title = getBaseTitle();
165 String count = "";
166 if (totalCount > 0) {
167 int selected = selectedElements.size();
168 count = " (" + (selected > 0 ? selected + "/" : "") + totalCount + ")";
169 }
170
171 return title + count;
172 }
173
174 public int getIndex() {
175 return index;
176 }
177
178 public void unselect() {
179 tree.clearSelection();
180 }
181
182 protected boolean checkFilter(String filter, String value) {
183 return (filter == null || filter.isEmpty() || value.toLowerCase().contains(filter.toLowerCase()));
184 }
185
186 protected boolean checkFilter(String filter, List<String> list) {
187 for (String value : list) {
188 if (checkFilter(filter, value))
189 return true;
190 }
191 return false;
192 }
193
194 protected abstract T createEmptyData();
195
196 protected abstract void fillData(T data);
197
198 protected abstract String keyToElement(String key);
199
200 protected abstract String keyToDisplay(String key);
201
202 protected abstract int loadData(DefaultMutableTreeNode root, T data, String filter);
203
204 private TreeCellRenderer generateCellRenderer() {
205 DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer() {
206 @Override
207 public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded,
208 boolean leaf, int row, boolean hasFocus) {
209 if (value instanceof DefaultMutableTreeNode) {
210 if (((DefaultMutableTreeNode) value).getLevel() > 1) {
211 setLeafIcon(null);
212 setLeafIcon(IconGenerator.get(Icon.empty, Size.x4));
213 } else {
214 setLeafIcon(IconGenerator.get(Icon.empty, Size.x16));
215 }
216 }
217
218 String display = value == null ? "" : value.toString();
219 display = keyToDisplay(display);
220
221 return super.getTreeCellRendererComponent(tree, display, selected, expanded, leaf, row, hasFocus);
222 }
223 };
224
225 renderer.setClosedIcon(IconGenerator.get(Icon.arrow_right, Size.x16));
226 renderer.setOpenIcon(IconGenerator.get(Icon.arrow_down, Size.x16));
227 renderer.setLeafIcon(IconGenerator.get(Icon.empty, Size.x16));
228
229 return renderer;
230 }
231 }