Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.desktop/macosx/classes/com/apple/laf/AquaFileSystemModel.java
41155 views
1
/*
2
* Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package com.apple.laf;
27
28
29
import java.awt.ComponentOrientation;
30
import java.beans.*;
31
import java.io.File;
32
import java.util.*;
33
import javax.swing.*;
34
import javax.swing.event.ListDataEvent;
35
import javax.swing.filechooser.FileSystemView;
36
import javax.swing.table.AbstractTableModel;
37
38
/**
39
* NavServices-like implementation of a file Table
40
*
41
* Some of it came from BasicDirectoryModel
42
*/
43
@SuppressWarnings("serial") // Superclass is not serializable across versions
44
class AquaFileSystemModel extends AbstractTableModel implements PropertyChangeListener {
45
private final JTable fFileList;
46
private FilesLoader filesLoader = null;
47
private Vector<File> files = null;
48
49
JFileChooser filechooser = null;
50
Vector<SortableFile> fileCache = null;
51
Object fileCacheLock;
52
53
Vector<File> directories = null;
54
int fetchID = 0;
55
56
private final boolean[] fSortAscending = {true, true};
57
// private boolean fSortAscending = true;
58
private boolean fSortNames = true;
59
private final String[] fColumnNames;
60
public static final String SORT_BY_CHANGED = "sortByChanged";
61
public static final String SORT_ASCENDING_CHANGED = "sortAscendingChanged";
62
63
public AquaFileSystemModel(final JFileChooser filechooser, final JTable filelist, final String[] colNames) {
64
fileCacheLock = new Object();
65
this.filechooser = filechooser;
66
fFileList = filelist;
67
fColumnNames = colNames;
68
validateFileCache();
69
updateSelectionMode();
70
}
71
72
void updateSelectionMode() {
73
// Save dialog lists can't be multi select, because all we're selecting is the next folder to open
74
final boolean b = filechooser.isMultiSelectionEnabled() && filechooser.getDialogType() != JFileChooser.SAVE_DIALOG;
75
fFileList.setSelectionMode(b ? ListSelectionModel.MULTIPLE_INTERVAL_SELECTION : ListSelectionModel.SINGLE_SELECTION);
76
}
77
78
public void propertyChange(final PropertyChangeEvent e) {
79
final String prop = e.getPropertyName();
80
if (prop == JFileChooser.DIRECTORY_CHANGED_PROPERTY || prop == JFileChooser.FILE_VIEW_CHANGED_PROPERTY || prop == JFileChooser.FILE_FILTER_CHANGED_PROPERTY || prop == JFileChooser.FILE_HIDING_CHANGED_PROPERTY) {
81
invalidateFileCache();
82
validateFileCache();
83
} else if (prop.equals(JFileChooser.MULTI_SELECTION_ENABLED_CHANGED_PROPERTY)) {
84
updateSelectionMode();
85
} else if (prop == JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY) {
86
invalidateFileCache();
87
validateFileCache();
88
} else if (prop.equals("componentOrientation")) {
89
ComponentOrientation o = (ComponentOrientation) e.getNewValue();
90
JFileChooser cc = (JFileChooser) e.getSource();
91
if (o != e.getOldValue()) {
92
cc.applyComponentOrientation(o);
93
}
94
fFileList.setComponentOrientation(o);
95
fFileList.getParent().getParent().setComponentOrientation(o);
96
97
}
98
if (prop == SORT_BY_CHANGED) {// $ Ought to just resort
99
fSortNames = (((Integer)e.getNewValue()).intValue() == 0);
100
invalidateFileCache();
101
validateFileCache();
102
fFileList.repaint();
103
}
104
if (prop == SORT_ASCENDING_CHANGED) {
105
final int sortColumn = (fSortNames ? 0 : 1);
106
fSortAscending[sortColumn] = ((Boolean)e.getNewValue()).booleanValue();
107
invalidateFileCache();
108
validateFileCache();
109
fFileList.repaint();
110
}
111
}
112
113
public void invalidateFileCache() {
114
files = null;
115
directories = null;
116
117
synchronized(fileCacheLock) {
118
if (fileCache != null) {
119
final int lastRow = fileCache.size();
120
fileCache = null;
121
fireTableRowsDeleted(0, lastRow);
122
}
123
}
124
}
125
126
public Vector<File> getDirectories() {
127
if (directories != null) { return directories; }
128
return directories;
129
}
130
131
public Vector<File> getFiles() {
132
if (files != null) { return files; }
133
files = new Vector<File>();
134
directories = new Vector<File>();
135
directories.addElement(filechooser.getFileSystemView().createFileObject(filechooser.getCurrentDirectory(), ".."));
136
137
synchronized(fileCacheLock) {
138
for (int i = 0; i < fileCache.size(); i++) {
139
final SortableFile sf = fileCache.elementAt(i);
140
final File f = sf.fFile;
141
if (filechooser.isTraversable(f)) {
142
directories.addElement(f);
143
} else {
144
files.addElement(f);
145
}
146
}
147
}
148
149
return files;
150
}
151
152
public void runWhenDone(final Runnable runnable){
153
synchronized (fileCacheLock) {
154
if (filesLoader != null) {
155
if (filesLoader.loadThread.isAlive()) {
156
filesLoader.queuedTasks.add(runnable);
157
return;
158
}
159
}
160
161
SwingUtilities.invokeLater(runnable);
162
}
163
}
164
165
public void validateFileCache() {
166
final File currentDirectory = filechooser.getCurrentDirectory();
167
168
if (currentDirectory == null) {
169
invalidateFileCache();
170
return;
171
}
172
173
if (filesLoader != null) {
174
// interrupt
175
filesLoader.loadThread.interrupt();
176
}
177
178
fetchID++;
179
180
// PENDING(jeff) pick the size more sensibly
181
invalidateFileCache();
182
synchronized(fileCacheLock) {
183
fileCache = new Vector<SortableFile>(50);
184
}
185
186
filesLoader = new FilesLoader(currentDirectory, fetchID);
187
}
188
189
public int getColumnCount() {
190
return 2;
191
}
192
193
public String getColumnName(final int col) {
194
return fColumnNames[col];
195
}
196
197
public Class<? extends Object> getColumnClass(final int col) {
198
if (col == 0) return File.class;
199
return Date.class;
200
}
201
202
public int getRowCount() {
203
synchronized(fileCacheLock) {
204
if (fileCache != null) {
205
return fileCache.size();
206
}
207
return 0;
208
}
209
}
210
211
// SAK: Part of fix for 3168263. The fileCache contains
212
// SortableFiles, so when finding a file in the list we need to
213
// first create a sortable file.
214
public boolean contains(final File o) {
215
synchronized(fileCacheLock) {
216
if (fileCache != null) {
217
return fileCache.contains(new SortableFile(o));
218
}
219
return false;
220
}
221
}
222
223
public int indexOf(final File o) {
224
synchronized(fileCacheLock) {
225
if (fileCache != null) {
226
final boolean isAscending = fSortNames ? fSortAscending[0] : fSortAscending[1];
227
final int row = fileCache.indexOf(new SortableFile(o));
228
return isAscending ? row : fileCache.size() - row - 1;
229
}
230
return 0;
231
}
232
}
233
234
// AbstractListModel interface
235
public Object getElementAt(final int row) {
236
return getValueAt(row, 0);
237
}
238
239
// AbstractTableModel interface
240
241
public Object getValueAt(int row, final int col) {
242
if (row < 0 || col < 0) return null;
243
final boolean isAscending = fSortNames ? fSortAscending[0] : fSortAscending[1];
244
synchronized(fileCacheLock) {
245
if (fileCache != null) {
246
if (!isAscending) row = fileCache.size() - row - 1;
247
return fileCache.elementAt(row).getValueAt(col);
248
}
249
return null;
250
}
251
}
252
253
// PENDING(jeff) - implement
254
public void intervalAdded(final ListDataEvent e) {
255
}
256
257
// PENDING(jeff) - implement
258
public void intervalRemoved(final ListDataEvent e) {
259
}
260
261
protected void sort(final Vector<Object> v) {
262
if (fSortNames) sSortNames.quickSort(v, 0, v.size() - 1);
263
else sSortDates.quickSort(v, 0, v.size() - 1);
264
}
265
266
// Liberated from the 1.1 SortDemo
267
//
268
// This is a generic version of C.A.R Hoare's Quick Sort
269
// algorithm. This will handle arrays that are already
270
// sorted, and arrays with duplicate keys.<BR>
271
//
272
// If you think of a one dimensional array as going from
273
// the lowest index on the left to the highest index on the right
274
// then the parameters to this function are lowest index or
275
// left and highest index or right. The first time you call
276
// this function it will be with the parameters 0, a.length - 1.
277
//
278
// @param a an integer array
279
// @param lo0 left boundary of array partition
280
// @param hi0 right boundary of array partition
281
abstract class QuickSort {
282
final void quickSort(final Vector<Object> v, final int lo0, final int hi0) {
283
int lo = lo0;
284
int hi = hi0;
285
SortableFile mid;
286
287
if (hi0 > lo0) {
288
// Arbitrarily establishing partition element as the midpoint of
289
// the array.
290
mid = (SortableFile)v.elementAt((lo0 + hi0) / 2);
291
292
// loop through the array until indices cross
293
while (lo <= hi) {
294
// find the first element that is greater than or equal to
295
// the partition element starting from the left Index.
296
//
297
// Nasty to have to cast here. Would it be quicker
298
// to copy the vectors into arrays and sort the arrays?
299
while ((lo < hi0) && lt((SortableFile)v.elementAt(lo), mid)) {
300
++lo;
301
}
302
303
// find an element that is smaller than or equal to
304
// the partition element starting from the right Index.
305
while ((hi > lo0) && lt(mid, (SortableFile)v.elementAt(hi))) {
306
--hi;
307
}
308
309
// if the indexes have not crossed, swap
310
if (lo <= hi) {
311
swap(v, lo, hi);
312
++lo;
313
--hi;
314
}
315
}
316
317
// If the right index has not reached the left side of array
318
// must now sort the left partition.
319
if (lo0 < hi) {
320
quickSort(v, lo0, hi);
321
}
322
323
// If the left index has not reached the right side of array
324
// must now sort the right partition.
325
if (lo < hi0) {
326
quickSort(v, lo, hi0);
327
}
328
329
}
330
}
331
332
private void swap(final Vector<Object> a, final int i, final int j) {
333
final Object T = a.elementAt(i);
334
a.setElementAt(a.elementAt(j), i);
335
a.setElementAt(T, j);
336
}
337
338
protected abstract boolean lt(SortableFile a, SortableFile b);
339
}
340
341
class QuickSortNames extends QuickSort {
342
protected boolean lt(final SortableFile a, final SortableFile b) {
343
final String aLower = a.fName.toLowerCase();
344
final String bLower = b.fName.toLowerCase();
345
return aLower.compareTo(bLower) < 0;
346
}
347
}
348
349
class QuickSortDates extends QuickSort {
350
protected boolean lt(final SortableFile a, final SortableFile b) {
351
return a.fDateValue < b.fDateValue;
352
}
353
}
354
355
// for speed in sorting, displaying
356
class SortableFile /* extends FileView */{
357
File fFile;
358
String fName;
359
long fDateValue;
360
Date fDate;
361
362
SortableFile(final File f) {
363
fFile = f;
364
fName = fFile.getName();
365
fDateValue = fFile.lastModified();
366
fDate = new Date(fDateValue);
367
}
368
369
public Object getValueAt(final int col) {
370
if (col == 0) return fFile;
371
return fDate;
372
}
373
374
public boolean equals(final Object other) {
375
final SortableFile otherFile = (SortableFile)other;
376
return otherFile.fFile.equals(fFile);
377
}
378
379
@Override
380
public int hashCode() {
381
return Objects.hashCode(fFile);
382
}
383
}
384
385
class FilesLoader implements Runnable {
386
Vector<Runnable> queuedTasks = new Vector<>();
387
File currentDirectory = null;
388
int fid;
389
Thread loadThread;
390
391
public FilesLoader(final File currentDirectory, final int fid) {
392
this.currentDirectory = currentDirectory;
393
this.fid = fid;
394
String name = "Aqua L&F File Loading Thread";
395
this.loadThread = new Thread(null, this, name, 0, false);
396
this.loadThread.start();
397
}
398
399
@Override
400
public void run() {
401
final Vector<DoChangeContents> runnables = new Vector<DoChangeContents>(10);
402
final FileSystemView fileSystem = filechooser.getFileSystemView();
403
404
final File[] list = fileSystem.getFiles(currentDirectory, filechooser.isFileHidingEnabled());
405
406
final Vector<Object> acceptsList = new Vector<Object>();
407
408
for (final File element : list) {
409
// Return all files to the file chooser. The UI will disable or enable
410
// the file name if the current filter approves.
411
acceptsList.addElement(new SortableFile(element));
412
}
413
414
// Sort based on settings.
415
sort(acceptsList);
416
417
// Don't separate directories from files
418
Vector<SortableFile> chunk = new Vector<SortableFile>(10);
419
final int listSize = acceptsList.size();
420
// run through list grabbing file/dirs in chunks of ten
421
for (int i = 0; i < listSize;) {
422
SortableFile f;
423
for (int j = 0; j < 10 && i < listSize; j++, i++) {
424
f = (SortableFile)acceptsList.elementAt(i);
425
chunk.addElement(f);
426
}
427
final DoChangeContents runnable = new DoChangeContents(chunk, fid);
428
runnables.addElement(runnable);
429
SwingUtilities.invokeLater(runnable);
430
chunk = new Vector<SortableFile>(10);
431
if (loadThread.isInterrupted()) {
432
// interrupted, cancel all runnables
433
cancelRunnables(runnables);
434
return;
435
}
436
}
437
438
synchronized (fileCacheLock) {
439
for (final Runnable r : queuedTasks) {
440
SwingUtilities.invokeLater(r);
441
}
442
}
443
}
444
445
public void cancelRunnables(final Vector<DoChangeContents> runnables) {
446
for (int i = 0; i < runnables.size(); i++) {
447
runnables.elementAt(i).cancel();
448
}
449
}
450
}
451
452
class DoChangeContents implements Runnable {
453
private Vector<SortableFile> contentFiles;
454
private boolean doFire = true;
455
private final Object lock = new Object();
456
private final int fid;
457
458
public DoChangeContents(final Vector<SortableFile> files, final int fid) {
459
this.contentFiles = files;
460
this.fid = fid;
461
}
462
463
synchronized void cancel() {
464
synchronized(lock) {
465
doFire = false;
466
}
467
}
468
469
public void run() {
470
if (fetchID == fid) {
471
synchronized(lock) {
472
if (doFire) {
473
synchronized(fileCacheLock) {
474
if (fileCache != null) {
475
for (int i = 0; i < contentFiles.size(); i++) {
476
fileCache.addElement(contentFiles.elementAt(i));
477
fireTableRowsInserted(i, i);
478
}
479
}
480
}
481
}
482
contentFiles = null;
483
directories = null;
484
}
485
}
486
}
487
}
488
489
final QuickSortNames sSortNames = new QuickSortNames();
490
final QuickSortDates sSortDates = new QuickSortDates();
491
}
492
493