Changeset 13927 in osm
- Timestamp:
- 2009-02-28T20:34:43+01:00 (16 years ago)
- Location:
- applications/editors/josm/plugins
- Files:
-
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
applications/editors/josm/plugins/agpifoj/src/org/openstreetmap/josm/plugins/agpifoj/AgpifojDialog.java
r13497 r13927 175 175 } 176 176 } 177 178 /** 179 * Returns whether an image is currently displayed 180 * @return If image is currently displayed 181 */ 182 public boolean hasImage() { 183 return currentEntry != null; 184 } 177 185 } -
applications/editors/josm/plugins/agpifoj/src/org/openstreetmap/josm/plugins/agpifoj/AgpifojLayer.java
r13497 r13927 53 53 54 54 private int currentPhoto = -1; 55 56 // These are used by the auto-guess function to store the result, 57 // so when the dialig is re-opened the users modifications don't 58 // get overwritten 59 public boolean hasTimeoffset = false; 60 public long timeoffset = 0; 55 61 56 62 /* -
applications/editors/josm/plugins/agpifoj/src/org/openstreetmap/josm/plugins/agpifoj/AgpifojPlugin.java
r13497 r13927 60 60 fc.setFileFilter(JPEG_FILE_FILTER); 61 61 62 fc.showOpenDialog(Main.parent);62 int result = fc.showOpenDialog(Main.parent); 63 63 64 64 File[] sel = fc.getSelectedFiles(); 65 if (sel == null || sel.length == 0 ) {65 if (sel == null || sel.length == 0 || result != JFileChooser.APPROVE_OPTION) { 66 66 return; 67 67 } -
applications/editors/josm/plugins/agpifoj/src/org/openstreetmap/josm/plugins/agpifoj/CorrelateGpxWithImages.java
r13775 r13927 10 10 import java.awt.Cursor; 11 11 import java.awt.Dimension; 12 import java.awt.event.ActionEvent; 13 import java.awt.event.ActionListener; 12 14 import java.awt.FlowLayout; 13 15 import java.awt.GridBagConstraints; 14 16 import java.awt.GridBagLayout; 15 import java.awt.event.ActionEvent;16 import java.awt.event.ActionListener;17 17 import java.io.File; 18 18 import java.io.FileInputStream; 19 import java.io.InputStream; 19 20 import java.io.IOException; 20 import java.io.InputStream;21 21 import java.text.ParseException; 22 22 import java.text.SimpleDateFormat; … … 26 26 import java.util.Comparator; 27 27 import java.util.Date; 28 import java.util.Hashtable; 28 29 import java.util.Iterator; 29 30 import java.util.List; … … 34 35 import javax.swing.AbstractListModel; 35 36 import javax.swing.ButtonGroup; 37 import javax.swing.event.ChangeEvent; 38 import javax.swing.event.ChangeListener; 39 import javax.swing.event.ListSelectionEvent; 40 import javax.swing.event.ListSelectionListener; 41 import javax.swing.filechooser.FileFilter; 36 42 import javax.swing.JButton; 37 43 import javax.swing.JComboBox; … … 43 49 import javax.swing.JRadioButton; 44 50 import javax.swing.JScrollPane; 51 import javax.swing.JSlider; 45 52 import javax.swing.JTextField; 46 53 import javax.swing.ListSelectionModel; 47 import javax.swing.event.ListSelectionEvent; 48 import javax.swing.event.ListSelectionListener; 49 import javax.swing.filechooser.FileFilter; 50 51 import org.openstreetmap.josm.Main; 54 52 55 import org.openstreetmap.josm.data.coor.EastNorth; 53 56 import org.openstreetmap.josm.data.gpx.GpxData; … … 55 58 import org.openstreetmap.josm.data.gpx.WayPoint; 56 59 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor; 60 import org.openstreetmap.josm.gui.ExtendedDialog; 57 61 import org.openstreetmap.josm.gui.layer.GpxLayer; 58 62 import org.openstreetmap.josm.gui.layer.Layer; 59 63 import org.openstreetmap.josm.io.GpxReader; 64 import org.openstreetmap.josm.Main; 60 65 import org.openstreetmap.josm.plugins.agpifoj.AgpifojLayer.ImageEntry; 61 66 import org.openstreetmap.josm.tools.ExifReader; 67 import org.openstreetmap.josm.tools.GBC; 62 68 import org.openstreetmap.josm.tools.ImageProvider; 63 69 import org.openstreetmap.josm.tools.PrimaryDateParser; 64 70 import org.xml.sax.SAXException; 71 65 72 66 73 /** This class displays the window to select the GPX file and the offset (timezone + delta). … … 547 554 GpxDataWrapper selectedGpx = null; 548 555 while (! isOk) { 549 int answer = JOptionPane.showConfirmDialog(Main.parent, panel, tr("Correlate images with GPX track"), JOptionPane.OK_CANCEL_OPTION); 550 if (answer == JOptionPane.CANCEL_OPTION) { 556 int answer = new ExtendedDialog(Main.parent, 557 tr("Correlate images with GPX track"), 558 panel, 559 new String[] { tr("Correlate"), tr("Auto-Guess"), tr("Cancel") }, 560 new String[] { "ok.png", "dialogs/gpx2imgManual.png", "cancel.png" }).getValue(); 561 562 if(answer != 1 && answer != 2) 551 563 return; 552 }564 553 565 // Check the selected values 554 566 Object item = cbGpx.getSelectedItem(); … … 560 572 } 561 573 selectedGpx = ((GpxDataWrapper) item); 574 575 if (answer == 2) { 576 autoGuess(selectedGpx.data); 577 return; 578 } 562 579 563 580 Float timezoneValue = parseTimezone(tfTimezone.getText().trim()); … … 592 609 593 610 // Construct a list of images that have a date, and sort them on the date. 594 ArrayList<ImageEntry> dateImgLst = new ArrayList<ImageEntry>(yLayer.data.size()); 595 if (rbAllImg.isSelected()) { 596 for (ImageEntry e : yLayer.data) { 597 if (e.time != null) { 598 dateImgLst.add(e); 599 } 600 } 601 602 } else if (rbNoExifImg.isSelected()) { 603 for (ImageEntry e : yLayer.data) { 604 if (e.time != null && e.exifCoor == null) { 605 dateImgLst.add(e); 606 } 607 } 608 609 } else { // rbUntaggedImg.isSelected() 610 for (ImageEntry e : yLayer.data) { 611 if (e.time != null && e.coor == null) { 612 dateImgLst.add(e); 613 } 614 } 615 } 616 617 int matched = matchGpxTrack(dateImgLst, selectedGpx.data, (long) (gpstimezone * 3600000) + delta * 1000); 611 ArrayList<ImageEntry> dateImgLst = getSortedImgList(rbAllImg.isSelected(), rbNoExifImg.isSelected()); 612 613 int matched = matchGpxTrack(dateImgLst, selectedGpx.data, (long) (gpstimezone * 3600) + delta); 618 614 619 615 // Search whether an other layer has yet defined some bounding box. … … 641 637 Main.main.map.repaint(); 642 638 643 JOptionPane.showMessageDialog(Main.parent, tr("Found {0} match s of {1} in GPX track {2}", matched, dateImgLst.size(), selectedGpx.name),639 JOptionPane.showMessageDialog(Main.parent, tr("Found {0} matches of {1} in GPX track {2}", matched, dateImgLst.size(), selectedGpx.name), 644 640 tr("GPX Track loaded"), 645 641 ((dateImgLst.size() > 0 && matched == 0) ? JOptionPane.WARNING_MESSAGE … … 647 643 648 644 } 649 650 private int matchGpxTrack(ArrayList<ImageEntry> dateImgLst, GpxData selectedGpx, long offset) { 651 int ret = 0; 652 645 646 // These variables all belong to "auto guess" but need to be accessible 647 // from the slider change listener 648 private int dayOffset; 649 private JLabel lblMatches; 650 private JLabel lblOffset; 651 private JLabel lblTimezone; 652 private JLabel lblMinutes; 653 private JLabel lblSeconds; 654 private JSlider sldTimezone; 655 private JSlider sldMinutes; 656 private JSlider sldSeconds; 657 private GpxData autoGpx; 658 private ArrayList<ImageEntry> autoImgs; 659 private long firstGPXDate = -1; 660 private long firstExifDate = -1; 661 662 /** 663 * Tries to automatically match opened photos to a given GPX track. Changes are applied 664 * immediately. Presents dialog with sliders for manual adjust. 665 * @param GpxData The GPX track to match against 666 */ 667 private void autoGuess(GpxData gpx) { 668 autoGpx = gpx; 669 autoImgs = getSortedImgList(true, false); 670 PrimaryDateParser dateParser = new PrimaryDateParser(); 671 672 // no images found, exit 673 if(autoImgs.size() <= 0) { 674 JOptionPane.showMessageDialog(Main.parent, 675 tr("The selected photos don't contain time information."), 676 tr("Photos don't contain time information"), JOptionPane.WARNING_MESSAGE); 677 return; 678 } 679 680 // Free the user's vision 681 Main.pleaseWaitDlg.setVisible(false); 682 AgpifojDialog dialog = AgpifojDialog.getInstance(); 683 dialog.action.button.setSelected(true); 684 dialog.action.actionPerformed(null); 685 // Will show first photo if none is selected yet 686 if(!dialog.hasImage()) 687 yLayer.showNextPhoto(); 688 // FIXME: If the dialog is minimized it will not be maximized. ToggleDialog is 689 // in need of a complete re-write to allow this in a reasonable way. 690 691 // Init variables 692 firstExifDate = autoImgs.get(0).time.getTime()/1000; 693 694 695 // Finds first GPX point 696 outer: for (GpxTrack trk : gpx.tracks) { 697 for (Collection<WayPoint> segment : trk.trackSegs) { 698 for (WayPoint curWp : segment) { 699 String curDateWpStr = (String) curWp.attr.get("time"); 700 if (curDateWpStr == null) continue; 701 702 try { 703 firstGPXDate = dateParser.parse(curDateWpStr).getTime()/1000; 704 break outer; 705 } catch(Exception e) {} 706 } 707 } 708 } 709 710 // No GPX timestamps found, exit 711 if(firstGPXDate < 0) { 712 JOptionPane.showMessageDialog(Main.parent, 713 tr("The selected GPX track doesn't contain timestamps. Please select another one."), 714 tr("GPX Track has no time information"), JOptionPane.WARNING_MESSAGE); 715 return; 716 } 717 718 // seconds 719 long diff = (yLayer.hasTimeoffset) 720 ? yLayer.timeoffset 721 : firstExifDate - firstGPXDate; 722 yLayer.timeoffset = diff; 723 yLayer.hasTimeoffset = true; 724 725 double diffInH = (double)diff/(60*60); // hours 726 727 // Find day difference 728 dayOffset = (int)Math.round(diffInH / 24); // days 729 double timezone = diff - dayOffset*24*60*60; // seconds 730 731 // In hours, rounded to two decimal places 732 timezone = (double)Math.round(timezone*100/(60*60)) / 100; 733 734 // Due to imprecise clocks we might get a "+3:28" timezone, which should obviously be 3:30 with 735 // -2 minutes offset. This determines the real timezone and finds offset. 736 double fixTimezone = (double)Math.round(timezone * 2)/2; // hours, rounded to one decimal place 737 int offset = (int)Math.round(diff - fixTimezone*60*60) - dayOffset*24*60*60; // seconds 738 739 /*System.out.println("phto " + firstExifDate); 740 System.out.println("gpx " + firstGPXDate); 741 System.out.println("diff " + diff); 742 System.out.println("difh " + diffInH); 743 System.out.println("days " + dayOffset); 744 System.out.println("time " + timezone); 745 System.out.println("fix " + fixTimezone); 746 System.out.println("offt " + offset);*/ 747 748 // This is called whenever one of the sliders is moved. 749 // It updates the labels and also calls the "match photos" code 750 class sliderListener implements ChangeListener { 751 public void stateChanged(ChangeEvent e) { 752 // parse slider position into real timezone 753 double tz = Math.abs(sldTimezone.getValue()); 754 String zone = tz % 2 == 0 755 ? (int)Math.floor(tz/2) + ":00" 756 : (int)Math.floor(tz/2) + ":30"; 757 if(sldTimezone.getValue() < 0) zone = "-" + zone; 758 759 lblTimezone.setText(tr("Timezone: {0}", zone)); 760 lblMinutes.setText(tr("Minutes: {0}", sldMinutes.getValue())); 761 lblSeconds.setText(tr("Seconds: {0}", sldSeconds.getValue())); 762 763 float gpstimezone = parseTimezone(zone).floatValue(); 764 765 // Reset previous position 766 for(ImageEntry x : autoImgs) { 767 x.coor = null; 768 x.pos = null; 769 } 770 771 long timediff = (long) (gpstimezone * 3600) 772 + dayOffset*24*60*60 773 + sldMinutes.getValue()*60 774 + sldSeconds.getValue(); 775 776 int matched = matchGpxTrack(autoImgs, autoGpx, timediff); 777 778 lblMatches.setText( 779 tr("Matched {0} of {1} photos to GPX track.", matched, autoImgs.size()) 780 + ((Math.abs(dayOffset) == 0) 781 ? "" 782 : " " + tr("(Time difference of {0} days)", Math.abs(dayOffset)) 783 ) 784 ); 785 786 int offset = (int)(firstGPXDate+timediff-firstExifDate); 787 int o = Math.abs(offset); 788 lblOffset.setText( 789 tr("Offset between track and photos: {0}m {1}s", 790 (offset < 0 ? "-" : "") + Long.toString(Math.round(o/60)), 791 Long.toString(Math.round(o%60)) 792 ) 793 ); 794 795 yLayer.timeoffset = timediff; 796 Main.main.map.repaint(); 797 } 798 } 799 800 // Info Labels 801 lblMatches = new JLabel(); 802 lblOffset = new JLabel(); 803 804 // Timezone Slider 805 // The slider allows to switch timezon from -12:00 to 12:00 in 30 minutes 806 // steps. Therefore the range is -24 to 24. 807 lblTimezone = new JLabel(); 808 sldTimezone = new JSlider(-24, 24, 0); 809 sldTimezone.setPaintLabels(true); 810 Hashtable labelTable = new Hashtable(); 811 labelTable.put(-24, new JLabel("-12:00")); 812 labelTable.put(-12, new JLabel( "-6:00")); 813 labelTable.put( 0, new JLabel( "0:00")); 814 labelTable.put( 12, new JLabel( "6:00")); 815 labelTable.put( 24, new JLabel( "12:00")); 816 sldTimezone.setLabelTable(labelTable); 817 818 // Minutes Slider 819 lblMinutes = new JLabel(); 820 sldMinutes = new JSlider(-15, 15, 0); 821 sldMinutes.setPaintLabels(true); 822 sldMinutes.setMajorTickSpacing(5); 823 824 // Seconds slider 825 lblSeconds = new JLabel(); 826 sldSeconds = new JSlider(-60, 60, 0); 827 sldSeconds.setPaintLabels(true); 828 sldSeconds.setMajorTickSpacing(30); 829 830 // Put everything together 831 JPanel p = new JPanel(new GridBagLayout()); 832 p.setPreferredSize(new Dimension(400, 230)); 833 p.add(lblMatches, GBC.eol().fill()); 834 p.add(lblOffset, GBC.eol().fill().insets(0, 0, 0, 10)); 835 p.add(lblTimezone, GBC.eol().fill()); 836 p.add(sldTimezone, GBC.eol().fill().insets(0, 0, 0, 10)); 837 p.add(lblMinutes, GBC.eol().fill()); 838 p.add(sldMinutes, GBC.eol().fill().insets(0, 0, 0, 10)); 839 p.add(lblSeconds, GBC.eol().fill()); 840 p.add(sldSeconds, GBC.eol().fill()); 841 842 // If there's an error in the calculation the found values 843 // will be off range for the sliders. Catch this error 844 // and inform the user about it. 845 try { 846 sldTimezone.setValue((int)(fixTimezone*2)); 847 sldMinutes.setValue(offset/60); 848 sldSeconds.setValue(offset%60); 849 } catch(Exception e) { 850 JOptionPane.showMessageDialog(Main.parent, 851 tr("An error occured while trying to match the photos to the GPX track." 852 +" You can adjust the sliders to manually match the photos."), 853 tr("Matching photos to track failed"), 854 JOptionPane.WARNING_MESSAGE); 855 } 856 857 // Call the sliderListener once manually so labels get adjusted 858 new sliderListener().stateChanged(null); 859 // Listeners added here, otherwise it tries to match three times 860 // (when setting the default values) 861 sldTimezone.addChangeListener(new sliderListener()); 862 sldMinutes.addChangeListener(new sliderListener()); 863 sldSeconds.addChangeListener(new sliderListener()); 864 865 // There is no way to cancel this dialog, all changes get applied 866 // immediately. Therefore "Close" is marked with an "OK" icon. 867 // Settings are only saved temporarily to the layer. 868 int answer = new ExtendedDialog(Main.parent, 869 tr("Adjust timezone and offset"), 870 p, 871 new String[] { tr("Close"), tr("Default Values") }, 872 new String[] { "ok.png", "dialogs/refresh.png"} 873 ).getValue(); 874 875 // User wants default values; discard old result and re-open dialog 876 if(answer == 2) { 877 yLayer.hasTimeoffset = false; 878 autoGuess(gpx); 879 } 880 } 881 882 /** 883 * Returns a list of images that fulfill the given criteria. 884 * Default setting is to return untagged images, but may be overwritten. 885 * @param boolean all -- returns all available images 886 * @param boolean noexif -- returns untagged images without EXIF-GPS coords 887 * @return ArrayList<ImageEntry> matching images 888 */ 889 private ArrayList<ImageEntry> getSortedImgList(boolean all, boolean noexif) { 890 ArrayList<ImageEntry> dateImgLst = new ArrayList<ImageEntry>(yLayer.data.size()); 891 if (all) { 892 for (ImageEntry e : yLayer.data) { 893 if (e.time != null) { 894 // Reset previous position 895 e.coor = null; 896 e.pos = null; 897 dateImgLst.add(e); 898 } 899 } 900 901 } else if (noexif) { 902 for (ImageEntry e : yLayer.data) { 903 if (e.time != null && e.exifCoor == null) { 904 dateImgLst.add(e); 905 } 906 } 907 908 } else { 909 for (ImageEntry e : yLayer.data) { 910 if (e.time != null && e.coor == null) { 911 dateImgLst.add(e); 912 } 913 } 914 } 915 653 916 Collections.sort(dateImgLst, new Comparator<ImageEntry>() { 654 917 public int compare(ImageEntry arg0, ImageEntry arg1) { … … 656 919 } 657 920 }); 921 922 return dateImgLst; 923 } 924 925 private int matchGpxTrack(ArrayList<ImageEntry> dateImgLst, GpxData selectedGpx, long offset) { 926 int ret = 0; 658 927 659 928 PrimaryDateParser dateParser = new PrimaryDateParser(); … … 671 940 672 941 try { 673 long curDateWp = dateParser.parse(curDateWpStr).getTime() + offset;942 long curDateWp = dateParser.parse(curDateWpStr).getTime()/1000 + offset; 674 943 ret += matchPoints(dateImgLst, prevWp, prevDateWp, curWp, curDateWp); 675 944 … … 694 963 695 964 private int matchPoints(ArrayList<ImageEntry> dateImgLst, WayPoint prevWp, long prevDateWp, WayPoint curWp, long curDateWp) { 696 int interval = prevDateWp > 0 ? ((int)Math.abs(curDateWp - prevDateWp))/2 : 500;965 double interval = prevDateWp > 0 ? ((int)Math.abs(curDateWp - prevDateWp)) : 1; 697 966 int ret = 0; 698 967 int i = getLastIndexOfListBefore(dateImgLst, curDateWp, interval); 699 if (i >= 0 && i < dateImgLst.size() && dateImgLst.get(i).time.getTime() +interval > prevDateWp) {968 if (i >= 0 && i < dateImgLst.size() && dateImgLst.get(i).time.getTime()/1000+interval > prevDateWp) { 700 969 Double speed = null; 701 970 Double prevElevation = null; … … 712 981 } catch (Exception e) {} 713 982 714 while(i >= 0 && inRadius(dateImgLst.get(i).time.getTime() , curDateWp, interval)) {983 while(i >= 0 && inRadius(dateImgLst.get(i).time.getTime()/1000, curDateWp, interval)) { 715 984 if(dateImgLst.get(i).coor == null) { 716 985 dateImgLst.get(i).pos = curWp.eastNorth; … … 726 995 long imgDate; 727 996 while(i >= 0 728 && (imgDate = dateImgLst.get(i).time.getTime() ) > prevDateWp) {997 && (imgDate = dateImgLst.get(i).time.getTime()/1000) > prevDateWp) { 729 998 if(dateImgLst.get(i).coor == null) { 730 999 dateImgLst.get(i).pos = new EastNorth( … … 745 1014 } 746 1015 747 private int getLastIndexOfListBefore(ArrayList<ImageEntry> dateImgLst, long searchedDate, intinterval) {1016 private int getLastIndexOfListBefore(ArrayList<ImageEntry> dateImgLst, long searchedDate, double interval) { 748 1017 int lstSize = dateImgLst.size(); 749 if (lstSize == 0 || searchedDate < dateImgLst.get(0).time.getTime() ) {1018 if (lstSize == 0 || searchedDate < dateImgLst.get(0).time.getTime()/1000) { 750 1019 return -1; 751 } else if (searchedDate-interval > dateImgLst.get(lstSize - 1).time.getTime() ) {1020 } else if (searchedDate-interval > dateImgLst.get(lstSize - 1).time.getTime()/1000) { 752 1021 return lstSize; 753 } else if (inRadius(searchedDate, dateImgLst.get(lstSize - 1).time.getTime() , interval)) {1022 } else if (inRadius(searchedDate, dateImgLst.get(lstSize - 1).time.getTime()/1000, interval)) { 754 1023 return lstSize - 1; 755 } else if (inRadius(searchedDate , dateImgLst.get(0).time.getTime() , interval)) {1024 } else if (inRadius(searchedDate , dateImgLst.get(0).time.getTime()/1000, interval)) { 756 1025 int curIndex = 0; 757 1026 while (curIndex + 1 < lstSize 758 && inRadius(dateImgLst.get(curIndex + 1).time.getTime() , searchedDate, interval)) {1027 && inRadius(dateImgLst.get(curIndex + 1).time.getTime()/1000, searchedDate, interval)) { 759 1028 curIndex++; 760 1029 } … … 767 1036 while (endIndex - startIndex > 1) { 768 1037 curIndex = (endIndex + startIndex) / 2; 769 long curDate = dateImgLst.get(curIndex).time.getTime() ;1038 long curDate = dateImgLst.get(curIndex).time.getTime()/1000; 770 1039 if (curDate-interval < searchedDate) { 771 1040 startIndex = curIndex; … … 775 1044 // Check that there is no image _after_ that one that have exactly the same date. 776 1045 while (curIndex + 1 < lstSize 777 && inRadius(dateImgLst.get(curIndex + 1).time.getTime() , searchedDate, interval)) {1046 && inRadius(dateImgLst.get(curIndex + 1).time.getTime()/1000, searchedDate, interval)) { 778 1047 curIndex++; 779 1048 } … … 892 1161 } 893 1162 894 private boolean inRadius(long time1, long time2, intinterval) {1163 private boolean inRadius(long time1, long time2, double interval) { 895 1164 return Math.abs(time1 - time2) < interval; 896 1165 } -
applications/editors/josm/plugins/wmsplugin/src/wmsplugin/Cache.java
r13645 r13927 4 4 import java.io.File; 5 5 import java.io.FileFilter; 6 import java.math.BigInteger; 7 import java.security.MessageDigest; 6 8 import java.util.Date; 7 9 import java.util.Iterator; … … 9 11 import java.util.TreeMap; 10 12 import javax.imageio.*; 13 11 14 12 15 import org.openstreetmap.josm.Main; … … 127 130 } 128 131 129 private String clean(String ident) { 130 return ident.replaceAll("[^a-zA-Z0-9]", ""); 132 private static String getUniqueFilename(String ident) { 133 try { 134 MessageDigest md = MessageDigest.getInstance("MD5"); 135 BigInteger number = new BigInteger(1, md.digest(ident.getBytes())); 136 return number.toString(16); 137 } catch(Exception e) { 138 // Fall back. Remove unsuitable characters and some random ones to shrink down path length. 139 // Limit it to 70 characters, that leaves about 190 for the path on Windows/NTFS 140 ident = ident.replaceAll("[^a-zA-Z0-9]", ""); 141 ident = ident.replaceAll("[acegikmoqsuwy]", ""); 142 return ident.substring(ident.length() - 70); 143 } 131 144 } 132 145 133 146 private File getPath(String ident) { 134 return new File(dir, clean(ident) + ".png");147 return new File(dir, getUniqueFilename(ident) + ".png"); 135 148 } 136 149 }
Note:
See TracChangeset
for help on using the changeset viewer.