1 | /**
|
---|
2 | *#########################################################################
|
---|
3 | *
|
---|
4 | * A component of the Gatherer application, part of the Greenstone digital
|
---|
5 | * library suite from the New Zealand Digital Library Project at the
|
---|
6 | * University of Waikato, New Zealand.
|
---|
7 | *
|
---|
8 | * <BR><BR>
|
---|
9 | *
|
---|
10 | * Author: John Thompson, Greenstone Digital Library, University of Waikato
|
---|
11 | *
|
---|
12 | * <BR><BR>
|
---|
13 | *
|
---|
14 | * Copyright (C) 1999 New Zealand Digital Library Project
|
---|
15 | *
|
---|
16 | * <BR><BR>
|
---|
17 | *
|
---|
18 | * This program is free software; you can redistribute it and/or modify
|
---|
19 | * it under the terms of the GNU General Public License as published by
|
---|
20 | * the Free Software Foundation; either version 2 of the License, or
|
---|
21 | * (at your option) any later version.
|
---|
22 | *
|
---|
23 | * <BR><BR>
|
---|
24 | *
|
---|
25 | * This program is distributed in the hope that it will be useful,
|
---|
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
28 | * GNU General Public License for more details.
|
---|
29 | *
|
---|
30 | * <BR><BR>
|
---|
31 | *
|
---|
32 | * You should have received a copy of the GNU General Public License
|
---|
33 | * along with this program; if not, write to the Free Software
|
---|
34 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
---|
35 | *########################################################################
|
---|
36 | */
|
---|
37 | package org.greenstone.gatherer.gui;
|
---|
38 |
|
---|
39 | import java.awt.*;
|
---|
40 | import java.awt.event.*;
|
---|
41 | import java.io.File;
|
---|
42 | import java.net.*;
|
---|
43 | import java.util.*;
|
---|
44 | import javax.swing.*;
|
---|
45 | import javax.swing.border.*;
|
---|
46 | import javax.swing.event.*;
|
---|
47 | import javax.swing.tree.*;
|
---|
48 | import org.greenstone.gatherer.Dictionary;
|
---|
49 | import org.greenstone.gatherer.Gatherer;
|
---|
50 | import org.greenstone.gatherer.WGet;
|
---|
51 | import org.greenstone.gatherer.file.FileNode;
|
---|
52 | import org.greenstone.gatherer.gui.GLIButton;
|
---|
53 | import org.greenstone.gatherer.gui.Preferences;
|
---|
54 | import org.greenstone.gatherer.gui.URLField;
|
---|
55 | import org.greenstone.gatherer.util.StaticStrings;
|
---|
56 | import org.greenstone.gatherer.util.Utility;
|
---|
57 |
|
---|
58 | /**
|
---|
59 | * @author John Thompson, Greenstone Digital Library, University of Waikato
|
---|
60 | * @version 2.1
|
---|
61 | */
|
---|
62 | public class MirrorPane
|
---|
63 | extends JPanel {
|
---|
64 |
|
---|
65 | private boolean download_button_enabled = false;
|
---|
66 | private boolean ready = false;
|
---|
67 | private JButton download_button;
|
---|
68 | private JCheckBox higher_checkbox;
|
---|
69 | private JCheckBox requisite_checkbox;
|
---|
70 | private JCheckBox same_host_checkbox;
|
---|
71 | private JScrollPane list_scroll;
|
---|
72 | private JComboBox depth_combobox;
|
---|
73 | private URLField url_field;
|
---|
74 | private Vector depth_model;
|
---|
75 | private WGet getter;
|
---|
76 |
|
---|
77 | public MirrorPane() {
|
---|
78 | super();
|
---|
79 | // Create a new wget thread
|
---|
80 | getter = new WGet();
|
---|
81 | getter.start();
|
---|
82 | // And retrieve its list of pending jobs
|
---|
83 | list_scroll = getter.getJobList();
|
---|
84 |
|
---|
85 | // Create
|
---|
86 | JPanel edit_pane = new JPanel();
|
---|
87 | JPanel details_pane = new JPanel();
|
---|
88 |
|
---|
89 | JPanel url_pane = new JPanel();
|
---|
90 | JLabel url_label = new JLabel();
|
---|
91 | Dictionary.registerText(url_label, "Mirroring.Source_URL");
|
---|
92 | url_field = new URLField(Gatherer.config.getColor("coloring.editable_foreground", false), Gatherer.config.getColor("coloring.editable_background", false), Gatherer.config.getColor("coloring.error_foreground", false), Gatherer.config.getColor("coloring.error_background", false));
|
---|
93 | Dictionary.registerTooltip(url_field, "Mirroring.Source_URL_Tooltip");
|
---|
94 |
|
---|
95 | depth_model = new Vector();
|
---|
96 | /* @todo - add to dictionary */
|
---|
97 | depth_model.add(new DepthEntry(0, "Mirroring.Download_Depth.Zero"));
|
---|
98 | /* @todo - add to dictionary */
|
---|
99 | depth_model.add(new DepthEntry(1, String.valueOf(1)));
|
---|
100 | /* @todo - add to dictionary */
|
---|
101 | depth_model.add(new DepthEntry(2, String.valueOf(3)));
|
---|
102 | /* @todo - add to dictionary */
|
---|
103 | depth_model.add(new DepthEntry(3, String.valueOf(3)));
|
---|
104 | /* @todo - add to dictionary */
|
---|
105 | depth_model.add(new DepthEntry(4, String.valueOf(4)));
|
---|
106 | /* @todo - add to dictionary */
|
---|
107 | depth_model.add(new DepthEntry(5, String.valueOf(5)));
|
---|
108 | /* @todo - add to dictionary */
|
---|
109 | depth_model.add(new DepthEntry(-1, "Mirroring.Download_Depth.Unlimited"));
|
---|
110 | JPanel depth_pane = new JPanel();
|
---|
111 | JLabel depth_label = new JLabel();
|
---|
112 | Dictionary.registerText(depth_label, "Mirroring.Download_Depth");
|
---|
113 | depth_combobox = new JComboBox(depth_model);
|
---|
114 | Dictionary.registerTooltip(depth_combobox, "Mirroring.Download_Depth_Tooltip");
|
---|
115 |
|
---|
116 | requisite_checkbox = new JCheckBox();
|
---|
117 | Dictionary.registerText(requisite_checkbox, "Mirroring.Download_Hidden");
|
---|
118 |
|
---|
119 | higher_checkbox = new JCheckBox();
|
---|
120 | Dictionary.registerText(higher_checkbox, "Mirroring.Higher_Directories");
|
---|
121 |
|
---|
122 | same_host_checkbox = new JCheckBox();
|
---|
123 | same_host_checkbox.setSelected(true);
|
---|
124 | Dictionary.registerText(same_host_checkbox, "Mirroring.Same_Host");
|
---|
125 |
|
---|
126 | JPanel button_pane = new JPanel();
|
---|
127 |
|
---|
128 | JButton preferences_button = new GLIButton("Mirroring.Further_Options");
|
---|
129 | preferences_button.setMnemonic(KeyEvent.VK_P);
|
---|
130 |
|
---|
131 | download_button = new GLIButton();
|
---|
132 | download_button.setEnabled(false);
|
---|
133 | download_button.setMnemonic(KeyEvent.VK_D);
|
---|
134 | Dictionary.registerBoth(download_button, "Mirroring.Download", "Mirroring.Download_Tooltip");
|
---|
135 |
|
---|
136 | // Connect
|
---|
137 | download_button.addActionListener(new DownloadButtonListener());
|
---|
138 | preferences_button.addActionListener(new PreferencesButtonActionListener());
|
---|
139 | url_field.getDocument().addDocumentListener(new DownloadButtonEnabler());
|
---|
140 |
|
---|
141 | // Layout
|
---|
142 | url_pane.setLayout(new BorderLayout(5,0));
|
---|
143 | url_pane.add(url_label, BorderLayout.WEST);
|
---|
144 | url_pane.add(url_field, BorderLayout.CENTER);
|
---|
145 |
|
---|
146 | depth_pane.setLayout(new GridLayout(1,3));
|
---|
147 | depth_pane.add(depth_label);
|
---|
148 | depth_pane.add(new JPanel());
|
---|
149 | depth_pane.add(depth_combobox);
|
---|
150 |
|
---|
151 | details_pane.setLayout(new GridLayout(5,1,0,5));
|
---|
152 | details_pane.add(url_pane);
|
---|
153 | details_pane.add(depth_pane);
|
---|
154 | details_pane.add(requisite_checkbox);
|
---|
155 | details_pane.add(higher_checkbox);
|
---|
156 | details_pane.add(same_host_checkbox);
|
---|
157 |
|
---|
158 | button_pane.setLayout(new GridLayout(1,3));
|
---|
159 | button_pane.add(preferences_button);
|
---|
160 | button_pane.add(new JPanel());
|
---|
161 | button_pane.add(download_button);
|
---|
162 |
|
---|
163 | edit_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(2,0,0,0), BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(Dictionary.get("Mirroring.Download_Controls")), BorderFactory.createEmptyBorder(2,2,2,2))));
|
---|
164 | edit_pane.setLayout(new BorderLayout());
|
---|
165 | edit_pane.add(details_pane, BorderLayout.CENTER);
|
---|
166 | edit_pane.add(button_pane, BorderLayout.SOUTH);
|
---|
167 |
|
---|
168 | setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
|
---|
169 | setLayout(new BorderLayout());
|
---|
170 | add(edit_pane, BorderLayout.NORTH);
|
---|
171 | add(list_scroll, BorderLayout.CENTER);
|
---|
172 | }
|
---|
173 |
|
---|
174 | public void afterDisplay() {
|
---|
175 | ready = true;
|
---|
176 | }
|
---|
177 |
|
---|
178 | public void collectionChanged(boolean ready) {
|
---|
179 | }
|
---|
180 |
|
---|
181 | /** This method is called whenever the Mirror pane is brought into focus and is a good time to display a warning message if WGet is not available or of an older, problematic, version.
|
---|
182 | */
|
---|
183 | public void gainFocus() {
|
---|
184 | if(!ready) {
|
---|
185 | return;
|
---|
186 | }
|
---|
187 | // Lets see what warning message we should display, if any.
|
---|
188 | String wget_version_str = Gatherer.config.getWGetVersion();
|
---|
189 | if(wget_version_str.equals(StaticStrings.NO_WGET_STR)) {
|
---|
190 | // If there was no WGet available then downloading is disabled entirely
|
---|
191 | download_button_enabled = false;
|
---|
192 | // And we tell the user why.
|
---|
193 | /** @todo - add to dictionary */
|
---|
194 | JOptionPane.showMessageDialog(Gatherer.g_man, "Mirroring.No_WGet", "Mirroring.No_WGet_Title", JOptionPane.ERROR_MESSAGE);
|
---|
195 | }
|
---|
196 | else if(wget_version_str.equals(StaticStrings.WGET_OLD_STR)) {
|
---|
197 | // Downloading is enabled
|
---|
198 | download_button_enabled = true;
|
---|
199 | // But we display a preventable warning message about the path problems.
|
---|
200 | WarningDialog dialog = new WarningDialog("warning.OldWGet", false);
|
---|
201 | dialog.display();
|
---|
202 | dialog.dispose();
|
---|
203 | dialog = null;
|
---|
204 | }
|
---|
205 | // Otherwise version must be ok
|
---|
206 | else {
|
---|
207 | download_button_enabled = true;
|
---|
208 | }
|
---|
209 | // It is also a good time to determine if the download should be enabled - ie if its allowed to be enabled and a valid URL is present in the field.
|
---|
210 | download_button.setEnabled(download_button_enabled && !url_field.isEmpty() && url_field.validateURL());
|
---|
211 | }
|
---|
212 |
|
---|
213 | public void setURL(String url) {
|
---|
214 | url_field.setText(url);
|
---|
215 | }
|
---|
216 |
|
---|
217 |
|
---|
218 | static public FileNode getWebCacheMapping()
|
---|
219 | {
|
---|
220 | if (Gatherer.config.get("workflow.mirror", true) || Gatherer.config.get("workflow.mirror", false)) {
|
---|
221 | /* @todo - add to dictionary */
|
---|
222 | return new FileNode(Utility.getCacheDir(), "Mirroring.Mirror_Cache");
|
---|
223 | }
|
---|
224 | return null;
|
---|
225 | }
|
---|
226 |
|
---|
227 | /** A DepthEntry contains a depth value, as an int, and a representitive text string. This allows for a more meaningful appearance than '0' for instance. */
|
---|
228 | private class DepthEntry
|
---|
229 | implements Comparable {
|
---|
230 | private int value;
|
---|
231 | private String text;
|
---|
232 | /** Default constructor.
|
---|
233 | * @param value the depth value as an int
|
---|
234 | * @param text how this entry show represent itself as a String
|
---|
235 | */
|
---|
236 | public DepthEntry(int value, String text) {
|
---|
237 | this.text = text;
|
---|
238 | this.value = value;
|
---|
239 | }
|
---|
240 | /** Determines the natural ordering of this DepthEntry and some other object
|
---|
241 | * @param object the Object to test for ordering
|
---|
242 | * @return >0 if the object is before this one, 0 if they are equal, or <0 if the object is after this one
|
---|
243 | */
|
---|
244 | public int compareTo(Object object) {
|
---|
245 | // If the object is a DepthEntry then this is easy pesy.
|
---|
246 | if(object instanceof DepthEntry) {
|
---|
247 | int target_value = ((DepthEntry) object).getValue();
|
---|
248 | return value - target_value;
|
---|
249 | }
|
---|
250 | // If the object is a String, then we should try parsing an int from it
|
---|
251 | if(object instanceof String) {
|
---|
252 | try {
|
---|
253 | int target_value = Integer.parseInt((String)object);
|
---|
254 | return value - target_value;
|
---|
255 | }
|
---|
256 | catch(Exception exception) {
|
---|
257 | }
|
---|
258 | }
|
---|
259 | // Otherwise we'll try a string comparison between our text and the toString of the object
|
---|
260 | return text.compareTo(object.toString());
|
---|
261 | }
|
---|
262 |
|
---|
263 | /** Determine if this DepthEntry is equal to some other object. Given that there is significant processing involved we push the resposiblility for this onto compareTo, so we only have the complex code once.
|
---|
264 | * @param object the Object to test for equality
|
---|
265 | * @return true if the object is equal to this depth entry, false otherwise
|
---|
266 | */
|
---|
267 | public boolean equals(Object object) {
|
---|
268 | return (compareTo(object) == 0);
|
---|
269 | }
|
---|
270 |
|
---|
271 | public int getValue() {
|
---|
272 | return value;
|
---|
273 | }
|
---|
274 |
|
---|
275 | public String toString() {
|
---|
276 | return text;
|
---|
277 | }
|
---|
278 | }
|
---|
279 |
|
---|
280 | private class DownloadButtonEnabler
|
---|
281 | implements DocumentListener {
|
---|
282 | /** Gives notification that an attribute or set of attributes changed. */
|
---|
283 | public void changedUpdate(DocumentEvent e) {
|
---|
284 | validateDownloadButton();
|
---|
285 | }
|
---|
286 | /** Gives notification that there was an insert into the document. */
|
---|
287 | public void insertUpdate(DocumentEvent e) {
|
---|
288 | validateDownloadButton();
|
---|
289 | }
|
---|
290 | /** Gives notification that a portion of the document has been removed. */
|
---|
291 | public void removeUpdate(DocumentEvent e) {
|
---|
292 | validateDownloadButton();
|
---|
293 | }
|
---|
294 | private void validateDownloadButton() {
|
---|
295 | download_button.setEnabled(download_button_enabled && !url_field.isEmpty() && url_field.validateURL());
|
---|
296 | }
|
---|
297 | }
|
---|
298 |
|
---|
299 | private class DownloadButtonListener
|
---|
300 | implements ActionListener {
|
---|
301 |
|
---|
302 | public void actionPerformed(ActionEvent event) {
|
---|
303 | // Retrieve the current url and confirm it is valid
|
---|
304 | String url_str = url_field.getText();
|
---|
305 | URL url = null;
|
---|
306 | try {
|
---|
307 | url = new URL(url_str);
|
---|
308 | }
|
---|
309 | catch(MalformedURLException error) {
|
---|
310 | JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("Mirroring.Invalid_URL"), Dictionary.get("Mirroring.Invalid_URL_Title"), JOptionPane.ERROR_MESSAGE);
|
---|
311 | }
|
---|
312 |
|
---|
313 | // Otherwise construct a new download job
|
---|
314 | Object depth_object = depth_combobox.getSelectedItem();
|
---|
315 | int depth_value = 0;
|
---|
316 | if(depth_object instanceof DepthEntry) {
|
---|
317 | depth_value = ((DepthEntry)depth_object).getValue();
|
---|
318 | }
|
---|
319 | else if(depth_object instanceof String) {
|
---|
320 | String depth_string = (String) depth_object;
|
---|
321 | try {
|
---|
322 | depth_value = Integer.parseInt(depth_string);
|
---|
323 | // Create a new entry for the purposes of comparison, and if needs be, addition
|
---|
324 | DepthEntry new_entry = new DepthEntry(depth_value, depth_string);
|
---|
325 | // Try to select the correct item from the combobox
|
---|
326 | depth_combobox.setSelectedItem(null);
|
---|
327 | depth_combobox.setSelectedItem(new_entry);
|
---|
328 | // We can see if that worked, and if it didn't add the entry
|
---|
329 | if(depth_combobox.getSelectedItem() == null) {
|
---|
330 | depth_combobox.addItem(new_entry);
|
---|
331 | depth_combobox.setSelectedItem(new_entry);
|
---|
332 | }
|
---|
333 | new_entry = null;
|
---|
334 | }
|
---|
335 | catch(Exception exception) {
|
---|
336 | /* @todo - add to dictionary */
|
---|
337 | JOptionPane.showMessageDialog(Gatherer.g_man, "Mirroring.Mirror_Depth.Invalid_Depth", "Mirroring.Mirror_Depth.Invalid_Depth_Title", JOptionPane.ERROR_MESSAGE);
|
---|
338 | return;
|
---|
339 | }
|
---|
340 | depth_string = null;
|
---|
341 | }
|
---|
342 | depth_object = null;
|
---|
343 |
|
---|
344 | File destination_file = Utility.getCacheDir();
|
---|
345 | destination_file.mkdirs(); // If they aren't already
|
---|
346 | String destination_filename = destination_file.getAbsolutePath();
|
---|
347 | if(!destination_filename.endsWith(File.separator)) {
|
---|
348 | destination_filename = destination_filename + File.separator;
|
---|
349 | }
|
---|
350 |
|
---|
351 | if(url != null) {
|
---|
352 | getter.newJob(!higher_checkbox.isSelected(), !same_host_checkbox.isSelected(), requisite_checkbox.isSelected(), url, depth_value, destination_filename);
|
---|
353 | }
|
---|
354 | }
|
---|
355 | }
|
---|
356 |
|
---|
357 | private class PreferencesButtonActionListener
|
---|
358 | implements ActionListener {
|
---|
359 | public void actionPerformed(ActionEvent event) {
|
---|
360 | new Preferences(Preferences.CONNECTION_PREFS);
|
---|
361 | }
|
---|
362 | }
|
---|
363 | }
|
---|