stories order by name
[fanfix.git] / src / be / nikiroo / fanfix_swing / gui / browser / BasicTab.java
CommitLineData
3cdf3fd8
NR
1package be.nikiroo.fanfix_swing.gui.browser;
2
3import java.awt.BorderLayout;
4import java.awt.Component;
5import java.awt.event.ActionEvent;
6import java.awt.event.ActionListener;
7import java.util.ArrayList;
62c7e07e
NR
8import java.util.Collection;
9import java.util.Collections;
10import java.util.Comparator;
3cdf3fd8
NR
11import java.util.List;
12import java.util.Map;
13
3cdf3fd8
NR
14import javax.swing.JTree;
15import javax.swing.SwingWorker;
3cdf3fd8
NR
16import javax.swing.event.TreeSelectionEvent;
17import javax.swing.event.TreeSelectionListener;
3cdf3fd8
NR
18import javax.swing.plaf.basic.BasicTreeUI;
19import javax.swing.tree.DefaultMutableTreeNode;
20import javax.swing.tree.DefaultTreeCellRenderer;
21import javax.swing.tree.DefaultTreeModel;
22import javax.swing.tree.TreeCellRenderer;
23import javax.swing.tree.TreePath;
24
3cdf3fd8 25import be.nikiroo.fanfix_swing.gui.SearchBar;
2a03ecc0 26import be.nikiroo.fanfix_swing.gui.utils.ListenerPanel;
3cdf3fd8 27import be.nikiroo.fanfix_swing.gui.utils.TreeCellSpanner;
2a03ecc0 28import be.nikiroo.fanfix_swing.gui.utils.TreeSnapshot;
3cdf3fd8
NR
29import be.nikiroo.fanfix_swing.gui.utils.UiHelper;
30import be.nikiroo.fanfix_swing.images.IconGenerator;
31import be.nikiroo.fanfix_swing.images.IconGenerator.Icon;
32import be.nikiroo.fanfix_swing.images.IconGenerator.Size;
33
2a03ecc0 34public abstract class BasicTab<T> extends ListenerPanel {
3cdf3fd8
NR
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;
2a03ecc0 43 private DefaultMutableTreeNode root;
3cdf3fd8
NR
44 private SearchBar searchBar;
45
46 public BasicTab(int index, String listenerCommand) {
47 setLayout(new BorderLayout());
48
49 this.index = index;
50 this.listenerCommand = listenerCommand;
51
52 data = createEmptyData();
53 totalCount = 0;
54
2a03ecc0 55 root = new DefaultMutableTreeNode();
3cdf3fd8
NR
56
57 tree = new JTree(root);
58 tree.setUI(new BasicTreeUI());
32ed6089
NR
59 TreeCellSpanner spanner = new TreeCellSpanner(tree,
60 generateCellRenderer());
3cdf3fd8
NR
61 tree.setCellRenderer(spanner);
62 tree.setRootVisible(false);
63 tree.setShowsRootHandles(false);
64
65 tree.addTreeSelectionListener(new TreeSelectionListener() {
66 @Override
67 public void valueChanged(TreeSelectionEvent e) {
68 List<String> elements = new ArrayList<String>();
69 TreePath[] paths = tree.getSelectionPaths();
70 if (paths != null) {
71 for (TreePath path : paths) {
72 String key = path.getLastPathComponent().toString();
73 elements.add(keyToElement(key));
74 }
75 }
76
77 List<String> selectedElements = new ArrayList<String>();
78 for (String element : elements) {
79 if (!selectedElements.contains(element)) {
80 selectedElements.add(element);
81 }
82 }
83
84 BasicTab.this.selectedElements = selectedElements;
85
2a03ecc0 86 fireActionPerformed(BasicTab.this.listenerCommand);
3cdf3fd8
NR
87 }
88 });
89
90 add(UiHelper.scroll(tree), BorderLayout.CENTER);
91
92 searchBar = new SearchBar();
93 add(searchBar, BorderLayout.NORTH);
94 searchBar.addActionListener(new ActionListener() {
95 @Override
96 public void actionPerformed(ActionEvent e) {
2a03ecc0 97 reloadData();
3cdf3fd8
NR
98 }
99 });
100
2a03ecc0
NR
101 reloadData();
102 }
103
104 public void reloadData() {
77e5ecd4
NR
105 final TreeSnapshot snapshot = new TreeSnapshot(tree) {
106 @Override
107 protected boolean isSamePath(TreePath oldPath, TreePath newPath) {
108 String oldString = oldPath.toString();
109 if (oldString.endsWith("/]"))
32ed6089
NR
110 oldString = oldString.substring(0, oldString.length() - 2)
111 + "]";
77e5ecd4
NR
112
113 String newString = newPath.toString();
114 if (newString.endsWith("/]"))
32ed6089
NR
115 newString = newString.substring(0, newString.length() - 2)
116 + "]";
77e5ecd4
NR
117
118 return oldString.equals(newString);
119 }
120 };
3cdf3fd8
NR
121 SwingWorker<Map<String, List<String>>, Integer> worker = new SwingWorker<Map<String, List<String>>, Integer>() {
122 @Override
32ed6089
NR
123 protected Map<String, List<String>> doInBackground()
124 throws Exception {
2a03ecc0
NR
125 fillData(data);
126 return null;
3cdf3fd8
NR
127 }
128
129 @Override
130 protected void done() {
2a03ecc0
NR
131 try {
132 get();
133 } catch (Exception e) {
134 // TODO: error
135 }
136
3cdf3fd8
NR
137 root.removeAllChildren();
138 totalCount = loadData(root, data, searchBar.getText());
139 ((DefaultTreeModel) tree.getModel()).reload();
140
2a03ecc0
NR
141 snapshot.apply();
142
143 fireActionPerformed(listenerCommand);
3cdf3fd8
NR
144 }
145 };
146 worker.execute();
147 }
148
149 /**
150 * The currently selected elements, or an empty list.
151 *
152 * @return the sources (cannot be NULL)
153 */
154 public List<String> getSelectedElements() {
155 return selectedElements;
156 }
157
158 public int getTotalCount() {
159 return totalCount;
160 }
161
162 public String getBaseTitle() {
163 return baseTitle;
164 }
165
166 public void setBaseTitle(String baseTitle) {
167 this.baseTitle = baseTitle;
168 }
169
170 public String getTitle() {
171 String title = getBaseTitle();
172 String count = "";
173 if (totalCount > 0) {
174 int selected = selectedElements.size();
32ed6089
NR
175 count = " (" + (selected > 0 ? selected + "/" : "") + totalCount
176 + ")";
3cdf3fd8
NR
177 }
178
179 return title + count;
180 }
181
182 public int getIndex() {
183 return index;
184 }
185
186 public void unselect() {
187 tree.clearSelection();
188 }
189
3cdf3fd8 190 protected boolean checkFilter(String filter, String value) {
32ed6089
NR
191 return (filter == null || filter.isEmpty()
192 || value.toLowerCase().contains(filter.toLowerCase()));
3cdf3fd8
NR
193 }
194
195 protected boolean checkFilter(String filter, List<String> list) {
196 for (String value : list) {
197 if (checkFilter(filter, value))
198 return true;
199 }
200 return false;
201 }
202
203 protected abstract T createEmptyData();
204
b63e7e0b
NR
205 // beware: you should update it OR clean/re-add it, but previous data may
206 // still be there
3cdf3fd8
NR
207 protected abstract void fillData(T data);
208
209 protected abstract String keyToElement(String key);
210
211 protected abstract String keyToDisplay(String key);
212
32ed6089
NR
213 protected abstract int loadData(DefaultMutableTreeNode root, T data,
214 String filter);
3cdf3fd8 215
62c7e07e
NR
216 protected void sort(List<String> values) {
217 Collections.sort(values, new Comparator<String>() {
218 @Override
219 public int compare(String o1, String o2) {
220 return ("" + o1).compareToIgnoreCase("" + o2);
221 }
222 });
223 }
224
3cdf3fd8
NR
225 private TreeCellRenderer generateCellRenderer() {
226 DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer() {
227 @Override
32ed6089
NR
228 public Component getTreeCellRendererComponent(JTree tree,
229 Object value, boolean selected, boolean expanded,
3cdf3fd8
NR
230 boolean leaf, int row, boolean hasFocus) {
231 if (value instanceof DefaultMutableTreeNode) {
232 if (((DefaultMutableTreeNode) value).getLevel() > 1) {
233 setLeafIcon(null);
234 setLeafIcon(IconGenerator.get(Icon.empty, Size.x4));
235 } else {
236 setLeafIcon(IconGenerator.get(Icon.empty, Size.x16));
237 }
238 }
239
240 String display = value == null ? "" : value.toString();
77e5ecd4 241 display = keyToDisplay(display);
3cdf3fd8 242
32ed6089
NR
243 return super.getTreeCellRendererComponent(tree, display,
244 selected, expanded, leaf, row, hasFocus);
3cdf3fd8
NR
245 }
246 };
247
248 renderer.setClosedIcon(IconGenerator.get(Icon.arrow_right, Size.x16));
249 renderer.setOpenIcon(IconGenerator.get(Icon.arrow_down, Size.x16));
250 renderer.setLeafIcon(IconGenerator.get(Icon.empty, Size.x16));
251
252 return renderer;
253 }
254}