1 /*
2 * NeonZip - archive tool
3 * Copyright (C) 2001 Peter Ivanov
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 *
19 *
20 * @author Peter Ivanov
21 * <a href=mailto:peterivanov@europe.com>peterivanov@europe.com</a>,
22 * <br>Copyright (c) 2001 Peter K. Ivanov.
23 *
24 * @version (December 30 2001)
25 *
26 */
27
28 package net.sourceforge.neonzip;
29
30 import javax.swing;
31 import javax.swing.event;
32 import javax.swing.table;
33 import javax.swing.border;
34 import java.awt;
35 import java.awt.event;
36 import java.awt.dnd;
37 import java.awt.datatransfer;
38 import java.util;
39 import java.io;
40 import java.text;
41 import java.net.URL;
42
43 import gnu.regexp;
44 import org.apache.log4j;
45
46 public final class Main extends JFrame {
47
48 Thread currentOperation;
49
50 Category log = Category.getInstance("neonzip.Main");
51
52 public final static String TITLE = "NeonZip";
53 public final static int WIDTH = 610;
54 public final static int HEIGHT = 450;
55
56 /* File Menu History */
57 private JMenu fMenuFile;
58 private JMenu fMenuRecent;
59 private FileHistoryManager fFileHistory;
60
61 /* List with last extraction paths */
62 private Vector fPathList;
63 private final static int fMaxExtractHistory = 6;
64
65 /* Properties manager */
66 private Utils.PropertiesManager fPropManager;
67
68 /* Properties */
69 private File fExtractDir;
70
71 /* Internationalization resources */
72 public static ResourceBundle fResources;
73
74 /* Actions */
75 private ExitAction exitAction;
76 private SortingAction sortingAction;
77 private OpenArchiveAction openArchiveAction;
78 private NewArchiveAction newArchiveAction;
79 private CloseArchiveAction closeArchiveAction;
80 private AddAction addAction;
81 private DeleteAction deleteAction;
82 private ExtractAction extractAction;
83 private SelectAllAction selectAllAction;
84 private InvertSelectionAction invertSelectionAction;
85 private LastOutputAction lastOutputAction;
86 private LastOpenHistoryAction lastOpenHistoryAction;
87 private TestIntegrityAction testIntegrityAction;
88
89 /* Main Table Component */
90 private ArchiveTable fTable;
91
92 /* Table Model - customized functionality */
93 private ArchiveTableModel fTableModel;
94 //private DefaultTableModel fTableModel;
95
96 /* Archive Supports */
97 private ArchiveSupport fSupport;
98
99 /* JTable data and columns */
100 private Vector fColumnNames;
101 private Vector fTableData;
102
103 private Vector fColumnsMaped;
104
105 /* Point to current system locale */
106 private Locale fLocale;
107
108 /* Localized data and time formatter (middle data/time format) */
109 public static DateFormat fDateFormat;
110
111 /* Last log output manager */
112 private Utils.OutputManager fOutputManager;
113
114 /* Status bar labels */
115 private JLabel fLeftStatusLabel;
116 private JLabel fRightStatusLabel;
117
118 /* Some predefined string for more optimization */
119 private String strTotal;
120 private String strFiles;
121 private String strSelected;
122 private String strBytes;
123
124 /* Progress monitor */
125 private NeonMonitor fMonitor;
126 private Thread fMonitorThread;
127
128 /**
129 * Main class constructor
130 */
131 public Main() {
132 super();
133
134 log.info("NeonZip starting...");
135
136 /* Determinate current locale */
137 fLocale = Locale.getDefault();
138
139 /* Internationalization bundles */
140 defineInternationalizationBundles();
141
142 /* Some predefined string for more optimization */
143 strTotal = fResources.getString("key.label.total") + " ";
144 strFiles = fResources.getString("key.label.files") + ", ";
145 strSelected = fResources.getString("key.label.selected") + " ";
146 strBytes = " " + fResources.getString("key.label.bytes");
147
148 /* Recent files history manager */
149 fFileHistory = new FileHistoryManager();
150
151 /* Components initialization */
152 initComponents();
153
154 /* Properties manager */
155 fPropManager = new Utils.PropertiesManager();
156 fPropManager.setAutoStore(false);
157 loadProperties();
158
159 /* Medium data/time format for table */
160 defineDataTimeFormat();
161
162 /* Crate some directories if is needed */
163 handleAppDirs();
164
165 /* Last log output manager */
166 fOutputManager = new Utils.OutputManager();
167
168 /* Monitor for operations progress */
169 fMonitor = new NeonMonitor(this);
170
171 // force zip support only for development needs
172 fSupport = new ZipSupport(this, fOutputManager, fTable, fTableData);
173
174 setGlassPane(new GlassComponent());
175 }
176
177
178
179 /**
180 * This method can be invoked inside archive supports.
181 * And is very similar to Open archive operation for current support.
182 */
183 public void refreshTable(File aCurrentArchive) {
184 // clear old table content
185 fTableData.clear();
186
187 Vector zipData = null;
188
189 // invoke archive support interface
190 try {
191 zipData = fSupport.open(aCurrentArchive);
192 } catch (Exception e) {
193 log.error("Error open file", e);
194 }
195 if (zipData == null) return;
196 //log.debug("Data in zip VECTOR: " + zipData.size());
197
198 fTableData.addAll(zipData);
199 fTable.updateUI();
200 setTitle(TITLE + " - " + Utils.removePath(aCurrentArchive.toString()));
201
202 // clear previous selection
203 ListSelectionModel tableSelection = fTable.getSelectionModel();
204 //tableSelection.setLeadSelectionIndex(0);
205 tableSelection.clearSelection();
206
207 this.setDefaultRightStatusMessage();
208 }
209
210
211 /**
212 * Components initialization.
213 */
214 private void initComponents() {
215 setTitle(TITLE);
216 setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
217 addWindowListener(new WindowAdapter() {
218 public void windowClosing(WindowEvent e) {
219 appClosing(e);
220 }
221 });
222
223 JPanel root = new JPanel(new BorderLayout());
224 JToolBar toolbar = new JToolBar();
225 JPanel content = new JPanel(new BorderLayout());
226 JPanel statusbar = new JPanel(new GridLayout(1, 2));
227
228 /* Status bar */
229 statusbar.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
230 fLeftStatusLabel = new JLabel(" ");
231 fRightStatusLabel = new JLabel(" ");
232 statusbar.add(fLeftStatusLabel);
233 statusbar.add(fRightStatusLabel);
234
235 root.add(toolbar, "North");
236 root.add(content, "Center");
237 content.add(statusbar, "South");
238
239 getContentPane().add(root);
240
241 /* Table initialization */
242 fColumnNames = initColumnNames();
243 fTableData = new Vector();
244
245 fTable = new ArchiveTable(fTableData, fColumnNames);
246 fTableModel = new ArchiveTableModel();
247 fTable.setModel(fTableModel);
248 fTable.setShowGrid(false);
249 fTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
250 fTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
251 fTable.setBackground(Color.white);
252 //fTable.setPreferredScrollableViewportSize(new Dimension(WIDTH, HEIGHT));
253 JScrollPane tablePane = new JScrollPane(fTable,
254 ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
255 ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
256 JViewport viewport = tablePane.getViewport();
257 viewport.setBackground(Color.white);
258 viewport.setDropTarget(new DropTarget(viewport, new ViewportDropTargetListener()));
259 ListSelectionModel selectModel = fTable.getSelectionModel();
260 selectModel.addListSelectionListener(new ListSelectionListener() {
261 public void valueChanged(ListSelectionEvent e) {
262 selectionListenerValueChanged(e);
263 }
264 });
265 // prepare table header to handle sorting
266 JTableHeader header = fTable.getTableHeader();
267 header.setUpdateTableInRealTime(true);
268 header.addMouseListener(fTableModel.new ColumnListener());
269 header.setReorderingAllowed(true);
270 this.initTableCellRenderer();
271 this.initColumnIdentifiers();
272 /* End table */
273
274 content.add(tablePane, "Center");
275
276 /* Actions */
277 exitAction = new ExitAction();
278 sortingAction = new SortingAction();
279 openArchiveAction = new OpenArchiveAction();
280 newArchiveAction = new NewArchiveAction();
281 closeArchiveAction = new CloseArchiveAction();
282 addAction = new AddAction();
283 deleteAction = new DeleteAction();
284 extractAction = new ExtractAction();
285 selectAllAction = new SelectAllAction();
286 invertSelectionAction = new InvertSelectionAction();
287 lastOutputAction = new LastOutputAction();
288 lastOpenHistoryAction = new LastOpenHistoryAction();
289 testIntegrityAction = new TestIntegrityAction();
290 /* End actions */
291
292 /* Toolbar */
293 JButton btnNewArchive = toolbar.add(newArchiveAction);
294 btnNewArchive.setIcon(loadImage("images/new_archive.png"));
295 btnNewArchive.setText("");
296 btnNewArchive.setToolTipText(fResources.getString("key.menu.new_archive"));
297
298 JButton btnOpenArchive = toolbar.add(openArchiveAction);
299 btnOpenArchive.setIcon(loadImage("images/open_archive.png"));
300 btnOpenArchive.setText("");
301 btnOpenArchive.setToolTipText(fResources.getString("key.menu.open_archive"));
302
303 JButton btnCloseArchive = toolbar.add(closeArchiveAction);
304 btnCloseArchive.setIcon(loadImage("images/close_archive.png"));
305 btnCloseArchive.setText("");
306 btnCloseArchive.setToolTipText(fResources.getString("key.menu.close_archive"));
307
308 toolbar.addSeparator();
309
310 JButton btnAdd = toolbar.add(addAction);
311 btnAdd.setIcon(loadImage("images/add.png"));
312 btnAdd.setText("");
313 btnAdd.setToolTipText(fResources.getString("key.menu.add"));
314
315 JButton btnDelete = toolbar.add(deleteAction);
316 btnDelete.setIcon(loadImage("images/delete.png"));
317 btnDelete.setText("");
318 btnDelete.setToolTipText(fResources.getString("key.menu.delete"));
319
320 JButton btnExtract = toolbar.add(extractAction);
321 btnExtract.setIcon(loadImage("images/extract.png"));
322 btnExtract.setText("");
323 btnExtract.setToolTipText(fResources.getString("key.menu.extract"));
324
325 /* Menus */
326 JMenuBar mainMenu = new JMenuBar();
327 setJMenuBar(mainMenu);
328
329 // File menu
330 fMenuFile = new JMenu(fResources.getString("key.menu.file"));
331 fMenuFile.setMnemonic(KeyEvent.VK_F);
332 mainMenu.add(fMenuFile);
333
334 // New Archive
335 JMenuItem menuNewArchive = new JMenuItem(newArchiveAction);
336 menuNewArchive.setMnemonic(KeyEvent.VK_N);
337 menuNewArchive.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N,
338 ActionEvent.CTRL_MASK));
339 fMenuFile.add(menuNewArchive);
340
341 // Open Archive
342 JMenuItem menuOpenArchive = new JMenuItem(openArchiveAction);
343 menuOpenArchive.setMnemonic(KeyEvent.VK_O);
344 menuOpenArchive.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O,
345 ActionEvent.CTRL_MASK));
346 fMenuFile.add(menuOpenArchive);
347
348 // Close Archive
349 JMenuItem menuCloseArchive = new JMenuItem(closeArchiveAction);
350 menuCloseArchive.setMnemonic(KeyEvent.VK_C);
351 menuCloseArchive.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_L,
352 ActionEvent.SHIFT_MASK));
353 fMenuFile.add(menuCloseArchive);
354 fMenuFile.addSeparator();
355
356 // Recent Files
357 fMenuRecent = new JMenu();
358 fMenuRecent.setText(Main.fResources.getString("key.menu.recent_files"));
359 fMenuRecent.addMenuListener(new RecentFilesMenuListener());
360 fMenuFile.add(fMenuRecent);
361
362 // Exit
363 fMenuFile.addSeparator();
364 JMenuItem menuExit = new JMenuItem(exitAction);
365 menuExit.setMnemonic(KeyEvent.VK_X);
366 menuExit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4,
367 ActionEvent.ALT_MASK));
368 fMenuFile.add(menuExit);
369
370 // Actions menu
371 JMenu menuActions = new JMenu(fResources.getString("key.menu.actions"));
372 menuActions.setMnemonic(KeyEvent.VK_A);
373 mainMenu.add(menuActions);
374
375 // Add
376 JMenuItem menuAdd = new JMenuItem(addAction);
377 menuAdd.setMnemonic(KeyEvent.VK_A);
378 menuAdd.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A,
379 ActionEvent.SHIFT_MASK));
380 menuActions.add(menuAdd);
381
382 // Delete
383 JMenuItem menuDelete = new JMenuItem(deleteAction);
384 menuDelete.setMnemonic(KeyEvent.VK_D);
385 menuDelete.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D,
386 ActionEvent.SHIFT_MASK));
387 menuActions.add(menuDelete);
388
389 // Extract
390 JMenuItem menuExtract = new JMenuItem(extractAction);
391 menuExtract.setMnemonic(KeyEvent.VK_E);
392 menuExtract.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E,
393 ActionEvent.SHIFT_MASK));
394 menuActions.add(menuExtract);
395
396 // Separator
397 menuActions.addSeparator();
398
399 // Select All
400 JMenuItem menuSelectAll = new JMenuItem(selectAllAction);
401 menuSelectAll.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A,
402 ActionEvent.CTRL_MASK));
403 menuActions.add(menuSelectAll);
404
405 // Invert Selection
406 JMenuItem menuInvertSelection = new JMenuItem(invertSelectionAction);
407 menuActions.add(menuInvertSelection);
408
409 // Separator
410 menuActions.addSeparator();
411
412 // Test integrity
413 JMenuItem menuTestIntegrity = new JMenuItem(testIntegrityAction);
414 menuTestIntegrity.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T,
415 ActionEvent.SHIFT_MASK));
416 menuActions.add(menuTestIntegrity);
417
418 // Options
419 JMenu menuOptions = new JMenu(fResources.getString("key.menu.options"));
420 menuOptions.setMnemonic(KeyEvent.VK_O);
421 menuOptions.addMenuListener(new OptionsMenuListener());
422 mainMenu.add(menuOptions);
423
424 // Sort
425 JMenu menuSorting = new JMenu(fResources.getString("key.menu.Sort"));
426 menuSorting.addMenuListener(new SortingMenuListener());
427 menuOptions.add(menuSorting);
428
429 // Sort by Name
430 JRadioButtonMenuItem menuNameSort =
431 new JRadioButtonMenuItem(sortingAction);
432 menuNameSort.setText(fResources.getString("key.menu.byName"));
433 menuNameSort.setSelected(true);
434 menuNameSort.setActionCommand("Name");
435 menuSorting.add(menuNameSort);
436
437 // Sort by Type
438 JRadioButtonMenuItem menuTypeSort =
439 new JRadioButtonMenuItem(sortingAction);
440 menuTypeSort.setText(fResources.getString("key.menu.byType"));
441 menuTypeSort.setActionCommand("Type");
442 menuSorting.add(menuTypeSort);
443
444 // Sort by date
445 JRadioButtonMenuItem menuDateSort =
446 new JRadioButtonMenuItem(sortingAction);
447 menuDateSort.setText(fResources.getString("key.menu.byDate"));
448 menuDateSort.setActionCommand("Modified");
449 menuSorting.add(menuDateSort);
450
451 // Sort by size
452 JRadioButtonMenuItem menuSizeSort =
453 new JRadioButtonMenuItem(sortingAction);
454 menuSizeSort.setText(fResources.getString("key.menu.bySize"));
455 menuSizeSort.setActionCommand("Size");
456 menuSorting.add(menuSizeSort);
457
458 // Sort by compression ratio
459 JRadioButtonMenuItem menuCompressionSort =
460 new JRadioButtonMenuItem(sortingAction);
461 menuCompressionSort.setText(fResources.getString("key.menu.byCompression_ratio"));
462 menuCompressionSort.setActionCommand("Ratio");
463 menuSorting.add(menuCompressionSort);
464
465 // Sort by packed size
466 JRadioButtonMenuItem menuPackedSort =
467 new JRadioButtonMenuItem(sortingAction);
468 menuPackedSort.setText(fResources.getString("key.menu.byPacked_size"));
469 menuPackedSort.setActionCommand("Packed");
470 menuSorting.add(menuPackedSort);
471
472 // Sort by CRC
473 JRadioButtonMenuItem menuCRCSort =
474 new JRadioButtonMenuItem(sortingAction);
475 menuCRCSort.setText(fResources.getString("key.menu.byCRC"));
476 menuCRCSort.setActionCommand("CRC");
477 menuSorting.add(menuCRCSort);
478
479 // Sort by Path
480 JRadioButtonMenuItem menuPathSort =
481 new JRadioButtonMenuItem(sortingAction);
482 menuPathSort.setText(fResources.getString("key.menu.byPath"));
483 menuPathSort.setActionCommand("Path");
484 menuSorting.add(menuPathSort);
485
486 // Sorting button group
487 ButtonGroup sorting = new ButtonGroup();
488 sorting.add(menuNameSort);
489 sorting.add(menuTypeSort);
490 sorting.add(menuDateSort);
491 sorting.add(menuSizeSort);
492 sorting.add(menuCompressionSort);
493 sorting.add(menuPackedSort);
494 sorting.add(menuCRCSort);
495 sorting.add(menuPathSort);
496
497 // Separator
498 menuOptions.addSeparator();
499
500 // View Last Output
501 JMenuItem menuLastOutput = new JMenuItem(lastOutputAction);
502 menuLastOutput.setMnemonic(KeyEvent.VK_W);
503 menuOptions.add(menuLastOutput);
504
505 /* End menus */
506 }
507
508
509
510 /**
511 * Define default names of table columns. We will give this data
512 * to Table object.
513 * @return Vector with default column names.
514 */
515 private final Vector initColumnNames() {
516 Vector result = new Vector(8);
517 result.add(fResources.getString("key.table.name"));
518 result.add(fResources.getString("key.table.type"));
519 result.add(fResources.getString("key.table.modified"));
520 result.add(fResources.getString("key.table.size"));
521 result.add(fResources.getString("key.table.ratio"));
522 result.add(fResources.getString("key.table.packed"));
523 result.add(fResources.getString("key.table.crc"));
524 result.add(fResources.getString("key.table.path"));
525
526 return result;
527 }
528
529
530
531 /**
532 * Init column identifiers. This is way to separate real column names
533 * from column names visible to the users.
534 */
535 private final void initColumnIdentifiers() {
536 TableColumnModel columnModel = fTable.getColumnModel();
537 TableColumn column0 = columnModel.getColumn(0);
538 TableColumn column1 = columnModel.getColumn(1);
539 TableColumn column2 = columnModel.getColumn(2);
540 TableColumn column3 = columnModel.getColumn(3);
541 TableColumn column4 = columnModel.getColumn(4);
542 TableColumn column5 = columnModel.getColumn(5);
543 TableColumn column6 = columnModel.getColumn(6);
544 TableColumn column7 = columnModel.getColumn(7);
545
546 column0.setIdentifier("name");
547 column1.setIdentifier("type");
548 column2.setIdentifier("modified");
549 column3.setIdentifier("size");
550 column4.setIdentifier("ratio");
551 column5.setIdentifier("packed");
552 column6.setIdentifier("crc");
553 column7.setIdentifier("path");
554 }
555
556
557
558 /**
559 * Define local specific data and time format.
560 * yyyy-m-dd hh:mm:ss
561 */
562 private final void defineDataTimeFormat() {
563 fDateFormat = DateFormat.getDateTimeInstance( DateFormat.MEDIUM,
564 DateFormat.MEDIUM,
565 fLocale);
566 }
567
568
569
570 /**
571 * Define local specific resources. Every kind of component support
572 * separate bundle for internationalization strings.
573 */
574 private final void defineInternationalizationBundles() {
575 try {
576 fResources = ResourceBundle.getBundle("net.sourceforge.neonzip.resources.ResourcesBundle", fLocale);
577 } catch (MissingResourceException e) {
578 // TO Do - add more logic here
579 log.error("language specific resources missing", e);
580 e.printStackTrace();
581 }
582 }
583
584
585
586 /**
587 * Close application event.
588 * @param aEvent window change status event
589 */
590 private void appClosing(WindowEvent aEvent) {
591 saveProperties();
592 System.exit(0);
593 }
594
595
596
597 /**
598 * Set specific cell renderer for columns to customize columns behaviour.
599 */
600 private void initTableCellRenderer() {
601 ArchiveTableCellRenderer cellRenderer = new ArchiveTableCellRenderer();
602 TableColumnModel columnModel = fTable.getColumnModel();
603 int columnCount = columnModel.getColumnCount();
604 for (int i = 0; i < columnCount; i++) {
605 TableColumn column = columnModel.getColumn(i);
606 if (i > 0) {
607 column.setCellRenderer(cellRenderer);
608 }
609 }
610 }
611
612
613
614 /**
615 * Table row selection listener for table selection changes.
616 * aEvent selection changes event
617 */
618 private void selectionListenerValueChanged(ListSelectionEvent aEvent) {
619 DefaultListSelectionModel selModel = (DefaultListSelectionModel) aEvent.getSource();
620 if (selModel.getValueIsAdjusting()) return;
621 setDefaultLeftStatusMessage();
622 }
623
624
625
626 /**
627 * Handle some directories if is needed. Example: Create application
628 * home directory if directory missing. For example when application
629 * runs for the first time home directory missing.
630 */
631 private void handleAppDirs() {
632 // application home directory
633 if (!Utils.hasAppHomeDir()) {
634 Utils.createAppHomeDir();
635 }
636 // application temporary directory
637 if (!Utils.hasTempDir()) {
638 Utils.createTempDir();
639 }
640 }
641
642
643
644 /**
645 * Handle application properties saving. Set properties to properties
646 * manager and after that invoke PropertiesManages.store() method
647 * and save all app properties to file.
648 */
649 private void saveProperties() {
650 // Save table columns count and width and names
651 TableColumnModel columnModel = fTable.getColumnModel();
652 int columnCount = columnModel.getColumnCount();
653 fPropManager.setProperty("columns.count", columnCount);
654 for (int i = 0; i < columnCount; i++) {
655 TableColumn column = columnModel.getColumn(i);
656 int columnWidth = column.getPreferredWidth();
657 fPropManager.setProperty("column" + i + ".width", columnWidth);
658 String name = fTable.getColumnName(i);
659 // removing ascending mark in column name
660 name = name.replace('>', ' ');
661 name = name.replace('<', ' ');
662 name = name.trim();
663 //// end removing
664 fPropManager.setProperty("column" + i + ".name", name);
665 }
666
667 // Save Main Window bounds (size and position)
668 Rectangle bounds = this.getBounds();
669 fPropManager.setProperty("frame.x", bounds.x);
670 fPropManager.setProperty("frame.y", bounds.y);
671 fPropManager.setProperty("frame.width", bounds.width);
672 fPropManager.setProperty("frame.height", bounds.height);
673
674 // Save last extraction directory
675 fPropManager.setProperty("extract.lastFolder", fExtractDir.toString());
676
677 // Sorted column
678 String sortColumn = (String) fColumnNames.get(fTableModel.getSortingColumn());
679 fPropManager.setProperty("sorting.column", sortColumn);
680
681 // Extraction path history
682 for (int i = 0; i < fPathList.size(); i++) {
683 if (i > fMaxExtractHistory) break;
684 String path = (String) fPathList.get(i);
685 fPropManager.setProperty("extract.path" + i, path);
686 }
687
688 // save last open archives history
689 int size = fFileHistory.size();
690 for (int i = 0; i < size; i++) {
691 String path = null;
692 try {
693 path = fFileHistory.get(i);
694 } catch (ArrayIndexOutOfBoundsException e) {
695 break;
696 }
697 fPropManager.setProperty("file.path" + i, path);
698 }
699
700 fPropManager.store();
701 }
702
703
704
705 /**
706 * Load properties from file thru PropertiesManager and apply to
707 * the objects.
708 */
709 private void loadProperties() {
710 // Read and set table columns width and names
711 Vector names = new Vector(initColumnNames());
712 fColumnsMaped = names;
713 TableColumnModel columnModel = fTable.getColumnModel();
714 int columnCount = fPropManager.getIntProperty("columns.count", 8);
715
716 for (int i = 0; i < columnCount; i++) {
717 String name = null;
718 try {
719 name = fPropManager.getProperty("column" + i + ".name",
720 (String) fColumnNames.get(i));
721 } catch(ArrayIndexOutOfBoundsException e) {
722 log.warn("table column names custom configuration problem", e);
723 continue;
724 }
725 int index = names.indexOf(name);
726
727 String element = null;
728 try {
729 element = (String) names.get(index);
730 } catch(ArrayIndexOutOfBoundsException e) {
731 log.warn("table column names custom configuration problem", e);
732 continue;
733 }
734 names.remove(index);
735 names.insertElementAt(element, i);
736 fTable.moveColumn(index, i);
737
738
739 int columnWidth = fPropManager.getIntProperty("column" + i + ".width", 200);
740 TableColumn column = columnModel.getColumn(i);
741 column.setPreferredWidth(columnWidth);
742 }
743
744 // Read Main Window size and position
745 int x = fPropManager.getIntProperty("frame.x", 0);
746 int y = fPropManager.getIntProperty("frame.y", 0);
747 int width = fPropManager.getIntProperty("frame.width", WIDTH);
748 int height = fPropManager.getIntProperty("frame.height", HEIGHT);
749 int tolerance = 5;
750 Rectangle win = new Rectangle(x, y, width, height);
751 Rectangle screen = new Rectangle(new Point(-tolerance, -tolerance),
752 Toolkit.getDefaultToolkit().getScreenSize());
753 screen.grow(tolerance * 2, tolerance * 2);
754 if (screen.contains(win))
755 this.setBounds(win);
756 else {
757 this.setSize(WIDTH, HEIGHT);
758 this.setLocation(0, 0);
759 }
760
761 // Read last extraction directory
762 String path = fPropManager.getProperty("extract.lastFolder");
763 if (path != null)
764 fExtractDir = new File(path);
765 else
766 fExtractDir = Utils.getCurrentDir();
767
768 // Sorting column
769 String column = fPropManager.getProperty("sorting.column");
770 if (column != null) {
771 int index = names.indexOf(column);
772 fTableModel.sortByColumnRefresh(index);
773 }
774
775 // Extraction path history
776 fPathList = new Vector();
777 String firstPath = fPropManager.getProperty("extract.path0");
778 if ((firstPath != null) && (firstPath.length() > 0)) {
779 for (int i = 0; i < fMaxExtractHistory; i++) {
780 path = fPropManager.getProperty("extract.path" + i);
781 if (path == null) break;
782 //log.debug("Add to extract history: " + path);
783 fPathList.add(i, path);
784 }
785 } else
786 fPathList.add(fExtractDir.toString());
787
788 // add last open archives history
789 path = fPropManager.getProperty("file.path0");
790 int size = fFileHistory.size();
791 if ((path != null) && (path.length() > 0)) {
792 for (int i = 0; i < size; i++) {
793 path = fPropManager.getProperty("file.path" + i);
794 if (path == null) break;
795 fFileHistory.add(path, true);
796 }
797 }
798
799 }
800
801
802
803 /**
804 * Load small image by given package path.
805 */
806 private ImageIcon loadImage(String aImage) {
807 URL url = getClass().getResource(aImage);
808 return new ImageIcon(url);
809 }
810
811
812
813 /**
814 * Set message to left or to right side of statusbar at the bottom
815 * of the application.
816 * Use SwingConstants.RIGHT, SwingConstants.LEFT
817 */
818 private void setStatusMessage(String aMessage, int aSide) {
819 switch(aSide) {
820 case SwingConstants.RIGHT: {
821 fRightStatusLabel.setText(aMessage);
822 break;
823 }
824 case SwingConstants.LEFT: {
825 fLeftStatusLabel.setText(aMessage);
826 break;
827 }
828 }
829 }
830
831
832
833 /**
834 * Send message to right side of status bar with default text.
835 * Example: Total 15 files, 134KB
836 */
837 private void setDefaultRightStatusMessage() {
838 setStatusMessage( strTotal +
839 fTableModel.getRowCount() + " " +
840 strFiles +
841 fSupport.getTotalUncompressedSize() / 1024 + "KB",
842 SwingConstants.RIGHT);
843 }
844
845
846
847 /**
848 * Send message to left side of status bar with default text.
849 * Example: Selected 15 files, 134KB
850 */
851 private void setDefaultLeftStatusMessage() {
852 long size = fSupport.getItemsUncompressedSize(fTable.getSelectedRows());
853 String composed;
854
855 if (size < 1024)
856 composed = strSelected +
857 fTable.getSelectedRowCount() + " " +
858 strFiles +
859 size + strBytes;
860 else
861 composed = strSelected +
862 fTable.getSelectedRowCount() + " " +
863 strFiles +
864 size / 1024 + "KB";
865
866 setStatusMessage(composed, SwingConstants.LEFT);
867 }
868
869
870
871 private void showLogDialog() {
872 LogDialog dialog = new LogDialog(Main.this, true);
873 dialog.setText(fOutputManager.getOutput());
874 dialog.show();
875 }
876
877
878
879 /**
880 * This method show monitor dialog in separate thread but only one
881 * thread for monitor is possible in same time.
882 */
883 public synchronized void showMonitor() {
884 this.getGlassPane().setVisible(true);
885 fMonitor.show();
886 }
887
888
889
890 /**
891 * This method hide monitor dialog, terminate and kill monitor thread.
892 */
893 public synchronized void hideMonitor() {
894 this.getGlassPane().setVisible(false);
895 fMonitor.dispose();
896 }
897
898
899
900 /**
901 * Return reference to current operation monitor.
902 */
903 public NeonMonitor getMonitor() {
904 return fMonitor;
905 }
906
907
908
909 /**
910 * Application startup.
911 */
912 public static void main(String[] args) {
913 configLog4j();
914 Main main = new Main();
915 main.show();
916 }
917
918
919
920 /**
921 * Configure Apache Log4j by config file
922 * If config file exists in project home directory then enable logging
923 * by given configuration.
924 * If config file not exists then disable all logging system.
925 */
926 private static void configLog4j() {
927 File conf = new File(Utils.getAppHomeDir(), "log4j.conf");
928 FileInputStream input = null;
929 Properties log4jProps = new Properties();
930 try {
931 input = new FileInputStream(conf);
932 log4jProps.load(input);
933 } catch (Exception e) {
934 /* log4j config missing. and all tracing disabled */
935 BasicConfigurator.configure();
936 Category.getDefaultHierarchy().disableAll();
937 }
938
939 PropertyConfigurator.configure(log4jProps);
940 }
941
942
943
944 /**
945 * Customize TableModel befavior.
946 */
947 private final class ArchiveTableModel extends AbstractTableModel {
948
949 private int fSortCol = 0; // column
950 private boolean fSortAsc = true; // direction
951
952 ArchiveTableModel() {
953 super();
954 }
955
956 /**
957 * Awlays return false and disable cell edit possibilities.
958 */
959 public boolean isCellEditable(int aRow, int aCol) {
960 return false;
961 }
962
963 public int getRowCount() {
964 return fTableData.size();
965 }
966
967 public int getColumnCount() {
968 return fColumnNames.size();
969 }
970
971 public Object getValueAt(int row, int column) {
972 Vector rowVector = (Vector) fTableData.elementAt(row);
973 return rowVector.elementAt(column);
974 }
975
976 public String getColumnName(int column) {
977 String str = super.getColumnName(column);
978 String result = null;
979 if (fColumnNames == null || fColumnNames.size() <= column) {
980 result = str;
981 }
982 Object id = fColumnNames.elementAt(column);
983 if (id == null) {
984 result = str;
985 }
986 else {
987 str = id.toString();
988 result = str;
989 }
990
991 if (column == fSortCol) result += fSortAsc ? " >>" : " <<";
992
993 return result;
994 }
995
996 /**
997 * Check for table row selection.
998 * @return true if has selected rows on the table; false otherwise
999 */
1000 public boolean isSelectedRow() {
1001 return fTable.getSelectedRowCount() > 0;
1002 }
1003
1004
1005
1006 /**
1007 * Check for table data.
1008 * @return true if table is empty (without row data); false otherwise
1009 */
1010 public boolean isTableEmpty() {
1011 return fTable.getRowCount() < 1;
1012 }
1013
1014
1015
1016 /**
1017 * Default ascending sort by first column
1018 */
1019 public void setDefaultSorting() {
1020 Collections.sort(fTableData, new ArchiveComparator(fSortCol
1021 , fSortAsc));
1022 }
1023
1024
1025
1026 /**
1027 * Sort by given column index. (Without JTable refreshing).
1028 */
1029 public void sortByColumn(int aIndex) {
1030 Collections.sort(fTableData, new ArchiveComparator(aIndex
1031 , fSortAsc));
1032 }
1033
1034
1035
1036 /**
1037 * Sort by given column index and refresh JTable.
1038 */
1039 public void sortByColumnRefresh(int aIndex) {
1040 sorting(aIndex);
1041 }
1042
1043
1044
1045 /**
1046 * Return index of sorted column.
1047 */
1048 public int getSortingColumn() {
1049 return fSortCol;
1050 }
1051
1052
1053
1054 private void sorting(int aColumnIndex) {
1055 TableColumnModel colModel = fTable.getColumnModel();
1056 int columnModelIndex = aColumnIndex;
1057 int modelIndex = colModel.getColumn(columnModelIndex).getModelIndex();
1058 if (modelIndex < 0)
1059 return;
1060 if (fSortCol == modelIndex)
1061 fSortAsc = !fSortAsc;
1062 else
1063 fSortCol = modelIndex;
1064
1065 for(int i = 0; i < fColumnNames.size(); i++) {
1066 TableColumn column = colModel.getColumn(i);
1067 column.setHeaderValue(fTableModel.getColumnName(column.getModelIndex()));
1068 }
1069
1070 fTable.getTableHeader().repaint();
1071 Collections.sort(fTableData,
1072 new ArchiveComparator(modelIndex, fSortAsc));
1073 fTable.tableChanged(new TableModelEvent(fTableModel));
1074 fTable.repaint();
1075 }
1076
1077
1078
1079 /**
1080 * If mouse clicked on column header then sort column or
1081 * reverce order of sort.
1082 */
1083 private final class ColumnListener extends MouseAdapter {
1084
1085 public ColumnListener() {
1086 // log.debug("ColumnListener created");
1087 }
1088
1089 public void mouseClicked(MouseEvent e) {
1090 TableColumnModel colModel = fTable.getColumnModel();
1091 int columnIndex = colModel.getColumnIndexAtX(e.getX());
1092 sorting(columnIndex);
1093 }
1094 }
1095
1096
1097
1098 /**
1099 * Comparator for archive data objects.
1100 * Uses current sort column and direction to determine
1101 * what to compare.
1102 */
1103 private final class ArchiveComparator implements Comparator {
1104
1105 private int fSortCol;
1106 private boolean fSortAsc;
1107
1108 DateFormat fDateFormat = DateFormat.getDateInstance();
1109
1110 public ArchiveComparator(int aSortCol, boolean aSortAsc) {
1111 fSortCol = aSortCol;
1112 fSortAsc = aSortAsc;
1113 }
1114
1115 public int compare(Object aObject1, Object aObject2) {
1116 Vector v1 = (Vector) aObject1;
1117 Vector v2 = (Vector) aObject2;
1118
1119 int result = 0;
1120
1121 switch(fSortCol) {
1122 // file name
1123 case 0: {
1124 String str1 = (String) v1.get(fSortCol);
1125 String str2 = (String) v2.get(fSortCol);
1126 result = str1.compareTo(str2);
1127 break;
1128 }
1129 // file type
1130 case 1: {
1131 String str1 = (String) v1.get(fSortCol);
1132 String str2 = (String) v2.get(fSortCol);
1133 result = str1.compareTo(str2);
1134 break;
1135 }
1136 // date
1137 case 2: {
1138 try {
1139 Date date1 = fDateFormat.parse((String) v1.get(fSortCol));
1140 Date date2 = fDateFormat.parse((String) v2.get(fSortCol));
1141 result = date1.compareTo(date2);
1142 } catch (ParseException e) {
1143 log.error("Invalid date while sorting", e);
1144 System.err.println(e);
1145 }
1146 break;
1147 }
1148 // size
1149 case 3: {
1150 double double1 = Double.parseDouble((String) v1.get(fSortCol));
1151 double double2 = Double.parseDouble((String) v2.get(fSortCol));
1152 result = double1 < double2 ? -1 : (double1 > double2 ? 1 : 0);
1153 break;
1154 }
1155 // ratio %%%%
1156 case 4: {
1157 String str1 = ((String) v1.get(fSortCol)).replace('%', ' ');
1158 String str2 = ((String) v2.get(fSortCol)).replace('%', ' ');
1159 double double1 = Double.parseDouble(str1);
1160 double double2 = Double.parseDouble(str2);
1161 result = double1 < double2 ? -1 : (double1 > double2 ? 1 : 0);
1162 break;
1163 }
1164 // packed size
1165 case 5: {
1166 double double1 = Double.parseDouble((String) v1.get(fSortCol));
1167 double double2 = Double.parseDouble((String) v2.get(fSortCol));
1168 result = double1 < double2 ? -1 : (double1 > double2 ? 1 : 0);
1169 break;
1170 }
1171 // CRC
1172 case 6: {
1173 String str1 = (String) v1.get(fSortCol);
1174 String str2 = (String) v2.get(fSortCol);
1175 result = str1.compareTo(str2);
1176 break;
1177 }
1178 // path
1179 case 7: {
1180 String str1 = (String) v1.get(fSortCol);
1181 String str2 = (String) v2.get(fSortCol);
1182 result = str1.compareTo(str2);
1183 break;
1184 }
1185 }
1186 if (!fSortAsc)
1187 result = -result;
1188 return result;
1189 }
1190
1191 public boolean equals(Object aObject) {
1192 return false;
1193 }
1194 }
1195 }
1196
1197
1198
1199 /**
1200 * Custom cell renderer behaviour - never draw selection.
1201 */
1202 private final class ArchiveTableCellRenderer extends DefaultTableCellRenderer {
1203
1204 public Component getTableCellRendererComponent( JTable table,
1205 Object value,
1206 boolean isSelected,
1207 boolean hasFocus,
1208 int row,
1209 int column) {
1210 return super.getTableCellRendererComponent( table,
1211 value,
1212 false,
1213 false,
1214 row,
1215 column);
1216 }
1217 }
1218
1219
1220
1221 /**
1222 * Extends JTable and disable selection of all columns except "Names".
1223 */
1224 public final class ArchiveTable extends JTable
1225 implements DropTargetListener {
1226
1227 private DropTarget fDropTarget = new DropTarget(this, this);
1228
1229 public ArchiveTable(Vector aRowData, Vector aColumnNames) {
1230 super(aRowData, aColumnNames);
1231 }
1232
1233
1234
1235 public ArchiveTable(TableModel tm) {
1236 super(tm);
1237 }
1238
1239
1240
1241 public ArchiveTable() {
1242 super();
1243 }
1244
1245
1246
1247 protected void processMouseEvent(MouseEvent e) {
1248 int view = this.columnAtPoint(new Point(e.getX(), e.getY()));
1249 int index = this.convertColumnIndexToModel(view);
1250 TableColumn column = this.getColumn("name");
1251 int nameIndex = column.getModelIndex();
1252 if (index != nameIndex) return;
1253 if (e.getID() == MouseEvent.MOUSE_CLICKED || e.getID() == MouseEvent.MOUSE_PRESSED) {
1254 }
1255 super.processMouseEvent(e);
1256 }
1257
1258
1259
1260 protected void processMouseMotionEvent(MouseEvent e) {
1261 int view = this.columnAtPoint(new Point(e.getX(), e.getY()));
1262 int index = this.convertColumnIndexToModel(view);
1263 TableColumn column = this.getColumn("name");
1264 int nameIndex = column.getModelIndex();
1265 if (index != nameIndex) return;
1266
1267 super.processMouseMotionEvent(e);
1268 }
1269
1270
1271
1272 public void dragEnter(DropTargetDragEvent e) {
1273 }
1274
1275
1276
1277 public void dragDropEnd(DragSourceDropEvent e) {
1278 // log.debug("dragDropEnd() " + e.toString());
1279 // log.debug("isDropSuccess: " + e.getDropSuccess());
1280 }
1281
1282
1283
1284 public void dragOver(DropTargetDragEvent e) {
1285 }
1286
1287
1288
1289 public void dragExit(DropTargetEvent e) {
1290 }
1291
1292
1293
1294 public void dropActionChanged(DropTargetDragEvent e) {
1295 }
1296
1297
1298
1299 /**
1300 * Provide Native to JTable drag and drop operation.
1301 */
1302 public synchronized void drop(DropTargetDropEvent e) {
1303 log.debug("drop() " + e.toString());
1304 try {
1305 Transferable tr = e.getTransferable();
1306 if (tr.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
1307 e.acceptDrop(DnDConstants.ACTION_COPY);
1308 java.util.List files = (java.util.List)
1309 tr.getTransferData(DataFlavor.javaFileListFlavor);
1310 final File[] fileArray = new File[files.size()];
1311 Iterator iterator = files.iterator();
1312 for (int i = 0; i < files.size(); i++) {
1313 File file = (File) files.get(i);
1314 log.debug("droped file: " + file.toString());
1315 fileArray[i] = file;
1316 }
1317 e.getDropTargetContext().dropComplete(true);
1318
1319 // check if droped file is supported archive; if this is
1320 // true then open vs. add
1321 File firstFile = fileArray[0];
1322 String extension = Utils.getExtension(firstFile.toString());
1323 Arrays.sort(ArchiveSupport.TYPES);
1324 int result = Arrays.binarySearch(ArchiveSupport.TYPES, extension.toLowerCase());
1325 log.debug("Search result: " + result);
1326 if (result >= 0) {
1327 Main.this.refreshTable(firstFile);
1328 return;
1329 }
1330 //
1331
1332 final File currentDir = searchCurrentDir(fileArray);
1333 log.debug("source dir: " + currentDir.toString());
1334 DragAddRunnable dragAdd =
1335 new DragAddRunnable(fileArray,
1336 currentDir,
1337 fSupport.getCurrentOpen(),
1338 null,
1339 AddDialog.ADD_ACTION,
1340 AddDialog.COMPRESSION_9,
1341 true,
1342 false);
1343 Thread thread = new Thread(dragAdd);
1344 thread.start();
1345 } else
1346 e.rejectDrop();
1347 } catch (IOException exc) {
1348 e.rejectDrop();
1349 log.info("drop() exception", exc);
1350 } catch (UnsupportedFlavorException exc) {
1351 e.rejectDrop();
1352 log.info("drop() exception", exc);
1353 }
1354
1355 }
1356
1357
1358
1359 /**
1360 * This method looking for current directory in DnD file list.
1361 * Of course the smaller dir is the current.
1362 */
1363 private File searchCurrentDir(File[] aFileList) {
1364 File smaller = aFileList[0];
1365 for (int i = 1; i < aFileList.length; i++) {
1366 File file = aFileList[i];
1367 int result = file.compareTo(smaller);
1368 if (result < 0) smaller = file;
1369 }
1370
1371 if (!smaller.isDirectory())
1372 return smaller.getParentFile();
1373 else
1374 return smaller;
1375 }
1376
1377
1378
1379 private final class DragAddRunnable implements Runnable {
1380
1381 private File[] fFileList;
1382 private File fCurrentDir;
1383 private File fArchive;
1384 private String fFilter;
1385 private int fAction;
1386 private int fCompression;
1387 private boolean fSubfolders;
1388 private boolean fPathinfo;
1389
1390 DragAddRunnable(File[] aFileList,
1391 File aCurrentDir,
1392 File aArchive,
1393 String aFilter,
1394 int aAction,
1395 int aCompression,
1396 boolean aSubfolders,
1397 boolean aPathinfo) {
1398
1399 fFileList = aFileList;
1400 fCurrentDir = aCurrentDir;
1401 fArchive = aArchive;
1402 fFilter = aFilter;
1403 fAction = aAction;
1404 fCompression = aCompression;
1405 fSubfolders = aSubfolders;
1406 fPathinfo = aPathinfo;
1407 }
1408
1409 public void run() {
1410 fSupport.add( fFileList,
1411 fCurrentDir,
1412 fArchive,
1413 fFilter,
1414 fAction,
1415 fCompression,
1416 fSubfolders,
1417 fPathinfo);
1418 refreshTable(fSupport.getCurrentOpen());
1419 }
1420
1421 }
1422
1423
1424
1425 } // End of ArchiveTable
1426
1427
1428
1429 /**
1430 * Viewport component that accept drop operations.
1431 * Support dnd and open of file from native to the scroll pane viewport.
1432 */
1433 private final class ViewportDropTargetListener extends DropTarget implements
1434 DropTargetListener {
1435
1436 ViewportDropTargetListener() {
1437 }
1438
1439 public void dragEnter(DropTargetDragEvent e) {
1440 }
1441
1442 public void dragExit(DropTargetEvent e) {
1443 }
1444
1445 public void dragOver(DropTargetDragEvent e) {
1446 }
1447
1448 public void drop(DropTargetDropEvent e) {
1449 try {
1450 Transferable tr = e.getTransferable();
1451 if (tr.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
1452 e.acceptDrop(DnDConstants.ACTION_COPY);
1453 java.util.List files = (java.util.List)
1454 tr.getTransferData(DataFlavor.javaFileListFlavor);
1455 File file = (File) files.get(0);
1456 e.getDropTargetContext().dropComplete(true);
1457 Main.this.refreshTable(file);
1458 } else
1459 e.rejectDrop();
1460 } catch (IOException exc) {
1461 e.rejectDrop();
1462 log.info("drop() exception", exc);
1463 } catch (UnsupportedFlavorException exc) {
1464 e.rejectDrop();
1465 log.info("drop() exception", exc);
1466 }
1467 }
1468
1469 public void dropActionChanged(DropTargetDragEvent e) {
1470 }
1471 }
1472
1473
1474
1475 /**
1476 * Listener for mouse events linked to Component(for test needs).
1477 */
1478 private final class TestMouseListener implements MouseListener {
1479
1480 TestMouseListener() {
1481 }
1482
1483 public void mousePressed(MouseEvent e) {
1484 log.debug("Mouse Pressed");
1485 }
1486
1487 public void mouseReleased(MouseEvent e) {
1488 }
1489
1490 public void mouseEntered(MouseEvent e) {
1491 }
1492
1493 public void mouseExited(MouseEvent e) {
1494 }
1495
1496 public void mouseClicked(MouseEvent e) {
1497 }
1498 }
1499
1500
1501
1502 /**
1503 * Options menu listener.
1504 */
1505 private final class OptionsMenuListener implements MenuListener {
1506
1507 private OptionsMenuListener() {
1508 }
1509
1510 public void menuCanceled(MenuEvent e) {
1511 }
1512
1513 public void menuDeselected(MenuEvent e) {
1514 }
1515
1516 public void menuSelected(MenuEvent e) {
1517 lastOutputAction.setEnabled(!fOutputManager.isEmpty());
1518 }
1519
1520 }
1521
1522
1523
1524 private final class RecentFilesMenuListener implements MenuListener {
1525
1526 private RecentFilesMenuListener() {
1527 }
1528
1529 public void menuCanceled(MenuEvent e) {
1530 }
1531
1532 public void menuDeselected(MenuEvent e) {
1533 }
1534
1535 public void menuSelected(MenuEvent e) {
1536 fFileHistory.drawToMenu();
1537 }
1538 }
1539
1540
1541
1542 /**
1543 * Sorting menu listener.
1544 */
1545 private final class SortingMenuListener implements MenuListener {
1546
1547 private SortingMenuListener() {
1548 }
1549
1550 public void menuCanceled(MenuEvent e) {
1551 }
1552
1553 public void menuDeselected(MenuEvent e) {
1554 }
1555
1556 public void menuSelected(MenuEvent e) {
1557 JMenu menu = (JMenu) e.getSource();
1558 ArrayList list = new ArrayList(8);
1559 list.add("Name");
1560 list.add("Type");
1561 list.add("Modified");
1562 list.add("Size");
1563 list.add("Ratio");
1564 list.add("Packed");
1565 list.add("CRC");
1566 list.add("Path");
1567
1568 String sorted = (String)
1569 fColumnNames.get(fTableModel.getSortingColumn());
1570
1571 int index = list.indexOf(sorted);
1572
1573 JRadioButtonMenuItem subMenu = (JRadioButtonMenuItem) menu.getMenuComponent(index);
1574 subMenu.setSelected(true);
1575 }
1576
1577 }
1578
1579
1580
1581 private final class RecentFilesAction extends AbstractAction {
1582 RecentFilesAction() {
1583 super();
1584 }
1585
1586 public void actionPerformed(ActionEvent e) {
1587 JMenuItem item = (JMenuItem) e.getSource();
1588 String filename = item.getText();
1589 File file = new File(filename);
1590 fFileHistory.add(filename, false);
1591 refreshTable(file);
1592 }
1593 }
1594
1595
1596
1597 /**
1598 * Exit menu action.
1599 */
1600 private final class ExitAction extends AbstractAction {
1601 ExitAction() {
1602 super(fResources.getString("key.menu.exit"));
1603 }
1604
1605 public void actionPerformed(ActionEvent aEvent) {
1606 Main.this.appClosing(new WindowEvent(Main.this, 0));
1607 }
1608 }
1609
1610
1611
1612 /**
1613 * Sorting menu action.
1614 */
1615 private final class SortingAction extends AbstractAction {
1616
1617 SortingAction() {
1618 }
1619
1620 public void actionPerformed(ActionEvent aEvent) {
1621 JRadioButtonMenuItem menu = (JRadioButtonMenuItem) aEvent.getSource();
1622 String command = menu.getActionCommand();
1623 int index = fColumnsMaped.indexOf(command);
1624 fTableModel.sortByColumnRefresh(index);
1625 }
1626 }
1627
1628
1629
1630 /**
1631 * Open Archive menu action
1632 */
1633 private final class OpenArchiveAction extends AbstractAction {
1634 OpenArchiveAction() {
1635 super(fResources.getString("key.menu.open_archive") + "...");
1636 }
1637
1638 public void actionPerformed(ActionEvent aEvent) {
1639 OpenRunnable runnable = new OpenRunnable();
1640 Thread thread = new Thread(runnable);
1641 thread.start();
1642 }
1643 }
1644
1645
1646
1647 /**
1648 * New Archive menu action
1649 */
1650 private final class NewArchiveAction extends AbstractAction {
1651 NewArchiveAction() {
1652 super(fResources.getString("key.menu.new_archive") + "...");
1653 }
1654
1655 public void actionPerformed(ActionEvent aEvent) {
1656 JFileChooser chooser = new JFileChooser();
1657 chooser.setDialogTitle(fResources.getString("key.title.new_archive"));
1658 NeonFileFilter filter = new NeonFileFilter();
1659 filter.addExtension("zip");
1660 filter.setDescription("ZIP " + fResources.getString("key.title.archives"));
1661 chooser.setFileFilter(filter);
1662 JPanel accessory = new JPanel();
1663 JCheckBox checkbox = new JCheckBox(fResources.getString("key.checkbox.add_dialog"));
1664 accessory.add(checkbox);
1665 chooser.setAccessory(accessory);
1666 int result = chooser.showDialog(Main.this, fResources.getString("key.button.new_archive"));
1667 if (result == JFileChooser.APPROVE_OPTION) {
1668 File file = chooser.getSelectedFile();
1669 String newfile = Utils.addExtension(file.toString(), "zip");
1670 fSupport.create(new File(newfile));
1671 setTitle(TITLE + " - " + Utils.removePath(newfile));
1672 // if add dialog checkbox is checked
1673 if (checkbox.isSelected()) {
1674 new AddAction().actionPerformed(new ActionEvent(this, 0, "invoke addactiom"));
1675 }
1676 }
1677 }
1678 }
1679
1680
1681
1682 /**
1683 * Close archive menu action
1684 */
1685 private final class CloseArchiveAction extends AbstractAction {
1686 CloseArchiveAction() {
1687 super(fResources.getString("key.menu.close_archive"));
1688 }
1689
1690 public void actionPerformed(ActionEvent aEvent) {
1691 fSupport.close();
1692 fTableData.clear();
1693 fTable.updateUI();
1694 setTitle(TITLE);
1695 }
1696 }
1697
1698
1699
1700 /**
1701 * Add menu action
1702 */
1703 private final class AddAction extends AbstractAction {
1704 AddAction() {
1705 super(fResources.getString("key.menu.add") + "...");
1706 }
1707
1708 public void actionPerformed(ActionEvent aEvent) {
1709 AddRunnable runnable = new AddRunnable();
1710 Thread thread = new Thread(runnable);
1711 thread.start();
1712 }
1713 }
1714
1715
1716
1717 /**
1718 * Delete menu action
1719 */
1720 private final class DeleteAction extends AbstractAction {
1721 DeleteAction() {
1722 super(fResources.getString("key.menu.delete") + "...");
1723 }
1724
1725 public void actionPerformed(ActionEvent aEvent) {
1726 DeleteRunnable runnable = new DeleteRunnable();
1727 Thread thread = new Thread(runnable);
1728 thread.start();
1729 }
1730 }
1731
1732
1733
1734 /**
1735 * Extract menu action
1736 */
1737 private final class ExtractAction extends AbstractAction {
1738 ExtractAction() {
1739 super(fResources.getString("key.menu.extract") + "...");
1740 }
1741
1742 public void actionPerformed(ActionEvent aEvent) {
1743 ExtractRunnable runnable = new ExtractRunnable();
1744 Thread thread = new Thread(runnable);
1745 thread.start();
1746 }
1747
1748
1749 }
1750
1751
1752
1753 /**
1754 * Select All menu action.
1755 */
1756 public final class SelectAllAction extends AbstractAction {
1757
1758 SelectAllAction() {
1759 super(fResources.getString("key.menu.select_all"));
1760 }
1761
1762 public void actionPerformed(ActionEvent aEvent) {
1763 if (fTableModel.isTableEmpty()) return;
1764 fTable.selectAll();
1765 }
1766
1767 }
1768
1769
1770
1771 /**
1772 * Invert selection action.
1773 */
1774 public final class InvertSelectionAction extends AbstractAction {
1775
1776 InvertSelectionAction() {
1777 super(fResources.getString("key.menu.invert_selection"));
1778 }
1779
1780 public void actionPerformed(ActionEvent aEvent) {
1781 int total_count = fTable.getRowCount();
1782 ArrayList selected = new ArrayList(total_count);
1783 ArrayList unselected = new ArrayList(total_count);
1784 // sort
1785 for (int i = 0; i < total_count; i++) {
1786 boolean selection = fTable.isRowSelected(i);
1787 if (selection)
1788 selected.add(new Integer(i));
1789 else
1790 unselected.add(new Integer(i));
1791 }
1792
1793 // deselect selection
1794 for (int i = 0; i < selected.size(); i++) {
1795 int index = ((Integer) selected.get(i)).intValue();
1796 fTable.removeRowSelectionInterval(index, index);
1797 }
1798
1799 // select unselected
1800 for (int i = 0; i < unselected.size(); i++) {
1801 int index = ((Integer) unselected.get(i)).intValue();
1802 fTable.addRowSelectionInterval(index, index);
1803 }
1804 }
1805
1806 }
1807
1808
1809
1810 /**
1811 * View Last Output action.
1812 */
1813 public final class LastOutputAction extends AbstractAction {
1814
1815 LastOutputAction() {
1816 super(fResources.getString("key.menu.view_last_output") + "...");
1817 }
1818
1819 public void actionPerformed(ActionEvent e) {
1820 showLogDialog();
1821 }
1822 }
1823
1824
1825
1826 private final class LastOpenHistoryAction extends AbstractAction {
1827
1828 LastOpenHistoryAction() {
1829 super();
1830 }
1831
1832 public void actionPerformed(ActionEvent e) {
1833
1834 }
1835
1836 }
1837
1838
1839
1840 /**
1841 * Test the integrity of current open archive.
1842 */
1843 private final class TestIntegrityAction extends AbstractAction {
1844
1845 TestIntegrityAction() {
1846 super(fResources.getString("key.menu.test_integrity"));
1847 }
1848
1849 public void actionPerformed(ActionEvent e) {
1850 TestIntegrityRunnable runnable = new TestIntegrityRunnable();
1851 Thread thread = new Thread(runnable);
1852 thread.start();
1853 }
1854
1855 }
1856
1857
1858
1859 /**
1860 * Extract action executed in separate thread
1861 */
1862 private final class ExtractRunnable implements Runnable {
1863
1864 ExtractRunnable() {
1865 }
1866
1867 public void run() {
1868 if (!Utils.isFileExists(fExtractDir.toString()))
1869 fExtractDir = Utils.getCurrentDir();
1870 ExtractDialog extract = new ExtractDialog(Main.this, true,
1871 fExtractDir, fPathList);
1872 extract.setSelectedFilesEnabled(fTableModel.isSelectedRow());
1873 extract.setSelectedFilesSelected(fTableModel.isSelectedRow());
1874 extract.setAllFilesSelected(!fTableModel.isSelectedRow());
1875 extract.setUseFolder(true);
1876 extract.show();
1877
1878 // user approved extraction
1879 if (extract.isApproved()) {
1880 // extraction path
1881 fExtractDir = extract.getExtractionPath();
1882 File destDir = extract.getExtractionPath();
1883
1884 if (!fPathList.contains(fExtractDir.toString()))
1885 fPathList.add(0, fExtractDir.toString());
1886 // filter only for the first SIX items
1887 if (fPathList.size() > fMaxExtractHistory)
1888 fPathList = new Vector(fPathList.subList(0, fMaxExtractHistory));
1889
1890 boolean overwriteExisting = extract.isOverwriteExistingChecked();
1891 boolean skipOlder = extract.isSkipOlderChecked();
1892 boolean useFolder = extract.isUseFolderChecked();
1893
1894 // validate destination directory
1895 if ((!destDir.isDirectory()) || (!destDir.exists())) {
1896 Utils.sayInformation(fResources.getString("key.message.dir_not_exists"),
1897 Main.this);
1898 return;
1899 }
1900
1901 // if user select files by filter - select on the table first
1902 if (extract.isFilteredFilesSelected()) {
1903 fTable.clearSelection();
1904 RE filter = Utils.getFileFilter(extract.getFileFilter());
1905 ArrayList filenames = fSupport.getAllFilenames();
1906 for (int i = 0; i < filenames.size(); i++) {
1907 String name = (String) filenames.get(i);
1908 if (filter.isMatch(name))
1909 fTable.addRowSelectionInterval(i, i);
1910 }
1911 fSupport.extract( fTable.getSelectedRows(),
1912 destDir,
1913 overwriteExisting,
1914 skipOlder,
1915 useFolder);
1916 }
1917
1918 // if user select all files option
1919 if (extract.isAllFilesSelected()) {
1920 int count = fTable.getRowCount();
1921 int[] indexes = new int[count];
1922 for (int i = 0; i < count; i++) {
1923 indexes[i] = i;
1924 }
1925 fSupport.extract( indexes,
1926 destDir,
1927 overwriteExisting,
1928 skipOlder,
1929 useFolder);
1930 }
1931
1932 // if extract user selection
1933 if (extract.isSelectedFilesSelected()) {
1934 if (fTableModel.isSelectedRow()) {
1935 fSupport.extract( fTable.getSelectedRows(),
1936 destDir,
1937 overwriteExisting,
1938 skipOlder,
1939 useFolder);
1940 }
1941 }
1942 }
1943 }
1944
1945 }// End of ExtractRunnable
1946
1947
1948
1949 /**
1950 * Open action executed in separate thread
1951 */
1952 private final class OpenRunnable implements Runnable {
1953
1954 OpenRunnable() {
1955 }
1956
1957 public void run() {
1958 File file = null;
1959 JFileChooser chooser = new JFileChooser();
1960 chooser.setDialogTitle(fResources.getString("key.title.open_archive"));
1961 NeonFileFilter filter = new NeonFileFilter();
1962 filter.addExtension("zip");
1963 filter.setDescription("ZIP " + fResources.getString("key.title.archives"));
1964 chooser.setFileFilter(filter);
1965 int value = chooser.showOpenDialog(Main.this);
1966
1967 if (value == JFileChooser.APPROVE_OPTION) {
1968 file = chooser.getSelectedFile();
1969
1970 // if file does not exists
1971 if (!file.exists()) {
1972 Utils.sayWarning(fResources.getString("key.message.file_not_exists"), Main.this);
1973 return;
1974 }
1975
1976 // clear old table content
1977 fTableData.clear();
1978 // clear previous selection
1979 ListSelectionModel tableSelection = fTable.getSelectionModel();
1980 tableSelection.setLeadSelectionIndex(0);
1981 tableSelection.clearSelection();
1982
1983 Vector zipData = null;
1984
1985 // invoke archive support interface
1986 try {
1987 zipData = fSupport.open(file);
1988 } catch (Exception e) {
1989 log.error("Error open file", e);
1990 }
1991
1992 // test sorting code
1993 //Collections.sort(zipData);
1994 // end of test and sorting code
1995
1996 fTableData.addAll(zipData);
1997 fTableModel.setDefaultSorting();
1998 fTable.updateUI();
1999 setTitle(TITLE + " - " + Utils.removePath(file.toString()));
2000
2001 // add to FileMenu history
2002 fFileHistory.add(file.toString(), false);
2003 ////
2004
2005 Main.this.setDefaultRightStatusMessage();
2006 }
2007 }
2008
2009 }
2010
2011
2012
2013 /**
2014 * Delete action executed in separate thread
2015 */
2016 private final class DeleteRunnable implements Runnable {
2017
2018 DeleteRunnable() {
2019 }
2020
2021 public void run() {
2022 DeleteDialog dialog = new DeleteDialog(Main.this, true);
2023 dialog.setEntireArchiveSelected(!fTableModel.isSelectedRow());
2024 dialog.setSelectedFilesSelected(fTableModel.isSelectedRow());
2025 dialog.setSelectedFilesEnabled(fTableModel.isSelectedRow());
2026 dialog.show();
2027
2028 if (dialog.isApproved()) {
2029 if (!fSupport.hasOpenFile()) return;
2030 // if user select files by filter - select on the table first
2031 if (dialog.isFilteredFilesSelected()) {
2032 fTable.clearSelection();
2033 RE filter = Utils.getFileFilter(dialog.getFileFilter());
2034 ArrayList filenames = fSupport.getAllFilenames();
2035 for (int i = 0; i < filenames.size(); i++) {
2036 String name = (String) filenames.get(i);
2037 if (filter.isMatch(name))
2038 fTable.addRowSelectionInterval(i, i);
2039 }
2040 fSupport.delete(fTable.getSelectedRows());
2041 }
2042 // if user select delete entire archive
2043 if (dialog.isEntireArchiveSelected()) {
2044 int count = fTable.getRowCount();
2045 int[] indexes = new int[count];
2046 for (int i = 0; i < count; i++) {
2047 indexes[i] = i;
2048 }
2049 fSupport.delete(indexes);
2050 }
2051 // if user select delete only selected files
2052 if (dialog.isSelectedFilesSelected()) {
2053 fSupport.delete(fTable.getSelectedRows());
2054 }
2055 }
2056 }
2057
2058 }// End of DeleteRunnable
2059
2060
2061
2062 /**
2063 * Add action executed in separated thread
2064 */
2065 private final class AddRunnable implements Runnable {
2066
2067 AddRunnable() {
2068 }
2069
2070 public void run() {
2071 AddDialog dialog = new AddDialog(Main.this, true);
2072 dialog.show();
2073
2074 File current = fSupport.getCurrentOpen();
2075 if (current == null) return;
2076 Date date_before = new Date(current.lastModified());
2077 // if simple button is clicked
2078 if (dialog.isSimpleActionSelected()) {
2079 fSupport.add( dialog.getSelectedFiles(),
2080 dialog.getCurrentDirectory(),
2081 fSupport.getCurrentOpen(),
2082 null,
2083 dialog.getAction(),
2084 dialog.getCompression(),
2085 dialog.isSubfoldersSelected(),
2086 dialog.isFullpathSelected());
2087
2088 }
2089
2090 // if add by regular expression button is clicked
2091 if (dialog.isFilterActionSelected()) {
2092 fSupport.add( dialog.getSelectedFiles(),
2093 dialog.getCurrentDirectory(),
2094 fSupport.getCurrentOpen(),
2095 dialog.getFileFilter(),
2096 dialog.getAction(),
2097 dialog.getCompression(),
2098 dialog.isSubfoldersSelected(),
2099 dialog.isFullpathSelected());
2100 }
2101 Date date_after = new Date(current.lastModified());
2102 if (log.isDebugEnabled()) {
2103 log.debug("Date before modification: " + date_before.toString());
2104 log.debug("Date after modification: " + date_after.toString());
2105 log.debug("Dates equals?: " + date_after.equals(date_before));
2106 }
2107 if (!date_after.equals(date_before)) {
2108 refreshTable(current);
2109 }
2110 }
2111
2112 } // End of AddRunnable
2113
2114
2115
2116 /**
2117 * Test of archive integrity running in separate thread.
2118 */
2119 private final class TestIntegrityRunnable implements Runnable {
2120
2121 TestIntegrityRunnable() {
2122 }
2123
2124 public void run() {
2125 if (!fSupport.hasOpenFile()) return;
2126 boolean result = fSupport.test();
2127 if (result) {
2128 fOutputManager.insertFirst(fResources.getString("key.message.no_errors_detected_in_compressed_data"));
2129 //fOutputManager.insertFirst("" + '\n');
2130 } else {
2131 fOutputManager.insertFirst(fResources.getString("key.message.errors_where_detected_in_compressed_data"));
2132 //fOutputManager.insertFirst("" + '\n');
2133 }
2134 SwingUtilities.invokeLater(new Runnable() {
2135 public void run() {
2136 showLogDialog();
2137 }
2138 });
2139 }
2140 }
2141
2142
2143
2144 /**
2145 * Manager for Menu File last opens history.
2146 */
2147 private final class FileHistoryManager {
2148
2149 private final static int fMaxSize = 6;
2150
2151 private Vector fHistoryList;
2152
2153 FileHistoryManager() {
2154 fHistoryList = new Vector();
2155 }
2156
2157
2158
2159 /**
2160 * Maximum size of this history list.
2161 */
2162 public int size() {
2163 return fMaxSize;
2164 }
2165
2166
2167
2168 /**
2169 * Append path to the beginning of the list.
2170 * This method watch the list can't exceed maximum size
2171 */
2172 public void add(String aPath, boolean aReverse) {
2173 if (!fHistoryList.contains(aPath)) {
2174 if (!aReverse)
2175 fHistoryList.add(0, aPath);
2176 else
2177 fHistoryList.add(fHistoryList.size(), aPath);
2178 } else { // if array already contains this item
2179 int index = fHistoryList.indexOf(aPath);
2180 fHistoryList.add(0, fHistoryList.remove(index));
2181 }
2182 if (fHistoryList.size() > fMaxSize)
2183 fHistoryList = new Vector(fHistoryList.subList(0, fMaxSize));
2184 }
2185
2186
2187
2188 /**
2189 * Get path string at the specified index.
2190 */
2191 public String get(int aIndex) {
2192 return (String) fHistoryList.get(aIndex);
2193 }
2194
2195
2196
2197 /**
2198 * Draw this history list to FileMenu.
2199 */
2200 public void drawToMenu() {
2201 fMenuRecent.removeAll();
2202 int size = fFileHistory.size();
2203 for (int i = 0; i < size; i++) {
2204 String file = null;
2205 try {
2206 file = fFileHistory.get(i);
2207 } catch(ArrayIndexOutOfBoundsException e) {
2208 break;
2209 }
2210 //fMenuRecent.insert(fFileHistory.get(i), 0);
2211 JMenuItem item = new JMenuItem(new RecentFilesAction());
2212 item.setText(fFileHistory.get(i));
2213 fMenuRecent.add(item);
2214 }
2215 }
2216
2217 }
2218
2219
2220
2221 /**
2222 * Component that disable all input events.
2223 */
2224 private class GlassComponent extends JComponent implements
2225 AWTEventListener {
2226
2227 Window parentWindow;
2228
2229 public GlassComponent() {
2230 super();
2231 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
2232 setOpaque(false);
2233
2234 addMouseListener(new MouseAdapter() {});
2235 }
2236
2237 /**
2238 * Override setVisible to install/remove key events hook
2239 that will allow us to
2240 * disable key events when the glass pane is visible.
2241 */
2242 public void setVisible(boolean visible) {
2243 if (visible) {
2244 if (this.parentWindow == null)
2245 this.parentWindow =
2246 SwingUtilities.windowForComponent(this);
2247
2248 Toolkit.getDefaultToolkit().addAWTEventListener
2249 (this, AWTEvent.KEY_EVENT_MASK);
2250 }
2251 else {
2252 Toolkit.getDefaultToolkit().removeAWTEventListener
2253 (this);
2254 }
2255 super.setVisible(visible);
2256 }
2257
2258 /**
2259 * Called whenever ther is an event in AWT queue. Note that
2260 the current implementation
2261 * skips all key events, not just events for this window.
2262 Logic can be enhanced to examine
2263 * the source of the event and it's parent window and skip
2264 only those events
2265 * that originated from disabled window
2266 */
2267 public void eventDispatched(AWTEvent event) {
2268 if (event instanceof KeyEvent && event.getSource()
2269 instanceof Component) {
2270 if (SwingUtilities.windowForComponent((Component)
2271 event.getSource()) == this.parentWindow)
2272 // Consume events only for our window
2273 ((KeyEvent)event).consume();
2274 }
2275 }
2276 }
2277
2278
2279
2280 }