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
.Patch
;
30 import ini
.trakem2
.persistence
.Loader
;
31 import ini
.trakem2
.utils
.Dispatcher
;
32 import ini
.trakem2
.utils
.IJError
;
33 import ini
.trakem2
.utils
.Utils
;
35 /** A class to manage "file not found" problems. */
36 public class FilePathRepair
{
38 private final Project project
;
39 private final PathTableModel data
= new PathTableModel();
40 private final JTable table
= new JTable(data
);
41 private final JFrame frame
;
43 private FilePathRepair(final Project project
) {
44 this.project
= project
;
45 this.frame
= ControlWindow
.createJFrame("Repair: " + project
);
48 private final Runnable
makeGUI() {
49 return new Runnable() { public void run() {
50 JScrollPane jsp
= new JScrollPane(table
);
51 jsp
.setPreferredSize(new Dimension(500, 500));
52 table
.addMouseListener(listener
);
53 JLabel label
= new JLabel("Double-click any to repair file path:");
54 JLabel label2
= new JLabel("(Any listed with identical parent folder will be fixed as well.)");
55 JPanel plabel
= new JPanel();
56 BoxLayout pbl
= new BoxLayout(plabel
, BoxLayout
.Y_AXIS
);
57 plabel
.setLayout(pbl
);
58 //plabel.setBorder(new LineBorder(Color.black, 1, true));
59 plabel
.setMinimumSize(new Dimension(400, 40));
62 JPanel all
= new JPanel();
63 BoxLayout bl
= new BoxLayout(all
, BoxLayout
.Y_AXIS
);
67 frame
.getContentPane().add(all
);
68 frame
.setDefaultCloseOperation(JFrame
.DO_NOTHING_ON_CLOSE
);
70 ij
.gui
.GUI
.center(frame
);
71 frame
.setVisible(true);
75 private static class PathTableModel
extends AbstractTableModel
{
76 final Vector
<Patch
> vp
= new Vector
<Patch
>();
77 final Vector
<String
> vpath
= new Vector
<String
>();
78 final HashSet
<Patch
> set
= new HashSet
<Patch
>();
80 public final String
getColumnName(final int col
) {
82 case 0: return "Image";
83 case 1: return "Nonexistent file path";
87 public final int getRowCount() { return vp
.size(); }
88 public final int getColumnCount() { return 2; }
89 public final Object
getValueAt(final int row
, final int col
) {
91 case 0: return vp
.get(row
);
92 case 1: return vpath
.get(row
);
96 public final boolean isCellEditable(final int row
, final int col
) {
99 public final void setValueAt(final Object value
, final int row
, final int col
) {} // ignore
101 synchronized public final void add(final Patch patch
) {
102 if (set
.contains(patch
)) return; // already here
104 final String path
= patch
.getFilePath();
105 final int i
= path
.lastIndexOf("-----#slice=");
106 vpath
.add(-1 == i ? path
: path
.substring(0, i
));
110 synchronized public final String
remove(final int row
) {
111 set
.remove(vp
.remove(row
));
112 return vpath
.remove(row
);
115 synchronized public final String
remove(final Patch p
) {
116 final int i
= vp
.indexOf(p
);
117 if (-1 == i
) return null;
118 set
.remove(vp
.remove(i
));
119 return vpath
.remove(i
);
125 static private final Hashtable
<Project
,FilePathRepair
> projects
= new Hashtable
<Project
,FilePathRepair
>();
127 static public void add(final Patch patch
) {
128 dispatcher
.exec(new Runnable() { public void run() {
129 final Project project
= patch
.getProject();
130 FilePathRepair fpr
= null;
131 synchronized (projects
) {
132 fpr
= projects
.get(project
);
134 fpr
= new FilePathRepair(project
);
135 projects
.put(project
, fpr
);
136 SwingUtilities
.invokeLater(fpr
.makeGUI());
139 if (!fpr
.frame
.isVisible()) {
140 fpr
.frame
.setVisible(true);
142 SwingUtilities
.invokeLater(new Repainter(fpr
));
148 static private class Repainter
implements Runnable
{
150 Repainter(final FilePathRepair fpr
) { this.fpr
= fpr
; }
153 fpr
.table
.updateUI();
156 } catch (Exception e
) { IJError
.print(e
); }
160 static private final Dispatcher dispatcher
= new Dispatcher("File path fixer");
162 static private MouseAdapter listener
= new MouseAdapter() {
163 public void mousePressed(final MouseEvent me
) {
164 final JTable table
= (JTable
) me
.getSource();
165 final PathTableModel data
= (PathTableModel
) table
.getModel();
166 final int row
= table
.rowAtPoint(me
.getPoint());
167 if (-1 == row
) return;
169 if (2 == me
.getClickCount()) {
170 dispatcher
.exec(new Runnable() { public void run() {
172 table
.setEnabled(false);
173 GenericDialog gd
= new GenericDialog("Fix paths");
174 gd
.addCheckbox("Fix others 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());
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 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
215 // Check for similar parent paths and see if they can be fixed
217 for (int i
=data
.vp
.size() -1; i
>-1; i
--) {
218 final String wrong_path
= data
.vpath
.get(i
);
219 final Patch p
= data
.vp
.get(i
);
220 if (wrong_path
.startsWith(wrong_parent_path
)) {
221 // try to fix as well
222 File file
= new File(new StringBuffer(good_parent_path
).append(wrong_path
.substring(wrong_parent_path
.length())).toString());
224 if (fixPatchPath(p
, file
.getAbsolutePath(), update_mipmaps
)) {
225 data
.remove(p
); // not by 'i' but by Patch, since if some fail the order is not the same
232 // if table is empty, close
233 if (0 == data
.vp
.size()) {
234 FilePathRepair fpr
= projects
.remove(patch
.getProject());
240 static private boolean fixPatchPath(final Patch patch
, final String new_path
, final boolean update_mipmaps
) {
242 // Open the image header to check that dimensions match
243 final Loader loader
= patch
.getProject().getLoader();
244 loader
.releaseToFit(Math
.max(Loader
.MIN_FREE_BYTES
, patch
.getOWidth() * patch
.getOHeight() * 10));
245 final Dimension dim
= loader
.getDimensions(new_path
);
247 Utils
.log(new StringBuffer("ERROR: could not open image at ").append(new_path
).toString()); // preserving backslashes
250 // Check and set dimensions
251 if (dim
.width
!= patch
.getOWidth() || dim
.height
!= patch
.getOHeight()) {
252 Utils
.log("ERROR different o_width,o_height for patch " + patch
+ "\n at new path " + new_path
);
256 loader
.removeFromUnloadable(patch
);
257 // Assign new image path
258 loader
.addedPatchFrom(new_path
, patch
);
259 // submit job to regenerate mipmaps in the background
260 if (update_mipmaps
) loader
.regenerateMipMaps(patch
);
262 } catch (Exception e
) {