1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.xnap.gui;
21
22 import java.awt.BorderLayout;
23 import java.awt.Color;
24 import java.awt.Insets;
25 import java.awt.event.ActionEvent;
26 import java.awt.event.ActionListener;
27 import java.awt.event.KeyAdapter;
28 import java.awt.event.KeyEvent;
29 import java.io.File;
30 import java.io.IOException;
31 import java.util.Arrays;
32 import java.util.List;
33 import java.util.Vector;
34 import javax.swing.AbstractAction;
35 import javax.swing.Action;
36 import javax.swing.Box;
37 import javax.swing.BoxLayout;
38 import javax.swing.DefaultCellEditor;
39 import javax.swing.JButton;
40 import javax.swing.JFileChooser;
41 import javax.swing.JLabel;
42 import javax.swing.JPanel;
43 import javax.swing.JPopupMenu;
44 import javax.swing.JScrollBar;
45 import javax.swing.JScrollPane;
46 import javax.swing.JSplitPane;
47 import javax.swing.JTable;
48 import javax.swing.JTextField;
49 import javax.swing.KeyStroke;
50 import javax.swing.border.EmptyBorder;
51 import javax.swing.event.ListSelectionEvent;
52 import javax.swing.event.ListSelectionListener;
53 import javax.swing.event.TreeSelectionEvent;
54 import javax.swing.event.TreeSelectionListener;
55 import org.apache.log4j.Logger;
56 import org.xnap.XNap;
57 import org.xnap.action.AbstractTogglePrefAction;
58 import org.xnap.action.AbstractXNapAction;
59 import org.xnap.gui.action.CopyFileAction;
60 import org.xnap.gui.action.CutFileAction;
61 import org.xnap.gui.action.EnqueueFileAction;
62 import org.xnap.gui.action.EraseAction;
63 import org.xnap.gui.action.OpenFileAction;
64 import org.xnap.gui.action.PasteFileAction;
65 import org.xnap.gui.action.PlayFileAction;
66 import org.xnap.gui.action.StopPlayerAction;
67 import org.xnap.gui.component.FileCompletionModel;
68 import org.xnap.gui.component.HistoryComboBox;
69 import org.xnap.gui.component.XNapButton;
70 import org.xnap.gui.component.XNapMenuItem;
71 import org.xnap.gui.component.XNapTextField;
72 import org.xnap.gui.event.DoubleClickListener;
73 import org.xnap.gui.event.PopupListener;
74 import org.xnap.gui.menu.OpenFileWithMenu;
75 import org.xnap.gui.menu.ViewerMenu;
76 import org.xnap.gui.shortcut.ShortcutManager;
77 import org.xnap.gui.table.LibraryTableModel;
78 import org.xnap.gui.tree.LibraryTree;
79 import org.xnap.gui.util.DragFilesSupport;
80 import org.xnap.gui.util.FocusHandler;
81 import org.xnap.gui.util.GUIHelper;
82 import org.xnap.gui.util.IconHelper;
83 import org.xnap.gui.util.SwingSynchronizedTask;
84 import org.xnap.gui.viewer.Viewer;
85 import org.xnap.gui.viewer.ViewerFrontend;
86 import org.xnap.gui.viewer.ViewerManager;
87 import org.xnap.io.Library;
88 import org.xnap.util.FileHelper;
89 import org.xnap.util.Formatter;
90 import org.xnap.util.Preferences;
91 import org.xnap.util.Scheduler;
92
93 public class LibraryPanel extends JPanel
94 implements ActionProvider, TreeSelectionListener, ListSelectionListener,
95 DirectoryProvider, FileProvider, ViewerFrontend
96 {
97
98
99
100 public static final String HISTORY_FILENAME
101 = FileHelper.getHomeDir() + "repository_search_history";
102
103
104
105 private Preferences prefs = Preferences.getInstance();
106
107 private LibraryTree lTree;
108 private JTable jta;
109 private JFileChooser jfc = null;
110 private LibraryTableModel ltm;
111 private DragFilesSupport dfs;
112
113 private JSplitPane jspH;
114 private JSplitPane jspV;
115
116 private JPanel jpURL;
117 private XNapTextField jtfURL;
118 private JPanel jpSearch;
119 private HistoryComboBox jcSearch;
120
121
122 private JPanel jpPreview;
123 private JScrollPane jspPreview;
124 private JLabel jlStatus;
125
126 private PlayFileAction playAction = new PlayFileAction(this);
127 private RefreshAction refreshAction = new RefreshAction();
128 private StopPlayerAction stopAction = new StopPlayerAction();
129 private EnqueueFileAction enqueueAction = new EnqueueFileAction(this);
130
131 private OpenFileAction acOpenFile = new OpenFileAction(this);
132 private DeleteAction deleteAction = new DeleteAction();
133 private RenameAction renameAction = new RenameAction();
134
135 private CopyFileAction acCopyFile = new CopyFileAction(this);
136 private CutFileAction acCutFile = new CutFileAction(this);
137 private PasteFileAction acPasteFile = new PasteFileAction(this);
138
139 private ShowURLAction acShowURL = new ShowURLAction();
140 private ShowSearchAction acShowSearch = new ShowSearchAction();
141
142 private File currentDir;
143 private boolean delayNextViewer;
144
145 private Viewer currentViewer;
146
147 private static Logger logger = Logger.getLogger(LibraryPanel.class);
148
149
150
151 public LibraryPanel()
152 {
153 initialize();
154 }
155
156
157
158 private void initialize()
159 {
160
161 Box bxURL = new Box(BoxLayout.X_AXIS);
162
163 jtfURL = new XNapTextField(20);
164 jtfURL.setCompletionModel(new FileCompletionModel());
165 jtfURL.setPreferences("libraryURL");
166
167 Action acEraseURL = new EraseAction(jtfURL);
168 JButton jbEraseURL = new JButton(acEraseURL);
169 jbEraseURL.setAlignmentY(0.5f);
170 jbEraseURL.setMargin(new Insets(1, 1, 1, 1));
171 bxURL.add(jbEraseURL);
172
173 bxURL.add(new JLabel(XNap.tr("Location", 1)));
174
175
176 bxURL.add(jtfURL);
177
178 jpURL = new JPanel(new BorderLayout());
179 jpURL.setVisible(prefs.getBoolean("showURLInLibraryPanel"));
180 jpURL.setBorder(new EmptyBorder(2, 2, 2, 2));
181 jpURL.add(bxURL, BorderLayout.CENTER);
182 acShowURL.setPanel(jpURL);
183
184
185 Box bxSearch = new Box(BoxLayout.X_AXIS);
186 Action acQuery = new QueryAction();
187
188 EraseAction acErase = new EraseAction();
189 ShortcutManager.getInstance().add(acErase, this);
190 JButton jbErase = new JButton(acErase);
191 jbErase.setMargin(new Insets(1, 1, 1, 1));
192 jbErase.setAlignmentY(0.5f);
193 bxSearch.add(jbErase);
194
195 JLabel searchLabel = new JLabel(XNap.tr("Library Search", 1));
196 bxSearch.add(searchLabel);
197
198 jcSearch = new HistoryComboBox(readHistoryFile(), 20);
199 searchLabel.setLabelFor(jcSearch.getTextField());
200 jcSearch.setPreferences("libararySearch");
201 GUIHelper.bindEnterKeyLocally(jcSearch.getTextField(), acQuery);
202
203 bxSearch.add(jcSearch);
204 acErase.setTextField(jcSearch.getTextField());
205
206 XNapButton jbQuery = new XNapButton(acQuery);
207 bxSearch.add(jbQuery);
208
209 jpSearch = new JPanel(new BorderLayout());
210 jpSearch.setVisible(prefs.getBoolean("showSearchInLibraryPanel"));
211
212 jpSearch.setBorder(new EmptyBorder(2, 2, 2, 2));
213 jpSearch.add(bxSearch, BorderLayout.CENTER);
214 acShowSearch.setPanel(jpSearch);
215
216
217 Box bxTop = new Box(BoxLayout.Y_AXIS);
218 bxTop.add(jpURL);
219 bxTop.add(jpSearch);
220
221
222 lTree = new LibraryTree(this);
223 lTree.addTreeSelectionListener(this);
224
225 JScrollPane jspTree = new JScrollPane();
226 jspTree.setViewportView(lTree);
227
228
229 JPopupMenu popup = new JPopupMenu();
230 popup.add(new XNapMenuItem(playAction));
231 popup.add(new XNapMenuItem(enqueueAction));
232 popup.addSeparator();
233 popup.add(new XNapMenuItem(acOpenFile));
234 popup.add(new OpenFileWithMenu(this));
235 popup.addSeparator();
236 popup.add(new ViewerMenu(this, this));
237 popup.addSeparator();
238 popup.add(new XNapMenuItem(acCopyFile));
239 popup.add(new XNapMenuItem(acCutFile));
240 popup.add(new XNapMenuItem(acPasteFile));
241 popup.addSeparator();
242 popup.add(new XNapMenuItem(renameAction));
243 popup.add(new XNapMenuItem(deleteAction));
244
245
246 ltm = new LibraryTableModel();
247 jta = ltm.createTable(Preferences.getInstance(), "library");
248 dfs = new DragFilesSupport(jta, this);
249 jta.getSelectionModel().addListSelectionListener(this);
250 jta.setShowGrid(false);
251 jta.addKeyListener(new ArrowKeyListener());
252
253 jta.addMouseListener(new PopupListener(popup));
254 DoubleClickListener.install(jta, acOpenFile);
255
256 GUIHelper.bindEmacsKeysToTable(jta);
257
258
259 ShortcutManager.getInstance().add(renameAction, jta);
260 ShortcutManager.getInstance().add(deleteAction, jta);
261 ShortcutManager.getInstance().add(refreshAction, this);
262
263 ShortcutManager.getInstance().add(enqueueAction, jta);
264 ShortcutManager.getInstance().add(playAction, jta);
265 ShortcutManager.getInstance().add(acOpenFile, jta);
266
267
268
269
270
271
272
273
274
275 JScrollPane jspTable = new JScrollPane();
276 jspTable.setViewportView(jta);
277
278
279 jpPreview = new JPanel(new BorderLayout());
280 jpPreview.setBackground(Color.gray);
281
282 jspPreview = new JScrollPane(jpPreview);
283
284 ActionListener al = jspPreview.getActionForKeyStroke
285 (KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0));
286 jspPreview.registerKeyboardAction
287 (al, KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, KeyEvent.CTRL_MASK),
288 WHEN_IN_FOCUSED_WINDOW);
289
290 al = jspPreview.getActionForKeyStroke
291 (KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0));
292 jspPreview.registerKeyboardAction
293 (al, KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, KeyEvent.CTRL_MASK
294 | KeyEvent.SHIFT_MASK),
295 WHEN_IN_FOCUSED_WINDOW);
296
297 JScrollBar jsb = jspPreview.getVerticalScrollBar();
298 jsb.setToolTipText(GUIHelper.tt(XNap.tr("Ctrl+Space to scroll down<br>Ctrl+Shift+Space to scroll up")));
299
300
301 jspV = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
302 jspV.add(jspTable, JSplitPane.TOP);
303 jspV.add(jspPreview, JSplitPane.BOTTOM);
304 jspV.setDividerLocation(prefs.getLibraryVerticalDividerLocation());
305 jspV.setOneTouchExpandable(true);
306 jspV.setBorder(GUIHelper.createEmptyBorder());
307
308 jlStatus = new JLabel(" ");
309
310 JPanel jpV = new JPanel(new BorderLayout());
311 jpV.add(jspV, "Center");
312 jpV.add(jlStatus, "South");
313
314 jspH = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
315 jspH.add(jspTree, JSplitPane.LEFT);
316 jspH.add(jpV, JSplitPane.RIGHT);
317 jspH.setDividerLocation(prefs.getLibraryHorizontalDividerLocation());
318 jspH.setOneTouchExpandable(true);
319 jspH.setBorder(GUIHelper.createEmptyBorder());
320
321
322 addComponentListener(new FocusHandler(jcSearch.getTextField()));
323
324
325 setLayout(new BorderLayout());
326 add(bxTop, BorderLayout.NORTH);
327 add(jspH, BorderLayout.CENTER);
328
329 GUIHelper.setMnemonics(this);
330 }
331
332 private void initializeShortcuts()
333 {
334 ShortcutManager sm = ShortcutManager.getInstance();
335 }
336
337 public Action[] getActions()
338 {
339 return new Action[] { playAction, enqueueAction, stopAction, null,
340 refreshAction, null,
341 acShowURL, acShowSearch, };
342 }
343
344 public void savePrefs()
345 {
346 prefs.setLibraryHorizontalDividerLocation(jspH.getDividerLocation());
347 prefs.setLibraryVerticalDividerLocation(jspV.getDividerLocation());
348 writeHistoryFile();
349 }
350
351 private void writeHistoryFile()
352 {
353 List l = Arrays.asList(jcSearch.getHistoryItems());
354 try {
355 FileHelper.writeBinary(new File(HISTORY_FILENAME), l);
356 }
357 catch (IOException ie) {
358 logger.debug("Error writing history file", ie);
359 }
360 }
361
362 private Vector readHistoryFile()
363 {
364 Vector v = new Vector();
365 try {
366 FileHelper.readBinary(new File(HISTORY_FILENAME), v);
367 }
368 catch (IOException ie) {
369 logger.debug("Error reading history file" , ie);
370 }
371 return v;
372 }
373
374 private void showDirectory(File f)
375 {
376 jtfURL.setText(f.getAbsolutePath());
377
378 currentDir = f;
379 ltm.clear();
380
381 if (f.canRead()) {
382 ltm.add(f);
383 ltm.resort();
384 }
385 resetPanel();
386 }
387
388 private void showFiles(File[] files)
389 {
390 ltm.clear();
391 ltm.add(files);
392 ltm.resort();
393 resetPanel();
394 }
395
396 /***
397 * Called when a directory has been selected.
398 */
399 public void valueChanged(TreeSelectionEvent e)
400 {
401 Object target = e.getPath().getLastPathComponent();
402
403 if (target instanceof File) {
404 File f = (File)target;
405 showDirectory(f);
406 }
407 else {
408 jtfURL.setText(null);
409 }
410 }
411
412 /***
413 * Called when the table's selection changes, we check what kind of
414 * file was selected and call the respective viewer plugin if available.
415 */
416 public void valueChanged(ListSelectionEvent e)
417 {
418
419 if (jta.getSelectedRowCount() > 1) {
420 resetPanel();
421 return;
422 }
423
424 int row = jta.getSelectedRow();
425 if (row != -1) {
426 if (delayNextViewer) {
427 delayNextViewer = false;
428 Scheduler.run(500, new DelayedViewerTask(row));
429 }
430 else {
431 display(ltm.get(row));
432 }
433 }
434 }
435
436 /***
437 * Implements the {@link DirectoryProvider} interface delegating the
438 * request to the {@link LibraryTree}.
439 */
440 public File getDirectory()
441 {
442 return lTree.getDirectory();
443 }
444
445 /***
446 * Implements the {@link DirectoryProvider#hasChanged}.
447 */
448 public void hasChanged(File directory)
449 {
450 if (currentDir != null) {
451 showDirectory(currentDir);
452 }
453 }
454
455 private File[] getFiles(boolean showMessage)
456 {
457 int rowC = jta.getSelectedRowCount();
458 int rows[] = jta.getSelectedRows();
459
460 if (showMessage && rowC == 0) {
461 StatusBar.setText(XNap.tr("Please select something first"));
462 return null;
463 }
464
465 File[] files = new File[rowC];
466
467 for (int i = 0; i < rowC; i++) {
468 files[i] = ltm.get(rows[i]);
469 }
470
471 return files;
472 }
473
474 /***
475 * Implements {@link FileProvider}.
476 */
477 public File[] getFiles()
478 {
479 return getFiles(true);
480 }
481
482 private void display(File file)
483 {
484 jtfURL.setText(file.getAbsolutePath());
485
486 if (currentViewer != null) {
487 currentViewer.close();
488 }
489
490 Viewer v = ViewerManager.getInstance().getDefaultViewer(file);
491 if (v != null && file.canRead()) {
492 display(v, file);
493 }
494 else {
495 if (v != null) {
496 StatusBar.setText
497 (XNap.tr("You do not have permission to read file"));
498 }
499 jspPreview.setViewportView(jpPreview);
500 }
501 }
502
503 /***
504 * Implements the {@link ViewerFrontend} interface.
505 */
506 public void display(Viewer viewer, File file)
507 {
508 jspPreview.setViewportView(viewer.getComponent());
509 currentViewer = viewer;
510 viewer.open(file);
511 }
512
513 private void resetPanel()
514 {
515 jspPreview.setViewportView(jpPreview);
516 updateStatus();
517 }
518
519 /***
520 * Needed for properties serialization.
521 */
522 public int getHorizontalDividerLocation()
523 {
524 return jspH.getDividerLocation();
525 }
526
527 /***
528 * Needed for properties deserialization.
529 */
530 public void setHorizontalDividerLocation(int newValue)
531 {
532 if (newValue == -1) {
533 jspH.setDividerLocation(200);
534 }
535 else {
536 jspH.setDividerLocation(newValue);
537 }
538 }
539
540 /***
541 * Needed for properties serialization.
542 */
543 public int getVerticalDividerLocation()
544 {
545 return jspV.getDividerLocation();
546 }
547
548 /***
549 * Needed for properties deserialization.
550 */
551 public void setVerticalDividerLocation(int newValue)
552 {
553 if (newValue == -1) {
554 jspV.setDividerLocation(200);
555 }
556 else {
557 jspV.setDividerLocation(newValue);
558 }
559 }
560
561 /***
562 * Updates the status line below the file table.
563 */
564 private void updateStatus()
565 {
566 StringBuffer sb = new StringBuffer();
567 File[] files = getFiles(false);
568 if (files.length == 0) {
569
570 sb.append(ltm.getRowCount());
571 sb.append(" files - ");
572 sb.append(Formatter.formatSize(ltm.getTotalSize()));
573 }
574 else if (files.length == 1) {
575 sb.append("1 file - ");
576 sb.append(Formatter.formatNumber(files[0].length()) + " bytes");
577 }
578 else {
579 long totalSize = 0;
580 for (int i = 0; i < files.length; i++) {
581 totalSize += files[i].length();
582 }
583 sb.append("selected - ");
584 sb.append(files.length);
585 sb.append(" files - ");
586 sb.append(Formatter.formatSize(totalSize));
587 }
588
589
590 sb.insert(0, " ");
591 jlStatus.setText(sb.toString());
592 }
593
594
595
596 /***
597 * Refresh file and directory listings
598 */
599 private class RefreshAction extends AbstractAction
600 {
601
602 public RefreshAction()
603 {
604 putValue(Action.NAME, XNap.tr("Refresh"));
605 putValue(Action.SHORT_DESCRIPTION,
606 XNap.tr("Refresh list of files"));
607 putValue(IconHelper.XNAP_ICON, "reload.png");
608 putValue(Action.ACTION_COMMAND_KEY, "libraryRefresh");
609 putValue(AbstractXNapAction.DEFAULT_KEYSTROKE,
610 KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0));
611 putValue(AbstractXNapAction.SHORTCUT_CATEGORY,
612 XNap.tr("Library"));
613 }
614
615 public void actionPerformed(ActionEvent event)
616 {
617 if (currentDir != null) {
618 showDirectory(currentDir);
619 }
620 }
621 }
622
623 private class DeleteAction extends AbstractAction
624 {
625 public DeleteAction()
626 {
627 putValue(Action.NAME, XNap.tr("Delete"));
628 putValue(Action.SHORT_DESCRIPTION,
629 XNap.tr("Deletes selected file(s)."));
630 putValue(IconHelper.XNAP_ICON, "editdelete.png");
631 putValue(Action.ACTION_COMMAND_KEY, "libraryFileDelete");
632 putValue(AbstractXNapAction.DEFAULT_KEYSTROKE,
633 KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0));
634 putValue(AbstractXNapAction.SHORTCUT_CATEGORY,
635 XNap.tr("Library"));
636 }
637
638 public void actionPerformed(ActionEvent event)
639 {
640 File[] files = getFiles();
641 if (files == null)
642 return;
643
644 files = Dialogs.showDeleteFilesDialog(LibraryPanel.this, files);
645
646 if (files != null) {
647 for (int i = 0; i < files.length; i++) {
648 if (!ltm.delete(files[i])) {
649 StatusBar.setText(XNap.tr("Could not delete {0}",
650 files[i].getName()));
651 }
652 }
653 }
654 }
655 }
656
657 private class RenameAction extends AbstractAction
658 {
659 public RenameAction()
660 {
661 putValue(Action.NAME, XNap.tr("Rename"));
662 putValue(Action.SHORT_DESCRIPTION,
663 XNap.tr("Renames the selected file."));
664 putValue(Action.ACTION_COMMAND_KEY, "libraryFileRename");
665 putValue(AbstractXNapAction.DEFAULT_KEYSTROKE,
666 KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0));
667 putValue(AbstractXNapAction.SHORTCUT_CATEGORY,
668 XNap.tr("Library"));
669 }
670
671 public void actionPerformed(ActionEvent event)
672 {
673 int i = jta.getSelectedRow();
674 if (i != -1) {
675 ltm.setCellEditable(i, LibraryTableModel.NAME);
676 jta.editCellAt(i, LibraryTableModel.NAME);
677 DefaultCellEditor dce = (DefaultCellEditor)jta.getCellEditor();
678 JTextField jtf = (JTextField)dce.getComponent();
679
680
681 jtf.grabFocus();
682 jtf.setCaretPosition(0);
683 ltm.setCellEditable(-1, -1);
684 }
685 }
686 }
687
688 private class ShowSearchAction extends AbstractTogglePrefAction
689 {
690 private JPanel jp;
691
692 public ShowSearchAction()
693 {
694 super("showSearchInLibraryPanel");
695 putValue(Action.NAME, XNap.tr("Show Search Input"));
696 putValue(IconHelper.XNAP_ICON, "filefind.png");
697 putValue(Action.SHORT_DESCRIPTION,
698 XNap.tr("Shows or hides the search field."));
699 }
700
701 public void setPanel(JPanel jp)
702 {
703 this.jp = jp;
704 }
705
706 public void toggled(boolean visible)
707 {
708 if (jp != null) {
709 jp.setVisible(visible);
710 }
711 }
712 }
713
714 private class ShowURLAction extends AbstractTogglePrefAction
715 {
716 private JPanel jp;
717
718 public ShowURLAction()
719 {
720 super("showURLInLibraryPanel");
721
722 putValue(Action.NAME, XNap.tr("Show URL Input"));
723 putValue(IconHelper.XNAP_ICON, "editclear.png");
724 putValue(Action.SHORT_DESCRIPTION,
725 XNap.tr("Shows or hides the URL field."));
726 }
727
728 public void setPanel(JPanel jp)
729 {
730 this.jp = jp;
731 }
732
733 public void toggled(boolean visible)
734 {
735 if (jp != null) {
736 jp.setVisible(visible);
737 }
738 }
739 }
740
741 /***
742 * Performs a search.
743 */
744 private class QueryAction extends AbstractAction
745 {
746
747 public QueryAction()
748 {
749 putValue(Action.NAME, XNap.tr("Query"));
750 putValue(IconHelper.XNAP_ICON, "filefind.png");
751 putValue(Action.SHORT_DESCRIPTION, XNap.tr("Performs search."));
752 }
753
754 public void actionPerformed(ActionEvent event)
755 {
756 String s = jcSearch.getText().trim();
757 if (s.length() > 0) {
758 File[] files = Library.getInstance().search(s);
759 if (files != null && files.length > 0) {
760 lTree.clearSelection();
761 showFiles(files);
762 StatusBar.setText(XNap.tr("Found {0} files",
763 new Integer(files.length)));
764 }
765 else {
766 StatusBar.setText(XNap.tr("{0} not found in library", s));
767 }
768 jcSearch.addDistinctItemAtTop(s);
769 }
770 else {
771 StatusBar.setText(XNap.tr("What are you trying to search for?"));
772 }
773 }
774
775 }
776
777 private class DelayedViewerTask extends SwingSynchronizedTask
778 {
779 private int showRow;
780
781 public DelayedViewerTask(int showRow)
782 {
783 this.showRow = showRow;
784 }
785
786 public void runSynchronized()
787 {
788 if (jta.getSelectedRowCount() == 1
789 && jta.getSelectedRow() == showRow) {
790 display(ltm.get(showRow));
791 }
792 }
793 }
794
795 private class ArrowKeyListener extends KeyAdapter {
796
797 public void keyPressed(KeyEvent e)
798 {
799 int c = e.getKeyCode();
800 if (c == KeyEvent.VK_PAGE_DOWN || c == KeyEvent.VK_PAGE_UP
801 || c == KeyEvent.VK_DOWN || c == KeyEvent.VK_UP) {
802 delayNextViewer = true;
803 }
804 }
805
806 }
807
808 }