1 package net.sf.bibkeeper;
2
3 import java.awt.event;
4 import java.awt;
5 import javax.swing;
6 import javax.swing.table;
7 import java.util;
8 import net.sf.bibkeeper.undo;
9 import javax.swing.undo.CompoundEdit;
10
11 public class StringDialog extends JDialog {
12
13 // A reference to the entry this object works on.
14 BibtexDatabase base;
15 BibtexBaseFrame baseFrame;
16 BibkeeperPrefs prefs;
17
18 // Layout objects.
19 GridBagLayout gbl = new GridBagLayout();
20 GridBagConstraints con = new GridBagConstraints();
21 JLabel lab;
22 Container conPane = getContentPane();
23 JToolBar tlb = new JToolBar();
24 JPanel pan = new JPanel();
25 StringTable table;
26 HelpAction helpAction;
27
28 public StringDialog(BibtexBaseFrame baseFrame, BibtexDatabase base, BibkeeperPrefs prefs) {
29 super(baseFrame);
30 this.baseFrame = baseFrame;
31 this.base = base;
32 this.prefs = prefs;
33
34 helpAction = new HelpAction
35 (baseFrame.helpDiag, GUIGlobals.stringEditorHelp, "Help");
36
37
38 addWindowListener(new WindowAdapter() {
39 public void windowClosing(WindowEvent e) {
40 closeAction.actionPerformed(null);
41 }
42 });
43
44 // We replace the default FocusTraversalPolicy with a subclass
45 // that only allows the StringTable to gain keyboard focus.
46 setFocusTraversalPolicy(new LayoutFocusTraversalPolicy() {
47 protected boolean accept(Component c) {
48 return (super.accept(c) && (c instanceof StringTable));
49 }
50 });
51
52 setLocation(prefs.getInt("stringsPosX"), prefs.getInt("stringsPosY"));
53 setSize(prefs.getInt("stringsSizeX"), prefs.getInt("stringsSizeY"));
54
55 pan.setLayout(gbl);
56 con.fill = GridBagConstraints.BOTH;
57 con.weighty = 1;
58 con.weightx = 1;
59 table = new StringTable(this, base);
60 if (base.getStringCount() > 0)
61 table.setRowSelectionInterval(0,0);
62
63 gbl.setConstraints(table.getPane(), con);
64 pan.add(table.getPane());
65
66 InputMap im = tlb.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
67 ActionMap am = tlb.getActionMap();
68 im.put(KeyStroke.getKeyStroke(GUIGlobals.addKey), "add");
69 am.put("add", newStringAction);
70 im.put(KeyStroke.getKeyStroke(GUIGlobals.removeKey), "remove");
71 am.put("remove", removeStringAction);
72 im.put(KeyStroke.getKeyStroke(GUIGlobals.upKey), "up");
73 am.put("up", stringUpAction);
74 im.put(KeyStroke.getKeyStroke(GUIGlobals.downKey), "down");
75 am.put("down", stringDownAction);
76 im.put(GUIGlobals.exitDialog, "close");
77 am.put("close", closeAction);
78 im.put(GUIGlobals.helpKeyStroke, "help");
79 am.put("help", helpAction);
80 im.put(GUIGlobals.undoStroke, "undo");
81 am.put("undo", undoAction);
82 im.put(GUIGlobals.redoStroke, "redo");
83 am.put("redo", redoAction);
84
85 //tlb.add(closeAction);
86 //tlb.addSeparator();
87 tlb.add(newStringAction);
88 tlb.add(removeStringAction);
89 tlb.addSeparator();
90 tlb.add(stringUpAction);
91 tlb.add(stringDownAction);
92 tlb.addSeparator();
93 tlb.add(helpAction);
94 conPane.add(tlb, BorderLayout.NORTH);
95 conPane.add(pan, BorderLayout.CENTER);
96
97 if (baseFrame.file != null)
98 setTitle(GUIGlobals.stringsTitle+baseFrame.file.getName());
99 else
100 setTitle(GUIGlobals.untitledStringsTitle);
101
102 }
103
104 public void refreshTable() {
105 table.revalidate();
106 table.repaint();
107 }
108
109 class StringTable extends JTable {
110 JScrollPane sp = new JScrollPane((JTable)this);
111 public StringTable(StringDialog parent, BibtexDatabase base) {
112 super(new StringTableModel(parent, base));
113 setShowVerticalLines(true);
114 setShowHorizontalLines(true);
115 setColumnSelectionAllowed(true);
116 DefaultCellEditor dce = new DefaultCellEditor(new JTextField());
117 dce.setClickCountToStart(2);
118 setDefaultEditor(String.class, dce);
119 TableColumnModel cm = getColumnModel();
120 cm.getColumn(0).setPreferredWidth(800);
121 cm.getColumn(1).setPreferredWidth(2000);
122 sp.getViewport().setBackground(GUIGlobals.tableBackground);
123 // getInputMap().remove(GUIGlobals.exitDialog);
124 getInputMap().put(GUIGlobals.exitDialog, "close");
125 getActionMap().put("close", closeAction);
126 getInputMap().put(GUIGlobals.helpKeyStroke, "help");
127 getActionMap().put("help", helpAction);
128
129 }
130
131 public JComponent getPane() {
132 return sp;
133 }
134
135 }
136
137 class StringTableModel extends AbstractTableModel {
138
139 BibtexDatabase base;
140 StringDialog parent;
141
142 public StringTableModel(StringDialog parent, BibtexDatabase base) {
143 this.parent = parent;
144 this.base = base;
145 }
146
147 public Object getValueAt(int row, int col) {
148 return ((col == 0) ?
149 base.getString(row).getName() :
150 base.getString(row).getContent());
151 }
152
153 public void setValueAt(Object value, int row, int col) {
154 // if (row >= base.getStringCount())
155 // return; // After a Remove operation the program somehow
156 // thinks the user is still editing an entry,
157 // which might now be outside
158 if (col == 0) {
159 // Change name of string.
160 if (!((String)value).equals(base.getString(row).getName())) {
161 if (base.hasStringLabel((String)value))
162 JOptionPane.showMessageDialog(parent,
163 "A string with that label "
164 +"already exists",
165 "Label",
166 JOptionPane.ERROR_MESSAGE);
167 else if ((value != null) && isNumber((String)value)) {
168 JOptionPane.showMessageDialog
169 (parent,
170 "The label of the string can not be a number.",
171 "Label",
172 JOptionPane.ERROR_MESSAGE);
173 }
174 else {
175 // Store undo information.
176 baseFrame.undoManager.addEdit
177 (new UndoableStringChange
178 (baseFrame, base.getString(row), true,
179 base.getString(row).getName(), (String)value));
180 base.getString(row).setName((String)value);
181 baseFrame.markBaseChanged();
182 }
183 }
184 } else {
185 // Change content of string.
186 if (!((String)value).equals(base.getString(row).getContent())) {
187 // Store undo information.
188 baseFrame.undoManager.addEdit
189 (new UndoableStringChange
190 (baseFrame, base.getString(row), false,
191 base.getString(row).getContent(), (String)value));
192
193 base.getString(row).setContent((String)value);
194 baseFrame.markBaseChanged();
195 }
196 }
197 }
198
199 public int getColumnCount() {
200 return 2;
201 }
202
203 public int getRowCount() {
204 return base.getStringCount();
205 }
206
207 public String getColumnName(int col) {
208 return ((col == 0) ?
209 "Name" : "Content");
210 }
211
212 public boolean isCellEditable(int row, int col) {
213 return true;
214 }
215 }
216
217 protected boolean isNumber(String name) {
218 // A pure integer number can not be used as a string label,
219 // since Bibtex will read it as a number.
220 try {
221 Integer.parseInt(name);
222 return true;
223 } catch (NumberFormatException ex) {
224 return false;
225 }
226
227 }
228
229 protected void assureNotEditing() {
230 if (table.isEditing()) {
231 int col = table.getEditingColumn(),
232 row = table.getEditingRow();
233 table.getCellEditor(row, col).stopCellEditing();
234 }
235 }
236
237 // The action concerned with closing the window.
238 CloseAction closeAction = new CloseAction(this);
239 class CloseAction extends AbstractAction {
240 StringDialog parent;
241 public CloseAction(StringDialog parent) {
242 super("Close window",
243 new ImageIcon(GUIGlobals.closeIconFile));
244 putValue(SHORT_DESCRIPTION, "Close window (Ctrl-Q)");
245 this.parent = parent;
246 }
247 public void actionPerformed(ActionEvent e) {
248 baseFrame.stringsClosing();
249 dispose();
250 Point p = getLocation();
251 Dimension d = getSize();
252 prefs.putInt("stringsPosX", p.x);
253 prefs.putInt("stringsPosY", p.y);
254 prefs.putInt("stringsSizeX", d.width);
255 prefs.putInt("stringsSizeY", d.height);
256 }
257 }
258
259 NewStringAction newStringAction = new NewStringAction(this);
260 class NewStringAction extends AbstractAction {
261 StringDialog parent;
262 public NewStringAction(StringDialog parent) {
263 super("New string",
264 new ImageIcon(GUIGlobals.addIconFile));
265 putValue(SHORT_DESCRIPTION, "New string (Ctrl-N)");
266 this.parent = parent;
267 }
268 public void actionPerformed(ActionEvent e) {
269 String name =
270 JOptionPane.showInputDialog(parent,
271 "Please enter the string's label");
272 if (name == null)
273 return;
274 if (isNumber(name)) {
275 JOptionPane.showMessageDialog
276 (parent,
277 "The label of the string can not be a number.",
278 "Label",
279 JOptionPane.ERROR_MESSAGE);
280 return;
281 }
282
283 try {
284 BibtexString bs = new BibtexString(name, "");
285
286 // Store undo information:
287 baseFrame.undoManager.addEdit
288 (new UndoableInsertString
289 (baseFrame, base, bs, base.getStringCount()));
290
291 base.addString(bs, base.getStringCount());
292 table.revalidate();
293 baseFrame.markBaseChanged();
294 } catch (KeyCollisionException ex) {
295 JOptionPane.showMessageDialog(parent,
296 "A string with that label "
297 +"already exists",
298 "Label",
299 JOptionPane.ERROR_MESSAGE);
300 }
301 }
302 }
303
304 StoreContentAction storeContentAction = new StoreContentAction(this);
305 class StoreContentAction extends AbstractAction {
306 StringDialog parent;
307 public StoreContentAction(StringDialog parent) {
308 super("Store string",
309 new ImageIcon(GUIGlobals.addIconFile));
310 putValue(SHORT_DESCRIPTION, "Store string (Ctrl-S)");
311 this.parent = parent;
312 }
313 public void actionPerformed(ActionEvent e) {
314 }
315 }
316
317 RemoveStringAction removeStringAction = new RemoveStringAction(this);
318 class RemoveStringAction extends AbstractAction {
319 StringDialog parent;
320 public RemoveStringAction(StringDialog parent) {
321 super("Remove selected strings",
322 new ImageIcon(GUIGlobals.removeIconFile));
323 putValue(SHORT_DESCRIPTION, "Remove selected strings (Shift-DEL)");
324 this.parent = parent;
325 }
326 public void actionPerformed(ActionEvent e) {
327 int[] sel = table.getSelectedRows();
328 if (sel.length > 0) {
329
330 // Make sure no cell is being edited, as caused by the
331 // keystroke. This makes the content hang on the screen.
332 assureNotEditing();
333
334 String msg = "Really delete the selected "+
335 ((sel.length>1) ? sel.length+" entries?" : "entry?");
336 int answer = JOptionPane.showConfirmDialog(parent, msg, "Delete strings",
337 JOptionPane.YES_NO_OPTION,
338 JOptionPane.QUESTION_MESSAGE);
339 if (answer == JOptionPane.YES_OPTION) {
340 CompoundEdit ce = new CompoundEdit();
341 for (int i=sel.length-1; i>=0; i--) {
342 // Delete the strings backwards to avoid moving indexes.
343
344 // Store undo information:
345 ce.addEdit(new UndoableRemoveString
346 (baseFrame, base,
347 base.getString(sel[i]), sel[i]));
348
349 base.removeString(sel[i]);
350 }
351 ce.end();
352 baseFrame.undoManager.addEdit(ce);
353
354 table.revalidate();
355 if (base.getStringCount() > 0)
356 table.setRowSelectionInterval(0,0);
357 table.repaint();
358 baseFrame.markBaseChanged();
359 }
360 }
361 }
362 }
363
364 StringUpAction stringUpAction = new StringUpAction();
365 class StringUpAction extends AbstractAction {
366 public StringUpAction() {
367 super("Move string upwards",
368 new ImageIcon(GUIGlobals.upIconFile));
369 putValue(SHORT_DESCRIPTION, "Move string upwards (Ctrl-Up)");
370 }
371 public void actionPerformed(ActionEvent e) {
372 int[] sel = table.getSelectedRows();
373 if ((sel.length == 1) && (sel[0] > 0)) {
374
375 // Make sure no cell is being edited, as caused by the
376 // keystroke. This makes the content hang on the screen.
377 assureNotEditing();
378 /* int col = table.getEditingColumn(),
379 row = table.getEditingRow();
380 table.getCellEditor(row, col).stopCellEditing();*/
381
382 // Store undo information:
383 baseFrame.undoManager.addEdit(new UndoableMoveString
384 (baseFrame, base, sel[0], true));
385
386 BibtexString bs = base.getString(sel[0]);
387 base.removeString(sel[0]);
388 try {
389 base.addString(bs, sel[0]-1);
390 } catch (KeyCollisionException ex) {}
391 table.revalidate();
392 table.setRowSelectionInterval(sel[0]-1, sel[0]-1);
393 table.repaint();
394 baseFrame.markBaseChanged();
395 }
396 }
397 }
398
399 StringDownAction stringDownAction = new StringDownAction();
400 class StringDownAction extends AbstractAction {
401 public StringDownAction() {
402 super("Move string downwards",
403 new ImageIcon(GUIGlobals.downIconFile));
404 putValue(SHORT_DESCRIPTION, "Move string downwards (Ctrl-Down)");
405 }
406 public void actionPerformed(ActionEvent e) {
407 int[] sel = table.getSelectedRows();
408 if ((sel.length == 1) && (sel[0]+1 < base.getStringCount())) {
409
410 // Make sure no cell is being edited, as caused by the
411 // keystroke. This makes the content hang on the screen.
412 assureNotEditing();
413
414 /*int col = table.getEditingColumn(),
415 row = table.getEditingRow();
416 table.getCellEditor(row, col).stopCellEditing();*/
417
418 // Store undo information:
419 baseFrame.undoManager.addEdit(new UndoableMoveString
420 (baseFrame, base, sel[0], false));
421
422
423 BibtexString bs = base.getString(sel[0]);
424 base.removeString(sel[0]);
425 try {
426 base.addString(bs, sel[0]+1);
427 } catch (KeyCollisionException ex) {}
428 table.revalidate();
429 table.setRowSelectionInterval(sel[0]+1, sel[0]+1);
430 table.repaint();
431 baseFrame.markBaseChanged();
432 }
433
434 }
435 }
436
437 UndoAction undoAction = new UndoAction();
438 class UndoAction extends AbstractAction {
439 public UndoAction() {
440 super("Undo", new ImageIcon(GUIGlobals.undoIconFile));
441 putValue(SHORT_DESCRIPTION, "Undo");
442 }
443 public void actionPerformed(ActionEvent e) {
444 baseFrame.undoAction.actionPerformed(null);
445 }
446 }
447
448 RedoAction redoAction = new RedoAction();
449 class RedoAction extends AbstractAction {
450 public RedoAction() {
451 super("Undo", new ImageIcon(GUIGlobals.redoIconFile));
452 putValue(SHORT_DESCRIPTION, "Redo");
453 }
454 public void actionPerformed(ActionEvent e) {
455 baseFrame.redoAction.actionPerformed(null);
456 }
457 }
458
459
460 }