OSDN Git Service

Add new source files
[armadillo/armadillo1.git] / src / jp / sfjp / armadillo / ui / window / MainWindow.java
1 package jp.sfjp.armadillo.ui.window;
2
3 import static jp.sfjp.armadillo.ui.window.MainWindow.ActionMode.*;
4 import static jp.sfjp.armadillo.ui.window.MainWindow.ActionMode.List;
5 import java.awt.*;
6 import java.awt.datatransfer.*;
7 import java.awt.dnd.*;
8 import java.awt.event.*;
9 import java.io.*;
10 import java.lang.Thread.UncaughtExceptionHandler;
11 import java.util.*;
12 import java.util.List;
13 import java.util.concurrent.*;
14 import javax.swing.*;
15 import jp.sfjp.armadillo.*;
16 import jp.sfjp.armadillo.archive.*;
17
18 public final class MainWindow extends JFrame {
19
20     static Logger log = Logger.getLogger(MainWindow.class);
21     static ResourceManager res = ResourceManager.getInstance("jp.sfjp.armadillo.ui.messages");
22     static Color doneBGC = Color.decode("#99ff99");
23
24     private final Executor executor;
25     private final Box box;
26     private final JComboBox archiveTypeCombo;
27     private final JCheckBox listMode;
28
29     public MainWindow() {
30         setTitle(getTitleString());
31         setDefaultCloseOperation(EXIT_ON_CLOSE);
32         this.executor = Executors.newCachedThreadPool();
33         this.box = Box.createVerticalBox();
34         this.archiveTypeCombo = new JComboBox(getArchiveExtensions());
35         this.listMode = new JCheckBox();
36         setDropTarget(new DropTarget(this, new DropTargetAdapter() {
37             @Override
38             public void drop(DropTargetDropEvent dtde) {
39                 Transferable t = dtde.getTransferable();
40                 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
41                     dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
42                     List<File> fileList;
43                     try {
44                         @SuppressWarnings("unchecked")
45                         List<File> o = (List<File>)t.getTransferData(DataFlavor.javaFileListFlavor);
46                         fileList = o;
47                     }
48                     catch (Exception ex) {
49                         if (ex instanceof RuntimeException)
50                             throw (RuntimeException)ex;
51                         throw new RuntimeException(ex);
52                     }
53                     for (final File file : fileList)
54                         startAction(file);
55                 }
56             }
57         }));
58         JScrollPane sp = new JScrollPane(box,
59                                          ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
60                                          ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
61         JPanel toolbar = new JPanel(new FlowLayout());
62         toolbar.add(new JButton(new AbstractAction(res.get("button.closeFinishedProgressPanel")) {
63             @Override
64             public void actionPerformed(ActionEvent e) {
65                 clearFinishedProgressPanel();
66             }
67         }));
68         toolbar.add(new JLabel("    "));
69         toolbar.add(new JLabel(res.get("label.extensionToCreateArchive")));
70         toolbar.add(archiveTypeCombo);
71         toolbar.add(new JLabel("    "));
72         toolbar.add(new JLabel(res.get("label.listMode")));
73         toolbar.add(listMode);
74         archiveTypeCombo.setPreferredSize(new Dimension(60, 20));
75         add(toolbar, BorderLayout.NORTH);
76         add(sp, BorderLayout.CENTER);
77     }
78
79     static String getTitleString() {
80         return res.get(".title", ArmadilloCommands.getVersionString());
81     }
82
83     static Object[] getArchiveExtensions() {
84         String[] extensions = res.get("extensionsToCreateArchive").split(",", -1);
85         List<String> a = new ArrayList<String>(extensions.length);
86         for (String extension : extensions)
87             a.add("." + extension);
88         return a.toArray();
89     }
90
91     void createArchive(File srcdir, File dst, ProgressNotifier notifier) throws IOException {
92         log.setEnteredMethodName("createArchive");
93         log.debug("start to create archive %s into %s", dst, srcdir);
94         ArmadilloCommands.createArchive(srcdir, dst, notifier);
95         log.trace("done");
96         log.atExit();
97     }
98
99     void showInfoListWindow(File src, ProgressNotifier progressNotifier) throws IOException {
100         log.setEnteredMethodName("showInfoListWindow");
101         log.debug("start to show list of archive %s", src);
102         ArchiveType type = ArchiveType.of(src.getName());
103         ListDialog d = new ListDialog(this, src, type, progressNotifier);
104         d.setSize(600, 400);
105         d.setModal(false);
106         d.setVisible(true);
107         log.trace("done");
108         log.atExit();
109     }
110
111     void extractArchive(File src, File dstdir, ProgressNotifier progressNotifier) throws IOException {
112         log.setEnteredMethodName("extractArchive");
113         log.debug("start to extract all files from archive %s into %s", src, dstdir);
114         ArmadilloCommands.extractAll(src, dstdir, progressNotifier);
115         log.trace("done");
116         log.atExit();
117     }
118
119     void extractArchive(File src,
120                         File dstdir,
121                         Set<String> targets,
122                         ProgressNotifier progressNotifier) throws IOException {
123         log.setEnteredMethodName("extractArchive(+targets)");
124         log.debug("start to extract %d files from archive %s into %s", targets.size(), src, dstdir);
125         ArmadilloCommands.extract(src, dstdir, targets, progressNotifier);
126         log.trace("done");
127         log.atExit();
128     }
129
130     ActionMode detectMode(File file) {
131         if (listMode.isSelected()) {
132             if (file.isDirectory())
133                 return Unknown;
134             return List;
135         }
136         if (file.isDirectory())
137             return Create;
138         if (ArchiveType.of(file.getName()) != ArchiveType.Unknown)
139             return Extract;
140         return Unknown;
141     }
142
143     void startAction(File... files) {
144         Set<String> emptySet = Collections.emptySet();
145         for (File file : files)
146             executor.execute(new ActionWorker(file, detectMode(file), emptySet, file.length()));
147     }
148
149     void startExtractAction(File src, long totalSize, Set<String> targets) {
150         executor.execute(new ActionWorker(src, Extract, targets, totalSize));
151     }
152
153     String getSelectedArchiveType() {
154         return String.valueOf(archiveTypeCombo.getSelectedItem());
155     }
156
157     void clearFinishedProgressPanel() {
158         final JComponent parent = box;
159         for (Component c : parent.getComponents())
160             if (c instanceof ProgressInfoPanel) {
161                 final ProgressInfoPanel infoPanel = (ProgressInfoPanel)c;
162                 if (infoPanel.isFinished()) {
163                     parent.remove(c);
164                     EventQueue.invokeLater(new Runnable() {
165                         @Override
166                         public void run() {
167                             ((JComponent)parent.getParent()).updateUI();
168                         }
169                     });
170                 }
171             }
172     }
173
174     ProgressInfoPanel addProgressInfoPanel(File src, File dst, ActionMode mode, Set<String> targets) {
175         ProgressInfoPanel infoPanel = new ProgressInfoPanel(box, mode, src, dst, targets);
176         synchronized (box) {
177             infoPanel.setMaximumSize(new Dimension(0x8000, infoPanel.getPreferredSize().height));
178             box.add(infoPanel);
179             box.revalidate();
180             return infoPanel;
181         }
182     }
183
184     private final class ActionWorker extends SwingWorker<Integer, Integer> {
185
186         final File file;
187         final ActionMode mode;
188         final Set<String> targets;
189         final long totalSize;
190         File dst;
191         ProgressInfoPanel infoPanel;
192         boolean errorOccurred;
193         String errorMessage;
194
195         ActionWorker(File file, ActionMode mode, Set<String> targets, long totalSize) {
196             this.file = file;
197             assert mode != AutoDetects;
198             this.mode = mode;
199             this.targets = targets;
200             this.totalSize = totalSize;
201             switch (this.mode) {
202                 case Create:
203                     final String extension = getSelectedArchiveType();
204                     this.dst = new File(file.getParentFile(), file.getName() + extension);
205                     break;
206                 case Extract:
207                     this.dst = ArmadilloCommands.getDestinationDirectory(file);
208                     break;
209                 default:
210             }
211             this.infoPanel = addProgressInfoPanel(file, dst, this.mode, targets);
212             this.errorOccurred = false;
213             this.errorMessage = "(unknown error)";
214         }
215
216         @Override
217         protected Integer doInBackground() throws Exception {
218             log.setEnteredMethodName("ActionWorker#doInBackground");
219             try {
220                 switch (mode == Unknown ? detectMode(file) : mode) {
221                     case Create:
222                         final long totalSizeInDir = ArmadilloCommands.getTotalSize(file);
223                         createArchive(file, dst, createProgressNotifier(totalSizeInDir));
224                         break;
225                     case List:
226                         showInfoListWindow(file, createProgressNotifier(totalSize));
227                         break;
228                     case Extract:
229                         if (targets.isEmpty())
230                             extractArchive(file, dst, createProgressNotifier(totalSize));
231                         else
232                             extractArchive(file, dst, targets, createProgressNotifier(totalSize));
233                         break;
234                     default:
235                 }
236             }
237             catch (Throwable ex) {
238                 log.error(ex);
239                 errorOccurred = true;
240                 errorMessage = String.format("error at %s: %s", file, ex);
241                 if (ex instanceof Exception)
242                     throw (Exception)ex;
243                 if (ex instanceof Error)
244                     throw (Error)ex;
245             }
246             return 0;
247         }
248
249         private ProgressNotifier createProgressNotifier(long totalSize) {
250             return infoPanel.getProgressNotifier(totalSize);
251         }
252
253         @Override
254         protected void done() {
255             infoPanel.setFinished(true);
256             if (errorOccurred)
257                 infoPanel.setMessage(errorMessage);
258         }
259
260     }
261
262     enum ActionMode {
263         AutoDetects, Create, List, Extract, Unknown;
264     }
265
266     private final class ProgressInfoPanel extends JPanel {
267
268         private final JComponent parent;
269         private final JProgressBar pbar;
270         private final JLabel lTimeInfo;
271         private final JLabel lMessage;
272         private final StringBuilder messageBuffer;
273
274         private boolean finished;
275         private final long startTime;
276
277         ProgressInfoPanel(final JComponent parent,
278                           ActionMode mode,
279                           File src,
280                           File dst,
281                           Set<String> targets) {
282             this.startTime = System.currentTimeMillis();
283             this.parent = parent;
284             this.pbar = new JProgressBar(0, 1000);
285             this.messageBuffer = new StringBuilder();
286             this.lTimeInfo = new JLabel();
287             this.lTimeInfo.setText(String.format("time: started at %tT", startTime));
288             this.lMessage = new JLabel();
289             setLayout(new GridLayout(5, 1));
290             // JButton closeButton = new JButton(new ImageIcon(getClass().getResource("close.png")));
291             // closeButton.addActionListener(new ActionListener() {
292             //     @Override
293             //     public void actionPerformed(ActionEvent e) {
294             //         if (isFinished()) {
295             //             parent.remove(thisPanel);
296             //             EventQueue.invokeLater(new Runnable() {
297             //                 @Override
298             //                 public void run() {
299             //                     ((JComponent)parent.getParent()).updateUI();
300             //                 }
301             //             });
302             //         }
303             //     }
304             // });
305             setMessage("");
306             setBorder(BorderFactory.createLineBorder(Color.GRAY));
307             JPanel p = new JPanel(new BorderLayout());
308             p.setOpaque(false);
309             p.add(new JLabel(res.get("mode.caption." + mode)), BorderLayout.CENTER);
310             // p.add(closeButton, BorderLayout.WEST);
311             add(p, BorderLayout.NORTH);
312             final String dstString = (mode == List) ? res.get("word.dialog") : String.valueOf(dst);
313             if (targets.isEmpty())
314                 add(new JLabel(String.format("%s -> %s", src, dstString)));
315             else
316                 add(new JLabel(String.format("%d files in %s -> %s", targets.size(), src, dstString)));
317             add(this.pbar);
318             add(this.lTimeInfo);
319             add(this.lMessage);
320         }
321
322         void setMessage(String msg) {
323             messageBuffer.append(msg);
324             lMessage.setText("message: " + messageBuffer);
325             parent.updateUI();
326         }
327
328         void setRate(int rate) {
329             pbar.setValue(rate);
330             parent.updateUI();
331         }
332
333         void setFinished(boolean finished) {
334             this.finished = finished;
335             final long endTime = System.currentTimeMillis();
336             final long elapsed = (endTime - startTime) / 1000L;
337             setMessage(res.get("status.done"));
338             lTimeInfo.setText(String.format("elapsed: %d secs ( %tT to %tT )",
339                                             elapsed,
340                                             startTime,
341                                             endTime));
342             setBackground(doneBGC);
343         }
344
345         boolean isFinished() {
346             return finished;
347         }
348
349         ProgressNotifier getProgressNotifier(long total) {
350             return new ItsProgressNotifier(total);
351         }
352
353         private final class ItsProgressNotifier extends ProgressNotifier implements Runnable {
354             protected ItsProgressNotifier(long total) {
355                 super(total);
356             }
357             @Override
358             public void progressNotified(long part, long total) {
359                 EventQueue.invokeLater(this);
360             }
361             @Override
362             public void run() {
363                 setRate(getRateAsIntPermill());
364                 updateUI();
365             }
366         }
367
368     }
369
370     public static void main(final String[] args) {
371         Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
372             @Override
373             public void uncaughtException(Thread t, Throwable e) {
374                 log.fatal(e, "at thread %s", t);
375                 e.printStackTrace();
376             }
377         });
378         EventQueue.invokeLater(new Runnable() {
379             @Override
380             public void run() {
381                 setLaF();
382                 MainWindow w = new MainWindow();
383                 w.setSize(640, 400);
384                 w.setLocationByPlatform(true);
385                 w.setVisible(true);
386                 for (final String arg : args)
387                     w.startAction(new File(arg));
388             }
389             void setLaF() {
390                 try {
391                     UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
392                 }
393                 catch (RuntimeException ex) {
394                     throw ex;
395                 }
396                 catch (Exception ex) {
397                     throw new IllegalStateException(ex);
398                 }
399             }
400         });
401     }
402
403 }