From 1e74123cfed4374b403574b6cd16bd8d4e73bf45 Mon Sep 17 00:00:00 2001 From: Normand Briere <nbriere@noware.ca> Date: Tue, 10 Jul 2018 22:17:11 -0400 Subject: [PATCH] Refactoring. --- timeflow/data/time/TimeUnit.java | 472 ++++++----- timeflow/app/TimeflowApp.java | 12 timeflow/views/ListView.java | 499 ++++++------ timeflow/data/time/RoughTime.java | 3 timeflow/vis/Mouseover.java | 176 ++-- timeflow/format/file/HtmlFormat.java | 264 +++--- timeflow/vis/timeline/AxisTicMarks.java | 220 ++-- timeflow/views/TimelineView.java | 94 + timeflow/views/AbstractVisualizationView.java | 8 timeflow/vis/VisualAct.java | 1 timeflow/app/ui/LinkTabPane.java | 414 +++++----- timeflow/vis/timeline/AxisRenderer.java | 151 +- 12 files changed, 1,220 insertions(+), 1,094 deletions(-) diff --git a/timeflow/app/TimeflowApp.java b/timeflow/app/TimeflowApp.java index 4826184..1bdc790 100755 --- a/timeflow/app/TimeflowApp.java +++ b/timeflow/app/TimeflowApp.java @@ -35,7 +35,8 @@ public JMenu filterMenu; JMenuItem save = new JMenuItem("Save"); FilterControlPanel filterControlPanel; - LinkTabPane leftPanel; + //LinkTabPane + JTabbedPane leftPanel; TFListener filterMenuMaker = new TFListener() { @Override @@ -90,14 +91,15 @@ // left tab area, with vertical gray divider. JPanel leftHolder = new JPanel(); - container.add(leftHolder, BorderLayout.WEST); + container.add(leftHolder, BorderLayout.EAST); // WEST); leftHolder.setLayout(new BorderLayout()); JPanel pad = new Pad(3, 3); pad.setBackground(Color.gray); leftHolder.add(pad, BorderLayout.EAST); - leftPanel = new LinkTabPane();//JTabbedPane(); + leftPanel = new //LinkTabPane(); + JTabbedPane(); leftHolder.add(leftPanel, BorderLayout.CENTER); JPanel configPanel = new JPanel(); @@ -112,9 +114,9 @@ configPanel.add(legend, BorderLayout.CENTER); legend.add(new SizeLegendPanel(model), BorderLayout.NORTH); legend.add(new ColorLegendPanel(model), BorderLayout.CENTER); - leftPanel.addTab(configPanel, "Display", true); + leftPanel.add(configPanel, "Display"); //, true); - leftPanel.addTab(filterControlPanel, "Filter", true); + leftPanel.add(filterControlPanel, "Filter"); //, true); // center tab area diff --git a/timeflow/app/ui/LinkTabPane.java b/timeflow/app/ui/LinkTabPane.java index 1f4ee01..911461c 100755 --- a/timeflow/app/ui/LinkTabPane.java +++ b/timeflow/app/ui/LinkTabPane.java @@ -8,201 +8,225 @@ import java.util.*; // custom JTabbedPane-like thing. -public class LinkTabPane extends JPanel { - - ArrayList<String> tabNames=new ArrayList<String>(); - HashMap<String, JComponent> tabMap=new HashMap<String, JComponent>(); - String currentName; - CardLayout cards=new CardLayout(); - JPanel center=new JPanel(); - LinkTop top=new LinkTop(); - - public LinkTabPane() - { - setBackground(Color.white); - setLayout(new BorderLayout()); - add(top, BorderLayout.NORTH); - add(center, BorderLayout.CENTER); - center.setLayout(cards); - top.addMouseListener(new MouseAdapter() { - @Override - public void mousePressed(MouseEvent e) { - String s=top.getName(e.getX()); - if (s!=null) - { - String old=currentName; - setCurrentName(s); - firePropertyChange("tab", old, s); - } - }}); - } - - public String getTitleAt(int i) - { - return tabNames.get(i); - } - - public void setSelectedIndex(int i) - { - setCurrentName(getTitleAt(i)); - } - - public void addTab(JComponent component, String name, boolean left) - { - tabNames.add(name); - tabMap.put(name, component); - center.add(component, name); - top.addName(name, left); - repaint(); - if (currentName==null) - currentName=name; - } - - public String getCurrentName() - { - return currentName; - } - - public void setCurrentName(final String currentName) - { - this.currentName=currentName; - top.repaint(); - SwingUtilities.invokeLater(new Runnable() {public void run() {cards.show(center, currentName);}}); +public class LinkTabPane extends JPanel +{ - } - - class LinkTop extends JPanel - { - int left, right; - ArrayList<HotLink> leftHots=new ArrayList<HotLink>(); - ArrayList<HotLink> rightHots=new ArrayList<HotLink>(); - Font font=new Font("Verdana", Font.PLAIN, 14); - FontMetrics fm=getFontMetrics(font); - - LinkTop() - { - setLayout(new FlowLayout(FlowLayout.LEFT)); - setBackground(new Color(220,220,220)); - } - - public void paintComponent(Graphics g1) - { - int w=getSize().width, h=getSize().height; - Graphics2D g=(Graphics2D)g1; - g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g.setColor(getBackground()); - g.fillRect(0,0,w,h); - g.setColor(Color.gray); - for (int i=0; i<2; i++) - { - g.drawLine(0,i,w,i); - g.drawLine(0,h-1-i,w,h-1-i); - } - - for (HotLink hot: leftHots) - { - draw(g, hot, h, 0); - } - - for (HotLink hot: rightHots) - { - draw(g, hot, h, w); - } - - for (int i=0; i<leftHots.size(); i++) - { - - if (i<leftHots.size()-1) - { - HotLink hot=leftHots.get(i); - for (int j=0; j<1; j++) - g.drawLine(hot.x+hot.width-1-j, 7, hot.x+hot.width-1-j, h-7); - } - } - - for (int i=0; i<rightHots.size(); i++) - { - - if (i<rightHots.size()-1) - { - HotLink hot=rightHots.get(i); - for (int j=0; j<1; j++) - g.drawLine(hot.x+w-1-j, 7, hot.x+w-1-j, h-7); - } - } - } - - void draw(Graphics g, HotLink hot, int h, int dx) - { - int x=hot.x+dx; - if (hot.s.equals(currentName)) - { - g.setColor(Color.lightGray); - g.fillRect(x,2,hot.width,h-4); - g.setColor(Color.gray); - g.drawLine(x-1, 0, x-1, h); - g.drawLine(x+hot.width-1, 0, x+hot.width-1, h); - } - g.setColor(Color.darkGray); - g.setFont(font); - int sw=fm.stringWidth(hot.s); - g.drawString(hot.s, x+(hot.width-sw)/2, h-10); - - } - - String getName(int x) - { - for (HotLink h: leftHots) - { - if (h.x<=x && h.x+h.width>x) - return h.s; - } - for (HotLink h: rightHots) - { - int w=getSize().width; - if (h.x+w<=x && h.x+h.width+w>x) - return h.s; - } + ArrayList<String> tabNames = new ArrayList<String>(); + HashMap<String, JComponent> tabMap = new HashMap<String, JComponent>(); + String currentName; + CardLayout cards = new CardLayout(); + JPanel center = new JPanel(); + LinkTop top = new LinkTop(); - if (leftHots.size()>0) - return leftHots.get(leftHots.size()-1).s; - if (rightHots.size()>0) - return rightHots.get(0).s; - return null; - } - - void addName(String name, boolean leftward) - { - if (leftward) - { - int x=right; - int w=fm.stringWidth(name)+24; - leftHots.add(new HotLink(name, x, 0, w, 30)); - right+=w; - } - else - { - int x=left; - int w=fm.stringWidth(name)+24; - rightHots.add(new HotLink(name, x-w, 0, w, 30)); - left-=w; - } - } - - class HotLink extends Rectangle - { - String s; - HotLink(String s, int x, int y, int w, int h) - { - super(x,y,w,h); - this.s=s; - } - } - - public Dimension getPreferredSize() - { - return new Dimension(30,30); - } - } - + public LinkTabPane() + { + setBackground(Color.white); + setLayout(new BorderLayout()); + add(top, BorderLayout.NORTH); + add(center, BorderLayout.CENTER); + center.setLayout(cards); + top.addMouseListener(new MouseAdapter() + { + + @Override + public void mousePressed(MouseEvent e) + { + String s = top.getName(e.getX()); + if (s != null) + { + String old = currentName; + setCurrentName(s); + firePropertyChange("tab", old, s); + } + } + }); + } + + public String getTitleAt(int i) + { + return tabNames.get(i); + } + + public void setSelectedIndex(int i) + { + setCurrentName(getTitleAt(i)); + } + + public void addTab(JComponent component, String name, boolean left) + { + tabNames.add(name); + tabMap.put(name, component); + center.add(component, name); + top.addName(name, left); + repaint(); + if (currentName == null) + { + currentName = name; + } + } + + public String getCurrentName() + { + return currentName; + } + + public void setCurrentName(final String currentName) + { + this.currentName = currentName; + top.repaint(); + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + cards.show(center, currentName); + } + }); + + } + + class LinkTop extends JPanel + { + int left, right; + ArrayList<HotLink> leftHots = new ArrayList<HotLink>(); + ArrayList<HotLink> rightHots = new ArrayList<HotLink>(); + Font font = new Font("Verdana", Font.PLAIN, 14); + FontMetrics fm = getFontMetrics(font); + + LinkTop() + { + setLayout(new FlowLayout(FlowLayout.LEFT)); + setBackground(new Color(220, 220, 220)); + } + + public void paintComponent(Graphics g1) + { + int w = getSize().width, h = getSize().height; + Graphics2D g = (Graphics2D) g1; + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setColor(getBackground()); + g.fillRect(0, 0, w, h); + g.setColor(Color.gray); + for (int i = 0; i < 2; i++) + { + g.drawLine(0, i, w, i); + g.drawLine(0, h - 1 - i, w, h - 1 - i); + } + + for (HotLink hot : leftHots) + { + draw(g, hot, h, 0); + } + + for (HotLink hot : rightHots) + { + draw(g, hot, h, w); + } + + for (int i = 0; i < leftHots.size(); i++) + { + + if (i < leftHots.size() - 1) + { + HotLink hot = leftHots.get(i); + for (int j = 0; j < 1; j++) + { + g.drawLine(hot.x + hot.width - 1 - j, 7, hot.x + hot.width - 1 - j, h - 7); + } + } + } + + for (int i = 0; i < rightHots.size(); i++) + { + + if (i < rightHots.size() - 1) + { + HotLink hot = rightHots.get(i); + for (int j = 0; j < 1; j++) + { + g.drawLine(hot.x + w - 1 - j, 7, hot.x + w - 1 - j, h - 7); + } + } + } + } + + void draw(Graphics g, HotLink hot, int h, int dx) + { + int x = hot.x + dx; + if (hot.s.equals(currentName)) + { + g.setColor(Color.lightGray); + g.fillRect(x, 2, hot.width, h - 4); + g.setColor(Color.gray); + g.drawLine(x - 1, 0, x - 1, h); + g.drawLine(x + hot.width - 1, 0, x + hot.width - 1, h); + } + g.setColor(Color.darkGray); + g.setFont(font); + int sw = fm.stringWidth(hot.s); + g.drawString(hot.s, x + (hot.width - sw) / 2, h - 10); + + } + + String getName(int x) + { + for (HotLink h : leftHots) + { + if (h.x <= x && h.x + h.width > x) + { + return h.s; + } + } + for (HotLink h : rightHots) + { + int w = getSize().width; + if (h.x + w <= x && h.x + h.width + w > x) + { + return h.s; + } + } + + if (leftHots.size() > 0) + { + return leftHots.get(leftHots.size() - 1).s; + } + if (rightHots.size() > 0) + { + return rightHots.get(0).s; + } + return null; + } + + void addName(String name, boolean leftward) + { + if (leftward) + { + int x = right; + int w = fm.stringWidth(name) + 24; + leftHots.add(new HotLink(name, x, 0, w, 30)); + right += w; + } else + { + int x = left; + int w = fm.stringWidth(name) + 24; + rightHots.add(new HotLink(name, x - w, 0, w, 30)); + left -= w; + } + } + + class HotLink extends Rectangle + { + String s; + + HotLink(String s, int x, int y, int w, int h) + { + super(x, y, w, h); + this.s = s; + } + } + + public Dimension getPreferredSize() + { + return new Dimension(30, 30); + } + } } diff --git a/timeflow/data/time/RoughTime.java b/timeflow/data/time/RoughTime.java index 9cadc5a..55b4bbb 100755 --- a/timeflow/data/time/RoughTime.java +++ b/timeflow/data/time/RoughTime.java @@ -100,7 +100,8 @@ public String format() { - return units.formatFull(time); + //return units.formatFull(time); + return TimeUnit.SECOND.formatFull(time); } public static int compare(RoughTime t1, RoughTime t2) diff --git a/timeflow/data/time/TimeUnit.java b/timeflow/data/time/TimeUnit.java index 760b3be..4032258 100755 --- a/timeflow/data/time/TimeUnit.java +++ b/timeflow/data/time/TimeUnit.java @@ -3,241 +3,255 @@ import java.util.*; import java.text.*; -public class TimeUnit { +public class TimeUnit +{ - public static final TimeUnit YEAR=new TimeUnit("Years", Calendar.YEAR, 365*24*60*60*1000L, "yyyy", "yyyy"); - public static final TimeUnit MONTH=new TimeUnit("Months", Calendar.MONTH, 30*24*60*60*1000L, "MMM", "MMM yyyy"); - public static final TimeUnit WEEK=new TimeUnit("Weeks", Calendar.WEEK_OF_YEAR, 7*24*60*60*1000L, "d", "MMM d yyyy"); - public static final TimeUnit DAY=new TimeUnit("Days", Calendar.DAY_OF_MONTH, 24*60*60*1000L, "d", "MMM d yyyy"); - public static final TimeUnit DAY_OF_WEEK=new TimeUnit("Days", Calendar.DAY_OF_WEEK, 24*60*60*1000L, "d", "MMM d yyyy"); - public static final TimeUnit HOUR=new TimeUnit("Hours", Calendar.HOUR_OF_DAY, 60*60*1000L, "kk:mm", "MMM d yyyy kk:mm"); - public static final TimeUnit MINUTE=new TimeUnit("Minutes", Calendar.MINUTE, 60*1000L, ":mm", "MMM d yyyy kk:mm"); - public static final TimeUnit SECOND=new TimeUnit("Seconds", Calendar.SECOND, 1000L, ":ss", "MMM d yyyy kk:mm:ss"); - public static final TimeUnit DECADE=multipleYears(10); - public static final TimeUnit CENTURY=multipleYears(100); - - private static final double DAY_SIZE=24*60*60*1000L; - - private int quantity; - private long roughSize; - private SimpleDateFormat format, fullFormat; - private String name; - private int calendarCode; - - private TimeUnit() - { - } - - private TimeUnit(String name, int calendarCode, long roughSize, String formatPattern, String fullFormatPattern) - { - this.name=name; - this.calendarCode=calendarCode; - this.roughSize=roughSize; - format=new SimpleDateFormat(formatPattern); - fullFormat=new SimpleDateFormat(fullFormatPattern); - quantity=1; - } - - public String toString() - { - return "[TimeUnit: "+name+"]"; - } + public static final TimeUnit YEAR = new TimeUnit("Years", Calendar.YEAR, 365 * 24 * 60 * 60 * 1000L, "yyyy", "yyyy"); + public static final TimeUnit MONTH = new TimeUnit("Months", Calendar.MONTH, 30 * 24 * 60 * 60 * 1000L, "MMM", "MMM yyyy"); + public static final TimeUnit WEEK = new TimeUnit("Weeks", Calendar.WEEK_OF_YEAR, 7 * 24 * 60 * 60 * 1000L, "d", "MMM d yyyy"); + public static final TimeUnit DAY = new TimeUnit("Days", Calendar.DAY_OF_MONTH, 24 * 60 * 60 * 1000L, "d", "MMM d yyyy"); + public static final TimeUnit DAY_OF_WEEK = new TimeUnit("Days", Calendar.DAY_OF_WEEK, 24 * 60 * 60 * 1000L, "d", "MMM d yyyy"); + public static final TimeUnit HOUR = new TimeUnit("Hours", Calendar.HOUR_OF_DAY, 60 * 60 * 1000L, "kk:mm", "MMM d yyyy kk:mm"); + public static final TimeUnit MINUTE = new TimeUnit("Minutes", Calendar.MINUTE, 60 * 1000L, ":mm", "MMM d yyyy kk:mm"); + public static final TimeUnit SECOND = new TimeUnit("Seconds", Calendar.SECOND, 1000L, ":ss", "MMM d yyyy kk:mm:ss"); + public static final TimeUnit DECADE = multipleYears(10); + public static final TimeUnit CENTURY = multipleYears(100); + private static final double DAY_SIZE = 24 * 60 * 60 * 1000L; + private int quantity; + private long roughSize; + private SimpleDateFormat format, fullFormat; + private String name; + private int calendarCode; - public static TimeUnit multipleYears(int numYears) - { - TimeUnit t=new TimeUnit(); - t.name=numYears+" Years"; - t.calendarCode=Calendar.YEAR; - t.roughSize=YEAR.roughSize*numYears; - t.format=YEAR.format; - t.fullFormat=YEAR.fullFormat; - t.quantity=numYears; - return t; - } - - public static TimeUnit multipleWeeks(int num) - { - TimeUnit t=new TimeUnit(); - t.name=num+" Weeks"; - t.calendarCode=Calendar.WEEK_OF_YEAR; - t.roughSize=WEEK.roughSize*num; - t.format=WEEK.format; - t.fullFormat=WEEK.fullFormat; - t.quantity=num; - return t; - } - - public TimeUnit times(int quantity) - { - TimeUnit t=new TimeUnit(); - t.name=quantity+" "+this.name; - t.calendarCode=this.calendarCode; - t.roughSize=this.roughSize*quantity; - t.format=this.format; - t.fullFormat=this.fullFormat; - t.quantity=quantity; - return t; - - } + private TimeUnit() + { + } - - public int numUnitsIn(TimeUnit u) - { - return (int)Math.round(u.getRoughSize()/(double)getRoughSize()); - } - - public boolean isDayOrLess() - { - return roughSize <= 24*60*60*1000L; - } - - public RoughTime roundDown(long timestamp) - { - return round(timestamp, false); - } - - public RoughTime roundUp(long timestamp) - { - return round(timestamp, true); - } - - private static final int[] calendarUnits={Calendar.SECOND, Calendar.MINUTE, Calendar.HOUR_OF_DAY, Calendar.DAY_OF_MONTH, Calendar.MONTH, Calendar.YEAR}; - public RoughTime round(long timestamp, boolean up) - { - Calendar c=TimeUtils.cal(timestamp); - - if (calendarCode==Calendar.WEEK_OF_YEAR ) - { - c.set(Calendar.DAY_OF_WEEK, c.getMinimum(Calendar.DAY_OF_WEEK)); - } - else - { - - // set to minimum all fields of finer granularity. - int roundingCode=calendarCode; - if (calendarCode==Calendar.WEEK_OF_YEAR || calendarCode==Calendar.DAY_OF_WEEK) - roundingCode=Calendar.DAY_OF_MONTH; - for (int i=0; i<calendarUnits.length; i++) - { - if (calendarUnits[i]==roundingCode) - break; - if (i==calendarUnits.length-1) - throw new IllegalArgumentException("Unsupported Calendar Unit: "+calendarCode); - c.set(calendarUnits[i], c.getMinimum(calendarUnits[i])); - } - if (quantity>1) - { - c.set(calendarCode, quantity*(c.get(calendarCode)/quantity)); - } - } - - // if rounding up, then add a unit at current granularity. - if (up) - c.add(calendarCode, quantity); - - return new RoughTime(c.getTimeInMillis(), this); - } - - public int get(long timestamp) - { - Calendar c= TimeUtils.cal(timestamp); - int n=c.get(calendarCode); - return quantity==1 ? n : n%quantity; - } - - public void addTo(RoughTime r) - { - addTo(r,1); - } - - public void addTo(RoughTime r, int times) - { - Calendar c=TimeUtils.cal(r.getTime()); - c.add(calendarCode, quantity*times); - r.setTime(c.getTimeInMillis()); - } - - // Finding the difference between two dates, in a given unit of time, - // is much subtler than you'd think! And annoyingly, the Calendar class does not do - // this for you, even though it actually "knows" how to do so since it - // can add fields. - // - // The most vexing problem is dealing with daylight savings time, - // which means that one day a year has 23 hours and one day has 25 hours. - // We also have to handle the fact that months and years aren't constant lengths. - // - // Rather than write all this ourselves, in this code we - // use the Calendar class to do the heavy lifting. - public long difference(long x, long y) - { - // If this is not one of the hard cases, - // just divide the timespan by the length of time unit. - // Note that we're not worrying about hours and daylight savings time. - if (calendarCode!=Calendar.YEAR && calendarCode!=Calendar.MONTH && - calendarCode!=Calendar.DAY_OF_MONTH && calendarCode!=Calendar.DAY_OF_WEEK && - calendarCode!=Calendar.WEEK_OF_YEAR) - { - return (x-y)/roughSize; - } - - Calendar c1=TimeUtils.cal(x), c2=TimeUtils.cal(y); - int diff=0; - switch (calendarCode) - { - case Calendar.YEAR: - return (c1.get(Calendar.YEAR)-c2.get(Calendar.YEAR))/quantity; - - case Calendar.MONTH: - diff= 12*(c1.get(Calendar.YEAR)-c2.get(Calendar.YEAR))+ - c1.get(Calendar.MONTH)-c2.get(Calendar.MONTH); - return diff/quantity; - - case Calendar.DAY_OF_MONTH: - case Calendar.DAY_OF_WEEK: - case Calendar.DAY_OF_YEAR: - case Calendar.WEEK_OF_MONTH: - case Calendar.WEEK_OF_YEAR: - // This is ugly, but believe me, it beats the alternative methods :-) - // We use the Calendar class's knowledge of daylight savings time. - // and also the fact that if we calculate this naively, then we aren't going - // to be off by more than one in either direction. - int naive=(int)Math.round((x-y)/(double)roughSize); - c2.add(calendarCode, naive*quantity); - if (c1.get(calendarCode)==c2.get(calendarCode)) - return naive/quantity; - c2.add(calendarCode, quantity); - if (c1.get(calendarCode)==c2.get(calendarCode)) - return naive/quantity+1; - return naive/quantity-1; - } - throw new IllegalArgumentException("Unexpected calendar code: "+calendarCode); - } + private TimeUnit(String name, int calendarCode, long roughSize, String formatPattern, String fullFormatPattern) + { + this.name = name; + this.calendarCode = calendarCode; + this.roughSize = roughSize; + format = new SimpleDateFormat(formatPattern); + fullFormat = new SimpleDateFormat(fullFormatPattern); + quantity = 1; + } - public long approxNumInRange(long start, long end) - { - return 1+(end-start)/roughSize; - } - - public long getRoughSize() { - return roughSize; - } + public String toString() + { + return "[TimeUnit: " + name + "]"; + } - public String format(Date date) - { - return format.format(date); - } + public static TimeUnit multipleYears(int numYears) + { + TimeUnit t = new TimeUnit(); + t.name = numYears + " Years"; + t.calendarCode = Calendar.YEAR; + t.roughSize = YEAR.roughSize * numYears; + t.format = YEAR.format; + t.fullFormat = YEAR.fullFormat; + t.quantity = numYears; + return t; + } - public String formatFull(Date date) - { - return fullFormat.format(date); - } + public static TimeUnit multipleWeeks(int num) + { + TimeUnit t = new TimeUnit(); + t.name = num + " Weeks"; + t.calendarCode = Calendar.WEEK_OF_YEAR; + t.roughSize = WEEK.roughSize * num; + t.format = WEEK.format; + t.fullFormat = WEEK.fullFormat; + t.quantity = num; + return t; + } - public String formatFull(long timestamp) - { - return fullFormat.format(new Date(timestamp)); - } + public TimeUnit times(int quantity) + { + TimeUnit t = new TimeUnit(); + t.name = quantity + " " + this.name; + t.calendarCode = this.calendarCode; + t.roughSize = this.roughSize * quantity; + t.format = this.format; + t.fullFormat = this.fullFormat; + t.quantity = quantity; + return t; - public String getName() { - return name; - } + } + + public int numUnitsIn(TimeUnit u) + { + return (int) Math.round(u.getRoughSize() / (double) getRoughSize()); + } + + public boolean isDayOrLess() + { + return roughSize <= 24 * 60 * 60 * 1000L; + } + + public RoughTime roundDown(long timestamp) + { + return round(timestamp, false); + } + + public RoughTime roundUp(long timestamp) + { + return round(timestamp, true); + } + private static final int[] calendarUnits = + { + Calendar.SECOND, Calendar.MINUTE, Calendar.HOUR_OF_DAY, Calendar.DAY_OF_MONTH, Calendar.MONTH, Calendar.YEAR + }; + + public RoughTime round(long timestamp, boolean up) + { + Calendar c = TimeUtils.cal(timestamp); + + if (calendarCode == Calendar.WEEK_OF_YEAR) + { + c.set(Calendar.DAY_OF_WEEK, c.getMinimum(Calendar.DAY_OF_WEEK)); + } else + { + + // set to minimum all fields of finer granularity. + int roundingCode = calendarCode; + if (calendarCode == Calendar.WEEK_OF_YEAR || calendarCode == Calendar.DAY_OF_WEEK) + { + roundingCode = Calendar.DAY_OF_MONTH; + } + for (int i = 0; i < calendarUnits.length; i++) + { + if (calendarUnits[i] == roundingCode) + { + break; + } + if (i == calendarUnits.length - 1) + { + throw new IllegalArgumentException("Unsupported Calendar Unit: " + calendarCode); + } + c.set(calendarUnits[i], c.getMinimum(calendarUnits[i])); + } + if (quantity > 1) + { + c.set(calendarCode, quantity * (c.get(calendarCode) / quantity)); + } + } + + // if rounding up, then add a unit at current granularity. + if (up) + { + c.add(calendarCode, quantity); + } + + return new RoughTime(c.getTimeInMillis(), this); + } + + public int get(long timestamp) + { + Calendar c = TimeUtils.cal(timestamp); + int n = c.get(calendarCode); + return quantity == 1 ? n : n % quantity; + } + + public void addTo(RoughTime r) + { + addTo(r, 1); + } + + public void addTo(RoughTime r, int times) + { + Calendar c = TimeUtils.cal(r.getTime()); + c.add(calendarCode, quantity * times); + r.setTime(c.getTimeInMillis()); + } + + // Finding the difference between two dates, in a given unit of time, + // is much subtler than you'd think! And annoyingly, the Calendar class does not do + // this for you, even though it actually "knows" how to do so since it + // can add fields. + // + // The most vexing problem is dealing with daylight savings time, + // which means that one day a year has 23 hours and one day has 25 hours. + // We also have to handle the fact that months and years aren't constant lengths. + // + // Rather than write all this ourselves, in this code we + // use the Calendar class to do the heavy lifting. + public long difference(long x, long y) + { + // If this is not one of the hard cases, + // just divide the timespan by the length of time unit. + // Note that we're not worrying about hours and daylight savings time. + if (calendarCode != Calendar.YEAR && calendarCode != Calendar.MONTH + && calendarCode != Calendar.DAY_OF_MONTH && calendarCode != Calendar.DAY_OF_WEEK + && calendarCode != Calendar.WEEK_OF_YEAR) + { + return (x - y) / roughSize; + } + + Calendar c1 = TimeUtils.cal(x), c2 = TimeUtils.cal(y); + int diff = 0; + switch (calendarCode) + { + case Calendar.YEAR: + return (c1.get(Calendar.YEAR) - c2.get(Calendar.YEAR)) / quantity; + + case Calendar.MONTH: + diff = 12 * (c1.get(Calendar.YEAR) - c2.get(Calendar.YEAR)) + + c1.get(Calendar.MONTH) - c2.get(Calendar.MONTH); + return diff / quantity; + + case Calendar.DAY_OF_MONTH: + case Calendar.DAY_OF_WEEK: + case Calendar.DAY_OF_YEAR: + case Calendar.WEEK_OF_MONTH: + case Calendar.WEEK_OF_YEAR: + // This is ugly, but believe me, it beats the alternative methods :-) + // We use the Calendar class's knowledge of daylight savings time. + // and also the fact that if we calculate this naively, then we aren't going + // to be off by more than one in either direction. + int naive = (int) Math.round((x - y) / (double) roughSize); + c2.add(calendarCode, naive * quantity); + if (c1.get(calendarCode) == c2.get(calendarCode)) + { + return naive / quantity; + } + c2.add(calendarCode, quantity); + if (c1.get(calendarCode) == c2.get(calendarCode)) + { + return naive / quantity + 1; + } + return naive / quantity - 1; + } + throw new IllegalArgumentException("Unexpected calendar code: " + calendarCode); + } + + public long approxNumInRange(long start, long end) + { + return 1 + (end - start) / roughSize; + } + + public long getRoughSize() + { + return roughSize; + } + + public String format(Date date) + { + return format.format(date); + } + + public String formatFull(Date date) + { + return fullFormat.format(date); + } + + public String formatFull(long timestamp) + { + return fullFormat.format(new Date(timestamp)); + } + + public String getName() + { + return name; + } } diff --git a/timeflow/format/file/HtmlFormat.java b/timeflow/format/file/HtmlFormat.java index cf50499..3792f28 100755 --- a/timeflow/format/file/HtmlFormat.java +++ b/timeflow/format/file/HtmlFormat.java @@ -9,135 +9,145 @@ public class HtmlFormat implements Export { - TFModel model; - java.util.List<Field> fields; - Field title; - - public HtmlFormat() {} - - public HtmlFormat(TFModel model) - { - setModel(model); - } - - public void setModel(TFModel model) - { - this.model=model; - fields=model.getDB().getFields(); - title=model.getDB().getField(VirtualField.LABEL); - } - - @Override - public void export(TFModel model, BufferedWriter out) throws Exception { - setModel(model); - out.write(makeHeader()); - for (Act a: model.getDB()) - out.write(makeItem(a)); - out.write(makeFooter()); - out.flush(); - } - - public void append(ActList acts, int start, int end, StringBuffer b) - { - for (int i=start; i<end; i++) - { - Act a=acts.get(i); - b.append(makeItem(a,i)); - } - } - - private String makeItem(Act act) - { - return makeItem(act, -1); - } - - public String makeItem(Act act, int id) - { - StringBuffer page=new StringBuffer(); - - - page.append("<tr><td valign=top align=left width=200><b>"); - if (title!=null) - { - Field f=model.getColorField(); - Color c=Color.black; - if (f!=null) - { - if (f.getType()==String.class) - c=model.getDisplay().makeColor(act.getString(f)); - else - { - String[] tags=act.getTextList(f); - if (tags.length==0) - c=Color.gray; - else - c=model.getDisplay().makeColor(tags[0]); - } - } - - page.append("<font size=+1 color="+htmlColor(c)+">"+act.getString(title)+"</font><br>"); - } - - Field startField=model.getDB().getField(VirtualField.START); - - if (startField!=null) - { - page.append("<font color=#999999>"+model.getDisplay().format( - act.getTime(startField))+"</font>"); - } - page.append("</b><br>"); - if (id>=0) - page.append("<a href=\"e"+id+"\">EDIT</a>"); - page.append("<br></td><td valign=top>"); - for (Field f: fields) - { - page.append("<b><font color=#003399>"+f.getName()+"</font></b> "); - Object val=act.get(f); - if (val instanceof URL) - { - page.append("<a href=\""+val+"\">"+val+"</a>"); - } - else - page.append(model.getDisplay().toString(val)); - page.append("<br>"); + TFModel model; + java.util.List<Field> fields; + Field title; - } - page.append("<br></td></tr>"); + public HtmlFormat() + { + } - return page.toString(); - } - - public String makeHeader() - { - StringBuffer page=new StringBuffer(); - page.append("<html><body><blockquote>"); - page.append("<br>File: "+model.getDbFile()+"<br>"); - page.append("Source: "+model.getDB().getSource()+"<br><br>"); - page.append("<br><br>"); - page.append("<table border=0>"); + public HtmlFormat(TFModel model) + { + setModel(model); + } - return page.toString(); - } - - public String makeFooter() - { - return "</table></blockquote></body></html>"; - } - - - static String htmlColor(Color c) - { - return '#'+hex2(c.getRed())+hex2(c.getGreen())+hex2(c.getBlue()); - } - - private static final String hexDigits="0123456789ABCDEF"; - private static String hex2(int n) - { - return hexDigits.charAt((n/16)%16)+""+hexDigits.charAt(n%16); - } - @Override - public String getName() { - return "HTML List"; - } + public void setModel(TFModel model) + { + this.model = model; + fields = model.getDB().getFields(); + title = model.getDB().getField(VirtualField.LABEL); + } + @Override + public void export(TFModel model, BufferedWriter out) throws Exception + { + setModel(model); + out.write(makeHeader()); + for (Act a : model.getDB()) + { + out.write(makeItem(a)); + } + out.write(makeFooter()); + out.flush(); + } + + public void append(ActList acts, int start, int end, StringBuffer b) + { + for (int i = start; i < end; i++) + { + Act a = acts.get(i); + b.append(makeItem(a, i)); + } + } + + private String makeItem(Act act) + { + return makeItem(act, -1); + } + + public String makeItem(Act act, int id) + { + StringBuffer page = new StringBuffer(); + + page.append("<tr><td valign=top align=left width=200><b>"); + if (title != null) + { + Field f = model.getColorField(); + Color c = Color.black; + if (f != null) + { + if (f.getType() == String.class) + { + c = model.getDisplay().makeColor(act.getString(f)); + } else + { + String[] tags = act.getTextList(f); + if (tags.length == 0) + { + c = Color.gray; + } else + { + c = model.getDisplay().makeColor(tags[0]); + } + } + } + + page.append("<font size=+1 color=" + htmlColor(c) + ">" + act.getString(title) + "</font><br>"); + } + + Field startField = model.getDB().getField(VirtualField.START); + + if (startField != null) + { + page.append("<font color=#999999>" + model.getDisplay().format(act.getTime(startField)) + "</font>"); + } + page.append("</b><br>"); + if (id >= 0) + { + page.append("<a href=\"e" + id + "\">EDIT</a>"); + } + page.append("<br></td><td valign=top>"); + for (Field f : fields) + { + page.append("<b><font color=#003399>" + f.getName() + "</font></b> "); + Object val = act.get(f); + if (val instanceof URL) + { + page.append("<a href=\"" + val + "\">" + val + "</a>"); + } else + { + page.append(model.getDisplay().toString(val)); + } + page.append("<br>"); + + } + page.append("<br></td></tr>"); + + return page.toString(); + } + + public String makeHeader() + { + StringBuffer page = new StringBuffer(); + page.append("<html><body><blockquote>"); + page.append("<br>File: " + model.getDbFile() + "<br>"); + page.append("Source: " + model.getDB().getSource() + "<br><br>"); + page.append("<br><br>"); + page.append("<table border=0>"); + + return page.toString(); + } + + public String makeFooter() + { + return "</table></blockquote></body></html>"; + } + + static String htmlColor(Color c) + { + return '#' + hex2(c.getRed()) + hex2(c.getGreen()) + hex2(c.getBlue()); + } + private static final String hexDigits = "0123456789ABCDEF"; + + private static String hex2(int n) + { + return hexDigits.charAt((n / 16) % 16) + "" + hexDigits.charAt(n % 16); + } + + @Override + public String getName() + { + return "HTML List"; + } } diff --git a/timeflow/views/AbstractVisualizationView.java b/timeflow/views/AbstractVisualizationView.java index 51026d6..349ee29 100755 --- a/timeflow/views/AbstractVisualizationView.java +++ b/timeflow/views/AbstractVisualizationView.java @@ -31,6 +31,10 @@ { this.model = model; + final JPopupMenu popup = new JPopupMenu(); + final JMenuItem edit = new JMenuItem("Edit"); + final JMenuItem delete = new JMenuItem("Delete"); + // deal with mouseovers. addMouseMotionListener(new MouseMotionListener() { @@ -50,9 +54,6 @@ } }); - - final JPopupMenu popup = new JPopupMenu(); - final JMenuItem edit = new JMenuItem("Edit"); edit.addActionListener(new ActionListener() { @@ -64,7 +65,6 @@ }); popup.add(edit); - final JMenuItem delete = new JMenuItem("Delete"); popup.add(delete); delete.addActionListener(new ActionListener() { diff --git a/timeflow/views/ListView.java b/timeflow/views/ListView.java index ec87f1c..1302bfa 100755 --- a/timeflow/views/ListView.java +++ b/timeflow/views/ListView.java @@ -20,247 +20,266 @@ import java.net.URL; import java.util.*; -public class ListView extends AbstractView { +public class ListView extends AbstractView +{ + private JEditorPane listDisplay; + private JComboBox sortMenu = new JComboBox(); + private ActComparator sort;//=ActComparator.byTime(); + private int maxPerPage = 50; + private int pageStart = 0; + private int lastSize = 0; + private ActList acts; + private Field sortField; + private JLabel pageLabel = new JLabel("Page", JLabel.LEFT); + private JComboBox pageMenu = new JComboBox(); + private boolean changing = false; + private JPanel controls; - private JEditorPane listDisplay; - private JComboBox sortMenu=new JComboBox(); - private ActComparator sort;//=ActComparator.byTime(); - private int maxPerPage=50; - private int pageStart=0; - private int lastSize=0; - private ActList acts; - private Field sortField; - - private JLabel pageLabel=new JLabel("Page", JLabel.LEFT); - private JComboBox pageMenu=new JComboBox(); - private boolean changing=false; - - private JPanel controls; - - public ListView(TFModel model) - { - super(model); - - listDisplay=HtmlDisplay.create(); - listDisplay.addHyperlinkListener(new LinkIt()); - JScrollPane scrollPane = new JScrollPane(listDisplay); - setLayout(new BorderLayout()); - add(scrollPane, BorderLayout.CENTER); - - - controls=new JPanel(); - controls.setLayout(null); - controls.setBackground(Color.white); - - int x=10, y=10; - int ch=25, pad=5, cw=160; - JLabel sortLabel=new JLabel("Sort Order", JLabel.LEFT); - controls.add(sortLabel); - sortLabel.setBounds(x,y,cw,ch); - y+=ch+pad; - - controls.add(sortMenu); - sortMenu.setBounds(x,y,cw,ch); - y+=ch+3*pad; - - controls.add(pageLabel); - pageLabel.setBounds(x,y,cw,ch); - y+=ch+pad; - controls.add(pageMenu); - pageMenu.setBounds(x,y,cw,ch); - - showPageMenu(false); - pageMenu.addActionListener(pageListener); - sortMenu.addActionListener(sortListener); - } - - protected JComponent _getControls() - { - return controls; - } - - ActionListener sortListener=new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - if (changing || sortMenu.getItemCount()<=0) // this means the action was fired after all items removed. - return; - sortField=getModel().getDB().getField((String)sortMenu.getSelectedItem()); - sort=sortField==null ? null : ActComparator.by(sortField); - setToFirstPage(); - makeList(); - }}; - - ActionListener pageListener=new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - if (changing) - return; - pageStart=maxPerPage*pageMenu.getSelectedIndex(); - System.out.println(e.getActionCommand()); - makeList(); - }}; - - @Override - protected void onscreen(boolean majorChange) - { - _note(null); - } - - public void _note(TFEvent e) { - changing=true; - if (e==null || e.affectsSchema() || e.affectsRowSet()) - { - sortMenu.removeActionListener(sortListener); - sortMenu.removeAllItems(); - pageStart=0; - java.util.List<Field> fields=getModel().getDB().getFields(); - Field firstField=null; - if (fields.size()>0) - firstField=fields.get(0); - for (Field f: fields) - { - sortMenu.addItem(f.getName()); - } - sortField=getModel().getDB().getField(VirtualField.START); - if (sortField!=null) - sortMenu.setSelectedItem(sortField.getName()); - else - sortField=firstField; - sortMenu.addActionListener(sortListener); - sort=null; - } - if (e!=null && e.affectsData()) - { - setToFirstPage(); - } - changing=false; - makeList(); - } - - private void setToFirstPage() - { - pageStart=0; - if (pageMenu.isVisible()) - { - pageMenu.removeActionListener(pageListener); - pageMenu.setSelectedIndex(0); - pageMenu.addActionListener(pageListener); - } - } - - void showPageMenu(boolean visible) - { - pageLabel.setVisible(visible); - pageMenu.setVisible(visible); - if (visible) - { - pageMenu.removeActionListener(pageListener); - pageMenu.setSelectedIndex(pageStart/maxPerPage); - pageMenu.addActionListener(pageListener); - } - } - - - void makeList() - { - HtmlFormat html=new HtmlFormat(); - html.setModel(getModel()); - StringBuffer page=new StringBuffer(); - - page.append(html.makeHeader()); - - - ActList as=getModel().getActs(); - if (as==null || as.size()==0 && getModel().getDB().size()==0) - { - page.append("<tr><td><h1><font color=#003399>Empty Database</font></h1></td></tr>"); - showPageMenu(false); - } - else - { - - if (sort==null) - { - Field timeField=getModel().getDB().getField(VirtualField.START); - if (timeField!=null) - sort=ActComparator.by(timeField); - } + public ListView(TFModel model) + { + super(model); - acts=as.copy(); - if (sort!=null) - Collections.sort(acts, sort); - - boolean pages=acts.size()>maxPerPage; - int last=Math.min(acts.size(), pageStart+maxPerPage); - if (pages) - { - int n=acts.size(); - if (lastSize!=n) - { - pageMenu.removeActionListener(pageListener); - pageMenu.removeAllItems(); - for (int i=0; i*maxPerPage<n;i++) - { - pageMenu.addItem("Items "+((i*maxPerPage)+1)+" to "+ - Math.min(n, (i+1)*maxPerPage)); - } - pageMenu.addActionListener(pageListener); - lastSize=n; - } - } - showPageMenu(pages); - - page.append("<tr><td><h1><font color=#003399>"+(pages? (pageStart+1)+"-"+(last) +" of ": "")+acts.size()+" Events</font></h1>"); - page.append("<br><br></td></tr>"); + listDisplay = HtmlDisplay.create(); + listDisplay.addHyperlinkListener(new LinkIt()); + JScrollPane scrollPane = new JScrollPane(listDisplay); + setLayout(new BorderLayout()); + add(scrollPane, BorderLayout.CENTER); - for (int i=pageStart; i<last; i++) - { - Act a=acts.get(i); - page.append(html.makeItem(a,i)); - } - } - page.append(html.makeFooter()); - listDisplay.setText(page.toString()); - listDisplay.setCaretPosition(0); - repaint(); - } - - - - - @Override - public String getName() { - return "List"; - } - - static class ArrayRenderer extends DefaultTableCellRenderer { - public void setValue(Object value) { - setText(Display.arrayToString((Object[])value)); - } - } - - public class LinkIt implements HyperlinkListener - { - public void hyperlinkUpdate(HyperlinkEvent e) - { - if (e.getEventType() != HyperlinkEvent.EventType.ACTIVATED) - return; - - String s=e.getDescription(); - System.out.println(s); - if (s.length()>0) - { - char c=s.charAt(0); - if (c=='e') // code for "edit" - { - int i=Integer.parseInt(s.substring(1)); - EditRecordPanel.edit(getModel(), acts.get(i)); - return; - } - - } - Display.launchBrowser(e.getURL().toString()); - - } - } + + controls = new JPanel(); + controls.setLayout(null); + controls.setBackground(Color.white); + + int x = 10, y = 10; + int ch = 25, pad = 5, cw = 160; + JLabel sortLabel = new JLabel("Sort Order", JLabel.LEFT); + controls.add(sortLabel); + sortLabel.setBounds(x, y, cw, ch); + y += ch + pad; + + controls.add(sortMenu); + sortMenu.setBounds(x, y, cw, ch); + y += ch + 3 * pad; + + controls.add(pageLabel); + pageLabel.setBounds(x, y, cw, ch); + y += ch + pad; + controls.add(pageMenu); + pageMenu.setBounds(x, y, cw, ch); + + showPageMenu(false); + pageMenu.addActionListener(pageListener); + sortMenu.addActionListener(sortListener); + } + + protected JComponent _getControls() + { + return controls; + } + ActionListener sortListener = new ActionListener() + { + + @Override + public void actionPerformed(ActionEvent e) + { + if (changing || sortMenu.getItemCount() <= 0) // this means the action was fired after all items removed. + { + return; + } + sortField = getModel().getDB().getField((String) sortMenu.getSelectedItem()); + sort = sortField == null ? null : ActComparator.by(sortField); + setToFirstPage(); + makeList(); + } + }; + ActionListener pageListener = new ActionListener() + { + + @Override + public void actionPerformed(ActionEvent e) + { + if (changing) + { + return; + } + pageStart = maxPerPage * pageMenu.getSelectedIndex(); + System.out.println(e.getActionCommand()); + makeList(); + } + }; + + @Override + protected void onscreen(boolean majorChange) + { + _note(null); + } + + public void _note(TFEvent e) + { + changing = true; + if (e == null || e.affectsSchema() || e.affectsRowSet()) + { + sortMenu.removeActionListener(sortListener); + sortMenu.removeAllItems(); + pageStart = 0; + java.util.List<Field> fields = getModel().getDB().getFields(); + Field firstField = null; + if (fields.size() > 0) + { + firstField = fields.get(0); + } + for (Field f : fields) + { + sortMenu.addItem(f.getName()); + } + sortField = getModel().getDB().getField(VirtualField.START); + if (sortField != null) + { + sortMenu.setSelectedItem(sortField.getName()); + } else + { + sortField = firstField; + } + sortMenu.addActionListener(sortListener); + sort = null; + } + if (e != null && e.affectsData()) + { + setToFirstPage(); + } + changing = false; + makeList(); + } + + private void setToFirstPage() + { + pageStart = 0; + if (pageMenu.isVisible()) + { + pageMenu.removeActionListener(pageListener); + pageMenu.setSelectedIndex(0); + pageMenu.addActionListener(pageListener); + } + } + + void showPageMenu(boolean visible) + { + pageLabel.setVisible(visible); + pageMenu.setVisible(visible); + if (visible) + { + pageMenu.removeActionListener(pageListener); + pageMenu.setSelectedIndex(pageStart / maxPerPage); + pageMenu.addActionListener(pageListener); + } + } + + void makeList() + { + HtmlFormat html = new HtmlFormat(); + html.setModel(getModel()); + StringBuffer page = new StringBuffer(); + + page.append(html.makeHeader()); + + ActList as = getModel().getActs(); + if (as == null || as.size() == 0 && getModel().getDB().size() == 0) + { + page.append("<tr><td><h1><font color=#003399>Empty Database</font></h1></td></tr>"); + showPageMenu(false); + } else + { + + if (sort == null) + { + Field timeField = getModel().getDB().getField(VirtualField.START); + if (timeField != null) + { + sort = ActComparator.by(timeField); + } + } + + acts = as.copy(); + if (sort != null) + { + Collections.sort(acts, sort); + } + + boolean pages = acts.size() > maxPerPage; + int last = Math.min(acts.size(), pageStart + maxPerPage); + if (pages) + { + int n = acts.size(); + if (lastSize != n) + { + pageMenu.removeActionListener(pageListener); + pageMenu.removeAllItems(); + for (int i = 0; i * maxPerPage < n; i++) + { + pageMenu.addItem("Items " + ((i * maxPerPage) + 1) + " to " + + Math.min(n, (i + 1) * maxPerPage)); + } + pageMenu.addActionListener(pageListener); + lastSize = n; + } + } + showPageMenu(pages); + + page.append("<tr><td><h1><font color=#003399>" + (pages ? (pageStart + 1) + "-" + (last) + " of " : "") + acts.size() + " Events</font></h1>"); + page.append("<br><br></td></tr>"); + + for (int i = pageStart; i < last; i++) + { + Act a = acts.get(i); + page.append(html.makeItem(a, i)); + } + } + page.append(html.makeFooter()); + listDisplay.setText(page.toString()); + listDisplay.setCaretPosition(0); + repaint(); + } + + @Override + public String getName() + { + return "List"; + } + + static class ArrayRenderer extends DefaultTableCellRenderer + { + + public void setValue(Object value) + { + setText(Display.arrayToString((Object[]) value)); + } + } + + public class LinkIt implements HyperlinkListener + { + + public void hyperlinkUpdate(HyperlinkEvent e) + { + if (e.getEventType() != HyperlinkEvent.EventType.ACTIVATED) + { + return; + } + + String s = e.getDescription(); + System.out.println(s); + if (s.length() > 0) + { + char c = s.charAt(0); + if (c == 'e') // code for "edit" + { + int i = Integer.parseInt(s.substring(1)); + EditRecordPanel.edit(getModel(), acts.get(i)); + return; + } + + } + Display.launchBrowser(e.getURL().toString()); + + } + } } diff --git a/timeflow/views/TimelineView.java b/timeflow/views/TimelineView.java index 778ece2..06190c6 100755 --- a/timeflow/views/TimelineView.java +++ b/timeflow/views/TimelineView.java @@ -1,21 +1,24 @@ package timeflow.views; import timeflow.app.ui.ComponentCluster; -import timeflow.data.db.*; +import timeflow.app.ui.EditRecordPanel; +import timeflow.data.db.DBUtils; import timeflow.data.time.*; import timeflow.model.*; -import timeflow.views.CalendarView.CalendarPanel; -import timeflow.views.CalendarView.ScrollingCalendar; -import timeflow.vis.*; +//import timeflow.views.CalendarView.CalendarPanel; +//import timeflow.views.CalendarView.ScrollingCalendar; +import timeflow.vis.Mouseover; +import timeflow.vis.TimeScale; +import timeflow.vis.VisualAct; import timeflow.vis.timeline.*; import java.awt.*; import java.awt.event.*; -import java.awt.image.*; +//import java.awt.image.*; import javax.swing.*; -import java.util.*; +import java.util.ArrayList; public class TimelineView extends AbstractView { @@ -67,12 +70,23 @@ // top part of grid: zoom buttons. ComponentCluster buttons = new ComponentCluster("Zoom"); - ImageIcon zoomOutIcon = new ImageIcon("images/zoom_out.gif"); - JButton zoomOut = new JButton(zoomOutIcon); + //ImageIcon zoomOutIcon = new ImageIcon("images/zoom_out.gif"); + JButton zoomIn = new JButton("In 1/2X"); + buttons.addContent(zoomIn); + zoomIn.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + Interval zoom = visuals.getViewInterval().subinterval(0.333, 0.667).intersection(visuals.getGlobalInterval()); + moveTime(zoom); + } + }); + + JButton zoomOut = new JButton("Out 2X"); buttons.addContent(zoomOut); zoomOut.addActionListener(new ActionListener() { - @Override public void actionPerformed(ActionEvent e) { @@ -82,7 +96,7 @@ }); ImageIcon zoomOut100Icon = new ImageIcon("images/zoom_out_100.gif"); - JButton zoomOutAll = new JButton(zoomOut100Icon); + JButton zoomOutAll = new JButton("Fit 100%"); buttons.addContent(zoomOutAll); zoomOutAll.addActionListener(new ActionListener() { @@ -221,17 +235,27 @@ public void run() { - int n = 15; + int n = 8; for (int i = 0; i < n; i++) { - long start = ((n - i) * i1.start + i * i2.start) / n; - long end = ((n - i) * i1.end + i * i2.end) / n; + final long start = ((n - i) * i1.start + i * i2.start) / n; + final long end = ((n - i) * i1.end + i * i2.end) / n; try { - visuals.setTimeBounds(start, end); - redraw(); +// visuals.setTimeBounds(start, end); +// redraw(); + SwingUtilities.invokeAndWait(new Runnable() + { + + @Override + public void run() + { + visuals.setTimeBounds(start, end); + redraw(); + } + }); - sleep(20); + //sleep(20); } catch (Exception e) { } @@ -312,7 +336,20 @@ { if (e.getClickCount() == 2) { - moveTime(visuals.getViewInterval().subinterval(.333, .667)); + Point p = new Point(e.getX(), e.getY()); + Mouseover o = find(p); + //moveTime(visuals.getViewInterval().subinterval(.333, .667)); + boolean onAct = o != null && o.thing instanceof VisualAct; + if (onAct) + { + VisualAct v = (VisualAct) o.thing; + selectedAct = v.getAct(); + EditRecordPanel.edit(getModel(), selectedAct); + } else + { + selectedTime = getTime(p); + EditRecordPanel.add(getModel(), selectedTime); + } } } @@ -358,6 +395,11 @@ int a = firstMouse.x; int b = mouse.x; + if (a == b) + { + return; + } + long start = visuals.getTimeScale().toTime(a); long end = visuals.getTimeScale().toTime(b); if (b - a < 0) @@ -397,12 +439,18 @@ @Override public void mouseWheelMoved(MouseWheelEvent e) { - //bar.setValue(bar.getValue() + e.getWheelRotation() * 10); - visuals.scale *= Math.exp(e.getWheelRotation()/10.0); - visuals.scale = visuals.layout(); - //System.out.println(visuals.scale); - timelinePanel.drawVisualization(); - scroller.calibrate(); + if (e.isMetaDown()) + { + visuals.scale *= Math.exp(e.getWheelRotation()/10.0); + visuals.scale = visuals.layout(); + //System.out.println(visuals.scale); + timelinePanel.drawVisualization(); + scroller.calibrate(); + } + else + { + bar.setValue(bar.getValue() + e.getWheelRotation() * 40); + } repaint(); } }); diff --git a/timeflow/vis/Mouseover.java b/timeflow/vis/Mouseover.java index adf3009..eb2b74b 100755 --- a/timeflow/vis/Mouseover.java +++ b/timeflow/vis/Mouseover.java @@ -5,90 +5,94 @@ import java.awt.*; import java.util.ArrayList; -public class Mouseover extends Rectangle { - public Object thing; - public Mouseover(Object thing, int x, int y, int w, int h) - { - super(x,y,w,h); - this.thing=thing; - } - - public void draw(Graphics2D g, int maxW, int maxH, Display display) - { - g.setColor(new Color(0,53,153)); - g.setColor(new Color(255,255,0,100)); - g.fill(this); - } - - protected void draw(Graphics2D g, int maxW, int maxH, Display display, ArrayList labels, int numLines) - { - if (labels==null || labels.size()==0) - return; - - // draw a background box. - - // find max number of chars, very very roughly! - int boxW=50; - for (int i=0; i<labels.size(); i+=2) - { - if (labels.get(i) instanceof String[]) - boxW=300; - else if (labels.get(i) instanceof String) - { - boxW=Math.max(boxW, 50+50*((String)labels.get(i)).length()); - } - } - - - boxW=Math.min(350, boxW); - int boxH=18*numLines+10; - int mx=this.x+this.width+5; - int my=this.y+this.height+35; - - // put box in a place where it does not obscure the data - // or go off screen. - if (my+boxH>maxH-10) - { - my=Math.max(10,this.y-boxH-5); - } - if (mx+boxW>maxW-10) - { - mx=Math.max(10,this.x-boxW-10); - } - int ty=my; - g.setColor(new Color(0,0,0,70)); - g.fillRoundRect(mx-11, my-16, boxW, boxH,12,12); - g.setColor(Color.white); - g.fillRoundRect(mx-15, my-20, boxW, boxH,12,12); - g.setColor(Color.darkGray); - g.drawRoundRect(mx-15, my-20, boxW, boxH,12,12); - - // finally, draw the darn labels. - for (int i=0; i<labels.size(); i+=2) - { - g.setFont(display.bold()); - String field=(String)labels.get(i); - g.drawString(field,mx,ty); - int sw=display.boldFontMetrics().stringWidth(field); - g.setFont(display.plain()); - Object o=labels.get(i+1); - if (o instanceof String) - { - g.drawString((String)o,mx+sw+9,ty); - ty+=18; - } - else - { - ArrayList<String> lines=(ArrayList<String>)o; - int dx=sw+9; - for (String line: lines) - { - g.drawString((String)line,mx+dx,ty); - ty+=18; - dx=0; - } - ty+=5; - } - } - } +public class Mouseover extends Rectangle +{ + public Object thing; + + public Mouseover(Object thing, int x, int y, int w, int h) + { + super(x, y, w, h); + this.thing = thing; + } + + public void draw(Graphics2D g, int maxW, int maxH, Display display) + { + g.setColor(new Color(0, 53, 153)); + g.setColor(new Color(255, 255, 0, 100)); + g.fill(this); + } + + protected void draw(Graphics2D g, int maxW, int maxH, Display display, ArrayList labels, int numLines) + { + if (labels == null || labels.size() == 0) + { + return; + } + + // draw a background box. + + // find max number of chars, very very roughly! + int boxW = 50; + for (int i = 0; i < labels.size(); i += 2) + { + if (labels.get(i) instanceof String[]) + { + boxW = 300; + } else if (labels.get(i) instanceof String) + { + boxW = Math.max(boxW, 50 + 50 * ((String) labels.get(i)).length()); + } + } + + + boxW = Math.min(350, boxW); + int boxH = 18 * numLines + 10; + int mx = this.x + this.width + 5; + int my = this.y + this.height + 35; + + // put box in a place where it does not obscure the data + // or go off screen. + if (my + boxH > maxH - 10) + { + my = Math.max(10, this.y - boxH - 5); + } + if (mx + boxW > maxW - 10) + { + mx = Math.max(10, this.x - boxW - 10); + } + int ty = my; + g.setColor(new Color(0, 0, 0, 70)); + g.fillRoundRect(mx - 11, my - 16, boxW, boxH, 12, 12); + g.setColor(Color.white); + g.fillRoundRect(mx - 15, my - 20, boxW, boxH, 12, 12); + g.setColor(Color.darkGray); + g.drawRoundRect(mx - 15, my - 20, boxW, boxH, 12, 12); + + // finally, draw the darn labels. + for (int i = 0; i < labels.size(); i += 2) + { + g.setFont(display.bold()); + String field = (String) labels.get(i); + g.drawString(field, mx, ty); + int sw = display.boldFontMetrics().stringWidth(field); + g.setFont(display.plain()); + Object o = labels.get(i + 1); + if (o instanceof String) + { + g.drawString((String) o, mx + sw + 9, ty); + ty += 18; + } else + { + ArrayList<String> lines = (ArrayList<String>) o; + int dx = sw + 9; + for (String line : lines) + { + g.drawString((String) line, mx + dx, ty); + ty += 18; + dx = 0; + } + ty += 5; + } + } + } } diff --git a/timeflow/vis/VisualAct.java b/timeflow/vis/VisualAct.java index 4c3569b..1351c06 100755 --- a/timeflow/vis/VisualAct.java +++ b/timeflow/vis/VisualAct.java @@ -12,7 +12,6 @@ public class VisualAct implements Comparable { - Color color; String label; String mouseOver; diff --git a/timeflow/vis/timeline/AxisRenderer.java b/timeflow/vis/timeline/AxisRenderer.java index 7d020cb..0372be6 100755 --- a/timeflow/vis/timeline/AxisRenderer.java +++ b/timeflow/vis/timeline/AxisRenderer.java @@ -9,80 +9,83 @@ import timeflow.vis.Mouseover; import timeflow.vis.TimeScale; -public class AxisRenderer { - - TimelineVisuals visuals; - - public AxisRenderer(TimelineVisuals visuals) - { - this.visuals=visuals; - } - - public void render(Graphics2D g, Collection<Mouseover> objectLocations) - { - TFModel model=visuals.getModel(); - g.setColor(model.getDisplay().getColor("chart.background")); - Rectangle bounds=visuals.getBounds(); - - TimeScale scale=visuals.getTimeScale(); - java.util.List<AxisTicMarks> t=AxisTicMarks.allRelevant(scale.getInterval()); - - int dateLabelH=model.getDisplay().getInt("timeline.datelabel.height"); - int y=bounds.y+bounds.height-dateLabelH; - - // draw in reverse order so bigger granularity at top. - int n=t.size(); - for (int i=0; i<n; i++) - { - render(t.get(i), g, bounds.x, y, dateLabelH-1, bounds.y, i==0, objectLocations); - y-=dateLabelH; - } - } - - void render(AxisTicMarks t, Graphics2D g, int x, int y, int h, int top, boolean full, Collection<Mouseover> objectLocations) - { - TFModel model=visuals.getModel(); +public class AxisRenderer +{ + TimelineVisuals visuals; - int n=t.tics.size(); - for (int i=0; i<n-1; i++) - { - - long start=t.tics.get(i); - long end=t.tics.get(i+1); - - int x0=Math.max(x,visuals.getTimeScale().toInt(start)); - int x1=visuals.getTimeScale().toInt(end); - - int dayOfWeek=TimeUtils.cal(start).get(Calendar.DAY_OF_WEEK); - - g.setColor(t.unit.isDayOrLess() && (dayOfWeek==1 || dayOfWeek==7) ? - new Color(245,245,245) : new Color(240,240,240)); + public AxisRenderer(TimelineVisuals visuals) + { + this.visuals = visuals; + } - g.fillRect(x0, y, x1-x0-1, h); - g.setColor(Color.white); - g.drawLine(x1-1, y, x1-1, y+h); - g.drawLine(x0,y+h,x1,y+h); - objectLocations.add(new Mouseover(new Interval(start,end), x0, y, x1-x0-1, h)); - - g.setFont(model.getDisplay().timeLabel()); - String label=full? t.unit.formatFull(start) : t.unit.format(new Date(start)); - int tx=x0+3; - int ty=y+h-5; - g.setColor(full ? Color.darkGray : Color.gray); - int sw=model.getDisplay().timeLabelFontMetrics().stringWidth(label); - if (sw<x1-tx-3) - g.drawString(label, tx,ty); - else - { - int c=label.indexOf(':'); - if (c>0) - { - label=label.substring(0,c); - sw=model.getDisplay().timeLabelFontMetrics().stringWidth(label); - if (sw<x1-tx-3) - g.drawString(label, tx,ty); - } - } - } - } + public void render(Graphics2D g, Collection<Mouseover> objectLocations) + { + TFModel model = visuals.getModel(); + g.setColor(model.getDisplay().getColor("chart.background")); + Rectangle bounds = visuals.getBounds(); + + TimeScale scale = visuals.getTimeScale(); + java.util.List<AxisTicMarks> t = AxisTicMarks.allRelevant(scale.getInterval()); + + int dateLabelH = model.getDisplay().getInt("timeline.datelabel.height"); + int y = bounds.y + bounds.height - dateLabelH; + + // draw in reverse order so bigger granularity at top. + int n = t.size(); + for (int i = 0; i < n; i++) + { + render(t.get(i), g, bounds.x, y, dateLabelH - 1, bounds.y, i == 0, objectLocations); + y -= dateLabelH; + } + } + + void render(AxisTicMarks t, Graphics2D g, int x, int y, int h, int top, boolean full, Collection<Mouseover> objectLocations) + { + TFModel model = visuals.getModel(); + + int n = t.tics.size(); + for (int i = 0; i < n - 1; i++) + { + + long start = t.tics.get(i); + long end = t.tics.get(i + 1); + + int x0 = Math.max(x, visuals.getTimeScale().toInt(start)); + int x1 = visuals.getTimeScale().toInt(end); + + int dayOfWeek = TimeUtils.cal(start).get(Calendar.DAY_OF_WEEK); + + g.setColor(t.unit.isDayOrLess() && (dayOfWeek == 1 || dayOfWeek == 7) + ? new Color(245, 245, 245) : new Color(240, 240, 240)); + + g.fillRect(x0, y, x1 - x0 - 1, h); + g.setColor(Color.white); + g.drawLine(x1 - 1, y, x1 - 1, y + h); + g.drawLine(x0, y + h, x1, y + h); + objectLocations.add(new Mouseover(new Interval(start, end), x0, y, x1 - x0 - 1, h)); + + g.setFont(model.getDisplay().timeLabel()); + String label = full ? t.unit.formatFull(start) : t.unit.format(new Date(start)); + int tx = x0 + 3; + int ty = y + h - 5; + g.setColor(full ? Color.darkGray : Color.gray); + int sw = model.getDisplay().timeLabelFontMetrics().stringWidth(label); + if (sw < x1 - tx - 3) + { + g.drawString(label, tx, ty); + } else + { + int c = label.indexOf(':'); + if (c > 0) + { + label = label.substring(0, c); + sw = model.getDisplay().timeLabelFontMetrics().stringWidth(label); + if (sw < x1 - tx - 3) + { + g.drawString(label, tx, ty); + } + } + } + } + } } diff --git a/timeflow/vis/timeline/AxisTicMarks.java b/timeflow/vis/timeline/AxisTicMarks.java index 85a16dd..edbd96b 100755 --- a/timeflow/vis/timeline/AxisTicMarks.java +++ b/timeflow/vis/timeline/AxisTicMarks.java @@ -4,114 +4,116 @@ import timeflow.data.time.*; -public class AxisTicMarks { - public TimeUnit unit; - public List<Long> tics; - - private static final TimeUnit[] units={ - TimeUnit.YEAR, TimeUnit.MONTH, TimeUnit.DAY, TimeUnit.HOUR, TimeUnit.MINUTE, TimeUnit.SECOND - }; - - private static final TimeUnit[] histUnits={ - TimeUnit.YEAR.times(100), TimeUnit.YEAR.times(50), TimeUnit.YEAR.times(25), - TimeUnit.YEAR.times(10), TimeUnit.YEAR.times(5), TimeUnit.YEAR.times(2), TimeUnit.YEAR, - TimeUnit.MONTH.times(6), TimeUnit.MONTH.times(3), TimeUnit.MONTH.times(2), TimeUnit.MONTH, - TimeUnit.WEEK, TimeUnit.DAY.times(2), TimeUnit.DAY, +public class AxisTicMarks +{ + public TimeUnit unit; + public List<Long> tics; + private static final TimeUnit[] units = + { + TimeUnit.YEAR, TimeUnit.MONTH, TimeUnit.DAY, TimeUnit.HOUR, TimeUnit.MINUTE, TimeUnit.SECOND + }; + private static final TimeUnit[] histUnits = + { + TimeUnit.YEAR.times(100), TimeUnit.YEAR.times(50), TimeUnit.YEAR.times(25), + TimeUnit.YEAR.times(10), TimeUnit.YEAR.times(5), TimeUnit.YEAR.times(2), TimeUnit.YEAR, + TimeUnit.MONTH.times(6), TimeUnit.MONTH.times(3), TimeUnit.MONTH.times(2), TimeUnit.MONTH, + TimeUnit.WEEK, TimeUnit.DAY.times(2), TimeUnit.DAY, + TimeUnit.HOUR, + TimeUnit.MINUTE, + TimeUnit.SECOND + }; - TimeUnit.HOUR, - TimeUnit.MINUTE, - TimeUnit.SECOND - }; - - public AxisTicMarks(TimeUnit unit, long start, long end) - { - this.unit=unit; - tics=new ArrayList<Long>(); - RoughTime r=unit.roundDown(start); - tics.add(r.getTime()); - do - { - unit.addTo(r); - tics.add(r.getTime()); - } while (r.getTime()<end); - } - - - - public static List<AxisTicMarks> allRelevant(Interval interval) - { - return allRelevant(interval.start, interval.end); - } - - public static List<AxisTicMarks> allRelevant(long start, long end) - { - return allRelevant(start, end, 40); - } - - public static AxisTicMarks histoTics(long start, long end) - { - for (int i=histUnits.length-1; i>=0; i--) - { - TimeUnit u=histUnits[i]; - long estimate=u.approxNumInRange(start, end); - if (estimate<200 || i==0) - { - AxisTicMarks t=new AxisTicMarks(u, start, end); - return t; - } - } - return null; - } - - public static List<AxisTicMarks> allRelevant(long start, long end, long maxTics) - { - List<AxisTicMarks> list=new ArrayList<AxisTicMarks>(); - - - for (int i=0; i<units.length; i++) - { - TimeUnit u=units[i]; - long estimate=u.approxNumInRange(start, end); - - if (estimate<maxTics) - { - AxisTicMarks t=new AxisTicMarks(u, start, end); - if (list.size()>0) - { - AxisTicMarks last=list.get(0); - if (last.tics.size()==t.tics.size()) - list.remove(0); - } - list.add(t); - - } - } - while (list.size()>2) - list.remove(0); - - if (list.size()==0) // uh oh! must be many years. we will add in bigger increments. - { - long length=end-start; - long size=365*24*60*60*1000L; - int m=1; - maxTics=15; - while (m<2000000000 && length/(m*size)>maxTics) - { - if (length/(2*m*size)<=maxTics) - { - m*=2; - break; - } - if (length/(5*m*size)<=maxTics) - { - m*=5; - break; - } - m*=10; - } - AxisTicMarks t=new AxisTicMarks(TimeUnit.multipleYears(m), start, end); - list.add(t); - } - return list; - } + public AxisTicMarks(TimeUnit unit, long start, long end) + { + this.unit = unit; + tics = new ArrayList<Long>(); + RoughTime r = unit.roundDown(start); + tics.add(r.getTime()); + do + { + unit.addTo(r); + tics.add(r.getTime()); + } while (r.getTime() < end); + } + + public static List<AxisTicMarks> allRelevant(Interval interval) + { + return allRelevant(interval.start, interval.end); + } + + public static List<AxisTicMarks> allRelevant(long start, long end) + { + return allRelevant(start, end, 40); + } + + public static AxisTicMarks histoTics(long start, long end) + { + for (int i = histUnits.length - 1; i >= 0; i--) + { + TimeUnit u = histUnits[i]; + long estimate = u.approxNumInRange(start, end); + if (estimate < 200 || i == 0) + { + AxisTicMarks t = new AxisTicMarks(u, start, end); + return t; + } + } + return null; + } + + public static List<AxisTicMarks> allRelevant(long start, long end, long maxTics) + { + List<AxisTicMarks> list = new ArrayList<AxisTicMarks>(); + + + for (int i = 0; i < units.length; i++) + { + TimeUnit u = units[i]; + long estimate = u.approxNumInRange(start, end); + + if (estimate < maxTics) + { + AxisTicMarks t = new AxisTicMarks(u, start, end); + if (list.size() > 0) + { + AxisTicMarks last = list.get(0); + if (last.tics.size() == t.tics.size()) + { + list.remove(0); + } + } + list.add(t); + + } + } + while (list.size() > 2) + { + list.remove(0); + } + + if (list.size() == 0) // uh oh! must be many years. we will add in bigger increments. + { + long length = end - start; + long size = 365 * 24 * 60 * 60 * 1000L; + int m = 1; + maxTics = 15; + while (m < 2000000000 && length / (m * size) > maxTics) + { + if (length / (2 * m * size) <= maxTics) + { + m *= 2; + break; + } + if (length / (5 * m * size) <= maxTics) + { + m *= 5; + break; + } + m *= 10; + } + AxisTicMarks t = new AxisTicMarks(TimeUnit.multipleYears(m), start, end); + list.add(t); + } + return list; + } } -- Gitblit v1.6.2