1 package ini
.trakem2
.persistence
;
4 import java
.util
.Hashtable
;
5 import java
.util
.HashSet
;
6 import java
.util
.Vector
;
9 import java
.awt
.Dimension
;
10 import java
.awt
.event
.MouseAdapter
;
11 import java
.awt
.event
.MouseEvent
;
13 import javax
.swing
.BoxLayout
;
14 import javax
.swing
.table
.AbstractTableModel
;
15 import javax
.swing
.JFrame
;
16 import javax
.swing
.JLabel
;
17 import javax
.swing
.JPanel
;
18 import javax
.swing
.border
.LineBorder
;
19 import javax
.swing
.JScrollPane
;
20 import javax
.swing
.JTable
;
21 import javax
.swing
.SwingUtilities
;
24 import ij
.gui
.GenericDialog
;
25 import ij
.io
.OpenDialog
;
27 import ini
.trakem2
.ControlWindow
;
28 import ini
.trakem2
.Project
;
29 import ini
.trakem2
.display
.Displayable
;
30 import ini
.trakem2
.display
.Patch
;
31 import ini
.trakem2
.persistence
.Loader
;
32 import ini
.trakem2
.utils
.Dispatcher
;
33 import ini
.trakem2
.utils
.IJError
;
34 import ini
.trakem2
.utils
.Utils
;
36 /** A class to manage "file not found" problems. */
37 public class FilePathRepair
{
39 private final Project project
;
40 private final PathTableModel data
= new PathTableModel();
41 private final JTable table
= new JTable(data
);
42 private final JFrame frame
;
44 private FilePathRepair(final Project project
) {
45 this.project
= project
;
46 this.frame
= ControlWindow
.createJFrame("Repair: " + project
);
49 private final Runnable
makeGUI() {
50 return new Runnable() { public void run() {
51 JScrollPane jsp
= new JScrollPane(table
);
52 jsp
.setPreferredSize(new Dimension(500, 500));
53 table
.addMouseListener(listener
);
54 JLabel label
= new JLabel("Double-click any to repair file path:");
55 JLabel label2
= new JLabel("(Any listed with identical parent folder will be fixed as well.)");
56 JPanel plabel
= new JPanel();
57 BoxLayout pbl
= new BoxLayout(plabel
, BoxLayout
.Y_AXIS
);
58 plabel
.setLayout(pbl
);
59 //plabel.setBorder(new LineBorder(Color.black, 1, true));
60 plabel
.setMinimumSize(new Dimension(400, 40));
63 JPanel all
= new JPanel();
64 BoxLayout bl
= new BoxLayout(all
, BoxLayout
.Y_AXIS
);
68 frame
.getContentPane().add(all
);
69 frame
.setDefaultCloseOperation(JFrame
.DO_NOTHING_ON_CLOSE
);
71 ij
.gui
.GUI
.center(frame
);
72 frame
.setVisible(true);
76 private static class PathTableModel
extends AbstractTableModel
{
77 final Vector
<Patch
> vp
= new Vector
<Patch
>();
78 final Vector
<String
> vpath
= new Vector
<String
>();
79 final HashSet
<Patch
> set
= new HashSet
<Patch
>();
81 public final String
getColumnName(final int col
) {
83 case 0: return "Image";
84 case 1: return "Nonexistent file path";
88 public final int getRowCount() { return vp
.size(); }
89 public final int getColumnCount() { return 2; }
90 public final Object
getValueAt(final int row
, final int col
) {
92 case 0: return vp
.get(row
);
93 case 1: return vpath
.get(row
);
97 public final boolean isCellEditable(final int row
, final int col
) {
100 public final void setValueAt(final Object value
, final int row
, final int col
) {} // ignore
102 synchronized public final void add(final Patch patch
) {
103 if (set
.contains(patch
)) return; // already here
105 vpath
.add(patch
.getImageFilePath());
109 synchronized public final String
remove(final int row
) {
110 set
.remove(vp
.remove(row
));
111 return vpath
.remove(row
);
114 synchronized public final String
remove(final Patch p
) {
115 final int i
= vp
.indexOf(p
);
116 if (-1 == i
) return null;
117 set
.remove(vp
.remove(i
));
118 return vpath
.remove(i
);
124 static private final Hashtable
<Project
,FilePathRepair
> projects
= new Hashtable
<Project
,FilePathRepair
>();
126 static public void add(final Patch patch
) {
127 dispatcher
.exec(new Runnable() { public void run() {
128 final Project project
= patch
.getProject();
129 FilePathRepair fpr
= null;
130 synchronized (projects
) {
131 fpr
= projects
.get(project
);
133 fpr
= new FilePathRepair(project
);
134 projects
.put(project
, fpr
);
135 SwingUtilities
.invokeLater(fpr
.makeGUI());
138 if (!fpr
.frame
.isVisible()) {
139 fpr
.frame
.setVisible(true);
141 SwingUtilities
.invokeLater(new Repainter(fpr
));
147 static private class Repainter
implements Runnable
{
149 Repainter(final FilePathRepair fpr
) { this.fpr
= fpr
; }
152 fpr
.table
.updateUI();
155 } catch (Exception e
) { IJError
.print(e
); }
159 static private final Dispatcher dispatcher
= new Dispatcher("File path fixer");
161 static private MouseAdapter listener
= new MouseAdapter() {
162 public void mousePressed(final MouseEvent me
) {
163 final JTable table
= (JTable
) me
.getSource();
164 final PathTableModel data
= (PathTableModel
) table
.getModel();
165 final int row
= table
.rowAtPoint(me
.getPoint());
166 if (-1 == row
) return;
168 if (2 == me
.getClickCount()) {
169 dispatcher
.exec(new Runnable() { public void run() {
171 table
.setEnabled(false);
172 GenericDialog gd
= new GenericDialog("Fix paths");
173 gd
.addCheckbox("Fix other listed image files with identical parent directory", true);
174 gd
.addCheckbox("Fix all image files in the project with identical parent directory", true);
175 gd
.addCheckbox("Update mipmaps for each fixed path", false);
177 if (!gd
.wasCanceled()) {
178 fixPath(table
, data
, row
, gd
.getNextBoolean(), gd
.getNextBoolean(), gd
.getNextBoolean());
180 } catch (Exception e
) {
183 table
.setEnabled(true);
190 static private void fixPath(final JTable table
, final PathTableModel data
, final int row
, final boolean fix_similar
, final boolean fix_all
, final boolean update_mipmaps
) throws Exception
{
191 synchronized (projects
) {
192 final Patch patch
= data
.vp
.get(row
);
193 if (null == patch
) return;
194 final String old_path
= patch
.getImageFilePath();
195 final File f
= new File(old_path
);
197 Utils
.log("File exists for " + patch
+ " at " + f
.getAbsolutePath() + "\n --> not updating.");
201 // Else, pop up file dialog
202 OpenDialog od
= new OpenDialog("Select image file", OpenDialog
.getDefaultDirectory(), null);
203 final String dir
= od
.getDirectory();
204 final String filename
= od
.getFileName();
205 if (null == dir
) return; // dialog was canceled
206 final String path
= new StringBuffer(dir
).append(filename
).toString(); // save concat, no backslash altered
207 if (!fixPatchPath(patch
, path
, update_mipmaps
)) {
211 String wrong_parent_path
= new File(data
.remove(row
)).getParent();
212 if (!wrong_parent_path
.endsWith(File
.separator
)) wrong_parent_path
= new StringBuffer(wrong_parent_path
).append(File
.separatorChar
).toString(); // backslash-safe
213 String good_parent_path
= dir
;
214 if (!dir
.endsWith(File
.separator
)) good_parent_path
= new StringBuffer(good_parent_path
).append(File
.separatorChar
).toString(); // backslash-safe
218 // Check for similar parent paths and see if they can be fixed
220 for (int i
=data
.vp
.size() -1; i
>-1; i
--) {
221 final String wrong_path
= data
.vpath
.get(i
);
222 final Patch p
= data
.vp
.get(i
);
223 if (wrong_path
.startsWith(wrong_parent_path
)) {
224 // try to fix as well
225 File file
= new File(new StringBuffer(good_parent_path
).append(wrong_path
.substring(wrong_parent_path
.length())).toString());
227 if (fixPatchPath(p
, file
.getAbsolutePath(), update_mipmaps
)) {
228 data
.remove(p
); // not by 'i' but by Patch, since if some fail the order is not the same
236 // traverse all Patch from the entire project
237 for (final Displayable d
: patch
.getLayerSet().getDisplayables(Patch
.class)) {
238 final Patch p
= (Patch
) d
;
239 final String wrong_path
= p
.getImageFilePath();
240 if (wrong_path
.startsWith(wrong_parent_path
)) {
241 File file
= new File(new StringBuffer(good_parent_path
).append(wrong_path
.substring(wrong_parent_path
.length())).toString());
243 fixPatchPath(p
, file
.getAbsolutePath(), update_mipmaps
);
250 // if table is empty, close
251 if (0 == data
.vp
.size()) {
252 FilePathRepair fpr
= projects
.remove(patch
.getProject());
256 Utils
.logAll("Fixed " + n_fixed
+ " image file path" + (n_fixed
> 1 ?
"s" : ""));
260 static private boolean fixPatchPath(final Patch patch
, final String new_path
, final boolean update_mipmaps
) {
262 // Open the image header to check that dimensions match
263 final Loader loader
= patch
.getProject().getLoader();
264 loader
.releaseToFit(Math
.max(Loader
.MIN_FREE_BYTES
, patch
.getOWidth() * patch
.getOHeight() * 10));
265 final Dimension dim
= loader
.getDimensions(new_path
);
267 Utils
.log(new StringBuffer("ERROR: could not open image at ").append(new_path
).toString()); // preserving backslashes
270 // Check and set dimensions
271 if (dim
.width
!= patch
.getOWidth() || dim
.height
!= patch
.getOHeight()) {
272 Utils
.log("ERROR different o_width,o_height for patch " + patch
+ "\n at new path " + new_path
);
276 loader
.removeFromUnloadable(patch
);
277 // Assign new image path
278 loader
.addedPatchFrom(new_path
, patch
);
279 // submit job to regenerate mipmaps in the background
280 if (update_mipmaps
) loader
.regenerateMipMaps(patch
);
282 } catch (Exception e
) {