fix tree snapshot for changing sources/authors
[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 // TODO: update is flickering...
131
132 root.removeAllChildren();
133 totalCount = loadData(root, data, searchBar.getText());
134 ((DefaultTreeModel) tree.getModel()).reload();
135
136 snapshot.apply();
137
138 fireActionPerformed(listenerCommand);
139 }
140 };
141 worker.execute();
142 }
143
144 /**
145 * The currently selected elements, or an empty list.
146 *
147 * @return the sources (cannot be NULL)
148 */
149 public List<String> getSelectedElements() {
150 return selectedElements;
151 }
152
153 public int getTotalCount() {
154 return totalCount;
155 }
156
157 public String getBaseTitle() {
158 return baseTitle;
159 }
160
161 public void setBaseTitle(String baseTitle) {
162 this.baseTitle = baseTitle;
163 }
164
165 public String getTitle() {
166 String title = getBaseTitle();
167 String count = "";
168 if (totalCount > 0) {
169 int selected = selectedElements.size();
170 count = " (" + (selected > 0 ? selected + "/" : "") + totalCount + ")";
171 }
172
173 return title + count;
174 }
175
176 public int getIndex() {
177 return index;
178 }
179
180 public void unselect() {
181 tree.clearSelection();
182 }
183
184 protected boolean checkFilter(String filter, String value) {
185 return (filter == null || filter.isEmpty() || value.toLowerCase().contains(filter.toLowerCase()));
186 }
187
188 protected boolean checkFilter(String filter, List<String> list) {
189 for (String value : list) {
190 if (checkFilter(filter, value))
191 return true;
192 }
193 return false;
194 }
195
196 protected abstract T createEmptyData();
197
198 protected abstract void fillData(T data);
199
200 protected abstract String keyToElement(String key);
201
202 protected abstract String keyToDisplay(String key);
203
204 protected abstract int loadData(DefaultMutableTreeNode root, T data, String filter);
205
206 private TreeCellRenderer generateCellRenderer() {
207 DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer() {
208 @Override
209 public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded,
210 boolean leaf, int row, boolean hasFocus) {
211 if (value instanceof DefaultMutableTreeNode) {
212 if (((DefaultMutableTreeNode) value).getLevel() > 1) {
213 setLeafIcon(null);
214 setLeafIcon(IconGenerator.get(Icon.empty, Size.x4));
215 } else {
216 setLeafIcon(IconGenerator.get(Icon.empty, Size.x16));
217 }
218 }
219
220 String display = value == null ? "" : value.toString();
221 display = keyToDisplay(display);
222
223 return super.getTreeCellRendererComponent(tree, display, selected, expanded, leaf, row, hasFocus);
224 }
225 };
226
227 renderer.setClosedIcon(IconGenerator.get(Icon.arrow_right, Size.x16));
228 renderer.setOpenIcon(IconGenerator.get(Icon.arrow_down, Size.x16));
229 renderer.setLeafIcon(IconGenerator.get(Icon.empty, Size.x16));
230
231 return renderer;
232 }
233 }