00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 package ikayaki.gui;
00024
00025 import ikayaki.*;
00026
00027 import javax.swing.*;
00028 import javax.swing.border.Border;
00029 import javax.swing.event.ListSelectionEvent;
00030 import javax.swing.event.ListSelectionListener;
00031 import javax.swing.table.AbstractTableModel;
00032 import javax.swing.table.TableColumn;
00033 import javax.swing.table.TableColumnModel;
00034 import java.awt.*;
00035 import java.awt.event.ActionEvent;
00036 import java.awt.event.ActionListener;
00037 import java.awt.event.MouseAdapter;
00038 import java.awt.event.MouseEvent;
00039 import java.io.File;
00040 import java.io.FileFilter;
00041 import java.text.DateFormat;
00042 import java.util.Arrays;
00043 import java.util.Comparator;
00044 import java.util.Date;
00045
00051 public class ProjectExplorerTable extends JTable implements ProjectListener {
00052
00056 private final ProjectComponent parent;
00057
00061 private boolean isCalibration;
00062
00063 private final ProjectExplorerTableModel explorerTableModel;
00064
00065 private final Comparator<File> explorerTableComparator = new ProjectExplorerTableComparator();
00066
00071 private Thread projectTypeCacher = new Thread();
00072
00076 private File directory;
00077
00081 private File[] files = new File[0];
00082
00086 private int selectedFile = -1;
00087
00091 private int explorerTableSortColumn = 0;
00092
00093
00094 private static final int COLUMN_UNDEFINED = -1;
00095 public static final int COLUMN_FILENAME = 0;
00096 public static final int COLUMN_TYPE = 1;
00097 public static final int COLUMN_LASTMOD = 2;
00098 public static final int COLUMN_LASTMEASURE = 3;
00099 public static final int COLUMN_UNMEASURED = 4;
00100 public static final String[] column_name = {"Name", "Type", "Modified", "Measured", "Elapsed"};
00101
00102
00103 public static final int[] default_columns = {COLUMN_FILENAME, COLUMN_TYPE, COLUMN_LASTMOD};
00104 public static final int[] calibration_columns = {COLUMN_FILENAME, COLUMN_LASTMEASURE, COLUMN_UNMEASURED};
00105
00110 private int[] columns = new int[0];
00111
00117 public ProjectExplorerTable(ProjectComponent parent) {
00118 this(parent, false);
00119 }
00120
00127 public ProjectExplorerTable(ProjectComponent parent, boolean isCalibration) {
00128 this.parent = parent;
00129 this.isCalibration = isCalibration;
00130
00131
00132 explorerTableModel = new ProjectExplorerTableModel();
00133 this.setModel(explorerTableModel);
00134
00135
00136
00137 this.getTableHeader().setReorderingAllowed(false);
00138 this.getTableHeader().setResizingAllowed(false);
00139 this.setShowGrid(false);
00140 this.setIntercellSpacing(new Dimension(0, 0));
00141 this.setDefaultRenderer(StyledWrapper.class, new StyledTableCellRenderer());
00142
00143
00144
00145
00146
00147 if (this.isCalibration) {
00148 setColumns(calibration_columns);
00149 } else {
00150 setColumns(default_columns);
00151 }
00152
00153
00154
00160 this.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
00161 public void valueChanged(ListSelectionEvent e) {
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173 if (e.getValueIsAdjusting() || getSelectedRow() == selectedFile) return;
00174 if (getSelectedRowCount() > 1) return;
00175
00176 Project project = getSelectedRow() == -1 ? null : Project.loadProject(files[getSelectedRow()]);
00177
00178
00179 if (project == null) {
00180
00181 if (selectedFile == -1) {
00182 clearSelection();
00183 } else {
00184 setRowSelectionInterval(selectedFile, selectedFile);
00185 }
00186 } else {
00187 ProjectExplorerTable.this.parent.setProject(project);
00188 }
00189 }
00190 });
00191
00196 this.addMouseListener(new MouseAdapter() {
00197 public void mouseClicked(MouseEvent e) {
00198
00199 if (e.getButton() != MouseEvent.BUTTON3) return;
00200
00201 int[] row;
00202 if (getSelectedRowCount() > 1) {
00203 row = getSelectedRows();
00204 } else {
00205 row = new int[]{rowAtPoint(e.getPoint())};
00206 }
00207
00208
00209 File[] file = new File[row.length];
00210 for (int n = 0; n < row.length; n++) file[n] = files[row[n]];
00211
00212
00213 ProjectExplorerPopupMenu explorerTablePopup = new ProjectExplorerPopupMenu(file);
00214 explorerTablePopup.show(ProjectExplorerTable.this, e.getX(), e.getY());
00215 }
00216 });
00217
00221 this.getTableHeader().addMouseListener(new MouseAdapter() {
00222 public void mouseClicked(MouseEvent e) {
00223
00224 if (e.getButton() != MouseEvent.BUTTON1) return;
00225
00226 TableColumnModel cm = getColumnModel();
00227 int viewColumn = cm.getColumnIndexAtX(e.getX());
00228 explorerTableSortColumn = cm.getColumn(viewColumn).getModelIndex();
00229
00230
00231 for (int col = 0; col < getColumnCount(); col++) {
00232 cm.getColumn(col).setHeaderValue(getColumnName(col));
00233 }
00234 getTableHeader().repaint();
00235
00236
00237 setDirectory(directory);
00238 }
00239 });
00240 }
00241
00247 public void setColumns(int[] columns) {
00248 if (columns != null) this.columns = columns;
00249
00250
00251 explorerTableModel.fireTableStructureChanged();
00252
00253
00254 for (int col = 0; col < this.columns.length; col++) {
00255 TableColumn column = this.getColumnModel().getColumn(col);
00256 switch (this.columns[col]) {
00257 case COLUMN_FILENAME:
00258 column.setPreferredWidth(130);
00259 break;
00260 case COLUMN_TYPE:
00261 column.setMinWidth(55);
00262 column.setMaxWidth(55);
00263 break;
00264 case COLUMN_LASTMOD:
00265 column.setMinWidth(95);
00266 column.setMaxWidth(95);
00267 break;
00268 case COLUMN_LASTMEASURE:
00269 column.setMinWidth(100);
00270 column.setMaxWidth(100);
00271 break;
00272 case COLUMN_UNMEASURED:
00273 column.setMinWidth(45);
00274 column.setMaxWidth(45);
00275 break;
00276 }
00277 }
00278 }
00279
00287 public void fitColumnWidths() {
00288 for (int col = 0; col < columns.length; col++) {
00289 if (columns[col] != COLUMN_FILENAME) {
00290
00291
00292 int width = 0;
00293 Component comp;
00294 for (int row = 0; row < getRowCount(); row++) {
00295 comp = getCellRenderer(row, col).getTableCellRendererComponent(this,
00296 getValueAt(row, col), false, false, row, col);
00297 width = Math.max(width, comp.getPreferredSize().width);
00298 }
00299 width += 5;
00300 if (columnModel.getColumn(col).getMaxWidth() < width) {
00301
00302 TableColumn c = columnModel.getColumn(col);
00303 c.setMaxWidth(width);
00304 c.setMinWidth(width);
00305 }
00306 }
00307 }
00308 }
00309
00315 public void setDirectory(File directory) {
00316 this.directory = directory;
00317 this.files = getProjectFiles(this.directory);
00318
00319
00320 if (explorerTableSortColumn != COLUMN_UNDEFINED) Arrays.sort(files, explorerTableComparator);
00321
00322
00323 selectedFile = -1;
00324 if (parent.getProject() != null) {
00325 for (int n = 0; n < files.length; n++) {
00326 if (parent.getProject().getFile().equals(files[n])) selectedFile = n;
00327 }
00328 }
00329
00330
00331 explorerTableModel.fireTableDataChanged();
00332 if (selectedFile != -1) {
00333 setRowSelectionInterval(selectedFile, selectedFile);
00334 SwingUtilities.invokeLater(new Runnable() {
00335 public void run() {
00336 scrollToRow(selectedFile);
00337 }
00338 });
00339 }
00340 }
00341
00347 private void scrollToRow(int rowIndex) {
00348 scrollRectToVisible(getCellRect(rowIndex, rowIndex, true));
00349 }
00350
00357 private File[] getProjectFiles(File directory) {
00358 if (directory == null) return new File[0];
00359
00360 File[] files = directory.listFiles(new FileFilter() {
00361 public boolean accept(File file) {
00362 return file.isFile() && file.getName().endsWith(Ikayaki.FILE_TYPE)
00363 && (!isCalibration || Project.getType(file) == Project.Type.CALIBRATION);
00364 }
00365 });
00366
00367 if (files == null) return new File[0];
00368
00369
00370
00371
00372
00373 try {
00374 projectTypeCacher.interrupt();
00375 projectTypeCacher.join();
00376 } catch (InterruptedException e) {
00377 }
00378
00379
00380 final File[] cacheFiles = files.clone();
00381
00382 projectTypeCacher = new Thread(new Runnable() {
00383 public void run() {
00384 for (File file : cacheFiles) {
00385 if (Thread.interrupted()) return;
00386 if (file.canRead()) Project.getType(file);
00387
00388
00389 SwingUtilities.invokeLater(new Runnable() {
00390 public void run() {
00391 fitColumnWidths();
00392 }
00393 });
00394 }
00395 }
00396 });
00397
00398
00399 projectTypeCacher.setPriority(Thread.MIN_PRIORITY);
00400 projectTypeCacher.start();
00401
00402 return files;
00403 }
00404
00410 public void projectUpdated(ProjectEvent event) {
00411 explorerTableModel.projectUpdated(event);
00412 }
00413
00417 private class ProjectExplorerTableModel extends AbstractTableModel implements ProjectListener {
00418
00419 private final StyledWrapper defaultWrapper = Settings.getDefaultWrapperInstance();
00420 private final StyledWrapper measuringWrapper = Settings.getMeasuringWrapperInstance();
00421 private final StyledWrapper doneRecentlyWrapper = Settings.getDoneRecentlyWrapperInstance();
00422 private final Font calibrationNoticeFont = ProjectExplorerTable.this.getFont().deriveFont(Font.BOLD);
00423
00427 private File measuringProjectFile;
00428
00432 private File doneRecentlyProjectFile;
00433
00434 public ProjectExplorerTableModel() {
00435
00436
00437 Border emptyBorder = BorderFactory.createEmptyBorder(1, 1, 1, 1);
00438 defaultWrapper.border = emptyBorder;
00439 defaultWrapper.selectedBorder = emptyBorder;
00440 defaultWrapper.focusBorder = emptyBorder;
00441 defaultWrapper.selectedFocusBorder = emptyBorder;
00442 measuringWrapper.border = emptyBorder;
00443 measuringWrapper.selectedBorder = emptyBorder;
00444 measuringWrapper.focusBorder = emptyBorder;
00445 measuringWrapper.selectedFocusBorder = emptyBorder;
00446 doneRecentlyWrapper.border = emptyBorder;
00447 doneRecentlyWrapper.selectedBorder = emptyBorder;
00448 doneRecentlyWrapper.focusBorder = emptyBorder;
00449 doneRecentlyWrapper.selectedFocusBorder = emptyBorder;
00450
00451
00452
00453
00454
00455 new Timer(5 * 60 * 1000, new ActionListener() {
00456 public void actionPerformed(ActionEvent e) {
00457
00458
00459 explorerTableModel.fireTableRowsUpdated(0, getRowCount() - 1);
00460
00461 }
00462 }).start();
00463 }
00464
00465 public String getColumnName(int column) {
00466
00467
00468
00469 return column_name[columns[column]] ;
00470 }
00471
00472 public int getRowCount() {
00473 return files.length;
00474 }
00475
00476 public int getColumnCount() {
00477 return columns.length;
00478 }
00479
00480 public Object getValueAt(int row, int column) {
00481
00482
00483 File file = files[row];
00484 Object value;
00485
00486 switch (columns[column]) {
00487 case COLUMN_FILENAME:
00488 String filename = file.getName();
00489 value = filename.substring(0, filename.length() - Ikayaki.FILE_TYPE.length());
00490 break;
00491
00492 case COLUMN_TYPE:
00493 Project.Type type = Project.getType(file);
00494 if (type != null) {
00495 value = type.toString();
00496 } else {
00497 value = null;
00498 }
00499 break;
00500
00501 case COLUMN_LASTMOD:
00502 value = DateFormat.getInstance().format(file.lastModified());
00503
00504 break;
00505
00506 case COLUMN_LASTMEASURE:
00507 Project p = Project.loadProject(file);
00508 if (p == null) {
00509 value = null;
00510 break;
00511 }
00512 Date date = p.getTimestamp();
00513
00514 if (date == null) {
00515 value = null;
00516 } else {
00517 value = DateFormat.getInstance().format(date);
00518 }
00519 break;
00520
00521 case COLUMN_UNMEASURED:
00522 p = Project.loadProject(file);
00523 if (p == null) {
00524 value = null;
00525 break;
00526 }
00527 date = p.getTimestamp();
00528 if (date == null) {
00529 value = null;
00530 } else {
00531 value = (new Date().getTime() - date.getTime()) / 3600000 + " h";
00532 }
00533 break;
00534
00535 default:
00536 assert false;
00537 value = null;
00538 break;
00539 }
00540
00541
00542 StyledWrapper wrapper;
00543 if (file.equals(measuringProjectFile)) {
00544 wrapper = measuringWrapper;
00545 } else if (file.equals(doneRecentlyProjectFile)) {
00546 wrapper = doneRecentlyWrapper;
00547 } else {
00548 wrapper = defaultWrapper;
00549 }
00550 wrapper.font = null;
00551
00552
00553 if (isCalibration) {
00554 Project p = Project.loadProject(file);
00555 if (p == null) {
00556 return null;
00557 }
00558 Date date = p.getTimestamp();
00559 if (date == null) {
00560 wrapper.font = calibrationNoticeFont;
00561 } else {
00562
00563 int hoursElapsed = (int) (new Date().getTime() - date.getTime()) / 3600000;
00564 if (hoursElapsed >= 18) {
00565 wrapper.font = calibrationNoticeFont;
00566 }
00567 }
00568 }
00569
00570
00571 wrapper.value = value;
00572 return wrapper;
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584 }
00585
00586 @Override public Class<?> getColumnClass(int columnIndex) {
00587 return StyledWrapper.class;
00588 }
00589
00600 public void projectUpdated(ProjectEvent event) {
00601 if (event.getType() == ProjectEvent.Type.FILE_SAVED) {
00602 File saved = event.getProject().getFile();
00603 for (int i = 0; i < files.length; i++) {
00604 if (files[i].equals(saved)) {
00605 explorerTableModel.fireTableRowsUpdated(i, i);
00606 return;
00607 }
00608 }
00609
00610 } else if (event.getType() == ProjectEvent.Type.STATE_CHANGED) {
00611 File file = event.getProject().getFile();
00612 boolean repaintTable = false;
00613
00614 Project.State state = event.getProject().getState();
00615 if (state == null) {
00616 state = Project.State.IDLE;
00617 }
00618 switch (state) {
00619 case IDLE:
00620 if (file.equals(measuringProjectFile)) {
00621
00622 measuringProjectFile = null;
00623 doneRecentlyProjectFile = file;
00624 repaintTable = true;
00625 }
00626 break;
00627 case MEASURING:
00628 case PAUSED:
00629 case ABORTED:
00630
00631 measuringProjectFile = file;
00632 doneRecentlyProjectFile = null;
00633 repaintTable = true;
00634 break;
00635 default:
00636 assert false;
00637 break;
00638 }
00639
00640
00641 if (repaintTable) {
00642
00643 int[] selectedRows = ProjectExplorerTable.this.getSelectedRows();
00644 fireTableDataChanged();
00645 for (int i : selectedRows) {
00646 ProjectExplorerTable.this.getSelectionModel().addSelectionInterval(i, i);
00647 }
00648 }
00649 }
00650 }
00651 }
00652
00656 private class ProjectExplorerTableComparator implements Comparator<File> {
00657 public int compare(File a, File b) {
00658 switch (columns[explorerTableSortColumn]) {
00659 case COLUMN_FILENAME:
00660 return a.compareTo(b);
00661 case COLUMN_TYPE:
00662
00663 Project.Type atype = Project.getType(a), btype = Project.getType(b);
00664 if (atype == null && btype == null) return 0;
00665 if (atype == null) return 1;
00666 if (btype == null) return -1;
00667
00668 return atype.compareTo(btype);
00669 case COLUMN_LASTMOD:
00670 long diff = a.lastModified() - b.lastModified();
00671 return diff == 0 ? 0 : (diff < 0 ? -1 : 1);
00672 case COLUMN_LASTMEASURE:
00673 return compareTimestamps(a, b);
00674 case COLUMN_UNMEASURED:
00675 return -compareTimestamps(a, b);
00676 default:
00677 return 0;
00678 }
00679 }
00680
00688 private int compareTimestamps(File a, File b) {
00689 Project aproject = Project.loadProject(a);
00690 Project bproject = Project.loadProject(b);
00691 Date adate = aproject == null ? null : aproject.getTimestamp();
00692 Date bdate = bproject == null ? null : bproject.getTimestamp();
00693 if (adate == null && bdate == null) return 0;
00694 if (adate == null) return -1;
00695 if (bdate == null) return 1;
00696 return adate.compareTo(bdate);
00697 }
00698 }
00699
00705 private class ProjectExplorerPopupMenu extends JPopupMenu {
00706
00710 private File[] files;
00711
00715 private File directory;
00716
00722 public ProjectExplorerPopupMenu(File[] xfiles) {
00723 if (xfiles == null || xfiles.length == 0) return;
00724
00725 this.files = xfiles;
00726 this.directory = files[0].getParentFile();
00727
00728 String filename, basename;
00729 if (files.length == 1) {
00730 filename = files[0].getName();
00731 basename = filename;
00732 if (basename.toLowerCase().endsWith(Ikayaki.FILE_TYPE)) {
00733 basename = basename.substring(0, basename.length() - Ikayaki.FILE_TYPE.length());
00734 }
00735 } else {
00736 filename = "selected " + files.length + " files";
00737 basename = "*";
00738 }
00739
00740 JMenuItem export = new JMenuItem("Export " + filename + " to");
00741 export.setFont(export.getFont().deriveFont(Font.BOLD));
00742 export.setEnabled(false);
00743 this.add(export);
00744
00745
00746 for (File dir : new File[]{null, directory, new File("A:/")}) {
00747 for (String type : new String[]{"dat", "tdt" }) {
00748 JMenuItem exportitem;
00749 if (dir == null) {
00750 exportitem = new JMenuItem(type.toUpperCase() + " file" +
00751 (files.length > 1 ? "s" : "") + "...");
00752 } else {
00753 exportitem = new JMenuItem(new File(dir, basename + "." + type).toString());
00754 }
00755
00756 this.add(exportitem);
00757
00762 exportitem.addActionListener(new ActionListener() {
00763 public void actionPerformed(ActionEvent e) {
00764 String filename = e.getActionCommand();
00765 String filetype = filename.substring(filename.length() - 3);
00766 File exportdir;
00767 boolean dirHasFile = false;
00768
00769 if (filetype.equals("...")) {
00770 filetype = filename.substring(0, 3).toLowerCase();
00771 JFileChooser chooser = new JFileChooser(directory);
00772
00773 if (files.length > 1) {
00774 chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
00775 } else {
00776 dirHasFile = true;
00777 }
00778
00779 chooser.setFileFilter(
00780 new GenericFileFilter(filetype.toUpperCase() + " File", filetype));
00781
00782 if (chooser.showSaveDialog(ProjectExplorerTable.this) == JFileChooser.APPROVE_OPTION) {
00783 exportdir = chooser.getSelectedFile();
00784 } else {
00785 return;
00786 }
00787
00788 } else {
00789 exportdir = new File(filename).getParentFile();
00790 }
00791
00792
00793 for (File f : files) {
00794
00795
00796
00797
00798
00799
00800
00801
00802
00803
00804
00805
00806 File exportfile;
00807 if (dirHasFile) {
00808 exportfile = exportdir;
00809 if (!exportfile.getName().toLowerCase().endsWith("." + filetype)) {
00810 exportfile = new File(exportfile.toString() + "." + filetype);
00811 }
00812 } else {
00813 String exportname = f.getName();
00814 if (exportname.toLowerCase().endsWith(Ikayaki.FILE_TYPE)) {
00815 exportname = exportname.substring(0,
00816 exportname.length() - Ikayaki.FILE_TYPE.length());
00817 }
00818 exportname += "." + filetype;
00819 exportfile = new File(exportdir, exportname);
00820 }
00821
00822
00823
00824 boolean ok = false;
00825 if (filetype.equals("dat")) {
00826 ok = Project.loadProject(f).exportToDAT(exportfile);
00827 } else if (filetype.equals("tdt")) {
00828 ok = Project.loadProject(f).exportToTDT(exportfile);
00829 } else if (filetype.equals("srm")) ok = Project.loadProject(f).exportToSRM(exportfile);
00830
00831
00832
00833
00834
00835
00836
00837
00838
00839 }
00840 }
00841 });
00842 }
00843 }
00844 }
00845 }
00846 }