1 package net.sf.bibkeeper.groups;
2
3 import javax.swing;
4 import javax.swing.event.ListSelectionListener;
5 import javax.swing.event.ListSelectionEvent;
6 import java.awt;
7 import java.awt.event;
8 import net.sf.bibkeeper;
9 import java.util.Vector;
10 import java.util.Hashtable;
11
12 public class GroupSelector extends SidePaneComponent
13 implements ListSelectionListener, ActionListener {
14
15 public static final int
16 DIM = 3, // The number of vector elements for each group.
17 OFFSET = 0; // The number of vector elements before first group.
18
19
20 JButton newButton = new JButton
21 (new ImageIcon(GUIGlobals.newSmallIconFile)),
22 helpButton = new JButton
23 (new ImageIcon(GUIGlobals.helpSmallIconFile)),
24 refresh = new JButton
25 (new ImageIcon(GUIGlobals.refreshSmallIconFile));
26
27 Color bgColor = Color.white;
28 JList list;
29 ListModel listModel;
30 JScrollPane sp;
31 GridBagLayout gbl = new GridBagLayout();
32 GridBagConstraints con = new GridBagConstraints();
33 Vector groups;
34 BibtexBaseFrame baseFrame;
35 String searchField;
36 JPopupMenu gropt = new JPopupMenu();
37 JCheckBox andCb = new JCheckBox("Intersection", true),
38 orCb = new JCheckBox("Union", false);
39 ButtonGroup bgr = new ButtonGroup();
40
41 SidePaneManager manager;
42 BibkeeperPrefs prefs;
43 GroupSelector ths;
44
45
46 /**
47 * The first element for each group defines which field to
48 * use for the quicksearch. The next two define the name and
49 * regexp for the group.
50 */
51 public GroupSelector(Vector groupData, BibtexBaseFrame baseFrame,
52 SidePaneManager manager, BibkeeperPrefs prefs) {
53 super(manager);
54 ths = this;
55 this.prefs = prefs;
56 groups = groupData;
57 this.manager = manager;
58 this.baseFrame = baseFrame;
59 double n = (double)(groups.size() - OFFSET);
60 while ((n>0) && (n/DIM != Math.floor(n/DIM))) {
61 groups.removeElementAt(groups.size()-1);
62 n = (double)(groups.size() - OFFSET);
63 // If the number of elements is not divisible by DIM, we're
64 // in trouble, so we must remove one or two elements.
65 }
66
67 Dimension butDim = new Dimension(24, 24);
68 newButton.setPreferredSize(butDim);
69 newButton.setMinimumSize(butDim);
70 refresh.setPreferredSize(butDim);
71 refresh.setMinimumSize(butDim);
72 helpButton.setPreferredSize(butDim);
73 helpButton.setMinimumSize(butDim);
74
75 newButton.addActionListener(this);
76 refresh.addActionListener(this);
77 andCb.addActionListener(this);
78 orCb.addActionListener(this);
79 newButton.setToolTipText("New group");
80 newButton.setToolTipText("Refresh view");
81 andCb.setToolTipText("Display only entries belonging to all selected"
82 +" groups.");
83 orCb.setToolTipText("Display all entries belonging to one or more "
84 +"of the selected groups.");
85 bgr.add(andCb);
86 bgr.add(orCb);
87
88 setLayout(gbl);
89
90 SidePaneHeader header = new SidePaneHeader
91 ("Groups", GUIGlobals.groupsIconFile, this);
92 con.gridwidth = GridBagConstraints.REMAINDER;
93 con.fill = GridBagConstraints.BOTH;
94 gbl.setConstraints(header, con);
95 add(header);
96 con.gridwidth = 1;
97 con.weightx = 1;
98 gbl.setConstraints(newButton, con);
99 add(newButton);
100 gbl.setConstraints(refresh, con);
101 add(refresh);
102
103 con.gridwidth = GridBagConstraints.REMAINDER;
104 HelpAction helpAction = new HelpAction(baseFrame.helpDiag,
105 GUIGlobals.groupsHelp,
106 "Help on groups");
107 helpButton.addActionListener(helpAction);
108 helpButton.setToolTipText("Help on groups");
109 gbl.setConstraints(helpButton, con);
110 add(helpButton);
111
112 list = new JList();
113 revalidateList();
114 //list.setSelectedIndex(0);
115 list.addListSelectionListener(this);
116 list.setPrototypeCellValue("Suitable length");
117 // The line above decides on the list's preferred width.
118 list.setVisibleRowCount(GUIGlobals.GROUPS_VISIBLE_ROWS);
119 list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
120 sp = new JScrollPane
121 (list, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
122 JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
123 con.gridwidth = GridBagConstraints.REMAINDER;
124 con.weighty = 1;
125 gbl.setConstraints(sp, con);
126 add(sp);
127
128 JPanel lower = new JPanel();
129 lower.setLayout(gbl);
130 lower.setBorder(BorderFactory.createEtchedBorder());
131 con.weighty = 0;
132 con.anchor = GridBagConstraints.WEST;
133 gbl.setConstraints(andCb, con);
134 lower.add(andCb);
135 gbl.setConstraints(orCb, con);
136 lower.add(orCb);
137 gbl.setConstraints(lower, con);
138 add(lower);
139 definePopup();
140 }
141
142 public void definePopup() {
143
144 gropt.add(modifyAction);
145
146 gropt.add(new AbstractAction("Remove") {
147 public void actionPerformed(ActionEvent e) {
148 String gname = (String)groups.elementAt
149 (OFFSET+DIM*(list.getSelectedIndex()-1)+1);
150 int conf = JOptionPane.showConfirmDialog
151 (baseFrame, "Remove group '"+gname+"'?",
152 "Remove group", JOptionPane.YES_NO_OPTION);
153 if (conf == JOptionPane.YES_OPTION) {
154 try {
155
156 int index = OFFSET+DIM*(list.getSelectedIndex()-1);
157 String field = (String)groups.elementAt(index),
158 name = (String)groups.elementAt(index+1),
159 regexp = (String)groups.elementAt(index+2);
160 for (int i=0; i<DIM; i++)
161 groups.removeElementAt(index);
162
163 revalidateList();
164
165 // Store undo information.
166 baseFrame.undoManager.addEdit
167 (new UndoableAddOrRemoveGroup
168 (ths, groups, index, false,
169 field, name, regexp));
170 baseFrame.markBaseChanged();
171 baseFrame.output("Removed group '"
172 +name+"'.");
173 } catch (ArrayIndexOutOfBoundsException ex) {
174 System.err.println("Unexpected error when "
175 +"trying to remove group.");
176 }
177 }
178 }
179 });
180
181 list.addMouseListener(new MouseAdapter() {
182 public void mousePressed(MouseEvent e) {
183 int index = list.locationToIndex(e.getPoint());
184 if (index == 0)
185 list.setSelectedIndex(0);
186 }
187 public void mouseClicked(MouseEvent e) {
188 int index = list.locationToIndex(e.getPoint());
189 if (e.getButton() == MouseEvent.BUTTON3) {
190 if ((index > 0) // Menu for index 0 also?
191 && (index < list.getModel().getSize())) {
192 list.setSelectedIndex(index);
193 gropt.show(list, e.getPoint().x,
194 list.indexToLocation(index).y);
195 }
196 } else {
197 if ((index > 0) && (e.getClickCount() == 2)) {
198 //list.setSelectedIndex(index);
199 modifyAction.actionPerformed
200 (new ActionEvent(list, 0, "Modify"));
201 }
202 }
203 }
204 });
205
206 }
207
208 public void valueChanged(ListSelectionEvent e) {
209 if (!e.getValueIsAdjusting() && !list.isSelectionEmpty()) {
210 int[] sel = list.getSelectedIndices();
211 if ((sel.length == 1) && (sel[0] == 0)) {
212 // Show all entries.
213 baseFrame.stopShowingGroup();
214 baseFrame.output("Displaying no group.");
215 } else {
216 // Show a search for the keyword of field sel*DIM+OFFSET-1
217 // in the vector. The following is analogue with
218 // the doSearch() method of SearchPane.
219
220 // We use a search rule set that takes care of multiple groups,
221 // in an AND or OR fashion.
222 AndOrSearchRuleSet searchRules =
223 new AndOrSearchRuleSet(andCb.isSelected());
224 for (int i=0; i<sel.length; i++) if (sel[i] > 0) {
225 SearchRule rule = new QuickSearchRule
226 ((String)groups.elementAt(sel[i]*DIM+OFFSET-3),
227 (String)groups.elementAt(sel[i]*DIM+OFFSET-1));
228 searchRules.addRule(rule) ;
229 }
230 Hashtable searchOptions = new Hashtable();
231 searchOptions.put("option", "dummy");
232 DatabaseSearch search = new DatabaseSearch
233 (searchOptions, searchRules, baseFrame,
234 DatabaseSearch.GROUPSEARCH);
235 search.start();
236 baseFrame.output("Updated group selection.");
237 //groups.elementAt(sel*DIM+OFFSET-DIM+1)+"'.");
238
239 }
240
241 //net.sf.bibkeeper.Util.pr("sk: "+list.getSelectedIndex());
242 }
243 }
244
245 public void revalidateList() {
246 revalidateList(0);
247 }
248
249 public void revalidateList(int sel) {
250 Vector newData = new Vector();
251 newData.add(GUIGlobals.SHOW_ALL);
252 for (int i=1+OFFSET; i<groups.size(); i+=DIM) {
253 newData.add(groups.elementAt(i));
254 }
255 list.clearSelection();
256 list.setListData(newData);
257 list.setSelectedIndex(sel);
258 }
259
260 public void actionPerformed(ActionEvent e) {
261 if (e.getSource() == refresh) {
262 valueChanged(new ListSelectionEvent(list, 1, 2, false));
263 }
264 if (e.getSource() == newButton) {
265
266 GroupDialog gd = new GroupDialog(baseFrame,
267 groups, -1, //groups.size(),
268 prefs.get("groupsDefaultField"));
269 gd.show();
270 if (gd.okPressed()) {
271 revalidateList((gd.index()-OFFSET)/DIM +1);
272 /*sp.getVerticalScrollBar().setValue
273 (sp.getVerticalScrollBar().getMaximum());*/
274 int index = gd.index();
275 baseFrame.undoManager.addEdit(new UndoableAddOrRemoveGroup
276 (this, groups, index, true,
277 (String)groups.elementAt(index),
278 (String)groups.elementAt(index+1),
279 (String)groups.elementAt(index+2)));
280 baseFrame.markBaseChanged();
281 baseFrame.output("Created group '"
282 +gd.name()+"'.");
283
284 }
285
286 }
287 else if (e.getSource() instanceof JCheckBox) {
288 valueChanged(new ListSelectionEvent(list, 1, 2, false));
289 }
290 }
291
292 public void componentOpening() {
293 valueChanged(new ListSelectionEvent(list, list.getSelectedIndex(),
294 list.getSelectedIndex(), false));
295 }
296
297 public void componentClosing() {
298 baseFrame.stopShowingGroup();
299 }
300
301 AbstractAction modifyAction = new AbstractAction("Modify") {
302 public void actionPerformed(ActionEvent e) {
303 int index = OFFSET+DIM*(list.getSelectedIndex()-1),
304 groupIndex = list.getSelectedIndex();
305 GroupDialog gd = new GroupDialog
306 (baseFrame, groups, index,
307 prefs.get("groupsDefaultField"));
308 gd.show();
309 if (gd.okPressed()) {
310 revalidateList((gd.index()-OFFSET)/DIM +1);
311
312 // Store undo information.
313 baseFrame.undoManager.addEdit
314 (new UndoableModifyGroup
315 (ths, groups, gd.index(),
316 gd.field(), gd.name(), gd.regexp(),
317 gd.oldField(), gd.oldName(), gd.oldRegexp()));
318
319 baseFrame.markBaseChanged();
320 baseFrame.output("Modified group '"+gd.name()+"'.");
321 }
322 }
323 };
324
325 public static int findPos(Vector groups, String name) {
326 int index = -1;
327 for (int i=OFFSET; i<groups.size(); i+=DIM) {
328 if (name.toLowerCase().compareTo
329 (((String)groups.elementAt(i+1)).toLowerCase()) < 0) {
330 index = i;
331 i = groups.size();
332 }
333 }
334 if (index == -1)
335 index = groups.size();
336 return index;
337 }
338 }