Lines 68-73
Link Here
|
68 |
import java.beans.PropertyChangeEvent; |
68 |
import java.beans.PropertyChangeEvent; |
69 |
import java.beans.PropertyChangeListener; |
69 |
import java.beans.PropertyChangeListener; |
70 |
import java.io.File; |
70 |
import java.io.File; |
|
|
71 |
import java.io.FileNotFoundException; |
71 |
import java.io.IOException; |
72 |
import java.io.IOException; |
72 |
import java.lang.ref.WeakReference; |
73 |
import java.lang.ref.WeakReference; |
73 |
import java.lang.reflect.Constructor; |
74 |
import java.lang.reflect.Constructor; |
Lines 95-100
Link Here
|
95 |
import java.util.logging.Logger; |
96 |
import java.util.logging.Logger; |
96 |
import java.util.regex.Matcher; |
97 |
import java.util.regex.Matcher; |
97 |
import java.util.regex.Pattern; |
98 |
import java.util.regex.Pattern; |
|
|
99 |
import java.util.regex.PatternSyntaxException; |
98 |
import javax.swing.AbstractAction; |
100 |
import javax.swing.AbstractAction; |
99 |
import javax.swing.AbstractListModel; |
101 |
import javax.swing.AbstractListModel; |
100 |
import javax.swing.Action; |
102 |
import javax.swing.Action; |
Lines 136-142
Link Here
|
136 |
import javax.swing.filechooser.FileSystemView; |
138 |
import javax.swing.filechooser.FileSystemView; |
137 |
import javax.swing.filechooser.FileView; |
139 |
import javax.swing.filechooser.FileView; |
138 |
import javax.swing.plaf.ActionMapUIResource; |
140 |
import javax.swing.plaf.ActionMapUIResource; |
139 |
import javax.swing.plaf.ComponentUI; |
|
|
140 |
import javax.swing.plaf.UIResource; |
141 |
import javax.swing.plaf.UIResource; |
141 |
import javax.swing.plaf.basic.BasicFileChooserUI; |
142 |
import javax.swing.plaf.basic.BasicFileChooserUI; |
142 |
import javax.swing.tree.DefaultMutableTreeNode; |
143 |
import javax.swing.tree.DefaultMutableTreeNode; |
Lines 157-169
Link Here
|
157 |
import org.openide.util.NbPreferences; |
158 |
import org.openide.util.NbPreferences; |
158 |
import org.openide.util.RequestProcessor; |
159 |
import org.openide.util.RequestProcessor; |
159 |
import org.openide.util.Utilities; |
160 |
import org.openide.util.Utilities; |
|
|
161 |
import sun.awt.shell.ShellFolder; |
162 |
import sun.swing.FilePane; |
160 |
|
163 |
|
161 |
/** |
164 |
/** |
162 |
* An implementation of a customized filechooser. |
165 |
* An implementation of a customized filechooser. |
163 |
* |
166 |
* |
164 |
* @author Soot Phengsy, inspired by Jeff Dinkins' Swing version |
167 |
* @author Soot Phengsy, inspired by Jeff Dinkins' Swing version |
165 |
*/ |
168 |
*/ |
166 |
class FileChooserUIImpl extends BasicFileChooserUI{ |
169 |
final class FileChooserUIImpl extends BasicFileChooserUI{ |
167 |
|
170 |
|
168 |
static final String USE_SHELL_FOLDER = "FileChooser.useShellFolder";//NOI18N |
171 |
static final String USE_SHELL_FOLDER = "FileChooser.useShellFolder";//NOI18N |
169 |
static final String NB_USE_SHELL_FOLDER = "nb.FileChooser.useShellFolder";//NOI18N |
172 |
static final String NB_USE_SHELL_FOLDER = "nb.FileChooser.useShellFolder";//NOI18N |
Lines 183-188
Link Here
|
183 |
private static final Logger LOG = Logger.getLogger(FileChooserUIImpl.class.getName()); |
186 |
private static final Logger LOG = Logger.getLogger(FileChooserUIImpl.class.getName()); |
184 |
|
187 |
|
185 |
private static final RequestProcessor RP = new RequestProcessor("Cnd File Chooser Update Worker"); // NOI18N |
188 |
private static final RequestProcessor RP = new RequestProcessor("Cnd File Chooser Update Worker"); // NOI18N |
|
|
189 |
private static final RequestProcessor APPROVE_RP = new RequestProcessor("Cnd File Chooser Update Worker"); // NOI18N |
186 |
|
190 |
|
187 |
private static final String TIMEOUT_KEY="nb.fileChooser.timeout"; // NOI18N |
191 |
private static final String TIMEOUT_KEY="nb.fileChooser.timeout"; // NOI18N |
188 |
|
192 |
|
Lines 275-288
Link Here
|
275 |
private final RequestProcessor.Task listFilesTask = RP.create(listFilesWorker); |
279 |
private final RequestProcessor.Task listFilesTask = RP.create(listFilesWorker); |
276 |
private volatile File curDir; |
280 |
private volatile File curDir; |
277 |
|
281 |
|
278 |
public static ComponentUI createUI(JComponent c) { |
282 |
private final Action approveSelectionAction; |
279 |
return new FileChooserUIImpl((JFileChooserEx) c); |
283 |
private FileFilter actualFileFilter = null; |
280 |
} |
284 |
private GlobFilter globFilter = null; |
281 |
|
285 |
|
282 |
public FileChooserUIImpl(FileChooserBuilder.JFileChooserEx filechooser) { |
286 |
public FileChooserUIImpl(FileChooserBuilder.JFileChooserEx filechooser) { |
283 |
super(filechooser); |
287 |
super(filechooser); |
|
|
288 |
approveSelectionAction = new ApproveSelectionAction(); |
284 |
} |
289 |
} |
285 |
|
290 |
|
|
|
291 |
@Override |
292 |
public JFileChooserEx getFileChooser() { |
293 |
return fileChooser; |
294 |
} |
295 |
|
286 |
@Override |
296 |
@Override |
287 |
public void installUI(JComponent c) { |
297 |
public void installUI(JComponent c) { |
288 |
super.installUI(c); |
298 |
super.installUI(c); |
Lines 3108-3112
Link Here
|
3108 |
} |
3118 |
} |
3109 |
|
3119 |
|
3110 |
} // end of UpdateWorker |
3120 |
} // end of UpdateWorker |
|
|
3121 |
|
3122 |
@Override |
3123 |
public Action getApproveSelectionAction() { |
3124 |
return approveSelectionAction; |
3125 |
} |
3126 |
|
3127 |
protected class ApproveSelectionAction extends AbstractAction { |
3128 |
|
3129 |
protected ApproveSelectionAction() { |
3130 |
super(FilePane.ACTION_APPROVE_SELECTION); |
3131 |
} |
3132 |
|
3133 |
@Override |
3134 |
public void actionPerformed(final ActionEvent e) { |
3135 |
if (isDirectorySelected()) { |
3136 |
File dir = getDirectory(); |
3137 |
if (dir != null) { |
3138 |
try { |
3139 |
// Strip trailing ".." |
3140 |
dir = ShellFolder.getNormalizedFile(dir); |
3141 |
} catch (IOException ex) { |
3142 |
// Ok, use f as is |
3143 |
} |
3144 |
changeDirectory(dir); |
3145 |
return; |
3146 |
} |
3147 |
} |
3148 |
|
3149 |
String filename = getFileName(); |
3150 |
|
3151 |
if (filename != null) { |
3152 |
// VK: why isn't it just trim() ? - do we really need leading spaces?? |
3153 |
// Remove whitespaces from end of filename |
3154 |
int i = filename.length() - 1; |
3155 |
while (i >=0 && filename.charAt(i) <= ' ') { |
3156 |
i--; |
3157 |
} |
3158 |
filename = filename.substring(0, i + 1); |
3159 |
} |
3160 |
|
3161 |
if (filename == null || filename.length() == 0) { |
3162 |
// no file selected, multiple selection off, therefore cancel the approve action |
3163 |
resetGlobFilter(); |
3164 |
return; |
3165 |
} |
3166 |
|
3167 |
// Unix: Resolve '~' to user's home directory |
3168 |
if (getFileChooser().isUnix()) { |
3169 |
if (filename.startsWith("~/")) { |
3170 |
filename = getFileChooser().getHomePath() + filename.substring(1); |
3171 |
} else if (filename.equals("~")) { |
3172 |
filename = getFileChooser().getHomePath(); |
3173 |
} |
3174 |
} |
3175 |
|
3176 |
// in the case of single selectiom, use selectedFiles.get(0) |
3177 |
final List<File> selectedFiles = new ArrayList<>(); |
3178 |
|
3179 |
enableAllButCancel(false); |
3180 |
ApproveSelectionFinisher finisher = new ApproveSelectionFinisher(e, filename, selectedFiles); |
3181 |
JFileChooserEx chooser = getFileChooser(); |
3182 |
ApproveSelectionThreadWorker worker = new ApproveSelectionThreadWorker( |
3183 |
e, filename, chooser.isMultiSelectionEnabled(), |
3184 |
chooser.getCurrentDirectory(), chooser.getFileSystemView(), |
3185 |
selectedFiles, finisher); |
3186 |
APPROVE_RP.post(worker); |
3187 |
} |
3188 |
} |
3189 |
|
3190 |
private static class ApproveSelectionThreadWorker implements Runnable { |
3191 |
|
3192 |
private final ActionEvent e; |
3193 |
private final String filename; |
3194 |
private final boolean multySelection; |
3195 |
private final File currentDir; |
3196 |
private final FileSystemView fs; |
3197 |
private final List<File> selectedFiles; |
3198 |
private final ApproveSelectionFinisher finisher; |
3199 |
|
3200 |
public ApproveSelectionThreadWorker(ActionEvent e, String filename, boolean multySelection, File currentDir, FileSystemView fs, List<File> selectedFiles, ApproveSelectionFinisher finisher) { |
3201 |
this.e = e; |
3202 |
this.filename = filename; |
3203 |
this.multySelection = multySelection; |
3204 |
this.currentDir = currentDir; |
3205 |
this.fs = fs; |
3206 |
this.selectedFiles = selectedFiles; |
3207 |
this.finisher = finisher; |
3208 |
} |
3209 |
|
3210 |
@Override |
3211 |
public void run() { |
3212 |
|
3213 |
if (multySelection && filename.length() > 1 && |
3214 |
filename.charAt(0) == '"' && filename.charAt(filename.length() - 1) == '"') { |
3215 |
|
3216 |
// VK: double space between \" breaks this?! |
3217 |
String[] files = filename.substring(1, filename.length() - 1).split("\" \""); |
3218 |
// Optimize searching files by names in "children" array |
3219 |
Arrays.sort(files); |
3220 |
|
3221 |
File[] children = null; |
3222 |
int childIndex = 0; |
3223 |
|
3224 |
for (String str : files) { |
3225 |
File file = fs.createFileObject(str); |
3226 |
if (!file.isAbsolute()) { |
3227 |
if (children == null) { |
3228 |
children = fs.getFiles(currentDir, false); |
3229 |
Arrays.sort(children); |
3230 |
} |
3231 |
for (int k = 0; k < children.length; k++) { |
3232 |
int l = (childIndex + k) % children.length; |
3233 |
if (children[l].getName().equals(str)) { |
3234 |
file = children[l]; |
3235 |
childIndex = l + 1; |
3236 |
break; |
3237 |
} |
3238 |
} |
3239 |
} |
3240 |
selectedFiles.add(file); |
3241 |
} |
3242 |
} else { |
3243 |
File selectedFile = fs.createFileObject(filename); |
3244 |
if (!selectedFile.isAbsolute()) { |
3245 |
selectedFile = fs.getChild(currentDir, filename); |
3246 |
} |
3247 |
selectedFiles.add(selectedFile); |
3248 |
} |
3249 |
} |
3250 |
} |
3251 |
|
3252 |
private class ApproveSelectionFinisher implements Runnable { |
3253 |
|
3254 |
private final String filename; |
3255 |
private final List<File> selectedFiles; |
3256 |
private final ActionEvent e; |
3257 |
|
3258 |
public ApproveSelectionFinisher(ActionEvent e, String filename, List<File> selectedFiles) { |
3259 |
this.e = e; |
3260 |
this.selectedFiles = selectedFiles; |
3261 |
this.filename = filename; |
3262 |
} |
3263 |
|
3264 |
@Override |
3265 |
public void run() { |
3266 |
|
3267 |
JFileChooser chooser = getFileChooser(); |
3268 |
resetGlobFilter(); |
3269 |
|
3270 |
if (selectedFiles.size() == 1) { |
3271 |
|
3272 |
File selectedFile = selectedFiles.get(0); |
3273 |
|
3274 |
// check for wildcard pattern |
3275 |
FileFilter currentFilter = chooser.getFileFilter(); |
3276 |
if (!selectedFile.exists() && isGlobPattern(filename)) { |
3277 |
changeDirectory(selectedFile.getParentFile()); |
3278 |
if (globFilter == null) { |
3279 |
globFilter = new GlobFilter(); |
3280 |
} |
3281 |
try { |
3282 |
globFilter.setPattern(selectedFile.getName()); |
3283 |
if (!(currentFilter instanceof GlobFilter)) { |
3284 |
actualFileFilter = currentFilter; |
3285 |
} |
3286 |
chooser.setFileFilter(null); |
3287 |
chooser.setFileFilter(globFilter); |
3288 |
return; |
3289 |
} catch (PatternSyntaxException pse) { |
3290 |
// Not a valid glob pattern. Abandon filter. |
3291 |
} |
3292 |
} |
3293 |
|
3294 |
// Check for directory change action |
3295 |
boolean isDir = (selectedFile != null && selectedFile.isDirectory()); |
3296 |
boolean isTrav = (selectedFile != null && chooser.isTraversable(selectedFile)); |
3297 |
boolean isDirSelEnabled = chooser.isDirectorySelectionEnabled(); |
3298 |
boolean isFileSelEnabled = chooser.isFileSelectionEnabled(); |
3299 |
boolean isCtrl = (e != null && (e.getModifiers() & |
3300 |
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()) != 0); |
3301 |
|
3302 |
if (isDir && isTrav && (isCtrl || !isDirSelEnabled)) { |
3303 |
changeDirectory(selectedFile); |
3304 |
return; |
3305 |
} else if ((isDir || !isFileSelEnabled) |
3306 |
&& (!isDir || !isDirSelEnabled) |
3307 |
&& (!isDirSelEnabled || selectedFile.exists())) { |
3308 |
selectedFiles.clear(); |
3309 |
} |
3310 |
} |
3311 |
|
3312 |
|
3313 |
if (!selectedFiles.isEmpty()) { |
3314 |
if (chooser.isMultiSelectionEnabled()) { |
3315 |
final File[] selectedFilesArray = selectedFiles.toArray(new File[selectedFiles.size()]); |
3316 |
chooser.setSelectedFiles(selectedFilesArray); |
3317 |
// Do it again. This is a fix for bug 4949273 to force the |
3318 |
// selected value in case the ListSelectionModel clears it |
3319 |
// for non-existing file names. |
3320 |
chooser.setSelectedFiles(selectedFilesArray); |
3321 |
} else { |
3322 |
chooser.setSelectedFile(selectedFiles.get(0)); |
3323 |
} |
3324 |
chooser.approveSelection(); |
3325 |
} else { |
3326 |
if (chooser.isMultiSelectionEnabled()) { |
3327 |
chooser.setSelectedFiles(null); |
3328 |
} else { |
3329 |
chooser.setSelectedFile(null); |
3330 |
} |
3331 |
chooser.cancelSelection(); |
3332 |
} |
3333 |
enableAllButCancel(true); |
3334 |
} |
3335 |
} |
3336 |
|
3337 |
private void enableAllButCancel(boolean enable) { |
3338 |
FileChooserUIImpl.this.approveButton.setEnabled(enable); |
3339 |
FileChooserUIImpl.this.topCombo.setEnabled(enable); |
3340 |
FileChooserUIImpl.this.filenameTextField.setEditable(enable); |
3341 |
FileChooserUIImpl.this.tree.setEnabled(enable); |
3342 |
} |
3343 |
|
3344 |
private void changeDirectory(File dir) { |
3345 |
JFileChooser fc = getFileChooser(); |
3346 |
// Traverse shortcuts on Windows |
3347 |
if (dir != null && FilePane.usesShellFolder(fc)) { |
3348 |
try { |
3349 |
ShellFolder shellFolder = ShellFolder.getShellFolder(dir); |
3350 |
|
3351 |
if (shellFolder.isLink()) { |
3352 |
File linkedTo = shellFolder.getLinkLocation(); |
3353 |
|
3354 |
// If linkedTo is null we try to use dir |
3355 |
if (linkedTo != null) { |
3356 |
if (fc.isTraversable(linkedTo)) { |
3357 |
dir = linkedTo; |
3358 |
} else { |
3359 |
return; |
3360 |
} |
3361 |
} else { |
3362 |
dir = shellFolder; |
3363 |
} |
3364 |
} |
3365 |
} catch (FileNotFoundException ex) { |
3366 |
return; |
3367 |
} |
3368 |
} |
3369 |
fc.setCurrentDirectory(dir); |
3370 |
if (fc.getFileSelectionMode() == JFileChooser.FILES_AND_DIRECTORIES && |
3371 |
fc.getFileSystemView().isFileSystem(dir)) { |
3372 |
|
3373 |
setFileName(dir.getAbsolutePath()); |
3374 |
} |
3375 |
} |
3376 |
|
3377 |
private void resetGlobFilter() { |
3378 |
if (actualFileFilter != null) { |
3379 |
JFileChooser chooser = getFileChooser(); |
3380 |
FileFilter currentFilter = chooser.getFileFilter(); |
3381 |
if (currentFilter != null && currentFilter.equals(globFilter)) { |
3382 |
chooser.setFileFilter(actualFileFilter); |
3383 |
chooser.removeChoosableFileFilter(globFilter); |
3384 |
} |
3385 |
actualFileFilter = null; |
3386 |
} |
3387 |
} |
3388 |
|
3389 |
private static boolean isGlobPattern(String filename) { |
3390 |
return ((File.separatorChar == '\\' && (filename.indexOf('*') >= 0 |
3391 |
|| filename.indexOf('?') >= 0)) |
3392 |
|| (File.separatorChar == '/' && (filename.indexOf('*') >= 0 |
3393 |
|| filename.indexOf('?') >= 0 |
3394 |
|| filename.indexOf('[') >= 0))); |
3395 |
} |
3396 |
|
3397 |
|
3398 |
/* A file filter which accepts file patterns containing |
3399 |
* the special wildcards *? on Windows and *?[] on Unix. |
3400 |
*/ |
3401 |
class GlobFilter extends FileFilter { |
3402 |
Pattern pattern; |
3403 |
String globPattern; |
3404 |
|
3405 |
public void setPattern(String globPattern) { |
3406 |
char[] gPat = globPattern.toCharArray(); |
3407 |
char[] rPat = new char[gPat.length * 2]; |
3408 |
boolean isWin32 = (File.separatorChar == '\\'); |
3409 |
boolean inBrackets = false; |
3410 |
int j = 0; |
3411 |
|
3412 |
this.globPattern = globPattern; |
3413 |
|
3414 |
if (isWin32) { |
3415 |
// On windows, a pattern ending with *.* is equal to ending with * |
3416 |
int len = gPat.length; |
3417 |
if (globPattern.endsWith("*.*")) { |
3418 |
len -= 2; |
3419 |
} |
3420 |
for (int i = 0; i < len; i++) { |
3421 |
switch(gPat[i]) { |
3422 |
case '*': |
3423 |
rPat[j++] = '.'; |
3424 |
rPat[j++] = '*'; |
3425 |
break; |
3426 |
|
3427 |
case '?': |
3428 |
rPat[j++] = '.'; |
3429 |
break; |
3430 |
|
3431 |
case '\\': |
3432 |
rPat[j++] = '\\'; |
3433 |
rPat[j++] = '\\'; |
3434 |
break; |
3435 |
|
3436 |
default: |
3437 |
if ("+()^$.{}[]".indexOf(gPat[i]) >= 0) { |
3438 |
rPat[j++] = '\\'; |
3439 |
} |
3440 |
rPat[j++] = gPat[i]; |
3441 |
break; |
3442 |
} |
3443 |
} |
3444 |
} else { |
3445 |
for (int i = 0; i < gPat.length; i++) { |
3446 |
switch(gPat[i]) { |
3447 |
case '*': |
3448 |
if (!inBrackets) { |
3449 |
rPat[j++] = '.'; |
3450 |
} |
3451 |
rPat[j++] = '*'; |
3452 |
break; |
3453 |
|
3454 |
case '?': |
3455 |
rPat[j++] = inBrackets ? '?' : '.'; |
3456 |
break; |
3457 |
|
3458 |
case '[': |
3459 |
inBrackets = true; |
3460 |
rPat[j++] = gPat[i]; |
3461 |
|
3462 |
if (i < gPat.length - 1) { |
3463 |
switch (gPat[i+1]) { |
3464 |
case '!': |
3465 |
case '^': |
3466 |
rPat[j++] = '^'; |
3467 |
i++; |
3468 |
break; |
3469 |
|
3470 |
case ']': |
3471 |
rPat[j++] = gPat[++i]; |
3472 |
break; |
3473 |
} |
3474 |
} |
3475 |
break; |
3476 |
|
3477 |
case ']': |
3478 |
rPat[j++] = gPat[i]; |
3479 |
inBrackets = false; |
3480 |
break; |
3481 |
|
3482 |
case '\\': |
3483 |
if (i == 0 && gPat.length > 1 && gPat[1] == '~') { |
3484 |
rPat[j++] = gPat[++i]; |
3485 |
} else { |
3486 |
rPat[j++] = '\\'; |
3487 |
if (i < gPat.length - 1 && "*?[]".indexOf(gPat[i+1]) >= 0) { |
3488 |
rPat[j++] = gPat[++i]; |
3489 |
} else { |
3490 |
rPat[j++] = '\\'; |
3491 |
} |
3492 |
} |
3493 |
break; |
3494 |
|
3495 |
default: |
3496 |
//if ("+()|^$.{}<>".indexOf(gPat[i]) >= 0) { |
3497 |
if (!Character.isLetterOrDigit(gPat[i])) { |
3498 |
rPat[j++] = '\\'; |
3499 |
} |
3500 |
rPat[j++] = gPat[i]; |
3501 |
break; |
3502 |
} |
3503 |
} |
3504 |
} |
3505 |
this.pattern = Pattern.compile(new String(rPat, 0, j), Pattern.CASE_INSENSITIVE); |
3506 |
} |
3507 |
|
3508 |
@Override |
3509 |
public boolean accept(File f) { |
3510 |
if (f == null) { |
3511 |
return false; |
3512 |
} |
3513 |
if (f.isDirectory()) { |
3514 |
return true; |
3515 |
} |
3516 |
return pattern.matcher(f.getName()).matches(); |
3517 |
} |
3518 |
|
3519 |
@Override |
3520 |
public String getDescription() { |
3521 |
return globPattern; |
3522 |
} |
3523 |
} |
3111 |
|
3524 |
|
3112 |
} |
3525 |
} |