fix filter re-add data problem
[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,
57 generateCellRenderer());
58 tree.setCellRenderer(spanner);
59 tree.setRootVisible(false);
60 tree.setShowsRootHandles(false);
61
62 tree.addTreeSelectionListener(new TreeSelectionListener() {
63 @Override
64 public void valueChanged(TreeSelectionEvent e) {
65 List<String> elements = new ArrayList<String>();
66 TreePath[] paths = tree.getSelectionPaths();
67 if (paths != null) {
68 for (TreePath path : paths) {
69 String key = path.getLastPathComponent().toString();
70 elements.add(keyToElement(key));
71 }
72 }
73
74 List<String> selectedElements = new ArrayList<String>();
75 for (String element : elements) {
76 if (!selectedElements.contains(element)) {
77 selectedElements.add(element);
78 }
79 }
80
81 BasicTab.this.selectedElements = selectedElements;
82
83 fireActionPerformed(BasicTab.this.listenerCommand);
84 }
85 });
86
87 add(UiHelper.scroll(tree), BorderLayout.CENTER);
88
89 searchBar = new SearchBar();
90 add(searchBar, BorderLayout.NORTH);
91 searchBar.addActionListener(new ActionListener() {
92 @Override
93 public void actionPerformed(ActionEvent e) {
94 reloadData();
95 }
96 });
97
98 reloadData();
99 }
100
101 public void reloadData() {
102 final TreeSnapshot snapshot = new TreeSnapshot(tree) {
103 @Override
104 protected boolean isSamePath(TreePath oldPath, TreePath newPath) {
105 String oldString = oldPath.toString();
106 if (oldString.endsWith("/]"))
107 oldString = oldString.substring(0, oldString.length() - 2)
108 + "]";
109
110 String newString = newPath.toString();
111 if (newString.endsWith("/]"))
112 newString = newString.substring(0, newString.length() - 2)
113 + "]";
114
115 return oldString.equals(newString);
116 }
117 };
118 SwingWorker<Map<String, List<String>>, Integer> worker = new SwingWorker<Map<String, List<String>>, Integer>() {
119 @Override
120 protected Map<String, List<String>> doInBackground()
121 throws Exception {
122 fillData(data);
123 return null;
124 }
125
126 @Override
127 protected void done() {
128 try {
129 get();
130 } catch (Exception e) {
131 // TODO: error
132 }
133
134 root.removeAllChildren();
135 totalCount = loadData(root, data, searchBar.getText());
136 ((DefaultTreeModel) tree.getModel()).reload();
137
138 snapshot.apply();
139
140 fireActionPerformed(listenerCommand);
141 }
142 };
143 worker.execute();
144 }
145
146 /**
147 * The currently selected elements, or an empty list.
148 *
149 * @return the sources (cannot be NULL)
150 */
151 public List<String> getSelectedElements() {
152 return selectedElements;
153 }
154
155 public int getTotalCount() {
156 return totalCount;
157 }
158
159 public String getBaseTitle() {
160 return baseTitle;
161 }
162
163 public void setBaseTitle(String baseTitle) {
164 this.baseTitle = baseTitle;
165 }
166
167 public String getTitle() {
168 String title = getBaseTitle();
169 String count = "";
170 if (totalCount > 0) {
171 int selected = selectedElements.size();
172 count = " (" + (selected > 0 ? selected + "/" : "") + totalCount
173 + ")";
174 }
175
176 return title + count;
177 }
178
179 public int getIndex() {
180 return index;
181 }
182
183 public void unselect() {
184 tree.clearSelection();
185 }
186
187 protected boolean checkFilter(String filter, String value) {
188 return (filter == null || filter.isEmpty()
189 || value.toLowerCase().contains(filter.toLowerCase()));
190 }
191
192 protected boolean checkFilter(String filter, List<String> list) {
193 for (String value : list) {
194 if (checkFilter(filter, value))
195 return true;
196 }
197 return false;
198 }
199
200 protected abstract T createEmptyData();
201
202 // beware: you should update it OR clean/re-add it, but previous data may
203 // still be there
204 protected abstract void fillData(T data);
205
206 protected abstract String keyToElement(String key);
207
208 protected abstract String keyToDisplay(String key);
209
210 protected abstract int loadData(DefaultMutableTreeNode root, T data,
211 String filter);
212
213 private TreeCellRenderer generateCellRenderer() {
214 DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer() {
215 @Override
216 public Component getTreeCellRendererComponent(JTree tree,
217 Object value, boolean selected, boolean expanded,
218 boolean leaf, int row, boolean hasFocus) {
219 if (value instanceof DefaultMutableTreeNode) {
220 if (((DefaultMutableTreeNode) value).getLevel() > 1) {
221 setLeafIcon(null);
222 setLeafIcon(IconGenerator.get(Icon.empty, Size.x4));
223 } else {
224 setLeafIcon(IconGenerator.get(Icon.empty, Size.x16));
225 }
226 }
227
228 String display = value == null ? "" : value.toString();
229 display = keyToDisplay(display);
230
231 return super.getTreeCellRendererComponent(tree, display,
232 selected, expanded, leaf, row, hasFocus);
233 }
234 };
235
236 renderer.setClosedIcon(IconGenerator.get(Icon.arrow_right, Size.x16));
237 renderer.setOpenIcon(IconGenerator.get(Icon.arrow_down, Size.x16));
238 renderer.setLeafIcon(IconGenerator.get(Icon.empty, Size.x16));
239
240 return renderer;
241 }
242 }